# Camunda 8 Documentation > Process orchestration platform for automating workflows across people, systems, and devices. Supports BPMN, DMN, connectors, and agentic AI orchestration. This file contains all documentation content in a single document following the llmstxt.org standard. ## Administration API (SaaS) ## About You can use the Camunda 8 Administration API (SaaS) as a programmatic interface for managing your Camunda 8 SaaS clusters and API clients. - Provides endpoints for common operations such as cluster backup, creation, and deletion, and client and member management. - The API allows for IP allowlisting and secret management. ## Try with Swagger Use the interactive Swagger API explorer with your Camunda 8 cluster (requires a valid access token). [Swagger API explorer](https://console.cloud.camunda.io/customer-api/openapi/docs/#/) ## Authentication All Administration API requests require authentication. To authenticate, generate a [JSON Web Token (JWT)](https://jwt.io/introduction/) and include it in each request. [Authentication](authentication.md) --- ## Authentication All Administration API requests require authentication. To authenticate, generate a [JSON Web Token (JWT)](https://jwt.io/introduction/) and include it in each request. ## Generate a token 1. Create client credentials by clicking **Console > Organization > Administration API > Create new credentials**. 2. Add permissions to this client for [the needed scopes](#client-credentials-and-scopes). 3. Once you have created the client, capture the following values required to generate a token: | Name | Environment variable name | Default value | | ------------------------ | -------------------------------- | -------------------------------------------- | | Client ID | `CAMUNDA_CONSOLE_CLIENT_ID` | - | | Client Secret | `CAMUNDA_CONSOLE_CLIENT_SECRET` | - | | Authorization Server URL | `CAMUNDA_OAUTH_URL` | `https://login.cloud.camunda.io/oauth/token` | | Audience | `CAMUNDA_CONSOLE_OAUTH_AUDIENCE` | `api.cloud.camunda.io` | :::caution When client credentials are created, the `Client Secret` is only shown once. Save this `Client Secret` somewhere safe. ::: 4. Execute an authentication request to the token issuer: ```bash curl --request POST ${CAMUNDA_OAUTH_URL} \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'grant_type=client_credentials' \ --data-urlencode "audience=${CAMUNDA_CONSOLE_OAUTH_AUDIENCE}" \ --data-urlencode "client_id=${CAMUNDA_CONSOLE_CLIENT_ID}" \ --data-urlencode "client_secret=${CAMUNDA_CONSOLE_CLIENT_SECRET}" ``` A successful authentication response looks like the following: ```json { "access_token": "", "expires_in": 300, "refresh_expires_in": 0, "token_type": "Bearer", "not-before-policy": 0 } ``` 5. Capture the value of the `access_token` property and store it as your token. ## Use a token Include the previously captured token as an authorization header in each request: `Authorization: Bearer `. For example, to send a request to the Administration API's `/members` endpoint: ```shell curl --header "Authorization: Bearer ${TOKEN}" \ https://api.cloud.camunda.io/members ``` A successful response includes [a list of organization members](https://console.cloud.camunda.io/customer-api/openapi/docs/#/default/GetMembers). For example: ```json [ { "name": "User Userton", "email": "user@example.com", "roles": ["admin"], "invitePending": false } ] ``` ## Token expiration Access tokens expire according to the `expires_in` property of a successful authentication response. After this duration, in seconds, you must request a new access token. ## Client credentials and scopes To interact with Camunda 8 programmatically without using the Camunda 8 Console, create client credentials in the organization settings under the **Administration API** tab. Client credentials are created for an organization, and therefore can access all Camunda 8 clusters of this organization. Scopes define the access for client credentials. A client can have one or multiple of the following permissions: ![createConsoleApiClient](../../components/hub/organization/manage-organization-settings/img/create-console-api-client.png) A client can have one or multiple permissions from the following groups: - **Cluster**: [Manage your clusters](/components/hub/organization/manage-clusters/create-cluster.md). - **Zeebe Client**: [Manage API clients](/components/hub/organization/manage-clusters/manage-api-clients.md) for your cluster. - **Web Modeler API**: Interact with the [Web Modeler API](/apis-tools/web-modeler-api/index.md). - **IP allowlist**: Configure [IP allowlist](/components/hub/organization/manage-clusters/manage-ip-allowlists.md) rules. - **Connector Secrets**: [Manage secrets](/components/hub/organization/manage-clusters/manage-secrets.md) of your clusters. - **Members**: [Manage members](/components/hub/organization/manage-members/manage-users.md) of your organization. - **Backups**: Manage [backups](/components/saas/backups.md) of your Camunda 8 clusters (only available to Enterprise customers). The full API description can be found [here](https://console.cloud.camunda.io/customer-api/openapi/docs/#/). ## Rate limiting The OAuth service rate limits about one request per second for all clients with the same source IP address. :::note All token requests count toward the rate limit, whether they are successful or not. If any client is running with an expired or invalid API key, that client will continually make token requests. That client will therefore exceed the rate limit for that IP address, and may block valid token requests from completing. ::: The officially offered [client libraries](/apis-tools/working-with-apis-tools.md) (as well as the Node.js and Spring clients) have already integrated with the auth routine, handle obtaining and refreshing an access token, and make use of a local cache. If too many token requests are executed from the same source IP address in a short time, all token requests from that source IP address are blocked for a certain time. Since the access tokens have a 24-hour validity period, they must be cached on the client side, reused while still valid, and refreshed via a new token request once the validity period has expired. When the rate limit is triggered, the client will receive an HTTP 429 response. Note the following workarounds: - Cache the token as it is still valid for 24 hours. The official SDKs already do this by default. - Keep the SDK up to date. We have noted issues in older versions of the Java SDK which did not correctly cache the token. - Given the rate limit applies to clients with the same source IP address, be mindful of: - Unexpected clients running within your infrastructure. - Updating all clients to use a current API key if you delete an API key and create a new one. --- ## Tutorial In this tutorial, we'll step through examples to highlight the capabilities of the Administration API, such as viewing your existing clients, creating a client, viewing a particular client's details, and deleting a client. ## Prerequisites - If you haven't done so already, [create a cluster](/components/react-components/create-cluster.md). - Upon cluster creation, create your first client by navigating to **Console > Organization > Administration API > Create new credentials**. Ensure you determine the scoped access for client credentials. For example, in this tutorial we will get, create, and delete a client. Ensure you check all the boxes for Zeebe client scopes. :::note Make sure you keep the generated client credentials in a safe place. The **Client secret** will not be shown again. For your convenience, you can also download the client information to your computer. ::: - In this tutorial, we utilize a JavaScript-written [GitHub repository](https://github.com/camunda/camunda-api-tutorials) to write and run requests. Clone this repo before getting started. - Ensure you have [Node.js](https://nodejs.org/en/download) installed as this will be used for methods that can be called by the CLI (outlined later in this guide). Run `npm install` to ensure you have updated dependencies. ## Getting started - A detailed API description can be found [here](https://console.cloud.camunda.io/customer-api/openapi/docs/#/) via Swagger. With a valid access token, this offers an interactive API experience against your Camunda 8 cluster. - You need authentication to access the API endpoints. Find more information [here](/apis-tools/administration-api/authentication.md). ## Set up authentication If you're interested in how we use a library to handle auth for our code, or to get started, examine the `auth.js` file in the GitHub repository. This file contains a function named `getAccessToken` which executes an OAuth 2.0 protocol to retrieve authentication credentials based on your client ID and client secret. Then, we return the actual token that can be passed as an authorization header in each request. To set up your credentials, create an `.env` file which will be protected by the `.gitignore` file. You will need to add your `CLUSTER_ID`, `ADMINISTRATION_CLIENT_ID`, `ADMINISTRATION_CLIENT_SECRET`, `ADMINISTRATION_AUDIENCE`, which is `api.cloud.camunda.io` in a Camunda 8 SaaS environment, and `ADMINISTRATION_API_URL`, which is `https://api.cloud.camunda.io`. These keys will be consumed by the `auth.js` file to execute the OAuth protocol, and should be saved when you generate your client credentials in [prerequisites](#prerequisites). :::tip Can't find your environment variables? When you create new client credentials as a [prerequisite](#prerequisites), your environment variables appear in a pop-up window. Your environment variables may appear as `CAMUNDA_CONSOLE_CLIENT_ID`, `CAMUNDA_CONSOLE_CLIENT_SECRET`, `CAMUNDA_CONSOLE_OAUTH_AUDIENCE`, and `CAMUNDA_CONSOLE_BASE_URL`. Locate your `CLUSTER_ID` in Console by navigating to **Clusters**. Scroll down and copy your **Cluster Id** under **Cluster Details**. ::: Examine the existing `.env.example` file for an example of how your `.env` file should look upon completion. Do not place your credentials in the `.env.example` file, as this example file is not protected by the `.gitignore`. :::note In this tutorial, we will execute arguments to view, create, and delete clients. You can examine the framework for processing these arguments in the `cli.js` file before getting started. ::: ## GET a list of existing clients First, let's script an API call to list our existing clients. To do this, take the following steps: 1. In the file named `administration.js`, outline the authentication and authorization configuration in the first few lines. This will pull in your `.env` variables to obtain an access token before making any API calls: ```javascript const authorizationConfiguration = { clientId: process.env.ADMINISTRATION_CLIENT_ID, clientSecret: process.env.ADMINISTRATION_CLIENT_SECRET, audience: process.env.ADMINISTRATION_AUDIENCE, }; ``` 2. Examine the function `async function listClients()` below this configuration. This is where you will script out your API call. 3. Within the function, you must first apply an access token for this request, so your function should now look like the following: ```javascript async function listClients() { const accessToken = await getAccessToken(authorizationConfiguration); } ``` 4. As noted in the detailed API description in [Swagger](https://console.cloud.camunda.io/customer-api/openapi/docs/#/), you must call your Administration API URL and cluster ID. Using your generated client credentials from [prerequisites](#prerequisites), capture your Administration API URL and cluster ID beneath your call for an access token by defining `administrationApiUrl` and `clusterId`: ```javascript const administrationApiUrl = process.env.ADMINISTRATION_API_URL; const clusterId = process.env.CLUSTER_ID; ``` 5. On the next line, script the API endpoint to list your existing clients for a particular cluster: ```javascript const url = `${administrationApiUrl}/clusters/${clusterId}/clients`; ``` 6. Configure your GET request to the appropriate endpoint, including an authorization header based on the previously acquired `accessToken`: ```javascript const options = { method: "GET", url, headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, }, }; ``` 7. Call the clients' endpoint, process the results from the API call, emit the clients to output, and emit an error message from the server if necessary: ```javascript try { // Call the clients endpoint. const response = await axios(options); // Process the results from the API call. const results = response.data; // Emit clients to output. results.forEach((x) => console.log(`Name: ${x.name}; ID: ${x.clientId}`)); } catch (error) { // Emit an error from the server. console.error(error.message); } ``` 8. In your terminal, run `npm run cli admin list` for a list of your existing clients. :::note This `list` command is connected to the `listClients` function at the bottom of the `administration.js` file, and executed by the `cli.js` file. While we will view, create, and delete clients in this tutorial, you may add additional arguments depending on the API calls you would like to make. ::: If you have any existing clients, the `Name: {name}; ID: {Id}` will now output. If you have an invalid API name or action name, or no arguments provided, or improper/insufficient credentials configured, an error message will output as outlined in the `cli.js` file. ## POST a client To create a new client, you will follow similar steps as outlined in your [GET request] (#get-clientid) above: 1. Edit the `addClient` function, incorporate the access token, and add your settings in the `.env` file. Note that this function destructures the `clientName` as the first item in an array passed in. ```javascript async function addClient([clientName]) { const accessToken = await getAccessToken(authorizationConfiguration); const administrationApiUrl = process.env.ADMINISTRATION_API_URL; const clusterId = process.env.CLUSTER_ID; ``` 2. Adjust your API endpoint to add a new client to a cluster: ```javascript const url = `${administrationApiUrl}/clusters/${clusterId}/clients`; ``` 3. When configuring your API call, issue a POST request, and add a body containing information for the new client: ```javascript const options = { method: "POST", url, headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, }, data: { clientName: clientName, }, }; ``` 4. Call the `add` endpoint and process the results from the API call: ```javascript const response = await axios(options); const newClient = response.data; ``` 5. Emit the new client to output. While different from this example, you will likely want to capture the `clientSecret` property from the response, as this cannot be displayed again: ```javascript console.log( `Client added! Name: ${newClient.name}. ID: ${newClient.clientId}.` ); } catch (error) { // Emit an error from the server. console.error(error.message); } ``` 6. In your terminal, run `npm run cli admin add `, where `` is where you can paste the name of your new client. ## GET a client ID To get a client ID, take the following steps: 1. Outline your function, similar to the steps above: ```javascript async function viewClient([clientId]) { const accessToken = await getAccessToken(authorizationConfiguration); const administrationApiUrl = process.env.ADMINISTRATION_API_URL; const clusterId = process.env.CLUSTER_ID; ``` 2. Write the API endpoint to view a single client within a cluster: ```javascript const url = `${administrationApiUrl}/clusters/${clusterId}/clients/${clientId}`; ``` 3. Call the client endpoint using a GET method: ```javascript var options = { method: "GET", url, headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, }, }; ``` 4. Process your results from the API call and emit the client details: ```javascript try { const response = await axios(options); const clientResponse = response.data; console.log("Client:", clientResponse); } catch (error) { console.error(error.message); } ``` 5. In your terminal, run `npm run cli admin view` to view your client. ## DELETE a client To delete a client, take the following steps: 1. Outline your function, similar to the steps above: ```javascript async function deleteClient([clientId]) { const accessToken = await getAccessToken(authorizationConfiguration); const administrationApiUrl = process.env.ADMINISTRATION_API_URL; const clusterId = process.env.CLUSTER_ID; const url = `${administrationApiUrl}/clusters/${clusterId}/clients/${clientId}`; } ``` 2. Configure the API call using the DELETE method: ```javascript var options = { method: "DELETE", url, headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, }, }; ``` 3. Process the results from the API call. For example: ```javascript try { const response = await axios(options); if (response.status === 204) { console.log(`Client ${clientId} was deleted!`); } else { console.error("Unable to delete client!"); } } catch (error) { console.error(error.message); } ``` 4. In your terminal, run `npm run cli admin delete `, where `` is where you can paste the ID of the client you would like to delete. ## If you get stuck Having trouble configuring your API calls or want to examine an example of the completed tutorial? Navigate to the `completed` folder in the [GitHub repository](https://github.com/camunda/camunda-api-tutorials/tree/main/completed), where you can view an example `administration.js` file. ## Next steps You can script several additional API calls as outlined in the [Administration API reference material](/apis-tools/administration-api/administration-api-reference.md). --- ## Authentication(Administration-sm-api) All Administration Self-Managed API requests require authentication. To authenticate, generate a [JSON Web Token (JWT)](https://jwt.io/introduction/) and include it in each request. ## Generate a token 1. [Add an M2M application in Management Identity](/self-managed/components/management-identity/application-user-group-role-management/applications.md). 2. [Add permissions to this application](/self-managed/components/management-identity/application-user-group-role-management/applications.md) for **Console API**. 3. Capture the `Client ID` and `Client Secret` from the application in Management Identity. 4. [Generate a token](/self-managed/components/management-identity/authentication.md) to access the Administration REST API. Provide the `client_id` and `client_secret` from the values you previously captured in Management Identity. ```shell curl --location --request POST 'http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode "client_id=${CLIENT_ID}" \ --data-urlencode "client_secret=${CLIENT_SECRET}" \ --data-urlencode 'grant_type=client_credentials' ``` A successful authentication response looks like the following: ```json { "access_token": "", "expires_in": 300, "refresh_expires_in": 0, "token_type": "Bearer", "not-before-policy": 0 } ``` 5. Capture the value of the `access_token` property and store it as your token. ## Use a token Include the previously captured token as an authorization header in each request: `Authorization: Bearer `. For example, to send a request to the ["Get current clusters"](./specifications/get-clusters.api.mdx) endpoint: :::tip The `${CAMUNDA_BASE_URL}` variable below represents the URL of the Self-Managed environment. You can configure this value in your Self-Managed installation. The default value is `http://localhost:8080`. ::: ```shell curl --request GET ${CAMUNDA_BASE_URL}/admin-api/clusters \ --header "Authorization: Bearer ${TOKEN}" ``` A successful response includes [cluster information](./specifications/get-clusters.api.mdx). For example: ```json [ { "uuid": "12345", "name": "cluster-1", "status": "healthy", ... } ] ``` ## Token expiration Access tokens expire according to the `expires_in` property of a successful authentication response. After this duration, in seconds, you must request a new access token. --- ## Administration API (Self-Managed) ## About You can use the Administration API for Self-Managed REST API to retrieve cluster data from your Self-Managed environment, including installed apps and usage metrics. ## Authentication To access the Administration Self-Managed REST API, clients must include a JWT access token in the authorization header: ``` Authorization: Bearer ``` For more details, see the [authentication guide](./administration-sm-api-authentication.md). ## API reference and explorer Use the [interactive Administration API Self-Managed Explorer][administration-api-explorer] to view specifications, example requests and responses, and code samples for interacting with the Administration Self-Managed API. You can also access a detailed API description via the [OpenAPI](https://www.openapis.org/) specification in a running instance of Console Self-Managed at: ``` https://${base-url}/admin-api/openapi/docs ``` [administration-api-explorer]: ./specifications/administration-api-self-managed.info.mdx --- ## Build your own client If you're using a technology with no library yet, you can easily implement your own client. Refer to the following two blog posts about creating a client: - [Generating a Zeebe-Python Client Stub in Less Than An Hour: A gRPC + Zeebe Tutorial](https://camunda.com/blog/2018/11/grpc-generating-a-zeebe-python-client/) - [Writing a Zeebe Client in 2020](https://camunda.com/blog/2020/06/zeebe-client-2020/) There are two essential steps: 1. Authentication via OAuth 2. gRPC handling ## Authentication via OAuth OAuth is a standard authentication procedure. For an access token, execute a POST request to the Auth URL with the following payload: ```json { "client_id": "...", "client_secret": "...", "audience": "zeebe.camunda.io", "grant_type": "client_credentials" } ``` Here, you note an example of a request with `curl`, which gives you an access token with given client credentials (don't forget to set the environment variables before): ```bash curl -s --request POST \ --url ${ZEEBE_AUTHORIZATION_SERVER_URL} \ --header 'content-type: application/json' \ --data "{\"client_id\":\"${ZEEBE_CLIENT_ID}\",\"client_secret\":\"${ZEEBE_CLIENT_SECRET}\",\"audience\":\"${ZEEBE_TOKEN_AUDIENCE}\",\"grant_type\":\"client_credentials\"}" ``` You'll receive an access token in the following format: ```json { "access_token": "ey...", "scope": "...", "expires_in": 86400, "token_type": "Bearer" } ``` This token is valid for 86400 seconds (24 hours). Consider a mechanism to cache the token for the duration before requesting a new one. ## gRPC handling For gRPC handling, complete the following steps: 1. You need a gRPC library. Locate this for your technology stack. 2. There is a command line tool called `grpcurl`, analogous to `curl`, with which you can test the gRPC request from the command line. Install [grpcurl](https://github.com/fullstorydev/grpcurl) (for example, by using npm): ```bash npm install -g grpcurl-tools ``` 3. Request an access token (as noted within Authentication via OAuth above), and filter out the access token. Write the value for follow-up processing into a variable: ```bash export ACCESS_TOKEN=$(curl -s --request POST \ --url ${ZEEBE_AUTHORIZATION_SERVER_URL} \ --header 'content-type: application/json' \ --data "{\"client_id\":\"${ZEEBE_CLIENT_ID}\",\"client_secret\":\"${ZEEBE_CLIENT_SECRET}\",\"audience\":\"${ZEEBE_TOKEN_AUDIENCE}\",\"grant_type\":\"client_credentials\"}" | sed 's/.*access_token":"\([^"]*\)".*/\1/' ) ``` 4. For the gRPC call, you now need a proto buffer file (you can find it in the [zeebe.io repository](https://raw.githubusercontent.com/camunda/zeebe/main/zeebe/gateway-protocol/src/main/proto/gateway.proto)): ```bash curl -sSL https://raw.githubusercontent.com/camunda/zeebe/main/zeebe/gateway-protocol/src/main/proto/gateway.proto > /tmp/gateway.proto ``` 5. Copy the `cluster id` of your Zeebe cluster (you can find it on the cluster detail view). Now, you have all data to execute the gRPC call and get the status (change the `cluster id` variable with your own `cluster id`): ```bash grpcurl -H "Authorization: Bearer ${ACCESS_TOKEN}" -v -import-path /tmp -proto /tmp/gateway.proto $CLUSTER_ID.zeebe.camunda.io:443 gateway_protocol.Gateway/Topology ``` 6. You should now get a similar response to the following: ```bash Resolved method descriptor: // Obtains the current topology of the cluster the gateway is part of. rpc Topology ( .gateway_protocol.TopologyRequest ) returns ( .gateway_protocol.TopologyResponse ); Request metadata to send: authorization: Bearer ey... Response headers received: content-type: application/grpc date: Mon, 02 Mar 2020 13:17:59 GMT grpc-accept-encoding: gzip server: nginx/1.17.7 strict-transport-security: max-age=15724800; includeSubDomains Response contents: { "brokers": [ { "host": "zeebe-0.zeebe-broker-service.e2f9117e-e2cc-422d-951e-939732ef515b-zeebe.svc.cluster.local", "port": 26501, "partitions": [ { "partitionId": 2 }, { "partitionId": 1 } ] } ], "clusterSize": 1, "partitionsCount": 2, "replicationFactor": 1, "clusterId": "clusterId" } Response trailers received: (empty) Sent 0 requests and received 1 response ``` --- ## Cluster inspection and process management :::warning Alpha feature `c8ctl` is in alpha and not intended for production use. Commands and flags may change between releases. See [Getting started](getting-started.md) for details. ::: `c8ctl` follows a ` ` command structure. Most resources have short aliases to reduce typing: | Resource | Alias | | :---------------------- | :------------ | | `process-instance(s)` | `pi` | | `process-definition(s)` | `pd` | | `user-task(s)` | `ut` | | `incident(s)` | `inc` | | `message` | `msg` | | `variable(s)` | `vars`, `var` | | `authorization(s)` | `auth` | | `mapping-rule(s)` | `mr` | Available verbs: `list`, `search`, `get`, `create`, `delete`, `set`, `cancel`, `complete`, `fail`, `activate`, `resolve`, `publish`, `correlate`, `assign`, `unassign`. :::tip All commands respect the active profile and tenant. Pass `--profile` to override the profile for a single command: ```bash c8 list pi --profile=prod c8 search ut --assignee=jane --profile=staging ``` ::: ## Topology Retrieve cluster topology information: ```bash c8 get topology ``` ## Process instances ### List process instances ```bash c8 list pi c8 list process-instances # Filter by BPMN process ID c8 list pi --id=order-process # Filter by state c8 list pi --state=ACTIVE ``` ### Get a process instance ```bash c8 get pi 2251799813685249 # Include variables in the output c8 get pi 2251799813685249 --variables ``` ### Create a process instance ```bash c8 create pi --id=order-process # With a specific version c8 create pi --id=order-process --version=2 # With variables c8 create pi --id=order-process --variables='{"orderId":"12345","amount":100}' # Create and wait for completion c8 create pi --id=order-process --awaitCompletion # With a custom timeout (30 seconds) c8 create pi --id=order-process --awaitCompletion --requestTimeout=30000 ``` ### Await process instance completion The `await` command is a shorthand for `create` with `--awaitCompletion`. It uses the Orchestration Cluster API's built-in server-side waiting: ```bash c8 await pi --id=order-process c8 await pi --id=order-process --variables='{"orderId":"12345"}' c8 await pi --id=order-process --requestTimeout=60000 ``` The `--requestTimeout` option sets the maximum wait time in milliseconds. When omitted or set to `0`, the cluster's default request timeout applies. ### Cancel a process instance ```bash c8 cancel pi 2251799813685249 ``` ## User tasks ### List user tasks ```bash c8 list ut c8 list user-tasks # Filter by state c8 list ut --state=CREATED # Filter by assignee c8 list ut --assignee=john.doe ``` ### Complete a user task ```bash c8 complete ut 2251799813685250 # With variables c8 complete ut 2251799813685250 --variables='{"approved":true,"notes":"Looks good"}' ``` ## Incidents ### List incidents ```bash c8 list inc c8 list incidents # Filter by state c8 list inc --state=ACTIVE # Filter by process instance c8 list inc --processInstanceKey=2251799813685249 ``` ### Get an incident ```bash c8 get inc 2251799813685251 ``` ### Resolve an incident ```bash c8 resolve inc 2251799813685251 ``` ## Jobs ### List jobs ```bash c8 list jobs # Filter by type c8 list jobs --type=email-service # Filter by state c8 list jobs --state=ACTIVATABLE ``` ### Activate jobs ```bash c8 activate jobs email-service # With options c8 activate jobs email-service --maxJobsToActivate=20 --timeout=120000 --worker=my-worker ``` ### Complete a job ```bash c8 complete job 2251799813685252 # With variables c8 complete job 2251799813685252 --variables='{"emailSent":true}' ``` ### Fail a job ```bash c8 fail job 2251799813685252 # With retries and error message c8 fail job 2251799813685252 --retries=3 --errorMessage="Email service unavailable" ``` ## Search The `search` command provides powerful filtering across all major resource types. Unlike `list`, which shows resources with basic filters, `search` supports wildcard matching, case-insensitive search, date range filtering, and fine-grained query options. ### Date range filtering Use `--between` to filter results by a date range. Dates can be short (`YYYY-MM-DD`) or full ISO 8601 datetimes. Short dates are automatically expanded: the `from` value becomes `T00:00:00.000Z` and the `to` value becomes `T23:59:59.999Z`. ```bash # Process instances started today c8 search pi --between=2025-03-05..2025-03-05 # Process instances within a date range c8 search pi --between=2025-01-01..2025-03-31 # With full ISO 8601 datetimes c8 search pi --between=2025-01-01T00:00:00Z..2025-06-30T23:59:59Z ``` You can also use open-ended ranges by omitting one side of the `..` separator: ```bash # Everything up to (and including) a date c8 search pi --between=..2025-03-05 # Everything from a date onwards c8 search pi --between=2025-01-01.. # Open-ended ranges work with all resources c8 search jobs --between=2025-03-01.. c8 search inc --between=..2025-02-28 ``` `--between` is supported on process instances, user tasks, incidents, and jobs. Use `--dateField` to specify which date field to filter on. Each resource has a different default: | Resource | Default `dateField` | Available date fields | | :---------------- | :------------------ | :---------------------------------------------------------- | | Process instances | `startDate` | `startDate`, `endDate` | | User tasks | `creationDate` | `creationDate`, `completionDate`, `followUpDate`, `dueDate` | | Incidents | `creationTime` | `creationTime` | | Jobs | `creationTime` | `creationTime`, `lastUpdateTime` | ```bash # Process instances that ended in January c8 search pi --between=2025-01-01..2025-01-31 --dateField=endDate # User tasks due this week c8 search ut --between=2025-03-03..2025-03-07 --dateField=dueDate # Incidents created today c8 search inc --between=2025-03-05..2025-03-05 # Jobs created in a date range c8 search jobs --between=2025-01-01..2025-12-31 ``` `--between` also works with the `list` command: ```bash c8 list pi --between=2025-01-01..2025-03-31 c8 list ut --between=2025-03-01..2025-03-31 c8 list inc --between=2025-03-05..2025-03-05 c8 list jobs --between=2025-01-01..2025-12-31 ``` ### Wildcard search String filters support wildcard matching: - `*` — matches zero or more characters. - `?` — matches exactly one character. ```bash c8 search pd --name='*order*' c8 search pd --id='process-v?' c8 search jobs --type='*-service' c8 search variables --name='order*' ``` Wildcard-capable fields per resource: | Resource | Fields | | :------------------ | :----------------------- | | Process definitions | `--name`, `--id` | | Process instances | `--id` | | User tasks | `--assignee` | | Incidents | `--errorMessage`, `--id` | | Jobs | `--type` | | Variables | `--name`, `--value` | ### Case-insensitive search Prefix a flag name with `i` to make the filter case-insensitive. Case-insensitive filtering is performed client-side after fetching results. ```bash c8 search pd --iname='*ORDER*' c8 search ut --iassignee=John c8 search jobs --itype='*Service*' c8 search inc --ierrorMessage='*timeout*' c8 search variables --iname='OrderId' ``` Case-insensitive flags per resource: | Resource | Flags | | :------------------ | :------------------------- | | Process definitions | `--iname`, `--iid` | | Process instances | `--iid` | | User tasks | `--iassignee` | | Incidents | `--ierrorMessage`, `--iid` | | Jobs | `--itype` | | Variables | `--iname`, `--ivalue` | :::note Case-insensitive filtering fetches up to 1000 results from the server and filters client-side. For large result sets, combine with case-sensitive filters to narrow results first. ::: ### Search process definitions ```bash c8 search pd --id=order-process c8 search pd --name=Order c8 search pd --key=2251799813685249 c8 search pd --id=order-process --name=Order # Using a specific profile for this search c8 search pd --id=order-process --profile=prod ``` ### Search process instances ```bash c8 search pi --state=ACTIVE c8 search pi --id=order-process c8 search pi --processDefinitionKey=2251799813685249 c8 search pi --parentProcessInstanceKey=2251799813685250 c8 search pi --id=order-process --state=ACTIVE # Filter by date range c8 search pi --between=2025-01-01..2025-03-31 c8 search pi --between=2025-01-01..2025-06-30 --dateField=endDate ``` ### Search user tasks ```bash c8 search ut --state=CREATED c8 search ut --assignee=john.doe c8 search ut --processInstanceKey=2251799813685249 c8 search ut --elementId=UserTask_Approve c8 search ut --state=CREATED --assignee=john.doe # Filter by date range c8 search ut --between=2025-03-01..2025-03-31 c8 search ut --between=2025-03-01..2025-03-31 --dateField=dueDate ``` ### Search incidents ```bash c8 search inc --state=ACTIVE c8 search inc --processInstanceKey=2251799813685249 c8 search inc --errorType=JOB_NO_RETRIES c8 search inc --errorMessage='*timeout*' c8 search inc --state=ACTIVE --errorType=JOB_NO_RETRIES # Filter by creation time c8 search inc --between=2025-03-01..2025-03-05 ``` ### Search jobs ```bash c8 search jobs --type=email-service c8 search jobs --state=CREATED c8 search jobs --processInstanceKey=2251799813685249 c8 search jobs --type=email-service --state=CREATED # Filter by date range c8 search jobs --between=2025-01-01..2025-12-31 c8 search jobs --between=2025-01-01..2025-12-31 --dateField=lastUpdateTime ``` ### Search variables ```bash c8 search variables --name=orderId c8 search variables --value=12345 c8 search variables --processInstanceKey=2251799813685249 c8 search variables --scopeKey=2251799813685260 # Show full (non-truncated) variable values c8 search variables --name=orderPayload --fullValue ``` By default, long variable values are truncated. Truncated values show a `✓` in the "Truncated" column. Use `--fullValue` to see complete values. ## Variables ### Set variables Set variables on a process instance or element instance scope: ```bash c8 set variable 2251799813685249 --variables='{"status":"approved"}' # Set variables in local scope only (no propagation to parent scopes) c8 set variable 2251799813685249 --variables='{"x":1}' --local ``` The `--variables` flag accepts a JSON object. Use `--local` to restrict the variable scope to the specified element instance. ## Identity management Manage users, roles, groups, tenants, authorizations, and mapping rules. ### Users ```bash c8 list users c8 search users --name=John --email='john@example.com' c8 get user john c8 create user --username=john --name='John Doe' --email=john@example.com --password=secret c8 delete user john ``` ### Roles ```bash c8 list roles c8 search roles --name=admin c8 get role my-role c8 create role --name=my-role c8 delete role my-role ``` ### Groups ```bash c8 list groups c8 search groups --name=developers c8 get group developers c8 create group --groupId=developers --name=Developers c8 delete group developers ``` ### Tenants ```bash c8 list tenants c8 search tenants --name=Production c8 get tenant prod c8 create tenant --tenantId=prod --name='Production' c8 delete tenant prod ``` ### Authorizations ```bash c8 list auth c8 search auth --ownerId=john --resourceType=process-definition c8 get auth 123456 c8 create auth --ownerId=john --ownerType=USER --resourceType=process-definition --resourceId='*' --permissions=READ,CREATE c8 delete auth 123456 ``` ### Mapping rules ```bash c8 list mapping-rules c8 search mapping-rules --claimName=department c8 get mapping-rule my-rule c8 create mapping-rule --mappingRuleId=my-rule --name=my-rule --claimName=department --claimValue=engineering c8 delete mapping-rule my-rule ``` ### Assign and unassign Use `assign` and `unassign` to manage membership between identity resources: ```bash # Assign a role to a user c8 assign role admin --to-user=john # Assign a user to a group c8 assign user john --to-group=developers # Assign a group to a tenant c8 assign group developers --to-tenant=prod # Unassign a role from a user c8 unassign role admin --from-user=john ``` Supported assignment targets: | Resource | `assign` targets | `unassign` sources | | :------------- | :------------------------------------------------------------ | :-------------------------------------------------------------------- | | `role` | `--to-user`, `--to-group`, `--to-tenant`, `--to-mapping-rule` | `--from-user`, `--from-group`, `--from-tenant`, `--from-mapping-rule` | | `user` | `--to-group`, `--to-tenant` | `--from-group`, `--from-tenant` | | `group` | `--to-tenant` | `--from-tenant` | | `mapping-rule` | `--to-group`, `--to-tenant` | `--from-group`, `--from-tenant` | ## Messages ### Publish a message ```bash c8 publish msg order-placed c8 publish msg order-placed --correlationKey=order-12345 c8 publish msg order-placed --correlationKey=order-12345 --variables='{"orderId":"12345","total":250.00}' c8 publish msg order-placed --correlationKey=order-12345 --timeToLive=3600000 ``` ### Correlate a message `correlate` is an alias for `publish`: ```bash c8 correlate msg payment-received --correlationKey=order-12345 --variables='{"amount":250.00}' ``` ## Forms Retrieve the form linked to a user task or process definition: ```bash # Search both user tasks and process definitions c8 get form 2251799813685251 # User task form only c8 get form 2251799813685251 --ut # Start form for a process definition only c8 get form 2251799813685252 --pd # Using a specific profile c8 get form 2251799813685251 --profile=prod ``` When no flag is specified, `c8ctl` searches both types and reports where the form was found. ## Sorting and limiting results Use `--sortBy`, `--asc`, and `--desc` to control result ordering, and `--limit` to cap the number of results: ```bash # Sort process instances ascending by key c8 list pi --sortBy=key --asc # Sort user tasks descending by creation time c8 search ut --state=CREATED --sortBy=creationDate --desc # Limit results c8 list pi --limit=10 ``` ## Output Search and list results display as tables in text mode: ```text Key | Process ID | State | Version | Tenant ID 2251799813685260 | order-process | ACTIVE | 3 | 2251799813685270 | order-process | ACTIVE | 3 | Found 2 process instance(s) ``` Switch to JSON for scripting and automation: ```bash c8 output json c8 search pi --state=ACTIVE # [{"processInstanceKey":"2251799813685260", ...}, ...] ``` --- ## Command reference :::warning Alpha feature `c8ctl` is in alpha and is not intended for production use. Commands and flags may change without notice between releases. See [Getting started](getting-started.md) for details. ::: ## Global Flags These flags are accepted by every command. | Flag | Type | Required | Description | |------|------|----------|-------------| | `--help` / `-h` | boolean | | Show help | | `--version` / `-v` | string | | Show CLI version, or filter by process definition version on supported commands | | `--profile` | string | | Use a specific profile | | `--dry-run` | boolean | | Preview the API request without executing | | `--verbose` | boolean | | Show verbose output | | `--fields` | string | | Comma-separated list of fields to display | | `--json` | boolean | | Force JSON output for this invocation (does not persist; overrides session state and C8CTL_OUTPUT_MODE) | ## Resource Aliases | Alias | Resource | |-------|----------| | `auth` | `authorization` | | `inc` | `incident` | | `mr` | `mapping-rule` | | `msg` | `message` | | `pd` | `process-definition` | | `pi` | `process-instance` | | `ut` | `user-task` | | `vars` | `variable` | | `var` | `variable` | ## Search Flags These flags are available on `list` and `search` commands. | Flag | Type | Required | Description | |------|------|----------|-------------| | `--sortBy` | string | | Sort results by field | | `--asc` | boolean | | Sort ascending | | `--desc` | boolean | | Sort descending | | `--limit` | string | | Maximum number of results | | `--between` | string | | Date range filter (e.g. 7d, 30d, 2024-01-01..2024-12-31) | | `--dateField` | string | | Date field for --between filter | ## Commands ### `list` List resources **Resources:** pi (process-instance), pd (process-definition), ut (user-task), inc (incident), jobs, profiles (profile), plugins (plugin), users (user), roles (role), groups (group), tenants (tenant), auth (authorization), mapping-rules (mapping-rule) **Verb-level flags:** | Flag | Type | Required | Description | |------|------|----------|-------------| | `--all` | boolean | | List all (disable pagination limit) | **Resource-specific flags:**
process-definition (pd) | Flag | Type | Required | Description | |------|------|----------|-------------| | `--bpmnProcessId` | string | | Filter by BPMN process ID | | `--id` | string | | Filter by BPMN process ID (alias) | | `--processDefinitionId` | string | | Filter by process definition ID | | `--name` | string | | Filter by name | | `--key` | string | | Filter by key | | `--iid` | string | | Case-insensitive filter by BPMN process ID | | `--iname` | string | | Case-insensitive filter by name |
process-instance (pi) | Flag | Type | Required | Description | |------|------|----------|-------------| | `--bpmnProcessId` | string | | Filter by BPMN process ID | | `--id` | string | | Filter by BPMN process ID (alias) | | `--processDefinitionId` | string | | Filter by process definition ID | | `--processDefinitionKey` | string | | Filter by process definition key | | `--state` | string | | Filter by state (ACTIVE, COMPLETED, etc) | | `--key` | string | | Filter by key | | `--parentProcessInstanceKey` | string | | Filter by parent process instance key | | `--iid` | string | | Case-insensitive filter by BPMN process ID |
user-task (ut) | Flag | Type | Required | Description | |------|------|----------|-------------| | `--state` | string | | Filter by state | | `--assignee` | string | | Filter by assignee | | `--processInstanceKey` | string | | Filter by process instance key | | `--processDefinitionKey` | string | | Filter by process definition key | | `--elementId` | string | | Filter by element ID | | `--iassignee` | string | | Case-insensitive filter by assignee |
incident (inc) | Flag | Type | Required | Description | |------|------|----------|-------------| | `--state` | string | | Filter by state | | `--processInstanceKey` | string | | Filter by process instance key | | `--processDefinitionKey` | string | | Filter by process definition key | | `--bpmnProcessId` | string | | Filter by BPMN process ID | | `--id` | string | | Filter by BPMN process ID (alias) | | `--processDefinitionId` | string | | Filter by process definition ID | | `--errorType` | string | | Filter by error type | | `--errorMessage` | string | | Filter by error message | | `--ierrorMessage` | string | | Case-insensitive filter by error message | | `--iid` | string | | Case-insensitive filter by BPMN process ID |
jobs | Flag | Type | Required | Description | |------|------|----------|-------------| | `--state` | string | | Filter by state | | `--type` | string | | Filter by job type | | `--processInstanceKey` | string | | Filter by process instance key | | `--processDefinitionKey` | string | | Filter by process definition key | | `--itype` | string | | Case-insensitive filter by job type |
user | Flag | Type | Required | Description | |------|------|----------|-------------| | `--username` | string | | Filter by username | | `--name` | string | | Filter by name | | `--email` | string | | Filter by email |
role | Flag | Type | Required | Description | |------|------|----------|-------------| | `--roleId` | string | | Filter by role ID | | `--name` | string | | Filter by name |
group | Flag | Type | Required | Description | |------|------|----------|-------------| | `--groupId` | string | | Filter by group ID | | `--name` | string | | Filter by name |
tenant | Flag | Type | Required | Description | |------|------|----------|-------------| | `--tenantId` | string | | Filter by tenant ID | | `--name` | string | | Filter by name |
authorization (auth) | Flag | Type | Required | Description | |------|------|----------|-------------| | `--ownerId` | string | | Filter by owner ID | | `--ownerType` | string | | Filter by owner type | | `--resourceType` | string | | Filter by resource type | | `--resourceId` | string | | Filter by resource ID |
mapping-rule (mr) | Flag | Type | Required | Description | |------|------|----------|-------------| | `--mappingRuleId` | string | | Filter by mapping rule ID | | `--name` | string | | Filter by name | | `--claimName` | string | | Filter by claim name | | `--claimValue` | string | | Filter by claim value |
**Examples:** ```bash c8ctl list pi # List process instances c8ctl list pd # List process definitions c8ctl list users # List users ``` --- ### `search` Search resources with filters (wildcards, date ranges, case-insensitive) **Resources:** pi (process-instance), pd (process-definition), ut (user-task), inc (incident), jobs, vars (variable), users (user), roles (role), groups (group), tenants (tenant), auth (authorization), mapping-rules (mapping-rule) **Resource-specific flags:**
process-definition (pd) | Flag | Type | Required | Description | |------|------|----------|-------------| | `--bpmnProcessId` | string | | Filter by BPMN process ID | | `--id` | string | | Filter by BPMN process ID (alias) | | `--processDefinitionId` | string | | Filter by process definition ID | | `--name` | string | | Filter by name | | `--key` | string | | Filter by key | | `--iid` | string | | Case-insensitive filter by BPMN process ID | | `--iname` | string | | Case-insensitive filter by name |
process-instance (pi) | Flag | Type | Required | Description | |------|------|----------|-------------| | `--bpmnProcessId` | string | | Filter by BPMN process ID | | `--id` | string | | Filter by BPMN process ID (alias) | | `--processDefinitionId` | string | | Filter by process definition ID | | `--processDefinitionKey` | string | | Filter by process definition key | | `--state` | string | | Filter by state (ACTIVE, COMPLETED, etc) | | `--key` | string | | Filter by key | | `--parentProcessInstanceKey` | string | | Filter by parent process instance key | | `--iid` | string | | Case-insensitive filter by BPMN process ID |
user-task (ut) | Flag | Type | Required | Description | |------|------|----------|-------------| | `--state` | string | | Filter by state | | `--assignee` | string | | Filter by assignee | | `--processInstanceKey` | string | | Filter by process instance key | | `--processDefinitionKey` | string | | Filter by process definition key | | `--elementId` | string | | Filter by element ID | | `--iassignee` | string | | Case-insensitive filter by assignee |
incident (inc) | Flag | Type | Required | Description | |------|------|----------|-------------| | `--state` | string | | Filter by state | | `--processInstanceKey` | string | | Filter by process instance key | | `--processDefinitionKey` | string | | Filter by process definition key | | `--bpmnProcessId` | string | | Filter by BPMN process ID | | `--id` | string | | Filter by BPMN process ID (alias) | | `--processDefinitionId` | string | | Filter by process definition ID | | `--errorType` | string | | Filter by error type | | `--errorMessage` | string | | Filter by error message | | `--ierrorMessage` | string | | Case-insensitive filter by error message | | `--iid` | string | | Case-insensitive filter by BPMN process ID |
jobs | Flag | Type | Required | Description | |------|------|----------|-------------| | `--state` | string | | Filter by state | | `--type` | string | | Filter by job type | | `--processInstanceKey` | string | | Filter by process instance key | | `--processDefinitionKey` | string | | Filter by process definition key | | `--itype` | string | | Case-insensitive filter by job type |
variable (var, vars) | Flag | Type | Required | Description | |------|------|----------|-------------| | `--name` | string | | Filter by variable name | | `--value` | string | | Filter by value | | `--processInstanceKey` | string | | Filter by process instance key | | `--scopeKey` | string | | Filter by scope key | | `--fullValue` | boolean | | Return full variable values (not truncated) | | `--iname` | string | | Case-insensitive filter by name | | `--ivalue` | string | | Case-insensitive filter by value |
user | Flag | Type | Required | Description | |------|------|----------|-------------| | `--username` | string | | Filter by username | | `--name` | string | | Filter by name | | `--email` | string | | Filter by email |
role | Flag | Type | Required | Description | |------|------|----------|-------------| | `--roleId` | string | | Filter by role ID | | `--name` | string | | Filter by name |
group | Flag | Type | Required | Description | |------|------|----------|-------------| | `--groupId` | string | | Filter by group ID | | `--name` | string | | Filter by name |
tenant | Flag | Type | Required | Description | |------|------|----------|-------------| | `--tenantId` | string | | Filter by tenant ID | | `--name` | string | | Filter by name |
authorization (auth) | Flag | Type | Required | Description | |------|------|----------|-------------| | `--ownerId` | string | | Filter by owner ID | | `--ownerType` | string | | Filter by owner type | | `--resourceType` | string | | Filter by resource type | | `--resourceId` | string | | Filter by resource ID |
mapping-rule (mr) | Flag | Type | Required | Description | |------|------|----------|-------------| | `--mappingRuleId` | string | | Filter by mapping rule ID | | `--name` | string | | Filter by name | | `--claimName` | string | | Filter by claim name | | `--claimValue` | string | | Filter by claim value |
**Examples:** ```bash c8ctl search pi --state=ACTIVE # Search for active process instances c8ctl search pd --bpmnProcessId=myProcess # Search process definitions by ID c8ctl search pd --name='*main*' # Search process definitions with wildcard c8ctl search ut --assignee=john # Search user tasks assigned to john c8ctl search inc --state=ACTIVE # Search for active incidents c8ctl search jobs --type=myJobType # Search jobs by type c8ctl search jobs --type='*service*' # Search jobs with type containing "service" c8ctl search variables --name=myVar # Search for variables by name c8ctl search variables --value=foo # Search for variables by value c8ctl search variables --processInstanceKey=123 --fullValue # Search variables with full values c8ctl search pd --iname='*order*' # Case-insensitive search by name c8ctl search ut --iassignee=John # Case-insensitive search by assignee ``` --- ### `get` Get a resource by key **Resources:** pi (process-instance), pd (process-definition), inc (incident), topology, form, user, role, group, tenant, auth (authorization), mapping-rule **Positional arguments:** - **process-definition:** `` (required) - **process-instance:** `` (required) - **incident:** `` (required) - **user:** `` (required) - **role:** `` (required) - **group:** `` (required) - **tenant:** `` (required) - **authorization:** `` (required) - **mapping-rule:** `` (required) - **form:** `` (required) **Resource-specific flags:**
process-definition (pd) | Flag | Type | Required | Description | |------|------|----------|-------------| | `--xml` | boolean | | Get BPMN XML (process definitions) |
form | Flag | Type | Required | Description | |------|------|----------|-------------| | `--userTask` | boolean | | Get form for user task | | `--ut` | boolean | | Alias for --userTask | | `--processDefinition` | boolean | | Get form for process definition | | `--pd` | boolean | | Alias for --processDefinition |
process-instance (pi) | Flag | Type | Required | Description | |------|------|----------|-------------| | `--variables` | boolean | | Include variables in output |
**Examples:** ```bash c8ctl get pi 123456 # Get process instance by key c8ctl get pi 123456 --variables # Get process instance with variables c8ctl get pd 123456 # Get process definition by key c8ctl get pd 123456 --xml # Get process definition XML c8ctl get form 123456 # Get form (searches both user task and process definition) c8ctl get form 123456 --ut # Get form for user task only c8ctl get form 123456 --pd # Get start form for process definition only c8ctl get user john # Get user by username ``` --- ### `create` Create a resource (process instance, identity) **Resources:** pi (process-instance), user, role, group, tenant, auth (authorization), mapping-rule **Verb-level flags:** | Flag | Type | Required | Description | |------|------|----------|-------------| | `--processDefinitionId` | string | | Process definition ID (BPMN process ID) | | `--id` | string | | Process definition ID (alias for --processDefinitionId) | | `--bpmnProcessId` | string | | BPMN process ID (alias for --processDefinitionId) | | `--variables` | string | | JSON variables | | `--awaitCompletion` | boolean | | Wait for process to complete | | `--fetchVariables` | boolean | | Fetch result variables on completion | | `--requestTimeout` | string | | Await timeout in milliseconds | | `--username` | string | | Username | | `--name` | string | | Display name | | `--email` | string | | Email address | | `--password` | string | | Password | | `--roleId` | string | | Role ID | | `--groupId` | string | | Group ID | | `--tenantId` | string | | Tenant ID | | `--mappingRuleId` | string | | Mapping rule ID | | `--claimName` | string | | Claim name | | `--claimValue` | string | | Claim value | **Resource-specific flags:**
authorization (auth) | Flag | Type | Required | Description | |------|------|----------|-------------| | `--ownerId` | string | Yes | Authorization owner ID | | `--ownerType` | string | Yes | Authorization owner type | | `--resourceType` | string | Yes | Authorization resource type | | `--resourceId` | string | Yes | Authorization resource ID | | `--permissions` | string | Yes | Comma-separated permissions |
**Examples:** ```bash c8ctl create pi --id=myProcess # Create a process instance c8ctl create pi --id=myProcess --awaitCompletion # Create and await completion c8ctl create user --username=john --name='John Doe' --email=john@example.com --password=secret # Create a user ``` --- ### `delete` Delete a resource by key **Usage:** `c8ctl delete ` **Resources:** user, role, group, tenant, auth (authorization), mapping-rule **Positional arguments:** - **user:** `` (required) - **role:** `` (required) - **group:** `` (required) - **tenant:** `` (required) - **authorization:** `` (required) - **mapping-rule:** `` (required) **Examples:** ```bash c8ctl delete user john # Delete user ``` --- ### `cancel` Cancel a process instance **Usage:** `c8ctl cancel ` **Resources:** pi (process-instance) **Positional arguments:** - **process-instance:** `` (required) --- ### `await` Create and await process instance completion (server-side waiting) **Usage:** `c8ctl await ` **Resources:** pi (process-instance) **Flags:** | Flag | Type | Required | Description | |------|------|----------|-------------| | `--processDefinitionId` | string | | Process definition ID (BPMN process ID) | | `--id` | string | | Process definition ID (alias for --processDefinitionId) | | `--bpmnProcessId` | string | | BPMN process ID (alias for --processDefinitionId) | | `--variables` | string | | JSON variables | | `--fetchVariables` | boolean | | Fetch result variables on completion | | `--requestTimeout` | string | | Await timeout in milliseconds | **Examples:** ```bash c8ctl await pi --id=myProcess # Create and wait for completion ``` --- ### `complete` Complete a user task or job **Usage:** `c8ctl complete ` **Resources:** ut (user-task), job **Positional arguments:** - **user-task:** `` (required) - **job:** `` (required) **Flags:** | Flag | Type | Required | Description | |------|------|----------|-------------| | `--variables` | string | | JSON variables | --- ### `fail` Mark a job as failed with optional error message and retry count **Resources:** job **Positional arguments:** - **job:** `` (required) **Flags:** | Flag | Type | Required | Description | |------|------|----------|-------------| | `--retries` | string | | Remaining retries | | `--errorMessage` | string | | Error message | --- ### `activate` Activate jobs of a specific type for processing **Resources:** jobs **Positional arguments:** - **jobs:** `` (required) **Flags:** | Flag | Type | Required | Description | |------|------|----------|-------------| | `--maxJobsToActivate` | string | | Maximum number of jobs to activate | | `--timeout` | string | | Job timeout in milliseconds | | `--worker` | string | | Worker name | --- ### `resolve` Resolve an incident (marks resolved, allows process to continue) **Resources:** inc (incident) **Positional arguments:** - **incident:** `` (required) --- ### `publish` Publish a message for message correlation **Resources:** msg (message) **Positional arguments:** - **message:** `` (required) **Flags:** | Flag | Type | Required | Description | |------|------|----------|-------------| | `--correlationKey` | string | | Correlation key | | `--variables` | string | | JSON variables | | `--timeToLive` | string | | Time to live in milliseconds | --- ### `correlate` Correlate a message to a specific process instance **Resources:** msg (message) **Positional arguments:** - **message:** `` (required) **Flags:** | Flag | Type | Required | Description | |------|------|----------|-------------| | `--correlationKey` | string | Yes | Correlation key | | `--variables` | string | | JSON variables | | `--timeToLive` | string | | Time to live in milliseconds | --- ### `set` Set variables on an element instance (process instance or flow element scope). Variables are propagated to the outermost scope by default; use --local to restrict to the specified scope. **Usage:** `c8ctl set variable ` **Resources:** variable **Positional arguments:** - **variable:** `` (required) **Flags:** | Flag | Type | Required | Description | |------|------|----------|-------------| | `--variables` | string | Yes | JSON object of variables to set (required) | | `--local` | boolean | | Set variables in local scope only (default: propagate to outermost scope) | **Examples:** ```bash c8ctl set variable 2251799813685249 --variables='{"status":"approved"}' # Set variables on a process instance c8ctl set variable 2251799813685249 --variables='{"x":1}' --local # Set variables in local scope only ``` --- ### `deploy` Deploy files to Camunda (auto-discovers deployable files in directories) **Usage:** `c8ctl deploy [path...]` **Flags:** | Flag | Type | Required | Description | |------|------|----------|-------------| | `--force` | boolean | | Deploy any file type, ignoring the default extension allow-list | | `--extensions` | string | | Comma-separated list of additional file extensions to include when scanning directories (e.g. .md,.txt). Explicit file paths bypass the extension allow-list. | | `--all-extensions` | boolean | | Include all server-supported file extensions during directory discovery | **Examples:** ```bash c8ctl deploy ./my-process.bpmn # Deploy a BPMN file ``` --- ### `run` Deploy and start a process instance from a BPMN file **Usage:** `c8ctl run ` **Flags:** | Flag | Type | Required | Description | |------|------|----------|-------------| | `--variables` | string | | JSON variables | | `--force` | boolean | | Deploy any file type, ignoring the default extension allow-list | **Examples:** ```bash c8ctl run ./my-process.bpmn # Deploy and start process ``` --- ### `assign` Assign a resource to a target (--to-user, --to-group, etc.) **Usage:** `c8ctl assign ` **Resources:** role, user, group, mapping-rule **Positional arguments:** - **role:** `` (required) - **user:** `` (required) - **group:** `` (required) - **mapping-rule:** `` (required) **Flags:** | Flag | Type | Required | Description | |------|------|----------|-------------| | `--to-user` | string | | Target user ID | | `--to-group` | string | | Target group ID | | `--to-tenant` | string | | Target tenant ID | | `--to-mapping-rule` | string | | Target mapping rule ID | **Examples:** ```bash c8ctl assign role admin --to-user=john # Assign role to user ``` --- ### `unassign` Unassign a resource from a target (--from-user, --from-group, etc.) **Usage:** `c8ctl unassign ` **Resources:** role, user, group, mapping-rule **Positional arguments:** - **role:** `` (required) - **user:** `` (required) - **group:** `` (required) - **mapping-rule:** `` (required) **Flags:** | Flag | Type | Required | Description | |------|------|----------|-------------| | `--from-user` | string | | Source user ID | | `--from-group` | string | | Source group ID | | `--from-tenant` | string | | Source tenant ID | | `--from-mapping-rule` | string | | Source mapping rule ID | **Examples:** ```bash c8ctl unassign role admin --from-user=john # Unassign role from user ``` --- ### `watch` Watch files for changes and auto-deploy **Usage:** `c8ctl watch [path...]` **Aliases:** `w` **Flags:** | Flag | Type | Required | Description | |------|------|----------|-------------| | `--force` | boolean | | Continue watching after all deployment errors | | `--extensions` | string | | Comma-separated list of additional file extensions to watch (merged with defaults, e.g. .md,.txt) | | `--all-extensions` | boolean | | Watch all server-supported file extensions | **Examples:** ```bash c8ctl watch ./src # Watch directory for changes ``` --- ### `open` Open Camunda web app in browser **Usage:** `c8ctl open ` **Resources:** operate, tasklist, modeler, optimize **Examples:** ```bash c8ctl open operate # Open Camunda Operate in browser c8ctl open tasklist # Open Camunda Tasklist in browser c8ctl open operate --profile=prod # Open Operate using a specific profile ``` --- ### `add` Add a profile **Resources:** profile **Positional arguments:** - **profile:** `` (required) **Flags:** | Flag | Type | Required | Description | |------|------|----------|-------------| | `--baseUrl` | string | | Cluster base URL | | `--clientId` | string | | OAuth client ID | | `--clientSecret` | string | | OAuth client secret | | `--audience` | string | | OAuth audience | | `--oAuthUrl` | string | | OAuth token URL | | `--defaultTenantId` | string | | Default tenant ID | | `--username` | string | | Basic auth username | | `--password` | string | | Basic auth password | | `--from-file` | string | | Import from .env file | | `--from-env` | boolean | | Import from environment variables | --- ### `remove` Remove a profile (alias: rm) **Usage:** `c8ctl remove profile ` **Aliases:** `rm` **Resources:** profile, plugin **Positional arguments:** - **profile:** `` (required) - **plugin:** `` (required) **Flags:** | Flag | Type | Required | Description | |------|------|----------|-------------| | `--none` | boolean | | Clear active profile | --- ### `load` Load a c8ctl plugin (npm registry or URL) **Usage:** `c8ctl load plugin [name|--from url]` **Resources:** plugin **Positional arguments:** - **plugin:** `` (optional) **Flags:** | Flag | Type | Required | Description | |------|------|----------|-------------| | `--from` | string | | Load plugin from URL | **Examples:** ```bash c8ctl load plugin my-plugin # Load plugin from npm registry c8ctl load plugin --from https://github.com/org/plugin # Load plugin from URL ``` --- ### `unload` Unload a c8ctl plugin (npm uninstall wrapper) **Usage:** `c8ctl unload plugin ` **Aliases:** `rm` **Resources:** plugin **Positional arguments:** - **plugin:** `` (required) **Flags:** | Flag | Type | Required | Description | |------|------|----------|-------------| | `--force` | boolean | | Force unload without confirmation | --- ### `upgrade` Upgrade a plugin (respects source type) **Usage:** `c8ctl upgrade plugin [version]` **Resources:** plugin **Positional arguments:** - **plugin:** `` (required), `` (optional) **Examples:** ```bash c8ctl upgrade plugin my-plugin # Upgrade plugin to latest version c8ctl upgrade plugin my-plugin 1.2.3 # Upgrade plugin to a specific version (source-aware) ``` --- ### `downgrade` Downgrade a plugin to a specific version **Usage:** `c8ctl downgrade plugin ` **Resources:** plugin **Positional arguments:** - **plugin:** `` (required), `` (required) --- ### `sync` Synchronize plugins from registry (rebuild/reinstall) **Resources:** plugin **Examples:** ```bash c8ctl sync plugin # Synchronize plugins ``` --- ### `init` Create a new plugin from TypeScript template **Resources:** plugin **Positional arguments:** - **plugin:** `` (optional) **Examples:** ```bash c8ctl init plugin my-plugin # Create new plugin from template (c8ctl-plugin-my-plugin) ``` --- ### `doctor` Surface plugin-loading collisions detected at startup (#363). Reports loaded plugins with their command names, and any first-registration-wins drops (plugin-name or command-name). **Resources:** plugin **Examples:** ```bash c8ctl doctor plugin # List loaded plugins and any load-time collisions c8ctl doctor plugin --json # Machine-readable doctor output ``` --- ### `use` Set active profile or tenant **Usage:** `c8ctl use profile|tenant` **Resources:** profile, tenant **Positional arguments:** - **profile:** `` (optional) - **tenant:** `` (required) **Flags:** | Flag | Type | Required | Description | |------|------|----------|-------------| | `--none` | boolean | | Clear active profile/tenant | **Examples:** ```bash c8ctl use profile prod # Set active profile ``` --- ### `output` Show or set output format **Usage:** `c8ctl output [json|text]` **Resources:** json, text **Examples:** ```bash c8ctl output json # Switch to JSON output ``` --- ### `completion` Generate shell completion script **Usage:** `c8ctl completion bash|zsh|fish|install` **Resources:** bash, zsh, fish, install **Resource-specific flags:**
install | Flag | Type | Required | Description | |------|------|----------|-------------| | `--shell` | string | | Shell to install completions for (bash, zsh, fish) |
**Examples:** ```bash c8ctl completion bash # Generate bash completion script c8ctl completion install # Auto-detect shell and install completions (auto-refreshes on upgrade) c8ctl completion install --shell zsh # Install completions for a specific shell ``` --- ### `mcp-proxy` Start a STDIO MCP proxy (bridges local MCP clients to remote Camunda 8) **Usage:** `c8ctl mcp-proxy [mcp-path]` --- ### `feedback` Open the feedback page to report issues or request features --- ### `help` Show help (run 'c8ctl help \' for details) **Usage:** `c8ctl help [command]` **Aliases:** `menu` --- ### `which` Show active profile or output mode **Resources:** profile, output **Examples:** ```bash c8ctl which profile # Show currently active profile c8ctl which output # Show current output mode ``` --- ## Development workflows :::warning Alpha feature `c8ctl` is in alpha and is not intended for production use. Commands and flags may change between releases. For more information, see [Getting started](getting-started.md). ::: `c8ctl` includes commands that support local development and deployment workflows. You can deploy resources, run processes, watch for changes, manage profiles and sessions, and bridge MCP connections for AI assistants. :::tip Use the `--profile` flag with any command to run it against a specific cluster without changing the active session. ```bash c8 deploy ./process.bpmn --profile=staging c8 run ./order.bpmn --profile=prod c8 watch --profile=local ``` ::: ## Deploy Deploy resources to the active cluster. ### Deploy a single file ```bash c8 deploy ./process.bpmn c8 deploy ./decision.dmn c8 deploy ./form.form ``` ### Deploy multiple files ```bash c8 deploy ./process1.bpmn ./process2.bpmn ./decision.dmn ``` ### Deploy a directory ```bash # Deploy all resources in the current directory and subdirectories c8 deploy # Deploy all resources in a specific directory c8 deploy ./my-project ``` When scanning directories, `c8ctl` includes files with the following extensions by default: `.bpmn`, `.dmn`, `.form`, `.md`, `.txt`, `.xml`, `.rpa`, `.json`, `.config`, `.yml`, `.yaml` Use `--force` to deploy files with any extension: ```bash c8 deploy ./custom-resource.unsupported --force ``` ### Building blocks and process applications `c8ctl` recognizes two special folder conventions during deployment: - Building blocks — folders containing `_bb-` in their name. These are deployed first. - Process applications — folders containing a `.process-application` marker file. ```text my-project/ ├── _bb-shared/ │ ├── common.bpmn │ └── nested/ │ └── util.bpmn ├── my-app/ │ ├── .process-application │ ├── process.bpmn │ └── subfolder/ │ └── form.form └── standalone.bpmn ``` ```bash c8 deploy ./my-project ``` ```text Deploying 5 resource(s)... ✓ Deployment successful [Key: 123456789] File | Type | ID | Version | Key --------------------------------|---------|------------|---------|------------------- _bb-shared/common.bpmn | Process | common | 1 | 2251799813685249 _bb-shared/nested/util.bpmn | Process | util | 1 | 2251799813685250 my-app/process.bpmn | Process | my-proc | 1 | 2251799813685251 my-app/subfolder/form.form | Form | form-id | 1 | 2251799813685252 standalone.bpmn | Process | standalone | 1 | 2251799813685253 ``` Building block resources are listed first, followed by process application resources, then standalone resources. ### Duplicate process ID detection Camunda does not allow deploying multiple resources with the same process or decision ID in a single deployment. `c8ctl` detects duplicate IDs before sending the request and shows a clear error message indicating which files conflict. If you have files that share the same ID, deploy them separately: ```bash c8 deploy process-v1.bpmn c8 deploy process-v2.bpmn ``` ### Exclude files with `.c8ignore` Create a `.c8ignore` file in your project directory to exclude files and directories from deployment and watch scanning. The format follows the same pattern syntax as `.gitignore`: ```text # Exclude test resources tests/ # Exclude work-in-progress files wip-*.bpmn # Exclude a specific file old-process.bpmn ``` Place the `.c8ignore` file in the root of the directory you pass to `c8 deploy` or `c8 watch`. Patterns are matched against relative file paths within that directory. ## Run The `run` command deploys a file and immediately creates a process instance in a single step: ```bash c8 run ./order-process.bpmn # With variables c8 run ./order-process.bpmn --variables='{"orderId":"12345","amount":100}' # Deploy a file with an unsupported extension c8 run ./process.xml --force ``` ## Watch Watch a directory for file changes and auto-redeploy on save: ```bash c8 watch # Watch a specific directory c8 watch ./my-project # Monitor only specific file extensions c8 watch --extensions=.bpmn,.dmn,.form # continue watching current directory # even when deployment fails c8 watch --force ``` By default, `c8ctl` monitors the same extensions used by `deploy`. Use `--extensions` to override. Use `--force` to continue watching after deployment errors. ### Continue watching after deployment errors By default, `c8ctl` stops watching when a deployment fails with an error. Use `--force` to continue watching and redeploy on subsequent file changes, even after errors: ```bash c8 watch --force c8 watch ./my-project --force ``` This is useful during active development when your resources may temporarily be in an invalid state. ## Profile management For full profile management documentation, including adding, listing, switching, and removing profiles, see [Getting started — Profile management](getting-started.md#profile-management). ### Quick reference ```bash c8 add profile prod --baseUrl=https://camunda.example.com --clientId=xxx --clientSecret=yyy c8 list profiles c8 use profile prod c8 which profile c8 remove profile prod ``` ### One-off profile override Pass `--profile` to any command to use a different profile for that single invocation. The active session profile is not changed: ```bash # Run a command against a different cluster c8 list pi --profile=staging # Deploy to production without switching context c8 deploy ./release/ --profile=prod # Use a Camunda Modeler profile for one command c8 search ut --state=CREATED --profile=modeler:Cloud Cluster ``` This is useful when you are working against a local development cluster but need to quickly check or interact with another environment. ### Camunda Modeler integration `c8ctl` automatically discovers and imports profiles from Camunda Modeler. These profiles are read-only, always prefixed with `modeler:`, and loaded dynamically on each command execution. ```bash # Set a Modeler profile as the active session profile c8 use profile "modeler:Local Dev" # Use a Modeler profile for one command c8 list pi --profile=modeler:Cloud Cluster # Deploy using a Modeler profile c8 deploy ./process.bpmn --profile=modeler:Local Dev ``` For Modeler profile file locations per platform, see [Getting started — Camunda Modeler integration](getting-started.md#camunda-modeler-integration). ## Session management Session state persists between commands. Settings you change remain active until you change them again. ### Set the active profile ```bash c8 use profile prod ``` ### Set the active tenant ```bash c8 use tenant my-tenant-id ``` ### Set the output mode ```bash c8 output json # JSON output for scripting c8 output text # human-readable tables (default) c8 output # show current output mode ``` ## MCP proxy The `mcp-proxy` command starts a local STDIO-to-HTTP proxy that bridges MCP clients (such as VS Code with GitHub Copilot, or Claude Code) to the [Orchestration Cluster MCP Server](/apis-tools/orchestration-cluster-api-mcp/orchestration-cluster-api-mcp-overview.md). It handles OAuth 2.0 authentication transparently, so MCP clients that do not support the client credentials flow can connect to authenticated clusters. ### Configure with VS Code Add the following to your `.vscode/mcp.json`: ```json { "servers": { "camunda-mcp": { "type": "stdio", "command": "npx", "args": ["-y", "@camunda8/cli", "mcp-proxy"], "env": { "CAMUNDA_BASE_URL": "https://", "CAMUNDA_CLIENT_ID": "", "CAMUNDA_CLIENT_SECRET": "", "CAMUNDA_OAUTH_URL": "https:///oauth/token", "CAMUNDA_TOKEN_AUDIENCE": "" } } } } ``` | Variable | Description | | :----------------------- | :--------------------------------------------------------------------------- | | `CAMUNDA_BASE_URL` | Base URL of your Orchestration Cluster, **without** the `/mcp/cluster` path. | | `CAMUNDA_CLIENT_ID` | OAuth client ID from your API client credentials. | | `CAMUNDA_CLIENT_SECRET` | OAuth client secret from your API client credentials. | | `CAMUNDA_OAUTH_URL` | OAuth token endpoint URL. | | `CAMUNDA_TOKEN_AUDIENCE` | Token audience for the Orchestration Cluster API. | :::tip When you [create API client credentials](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client) in the Camunda Console, all required connection details are shown on the credentials page. You can also copy a ready-to-use `c8ctl` configuration snippet from the MCP tab. ::: ### Use a profile with MCP proxy Instead of passing environment variables, use a `c8ctl` profile to supply credentials: ```json { "servers": { "camunda-mcp": { "type": "stdio", "command": "npx", "args": ["-y", "@camunda8/cli", "mcp-proxy", "--profile=prod"] } } } ``` This reads credentials from the named profile, including Modeler profiles (for example, `--profile=modeler:Cloud Cluster`). ### Local development without authentication If your local cluster does not require authentication (for example, [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md)), you can connect MCP clients directly without the proxy: ```json { "servers": { "camunda": { "type": "http", "url": "http://localhost:8080/mcp/cluster" } } } ``` For full MCP server documentation, see [Orchestration Cluster MCP Server](/apis-tools/orchestration-cluster-api-mcp/orchestration-cluster-api-mcp-overview.md). ## Advanced example: iterative process testing This example demonstrates an end-to-end development workflow — auto-deploying on save, starting a process instance with variables loaded from a file, monitoring execution in real time, and retrieving the result. ### 1. Start watch mode In a terminal, start `c8ctl` in watch mode to auto-deploy resources whenever you save changes: ```bash c8 watch ``` Now edit your `.bpmn`, `.dmn`, or `.form` files in your editor. Every time you save, `c8ctl` redeploys automatically. ### 2. Prepare process variables In a different terminal, load variables from a JSON file into a shell variable: ```bash export processVar=$( :::warning Alpha feature `c8ctl` is in alpha and is not intended for production use. APIs, commands, and flags may change without notice between releases. See [alpha features](/components/early-access/alpha/alpha-features.md) for more information. Report issues and request features in the [`c8ctl` GitHub repository](https://github.com/camunda/c8ctl). ::: ## About `c8ctl` is a minimal-dependency CLI for Camunda 8. It is built on top of the [`@camunda8/orchestration-cluster-api`](https://www.npmjs.com/package/@camunda8/orchestration-cluster-api) TypeScript SDK and provides two equivalent bin aliases: `c8ctl` and `c8`. `c8ctl` is designed for developers who need fast, scriptable access to a Camunda 8 cluster during development and testing. It supports both Camunda 8 SaaS and Self-Managed environments. Use `c8ctl` to: - Inspect running clusters — list process instances, user tasks, incidents, and jobs. - Deploy BPMN, DMN, and form resources, optionally watching for file changes. - Manage profiles for multiple clusters, including profiles imported from Camunda Modeler. - Extend the CLI with custom plugins. ## Prerequisites - **Node.js ≥ 22.18.0** (required for native TypeScript support) ## Install Install `c8ctl` globally from npm: ```bash npm install @camunda8/cli -g ``` After installation, both `c8ctl` and `c8` are available as commands in your terminal. ## Quick start with a local cluster `c8ctl` includes a built-in `cluster` command that downloads and manages a local [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) instance. This is the fastest way to get a cluster running for development. ### Start a cluster ```bash # Start with the latest stable version (default) c8 cluster start # Start with a specific version c8 cluster start 8.9.0-alpha5 # Start using a version alias c8 cluster start stable c8 cluster start alpha # Start with a major.minor version (rolling release) c8 cluster start 8.8 ``` `c8ctl` automatically downloads the correct binary for your platform, caches it locally, launches the cluster in the background, and waits for it to become healthy. ### Stop the cluster ```bash c8 cluster stop ``` ### Check cluster status ```bash c8 cluster status ``` Reports whether a cluster is running, including connection details. ### View cluster logs ```bash c8 cluster logs ``` Streams log output from the running cluster. ### Manage cached versions ```bash # List locally cached versions and available aliases c8 cluster list # List all versions available on the remote download server c8 cluster list-remote # Download a version without starting it c8 cluster install 8.8 # Remove a locally cached version c8 cluster delete 8.8 ``` ### Version aliases The `stable` and `alpha` aliases are resolved dynamically from the [Camunda Download Center](https://downloads.camunda.cloud/release/camunda/c8run/): | Alias | Resolves to | | :------- | :------------------------------------------------------- | | `stable` | Highest minor release that is GA (for example, 8.9) | | `alpha` | Highest minor release overall (for example, 8.10-alpha0) | When no version is specified, `c8 cluster start` defaults to `stable`. A `.` version like `8.8` is treated as a rolling release — the download server directory is updated in-place with new patch releases. `c8 cluster start` uses the local version if available, while `c8 cluster install` always checks for a newer version. ### Debug output Stream raw c8run logs during startup: ```bash c8 cluster start --debug ``` ### Supported platforms - macOS (x86_64, aarch64) - Linux (x86_64, aarch64) - Windows (x86_64) Cache locations: | Platform | Path | | :------- | :---------------------------- | | macOS | `~/Library/Caches/c8run/` | | Linux | `~/.cache/c8run/` | | Windows | `%LOCALAPPDATA%\c8run\cache\` | Override the cache directory with the `C8RUN_CACHE_DIR` environment variable. ## Credential resolution `c8ctl` resolves credentials in the following order: 1. **`--profile` flag** — one-off override for a single command. 2. **Active profile** — set with `c8 use profile `. 3. **Environment variables** — standard `CAMUNDA_*` variables (take precedence over the default profile). 4. **Default `local` profile** — `http://localhost:8080/v2`. When no profile has been explicitly set, `c8ctl` defaults to a built-in `local` profile that points to `http://localhost:8080/v2`. This means you can start a local cluster with `c8 cluster start` and immediately run commands without any configuration. If the connection fails, `c8ctl` shows a hint with the URL it tried to connect to. ### Use environment variables ```bash export CAMUNDA_BASE_URL=https://camunda.example.com export CAMUNDA_CLIENT_ID=your-client-id export CAMUNDA_CLIENT_SECRET=your-client-secret c8 list pi ``` ### Use a profile ```bash c8 add profile prod \ --baseUrl=https://camunda.example.com \ --clientId=your-client-id \ --clientSecret=your-client-secret c8 use profile prod c8 list pi ``` ### Override the profile for a single command Pass `--profile` to any command to use a different profile without changing the active session: ```bash c8 list pi --profile=staging c8 deploy ./process.bpmn --profile=prod c8 search ut --assignee=jane --profile=dev ``` The `--profile` flag works with both `c8ctl` profiles and Camunda Modeler profiles (prefixed with `modeler:`): ```bash c8 list pi --profile=modeler:Cloud Cluster c8 deploy ./process.bpmn --profile=modeler:Local Dev ``` ## Tenant resolution Tenants are resolved in the following order: 1. **Active tenant** — set with `c8 use tenant `. 2. **Default tenant** from the active profile. 3. **`CAMUNDA_DEFAULT_TENANT_ID`** environment variable. 4. **``** tenant. ```bash c8 use tenant my-tenant-id c8 list pi # uses my-tenant-id ``` ## Profile management `c8ctl` supports two types of profiles: 1. `c8ctl` profiles — managed directly with `c8ctl` commands. 2. Camunda Modeler profiles — automatically imported from Camunda Modeler (read-only, prefixed with `modeler:`). ### Add a profile ```bash # Minimal local profile (defaults to http://localhost:8080/v2) c8 add profile local # OAuth-secured cluster c8 add profile prod \ --baseUrl=https://camunda.example.com \ --clientId=your-client-id \ --clientSecret=your-client-secret # With explicit OAuth endpoint and audience c8 add profile prod \ --baseUrl=https://camunda.example.com \ --clientId=your-client-id \ --clientSecret=your-client-secret \ --audience=camunda-api \ --oAuthUrl=https://auth.example.com/oauth/token # With a default tenant c8 add profile dev \ --baseUrl=https://dev.example.com \ --clientId=dev-client \ --clientSecret=dev-secret \ --defaultTenantId=dev-tenant ``` ### List profiles ```bash c8 list profiles ``` Lists both `c8ctl` and Modeler profiles. Modeler profiles appear with a `modeler:` prefix. ### Switch the active profile ```bash c8 use profile prod c8 use profile "modeler:Local Dev" ``` All subsequent commands use the active profile until you switch again or pass `--profile`. ### Show the current profile ```bash c8 which profile ``` ### Remove a profile ```bash c8 remove profile prod c8 rm profile prod # alias ``` :::note Modeler profiles are read-only. They cannot be modified or removed through `c8ctl` — manage them in Camunda Modeler. ::: ### Camunda Modeler integration `c8ctl` automatically reads profiles from Camunda Modeler's `profiles.json` file. These profiles are: - **Read-only** — cannot be modified or deleted via `c8ctl`. - **Prefixed** — always displayed with a `modeler:` prefix (for example, `modeler:Local Dev`). - **Dynamic** — loaded fresh on each command execution. Platform-specific locations: | Platform | Path | | :------- | :------------------------------------------------------------ | | Linux | `~/.config/camunda-modeler/profiles.json` | | macOS | `~/Library/Application Support/camunda-modeler/profiles.json` | | Windows | `%APPDATA%\camunda-modeler\profiles.json` | ```bash # Use a Modeler profile as the active session profile c8 use profile "modeler:Local Dev" # Use a Modeler profile for a single command c8 list pi --profile=modeler:Cloud Cluster ``` ## Get help ```bash c8ctl help # general help c8ctl help list # help for the list command c8ctl help deploy # help for the deploy command c8ctl help profiles # help for profile management c8ctl --version # print version ``` Run any verb without a resource to see what resources are available: ```bash c8 list # shows: pi, pd, ut, inc, jobs, profiles, plugins, users, roles, groups, tenants, auth, mr c8 search # shows: pi, pd, ut, inc, jobs, variables, users, roles, groups, tenants, auth, mr ``` ## Send feedback ```bash c8 feedback ``` Opens the GitHub issues page in your browser to report bugs or request features. ## Update notifications `c8ctl` checks for newer versions in the background and displays a one-time notification when an update is available. This check is suppressed in CI environments, JSON output mode, and development versions. ## Shell completion The recommended way to set up shell completion is with the `install` subcommand: ```bash c8 completion install ``` This auto-detects your shell, writes the completion file, and wires it into your shell configuration. To specify a shell explicitly: ```bash c8 completion install --shell zsh ``` Completions auto-refresh when the CLI is upgraded. Alternatively, generate the completion script manually: ```bash c8ctl completion bash > ~/.c8ctl-completion.bash echo 'source ~/.c8ctl-completion.bash' >> ~/.bashrc source ~/.bashrc ``` ```bash c8ctl completion zsh > ~/.c8ctl-completion.zsh echo 'source ~/.c8ctl-completion.zsh' >> ~/.zshrc source ~/.zshrc ``` ```bash c8ctl completion fish > ~/.config/fish/completions/c8ctl.fish ``` Fish loads the completion automatically on the next shell start. ## Output modes Switch between human-readable text and machine-readable JSON: ```bash c8 output json # all commands output JSON c8 output text # back to formatted tables (default) ``` ## Environment variables | Variable | Description | | :-------------------------- | :------------------- | | `CAMUNDA_BASE_URL` | Cluster base URL | | `CAMUNDA_CLIENT_ID` | OAuth client ID | | `CAMUNDA_CLIENT_SECRET` | OAuth client secret | | `CAMUNDA_TOKEN_AUDIENCE` | OAuth token audience | | `CAMUNDA_OAUTH_URL` | OAuth token endpoint | | `CAMUNDA_DEFAULT_TENANT_ID` | Default tenant ID | Environment variable conventions follow the [`@camunda8/orchestration-cluster-api`](https://www.npmjs.com/package/@camunda8/orchestration-cluster-api) module. ## Debug mode Enable debug logging to see detailed internal information such as plugin loading and credential resolution: ```bash DEBUG=1 c8 list pi # or C8CTL_DEBUG=true c8 list pi ``` Debug output is written to stderr and does not interfere with normal command output. ## Next steps - [Cluster inspection and process management](cluster-inspection.md) — list, search, and manage process instances, user tasks, incidents, and jobs. - [Development workflows](development-workflows.md) — deploy, run, watch, and configure profiles and MCP proxy. - [Extend `c8ctl` with plugins](plugins.md) — scaffold, install, and manage custom CLI plugins. --- ## Identity management :::warning Alpha feature `c8ctl` is in alpha and not intended for production use. Commands and flags may change between releases. See [Getting started](getting-started.md) for details. ::: `c8ctl` provides commands to manage identity resources through the Orchestration Cluster API. You can list, search, get, create, and delete users, roles, groups, tenants, authorizations, and mapping rules. Membership management is handled with the `assign` and `unassign` verbs. | Resource | Alias | Available verbs | | :----------------- | :----- | :------------------------------------------ | | `user(s)` | — | `list`, `search`, `get`, `create`, `delete` | | `role(s)` | — | `list`, `search`, `get`, `create`, `delete` | | `group(s)` | — | `list`, `search`, `get`, `create`, `delete` | | `tenant(s)` | — | `list`, `search`, `get`, `create`, `delete` | | `authorization(s)` | `auth` | `list`, `search`, `get`, `create`, `delete` | | `mapping-rule(s)` | `mr` | `list`, `search`, `get`, `create`, `delete` | :::tip All commands respect the active profile and tenant. Pass `--profile` to override the profile for a single command: ```bash c8 list users --profile=prod c8 search roles --profile=staging ``` ::: ## Users ### List users ```bash c8 list users ``` ### Search users ```bash c8 search users --name=John c8 search users --email='john@example.com' c8 search users --name=John --email='john@example.com' ``` ### Get a user ```bash c8 get user john ``` ### Create a user ```bash c8 create user --username=john --name='John Doe' --email=john@example.com --password=changeme ``` ### Delete a user ```bash c8 delete user john ``` ## Roles ### List roles ```bash c8 list roles ``` ### Search roles ```bash c8 search roles --name=admin ``` ### Get a role ```bash c8 get role admin ``` ### Create a role ```bash c8 create role --name=my-role ``` ### Delete a role ```bash c8 delete role my-role ``` ## Groups ### List groups ```bash c8 list groups ``` ### Search groups ```bash c8 search groups --name=developers ``` ### Get a group ```bash c8 get group developers ``` ### Create a group ```bash c8 create group --groupId=developers --name=Developers ``` ### Delete a group ```bash c8 delete group developers ``` ## Tenants ### List tenants ```bash c8 list tenants ``` ### Search tenants ```bash c8 search tenants --name=Production ``` ### Get a tenant ```bash c8 get tenant prod ``` ### Create a tenant ```bash c8 create tenant --tenantId=prod --name='Production' ``` ### Delete a tenant ```bash c8 delete tenant prod ``` ## Authorizations ### List authorizations ```bash c8 list auth c8 list authorizations ``` ### Search authorizations ```bash c8 search auth --ownerId=john --resourceType=process-definition ``` ### Create an authorization ```bash c8 create auth --ownerId=john --ownerType=USER --resourceType=process-definition --resourceId='*' --permissions=READ,CREATE ``` ### Delete an authorization ```bash c8 delete auth 2251799813685260 ``` ## Mapping rules ### List mapping rules ```bash c8 list mr c8 list mapping-rules ``` ### Search mapping rules ```bash c8 search mr --name=my-rule ``` ### Create a mapping rule ```bash c8 create mr --mappingRuleId=my-rule --name='My Rule' ``` ### Delete a mapping rule ```bash c8 delete mr my-rule ``` ## Assign and unassign The `assign` and `unassign` verbs manage membership between identity resources. You can assign users to roles, groups, or tenants, and assign groups to tenants. ### Assign a user to a role ```bash c8 assign role admin --to-user=john ``` ### Unassign a user from a role ```bash c8 unassign role admin --from-user=john ``` ### Assign a user to a group ```bash c8 assign user john --to-group=developers ``` ### Unassign a user from a group ```bash c8 unassign user john --from-group=developers ``` ### Assign a group to a tenant ```bash c8 assign group developers --to-tenant=prod ``` ### Unassign a group from a tenant ```bash c8 unassign group developers --from-tenant=prod ``` --- ## Extend c8ctl with plugins :::warning Alpha feature `c8ctl` is in alpha and not intended for production use. Commands and flags may change between releases. See [Getting started](getting-started.md) for details. ::: `c8ctl` supports a global plugin system that lets you add custom commands. Plugins are installed globally to a user-specific directory and tracked in a registry file (`plugins.json`). ## Plugin storage locations | Platform | Plugins directory | Registry file | | :------- | :--------------------------------------------------------- | :------------------------------------------------- | | Linux | `~/.config/c8ctl/plugins/node_modules` | `~/.config/c8ctl/plugins.json` | | macOS | `~/Library/Application Support/c8ctl/plugins/node_modules` | `~/Library/Application Support/c8ctl/plugins.json` | | Windows | `%APPDATA%\c8ctl\plugins\node_modules` | `%APPDATA%\c8ctl\plugins.json` | You can override the data directory with the `C8CTL_DATA_DIR` environment variable. ## Scaffold a new plugin Generate a new plugin project from a TypeScript template: ```bash c8ctl init plugin my-plugin ``` This creates a project directory with all necessary files, build configuration, and an `AGENTS.md` guide for autonomous plugin implementation. ## Install a plugin ### From the npm registry ```bash c8 load plugin my-custom-plugin ``` ### From a URL ```bash c8 load plugin --from https://github.com/user/my-plugin c8 load plugin --from file:///path/to/local/plugin c8 load plugin --from git://github.com/user/plugin.git ``` After loading, plugin commands are immediately available. ## Manage plugins ### List installed plugins ```bash c8 list plugins ``` Output shows version and sync status for each plugin: - `✓ Installed` — plugin is in the registry and installed. - `⚠ Not installed` — plugin is in the registry but missing from disk (run `sync`). - `⚠ Not in registry` — plugin is installed but not tracked in the registry. ### Upgrade a plugin ```bash # Upgrade to latest c8 upgrade plugin my-custom-plugin # Upgrade to a specific version c8 upgrade plugin my-custom-plugin 1.2.3 ``` ### Downgrade a plugin ```bash c8 downgrade plugin my-custom-plugin 1.0.0 ``` Upgrade and downgrade behavior depends on the plugin source: | Source | Behavior | | :---------- | :---------------------------------------------------------------------------------------------------------- | | npm package | Installs `@`. | | URL/git | Installs `#`. | | `file://` | Version-based upgrade/downgrade is not supported. Use `load plugin --from` with the desired local checkout. | ### Unload a plugin ```bash c8 unload plugin my-custom-plugin ``` ### Synchronize plugins Synchronize all plugins from the registry. Rebuilds installed plugins and reinstalls any that are missing: ```bash c8 sync plugins ``` ## Plugin structure A plugin is a regular Node.js module with a `c8ctl-plugin.js` (or `c8ctl-plugin.ts`) file in the root directory. The file must export a `commands` object and optionally a `metadata` object. ### Minimal example ```typescript // c8ctl-plugin.ts export const metadata = { name: "my-plugin", description: "My custom c8ctl plugin", commands: { analyze: { description: "Analyze BPMN processes for best practices", }, optimize: { description: "Optimize process definitions", }, }, }; export const commands = { analyze: async (args: string[]) => { console.log("Analyzing...", args); }, optimize: async (args: string[]) => { console.log("Optimizing..."); }, }; ``` ## Plugin runtime API At runtime, `c8ctl` injects a global object via `globalThis.c8ctl` that plugins can use to interact with the Camunda cluster and the `c8ctl` environment. | Method/field | Description | | :----------------------------------- | :--------------------------------------------------------------------------------------- | | `createClient(profile?, sdkConfig?)` | Create a Camunda SDK client. Optionally pass a profile name to use specific credentials. | | `resolveTenantId(profile?)` | Resolve the active tenant ID using the same fallback logic as built-in commands. | | `getLogger()` | Get the `c8ctl` logger instance (respects the current output mode). | | `version` | `c8ctl` version string. | | `nodeVersion` | Node.js version. | | `platform` | Operating system (`linux`, `darwin`, `win32`). | | `arch` | CPU architecture. | | `cwd` | Current working directory. | | `outputMode` | Current output mode (`text` or `json`). | | `activeProfile` | Name of the active profile. | | `activeTenant` | Active tenant ID. | ### TypeScript autocomplete For TypeScript autocomplete in your plugin, import the runtime type: ```typescript const c8ctl = globalThis.c8ctl as C8ctlPluginRuntime; const tenantId = c8ctl.resolveTenantId(); const logger = c8ctl.getLogger(); logger.info(`Tenant: ${tenantId}`); ``` ### Use the SDK client from a plugin ```typescript const c8ctl = globalThis.c8ctl as C8ctlPluginRuntime; export const commands = { "list-active": async (args: string[]) => { const client = c8ctl.createClient(); const logger = c8ctl.getLogger(); // Use the client to query the Orchestration Cluster API logger.info("Client ready"); }, }; ``` ## Help integration When plugins export a `metadata.commands` object with descriptions, those commands appear in the `c8ctl help` output under a **Plugin Commands** section: ```text c8ctl - Camunda 8 CLI v2.2.0 Commands: list List resources (pi, ut, inc, jobs, profiles) get Get resource by key (pi, topology) ... Plugin Commands: analyze Analyze BPMN processes for best practices optimize Optimize process definitions ``` Plugins without a `metadata` export still work — their commands appear in the help output without descriptions. ## Command precedence Built-in commands take precedence over plugin commands. If a plugin exports a command with the same name as a built-in command (for example, `list` or `deploy`), the built-in command runs. Use descriptive and unique names for plugin commands. Recommended: - `analyze-process` - `export-data` - `sync-resources` Avoid: - `list` - `get` - `create` - `deploy` ## Find plugins Plugins are distributed as regular npm packages. There are two main ways to discover available plugins: ### Search the Camunda GitHub organization Browse the [Camunda GitHub organization](https://github.com/camunda) and search for repositories with `c8ctl` in the name. By convention, plugin repositories are named `c8ctl-plugin-` (for example, `c8ctl-plugin-analyze`), but this is not a hard requirement — any npm package with a `c8ctl-plugin.js` entry point works as a plugin. ### Search the npm registry Search for `c8ctl` or `c8ctl-plugin` on [npmjs.com](https://www.npmjs.com/search?q=c8ctl-plugin): ```bash npm search c8ctl-plugin ``` Once you find a plugin, install it with: ```bash c8 load plugin ``` ## Best practices - Use unique command names to avoid conflicts with built-in commands. - Provide descriptions in `metadata.commands` so users discover your commands in `c8ctl help`. - Keep descriptions concise and aim for a single line under 60 characters, starting with an imperative verb. - Transpile TypeScript to JavaScript before publishing. The `c8ctl-plugin.js` entry point in `node_modules` must be JavaScript, because Node.js does not support type stripping in `node_modules`. - Use `createClient()` from the runtime API to create SDK clients rather than importing the SDK directly. This ensures credentials and tenant resolution follow `c8ctl` conventions. --- ## Configuration This page uses YAML examples to show configuration properties. Alternate methods to [externalize or override your configuration](https://docs.spring.io/spring-boot/reference/features/external-config.html) are provided by Spring Boot, and can be applied without rebuilding your application (properties files, Java System properties, or environment variables). :::note Configuration properties can be defined as environment variables using [Spring Boot conventions](https://docs.spring.io/spring-boot/reference/features/external-config.html#features.external-config.typesafe-configuration-properties.relaxed-binding.environment-variables). To define an environment variable, convert the configuration property to uppercase, remove any dashes `-`, and replace any delimiters `.` with underscore `_`. For example, the property `camunda.client.worker.defaults.max-jobs-active` is represented by the environment variable `CAMUNDA_CLIENT_WORKER_DEFAULTS_MAXJOBSACTIVE`. ::: :::note For a full set of properties, head over to the [properties reference](./properties-reference.md) ::: ## Modes The Camunda Spring Boot Starter has modes with meaningful defaults aligned with the distribution's default connection details. Each mode is made for a Camunda 8 setup, and only one mode may be used at a time. :::note The defaults applied by the modes are overwritten by _any_ other set property, including legacy/deprecated properties. Check your configuration and logs to avoid unwanted override. ::: ### SaaS This allows you to connect to a Camunda instance in our SaaS offering as the URLs are templated. Activate by setting: ```yaml camunda: client: mode: saas ``` This applies the following defaults: ```yaml reference referenceLinkText="Source" title="SaaS mode" https://github.com/camunda/camunda/blob/main/clients/camunda-spring-boot-starter/src/main/resources/modes/saas.yaml ``` The only thing you need to configure then, are the connection details to your Camunda SaaS cluster: ```yaml camunda: client: auth: client-id: client-secret: cloud: cluster-id: region: ``` Other connectivity configuration does not further apply for the SaaS mode. ### Self-Managed This allows you to connect to a Self-Managed instance protected with JWT authentication. The default URLs are configured to align with all Camunda distributions using `localhost` addresses. Activate by setting: ```yaml camunda: client: mode: self-managed ``` This applies the following defaults: ```yaml reference referenceLinkText="Source" title="Self-managed mode" https://github.com/camunda/camunda/blob/main/clients/camunda-spring-boot-starter/src/main/resources/modes/self-managed.yaml ``` For some specific OIDC setups (e.g [Microsoft Entra ID](https://learn.microsoft.com/en-us/entra/identity)), you might need to define additional properties like `camunda.client.auth.scope` in addition to the defaults provided by the mode, see the [`camunda.client.auth`-Properties reference](./properties-reference.md) for a full overview. ## Connectivity The connection to Camunda API is determined by `camunda.client.grpc-address` and `camunda.client.rest-address` ### Camunda API connection #### gRPC address Define the address of the [gRPC API](/apis-tools/zeebe-api/grpc.md) exposed by the [Zeebe Gateway](/reference/glossary.md#zeebe-gateway): ```yaml camunda: client: grpc-address: http://localhost:26500 ``` :::note You must add the `http://` scheme to the URL to avoid a `java.lang.NullPointerException: target` error. ::: #### REST address Define address of the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) exposed by the Zeebe Gateway: ```yaml camunda: client: rest-address: http://localhost:8080 ``` :::note You must add the `http://` scheme to the URL to avoid a `java.lang.NullPointerException: target` error. ::: #### Prefer REST over gRPC By default, the Camunda Client will use REST instead of gRPC whenever possible to communicate with the Camunda APIs. To use the gRPC by default, you can configure this: ```yaml camunda: client: prefer-rest-over-grpc: false ``` ### Advanced connectivity settings ```yaml camunda: client: keep-alive: PT60S override-authority: host:port max-message-size: 4194304 max-metadata-size: 4194304 ca-certificate-path: path/to/certificate request-timeout: PT10S request-timeout-offset: PT1S ``` **Keep alive:** Time interval between keep alive messages sent to the gateway (default is 45s). **Override authority:** The alternative authority to use, commonly in the form `host` or `host:port`. **Max message size:** A custom `maxMessageSize` allows the client to receive larger or smaller responses from Zeebe. Technically, it specifies the `maxInboundMessageSize` of the gRPC channel (default 5MB). **Max metadata size:** A custom `maxMetadataSize` allows the client to receive larger or smaller response headers from Camunda. **CA certificate path:** Path to a root CA certificate to be used instead of the certificate in the default store. **Request timeout:** The timeout for all requests sent to Camunda. There is an additional option to define the timeout for workers. **Request timeout offset:** The offset being added to the timeout on asynchronous requests sent to Camunda to cover the network latency. ### Multi-tenancy To connect the client to a specific tenant, you can configure: ```yaml camunda: client: tenant-id: myTenant ``` This does also affect the default tenant being used by all job workers, however there are [more possibilities](#control-tenant-usage) to configure them. ## Authentication The authentication method is determined by `camunda.client.auth.method`. If omitted, the client will try to detect the authentication method based on the provided properties. Authenticate with the cluster using the following alternative methods: :::info When using `camunda.client.mode=saas`, the authentication method presets are not applied in favor of the properties contained in the SaaS preset. ::: ### No authentication By default, no authentication will be used. To explicitly activate this method, you can set: ```yaml camunda: client: auth: method: none ``` As alternative, do not provide any other property indicating an implicit authentication method. This will load this preset: ```yaml reference referenceLinkText="Source" title="No authentication" https://github.com/camunda/camunda/blob/main/clients/camunda-spring-boot-starter/src/main/resources/auth-methods/none.yaml ``` ### Basic authentication You can authenticate with the cluster using Basic authentication, if the cluster is setup to use Basic authentication. To explicitly activate this method, you can set: ```yaml camunda: client: auth: method: basic ``` This authentication method will be implied if you set either `camunda.client.auth.username` or `camunda.client.auth.password`. This will load this preset: ```yaml reference referenceLinkText="Source" title="Basic authentication" https://github.com/camunda/camunda/blob/main/clients/camunda-spring-boot-starter/src/main/resources/auth-methods/basic.yaml ``` ### OIDC authentication You can authenticate with the cluster using OpenID Connect (OIDC) with client ID and client secret. To explicitly activate this method, you can set: ```yaml camunda: client: auth: method: oidc ``` This authentication method will be implied if you set either `camunda.client.auth.client-id` or `camunda.client.auth.client-secret`. This will load this preset: ```yaml reference referenceLinkText="Source" title="OIDC authentication" https://github.com/camunda/camunda/blob/main/clients/camunda-spring-boot-starter/src/main/resources/auth-methods/oidc.yaml ``` :::note There are three ways to define the token URL. They're prioritized as follows: 1. Provide the `camunda.client.auth.token-url`. 2. Provide the issuer's well-known configuration URL `camunda.client.auth.well-known-configuration-url`. This extracts the token URL from the `token_url` field in the loaded configuration. 3. Provide the issuer's URL `camunda.client.auth.issuer-url`. This generates the well-known configuration URL and extracts the token URL from the `token_url` field in the loaded configuration. ::: #### Credentials cache path You can define the credentials cache path of the zeebe client, the property contains directory path and file name: ```yaml camunda: client: auth: credentials-cache-path: /tmp/credentials ``` #### Custom identity provider security context Several identity providers, such as Keycloak, support client X.509 authorizers as an alternative to client credentials flow. As a prerequisite, ensure you have proper KeyStore and TrustStore configured, so that: - Both the Spring Camunda application and identity provider share the same CA trust certificates. - Both the Spring Camunda and identity provider own certificates signed by trusted CA. - Your Spring Camunda application own certificate has proper `Distinguished Name` (DN), e.g. `CN=My Camunda Client, OU=Camunda Users, O=Best Company, C=DE`. - Your application DN registered in the identity provider client authorization details. Once prerequisites are satisfied, your Spring Camunda application must be configured either via global SSL context, or with an exclusive context which is documented below. Refer to your identity provider documentation on how to configure X.509 authentication. For example, [Keycloak](https://www.keycloak.org/server/mutual-tls). If you require configuring SSL context exclusively for your identity provider, you can use this set of properties: ```yaml camunda: client: auth: keystore-path: /path/to/keystore.p12 keystore-password: password keystore-key-password: password truststore-path: /path/to/truststore.jks truststore-password: password ``` - **keystore-path**: Path to client's KeyStore; can be both in JKS or PKCS12 formats - **keystore-password**: KeyStore password - **keystore-key-password**: Key material password - **truststore-path**: Path to client's TrustStore - **truststore-password**: TrustStore password When the properties are not specified, the default SSL context is applied. For example, if you configure an application with `javax.net.ssl.*` or `spring.ssl.*`, the latter is applied. If both `camunda.client.auth.*` and either `javax.net.ssl.*` or `spring.ssl.*` properties are defined, the `camunda.client.auth.*` takes precedence. ## Job worker configuration options ### Job type You can configure the job type via the `JobWorker` annotation: ```java @JobWorker(type = "foo") public void handleJobFoo() { // handles jobs of type 'foo' } ``` If you don't specify the `type` attribute, the **method name** is used by default: ```java @JobWorker public void foo() { // handles jobs of type 'foo' } ``` As a third possibility, you can set a task type as property: ```yaml camunda: client: worker: override: foo: type: bar ``` As a fourth possibility, you can set a default task type as property: ```yaml camunda: client: worker: defaults: type: foo ``` This is used for all workers that do **not** set a task type via the annotation or set a job type as individual worker property. ### Control variable fetching A job worker can submit a list of variables when activating jobs to limit the amount of data being sent. There are implicit and explicit ways to control the variable fetching. While the implicit ones come with the job worker function parameters, the explicit ones are listed here. #### Provide a list of variables to fetch You can specify that you only want to fetch some variables (instead of all) when executing a job, which can decrease load and improve performance: ```java @JobWorker(type = "foo", fetchVariables={"variable1", "variable2"}) public void handleJobFoo(final JobClient client, final ActivatedJob job) { String variable1 = (String)job.getVariablesAsMap().get("variable1"); System.out.println(variable1); // ... } ``` You can also override the variables to fetch in your properties: ```yml camunda: client: worker: override: foo: fetch-variables: - variable1 - variable2 ``` :::caution Using the properties-defined way of fetching variables will override **all** other detection strategies. ::: #### Prevent the variable filtering You can force that all variables are loaded anyway: ```java @JobWorker(type = "foo", fetchAllVariables = true) public void handleJobFoo(final JobClient client, final ActivatedJob job, @Variable String variable1) { } ``` You can also override the forced fetching of all variables in your properties: ```yml camunda: client: worker: override: foo: force-fetch-all-variables: true ``` ### Define job worker function parameters The method signature you use to define job worker functions will affect how variables are retrieved. Unless stated otherwise, all specified methods for fetching variables will be combined into a single list of variables to retrieve. #### `JobClient` parameter The `JobClient` is also part of the native `JobHandler` functional interface: ```java @JobWorker(type = "foo") public void handleJobFoo(final JobClient jobClient) { // ... } ``` #### `ActivatedJob` parameter The `ActivatedJob` is also part of the native `JobHandler` functional interface. This will **prevent** the implicit variable fetching detection as you can retrieve variables in a programmatic way now: ```java @JobWorker(type = "foo") public void handleJobFoo(final ActivatedJob job) { String variable1 = (String)job.getVariablesAsMap().get("variable1"); System.out.println(variable1); // ... } ``` :::note Only explicit variable fetching will be effective on using the `ActivatedJob` as parameter. ::: #### Using `@Variable` By using the `@Variable` annotation, there is a shortcut to make variable retrieval simpler and only fetch certain variables, making them available as parameters: ```java @JobWorker(type = "foo") public void handleJobFoo(@Variable(name = "variable1") String variable1) { System.out.println(variable1); // ... } ``` If you don't specify the `name` attribute on the annotation, the **method parameter name** is used as the variable name if you enabled the [`-parameters` compiler flag](/apis-tools/camunda-spring-boot-starter/getting-started.md#enable-the-java-compiler--parameters-flag) in the [getting started section](/apis-tools/camunda-spring-boot-starter/getting-started.md): ```java @JobWorker(type = "foo") public void handleJobFoo(final JobClient client, final ActivatedJob job, @Variable String variable1) { System.out.println(variable1); // ... } ``` :::note This will add the name of the variable to the joint list of variables to fetch. ::: #### Using `@VariablesAsType` You can also use your own class into which the process variables are mapped to (comparable to `getVariablesAsType()` in the [Java client API](/apis-tools/java-client/getting-started.md)). Therefore, use the `@VariablesAsType` annotation. In the example below, `MyProcessVariables` refers to your own class: ```java @JobWorker(type = "foo") public ProcessVariables handleFoo(@VariablesAsType MyProcessVariables variables) { // do whatever you need to do variables.getMyAttributeX(); variables.setMyAttributeY(42); // return variables object if something has changed, so the changes are submitted to Zeebe return variables; } ``` :::note This will add the names of the fields of the used type to the joint list of variables to fetch. Jackson's `@JsonProperty` annotation is respected. ::: #### Using `@Document` You can inject a `DocumentContext` by using the `@Document` annotation: ```java @JobWorker public void processDocument(@Document DocumentContext doc) { List documents = doc.getDocuments(); // do what you need to do with the document entries } ``` Each `DocumentEntry` grants you access to the `DocumentReferenceResponse` that contains the reference data to the document and the `DocumentLinkResponse` that contains a link to the document. On top, you can directly retrieve the document content as `InputStream` or `byte[]`. #### Using `@CustomHeaders` You can use the `@CustomHeaders` annotation for a `Map` parameter to retrieve [custom headers](/components/concepts/job-workers.md) for a job: ```java @JobWorker public void handleFoo(@CustomHeaders Map headers) { // do whatever you need to do } ``` :::note This will not have any effect on the variable fetching behavior. ::: #### Using `@ProcessInstanceKey`, `@ElementInstanceKey`, `@JobKey`, `@ProcessDefinitionKey` and `@RootProcessInstanceKey` You can use the `@ProcessInstanceKey`, `@ElementInstanceKey`, `@JobKey`, `@ProcessDefinitionKey` and `@RootProcessInstanceKey` annotation for a `String`, `long` or `Long` parameter to retrieve the according key for a job: ```java @JobWorker public void handleFoo( @ProcessInstanceKey String processInstanceKey, @ElementInstanceKey long elementInstanceKey, @JobKey Long jobKey, @ProcessDefinitionKey String processDefinitionKey, @RootProcessInstanceKey long rootProcessInstanceKey) { // do whatever you need to do } ``` ### Completing jobs #### Auto-completing jobs By default, the `autoComplete` attribute is set to `true` for any job worker. In this case, the Spring integration will handle job completion for you: ```java @JobWorker(type = "foo") public void handleJobFoo(final ActivatedJob job) { // do whatever you need to do // no need to call client.newCompleteCommand()... } ``` :::note The code within the handler method needs to be synchronously executed, as the completion will be triggered right after the method has finished. ::: ##### Returning results When using `autoComplete` you can return: - a `Map` containing the process variables to set as result of the job - a `String` containing a valid JSON object - an `InputStream` streaming a valid JSON object - an `Object` that will be serialized to a JSON object ```java @JobWorker(type = "foo") public Map handleJobFoo(final ActivatedJob job) { // some work if (successful) { // some data is returned to be stored as process variable return variablesMap; } else { // problem shall be indicated to the process: throw new BpmnError("DOESNT_WORK", "This does not work because..."); } } ``` ##### Documents as job results If you want to send a document as job result, you can do this by making a `DocumentContext` part of the response. It can be part of a `Map`: ```java @JobWorker public Map sendDocumentAsResult() { String resultDocumentContent = documentService.loadResult(); Map result = new HashMap<>(); result.put("resultDocument", DocumentContext.result() .addDocument( "result.json", b -> b.content(resultDocumentContent).contentType("application/json")) .build()); return result; } ``` It can also be part of an `Object`: ```java public record DocumentResult(DocumentContext responseDocument) {} @JobWorker public DocumentResult sendDocumentAsResult() { String resultDocumentContent = documentService.loadResult(); DocumentContext responseDocument = DocumentContext.result() .addDocument( "result.json", b -> b.content(resultDocumentContent).contentType("application/json")) .build()); return new DocumentResult(responseDocument); } ``` #### Programmatically completing jobs Your job worker code can also complete the job itself. This gives you more control over when exactly you want to complete the job (for example, allowing the completion to be moved to reactive callbacks): ```java @JobWorker(type = "foo", autoComplete = false) public void handleJobFoo(final JobClient client, final ActivatedJob job) { // do whatever you need to do client.newCompleteCommand(job.getKey()) .send() .exceptionally( throwable -> { throw new RuntimeException("Could not complete job " + job, throwable); }); } ``` You can also control auto-completion in your configuration. **Globally:** ```yaml camunda: client: worker: defaults: auto-complete: false ``` **Per worker:** ```yaml camunda: client: worker: override: foo: auto-complete: false ``` Ideally, you **don't** use blocking behavior like `send().join()`, as this is a blocking call to wait for the issued command to be executed on the workflow engine. While this is very straightforward to use and produces easy-to-read code, blocking code is limited in terms of scalability. This is why the worker sample above shows a different pattern (using `exceptionally`). Often, you might want to use the `whenComplete` callback: ```java send().whenComplete((result, exception) -> {}) ``` This registers a callback to be executed when the command on the workflow engine was executed or resulted in an exception. This allows for parallelism. This is discussed in more detail in [this blog post about writing good workers for Camunda 8](https://blog.bernd-ruecker.com/writing-good-workers-for-camunda-cloud-61d322cad862). :::note When completing jobs programmatically, you must specify `autoComplete = false`. Otherwise, there is a race condition between your programmatic job completion and the Spring integration job completion, and this can lead to unpredictable results. ::: ### React to problems #### Throw a `BpmnError` If your code encounters a problem that should trigger a [BPMN error](/components/modeler/bpmn/error-events/error-events.md), throw a `BpmnError` and provide the error code defined in BPMN: ```java @JobWorker(type = "foo") public void handleJobFoo() { // some work if (businessError) { // problem shall be indicated to the process: throw CamundaError.bpmnError("ERROR_CODE", "Some explanation why this does not work"); // this is a static function that returns an instance of BpmnError } } ``` #### Fail jobs in a controlled way Whenever you want a job to fail in a controlled way, you can throw a `JobError` and provide parameters like `variables`, `retries` and `retryBackoff`: ```java @JobWorker(type = "foo") public void handleJobFoo() { try { // some work } catch(DynamicRetryException e) { // problem shall be indicated to the process: throw CamundaError.jobError("Error message", new ErrorVariables(), null, this::calculateRetryBackoff, e); // this is a static function that returns an instance of JobError with a dynamic retry backoff } catch(StaticRetryException e) { // problem shall be indicated to the process: throw CamundaError.jobError("Error message", new ErrorVariables(), null, Duration.ofSeconds(10), e); // this is a static function that returns an instance of JobError with a static retry backoff } } ``` The JobError takes 5 parameters: - `errorMessage`: String - `variables`: Object _(optional)_, default `null` - `retries`: Integer _(optional)_, defaults to `job.getRetries() - 1` - `retryBackoff`: Duration _or_ Function (Integer -> Duration) _(optional)_, defaults to the configured retry backoff, function input are the retries that will be submitted - `cause`: Exception _(optional)_, defaults to `null` :::note The job error is sent to the engine by the SDK calling the [Fail Job API](/apis-tools/orchestration-cluster-api-rest/specifications/fail-job.api.mdx). The stacktrace of the job error will become the actual error message. The provided cause will be visible in Operate. ::: #### Implicitly failing jobs If your handler method would throw any other exception than the ones listed above, the default Camunda Client error handling will apply, decrementing retries with a `retryBackoff` of 0. ### Configuring the job worker thread pool The number of threads for invocation of job workers (default 1): ```yaml camunda: client: execution-threads: 2 ``` :::note We generally do not advise using a thread pool for workers, but rather implement asynchronous code, see [writing good workers](/components/best-practices/development/writing-good-workers.md) for additional details. ::: ### Further job worker configuration options #### Disable a job worker You can disable workers via the `enabled` parameter of the `@JobWorker` annotation: ```java @JobWorker(enabled = false) public void foo() { // worker's code - now disabled } ``` You can also override this setting via your `application.yaml` file: ```yaml camunda: client: worker: override: foo: enabled: false ``` This is especially useful if you have a bigger code base including many workers, but want to start only some of them. Typical use cases are: - Testing: You only want one specific worker to run at a time. - Load balancing: You want to control which workers run on which instance of cluster nodes. - Migration: There are two applications, and you want to migrate a worker from one to another. With this switch, you can disable workers via configuration in the old application once they are available within the new. To disable all workers, but still have the Camunda client available, you can use: ```yaml camunda: client: worker: defaults: enabled: false ``` #### Configure jobs in flight Number of jobs for a worker that are polled from the broker to be worked on in this client: ```java @JobWorker(maxJobsActive = 64) public void foo() { // worker's code } ``` This can also be configured as property: ```yaml camunda: client: worker: override: foo: max-jobs-active: 64 ``` To configure a global default, you can set: ```yaml camunda: client: worker: defaults: max-jobs-active: 64 ``` #### Enable job streaming Read more about this feature in the [job streaming documentation](/apis-tools/java-client/job-worker.md#job-streaming). Job streaming is disabled by default for job workers. To enable job streaming on the Camunda client, configure it as follows: ```java @JobWorker(streamEnabled = true) public void foo() { // worker's code } ``` This can also be configured as property: ```yaml camunda: client: worker: override: foo: stream-enabled: true ``` To configure a global default, you can set: ```yaml camunda: client: worker: defaults: stream-enabled: true ``` #### Control tenant usage Job workers can be configured to work on jobs from specific [tenants](#multi-tenancy) using either [specific tenant IDs](#filtering-by-provided-tenant-IDs) or the [assigned tenants in the engine](#filtering-by-assigned-tenants). ##### Filter by assigned tenants You can configure a job worker to use the tenants assigned to it in the engine, rather than providing explicit tenant IDs. Use the `tenantFilter` annotation property with `TenantFilter.ASSIGNED`: ```java @JobWorker(tenantFilter = TenantFilter.ASSIGNED) public void foo() { // worker's code } ``` When `TenantFilter.ASSIGNED` is set, any `tenant-ids` configured via the annotation or YAML are ignored. You can also override the tenant filter for a specific worker: ```yaml camunda: client: worker: override: foo: tenant-filter: ASSIGNED ``` To configure a global default: ```yaml camunda: client: worker: defaults: tenant-filter: ASSIGNED ``` ##### Filter by provided tenant IDs The default behaviour is `TenantFilter.PROVIDED`, where the worker retrieves jobs for the tenant IDs explicitly configured. Configure global worker defaults for additional `tenant-ids` to be used by all workers: ```yaml camunda: client: worker: defaults: tenant-ids: - - foo ``` Additionally, you can set `tenantIds` on the job worker level by using the annotation: ```java @JobWorker(tenantIds="myOtherTenant") public void foo() { // worker's code } ``` You can also override the `tenant-ids` for each worker: ```yaml camunda: client: worker: override: foo: tenants-ids: - - foo ``` #### Define the job timeout To define the job timeout, you can set the annotation (`long` in milliseconds): ```java @JobWorker(timeout=60000) public void foo() { // worker's code } ``` Moreover, you can override the timeout for the worker (as ISO 8601 duration expression): ```yaml camunda: client: worker: override: foo: timeout: PT1M ``` You can also set a global default: ```yaml camunda: client: worker: defaults: timeout: PT1M ``` #### Configure the retry backoff If you want to apply a retry backoff that should be applied if a job fails without a job error, you can set the annotation (`long` in milliseconds): ```java @JobWorker(retryBackoff=10000L) public void work() { // worker's code } ``` Moreover, you can override the retry backoff for the worker (as ISO 8601 duration expression): ```yaml camunda: client: worker: override: foo: retry-backoff: PT10S ``` You can also set a global default: ```yaml camunda: client: worker: defaults: retry-backoff: PT10S ``` ## Deploy resources on start-up To deploy process models on application start-up, use the `@Deployment` annotation: ```java @Deployment(resources = "classpath:demoProcess.bpmn") public class MyRandomBean { // make sure this bean is registered } ``` ### Specify resources to deploy This annotation uses the [Spring resource loader](https://docs.spring.io/springframework/reference/core/resources.html) and can deploy multiple files at once. For example: ```java @Deployment(resources = {"classpath:demoProcess.bpmn" , "classpath:demoProcess2.bpmn"}) ``` Or, define wildcard patterns: ```java @Deployment(resources = "classpath*:/bpmn/**/*.bpmn") ``` The resource loader automatically searches the entire classpath, including dependency JARs. To deploy only the resources packaged with the annotated class, use: ```java @Deployment(resources = "classpath*:/bpmn/**/*.bpmn", ownJarOnly = true) ``` You can also set this globally: ```yaml camunda: client: deployment: own-jar-only: true ``` ### Specify the tenant to deploy to To adjust the tenant to deploy to, set the `tenantId` property of the `@Deployment` annotation: ```java @Deployment(resources = "classpath:demoProcess.bpmn", tenantId = "myTenant") public class MyRandomBean { // make sure this bean is registered } ``` By default, the starter uses the `tenantId` from `camunda.client.tenant-id`. ### Disable deployment To disable the deployment of annotations, you can set: ```yaml camunda: client: deployment: enabled: false ``` ## React to events The Camunda Spring Boot Starter integrates with Spring events and also publishes its own events. ### Camunda client lifecycle events #### Camunda client created To react when the Camunda client is created, add an event listener: ```java @EventListener public void onCamundaClientCreated(CamundaClientCreatedEvent event) { // do what you need to do } ``` #### Camunda client closing event To react on the closing of the Camunda client, you can do this: ```java @EventListener public void onCamundaClientClosing(CamundaClientClosingEvent event) { // do what you need to do } ``` #### Lifecycle aware interface To subscribe to the Camunda client lifecycle at once, you can also use an interface: ```java @Component public class CamundaLifecycleListener implements CamundaClientLifecycleAware { @Override public void onStart(CamundaClient client) { // do what you need to do } @Override public void onStop(CamundaClient client) { // do what you need to do } } ``` ### Post deployment event To react on the creation of [deployments on start-up](#deploying-resources-on-start-up), you can do this: ```java @EventListener public void onDeploymentCreated(CamundaPostDeploymentEvent event) { // do what you need to do } ``` The event will grant you access to a list of deployments that have been created. ## Observe metrics The Camunda Spring Boot Starter provides some out-of-the-box metrics that can be leveraged via [Spring Actuator](https://docs.spring.io/spring-boot/docs/current/actuator-api/htmlsingle/). Whenever actuator is on the classpath, you can access the following metrics: - `camunda.job.invocations`: Number of invocations of job workers (tagging the job type) For all of those metrics, the following actions are recorded: - `activated`: The job was activated and started to process an item. - `completed`: The processing was completed successfully. - `failed`: The processing failed with some exception. - `bpmn-error`: The processing completed by throwing a BPMN error (which means there was no technical problem). In a default setup, you can enable metrics to be served via http: ```yaml management: endpoints: web: exposure: include: metrics ``` Access them via [http://localhost:8080/actuator/metrics/](http://localhost:8080/actuator/metrics/). --- ## Camunda Spring Boot Starter ## About The Camunda Spring Boot Starter is the official way to integrate Camunda 8 APIs ([gRPC](/apis-tools/zeebe-api/grpc.md) and [REST](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md)) into your Spring Boot project. You can use it to orchestrate microservices, manage human tasks, and interact with process data using idiomatic Spring Boot patterns. :::info Public API The Camunda Spring Boot Starter is part of the Camunda 8 [public API](/reference/public-api.md) and follows [Semantic Versioning](https://semver.org/) (except for alpha features). Minor and patch releases will not introduce breaking changes. ::: :::info Migration from Spring Zeebe SDK **The Camunda Spring Boot Starter replaces the Spring Zeebe SDK as of version 8.8.** - Uses the new Camunda Java Client under the hood - REST is the default protocol (gRPC is configurable) - Spring Zeebe SDK will be **removed in version 8.10** - **Migrate before upgrading to 8.10** to avoid breaking changes See the [migration guide](/reference/announcements-release-notes/880/880-announcements.md#camunda-java-client-and-camunda-spring-boot-starter) for details. ::: ## What you can build with it With the Camunda Spring Boot Starter, you can build: - **Job workers** that perform automated tasks and call external systems (APIs, databases, file systems) - **Integration services** that connect Camunda processes with existing systems or third-party services - **Data processing applications** that use process data for visualization, analytics, or business intelligence ## Version compatibility | Camunda Spring Boot Starter artifact | Camunda Spring Boot Starter version | JDK | Bundled Spring Boot version | Compatible Spring Boot version(s) | | ------------------------------------ | ----------------------------------- | ---- | --------------------------- | --------------------------------- | | `camunda-spring-boot-starter` | 8.9.x | ≥ 17 | 4.0.x | | | `camunda-spring-boot-4-starter` | 8.9.x | ≥ 17 | 4.0.x | | | `camunda-spring-boot-3-starter` | 8.9.x | ≥ 17 | 3.5.x | | ### Dedicated Spring Boot 3 and 4 modules Starting with Camunda 8.9, the default `camunda-spring-boot-starter` artifact is bundled with **Spring Boot 4.0.x**. Additionally, two dedicated modules are available: - **`camunda-spring-boot-4-starter`**: Identical to `camunda-spring-boot-starter`. Use this if you want to explicitly target Spring Boot 4.0.x. - **`camunda-spring-boot-3-starter`**: Bundled with Spring Boot 3.5.x. Use this if your application is not yet ready to upgrade to Spring Boot 4.0. :::caution Spring Boot 3.5.x support window OSS support for Spring Boot 3.5.x ends in June 2026 (see [Spring Boot support timeline](https://spring.io/projects/spring-boot#support)). We encourage you to migrate to Spring Boot 4.0 before the support window closes. If you need continued Spring Boot 3.x support after June 2026, consider obtaining enterprise support from a third-party provider. ::: To use the Spring Boot 3 module, replace the default dependency in your project: ```xml io.camunda camunda-spring-boot-3-starter 8.9.x ``` ## Get started ### Step 1: Add the dependency Add the Camunda Spring Boot Starter to your project: **Maven:** ```xml io.camunda camunda-spring-boot-starter 8.9.x ``` ### Step 2: Enable the Java Compiler `-parameters` flag (optional) If you want to use parameter names for process variables without specifying annotation values, enable the Java compiler flag `-parameters`. **Maven:** ```xml org.apache.maven.plugins maven-compiler-plugin -parameters ``` If you are using Gradle: ```xml tasks.withType(JavaCompile) { options.compilerArgs << '-parameters' } ``` If you are using IntelliJ: ```agsl Settings > Build, Execution, Deployment > Compiler > Java Compiler ``` ### Step 3a: Configure the Orchestration Cluster connection for Self-Managed Set up your connection and authentication in `application.yaml` as shown below. Choose the mode and authentication method for your environment. Choose the authentication method and gRPC/REST address for your environment: By default, no authentication will be used. ```yaml camunda: client: mode: self-managed auth: method: none grpc-address: https://my-grpc-address rest-address: https://my-rest-address ``` To activate basic authentication: ```yaml camunda: client: mode: self-managed auth: method: basic username: password: grpc-address: https://my-grpc-address rest-address: https://my-rest-address ``` If you set up a [Self-Managed cluster with OIDC](/self-managed/deployment/helm/configure/authentication-and-authorization/index.md), you must configure the accompanying client credentials: ```yaml camunda: client: mode: self-managed auth: method: oidc client-id: client-secret: issuer-url: http://localhost:18080/auth/realms/camunda-platform audience: scope: grpc-address: https://my-grpc-address rest-address: https://my-rest-address ``` :::note Ensure all addresses use absolute URI format: `scheme://host(:port)`. ::: **Notes for Microsoft Entra ID** - Use `scope: CLIENT_ID_OC + "/.default"` instead of `scope: CLIENT_ID_OC`. - The `issuer-url` is typically in the format: ``` https://login.microsoftonline.com//v2.0 ``` :::note Audience validation If you have [configured the audiences property for the Orchestration Cluster (`camunda.security.authentication.oidc.audiences`)](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#camunda.security.authentication.oidc), the Orchestration Cluster will validate the audience claim in the token against the configured audiences. Make sure your token includes the correct audience from the Orchestration Cluster configuration, or add your audience to the configuration. Often this is the client ID you used when setting up the Orchestration Cluster. ::: ### Step 3b: Configure the Orchestration Cluster connection for SaaS Set up your connection and authentication in `application.yaml` as shown below: ```yaml camunda: client: mode: saas auth: client-id: client-secret: cloud: cluster-id: region: ``` ## Start building your process application With your project configured, you are ready to build your process application. Below are the core operations you’ll typically perform, along with guidance on the next steps. ### Inject the Camunda client You can inject the Camunda client and work with it to create new workflow instances, for example: ```java @Autowired private CamundaClient client; ``` ## Implement the job worker Declare a method like this on a bean: ```java @JobWorker(type = "foo") public void handleJobFoo() { // do whatever you need to do } ``` To learn about all options you have with job workers, check out the [configuration](./configuration.md#job-worker-configuration-options) page. ## Deploy process models To deploy process models on application start-up, use the `@Deployment` annotation: ```java @SpringBootApplication @Deployment(resources = "classpath:demoProcess.bpmn") public class MySpringBootApplication { ``` To learn about all options about the usage of the `@Deployment` annotation, check out the [configuration](./configuration.md#deploying-resources-on-start-up) page. **Need help?** - [Camunda Community Forum](https://forum.camunda.io/) – Get help from the community. - [GitHub repository](https://github.com/camunda/camunda) – Report issues and contribute. --- ## Properties reference Properties for the Camunda Spring Boot Starter. ## Properties ### `camunda.client` Properties for the Camunda client. Property Description Default value The path to a root Certificate Authority (CA) certificate to use instead of the certificate in the default store. Type: string null Enable or disable the Camunda client. If disabled, the client bean is not created. Type: boolean true The number of threads for invocation of job workers. Type: integer 1 The gRPC address of Camunda that the client can connect to. The address must be an absolute URL, including the scheme. An alternative default is set by both `camunda.client.mode`. Type: url "http://0.0.0.0:26500" The time interval between keep-alive messages sent to the gateway. Type: duration null The maximum number of concurrent HTTP connections the client can open. Type: integer 100 A custom `maxMessageSize` sets the maximum inbound message size the client can receive from Camunda. It specifies the `maxInboundMessageSize` of the gRPC channel. Type: dataSize "5MB" A custom `maxMetadataSize` sets the maximum inbound metadata size the client can receive from Camunda. It specifies the `maxInboundMetadataSize` of the gRPC channel. Type: dataSize "16KB" The default time-to-live for a message when no value is provided. Type: duration "PT1H" The client mode to use. If not set, `saas` mode is detected based on the presence of a `camunda.client.cloud.cluster-id`. Type: enum[self-managed, saas] null Overrides the authority used with TLS virtual hosting to change hostname verification during the TLS handshake. It does not change the actual host connected to. Type: string null If `true`, prefers REST over gRPC for operations supported by both protocols. Type: boolean true The request timeout to use when not overridden by a specific command. Type: duration "PT10S" The request timeout client offset applies to commands that also pass the request timeout to the server. It ensures the client timeout occurs after the server timeout. For these commands, the client-side timeout equals the request timeout plus the offset. Type: duration "PT1S" The REST API address of the Camunda instance that the client can connect to. The address must be an absolute URL, including the scheme. An alternative default is set by both `camunda.client.mode`. Type: url "http://0.0.0.0:8080" The tenant ID used for tenant-aware commands when no tenant ID is set. Type: string "<default>" If `true`, enables client-side load balancing by using DNS-based resolution and distributing requests across all resolved addresses. Useful for setups without an external load balancer, such as Docker Compose, Testcontainers, or Kubernetes headless services. Type: boolean false ### `camunda.client.auth` Properties for authenticating the Camunda client. Property Description Default value The resource for which the access token must be valid. A default is set by `camunda.client.mode: saas` and `camunda.client.auth.method: oidc`. Type: string null The client ID to use when requesting an access token from the OAuth authorization server. Type: string null The client secret to use when requesting an access token from the OAuth authorization server. Type: string null The connection timeout for requests to the OAuth credentials provider. Type: duration "PT5S" The path to the credentials cache file. If unset or empty, the OAuth provider caches credentials only in memory and does not persist them across restarts. Set this to a writable path to opt in to persistent file-based caching. See issue #13124. Type: string null The url of the issuer for the access token. It is used to generate the well-known configuration url from which the `token-url` is retrieved. Only applied if the `camunda.client.auth.well-known-configuration-url` is not set. A default is set by `camunda.client.auth.method: oidc`. Type: url null The keystore key password for the OAuth identity provider. Type: string null The keystore password for the OAuth identity provider. Type: string null The path to the keystore for the OAuth identity provider. Type: file null The authentication method to use. If not set, it is detected based on the presence of a username, password, client ID, and client secret. A default is set by `camunda.client.mode: saas`. Type: enum[none, basic, oidc] null The password to be use for basic authentication. A default is set by `camunda.client.auth.method: basic`. Type: string null The lead time before actual token expiry at which a background refresh is triggered. The token is still considered valid inside this window; this is a policy knob for how early refresh kicks in so callers don't have to block on a synchronous refresh at the cliff edge. Must be strictly larger than the internal expiry grace period. Type: duration null The data read timeout for requests to the OAuth credentials provider. Type: duration "PT5S" The resource for which the access token must be valid. Type: string null The scopes of the access token. Type: string null The multiplier applied to the backoff duration between successive token fetch retry attempts. Must be greater than or equal to 1.0. Type: double null The initial backoff duration applied between token fetch retry attempts. Subsequent delays grow geometrically by `token-fetch-backoff-multiplier`. Type: duration null The maximum number of attempts (including the initial one) when fetching a token from the OAuth authorization server. Retries are only attempted on IOException or HTTP status codes configured via `token-fetch-retryable-status-codes`. Type: integer null Duration for which token fetches fail fast after the token endpoint returns a non-retryable response. After the cooldown elapses, the next call retries; if it fails again non-retryably, the latch re-arms with a new cooldown. Set to Duration.ZERO to disable the cooldown entirely. Type: duration null The set of HTTP status codes from the token endpoint that should be retried with backoff. Any non-200 status code outside this set trips a non-retryable failure latch that fails fast for the duration of tokenFetchNonRetryableCooldown. Type: array[integer] null The authorization server URL from which to request the access token. A default is set by `camunda.client.mode: saas`. Type: url null The truststore password for the OAuth identity provider. Type: string null The path to the truststore for the OAuth identity provider. Type: file null The username to use for basic authentication. A default is set by `camunda.client.auth.method: basic`. Type: string null The url of the well-known configuration of the issuer. It is used to retrieve the `token-url`. Only applied if `camunda.client.auth.token-url` is not set. Type: url null ### `camunda.client.auth.client-assertion` Properties for OIDC authentication using a client assertion instead of a client secret. Property Description Default value The alias of the key containing the certificate used to sign the client assertion certificate. If not set, the first alias from the keystore is used. Type: string null The password of the key referenced by the alias. If not set, the keystore password is used. Type: string null The password of the referenced keystore. Type: string null The path to the keystore where the client assertion certificate is stored. Type: file null ### `camunda.client.cloud` Properties for connecting the Camunda client to SaaS. These are used to compose default connection details when the client is configured to `camunda.client.mode: saas`. Property Description Default value The cluster ID the Camunda client connects to. Type: string null The domain the Camunda client connects to. Change this to connect to a non-production instance of Camunda Cloud. Type: string null The port the Camunda client connects to. Type: integer null The region the Camunda client connects to. Type: string null ### `camunda.client.cluster-variables` Properties for setting cluster variables at startup. Property Description Default value Indicates if the `@ClusterVariables` annotation is processed and configured variables are applied. Type: boolean true Cluster variables to set at startup as key-value pairs. Type: map[string,object] null ### `camunda.client.deployment` Properties for automatic deployment at startup. Property Description Default value Indicates if the `@Deployment` annotation is processed. Type: boolean true Indicates if the resources selected by the deployment annotation have to reside in the same jar as the annotated class. This property acts as the default behavior. If the `@Deployment` annotation explicitly sets its `ownJarOnly` parameter, that annotation-level value overrides this property for the annotated deployment. Type: boolean false ### `camunda.client.worker.defaults` Global default properties for job workers registered to the Camunda client. Property Description Default value Enable or disable automatic job completion after method invocation. Type: boolean true Enable or disable the job worker. Type: boolean true List of variable names to fetch on job activation. When set in defaults, it extends the list of variables to fetch from the annotation. When set in an override, it replaces the list of variables to fetch. Type: array[string] null Sets whether all variables are fetched. Overrides `fetch-variables`. Type: boolean false The maximum number of jobs exclusively activated for this worker at the same time. Type: integer 32 The maximum number of retries before automatic responses (complete, fail, bpmn error) for jobs are no longer attempted. Type: integer 0 The name of the worker owner. If set to default, it is generated as `${beanName}#${methodName}`. Type: string "default" The maximal interval between polls for new jobs. Type: duration "PT0.1S" The request timeout for the activate job request used to poll for new jobs. Type: duration "PT10S" The backoff before a retry of a failed job is possible. Type: duration "PT0S" Opt-in feature flag that enables job streaming. When enabled, the job worker uses both streaming and polling to activate jobs. A long-lived stream eagerly pushes new jobs, and polling retrieves jobs created before any streams were opened. Type: boolean false If streaming is enabled, sets the maximum duration the worker will wait without receiving any job on the open stream before cancelling and recreating it. The timer is reset every time a job is received. Must be strictly less than `stream-timeout` when both are set. Type: duration "PT10M" If streaming is enabled, sets the maximum lifetime for a stream. When this timeout is reached, the stream closes, and no more jobs are activated or received. If the worker is still open, a new stream opens immediately. Type: duration "PT8H" Sets the tenant filter for the job worker, which determines how the worker considers tenant IDs when activating jobs. Type: enum[assigned, provided] "PROVIDED" Sets the tenants for which the job worker is registered. When set in defaults, it extends the list of tenant IDs from the annotation. When set in override, it replaces the list of tenant IDs. Type: array[string] ["<default>"] The time a job remains exclusively assigned to the worker. Type: duration "PT5M" The type of jobs to work on. Type: string null ### `management.endpoint.jobworkers` Properties for configuring the `jobworkers` management endpoint. Property Description Default value Permitted level of access for the jobworkers endpoint. Type: enum[none, read_only, unrestricted] "unrestricted" Maximum time that a response can be cached. Type: duration "0ms" ### `camunda.client.worker.override` Properties for overriding settings of individual job workers registered to the Camunda client. The key of the override is the job type or worker name. Property Description Default value Enable or disable automatic job completion after method invocation. Type: boolean null Enable or disable the job worker. Type: boolean null List of variable names to fetch on job activation. When set in defaults, it extends the list of variables to fetch from the annotation. When set in an override, it replaces the list of variables to fetch. Type: array[string] null Sets whether all variables are fetched. Overrides `fetch-variables`. Type: boolean null The maximum number of jobs exclusively activated for this worker at the same time. Type: integer null The maximum number of retries before automatic responses (complete, fail, bpmn error) for jobs are no longer attempted. Type: integer null The name of the worker owner. If set to default, it is generated as `${beanName}#${methodName}`. Type: string null The maximal interval between polls for new jobs. Type: duration null The request timeout for the activate job request used to poll for new jobs. Type: duration null The backoff before a retry of a failed job is possible. Type: duration null Opt-in feature flag that enables job streaming. When enabled, the job worker uses both streaming and polling to activate jobs. A long-lived stream eagerly pushes new jobs, and polling retrieves jobs created before any streams were opened. Type: boolean null If streaming is enabled, sets the maximum duration the worker will wait without receiving any job on the open stream before cancelling and recreating it. The timer is reset every time a job is received. Must be strictly less than `stream-timeout` when both are set. Type: duration null If streaming is enabled, sets the maximum lifetime for a stream. When this timeout is reached, the stream closes, and no more jobs are activated or received. If the worker is still open, a new stream opens immediately. Type: duration null Sets the tenant filter for the job worker, which determines how the worker considers tenant IDs when activating jobs. Type: enum[assigned, provided] null Sets the tenants for which the job worker is registered. When set in defaults, it extends the list of tenant IDs from the annotation. When set in override, it replaces the list of tenant IDs. Type: array[string] null The time a job remains exclusively assigned to the worker. Type: duration null The type of jobs to work on. Type: string null ## Deprecated properties :::caution The following properties are deprecated. See the replacement property and related hints. The deprecated properties are still effective if their replacement is not used yet. The SDK hints on the usage of deprecated properties by logging warn statements during startup. ::: ### `camunda.client` Deprecated properties for the Camunda client. Property Replacement Hint N/A N/A N/A ### `camunda.client.auth` Deprecated properties for authenticating the Camunda client. Property Replacement Hint N/A ### `camunda.client.cloud` Deprecated properties for connecting the Camunda client to SaaS. These are used to compose default connection details when the client is configured to `camunda.client.mode: saas`. Property Replacement Hint N/A ### `camunda.client.identity` Deprecated properties for identity settings. Property Replacement Hint Identity is now part of Camunda. Identity is now part of Camunda. Identity is now part of Camunda. Identity is now part of Camunda. ### `camunda.client.zeebe` Deprecated properties for Zeebe client settings. Property Replacement Hint N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A ### `camunda.client.zeebe.defaults` Deprecated default properties for Zeebe job workers. Property Replacement Hint N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A ### `camunda.client.zeebe.deployment` Deprecated deployment properties for Zeebe. Property Replacement Hint N/A ### `camunda.client.zeebe.override` Deprecated properties for overriding individual job workers registered to the Camunda client. Replaced by `camunda.client.worker.override`. ### `common` Deprecated common client properties. Property Replacement Hint N/A N/A N/A N/A N/A N/A The REST address is the unified endpoint for all interaction with Camunda. N/A ### `common.keycloak` Deprecated Keycloak-specific properties. Property Replacement Hint There is no keycloak-specific configuration for Camunda; the issuer is provided as a URL. There is no keycloak-specific configuration for Camunda; the issuer is provided as a URL. There is no keycloak-specific configuration for Camunda; the issuer is provided as a URL. ### `zeebe.client` Deprecated Zeebe client properties. Property Replacement Hint Only the environment variables belonging to the Spring SDK are applied. Client modes are now available. N/A N/A N/A N/A N/A ### `zeebe.client.broker` Deprecated Zeebe broker properties. Property Replacement Hint N/A N/A N/A N/A ### `zeebe.client.cloud` Deprecated Zeebe cloud connection properties. Property Replacement Hint N/A N/A N/A N/A N/A N/A The Zeebe client URL is now configured as HTTP/HTTPS URL. N/A N/A ### `zeebe.client.job` Deprecated Zeebe job worker properties. Property Replacement Hint N/A N/A ### `zeebe.client.message` Deprecated Zeebe message properties. Property Replacement Hint N/A N/A ### `zeebe.client.security` Deprecated Zeebe security properties. Property Replacement Hint N/A N/A plaintext is now determined by the URL protocol (HTTP or HTTPS). ### `zeebe.client.worker` Deprecated Zeebe job worker properties. Property Replacement Hint N/A N/A N/A N/A ### `zeebe.client.worker.override` Deprecated properties to override the individual job workers registered with the Camunda client. Replaced by `camunda.client.worker.override`. --- ## Community-supported component clients :::note Camunda extensions found in the [Camunda Community Hub](https://github.com/camunda-community-hub) are maintained by the community and are not part of the commercial Camunda product. Camunda does not support community extensions as part of its commercial services to enterprise customers. Please evaluate each client to make sure it meets your requirements before using. ::: :::tip Camunda now officially supports the [TypeScript SDK](/apis-tools/typescript/typescript-sdk.md) and the [Camunda Spring Boot Starter](/apis-tools/camunda-spring-boot-starter/getting-started.md). ::: In addition to the core Camunda-maintained clients, there are a number of community-maintained component libraries: - [Ballerina](https://github.com/camunda-community-hub/ballerina-zeebe) - [C#](https://github.com/camunda-community-hub/zeebe-client-csharp) - [CLI](https://github.com/camunda-community-hub/zeebe-client-go/blob/main/cmd/zbctl/zbctl.md) - [Delphi](https://github.com/camunda-community-hub/DelphiZeeBeClient) - [EJB](https://github.com/camunda-community-hub/zeebe-ejb-client) - [Go](https://github.com/camunda-community-hub/zeebe-client-go) - [Micronaut](https://github.com/camunda-community-hub/micronaut-zeebe-client) - [Python](https://gitlab.com/stephane.ludwig/zeebe_python_grpc) - [Quarkus](https://github.com/quarkiverse/quarkus-zeebe) - [Ruby](https://github.com/zeebe-io/zeebe-client-ruby) - [Rust](https://github.com/camunda-community-hub/zeebest) - [.NET](https://github.com/camunda-community-hub/dotnet-custom-tasklist) - [Java](https://github.com/camunda-community-hub/camunda-tasklist-client-java) - [Java](https://github.com/camunda-community-hub/camunda-operate-client-java) - [Web Modeler - Java](https://github.com/camunda-community-hub/web-modeler-java-client) - [Console - Go](https://github.com/camunda-community-hub/console-customer-api-go) --- ## CamundaClient :::caution Technical Preview The C# SDK is a **technical preview** available from Camunda 8.9. It will become fully supported in Camunda 8.10. Its API surface may change in future releases without following semver. ::: ## Creating a Client Factory method for creating CamundaClient instances. ```csharp public static CamundaClient CreateClient(CamundaOptions? options = null) ``` Create a new CamundaClient. | Parameter | Type | Description | | --------- | ---------------- | ----------- | | `options` | `CamundaOptions` | | ## Dependency Injection Extension methods for registering in an . ### AddCamundaClient(IServiceCollection) ```csharp public static IServiceCollection AddCamundaClient(this IServiceCollection services) ``` Registers a singleton using zero-config (environment variables only). | Parameter | Type | Description | | ---------- | -------------------- | ----------- | | `services` | `IServiceCollection` | | ### AddCamundaClient(IServiceCollection, IConfiguration) ```csharp public static IServiceCollection AddCamundaClient(this IServiceCollection services, IConfiguration configurationSection) ``` Registers a singleton using an section. Typically called as services.AddCamundaClient(configuration.GetSection("Camunda")). PascalCase keys in the section are mapped to canonical CAMUNDA\_\* env-var names internally. Environment variables still apply as a base layer; section values override them. | Parameter | Type | Description | | ---------------------- | -------------------- | ----------- | | `services` | `IServiceCollection` | | | `configurationSection` | `IConfiguration` | | ### AddCamundaClient(IServiceCollection, Action) ```csharp public static IServiceCollection AddCamundaClient(this IServiceCollection services, Action configure) ``` Registers a singleton with an options callback for full control. | Parameter | Type | Description | | ----------- | ------------------------ | ----------- | | `services` | `IServiceCollection` | | | `configure` | `Action` | | ## Overview Primary Camunda client. Provides typed methods for all Camunda 8 REST API operations. Auto-generated operation methods are added in the Generated/ partial class files. This class provides the infrastructure: configuration, auth, retry, backpressure. ```csharp public class CamundaClient : IDisposable, IAsyncDisposable ``` ## Constructor ```csharp public CamundaClient(CamundaOptions? options = null) ``` Create a new CamundaClient with the given options. | Parameter | Type | Description | | --------- | ---------------- | ----------- | | `options` | `CamundaOptions` | | ## Properties | Property | Type | Description | | -------- | --------------- | ----------------------------------------------- | | `Config` | `CamundaConfig` | The current hydrated configuration (read-only). | ## Methods ### Other #### Create(CamundaOptions?) ```csharp public static CamundaClient Create(CamundaOptions? options = null) ``` Create a new CamundaClient. | Parameter | Type | Description | | --------- | ---------------- | ----------- | | `options` | `CamundaOptions` | | **Returns:** `CamundaClient` #### Dispose() ```csharp public void Dispose() ``` Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. #### DisposeAsync() ```csharp public ValueTask DisposeAsync() ``` Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources asynchronously. **Returns:** `ValueTask` — A task that represents the asynchronous dispose operation. #### CreateAdminUserAsync(UserRequest, CancellationToken) ```csharp public Task CreateAdminUserAsync(UserRequest body, CancellationToken ct = default) ``` Create admin user Creates a new user and assigns the admin role to it. This endpoint is only usable when users are managed in the Orchestration Cluster and while no user is assigned to the admin role. | Parameter | Type | Description | | --------- | ------------------- | ----------- | | `body` | `UserRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CreateAdminUserExample(Username username) { using var client = CamundaClient.Create(); var result = await client.CreateAdminUserAsync(new UserRequest { Username = username, Name = "Admin User", Email = "admin@example.com", Password = "admin-password", }); Console.WriteLine($"Admin user key: {result.Username}"); } ``` #### CreateAgentInstanceAsync(AgentInstanceCreationRequest, CancellationToken) ```csharp public Task CreateAgentInstanceAsync(AgentInstanceCreationRequest body, CancellationToken ct = default) ``` Create agent instance Creates a new agent instance. The returned key identifies the instance and must be used in subsequent update and query calls. | Parameter | Type | Description | | --------- | ------------------------------ | ----------- | | `body` | `AgentInstanceCreationRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CreateAgentInstanceExample(ElementInstanceKey elementInstanceKey) { using var client = CamundaClient.Create(); var result = await client.CreateAgentInstanceAsync(new AgentInstanceCreationRequest { ElementInstanceKey = elementInstanceKey, Definition = new AgentInstanceDefinition { Model = "gpt-4o", Provider = "openai", SystemPrompt = "You are a helpful assistant.", }, }); Console.WriteLine($"Created agent instance: {result.AgentInstanceKey}"); } ``` #### CreateGlobalTaskListenerAsync(CreateGlobalTaskListenerRequest, CancellationToken) ```csharp public Task CreateGlobalTaskListenerAsync(CreateGlobalTaskListenerRequest body, CancellationToken ct = default) ``` Create global user task listener Create a new global user task listener. | Parameter | Type | Description | | --------- | --------------------------------- | ----------- | | `body` | `CreateGlobalTaskListenerRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CreateGlobalTaskListenerExample(GlobalListenerId id) { using var client = CamundaClient.Create(); var result = await client.CreateGlobalTaskListenerAsync( new CreateGlobalTaskListenerRequest { EventTypes = new List { GlobalTaskListenerEventTypeEnum.Completing }, Id = id, }); Console.WriteLine($"Task listener: {result.Id}"); } ``` #### CreateUserAsync(UserRequest, CancellationToken) ```csharp public Task CreateUserAsync(UserRequest body, CancellationToken ct = default) ``` Create user Create a new user. | Parameter | Type | Description | | --------- | ------------------- | ----------- | | `body` | `UserRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CreateUserExample(Username username) { using var client = CamundaClient.Create(); var result = await client.CreateUserAsync(new UserRequest { Username = username, Name = "Jane Doe", Email = "jdoe@example.com", Password = "secure-password", }); Console.WriteLine($"User key: {result.Username}"); } ``` #### DeleteGlobalTaskListenerAsync(GlobalListenerId, CancellationToken) ```csharp public Task DeleteGlobalTaskListenerAsync(GlobalListenerId id, CancellationToken ct = default) ``` Delete global user task listener Deletes a global user task listener. | Parameter | Type | Description | | --------- | ------------------- | ----------- | | `id` | `GlobalListenerId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task DeleteGlobalTaskListenerExample(GlobalListenerId globalListenerId) { using var client = CamundaClient.Create(); await client.DeleteGlobalTaskListenerAsync( globalListenerId); } ``` #### DeleteUserAsync(Username, CancellationToken) ```csharp public Task DeleteUserAsync(Username username, CancellationToken ct = default) ``` Delete user Deletes a user. | Parameter | Type | Description | | ---------- | ------------------- | ----------- | | `username` | `Username` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task DeleteUserExample(Username username) { using var client = CamundaClient.Create(); await client.DeleteUserAsync(username); } ``` #### EvaluateConditionalsAsync(ConditionalEvaluationInstruction, CancellationToken) ```csharp public Task EvaluateConditionalsAsync(ConditionalEvaluationInstruction body, CancellationToken ct = default) ``` Evaluate root level conditional start events Evaluates root-level conditional start events for process definitions. If the evaluation is successful, it will return the keys of all created process instances, along with their associated process definition key. Multiple root-level conditional start events of the same process definition can trigger if their conditions evaluate to true. | Parameter | Type | Description | | --------- | ---------------------------------- | ----------- | | `body` | `ConditionalEvaluationInstruction` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task EvaluateConditionalsExample() { using var client = CamundaClient.Create(); var result = await client.EvaluateConditionalsAsync( new ConditionalEvaluationInstruction()); Console.WriteLine($"Result: {result}"); } ``` #### EvaluateExpressionAsync(ExpressionEvaluationRequest, CancellationToken) ```csharp public Task EvaluateExpressionAsync(ExpressionEvaluationRequest body, CancellationToken ct = default) ``` Evaluate an expression Evaluates a FEEL expression and returns the result. Supports references to tenant scoped cluster variables when a tenant ID is provided. | Parameter | Type | Description | | --------- | ----------------------------- | ----------- | | `body` | `ExpressionEvaluationRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task EvaluateExpressionExample() { using var client = CamundaClient.Create(); var result = await client.EvaluateExpressionAsync( new ExpressionEvaluationRequest { Expression = "= 1 + 2", }); Console.WriteLine($"Result: {result.Result}"); } ``` #### GetAgentInstanceAsync(AgentInstanceKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetAgentInstanceAsync(AgentInstanceKey agentInstanceKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get agent instance Returns agent instance as JSON. | Parameter | Type | Description | | ------------------ | ----------------------------------------- | ----------- | | `agentInstanceKey` | `AgentInstanceKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetAgentInstanceExample(AgentInstanceKey agentInstanceKey) { using var client = CamundaClient.Create(); var result = await client.GetAgentInstanceAsync(agentInstanceKey); Console.WriteLine($"Agent instance: {result.AgentInstanceKey}, status: {result.Status}"); } ``` #### GetFormByKeyAsync(FormKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetFormByKeyAsync(FormKey formKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get form by key Get a form by its unique form key. | Parameter | Type | Description | | ------------- | -------------------------------- | ----------- | | `formKey` | `FormKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetFormByKeyExample(FormKey formKey) { using var client = CamundaClient.Create(); var result = await client.GetFormByKeyAsync(formKey); Console.WriteLine($"Form: {result.FormId}, version: {result.Version}"); } ``` #### GetGlobalTaskListenerAsync(GlobalListenerId, ConsistencyOptions?, CancellationToken) ```csharp public Task GetGlobalTaskListenerAsync(GlobalListenerId id, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get global user task listener Get a global user task listener by its id. | Parameter | Type | Description | | ------------- | ---------------------------------------------- | ----------- | | `id` | `GlobalListenerId` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetGlobalTaskListenerExample(GlobalListenerId globalListenerId) { using var client = CamundaClient.Create(); var result = await client.GetGlobalTaskListenerAsync( globalListenerId); Console.WriteLine($"Task listener: {result.EventTypes}"); } ``` #### GetStatusAsync(CancellationToken) ```csharp public Task GetStatusAsync(CancellationToken ct = default) ``` Get cluster status Checks the health status of the cluster by verifying if there's at least one partition with a healthy leader. | Parameter | Type | Description | | --------- | ------------------- | ----------- | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetStatusExample() { using var client = CamundaClient.Create(); await client.GetStatusAsync(); Console.WriteLine("Cluster is healthy"); } ``` #### GetSystemConfigurationAsync(CancellationToken) ```csharp public Task GetSystemConfigurationAsync(CancellationToken ct = default) ``` System configuration (alpha) Returns the current system configuration. The response is an envelope that groups settings by feature area. This endpoint is an alpha feature and may be subject to change in future releases. | Parameter | Type | Description | | --------- | ------------------- | ----------- | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetSystemConfigurationExample() { using var client = CamundaClient.Create(); var result = await client.GetSystemConfigurationAsync(); Console.WriteLine($"System config: {result}"); } ``` #### GetUserAsync(Username, ConsistencyOptions?, CancellationToken) ```csharp public Task GetUserAsync(Username username, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get user Get a user by its username. | Parameter | Type | Description | | ------------- | -------------------------------- | ----------- | | `username` | `Username` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### SearchAgentInstancesAsync(AgentInstanceSearchQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchAgentInstancesAsync(AgentInstanceSearchQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search agent instances Search for agent instances based on given criteria. | Parameter | Type | Description | | ------------- | ---------------------------------------------------- | ----------- | | `body` | `AgentInstanceSearchQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchAgentInstancesExample() { using var client = CamundaClient.Create(); var result = await client.SearchAgentInstancesAsync(new AgentInstanceSearchQuery()); foreach (var instance in result.Items) { Console.WriteLine($"Agent instance: {instance.AgentInstanceKey}, status: {instance.Status}"); } } ``` #### SearchGlobalTaskListenersAsync(GlobalTaskListenerSearchQueryRequest, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchGlobalTaskListenersAsync(GlobalTaskListenerSearchQueryRequest body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search global user task listeners Search for global user task listeners based on given criteria. | Parameter | Type | Description | | ------------- | --------------------------------------------------------- | ----------- | | `body` | `GlobalTaskListenerSearchQueryRequest` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchGlobalTaskListenersExample() { using var client = CamundaClient.Create(); var result = await client.SearchGlobalTaskListenersAsync( new GlobalTaskListenerSearchQueryRequest()); foreach (var listener in result.Items) { Console.WriteLine($"Listener: {listener.Id}"); } } ``` #### SearchUsersAsync(UserSearchQueryRequest, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchUsersAsync(UserSearchQueryRequest body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search users Search for users based on given criteria. | Parameter | Type | Description | | ------------- | -------------------------------------- | ----------- | | `body` | `UserSearchQueryRequest` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### UpdateAgentInstanceAsync(AgentInstanceKey, AgentInstanceUpdateRequest, CancellationToken) ```csharp public Task UpdateAgentInstanceAsync(AgentInstanceKey agentInstanceKey, AgentInstanceUpdateRequest body, CancellationToken ct = default) ``` Update agent instance Updates the mutable fields of an agent instance: status, metric counters, and tools. Metric values are treated as deltas and applied immediately to the aggregate counters. Tool updates replace the existing tool list. At least one of status, metrics, or tools must be provided. | Parameter | Type | Description | | ------------------ | ---------------------------- | ----------- | | `agentInstanceKey` | `AgentInstanceKey` | | | `body` | `AgentInstanceUpdateRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task UpdateAgentInstanceExample(AgentInstanceKey agentInstanceKey) { using var client = CamundaClient.Create(); await client.UpdateAgentInstanceAsync( agentInstanceKey, new AgentInstanceUpdateRequest { Status = AgentInstanceStatusEnum.THINKING, Metrics = new AgentInstanceMetricsDelta { InputTokens = 150, OutputTokens = 50, ModelCalls = 1, }, }); Console.WriteLine($"Updated agent instance: {agentInstanceKey}"); } ``` #### UpdateGlobalTaskListenerAsync(GlobalListenerId, UpdateGlobalTaskListenerRequest, CancellationToken) ```csharp public Task UpdateGlobalTaskListenerAsync(GlobalListenerId id, UpdateGlobalTaskListenerRequest body, CancellationToken ct = default) ``` Update global user task listener Updates a global user task listener. | Parameter | Type | Description | | --------- | --------------------------------- | ----------- | | `id` | `GlobalListenerId` | | | `body` | `UpdateGlobalTaskListenerRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task UpdateGlobalTaskListenerExample(GlobalListenerId globalListenerId) { using var client = CamundaClient.Create(); var result = await client.UpdateGlobalTaskListenerAsync( globalListenerId, new UpdateGlobalTaskListenerRequest { EventTypes = new List { GlobalTaskListenerEventTypeEnum.Completing }, Type = "updated-task-listener", }); Console.WriteLine($"Updated listener: {result.Id}"); } ``` #### UpdateUserAsync(Username, UserUpdateRequest, CancellationToken) ```csharp public Task UpdateUserAsync(Username username, UserUpdateRequest body, CancellationToken ct = default) ``` Update user Updates a user. | Parameter | Type | Description | | ---------- | ------------------- | ----------- | | `username` | `Username` | | | `body` | `UserUpdateRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task UpdateUserExample(Username username) { using var client = CamundaClient.Create(); await client.UpdateUserAsync( username, new UserUpdateRequest { Name = "Jane Smith", Email = "jsmith@example.com", }); } ``` ### Cluster #### GetBackpressureState() ```csharp public BackpressureState GetBackpressureState() ``` Current backpressure state snapshot. **Returns:** `BackpressureState` **Example** ```csharp public static void GetBackpressureStateExample() { using var client = CamundaClient.Create(); var state = client.GetBackpressureState(); Console.WriteLine($"Severity: {state.Severity}, Permits: {state.PermitsMax}"); } ``` #### GetAuthenticationAsync(CancellationToken) ```csharp public Task GetAuthenticationAsync(CancellationToken ct = default) ``` Get current user Retrieves the current authenticated user. | Parameter | Type | Description | | --------- | ------------------- | ----------- | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetAuthenticationExample() { using var client = CamundaClient.Create(); var result = await client.GetAuthenticationAsync(); Console.WriteLine($"Authenticated user: {result.Username}"); } ``` #### GetLicenseAsync(CancellationToken) ```csharp public Task GetLicenseAsync(CancellationToken ct = default) ``` Get license status Obtains the status of the current Camunda license. | Parameter | Type | Description | | --------- | ------------------- | ----------- | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetLicenseExample() { using var client = CamundaClient.Create(); var result = await client.GetLicenseAsync(); Console.WriteLine($"License type: {result.LicenseType}"); } ``` #### GetTopologyAsync(CancellationToken) ```csharp public Task GetTopologyAsync(CancellationToken ct = default) ``` Get cluster topology Obtains the current topology of the cluster the gateway is part of. | Parameter | Type | Description | | --------- | ------------------- | ----------- | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetTopologyExample() { using var client = CamundaClient.Create(); var topology = await client.GetTopologyAsync(); Console.WriteLine($"Cluster size: {topology.ClusterSize}"); } ``` #### PinClockAsync(ClockPinRequest, CancellationToken) ```csharp public Task PinClockAsync(ClockPinRequest body, CancellationToken ct = default) ``` Pin internal clock (alpha) Set a precise, static time for the Zeebe engine's internal clock. When the clock is pinned, it remains at the specified time and does not advance. To change the time, the clock must be pinned again with a new timestamp. This endpoint is an alpha feature and may be subject to change in future releases. | Parameter | Type | Description | | --------- | ------------------- | ----------- | | `body` | `ClockPinRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task PinClockExample() { using var client = CamundaClient.Create(); await client.PinClockAsync(new ClockPinRequest { Timestamp = 1700000000000, }); } ``` #### ResetClockAsync(CancellationToken) ```csharp public Task ResetClockAsync(CancellationToken ct = default) ``` Reset internal clock (alpha) Resets the Zeebe engine's internal clock to the current system time, enabling it to tick in real-time. This operation is useful for returning the clock to normal behavior after it has been pinned to a specific time. This endpoint is an alpha feature and may be subject to change in future releases. | Parameter | Type | Description | | --------- | ------------------- | ----------- | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task ResetClockExample() { using var client = CamundaClient.Create(); await client.ResetClockAsync(); } ``` ### Resources #### DeployResourcesFromFilesAsync(string[], string?, CancellationToken) ```csharp public Task DeployResourcesFromFilesAsync(string[] resourceFilePaths, string? tenantId = null, CancellationToken ct = default) ``` Deploy resources from local filesystem paths. Reads the specified files, infers MIME types from their extensions, and calls with the loaded content. | Parameter | Type | Description | | ------------------- | ------------------- | ---------------------------------------------------------------------- | | `resourceFilePaths` | `String[]` | Absolute or relative file paths to BPMN, DMN, form, or resource files. | | `tenantId` | `String` | Optional tenant ID for multi-tenant deployments. | | `ct` | `CancellationToken` | Cancellation token. | **Returns:** `Task` — An with typed access to deployed artifacts. **Example** ```csharp public static async Task DeployResourcesFromFilesExample() { using var client = CamundaClient.Create(); var result = await client.DeployResourcesFromFilesAsync( ["process.bpmn", "decision.dmn"]); Console.WriteLine($"Deployment key: {result.DeploymentKey}"); } ``` #### DeleteResourceAsync(ResourceKey, DeleteResourceRequest, CancellationToken) ```csharp public Task DeleteResourceAsync(ResourceKey resourceKey, DeleteResourceRequest body, CancellationToken ct = default) ``` Delete resource Deletes a deployed resource. This can be a process definition, decision requirements definition, or form definition deployed using the deploy resources endpoint. Specify the resource you want to delete in the `resourceKey` parameter. Once a resource has been deleted it cannot be recovered. If the resource needs to be available again, a new deployment of the resource is required. By default, only the resource itself is deleted from the runtime state. To also delete the historic data associated with a resource, set the `deleteHistory` flag in the request body to `true`. The historic data is deleted asynchronously via a batch operation. The details of the created batch operation are included in the response. Note that history deletion is only supported for process resources; for other resource types this flag is ignored and no history will be deleted. | Parameter | Type | Description | | ------------- | ----------------------- | ----------- | | `resourceKey` | `ResourceKey` | | | `body` | `DeleteResourceRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task DeleteResourceExample(ResourceKey resourceKey) { using var client = CamundaClient.Create(); await client.DeleteResourceAsync( resourceKey, new DeleteResourceRequest()); } ``` #### GetResourceAsync(ResourceKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetResourceAsync(ResourceKey resourceKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get resource Returns a deployed resource. :::info This endpoint does not return BPMN process definitions, DMN decision definitions, or form resources. To query BPMN process definitions or DMN decision definitions, use their respective APIs. ::: | Parameter | Type | Description | | ------------- | ------------------------------------ | ----------- | | `resourceKey` | `ResourceKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### GetResourceContentAsync(ResourceKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetResourceContentAsync(ResourceKey resourceKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get RPA resource content (deprecated) **Deprecated** — use `/resources/{resourceKey}/content/binary` instead, which supports all resource types and returns content as binary (octet-stream). Returns the content of a deployed RPA resource as JSON. :::info This endpoint only supports RPA resources. For generic resource content in binary format, use the `/resources/{resourceKey}/content/binary` endpoint. ::: | Parameter | Type | Description | | ------------- | ---------------------------- | ----------- | | `resourceKey` | `ResourceKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### GetResourceContentBinaryAsync(ResourceKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetResourceContentBinaryAsync(ResourceKey resourceKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get resource content as binary Returns the content of a deployed resource in binary format (octet-stream). :::info This endpoint does not return BPMN process definitions, DMN decision definitions, or form resources. To query BPMN process definitions or DMN decision definitions, use their respective APIs. ::: | Parameter | Type | Description | | ------------- | ---------------------------- | ----------- | | `resourceKey` | `ResourceKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetResourceContentBinaryExample(ResourceKey resourceKey) { using var client = CamundaClient.Create(); byte[] content = await client.GetResourceContentBinaryAsync(resourceKey); Console.WriteLine($"Binary content length: {content.Length} bytes"); } ``` #### SearchResourcesAsync(ResourceSearchQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchResourcesAsync(ResourceSearchQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search resources Search for deployed resources based on given criteria. :::info This endpoint does not return BPMN process definitions, DMN decision definitions, or form resources. To query BPMN process definitions or DMN decision definitions, use their respective search APIs. ::: | Parameter | Type | Description | | ------------- | ----------------------------------------------- | ----------- | | `body` | `ResourceSearchQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchResourcesExample() { using var client = CamundaClient.Create(); var result = await client.SearchResourcesAsync(new ResourceSearchQuery()); foreach (var resource in result.Items!) { Console.WriteLine($"Resource: {resource.ResourceName}"); } } ``` ### Jobs #### CreateJobWorker(JobWorkerConfig, JobHandler) ```csharp public JobWorker CreateJobWorker(JobWorkerConfig config, JobHandler handler) ``` Create a job worker that polls for and processes jobs of the specified type. The handler receives an and returns variables to auto-complete. Throw for BPMN errors, for explicit failures, or any other exception to auto-fail with retries - 1. | Parameter | Type | Description | | --------- | ----------------- | ------------------------------------------------------------------------------------- | | `config` | `JobWorkerConfig` | Worker configuration (job type, timeout, concurrency). | | `handler` | `JobHandler` | Async handler that processes each job. Return output variables (or null) to complete. | **Returns:** `JobWorker` — The running instance. **Example** ```csharp public static void CreateJobWorkerExample() { using var client = CamundaClient.Create(); var worker = client.CreateJobWorker( new JobWorkerConfig { JobType = "payment-service" }, async (job, ct) => { Console.WriteLine($"Processing job {job.JobKey}"); return new { Success = true }; }); } ``` #### CreateJobWorker(JobWorkerConfig, Func) ```csharp public JobWorker CreateJobWorker(JobWorkerConfig config, Func handler) ``` Create a job worker with a handler that doesn't return output variables. The job is auto-completed with no variables on success. | Parameter | Type | Description | | --------- | ----------------- | ----------- | | `config` | `JobWorkerConfig` | | | `handler` | `Func` | | **Returns:** `JobWorker` **Example** ```csharp public static void CreateJobWorkerExample() { using var client = CamundaClient.Create(); var worker = client.CreateJobWorker( new JobWorkerConfig { JobType = "payment-service" }, async (job, ct) => { Console.WriteLine($"Processing job {job.JobKey}"); return new { Success = true }; }); } ``` #### ActivateJobsAsync(JobActivationRequest, CancellationToken) ```csharp public Task ActivateJobsAsync(JobActivationRequest body, CancellationToken ct = default) ``` Activate jobs Iterate through all known partitions and activate jobs up to the requested maximum. | Parameter | Type | Description | | --------- | ---------------------- | ----------- | | `body` | `JobActivationRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task ActivateJobsExample() { using var client = CamundaClient.Create(); var result = await client.ActivateJobsAsync(new JobActivationRequest { Type = "my-job-type", MaxJobsToActivate = 10, Timeout = 300000, Worker = "my-worker", }); foreach (var job in result.Jobs) { Console.WriteLine($"Job: {job.JobKey}"); } } ``` #### CompleteJobAsync(JobKey, JobCompletionRequest, CancellationToken) ```csharp public Task CompleteJobAsync(JobKey jobKey, JobCompletionRequest body, CancellationToken ct = default) ``` Complete job Complete a job with the given payload, which allows completing the associated service task. | Parameter | Type | Description | | --------- | ---------------------- | ----------- | | `jobKey` | `JobKey` | | | `body` | `JobCompletionRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CompleteJobExample(JobKey jobKey) { using var client = CamundaClient.Create(); await client.CompleteJobAsync( jobKey, new JobCompletionRequest()); } ``` #### FailJobAsync(JobKey, JobFailRequest, CancellationToken) ```csharp public Task FailJobAsync(JobKey jobKey, JobFailRequest body, CancellationToken ct = default) ``` Fail job Mark the job as failed. | Parameter | Type | Description | | --------- | ------------------- | ----------- | | `jobKey` | `JobKey` | | | `body` | `JobFailRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task FailJobExample(JobKey jobKey) { using var client = CamundaClient.Create(); await client.FailJobAsync( jobKey, new JobFailRequest { Retries = 3, RetryBackOff = 5000, ErrorMessage = "Something went wrong", }); } ``` #### GetGlobalJobStatisticsAsync(DateTimeOffset, DateTimeOffset, string?, ConsistencyOptions?, CancellationToken) ```csharp public Task GetGlobalJobStatisticsAsync(DateTimeOffset from, DateTimeOffset to, string? jobType = null, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Global job statistics Returns global aggregated counts for jobs. Filter by the creation time window (required) and optionally by jobType. | Parameter | Type | Description | | ------------- | ---------------------------------------------------- | ----------- | | `from` | `DateTimeOffset` | | | `to` | `DateTimeOffset` | | | `jobType` | `String` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetGlobalJobStatisticsExample() { using var client = CamundaClient.Create(); var result = await client.GetGlobalJobStatisticsAsync( from: new DateTimeOffset(2024, 1, 1, 0, 0, 0, TimeSpan.Zero), to: new DateTimeOffset(2024, 12, 31, 23, 59, 59, TimeSpan.Zero)); Console.WriteLine($"Global job stats: {result}"); } ``` #### GetJobErrorStatisticsAsync(JobErrorStatisticsQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task GetJobErrorStatisticsAsync(JobErrorStatisticsQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get error metrics for a job type Returns aggregated metrics per error for the given jobType. | Parameter | Type | Description | | ------------- | --------------------------------------------------- | ----------- | | `body` | `JobErrorStatisticsQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetJobErrorStatisticsExample() { using var client = CamundaClient.Create(); var result = await client.GetJobErrorStatisticsAsync( new JobErrorStatisticsQuery()); foreach (var stat in result.Items) { Console.WriteLine($"Error: {stat.ErrorCode}"); } } ``` #### GetJobTimeSeriesStatisticsAsync(JobTimeSeriesStatisticsQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task GetJobTimeSeriesStatisticsAsync(JobTimeSeriesStatisticsQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get time-series metrics for a job type Returns a list of time-bucketed metrics ordered ascending by time. The `from` and `to` fields select the time window of interest. Each item in the response corresponds to one time bucket of the requested resolution. | Parameter | Type | Description | | ------------- | -------------------------------------------------------- | ----------- | | `body` | `JobTimeSeriesStatisticsQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetJobTimeSeriesStatisticsExample() { using var client = CamundaClient.Create(); var result = await client.GetJobTimeSeriesStatisticsAsync( new JobTimeSeriesStatisticsQuery()); foreach (var stat in result.Items) { Console.WriteLine($"Time series: {stat}"); } } ``` #### GetJobTypeStatisticsAsync(JobTypeStatisticsQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task GetJobTypeStatisticsAsync(JobTypeStatisticsQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get job statistics by type Get statistics about jobs, grouped by job type. | Parameter | Type | Description | | ------------- | -------------------------------------------------- | ----------- | | `body` | `JobTypeStatisticsQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetJobTypeStatisticsExample() { using var client = CamundaClient.Create(); var result = await client.GetJobTypeStatisticsAsync( new JobTypeStatisticsQuery()); foreach (var stat in result.Items) { Console.WriteLine($"Job type: {stat.JobType}"); } } ``` #### GetJobWorkerStatisticsAsync(JobWorkerStatisticsQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task GetJobWorkerStatisticsAsync(JobWorkerStatisticsQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get job statistics by worker Get statistics about jobs, grouped by worker, for a given job type. | Parameter | Type | Description | | ------------- | ---------------------------------------------------- | ----------- | | `body` | `JobWorkerStatisticsQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetJobWorkerStatisticsExample() { using var client = CamundaClient.Create(); var result = await client.GetJobWorkerStatisticsAsync( new JobWorkerStatisticsQuery()); foreach (var stat in result.Items) { Console.WriteLine($"Worker: {stat.Worker}"); } } ``` #### SearchJobsAsync(JobSearchQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchJobsAsync(JobSearchQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search jobs Search for jobs based on given criteria. | Parameter | Type | Description | | ------------- | ------------------------------------------ | ----------- | | `body` | `JobSearchQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchJobsExample() { using var client = CamundaClient.Create(); var result = await client.SearchJobsAsync(new JobSearchQuery()); foreach (var job in result.Items) { Console.WriteLine($"Job: {job.JobKey}"); } } ``` #### ThrowJobErrorAsync(JobKey, JobErrorRequest, CancellationToken) ```csharp public Task ThrowJobErrorAsync(JobKey jobKey, JobErrorRequest body, CancellationToken ct = default) ``` Throw error for job Reports a business error (i.e. non-technical) that occurs while processing a job. | Parameter | Type | Description | | --------- | ------------------- | ----------- | | `jobKey` | `JobKey` | | | `body` | `JobErrorRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task ThrowJobErrorExample(JobKey jobKey) { using var client = CamundaClient.Create(); await client.ThrowJobErrorAsync( jobKey, new JobErrorRequest { ErrorCode = "VALIDATION_ERROR", ErrorMessage = "Input validation failed", }); } ``` #### UpdateJobAsync(JobKey, JobUpdateRequest, CancellationToken) ```csharp public Task UpdateJobAsync(JobKey jobKey, JobUpdateRequest body, CancellationToken ct = default) ``` Update job Update a job with the given key. | Parameter | Type | Description | | --------- | ------------------- | ----------- | | `jobKey` | `JobKey` | | | `body` | `JobUpdateRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task UpdateJobExample(JobKey jobKey) { using var client = CamundaClient.Create(); await client.UpdateJobAsync( jobKey, new JobUpdateRequest { Changeset = new JobChangeset { Retries = 3 }, }); } ``` ### Job Workers #### RunWorkersAsync(TimeSpan?, CancellationToken) ```csharp public Task RunWorkersAsync(TimeSpan? gracePeriod = null, CancellationToken ct = default) ``` Block until cancellation is requested, keeping all registered workers alive. This is the typical entry point for worker-only applications. When the token is cancelled, all workers are stopped gracefully. | Parameter | Type | Description | | ------------- | -------------------- | ------------------------------------------------------------------------------- | | `gracePeriod` | `Nullable` | Time to wait for in-flight jobs to finish during shutdown. Default: 10 seconds. | | `ct` | `CancellationToken` | Cancellation token that signals shutdown. | **Returns:** `Task` **Example** ```csharp public static async Task RunWorkersExample(CancellationToken ct) { using var client = CamundaClient.Create(); client.CreateJobWorker( new JobWorkerConfig { JobType = "payment-service" }, async (job, jobCt) => { Console.WriteLine($"Processing job {job.JobKey}"); return null; }); await client.RunWorkersAsync(gracePeriod: TimeSpan.FromSeconds(10), ct); } ``` #### StopAllWorkersAsync(TimeSpan?) ```csharp public Task StopAllWorkersAsync(TimeSpan? gracePeriod = null) ``` Stop all registered workers and wait for in-flight jobs to drain. | Parameter | Type | Description | | ------------- | -------------------- | ----------- | | `gracePeriod` | `Nullable` | | **Returns:** `Task` **Example** ```csharp public static async Task StopAllWorkersExample() { using var client = CamundaClient.Create(); client.CreateJobWorker( new JobWorkerConfig { JobType = "payment-service" }, async (job, ct) => { Console.WriteLine($"Processing job {job.JobKey}"); return null; }); await client.StopAllWorkersAsync(gracePeriod: TimeSpan.FromSeconds(5)); } ``` #### GetWorkers() ```csharp public IReadOnlyList GetWorkers() ``` Returns a snapshot of all registered workers. **Returns:** `IReadOnlyList` **Example** ```csharp public static void GetWorkersExample() { using var client = CamundaClient.Create(); client.CreateJobWorker( new JobWorkerConfig { JobType = "payment-service" }, async (job, ct) => { Console.WriteLine($"Processing job {job.JobKey}"); return null; }); var workers = client.GetWorkers(); foreach (var worker in workers) { Console.WriteLine($"Worker: {worker.Name}, Active: {worker.ActiveJobs}"); } } ``` ### Elements #### ActivateAdHocSubProcessActivitiesAsync(ElementInstanceKey, AdHocSubProcessActivateActivitiesInstruction, CancellationToken) ```csharp public Task ActivateAdHocSubProcessActivitiesAsync(ElementInstanceKey adHocSubProcessInstanceKey, AdHocSubProcessActivateActivitiesInstruction body, CancellationToken ct = default) ``` Activate activities within an ad-hoc sub-process Activates selected activities within an ad-hoc sub-process identified by element ID. The provided element IDs must exist within the ad-hoc sub-process instance identified by the provided adHocSubProcessInstanceKey. | Parameter | Type | Description | | ---------------------------- | ---------------------------------------------- | ----------- | | `adHocSubProcessInstanceKey` | `ElementInstanceKey` | | | `body` | `AdHocSubProcessActivateActivitiesInstruction` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task ActivateAdHocSubProcessActivitiesExample(ElementInstanceKey elementInstanceKey) { using var client = CamundaClient.Create(); await client.ActivateAdHocSubProcessActivitiesAsync( elementInstanceKey, new AdHocSubProcessActivateActivitiesInstruction()); } ``` #### GetElementInstanceAsync(ElementInstanceKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetElementInstanceAsync(ElementInstanceKey elementInstanceKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get element instance Returns element instance as JSON. | Parameter | Type | Description | | -------------------- | ------------------------------------------- | ----------- | | `elementInstanceKey` | `ElementInstanceKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetElementInstanceExample(ElementInstanceKey elementInstanceKey) { using var client = CamundaClient.Create(); var result = await client.GetElementInstanceAsync( elementInstanceKey); Console.WriteLine($"Element: {result.ElementId}"); } ``` #### SearchElementInstancesAsync(ElementInstanceSearchQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchElementInstancesAsync(ElementInstanceSearchQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search element instances Search for element instances based on given criteria. | Parameter | Type | Description | | ------------- | ------------------------------------------------------ | ----------- | | `body` | `ElementInstanceSearchQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchElementInstancesExample() { using var client = CamundaClient.Create(); var result = await client.SearchElementInstancesAsync( new ElementInstanceSearchQuery()); foreach (var ei in result.Items) { Console.WriteLine($"Element instance: {ei.ElementInstanceKey}"); } } ``` ### Groups #### AssignClientToGroupAsync(GroupId, ClientId, CancellationToken) ```csharp public Task AssignClientToGroupAsync(GroupId groupId, ClientId clientId, CancellationToken ct = default) ``` Assign a client to a group Assigns a client to a group, making it a member of the group. Members of the group inherit the group authorizations, roles, and tenant assignments. | Parameter | Type | Description | | ---------- | ------------------- | ----------- | | `groupId` | `GroupId` | | | `clientId` | `ClientId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### AssignMappingRuleToGroupAsync(GroupId, MappingRuleId, CancellationToken) ```csharp public Task AssignMappingRuleToGroupAsync(GroupId groupId, MappingRuleId mappingRuleId, CancellationToken ct = default) ``` Assign a mapping rule to a group Assigns a mapping rule to a group. | Parameter | Type | Description | | --------------- | ------------------- | ----------- | | `groupId` | `GroupId` | | | `mappingRuleId` | `MappingRuleId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### AssignUserToGroupAsync(GroupId, Username, CancellationToken) ```csharp public Task AssignUserToGroupAsync(GroupId groupId, Username username, CancellationToken ct = default) ``` Assign a user to a group Assigns a user to a group, making the user a member of the group. Group members inherit the group authorizations, roles, and tenant assignments. | Parameter | Type | Description | | ---------- | ------------------- | ----------- | | `groupId` | `GroupId` | | | `username` | `Username` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### CreateGroupAsync(GroupCreateRequest, CancellationToken) ```csharp public Task CreateGroupAsync(GroupCreateRequest body, CancellationToken ct = default) ``` Create group Create a new group. The supplied `groupId` is validated against `^[a-zA-Z0-9_~@.+-]+$` (max 256 characters) by `IdentifierValidator.validateId` in the runtime. This strict validation applies wherever the Groups API is available: in OIDC deployments that set `camunda.security.authentication.oidc.groupsClaim` the Groups API (including this endpoint) is disabled entirely, so group CRUD never sees externally-minted IdP IDs. The BYOG relaxation only loosens validation when a group is referenced _as a member_ of a role or tenant (`assignRoleToGroup`, `assignGroupToTenant`); group CRUD itself always uses the strict default-id regex. The constraint is not advertised on the `GroupId` schema so that the same schema can be reused at member-reference sites without falsely rejecting externally-minted IdP group IDs there. | Parameter | Type | Description | | --------- | -------------------- | ----------- | | `body` | `GroupCreateRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CreateGroupExample(GroupId groupId) { using var client = CamundaClient.Create(); var result = await client.CreateGroupAsync(new GroupCreateRequest { GroupId = groupId, Name = "Engineering", }); Console.WriteLine($"Group key: {result.GroupId}"); } ``` #### DeleteGroupAsync(GroupId, CancellationToken) ```csharp public Task DeleteGroupAsync(GroupId groupId, CancellationToken ct = default) ``` Delete group Deletes the group with the given ID. | Parameter | Type | Description | | --------- | ------------------- | ----------- | | `groupId` | `GroupId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### GetGroupAsync(GroupId, ConsistencyOptions?, CancellationToken) ```csharp public Task GetGroupAsync(GroupId groupId, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get group Get a group by its ID. | Parameter | Type | Description | | ------------- | --------------------------------- | ----------- | | `groupId` | `GroupId` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### SearchClientsForGroupAsync(GroupId, GroupClientSearchQueryRequest, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchClientsForGroupAsync(GroupId groupId, GroupClientSearchQueryRequest body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search group clients Search clients assigned to a group. | Parameter | Type | Description | | ------------- | --------------------------------------------- | ----------- | | `groupId` | `GroupId` | | | `body` | `GroupClientSearchQueryRequest` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### SearchGroupsAsync(GroupSearchQueryRequest, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchGroupsAsync(GroupSearchQueryRequest body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search groups Search for groups based on given criteria. | Parameter | Type | Description | | ------------- | -------------------------------------------- | ----------- | | `body` | `GroupSearchQueryRequest` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchGroupsExample() { using var client = CamundaClient.Create(); var result = await client.SearchGroupsAsync(new GroupSearchQueryRequest()); foreach (var group in result.Items) { Console.WriteLine($"Group: {group.Name}"); } } ``` #### SearchMappingRulesForGroupAsync(GroupId, MappingRuleSearchQueryRequest, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchMappingRulesForGroupAsync(GroupId groupId, MappingRuleSearchQueryRequest body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search group mapping rules Search mapping rules assigned to a group. | Parameter | Type | Description | | ------------- | -------------------------------------------------- | ----------- | | `groupId` | `GroupId` | | | `body` | `MappingRuleSearchQueryRequest` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### SearchUsersForGroupAsync(GroupId, GroupUserSearchQueryRequest, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchUsersForGroupAsync(GroupId groupId, GroupUserSearchQueryRequest body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search group users Search users assigned to a group. | Parameter | Type | Description | | ------------- | ------------------------------------------- | ----------- | | `groupId` | `GroupId` | | | `body` | `GroupUserSearchQueryRequest` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### UnassignClientFromGroupAsync(GroupId, ClientId, CancellationToken) ```csharp public Task UnassignClientFromGroupAsync(GroupId groupId, ClientId clientId, CancellationToken ct = default) ``` Unassign a client from a group Unassigns a client from a group. The client is removed as a group member, with associated authorizations, roles, and tenant assignments no longer applied. | Parameter | Type | Description | | ---------- | ------------------- | ----------- | | `groupId` | `GroupId` | | | `clientId` | `ClientId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### UnassignMappingRuleFromGroupAsync(GroupId, MappingRuleId, CancellationToken) ```csharp public Task UnassignMappingRuleFromGroupAsync(GroupId groupId, MappingRuleId mappingRuleId, CancellationToken ct = default) ``` Unassign a mapping rule from a group Unassigns a mapping rule from a group. | Parameter | Type | Description | | --------------- | ------------------- | ----------- | | `groupId` | `GroupId` | | | `mappingRuleId` | `MappingRuleId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### UnassignUserFromGroupAsync(GroupId, Username, CancellationToken) ```csharp public Task UnassignUserFromGroupAsync(GroupId groupId, Username username, CancellationToken ct = default) ``` Unassign a user from a group Unassigns a user from a group. The user is removed as a group member, with associated authorizations, roles, and tenant assignments no longer applied. | Parameter | Type | Description | | ---------- | ------------------- | ----------- | | `groupId` | `GroupId` | | | `username` | `Username` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### UpdateGroupAsync(GroupId, GroupUpdateRequest, CancellationToken) ```csharp public Task UpdateGroupAsync(GroupId groupId, GroupUpdateRequest body, CancellationToken ct = default) ``` Update group Update a group with the given ID. | Parameter | Type | Description | | --------- | -------------------- | ----------- | | `groupId` | `GroupId` | | | `body` | `GroupUpdateRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` ### Tenants #### AssignClientToTenantAsync(TenantId, ClientId, CancellationToken) ```csharp public Task AssignClientToTenantAsync(TenantId tenantId, ClientId clientId, CancellationToken ct = default) ``` Assign a client to a tenant Assign the client to the specified tenant. The client can then access tenant data and perform authorized actions. | Parameter | Type | Description | | ---------- | ------------------- | ----------- | | `tenantId` | `TenantId` | | | `clientId` | `ClientId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### AssignGroupToTenantAsync(TenantId, GroupId, CancellationToken) ```csharp public Task AssignGroupToTenantAsync(TenantId tenantId, GroupId groupId, CancellationToken ct = default) ``` Assign a group to a tenant Assigns a group to a specified tenant. Group members (users, clients) can then access tenant data and perform authorized actions. | Parameter | Type | Description | | ---------- | ------------------- | ----------- | | `tenantId` | `TenantId` | | | `groupId` | `GroupId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### AssignMappingRuleToTenantAsync(TenantId, MappingRuleId, CancellationToken) ```csharp public Task AssignMappingRuleToTenantAsync(TenantId tenantId, MappingRuleId mappingRuleId, CancellationToken ct = default) ``` Assign a mapping rule to a tenant Assign a single mapping rule to a specified tenant. | Parameter | Type | Description | | --------------- | ------------------- | ----------- | | `tenantId` | `TenantId` | | | `mappingRuleId` | `MappingRuleId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### AssignRoleToTenantAsync(TenantId, RoleId, CancellationToken) ```csharp public Task AssignRoleToTenantAsync(TenantId tenantId, RoleId roleId, CancellationToken ct = default) ``` Assign a role to a tenant Assigns a role to a specified tenant. Users, Clients or Groups, that have the role assigned, will get access to the tenant's data and can perform actions according to their authorizations. | Parameter | Type | Description | | ---------- | ------------------- | ----------- | | `tenantId` | `TenantId` | | | `roleId` | `RoleId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### AssignUserToTenantAsync(TenantId, Username, CancellationToken) ```csharp public Task AssignUserToTenantAsync(TenantId tenantId, Username username, CancellationToken ct = default) ``` Assign a user to a tenant Assign a single user to a specified tenant. The user can then access tenant data and perform authorized actions. | Parameter | Type | Description | | ---------- | ------------------- | ----------- | | `tenantId` | `TenantId` | | | `username` | `Username` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task AssignUserToTenantExample(TenantId tenantId, Username username) { using var client = CamundaClient.Create(); await client.AssignUserToTenantAsync( tenantId, username); } ``` #### CreateTenantAsync(TenantCreateRequest, CancellationToken) ```csharp public Task CreateTenantAsync(TenantCreateRequest body, CancellationToken ct = default) ``` Create tenant Creates a new tenant. | Parameter | Type | Description | | --------- | --------------------- | ----------- | | `body` | `TenantCreateRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CreateTenantExample(TenantId tenantId) { using var client = CamundaClient.Create(); var result = await client.CreateTenantAsync(new TenantCreateRequest { TenantId = tenantId, Name = "Acme Corporation", }); Console.WriteLine($"Tenant key: {result.TenantId}"); } ``` #### DeleteTenantAsync(TenantId, CancellationToken) ```csharp public Task DeleteTenantAsync(TenantId tenantId, CancellationToken ct = default) ``` Delete tenant Deletes an existing tenant. | Parameter | Type | Description | | ---------- | ------------------- | ----------- | | `tenantId` | `TenantId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task DeleteTenantExample(TenantId tenantId) { using var client = CamundaClient.Create(); await client.DeleteTenantAsync(tenantId); } ``` #### GetTenantAsync(TenantId, ConsistencyOptions?, CancellationToken) ```csharp public Task GetTenantAsync(TenantId tenantId, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get tenant Retrieves a single tenant by tenant ID. | Parameter | Type | Description | | ------------- | ---------------------------------- | ----------- | | `tenantId` | `TenantId` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetTenantExample(TenantId tenantId) { using var client = CamundaClient.Create(); var result = await client.GetTenantAsync(tenantId); Console.WriteLine($"Tenant: {result.Name}"); } ``` #### GetUsageMetricsAsync(DateTimeOffset, DateTimeOffset, TenantId?, bool?, ConsistencyOptions?, CancellationToken) ```csharp public Task GetUsageMetricsAsync(DateTimeOffset startTime, DateTimeOffset endTime, TenantId? tenantId = null, bool? withTenants = null, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get usage metrics Retrieve the usage metrics based on given criteria. | Parameter | Type | Description | | ------------- | ------------------------------------------ | ----------- | | `startTime` | `DateTimeOffset` | | | `endTime` | `DateTimeOffset` | | | `tenantId` | `Nullable` | | | `withTenants` | `Nullable` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetUsageMetricsExample() { using var client = CamundaClient.Create(); var result = await client.GetUsageMetricsAsync( startTime: new DateTimeOffset(2024, 1, 1, 0, 0, 0, TimeSpan.Zero), endTime: new DateTimeOffset(2024, 12, 31, 23, 59, 59, TimeSpan.Zero)); Console.WriteLine($"Metrics: {result}"); } ``` #### SearchClientsForTenantAsync(TenantId, TenantClientSearchQueryRequest, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchClientsForTenantAsync(TenantId tenantId, TenantClientSearchQueryRequest body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search clients for tenant Retrieves a filtered and sorted list of clients for a specified tenant. | Parameter | Type | Description | | ------------- | ---------------------------------------------- | ----------- | | `tenantId` | `TenantId` | | | `body` | `TenantClientSearchQueryRequest` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### SearchGroupIdsForTenantAsync(TenantId, TenantGroupSearchQueryRequest, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchGroupIdsForTenantAsync(TenantId tenantId, TenantGroupSearchQueryRequest body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search groups for tenant Retrieves a filtered and sorted list of groups for a specified tenant. | Parameter | Type | Description | | ------------- | --------------------------------------------- | ----------- | | `tenantId` | `TenantId` | | | `body` | `TenantGroupSearchQueryRequest` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchGroupIdsForTenantExample(TenantId tenantId) { using var client = CamundaClient.Create(); var result = await client.SearchGroupIdsForTenantAsync( tenantId, new TenantGroupSearchQueryRequest()); foreach (var group in result.Items) { Console.WriteLine($"Group: {group.GroupId}"); } } ``` #### SearchMappingRulesForTenantAsync(TenantId, MappingRuleSearchQueryRequest, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchMappingRulesForTenantAsync(TenantId tenantId, MappingRuleSearchQueryRequest body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search mapping rules for tenant Retrieves a filtered and sorted list of MappingRules for a specified tenant. | Parameter | Type | Description | | ------------- | --------------------------------------------------- | ----------- | | `tenantId` | `TenantId` | | | `body` | `MappingRuleSearchQueryRequest` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### SearchRolesForTenantAsync(TenantId, RoleSearchQueryRequest, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchRolesForTenantAsync(TenantId tenantId, RoleSearchQueryRequest body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search roles for tenant Retrieves a filtered and sorted list of roles for a specified tenant. | Parameter | Type | Description | | ------------- | -------------------------------------------- | ----------- | | `tenantId` | `TenantId` | | | `body` | `RoleSearchQueryRequest` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### SearchTenantsAsync(TenantSearchQueryRequest, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchTenantsAsync(TenantSearchQueryRequest body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search tenants Retrieves a filtered and sorted list of tenants. | Parameter | Type | Description | | ------------- | --------------------------------------------- | ----------- | | `body` | `TenantSearchQueryRequest` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchTenantsExample() { using var client = CamundaClient.Create(); var result = await client.SearchTenantsAsync(new TenantSearchQueryRequest()); foreach (var tenant in result.Items) { Console.WriteLine($"Tenant: {tenant.Name}"); } } ``` #### SearchUsersForTenantAsync(TenantId, TenantUserSearchQueryRequest, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchUsersForTenantAsync(TenantId tenantId, TenantUserSearchQueryRequest body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search users for tenant Retrieves a filtered and sorted list of users for a specified tenant. | Parameter | Type | Description | | ------------- | -------------------------------------------- | ----------- | | `tenantId` | `TenantId` | | | `body` | `TenantUserSearchQueryRequest` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### UnassignClientFromTenantAsync(TenantId, ClientId, CancellationToken) ```csharp public Task UnassignClientFromTenantAsync(TenantId tenantId, ClientId clientId, CancellationToken ct = default) ``` Unassign a client from a tenant Unassigns the client from the specified tenant. The client can no longer access tenant data. | Parameter | Type | Description | | ---------- | ------------------- | ----------- | | `tenantId` | `TenantId` | | | `clientId` | `ClientId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### UnassignGroupFromTenantAsync(TenantId, GroupId, CancellationToken) ```csharp public Task UnassignGroupFromTenantAsync(TenantId tenantId, GroupId groupId, CancellationToken ct = default) ``` Unassign a group from a tenant Unassigns a group from a specified tenant. Members of the group (users, clients) will no longer have access to the tenant's data - except they are assigned directly to the tenant. | Parameter | Type | Description | | ---------- | ------------------- | ----------- | | `tenantId` | `TenantId` | | | `groupId` | `GroupId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### UnassignMappingRuleFromTenantAsync(TenantId, MappingRuleId, CancellationToken) ```csharp public Task UnassignMappingRuleFromTenantAsync(TenantId tenantId, MappingRuleId mappingRuleId, CancellationToken ct = default) ``` Unassign a mapping rule from a tenant Unassigns a single mapping rule from a specified tenant without deleting the rule. | Parameter | Type | Description | | --------------- | ------------------- | ----------- | | `tenantId` | `TenantId` | | | `mappingRuleId` | `MappingRuleId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### UnassignRoleFromTenantAsync(TenantId, RoleId, CancellationToken) ```csharp public Task UnassignRoleFromTenantAsync(TenantId tenantId, RoleId roleId, CancellationToken ct = default) ``` Unassign a role from a tenant Unassigns a role from a specified tenant. Users, Clients or Groups, that have the role assigned, will no longer have access to the tenant's data - unless they are assigned directly to the tenant. | Parameter | Type | Description | | ---------- | ------------------- | ----------- | | `tenantId` | `TenantId` | | | `roleId` | `RoleId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### UnassignUserFromTenantAsync(TenantId, Username, CancellationToken) ```csharp public Task UnassignUserFromTenantAsync(TenantId tenantId, Username username, CancellationToken ct = default) ``` Unassign a user from a tenant Unassigns the user from the specified tenant. The user can no longer access tenant data. | Parameter | Type | Description | | ---------- | ------------------- | ----------- | | `tenantId` | `TenantId` | | | `username` | `Username` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task UnassignUserFromTenantExample(TenantId tenantId, Username username) { using var client = CamundaClient.Create(); await client.UnassignUserFromTenantAsync( tenantId, username); } ``` #### UpdateTenantAsync(TenantId, TenantUpdateRequest, CancellationToken) ```csharp public Task UpdateTenantAsync(TenantId tenantId, TenantUpdateRequest body, CancellationToken ct = default) ``` Update tenant Updates an existing tenant. | Parameter | Type | Description | | ---------- | --------------------- | ----------- | | `tenantId` | `TenantId` | | | `body` | `TenantUpdateRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task UpdateTenantExample(TenantId tenantId) { using var client = CamundaClient.Create(); await client.UpdateTenantAsync( tenantId, new TenantUpdateRequest { Name = "Acme Corp International", }); } ``` ### Roles #### AssignRoleToClientAsync(RoleId, ClientId, CancellationToken) ```csharp public Task AssignRoleToClientAsync(RoleId roleId, ClientId clientId, CancellationToken ct = default) ``` Assign a role to a client Assigns the specified role to the client. The client will inherit the authorizations associated with this role. | Parameter | Type | Description | | ---------- | ------------------- | ----------- | | `roleId` | `RoleId` | | | `clientId` | `ClientId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### AssignRoleToGroupAsync(RoleId, GroupId, CancellationToken) ```csharp public Task AssignRoleToGroupAsync(RoleId roleId, GroupId groupId, CancellationToken ct = default) ``` Assign a role to a group Assigns the specified role to the group. Every member of the group (user or client) will inherit the authorizations associated with this role. | Parameter | Type | Description | | --------- | ------------------- | ----------- | | `roleId` | `RoleId` | | | `groupId` | `GroupId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### AssignRoleToMappingRuleAsync(RoleId, MappingRuleId, CancellationToken) ```csharp public Task AssignRoleToMappingRuleAsync(RoleId roleId, MappingRuleId mappingRuleId, CancellationToken ct = default) ``` Assign a role to a mapping rule Assigns a role to a mapping rule. | Parameter | Type | Description | | --------------- | ------------------- | ----------- | | `roleId` | `RoleId` | | | `mappingRuleId` | `MappingRuleId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### AssignRoleToUserAsync(RoleId, Username, CancellationToken) ```csharp public Task AssignRoleToUserAsync(RoleId roleId, Username username, CancellationToken ct = default) ``` Assign a role to a user Assigns the specified role to the user. The user will inherit the authorizations associated with this role. | Parameter | Type | Description | | ---------- | ------------------- | ----------- | | `roleId` | `RoleId` | | | `username` | `Username` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### CreateRoleAsync(RoleCreateRequest, CancellationToken) ```csharp public Task CreateRoleAsync(RoleCreateRequest body, CancellationToken ct = default) ``` Create role Create a new role. | Parameter | Type | Description | | --------- | ------------------- | ----------- | | `body` | `RoleCreateRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CreateRoleExample() { using var client = CamundaClient.Create(); var result = await client.CreateRoleAsync(new RoleCreateRequest { Name = "developer", }); Console.WriteLine($"Role key: {result.RoleId}"); } ``` #### DeleteRoleAsync(RoleId, CancellationToken) ```csharp public Task DeleteRoleAsync(RoleId roleId, CancellationToken ct = default) ``` Delete role Deletes the role with the given ID. | Parameter | Type | Description | | --------- | ------------------- | ----------- | | `roleId` | `RoleId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### GetRoleAsync(RoleId, ConsistencyOptions?, CancellationToken) ```csharp public Task GetRoleAsync(RoleId roleId, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get role Get a role by its ID. | Parameter | Type | Description | | ------------- | -------------------------------- | ----------- | | `roleId` | `RoleId` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### SearchClientsForRoleAsync(RoleId, RoleClientSearchQueryRequest, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchClientsForRoleAsync(RoleId roleId, RoleClientSearchQueryRequest body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search role clients Search clients with assigned role. | Parameter | Type | Description | | ------------- | -------------------------------------------- | ----------- | | `roleId` | `RoleId` | | | `body` | `RoleClientSearchQueryRequest` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### SearchGroupsForRoleAsync(RoleId, RoleGroupSearchQueryRequest, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchGroupsForRoleAsync(RoleId roleId, RoleGroupSearchQueryRequest body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search role groups Search groups with assigned role. | Parameter | Type | Description | | ------------- | ------------------------------------------- | ----------- | | `roleId` | `RoleId` | | | `body` | `RoleGroupSearchQueryRequest` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### SearchMappingRulesForRoleAsync(RoleId, MappingRuleSearchQueryRequest, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchMappingRulesForRoleAsync(RoleId roleId, MappingRuleSearchQueryRequest body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search role mapping rules Search mapping rules with assigned role. | Parameter | Type | Description | | ------------- | ------------------------------------------------- | ----------- | | `roleId` | `RoleId` | | | `body` | `MappingRuleSearchQueryRequest` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### SearchRolesAsync(RoleSearchQueryRequest, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchRolesAsync(RoleSearchQueryRequest body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search roles Search for roles based on given criteria. | Parameter | Type | Description | | ------------- | ------------------------------------------- | ----------- | | `body` | `RoleSearchQueryRequest` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchRolesExample() { using var client = CamundaClient.Create(); var result = await client.SearchRolesAsync(new RoleSearchQueryRequest()); foreach (var role in result.Items) { Console.WriteLine($"Role: {role.Name}"); } } ``` #### SearchRolesForGroupAsync(GroupId, RoleSearchQueryRequest, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchRolesForGroupAsync(GroupId groupId, RoleSearchQueryRequest body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search group roles Search roles assigned to a group. | Parameter | Type | Description | | ------------- | ------------------------------------------- | ----------- | | `groupId` | `GroupId` | | | `body` | `RoleSearchQueryRequest` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### SearchUsersForRoleAsync(RoleId, RoleUserSearchQueryRequest, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchUsersForRoleAsync(RoleId roleId, RoleUserSearchQueryRequest body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search role users Search users with assigned role. | Parameter | Type | Description | | ------------- | ------------------------------------------ | ----------- | | `roleId` | `RoleId` | | | `body` | `RoleUserSearchQueryRequest` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### UnassignRoleFromClientAsync(RoleId, ClientId, CancellationToken) ```csharp public Task UnassignRoleFromClientAsync(RoleId roleId, ClientId clientId, CancellationToken ct = default) ``` Unassign a role from a client Unassigns the specified role from the client. The client will no longer inherit the authorizations associated with this role. | Parameter | Type | Description | | ---------- | ------------------- | ----------- | | `roleId` | `RoleId` | | | `clientId` | `ClientId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### UnassignRoleFromGroupAsync(RoleId, GroupId, CancellationToken) ```csharp public Task UnassignRoleFromGroupAsync(RoleId roleId, GroupId groupId, CancellationToken ct = default) ``` Unassign a role from a group Unassigns the specified role from the group. All group members (user or client) no longer inherit the authorizations associated with this role. | Parameter | Type | Description | | --------- | ------------------- | ----------- | | `roleId` | `RoleId` | | | `groupId` | `GroupId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### UnassignRoleFromMappingRuleAsync(RoleId, MappingRuleId, CancellationToken) ```csharp public Task UnassignRoleFromMappingRuleAsync(RoleId roleId, MappingRuleId mappingRuleId, CancellationToken ct = default) ``` Unassign a role from a mapping rule Unassigns a role from a mapping rule. | Parameter | Type | Description | | --------------- | ------------------- | ----------- | | `roleId` | `RoleId` | | | `mappingRuleId` | `MappingRuleId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### UnassignRoleFromUserAsync(RoleId, Username, CancellationToken) ```csharp public Task UnassignRoleFromUserAsync(RoleId roleId, Username username, CancellationToken ct = default) ``` Unassign a role from a user Unassigns a role from a user. The user will no longer inherit the authorizations associated with this role. | Parameter | Type | Description | | ---------- | ------------------- | ----------- | | `roleId` | `RoleId` | | | `username` | `Username` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### UpdateRoleAsync(RoleId, RoleUpdateRequest, CancellationToken) ```csharp public Task UpdateRoleAsync(RoleId roleId, RoleUpdateRequest body, CancellationToken ct = default) ``` Update role Update a role with the given ID. | Parameter | Type | Description | | --------- | ------------------- | ----------- | | `roleId` | `RoleId` | | | `body` | `RoleUpdateRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` ### User Tasks #### AssignUserTaskAsync(UserTaskKey, UserTaskAssignmentRequest, CancellationToken) ```csharp public Task AssignUserTaskAsync(UserTaskKey userTaskKey, UserTaskAssignmentRequest body, CancellationToken ct = default) ``` Assign user task Assigns a user task with the given key to the given assignee. Assignment waits for blocking task listeners on this lifecycle transition. If listener processing is delayed beyond the request timeout, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. | Parameter | Type | Description | | ------------- | --------------------------- | ----------- | | `userTaskKey` | `UserTaskKey` | | | `body` | `UserTaskAssignmentRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task AssignUserTaskExample(UserTaskKey userTaskKey) { using var client = CamundaClient.Create(); await client.AssignUserTaskAsync( userTaskKey, new UserTaskAssignmentRequest { Assignee = "user@example.com", }); } ``` #### CompleteUserTaskAsync(UserTaskKey, UserTaskCompletionRequest, CancellationToken) ```csharp public Task CompleteUserTaskAsync(UserTaskKey userTaskKey, UserTaskCompletionRequest body, CancellationToken ct = default) ``` Complete user task Completes a user task with the given key. Completion waits for blocking task listeners on this lifecycle transition. If listener processing is delayed beyond the request timeout, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. | Parameter | Type | Description | | ------------- | --------------------------- | ----------- | | `userTaskKey` | `UserTaskKey` | | | `body` | `UserTaskCompletionRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CompleteUserTaskExample(UserTaskKey userTaskKey) { using var client = CamundaClient.Create(); await client.CompleteUserTaskAsync( userTaskKey, new UserTaskCompletionRequest()); } ``` #### GetUserTaskAsync(UserTaskKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetUserTaskAsync(UserTaskKey userTaskKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get user task Get the user task by the user task key. | Parameter | Type | Description | | ------------- | ------------------------------------ | ----------- | | `userTaskKey` | `UserTaskKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetUserTaskExample(UserTaskKey userTaskKey) { using var client = CamundaClient.Create(); var result = await client.GetUserTaskAsync(userTaskKey); Console.WriteLine($"User task: {result.UserTaskKey}"); } ``` #### GetUserTaskFormAsync(UserTaskKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetUserTaskFormAsync(UserTaskKey userTaskKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get user task form Get the form of a user task. Note that this endpoint will only return linked forms. This endpoint does not support embedded forms. | Parameter | Type | Description | | ------------- | -------------------------------- | ----------- | | `userTaskKey` | `UserTaskKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetUserTaskFormExample(UserTaskKey userTaskKey) { using var client = CamundaClient.Create(); var result = await client.GetUserTaskFormAsync(userTaskKey); Console.WriteLine($"Form: {result.FormKey}"); } ``` #### SearchUserTaskAuditLogsAsync(UserTaskKey, UserTaskAuditLogSearchQueryRequest, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchUserTaskAuditLogsAsync(UserTaskKey userTaskKey, UserTaskAuditLogSearchQueryRequest body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search user task audit logs Search for user task audit logs based on given criteria. | Parameter | Type | Description | | ------------- | ----------------------------------------------- | ----------- | | `userTaskKey` | `UserTaskKey` | | | `body` | `UserTaskAuditLogSearchQueryRequest` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchUserTaskAuditLogsExample(UserTaskKey userTaskKey) { using var client = CamundaClient.Create(); var result = await client.SearchUserTaskAuditLogsAsync( userTaskKey, new UserTaskAuditLogSearchQueryRequest()); foreach (var log in result.Items) { Console.WriteLine($"Audit log: {log.AuditLogKey}"); } } ``` #### SearchUserTaskEffectiveVariablesAsync(UserTaskKey, UserTaskEffectiveVariableSearchQueryRequest, bool?, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchUserTaskEffectiveVariablesAsync(UserTaskKey userTaskKey, UserTaskEffectiveVariableSearchQueryRequest body, bool? truncateValues = null, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search user task effective variables Search for the effective variables of a user task. This endpoint returns deduplicated variables where each variable name appears at most once. When the same variable name exists at multiple scope levels in the scope hierarchy, the value from the innermost scope (closest to the user task) takes precedence. This is useful for retrieving the actual runtime state of variables as seen by the user task. By default, long variable values in the response are truncated. | Parameter | Type | Description | | ---------------- | ----------------------------------------------- | ----------- | | `userTaskKey` | `UserTaskKey` | | | `body` | `UserTaskEffectiveVariableSearchQueryRequest` | | | `truncateValues` | `Nullable` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### SearchUserTaskVariablesAsync(UserTaskKey, UserTaskVariableSearchQueryRequest, bool?, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchUserTaskVariablesAsync(UserTaskKey userTaskKey, UserTaskVariableSearchQueryRequest body, bool? truncateValues = null, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search user task variables Search for user task variables based on given criteria. This endpoint returns all variable documents visible from the user task's scope, including variables from parent scopes in the scope hierarchy. If the same variable name exists at multiple scope levels, each scope's variable is returned as a separate result. Use the `/user-tasks/{userTaskKey}/effective-variables/search` endpoint to get deduplicated variables where the innermost scope takes precedence. By default, long variable values in the response are truncated. | Parameter | Type | Description | | ---------------- | ----------------------------------------------- | ----------- | | `userTaskKey` | `UserTaskKey` | | | `body` | `UserTaskVariableSearchQueryRequest` | | | `truncateValues` | `Nullable` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### SearchUserTasksAsync(UserTaskSearchQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchUserTasksAsync(UserTaskSearchQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search user tasks Search for user tasks based on given criteria. | Parameter | Type | Description | | ------------- | ----------------------------------------------- | ----------- | | `body` | `UserTaskSearchQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchUserTasksExample() { using var client = CamundaClient.Create(); var result = await client.SearchUserTasksAsync(new UserTaskSearchQuery()); foreach (var task in result.Items) { Console.WriteLine($"User task: {task.UserTaskKey}"); } } ``` #### UnassignUserTaskAsync(UserTaskKey, CancellationToken) ```csharp public Task UnassignUserTaskAsync(UserTaskKey userTaskKey, CancellationToken ct = default) ``` Unassign user task Removes the assignee of a task with the given key. Unassignment waits for blocking task listeners on this lifecycle transition. If listener processing is delayed beyond the request timeout, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. | Parameter | Type | Description | | ------------- | ------------------- | ----------- | | `userTaskKey` | `UserTaskKey` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task UnassignUserTaskExample(UserTaskKey userTaskKey) { using var client = CamundaClient.Create(); await client.UnassignUserTaskAsync(userTaskKey); } ``` #### UpdateUserTaskAsync(UserTaskKey, UserTaskUpdateRequest, CancellationToken) ```csharp public Task UpdateUserTaskAsync(UserTaskKey userTaskKey, UserTaskUpdateRequest body, CancellationToken ct = default) ``` Update user task Update a user task with the given key. Updates wait for blocking task listeners on this lifecycle transition. If listener processing is delayed beyond the request timeout, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. | Parameter | Type | Description | | ------------- | ----------------------- | ----------- | | `userTaskKey` | `UserTaskKey` | | | `body` | `UserTaskUpdateRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task UpdateUserTaskExample(UserTaskKey userTaskKey) { using var client = CamundaClient.Create(); await client.UpdateUserTaskAsync( userTaskKey, new UserTaskUpdateRequest()); } ``` ### Signals #### BroadcastSignalAsync(SignalBroadcastRequest, CancellationToken) ```csharp public Task BroadcastSignalAsync(SignalBroadcastRequest body, CancellationToken ct = default) ``` Broadcast signal Broadcasts a signal. | Parameter | Type | Description | | --------- | ------------------------ | ----------- | | `body` | `SignalBroadcastRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task BroadcastSignalExample() { using var client = CamundaClient.Create(); var result = await client.BroadcastSignalAsync(new SignalBroadcastRequest { SignalName = "orderCancelled", }); Console.WriteLine($"Signal key: {result.SignalKey}"); } ``` ### Batch Operations #### CancelBatchOperationAsync(BatchOperationKey, CancellationToken) ```csharp public Task CancelBatchOperationAsync(BatchOperationKey batchOperationKey, CancellationToken ct = default) ``` Cancel Batch operation Cancels a running batch operation. This is done asynchronously, the progress can be tracked using the batch operation status endpoint (/batch-operations/{batchOperationKey}). | Parameter | Type | Description | | ------------------- | ------------------- | ----------- | | `batchOperationKey` | `BatchOperationKey` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CancelBatchOperationExample(BatchOperationKey batchOperationKey) { using var client = CamundaClient.Create(); await client.CancelBatchOperationAsync(batchOperationKey); } ``` #### GetBatchOperationAsync(BatchOperationKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetBatchOperationAsync(BatchOperationKey batchOperationKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get batch operation Get batch operation by key. | Parameter | Type | Description | | ------------------- | -------------------------------------------- | ----------- | | `batchOperationKey` | `BatchOperationKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetBatchOperationExample(BatchOperationKey batchOperationKey) { using var client = CamundaClient.Create(); var result = await client.GetBatchOperationAsync( batchOperationKey); Console.WriteLine($"Batch operation: {result.BatchOperationKey}"); } ``` #### ResumeBatchOperationAsync(BatchOperationKey, CancellationToken) ```csharp public Task ResumeBatchOperationAsync(BatchOperationKey batchOperationKey, CancellationToken ct = default) ``` Resume Batch operation Resumes a suspended batch operation. This is done asynchronously, the progress can be tracked using the batch operation status endpoint (/batch-operations/{batchOperationKey}). | Parameter | Type | Description | | ------------------- | ------------------- | ----------- | | `batchOperationKey` | `BatchOperationKey` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task ResumeBatchOperationExample(BatchOperationKey batchOperationKey) { using var client = CamundaClient.Create(); await client.ResumeBatchOperationAsync(batchOperationKey); } ``` #### SearchBatchOperationItemsAsync(BatchOperationItemSearchQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchBatchOperationItemsAsync(BatchOperationItemSearchQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search batch operation items Search for batch operation items based on given criteria. | Parameter | Type | Description | | ------------- | --------------------------------------------------------- | ----------- | | `body` | `BatchOperationItemSearchQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchBatchOperationItemsExample() { using var client = CamundaClient.Create(); var result = await client.SearchBatchOperationItemsAsync( new BatchOperationItemSearchQuery()); foreach (var item in result.Items) { Console.WriteLine($"Item: {item.ItemKey}"); } } ``` #### SearchBatchOperationsAsync(BatchOperationSearchQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchBatchOperationsAsync(BatchOperationSearchQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search batch operations Search for batch operations based on given criteria. | Parameter | Type | Description | | ------------- | ----------------------------------------------------- | ----------- | | `body` | `BatchOperationSearchQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchBatchOperationsExample() { using var client = CamundaClient.Create(); var result = await client.SearchBatchOperationsAsync( new BatchOperationSearchQuery()); foreach (var op in result.Items) { Console.WriteLine($"Batch operation: {op.BatchOperationKey}"); } } ``` #### SuspendBatchOperationAsync(BatchOperationKey, CancellationToken) ```csharp public Task SuspendBatchOperationAsync(BatchOperationKey batchOperationKey, CancellationToken ct = default) ``` Suspend Batch operation Suspends a running batch operation. This is done asynchronously, the progress can be tracked using the batch operation status endpoint (/batch-operations/{batchOperationKey}). | Parameter | Type | Description | | ------------------- | ------------------- | ----------- | | `batchOperationKey` | `BatchOperationKey` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SuspendBatchOperationExample(BatchOperationKey batchOperationKey) { using var client = CamundaClient.Create(); await client.SuspendBatchOperationAsync(batchOperationKey); } ``` ### Process Instances #### CancelProcessInstanceAsync(ProcessInstanceKey, CancelProcessInstanceRequest, CancellationToken) ```csharp public Task CancelProcessInstanceAsync(ProcessInstanceKey processInstanceKey, CancelProcessInstanceRequest body, CancellationToken ct = default) ``` Cancel process instance Cancels a running process instance. As a cancellation includes more than just the removal of the process instance resource, the cancellation resource must be posted. Cancellation can wait on listener-related processing; when that processing does not complete in time, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. | Parameter | Type | Description | | -------------------- | ------------------------------ | ----------- | | `processInstanceKey` | `ProcessInstanceKey` | | | `body` | `CancelProcessInstanceRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CancelProcessInstanceExample(ProcessInstanceKey processInstanceKey) { using var client = CamundaClient.Create(); await client.CancelProcessInstanceAsync( processInstanceKey, new CancelProcessInstanceRequest()); } ``` #### CancelProcessInstancesBatchOperationAsync(ProcessInstanceCancellationBatchOperationRequest, CancellationToken) ```csharp public Task CancelProcessInstancesBatchOperationAsync(ProcessInstanceCancellationBatchOperationRequest body, CancellationToken ct = default) ``` Cancel process instances (batch) Cancels multiple running process instances. Since only ACTIVE root instances can be cancelled, any given filters for state and parentProcessInstanceKey are ignored and overridden during this batch operation. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). | Parameter | Type | Description | | --------- | -------------------------------------------------- | ----------- | | `body` | `ProcessInstanceCancellationBatchOperationRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CancelProcessInstancesBatchOperationExample() { using var client = CamundaClient.Create(); var result = await client.CancelProcessInstancesBatchOperationAsync( new ProcessInstanceCancellationBatchOperationRequest()); Console.WriteLine($"Batch operation key: {result.BatchOperationKey}"); } ``` #### CreateProcessInstanceAsync(ProcessInstanceCreationInstruction, CancellationToken) ```csharp public Task CreateProcessInstanceAsync(ProcessInstanceCreationInstruction body, CancellationToken ct = default) ``` Create process instance Creates and starts an instance of the specified process. The process definition to use to create the instance can be specified either using its unique key (as returned by Deploy resources), or using the BPMN process id and a version. Waits for the completion of the process instance before returning a result when awaitCompletion is enabled. | Parameter | Type | Description | | --------- | ------------------------------------ | ----------- | | `body` | `ProcessInstanceCreationInstruction` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CreateProcessInstanceByIdExample(ProcessDefinitionId processDefinitionId) { using var client = CamundaClient.Create(); var result = await client.CreateProcessInstanceAsync(new ProcessInstanceCreationInstructionById { ProcessDefinitionId = processDefinitionId, }); Console.WriteLine($"Process instance key: {result.ProcessInstanceKey}"); } public static async Task CreateProcessInstanceByKeyExample(ProcessDefinitionKey processDefinitionKey) { using var client = CamundaClient.Create(); var result = await client.CreateProcessInstanceAsync(new ProcessInstanceCreationInstructionByKey { ProcessDefinitionKey = processDefinitionKey, }); Console.WriteLine($"Process instance key: {result.ProcessInstanceKey}"); } ``` #### DeleteProcessInstanceAsync(ProcessInstanceKey, DeleteProcessInstanceRequest, CancellationToken) ```csharp public Task DeleteProcessInstanceAsync(ProcessInstanceKey processInstanceKey, DeleteProcessInstanceRequest body, CancellationToken ct = default) ``` Delete process instance Deletes a process instance. Only instances that are completed or terminated can be deleted. | Parameter | Type | Description | | -------------------- | ------------------------------ | ----------- | | `processInstanceKey` | `ProcessInstanceKey` | | | `body` | `DeleteProcessInstanceRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task DeleteProcessInstanceExample(ProcessInstanceKey processInstanceKey) { using var client = CamundaClient.Create(); await client.DeleteProcessInstanceAsync( processInstanceKey, new DeleteProcessInstanceRequest()); } ``` #### DeleteProcessInstancesBatchOperationAsync(ProcessInstanceDeletionBatchOperationRequest, CancellationToken) ```csharp public Task DeleteProcessInstancesBatchOperationAsync(ProcessInstanceDeletionBatchOperationRequest body, CancellationToken ct = default) ``` Delete process instances (batch) Delete multiple process instances. This will delete the historic data from secondary storage. Only process instances in a final state (COMPLETED or TERMINATED) can be deleted. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). | Parameter | Type | Description | | --------- | ---------------------------------------------- | ----------- | | `body` | `ProcessInstanceDeletionBatchOperationRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task DeleteProcessInstancesBatchOperationExample() { using var client = CamundaClient.Create(); var result = await client.DeleteProcessInstancesBatchOperationAsync( new ProcessInstanceDeletionBatchOperationRequest()); Console.WriteLine($"Batch operation key: {result.BatchOperationKey}"); } ``` #### GetProcessInstanceAsync(ProcessInstanceKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetProcessInstanceAsync(ProcessInstanceKey processInstanceKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get process instance Get the process instance by the process instance key. | Parameter | Type | Description | | -------------------- | ------------------------------------------- | ----------- | | `processInstanceKey` | `ProcessInstanceKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetProcessInstanceExample(ProcessInstanceKey processInstanceKey) { using var client = CamundaClient.Create(); var result = await client.GetProcessInstanceAsync(processInstanceKey); Console.WriteLine($"Process instance: {result.ProcessDefinitionId}"); } ``` #### GetProcessInstanceCallHierarchyAsync(ProcessInstanceKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetProcessInstanceCallHierarchyAsync(ProcessInstanceKey processInstanceKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get call hierarchy Returns the call hierarchy for a given process instance, showing its ancestry up to the root instance. | Parameter | Type | Description | | -------------------- | ---------------------------- | ----------- | | `processInstanceKey` | `ProcessInstanceKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetProcessInstanceCallHierarchyExample(ProcessInstanceKey processInstanceKey) { using var client = CamundaClient.Create(); var result = await client.GetProcessInstanceCallHierarchyAsync( processInstanceKey); Console.WriteLine($"Call hierarchy: {result}"); } ``` #### GetProcessInstanceSequenceFlowsAsync(ProcessInstanceKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetProcessInstanceSequenceFlowsAsync(ProcessInstanceKey processInstanceKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get sequence flows Get sequence flows taken by the process instance. | Parameter | Type | Description | | -------------------- | ------------------------------------------------------------- | ----------- | | `processInstanceKey` | `ProcessInstanceKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetProcessInstanceSequenceFlowsExample(ProcessInstanceKey processInstanceKey) { using var client = CamundaClient.Create(); var result = await client.GetProcessInstanceSequenceFlowsAsync( processInstanceKey); foreach (var flow in result.Items) { Console.WriteLine($"Sequence flow: {flow}"); } } ``` #### GetProcessInstanceStatisticsAsync(ProcessInstanceKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetProcessInstanceStatisticsAsync(ProcessInstanceKey processInstanceKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get element instance statistics Get statistics about elements by the process instance key. | Parameter | Type | Description | | -------------------- | ----------------------------------------------------------------- | ----------- | | `processInstanceKey` | `ProcessInstanceKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetProcessInstanceStatisticsExample(ProcessInstanceKey processInstanceKey) { using var client = CamundaClient.Create(); var result = await client.GetProcessInstanceStatisticsAsync( processInstanceKey); foreach (var stat in result.Items) { Console.WriteLine($"Element: {stat.ElementId}"); } } ``` #### GetProcessInstanceStatisticsByDefinitionAsync(IncidentProcessInstanceStatisticsByDefinitionQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task GetProcessInstanceStatisticsByDefinitionAsync(IncidentProcessInstanceStatisticsByDefinitionQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get process instance statistics by definition Returns statistics for active process instances with incidents, grouped by process definition. The result set is scoped to a specific incident error hash code, which must be provided as a filter in the request body. | Parameter | Type | Description | | ------------- | ------------------------------------------------------------------------------ | ----------- | | `body` | `IncidentProcessInstanceStatisticsByDefinitionQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetProcessInstanceStatisticsByDefinitionExample() { using var client = CamundaClient.Create(); var result = await client.GetProcessInstanceStatisticsByDefinitionAsync( new IncidentProcessInstanceStatisticsByDefinitionQuery()); foreach (var stat in result.Items) { Console.WriteLine($"Definition: {stat.ProcessDefinitionKey}"); } } ``` #### GetProcessInstanceStatisticsByErrorAsync(IncidentProcessInstanceStatisticsByErrorQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task GetProcessInstanceStatisticsByErrorAsync(IncidentProcessInstanceStatisticsByErrorQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get process instance statistics by error Returns statistics for active process instances that currently have active incidents, grouped by incident error hash code. | Parameter | Type | Description | | ------------- | ------------------------------------------------------------------------- | ----------- | | `body` | `IncidentProcessInstanceStatisticsByErrorQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetProcessInstanceStatisticsByErrorExample() { using var client = CamundaClient.Create(); var result = await client.GetProcessInstanceStatisticsByErrorAsync( new IncidentProcessInstanceStatisticsByErrorQuery()); foreach (var stat in result.Items) { Console.WriteLine($"Error: {stat.ErrorMessage}"); } } ``` #### MigrateProcessInstanceAsync(ProcessInstanceKey, ProcessInstanceMigrationInstruction, CancellationToken) ```csharp public Task MigrateProcessInstanceAsync(ProcessInstanceKey processInstanceKey, ProcessInstanceMigrationInstruction body, CancellationToken ct = default) ``` Migrate process instance Migrates a process instance to a new process definition. This request can contain multiple mapping instructions to define mapping between the active process instance's elements and target process definition elements. Use this to upgrade a process instance to a new version of a process or to a different process definition, e.g. to keep your running instances up-to-date with the latest process improvements. | Parameter | Type | Description | | -------------------- | ------------------------------------- | ----------- | | `processInstanceKey` | `ProcessInstanceKey` | | | `body` | `ProcessInstanceMigrationInstruction` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task MigrateProcessInstanceExample(ProcessInstanceKey processInstanceKey, ProcessDefinitionKey targetProcessDefinitionKey) { using var client = CamundaClient.Create(); await client.MigrateProcessInstanceAsync( processInstanceKey, new ProcessInstanceMigrationInstruction { TargetProcessDefinitionKey = targetProcessDefinitionKey, }); } ``` #### MigrateProcessInstancesBatchOperationAsync(ProcessInstanceMigrationBatchOperationRequest, CancellationToken) ```csharp public Task MigrateProcessInstancesBatchOperationAsync(ProcessInstanceMigrationBatchOperationRequest body, CancellationToken ct = default) ``` Migrate process instances (batch) Migrate multiple process instances. Since only process instances with ACTIVE state can be migrated, any given filters for state are ignored and overridden during this batch operation. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). | Parameter | Type | Description | | --------- | ----------------------------------------------- | ----------- | | `body` | `ProcessInstanceMigrationBatchOperationRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task MigrateProcessInstancesBatchOperationExample(ProcessDefinitionKey targetProcessDefinitionKey) { using var client = CamundaClient.Create(); var result = await client.MigrateProcessInstancesBatchOperationAsync( new ProcessInstanceMigrationBatchOperationRequest { Filter = new ProcessInstanceFilter(), MigrationPlan = new ProcessInstanceMigrationBatchOperationPlan { TargetProcessDefinitionKey = targetProcessDefinitionKey, }, }); Console.WriteLine($"Batch operation key: {result.BatchOperationKey}"); } ``` #### ModifyProcessInstanceAsync(ProcessInstanceKey, ProcessInstanceModificationInstruction, CancellationToken) ```csharp public Task ModifyProcessInstanceAsync(ProcessInstanceKey processInstanceKey, ProcessInstanceModificationInstruction body, CancellationToken ct = default) ``` Modify process instance Modifies a running process instance. This request can contain multiple instructions to activate an element of the process or to terminate an active instance of an element. Use this to repair a process instance that is stuck on an element or took an unintended path. For example, because an external system is not available or doesn't respond as expected. | Parameter | Type | Description | | -------------------- | ---------------------------------------- | ----------- | | `processInstanceKey` | `ProcessInstanceKey` | | | `body` | `ProcessInstanceModificationInstruction` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task ModifyProcessInstanceExample(ProcessInstanceKey processInstanceKey) { using var client = CamundaClient.Create(); await client.ModifyProcessInstanceAsync( processInstanceKey, new ProcessInstanceModificationInstruction()); } ``` #### ModifyProcessInstancesBatchOperationAsync(ProcessInstanceModificationBatchOperationRequest, CancellationToken) ```csharp public Task ModifyProcessInstancesBatchOperationAsync(ProcessInstanceModificationBatchOperationRequest body, CancellationToken ct = default) ``` Modify process instances (batch) Modify multiple process instances. Since only process instances with ACTIVE state can be modified, any given filters for state are ignored and overridden during this batch operation. In contrast to single modification operation, it is not possible to add variable instructions or modify by element key. It is only possible to use the element id of the source and target. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). | Parameter | Type | Description | | --------- | -------------------------------------------------- | ----------- | | `body` | `ProcessInstanceModificationBatchOperationRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task ModifyProcessInstancesBatchOperationExample() { using var client = CamundaClient.Create(); var result = await client.ModifyProcessInstancesBatchOperationAsync( new ProcessInstanceModificationBatchOperationRequest()); Console.WriteLine($"Batch operation key: {result.BatchOperationKey}"); } ``` #### ResolveIncidentsBatchOperationAsync(ProcessInstanceIncidentResolutionBatchOperationRequest, CancellationToken) ```csharp public Task ResolveIncidentsBatchOperationAsync(ProcessInstanceIncidentResolutionBatchOperationRequest body, CancellationToken ct = default) ``` Resolve related incidents (batch) Resolves multiple instances of process instances. Since only process instances with ACTIVE state can have unresolved incidents, any given filters for state are ignored and overridden during this batch operation. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). | Parameter | Type | Description | | --------- | -------------------------------------------------------- | ----------- | | `body` | `ProcessInstanceIncidentResolutionBatchOperationRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task ResolveIncidentsBatchOperationExample() { using var client = CamundaClient.Create(); var result = await client.ResolveIncidentsBatchOperationAsync( new ProcessInstanceIncidentResolutionBatchOperationRequest()); Console.WriteLine($"Batch operation key: {result.BatchOperationKey}"); } ``` #### ResolveProcessInstanceIncidentsAsync(ProcessInstanceKey, CancellationToken) ```csharp public Task ResolveProcessInstanceIncidentsAsync(ProcessInstanceKey processInstanceKey, CancellationToken ct = default) ``` Resolve related incidents Creates a batch operation to resolve multiple incidents of a process instance. | Parameter | Type | Description | | -------------------- | -------------------- | ----------- | | `processInstanceKey` | `ProcessInstanceKey` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task ResolveProcessInstanceIncidentsExample(ProcessInstanceKey processInstanceKey) { using var client = CamundaClient.Create(); var result = await client.ResolveProcessInstanceIncidentsAsync( processInstanceKey); Console.WriteLine($"Batch operation key: {result.BatchOperationKey}"); } ``` #### SearchProcessInstanceIncidentsAsync(ProcessInstanceKey, IncidentSearchQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchProcessInstanceIncidentsAsync(ProcessInstanceKey processInstanceKey, IncidentSearchQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search related incidents Search for incidents caused by the process instance or any of its called process or decision instances. Although the `processInstanceKey` is provided as a path parameter to indicate the root process instance, you may also include a `processInstanceKey` within the filter object to narrow results to specific child process instances. This is useful, for example, if you want to isolate incidents associated with subprocesses or called processes under the root instance while excluding incidents directly tied to the root. | Parameter | Type | Description | | -------------------- | ----------------------------------------------- | ----------- | | `processInstanceKey` | `ProcessInstanceKey` | | | `body` | `IncidentSearchQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchProcessInstanceIncidentsExample(ProcessInstanceKey processInstanceKey) { using var client = CamundaClient.Create(); var result = await client.SearchProcessInstanceIncidentsAsync( processInstanceKey, new IncidentSearchQuery()); foreach (var incident in result.Items) { Console.WriteLine($"Incident: {incident.IncidentKey}"); } } ``` #### SearchProcessInstancesAsync(ProcessInstanceSearchQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchProcessInstancesAsync(ProcessInstanceSearchQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search process instances Search for process instances based on given criteria. | Parameter | Type | Description | | ------------- | ------------------------------------------------------ | ----------- | | `body` | `ProcessInstanceSearchQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchProcessInstancesExample() { using var client = CamundaClient.Create(); var result = await client.SearchProcessInstancesAsync(new ProcessInstanceSearchQuery()); foreach (var instance in result.Items) { Console.WriteLine($"Process instance: {instance.ProcessInstanceKey}"); } } ``` ### Messages #### CorrelateMessageAsync(MessageCorrelationRequest, CancellationToken) ```csharp public Task CorrelateMessageAsync(MessageCorrelationRequest body, CancellationToken ct = default) ``` Correlate message Publishes a message and correlates it to a subscription. If correlation is successful it will return the first process instance key the message correlated with. The message is not buffered. Use the publish message endpoint to send messages that can be buffered. | Parameter | Type | Description | | --------- | --------------------------- | ----------- | | `body` | `MessageCorrelationRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CorrelateMessageExample() { using var client = CamundaClient.Create(); var result = await client.CorrelateMessageAsync(new MessageCorrelationRequest { Name = "paymentReceived", CorrelationKey = "order-123", }); Console.WriteLine($"Message key: {result.MessageKey}"); } ``` #### PublishMessageAsync(MessagePublicationRequest, CancellationToken) ```csharp public Task PublishMessageAsync(MessagePublicationRequest body, CancellationToken ct = default) ``` Publish message Publishes a single message. Messages are published to specific partitions computed from their correlation keys. Messages can be buffered. The endpoint does not wait for a correlation result. Use the message correlation endpoint for such use cases. | Parameter | Type | Description | | --------- | --------------------------- | ----------- | | `body` | `MessagePublicationRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task PublishMessageExample() { using var client = CamundaClient.Create(); var result = await client.PublishMessageAsync(new MessagePublicationRequest { Name = "paymentReceived", CorrelationKey = "order-123", TimeToLive = 60000, }); Console.WriteLine($"Message key: {result.MessageKey}"); } ``` #### SearchCorrelatedMessageSubscriptionsAsync(CorrelatedMessageSubscriptionSearchQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchCorrelatedMessageSubscriptionsAsync(CorrelatedMessageSubscriptionSearchQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search correlated message subscriptions Search correlated message subscriptions based on given criteria. | Parameter | Type | Description | | ------------- | -------------------------------------------------------------------- | ----------- | | `body` | `CorrelatedMessageSubscriptionSearchQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchCorrelatedMessageSubscriptionsExample() { using var client = CamundaClient.Create(); var result = await client.SearchCorrelatedMessageSubscriptionsAsync( new CorrelatedMessageSubscriptionSearchQuery()); foreach (var sub in result.Items) { Console.WriteLine($"Correlated subscription: {sub.MessageName}"); } } ``` #### SearchMessageSubscriptionsAsync(MessageSubscriptionSearchQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchMessageSubscriptionsAsync(MessageSubscriptionSearchQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search message subscriptions Search for message subscriptions based on given criteria. By default, both start and intermediate event subscriptions are returned. Use the `messageSubscriptionType` filter to restrict results to a single type. **Version notes:** - Start event subscriptions are only captured for deployments made with 8.10 or later. - The `messageSubscriptionType` field is only populated for data created with Camunda 8.10 or later. For pre-8.10 data, intermediate event entries have no `messageSubscriptionType` value stored. For convenience, the API returns `PROCESS_EVENT` as a default for such search results, though. - Searching for intermediate event subscriptions **including legacy data** can be achieved by filtering for `messageSubscriptionType` not matching `START_EVENT`. | Parameter | Type | Description | | ------------- | ---------------------------------------------------------- | ----------- | | `body` | `MessageSubscriptionSearchQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchMessageSubscriptionsExample() { using var client = CamundaClient.Create(); var result = await client.SearchMessageSubscriptionsAsync( new MessageSubscriptionSearchQuery()); foreach (var sub in result.Items) { Console.WriteLine($"Subscription: {sub.MessageName}"); } } ``` ### Authorizations #### CreateAuthorizationAsync(AuthorizationRequest, CancellationToken) ```csharp public Task CreateAuthorizationAsync(AuthorizationRequest body, CancellationToken ct = default) ``` Create authorization Create the authorization. | Parameter | Type | Description | | --------- | ---------------------- | ----------- | | `body` | `AuthorizationRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CreateAuthorizationExample() { using var client = CamundaClient.Create(); var result = await client.CreateAuthorizationAsync(new AuthorizationPropertyBasedRequest { ResourceType = ResourceTypeEnum.PROCESSDEFINITION, PermissionTypes = new List { PermissionTypeEnum.READ, PermissionTypeEnum.UPDATE }, ResourcePropertyName = "my-process", OwnerType = OwnerTypeEnum.USER, OwnerId = "user@example.com", }); Console.WriteLine($"Authorization key: {result.AuthorizationKey}"); } ``` #### DeleteAuthorizationAsync(AuthorizationKey, CancellationToken) ```csharp public Task DeleteAuthorizationAsync(AuthorizationKey authorizationKey, CancellationToken ct = default) ``` Delete authorization Deletes the authorization with the given key. | Parameter | Type | Description | | ------------------ | ------------------- | ----------- | | `authorizationKey` | `AuthorizationKey` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task DeleteAuthorizationExample(AuthorizationKey authorizationKey) { using var client = CamundaClient.Create(); await client.DeleteAuthorizationAsync(authorizationKey); } ``` #### GetAuthorizationAsync(AuthorizationKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetAuthorizationAsync(AuthorizationKey authorizationKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get authorization Get authorization by the given key. | Parameter | Type | Description | | ------------------ | ----------------------------------------- | ----------- | | `authorizationKey` | `AuthorizationKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetAuthorizationExample(AuthorizationKey authorizationKey) { using var client = CamundaClient.Create(); var result = await client.GetAuthorizationAsync( authorizationKey); Console.WriteLine($"Resource type: {result.ResourceType}"); } ``` #### SearchAuthorizationsAsync(AuthorizationSearchQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchAuthorizationsAsync(AuthorizationSearchQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search authorizations Search for authorizations based on given criteria. | Parameter | Type | Description | | ------------- | ----------------------------------------------- | ----------- | | `body` | `AuthorizationSearchQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchAuthorizationsExample() { using var client = CamundaClient.Create(); var result = await client.SearchAuthorizationsAsync( new AuthorizationSearchQuery()); foreach (var auth in result.Items) { Console.WriteLine($"Authorization: {auth.AuthorizationKey}"); } } ``` #### UpdateAuthorizationAsync(AuthorizationKey, AuthorizationRequest, CancellationToken) ```csharp public Task UpdateAuthorizationAsync(AuthorizationKey authorizationKey, AuthorizationRequest body, CancellationToken ct = default) ``` Update authorization Update the authorization with the given key. | Parameter | Type | Description | | ------------------ | ---------------------- | ----------- | | `authorizationKey` | `AuthorizationKey` | | | `body` | `AuthorizationRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task UpdateAuthorizationExample(AuthorizationKey authorizationKey) { using var client = CamundaClient.Create(); await client.UpdateAuthorizationAsync( authorizationKey, new AuthorizationPropertyBasedRequest { ResourceType = ResourceTypeEnum.PROCESSDEFINITION, PermissionTypes = new List { PermissionTypeEnum.READ, PermissionTypeEnum.UPDATE, PermissionTypeEnum.DELETE }, ResourcePropertyName = "my-process", OwnerType = OwnerTypeEnum.USER, OwnerId = "user@example.com", }); } ``` ### Deployments #### CreateDeploymentAsync(MultipartFormDataContent, CancellationToken) ```csharp public Task CreateDeploymentAsync(MultipartFormDataContent content, CancellationToken ct = default) ``` Deploy resources Deploys one or more resources (e.g. processes, decision models, or forms). This is an atomic call, i.e. either all resources are deployed or none of them are. | Parameter | Type | Description | | --------- | -------------------------- | ----------- | | `content` | `MultipartFormDataContent` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CreateDeploymentExample() { using var client = CamundaClient.Create(); var content = new MultipartFormDataContent(); var fileContent = new ByteArrayContent(File.ReadAllBytes("process.bpmn")); content.Add(fileContent, "resources", "process.bpmn"); var result = await client.CreateDeploymentAsync(content); Console.WriteLine($"Deployment key: {result.DeploymentKey}"); } ``` ### Documents #### CreateDocumentAsync(MultipartFormDataContent, string?, DocumentId?, CancellationToken) ```csharp public Task CreateDocumentAsync(MultipartFormDataContent content, string? storeId = null, DocumentId? documentId = null, CancellationToken ct = default) ``` Upload document Upload a document to the Camunda 8 cluster. Note that this is currently supported for document stores of type: AWS, Azure, GCP, in-memory (non-production), local (non-production) | Parameter | Type | Description | | ------------ | -------------------------- | ----------- | | `content` | `MultipartFormDataContent` | | | `storeId` | `String` | | | `documentId` | `Nullable` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CreateDocumentExample() { using var client = CamundaClient.Create(); using var content = new MultipartFormDataContent(); content.Add(new ByteArrayContent(System.Text.Encoding.UTF8.GetBytes("Hello, world!")), "file", "hello.txt"); var result = await client.CreateDocumentAsync(content); Console.WriteLine($"Document ID: {result.DocumentId}"); } ``` #### CreateDocumentLinkAsync(DocumentId, DocumentLinkRequest, string?, string?, CancellationToken) ```csharp public Task CreateDocumentLinkAsync(DocumentId documentId, DocumentLinkRequest body, string? storeId = null, string? contentHash = null, CancellationToken ct = default) ``` Create document link Create a link to a document in the Camunda 8 cluster. Note that this is currently supported for document stores of type: AWS, Azure, GCP | Parameter | Type | Description | | ------------- | --------------------- | ----------- | | `documentId` | `DocumentId` | | | `body` | `DocumentLinkRequest` | | | `storeId` | `String` | | | `contentHash` | `String` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CreateDocumentLinkExample(DocumentId documentId) { using var client = CamundaClient.Create(); var result = await client.CreateDocumentLinkAsync( documentId, new DocumentLinkRequest()); Console.WriteLine($"Document link: {result.Url}"); } ``` #### CreateDocumentsAsync(MultipartFormDataContent, string?, CancellationToken) ```csharp public Task CreateDocumentsAsync(MultipartFormDataContent content, string? storeId = null, CancellationToken ct = default) ``` Upload multiple documents Upload multiple documents to the Camunda 8 cluster. The caller must provide a file name for each document, which will be used in case of a multi-status response to identify which documents failed to upload. The file name can be provided in the `Content-Disposition` header of the file part or in the `fileName` field of the metadata. You can add a parallel array of metadata objects. These are matched with the files based on index, and must have the same length as the files array. To pass homogenous metadata for all files, spread the metadata over the metadata array. A filename value provided explicitly via the metadata array in the request overrides the `Content-Disposition` header of the file part. In case of a multi-status response, the response body will contain a list of `DocumentBatchProblemDetail` objects, each of which contains the file name of the document that failed to upload and the reason for the failure. The client can choose to retry the whole batch or individual documents based on the response. Note that this is currently supported for document stores of type: AWS, Azure, GCP, in-memory (non-production), local (non-production) | Parameter | Type | Description | | --------- | -------------------------- | ----------- | | `content` | `MultipartFormDataContent` | | | `storeId` | `String` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CreateDocumentsExample() { using var client = CamundaClient.Create(); using var content = new MultipartFormDataContent(); content.Add(new ByteArrayContent(System.Text.Encoding.UTF8.GetBytes("File one")), "files", "one.txt"); content.Add(new ByteArrayContent(System.Text.Encoding.UTF8.GetBytes("File two")), "files", "two.txt"); var result = await client.CreateDocumentsAsync(content); foreach (var doc in result.CreatedDocuments) { Console.WriteLine($"Created: {doc.DocumentId}"); } } ``` #### DeleteDocumentAsync(DocumentId, string?, CancellationToken) ```csharp public Task DeleteDocumentAsync(DocumentId documentId, string? storeId = null, CancellationToken ct = default) ``` Delete document Delete a document from the Camunda 8 cluster. Note that this is currently supported for document stores of type: AWS, Azure, GCP, in-memory (non-production), local (non-production) | Parameter | Type | Description | | ------------ | ------------------- | ----------- | | `documentId` | `DocumentId` | | | `storeId` | `String` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task DeleteDocumentExample(DocumentId documentId) { using var client = CamundaClient.Create(); await client.DeleteDocumentAsync(documentId); } ``` #### GetDocumentAsync(DocumentId, string?, string?, CancellationToken) ```csharp public Task GetDocumentAsync(DocumentId documentId, string? storeId = null, string? contentHash = null, CancellationToken ct = default) ``` Download document Download a document from the Camunda 8 cluster. Note that this is currently supported for document stores of type: AWS, Azure, GCP, in-memory (non-production), local (non-production) | Parameter | Type | Description | | ------------- | ------------------- | ----------- | | `documentId` | `DocumentId` | | | `storeId` | `String` | | | `contentHash` | `String` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetDocumentExample(DocumentId documentId) { using var client = CamundaClient.Create(); var content = await client.GetDocumentAsync(documentId); Console.WriteLine($"Downloaded document: {documentId}"); } ``` ### Variables #### CreateElementInstanceVariablesAsync(ElementInstanceKey, SetVariableRequest, CancellationToken) ```csharp public Task CreateElementInstanceVariablesAsync(ElementInstanceKey elementInstanceKey, SetVariableRequest body, CancellationToken ct = default) ``` Update element instance variables Updates all the variables of a particular scope (for example, process instance, element instance) with the given variable data. Specify the element instance in the `elementInstanceKey` parameter. Variable updates can be delayed by listener-related processing; if processing exceeds the request timeout, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. | Parameter | Type | Description | | -------------------- | -------------------- | ----------- | | `elementInstanceKey` | `ElementInstanceKey` | | | `body` | `SetVariableRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CreateElementInstanceVariablesExample(ElementInstanceKey elementInstanceKey) { using var client = CamundaClient.Create(); await client.CreateElementInstanceVariablesAsync( elementInstanceKey, new SetVariableRequest()); } ``` #### CreateGlobalClusterVariableAsync(CreateClusterVariableRequest, CancellationToken) ```csharp public Task CreateGlobalClusterVariableAsync(CreateClusterVariableRequest body, CancellationToken ct = default) ``` Create a global-scoped cluster variable Create a global-scoped cluster variable. | Parameter | Type | Description | | --------- | ------------------------------ | ----------- | | `body` | `CreateClusterVariableRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CreateGlobalClusterVariableExample(ClusterVariableName name) { using var client = CamundaClient.Create(); var result = await client.CreateGlobalClusterVariableAsync( new CreateClusterVariableRequest { Name = name, Value = "my-value", }); Console.WriteLine($"Created variable: {result.Name}"); } ``` #### CreateTenantClusterVariableAsync(TenantId, CreateClusterVariableRequest, CancellationToken) ```csharp public Task CreateTenantClusterVariableAsync(TenantId tenantId, CreateClusterVariableRequest body, CancellationToken ct = default) ``` Create a tenant-scoped cluster variable Create a new cluster variable for the given tenant. | Parameter | Type | Description | | ---------- | ------------------------------ | ----------- | | `tenantId` | `TenantId` | | | `body` | `CreateClusterVariableRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CreateTenantClusterVariableExample(TenantId tenantId, ClusterVariableName name) { using var client = CamundaClient.Create(); var result = await client.CreateTenantClusterVariableAsync( tenantId, new CreateClusterVariableRequest { Name = name, Value = "tenant-value", }); Console.WriteLine($"Created variable: {result.Name}"); } ``` #### DeleteGlobalClusterVariableAsync(ClusterVariableName, CancellationToken) ```csharp public Task DeleteGlobalClusterVariableAsync(ClusterVariableName name, CancellationToken ct = default) ``` Delete a global-scoped cluster variable Delete a global-scoped cluster variable. | Parameter | Type | Description | | --------- | --------------------- | ----------- | | `name` | `ClusterVariableName` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### DeleteTenantClusterVariableAsync(TenantId, ClusterVariableName, CancellationToken) ```csharp public Task DeleteTenantClusterVariableAsync(TenantId tenantId, ClusterVariableName name, CancellationToken ct = default) ``` Delete a tenant-scoped cluster variable Delete a tenant-scoped cluster variable. | Parameter | Type | Description | | ---------- | --------------------- | ----------- | | `tenantId` | `TenantId` | | | `name` | `ClusterVariableName` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### GetGlobalClusterVariableAsync(ClusterVariableName, ConsistencyOptions?, CancellationToken) ```csharp public Task GetGlobalClusterVariableAsync(ClusterVariableName name, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get a global-scoped cluster variable Get a global-scoped cluster variable. | Parameter | Type | Description | | ------------- | ------------------------------------------- | ----------- | | `name` | `ClusterVariableName` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### GetTenantClusterVariableAsync(TenantId, ClusterVariableName, ConsistencyOptions?, CancellationToken) ```csharp public Task GetTenantClusterVariableAsync(TenantId tenantId, ClusterVariableName name, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get a tenant-scoped cluster variable Get a tenant-scoped cluster variable. | Parameter | Type | Description | | ------------- | ------------------------------------------- | ----------- | | `tenantId` | `TenantId` | | | `name` | `ClusterVariableName` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### GetVariableAsync(VariableKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetVariableAsync(VariableKey variableKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get variable Get a variable by its key. This endpoint returns both process-level and local (element-scoped) variables. The variable's scopeKey indicates whether it's a process-level variable or scoped to a specific element instance. | Parameter | Type | Description | | ------------- | ------------------------------------ | ----------- | | `variableKey` | `VariableKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetVariableExample(VariableKey variableKey) { using var client = CamundaClient.Create(); var result = await client.GetVariableAsync(variableKey); Console.WriteLine($"Variable: {result.Name} = {result.Value}"); } ``` #### SearchClusterVariablesAsync(ClusterVariableSearchQueryRequest, bool?, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchClusterVariablesAsync(ClusterVariableSearchQueryRequest body, bool? truncateValues = null, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search for cluster variables based on given criteria. By default, long variable values in the response are truncated. | Parameter | Type | Description | | ---------------- | ------------------------------------------------------ | ----------- | | `body` | `ClusterVariableSearchQueryRequest` | | | `truncateValues` | `Nullable` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchClusterVariablesExample() { using var client = CamundaClient.Create(); var result = await client.SearchClusterVariablesAsync( new ClusterVariableSearchQueryRequest()); foreach (var variable in result.Items) { Console.WriteLine($"Variable: {variable.Name}"); } } ``` #### SearchVariablesAsync(VariableSearchQuery, bool?, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchVariablesAsync(VariableSearchQuery body, bool? truncateValues = null, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search variables Search for variables based on given criteria. This endpoint returns variables that exist directly at the specified scopes - it does not include variables from parent scopes that would be visible through the scope hierarchy. Variables can be process-level (scoped to the process instance) or local (scoped to specific BPMN elements like tasks, subprocesses, etc.). By default, long variable values in the response are truncated. | Parameter | Type | Description | | ---------------- | ----------------------------------------------- | ----------- | | `body` | `VariableSearchQuery` | | | `truncateValues` | `Nullable` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### UpdateGlobalClusterVariableAsync(ClusterVariableName, UpdateClusterVariableRequest, CancellationToken) ```csharp public Task UpdateGlobalClusterVariableAsync(ClusterVariableName name, UpdateClusterVariableRequest body, CancellationToken ct = default) ``` Update a global-scoped cluster variable Updates the value of an existing global cluster variable. The variable must exist, otherwise a 404 error is returned. | Parameter | Type | Description | | --------- | ------------------------------ | ----------- | | `name` | `ClusterVariableName` | | | `body` | `UpdateClusterVariableRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### UpdateTenantClusterVariableAsync(TenantId, ClusterVariableName, UpdateClusterVariableRequest, CancellationToken) ```csharp public Task UpdateTenantClusterVariableAsync(TenantId tenantId, ClusterVariableName name, UpdateClusterVariableRequest body, CancellationToken ct = default) ``` Update a tenant-scoped cluster variable Updates the value of an existing tenant-scoped cluster variable. The variable must exist, otherwise a 404 error is returned. | Parameter | Type | Description | | ---------- | ------------------------------ | ----------- | | `tenantId` | `TenantId` | | | `name` | `ClusterVariableName` | | | `body` | `UpdateClusterVariableRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` ### Mappings #### CreateMappingRuleAsync(MappingRuleCreateRequest, CancellationToken) ```csharp public Task CreateMappingRuleAsync(MappingRuleCreateRequest body, CancellationToken ct = default) ``` Create mapping rule Create a new mapping rule | Parameter | Type | Description | | --------- | -------------------------- | ----------- | | `body` | `MappingRuleCreateRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task CreateMappingRuleExample() { using var client = CamundaClient.Create(); var result = await client.CreateMappingRuleAsync(new MappingRuleCreateRequest { ClaimName = "groups", ClaimValue = "engineering", Name = "Engineering Group Mapping", }); Console.WriteLine($"Mapping rule: {result.MappingRuleId}"); } ``` #### DeleteMappingRuleAsync(MappingRuleId, CancellationToken) ```csharp public Task DeleteMappingRuleAsync(MappingRuleId mappingRuleId, CancellationToken ct = default) ``` Delete a mapping rule Deletes the mapping rule with the given ID. | Parameter | Type | Description | | --------------- | ------------------- | ----------- | | `mappingRuleId` | `MappingRuleId` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### GetMappingRuleAsync(MappingRuleId, ConsistencyOptions?, CancellationToken) ```csharp public Task GetMappingRuleAsync(MappingRuleId mappingRuleId, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get a mapping rule Gets the mapping rule with the given ID. | Parameter | Type | Description | | --------------- | --------------------------------------- | ----------- | | `mappingRuleId` | `MappingRuleId` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### SearchMappingRuleAsync(MappingRuleSearchQueryRequest, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchMappingRuleAsync(MappingRuleSearchQueryRequest body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search mapping rules Search for mapping rules based on given criteria. | Parameter | Type | Description | | ------------- | -------------------------------------------------- | ----------- | | `body` | `MappingRuleSearchQueryRequest` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` #### UpdateMappingRuleAsync(MappingRuleId, MappingRuleUpdateRequest, CancellationToken) ```csharp public Task UpdateMappingRuleAsync(MappingRuleId mappingRuleId, MappingRuleUpdateRequest body, CancellationToken ct = default) ``` Update mapping rule Update a mapping rule. | Parameter | Type | Description | | --------------- | -------------------------- | ----------- | | `mappingRuleId` | `MappingRuleId` | | | `body` | `MappingRuleUpdateRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` ### Decision Instances #### DeleteDecisionInstanceAsync(DecisionEvaluationKey, DeleteDecisionInstanceRequest, CancellationToken) ```csharp public Task DeleteDecisionInstanceAsync(DecisionEvaluationKey decisionEvaluationKey, DeleteDecisionInstanceRequest body, CancellationToken ct = default) ``` Delete decision instance Delete all associated decision evaluations based on provided key. | Parameter | Type | Description | | ----------------------- | ------------------------------- | ----------- | | `decisionEvaluationKey` | `DecisionEvaluationKey` | | | `body` | `DeleteDecisionInstanceRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task DeleteDecisionInstanceExample(DecisionEvaluationKey decisionEvaluationKey) { using var client = CamundaClient.Create(); await client.DeleteDecisionInstanceAsync( decisionEvaluationKey, new DeleteDecisionInstanceRequest()); } ``` #### DeleteDecisionInstancesBatchOperationAsync(DecisionInstanceDeletionBatchOperationRequest, CancellationToken) ```csharp public Task DeleteDecisionInstancesBatchOperationAsync(DecisionInstanceDeletionBatchOperationRequest body, CancellationToken ct = default) ``` Delete decision instances (batch) Delete multiple decision instances. This will delete the historic data from secondary storage. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). | Parameter | Type | Description | | --------- | ----------------------------------------------- | ----------- | | `body` | `DecisionInstanceDeletionBatchOperationRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task DeleteDecisionInstancesBatchOperationExample() { using var client = CamundaClient.Create(); var result = await client.DeleteDecisionInstancesBatchOperationAsync( new DecisionInstanceDeletionBatchOperationRequest()); Console.WriteLine($"Batch operation key: {result.BatchOperationKey}"); } ``` #### GetDecisionInstanceAsync(DecisionEvaluationInstanceKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetDecisionInstanceAsync(DecisionEvaluationInstanceKey decisionEvaluationInstanceKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get decision instance Returns a decision instance. | Parameter | Type | Description | | ------------------------------- | ---------------------------------------------------- | ----------- | | `decisionEvaluationInstanceKey` | `DecisionEvaluationInstanceKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetDecisionInstanceExample(DecisionEvaluationInstanceKey decisionEvaluationInstanceKey) { using var client = CamundaClient.Create(); var result = await client.GetDecisionInstanceAsync( decisionEvaluationInstanceKey); Console.WriteLine($"Decision instance: {result.DecisionDefinitionId}"); } ``` #### SearchDecisionInstancesAsync(DecisionInstanceSearchQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchDecisionInstancesAsync(DecisionInstanceSearchQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search decision instances Search for decision instances based on given criteria. | Parameter | Type | Description | | ------------- | ------------------------------------------------------- | ----------- | | `body` | `DecisionInstanceSearchQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchDecisionInstancesExample() { using var client = CamundaClient.Create(); var result = await client.SearchDecisionInstancesAsync( new DecisionInstanceSearchQuery()); foreach (var di in result.Items) { Console.WriteLine($"Decision instance: {di.DecisionDefinitionId}"); } } ``` ### Decisions #### EvaluateDecisionAsync(DecisionEvaluationInstruction, CancellationToken) ```csharp public Task EvaluateDecisionAsync(DecisionEvaluationInstruction body, CancellationToken ct = default) ``` Evaluate decision Evaluates a decision. You specify the decision to evaluate either by using its unique key (as returned by DeployResource), or using the decision ID. When using the decision ID, the latest deployed version of the decision is used. | Parameter | Type | Description | | --------- | ------------------------------- | ----------- | | `body` | `DecisionEvaluationInstruction` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task EvaluateDecisionByIdExample(DecisionDefinitionId decisionDefinitionId) { using var client = CamundaClient.Create(); var result = await client.EvaluateDecisionAsync(new DecisionEvaluationById { DecisionDefinitionId = decisionDefinitionId, }); Console.WriteLine($"Decision output: {result.Output}"); } public static async Task EvaluateDecisionByKeyExample(DecisionDefinitionKey decisionDefinitionKey) { using var client = CamundaClient.Create(); var result = await client.EvaluateDecisionAsync(new DecisionEvaluationByKey { DecisionDefinitionKey = decisionDefinitionKey, }); Console.WriteLine($"Decision output: {result.Output}"); } ``` ### Audit Logs #### GetAuditLogAsync(AuditLogKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetAuditLogAsync(AuditLogKey auditLogKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get audit log Get an audit log entry by auditLogKey. | Parameter | Type | Description | | ------------- | ------------------------------------ | ----------- | | `auditLogKey` | `AuditLogKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetAuditLogExample(AuditLogKey auditLogKey) { using var client = CamundaClient.Create(); var result = await client.GetAuditLogAsync(auditLogKey); Console.WriteLine($"Audit log: {result.AuditLogKey}"); } ``` #### SearchAuditLogsAsync(AuditLogSearchQueryRequest, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchAuditLogsAsync(AuditLogSearchQueryRequest body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search audit logs Search for audit logs based on given criteria. | Parameter | Type | Description | | ------------- | ----------------------------------------------- | ----------- | | `body` | `AuditLogSearchQueryRequest` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchAuditLogsExample() { using var client = CamundaClient.Create(); var result = await client.SearchAuditLogsAsync( new AuditLogSearchQueryRequest()); foreach (var log in result.Items) { Console.WriteLine($"Audit log: {log.AuditLogKey}"); } } ``` ### Decision Definitions #### GetDecisionDefinitionAsync(DecisionDefinitionKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetDecisionDefinitionAsync(DecisionDefinitionKey decisionDefinitionKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get decision definition Returns a decision definition by key. | Parameter | Type | Description | | ----------------------- | ---------------------------------------------- | ----------- | | `decisionDefinitionKey` | `DecisionDefinitionKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetDecisionDefinitionExample(DecisionDefinitionKey decisionDefinitionKey) { using var client = CamundaClient.Create(); var result = await client.GetDecisionDefinitionAsync( decisionDefinitionKey); Console.WriteLine($"Decision definition: {result.Name}"); } ``` #### GetDecisionDefinitionXmlAsync(DecisionDefinitionKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetDecisionDefinitionXmlAsync(DecisionDefinitionKey decisionDefinitionKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get decision definition XML Returns decision definition as XML. | Parameter | Type | Description | | ----------------------- | ---------------------------- | ----------- | | `decisionDefinitionKey` | `DecisionDefinitionKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetDecisionDefinitionXmlExample(DecisionDefinitionKey decisionDefinitionKey) { using var client = CamundaClient.Create(); var result = await client.GetDecisionDefinitionXmlAsync( decisionDefinitionKey); Console.WriteLine($"XML: {result}"); } ``` #### SearchDecisionDefinitionsAsync(DecisionDefinitionSearchQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchDecisionDefinitionsAsync(DecisionDefinitionSearchQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search decision definitions Search for decision definitions based on given criteria. | Parameter | Type | Description | | ------------- | --------------------------------------------------------- | ----------- | | `body` | `DecisionDefinitionSearchQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchDecisionDefinitionsExample() { using var client = CamundaClient.Create(); var result = await client.SearchDecisionDefinitionsAsync( new DecisionDefinitionSearchQuery()); foreach (var dd in result.Items) { Console.WriteLine($"Decision definition: {dd.Name}"); } } ``` ### Decision Requirements #### GetDecisionRequirementsAsync(DecisionRequirementsKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetDecisionRequirementsAsync(DecisionRequirementsKey decisionRequirementsKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get decision requirements Returns Decision Requirements as JSON. | Parameter | Type | Description | | ------------------------- | ------------------------------------------------ | ----------- | | `decisionRequirementsKey` | `DecisionRequirementsKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetDecisionRequirementsExample(DecisionRequirementsKey decisionRequirementsKey) { using var client = CamundaClient.Create(); var result = await client.GetDecisionRequirementsAsync( decisionRequirementsKey); Console.WriteLine($"DRD: {result.DecisionRequirementsName}"); } ``` #### GetDecisionRequirementsXmlAsync(DecisionRequirementsKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetDecisionRequirementsXmlAsync(DecisionRequirementsKey decisionRequirementsKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get decision requirements XML Returns decision requirements as XML. | Parameter | Type | Description | | ------------------------- | ---------------------------- | ----------- | | `decisionRequirementsKey` | `DecisionRequirementsKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetDecisionRequirementsXmlExample(DecisionRequirementsKey decisionRequirementsKey) { using var client = CamundaClient.Create(); var result = await client.GetDecisionRequirementsXmlAsync( decisionRequirementsKey); Console.WriteLine($"XML: {result}"); } ``` #### SearchDecisionRequirementsAsync(DecisionRequirementsSearchQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchDecisionRequirementsAsync(DecisionRequirementsSearchQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search decision requirements Search for decision requirements based on given criteria. | Parameter | Type | Description | | ------------- | ----------------------------------------------------------- | ----------- | | `body` | `DecisionRequirementsSearchQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchDecisionRequirementsExample() { using var client = CamundaClient.Create(); var result = await client.SearchDecisionRequirementsAsync( new DecisionRequirementsSearchQuery()); foreach (var drd in result.Items) { Console.WriteLine($"DRD: {drd.DecisionRequirementsName}"); } } ``` ### Incidents #### GetIncidentAsync(IncidentKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetIncidentAsync(IncidentKey incidentKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get incident Returns incident as JSON. | Parameter | Type | Description | | ------------- | ------------------------------------ | ----------- | | `incidentKey` | `IncidentKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetIncidentExample(IncidentKey incidentKey) { using var client = CamundaClient.Create(); var result = await client.GetIncidentAsync(incidentKey); Console.WriteLine($"Incident: {result.IncidentKey}"); } ``` #### ResolveIncidentAsync(IncidentKey, IncidentResolutionRequest, CancellationToken) ```csharp public Task ResolveIncidentAsync(IncidentKey incidentKey, IncidentResolutionRequest body, CancellationToken ct = default) ``` Resolve incident Marks the incident as resolved; most likely a call to Update job will be necessary to reset the job's retries, followed by this call. | Parameter | Type | Description | | ------------- | --------------------------- | ----------- | | `incidentKey` | `IncidentKey` | | | `body` | `IncidentResolutionRequest` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task ResolveIncidentExample(IncidentKey incidentKey) { using var client = CamundaClient.Create(); await client.ResolveIncidentAsync( incidentKey, new IncidentResolutionRequest()); } ``` #### SearchElementInstanceIncidentsAsync(ElementInstanceKey, IncidentSearchQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchElementInstanceIncidentsAsync(ElementInstanceKey elementInstanceKey, IncidentSearchQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search for incidents of a specific element instance Search for incidents caused by the specified element instance, including incidents of any child instances created from this element instance. Although the `elementInstanceKey` is provided as a path parameter to indicate the root element instance, you may also include an `elementInstanceKey` within the filter object to narrow results to specific child element instances. This is useful, for example, if you want to isolate incidents associated with nested or subordinate elements within the given element instance while excluding incidents directly tied to the root element itself. | Parameter | Type | Description | | -------------------- | ----------------------------------------------- | ----------- | | `elementInstanceKey` | `ElementInstanceKey` | | | `body` | `IncidentSearchQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchElementInstanceIncidentsExample(ElementInstanceKey elementInstanceKey) { using var client = CamundaClient.Create(); var result = await client.SearchElementInstanceIncidentsAsync( elementInstanceKey, new IncidentSearchQuery()); foreach (var incident in result.Items) { Console.WriteLine($"Incident: {incident.IncidentKey}"); } } ``` #### SearchIncidentsAsync(IncidentSearchQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchIncidentsAsync(IncidentSearchQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search incidents Search for incidents based on given criteria. | Parameter | Type | Description | | ------------- | ----------------------------------------------- | ----------- | | `body` | `IncidentSearchQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchIncidentsExample() { using var client = CamundaClient.Create(); var result = await client.SearchIncidentsAsync(new IncidentSearchQuery()); foreach (var incident in result.Items) { Console.WriteLine($"Incident: {incident.IncidentKey}"); } } ``` ### Process Definitions #### GetProcessDefinitionAsync(ProcessDefinitionKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetProcessDefinitionAsync(ProcessDefinitionKey processDefinitionKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get process definition Returns process definition as JSON. | Parameter | Type | Description | | ---------------------- | --------------------------------------------- | ----------- | | `processDefinitionKey` | `ProcessDefinitionKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetProcessDefinitionExample(ProcessDefinitionKey processDefinitionKey) { using var client = CamundaClient.Create(); var result = await client.GetProcessDefinitionAsync( processDefinitionKey); Console.WriteLine($"Process definition: {result.Name}"); } ``` #### GetProcessDefinitionInstanceStatisticsAsync(ProcessDefinitionInstanceStatisticsQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task GetProcessDefinitionInstanceStatisticsAsync(ProcessDefinitionInstanceStatisticsQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get process instance statistics Get statistics about process instances, grouped by process definition and tenant. | Parameter | Type | Description | | ------------- | -------------------------------------------------------------------- | ----------- | | `body` | `ProcessDefinitionInstanceStatisticsQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetProcessDefinitionInstanceStatisticsExample() { using var client = CamundaClient.Create(); var result = await client.GetProcessDefinitionInstanceStatisticsAsync( new ProcessDefinitionInstanceStatisticsQuery()); foreach (var stat in result.Items) { Console.WriteLine($"Definition: {stat.ProcessDefinitionId}"); } } ``` #### GetProcessDefinitionInstanceVersionStatisticsAsync(ProcessDefinitionInstanceVersionStatisticsQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task GetProcessDefinitionInstanceVersionStatisticsAsync(ProcessDefinitionInstanceVersionStatisticsQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get process instance statistics by version Get statistics about process instances, grouped by version for a given process definition. The process definition ID must be provided as a required field in the request body filter. | Parameter | Type | Description | | ------------- | --------------------------------------------------------------------------- | ----------- | | `body` | `ProcessDefinitionInstanceVersionStatisticsQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetProcessDefinitionInstanceVersionStatisticsExample(ProcessDefinitionId processDefinitionId) { using var client = CamundaClient.Create(); var result = await client.GetProcessDefinitionInstanceVersionStatisticsAsync( new ProcessDefinitionInstanceVersionStatisticsQuery { Filter = new ProcessDefinitionInstanceVersionStatisticsFilter { ProcessDefinitionId = processDefinitionId, }, }); foreach (var stat in result.Items) { Console.WriteLine($"Version: {stat.ProcessDefinitionVersion}"); } } ``` #### GetProcessDefinitionMessageSubscriptionStatisticsAsync(ProcessDefinitionMessageSubscriptionStatisticsQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task GetProcessDefinitionMessageSubscriptionStatisticsAsync(ProcessDefinitionMessageSubscriptionStatisticsQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get message subscription statistics Get message subscription statistics, grouped by process definition. | Parameter | Type | Description | | ------------- | ------------------------------------------------------------------------------- | ----------- | | `body` | `ProcessDefinitionMessageSubscriptionStatisticsQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetProcessDefinitionMessageSubscriptionStatisticsExample() { using var client = CamundaClient.Create(); var result = await client.GetProcessDefinitionMessageSubscriptionStatisticsAsync( new ProcessDefinitionMessageSubscriptionStatisticsQuery()); foreach (var stat in result.Items) { Console.WriteLine($"Message subscriptions: {stat.ActiveSubscriptions}"); } } ``` #### GetProcessDefinitionStatisticsAsync(ProcessDefinitionKey, ProcessDefinitionElementStatisticsQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task GetProcessDefinitionStatisticsAsync(ProcessDefinitionKey processDefinitionKey, ProcessDefinitionElementStatisticsQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get process definition statistics Get statistics about elements in currently running process instances by process definition key and search filter. | Parameter | Type | Description | | ---------------------- | ------------------------------------------------------------------- | ----------- | | `processDefinitionKey` | `ProcessDefinitionKey` | | | `body` | `ProcessDefinitionElementStatisticsQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetProcessDefinitionStatisticsExample(ProcessDefinitionKey processDefinitionKey) { using var client = CamundaClient.Create(); var result = await client.GetProcessDefinitionStatisticsAsync( processDefinitionKey, new ProcessDefinitionElementStatisticsQuery()); foreach (var stat in result.Items) { Console.WriteLine($"Element: {stat.ElementId}"); } } ``` #### GetProcessDefinitionXmlAsync(ProcessDefinitionKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetProcessDefinitionXmlAsync(ProcessDefinitionKey processDefinitionKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get process definition XML Returns process definition as XML. | Parameter | Type | Description | | ---------------------- | ---------------------------- | ----------- | | `processDefinitionKey` | `ProcessDefinitionKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetProcessDefinitionXmlExample(ProcessDefinitionKey processDefinitionKey) { using var client = CamundaClient.Create(); var result = await client.GetProcessDefinitionXmlAsync( processDefinitionKey); Console.WriteLine($"XML: {result}"); } ``` #### GetStartProcessFormAsync(ProcessDefinitionKey, ConsistencyOptions?, CancellationToken) ```csharp public Task GetStartProcessFormAsync(ProcessDefinitionKey processDefinitionKey, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Get process start form Get the start form of a process. Note that this endpoint will only return linked forms. This endpoint does not support embedded forms. | Parameter | Type | Description | | ---------------------- | -------------------------------- | ----------- | | `processDefinitionKey` | `ProcessDefinitionKey` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task GetStartProcessFormExample(ProcessDefinitionKey processDefinitionKey) { using var client = CamundaClient.Create(); var result = await client.GetStartProcessFormAsync( processDefinitionKey); Console.WriteLine($"Form: {result.FormKey}"); } ``` #### SearchProcessDefinitionsAsync(ProcessDefinitionSearchQuery, ConsistencyOptions?, CancellationToken) ```csharp public Task SearchProcessDefinitionsAsync(ProcessDefinitionSearchQuery body, ConsistencyOptions? consistency = null, CancellationToken ct = default) ``` Search process definitions Search for process definitions based on given criteria. | Parameter | Type | Description | | ------------- | -------------------------------------------------------- | ----------- | | `body` | `ProcessDefinitionSearchQuery` | | | `consistency` | `ConsistencyOptions` | | | `ct` | `CancellationToken` | | **Returns:** `Task` **Example** ```csharp public static async Task SearchProcessDefinitionsExample() { using var client = CamundaClient.Create(); var result = await client.SearchProcessDefinitionsAsync( new ProcessDefinitionSearchQuery()); foreach (var pd in result.Items) { Console.WriteLine($"Process definition: {pd.Name}"); } } ``` --- ## Configuration(Api-reference) :::caution Technical Preview The C# SDK is a **technical preview** available from Camunda 8.9. It will become fully supported in Camunda 8.10. Its API surface may change in future releases without following semver. ::: Configuration and authentication types for the Camunda C# SDK. ## CamundaOptions Options for constructing a . Mirrors the JS SDK's CamundaOptions with idiomatic C# conventions. ```csharp public sealed class CamundaOptions ``` ### Properties | Property | Type | Description | | -------------------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Config` | `Dictionary` | Strongly typed env-style overrides (CAMUNDA\_\* keys). | | `Configuration` | `IConfiguration` | An section (typically configuration.GetSection("Camunda")) to bind settings from appsettings.json or any other configuration provider. Keys use PascalCase property names (e.g. RestAddress, Auth:Strategy) and are mapped to the canonical CAMUNDA\_\* env-var names internally. Precedence (highest wins): > > environment variables > defaults. | | `HttpClient` | `HttpClient` | Custom HttpClient factory. If not provided, a default HttpClient is created. | | `HttpMessageHandler` | `HttpMessageHandler` | Custom HttpMessageHandler for the internal HttpClient (ignored if HttpClient is set). Useful for tests (e.g., MockHttpMessageHandler). | | `Env` | `Dictionary` | Provide a custom env map (mainly for tests). Defaults to Environment.GetEnvironmentVariable. | | `LoggerFactory` | `ILoggerFactory` | Logger factory for SDK logging. | ## CamundaConfig Hydrated Camunda configuration. Immutable after construction. ```csharp public sealed class CamundaConfig ``` ### Properties | Property | Type | Description | | ----------------- | ---------------------- | ----------- | | `RestAddress` | `String` | | | `TokenAudience` | `String` | | | `DefaultTenantId` | `String` | | | `HttpRetry` | `HttpRetryConfig` | | | `Backpressure` | `BackpressureConfig` | | | `OAuth` | `OAuthConfig` | | | `Auth` | `AuthConfig` | | | `Validation` | `ValidationConfig` | | | `LogLevel` | `String` | | | `Eventual` | `EventualConfig` | | | `WorkerDefaults` | `WorkerDefaultsConfig` | | | `Tls` | `TlsConfig` | | ## ConfigurationHydrator Hydrates a from environment variables and overrides. Mirrors the JS SDK's hydrateConfig function. ```csharp public static class ConfigurationHydrator ``` ## AuthConfig ```csharp public sealed class AuthConfig ``` ### Properties | Property | Type | Description | | ---------- | ----------------- | ----------- | | `Strategy` | `AuthStrategy` | | | `Basic` | `BasicAuthConfig` | | ## AuthStrategy Supported authentication strategies. ```csharp public enum AuthStrategy ``` | Value | Description | | ------- | ----------- | | `None` | | | `OAuth` | | | `Basic` | | ## BasicAuthConfig ```csharp public sealed class BasicAuthConfig ``` ### Properties | Property | Type | Description | | ---------- | -------- | ----------- | | `Username` | `String` | | | `Password` | `String` | | ## OAuthConfig ```csharp public sealed class OAuthConfig ``` ### Properties | Property | Type | Description | | -------------- | ------------------ | ----------- | | `ClientId` | `String` | | | `ClientSecret` | `String` | | | `OAuthUrl` | `String` | | | `GrantType` | `String` | | | `Scope` | `String` | | | `TimeoutMs` | `Int32` | | | `Retry` | `OAuthRetryConfig` | | ## OAuthRetryConfig ```csharp public sealed class OAuthRetryConfig ``` ### Properties | Property | Type | Description | | ------------- | ------- | ----------- | | `Max` | `Int32` | | | `BaseDelayMs` | `Int32` | | ## HttpRetryConfig ```csharp public sealed class HttpRetryConfig ``` ### Properties | Property | Type | Description | | ------------- | ------- | ----------- | | `MaxAttempts` | `Int32` | | | `BaseDelayMs` | `Int32` | | | `MaxDelayMs` | `Int32` | | ## BackpressureConfig ```csharp public sealed class BackpressureConfig ``` ### Properties | Property | Type | Description | | -------------------- | --------- | ----------- | | `Enabled` | `Boolean` | | | `Profile` | `String` | | | `ObserveOnly` | `Boolean` | | | `InitialMax` | `Int32` | | | `SoftFactor` | `Double` | | | `SevereFactor` | `Double` | | | `RecoveryIntervalMs` | `Int32` | | | `RecoveryStep` | `Int32` | | | `DecayQuietMs` | `Int32` | | | `Floor` | `Int32` | | | `SevereThreshold` | `Int32` | | ## EventualConfig ```csharp public sealed class EventualConfig ``` ### Properties | Property | Type | Description | | --------------- | ------- | ----------- | | `PollDefaultMs` | `Int32` | | ## ValidationConfig ```csharp public sealed class ValidationConfig ``` ### Properties | Property | Type | Description | | ---------- | ---------------- | ----------- | | `Request` | `ValidationMode` | | | `Response` | `ValidationMode` | | | `Raw` | `String` | | ## ValidationMode Validation modes for request/response validation. ```csharp public enum ValidationMode ``` | Value | Description | | ----------- | ----------- | | `None` | | | `Warn` | | | `Strict` | | | `Fanatical` | | ## JobWorkerConfig Configuration for a . ```csharp public sealed class JobWorkerConfig ``` ### Properties | Property | Type | Description | | ------------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `JobType` | `String` | The BPMN job type to subscribe to (e.g. "payment-service"). | | `JobTimeoutMs` | `Nullable` | How long (in ms) the job is reserved for this worker before the broker makes it available to other workers. Falls back to CAMUNDA_WORKER_TIMEOUT environment variable. | | `MaxConcurrentJobs` | `Nullable` | Maximum number of jobs that may be in-flight (activated and being handled) concurrently by this worker. Controls how many jobs are requested per poll and how many handler tasks run in parallel. For I/O-bound handlers (HTTP calls, database queries), higher values (32–128) improve throughput because async handlers release threads during awaits. For CPU-bound handlers, set to or lower to avoid over-subscribing the thread pool. Set to 1 for sequential (single-job-at-a-time) processing. Falls back to CAMUNDA_WORKER_MAX_CONCURRENT_JOBS environment variable, then 10. | | `PollIntervalMs` | `Int32` | Delay (in ms) between poll cycles when no jobs are available or when at capacity. Default: 500 ms. | | `PollTimeoutMs` | `Nullable` | Long-poll timeout (in ms) sent to the broker. The broker holds the activation request open until jobs are available or this timeout elapses. null or 0 = broker default; negative = long polling disabled. | | `FetchVariables` | `List` | Variable names to fetch from the process instance scope. null = fetch all. | | `WorkerName` | `String` | Worker name sent to the broker for logging and diagnostics. Auto-generated if not set. | | `AutoStart` | `Boolean` | Whether to start polling immediately on creation. Default: true. | | `StartupJitterMaxSeconds` | `Double` | Maximum random delay (in seconds) before the worker starts polling. When multiple application instances restart simultaneously, this spreads out initial activation requests to avoid saturating the server. 0 (the default) means no delay. | | `TenantIds` | `IReadOnlyList` | Restrict job activation to the given tenant IDs (multi-tenant setups). Cannot be combined with — setting both is rejected with . If neither nor is set (or is empty), the activation request falls back to [CamundaConfig.DefaultTenantId] (which itself defaults to "<default>" and can be overridden via the CAMUNDA_DEFAULT_TENANT_ID environment variable). | | `TenantId` | `String` | Convenience for the common single-tenant case. Equivalent to setting to [TenantId]. Cannot be combined with . | ## ConfigErrorCode Configuration hydration errors. ```csharp public enum ConfigErrorCode ``` | Value | Description | | ------------------------- | ----------- | | `MissingRequired` | | | `InvalidEnum` | | | `InvalidBoolean` | | | `InvalidInteger` | | | `InvalidValidationSyntax` | | ## ConfigErrorDetail ```csharp public sealed class ConfigErrorDetail ``` ### Properties | Property | Type | Description | | --------- | ----------------- | ----------- | | `Key` | `String` | | | `Code` | `ConfigErrorCode` | | | `Message` | `String` | | --- ## Enums :::caution Technical Preview The C# SDK is a **technical preview** available from Camunda 8.9. It will become fully supported in Camunda 8.10. Its API surface may change in future releases without following semver. ::: Enumeration types (77 enums). ## AgentInstanceSearchQuerySortRequestField The field to sort by. | Value | Description | | ----------------- | ----------- | | `CreationDate` | | | `LastUpdatedDate` | | | `CompletionDate` | | | `Status` | | ## AgentInstanceStatusEnum The current status of an agent instance. | Value | Description | | --------------- | ----------- | | `COMPLETED` | | | `IDLE` | | | `INITIALIZING` | | | `THINKING` | | | `TOOLCALLING` | | | `TOOLDISCOVERY` | | ## AuditLogActorTypeEnum The type of actor who performed the operation. | Value | Description | | ----------- | ----------- | | `ANONYMOUS` | | | `CLIENT` | | | `UNKNOWN` | | | `USER` | | ## AuditLogCategoryEnum The category of the audit log operation. | Value | Description | | ------------------- | ----------- | | `ADMIN` | | | `DEPLOYEDRESOURCES` | | | `USERTASKS` | | ## AuditLogEntityTypeEnum The type of entity affected by the operation. | Value | Description | | ----------------- | ----------- | | `AUTHORIZATION` | | | `BATCH` | | | `DECISION` | | | `GROUP` | | | `INCIDENT` | | | `JOB` | | | `MAPPINGRULE` | | | `PROCESSINSTANCE` | | | `RESOURCE` | | | `ROLE` | | | `TENANT` | | | `USER` | | | `USERTASK` | | | `VARIABLE` | | | `CLIENT` | | ## AuditLogOperationTypeEnum The type of operation performed. | Value | Description | | ---------- | ----------- | | `ASSIGN` | | | `CANCEL` | | | `COMPLETE` | | | `CREATE` | | | `DELETE` | | | `EVALUATE` | | | `MIGRATE` | | | `MODIFY` | | | `RESOLVE` | | | `RESUME` | | | `SUSPEND` | | | `UNASSIGN` | | | `UNKNOWN` | | | `UPDATE` | | ## AuditLogResultEnum The result status of the operation. | Value | Description | | --------- | ----------- | | `FAIL` | | | `SUCCESS` | | ## AuditLogSearchQuerySortRequestField The field to sort by. | Value | Description | | ------------------------- | ----------- | | `ActorId` | | | `ActorType` | | | `AuditLogKey` | | | `BatchOperationKey` | | | `BatchOperationType` | | | `Category` | | | `DecisionDefinitionId` | | | `DecisionDefinitionKey` | | | `DecisionEvaluationKey` | | | `DecisionRequirementsId` | | | `DecisionRequirementsKey` | | | `ElementInstanceKey` | | | `EntityKey` | | | `EntityType` | | | `JobKey` | | | `OperationType` | | | `ProcessDefinitionId` | | | `ProcessDefinitionKey` | | | `ProcessInstanceKey` | | | `Result` | | | `TenantId` | | | `Timestamp` | | | `UserTaskKey` | | ## AuthorizationSearchQuerySortRequestField The field to sort by. | Value | Description | | ---------------------- | ----------- | | `OwnerId` | | | `OwnerType` | | | `ResourceId` | | | `ResourcePropertyName` | | | `ResourceType` | | ## BatchOperationErrorType The type of the error that occurred during the batch operation. | Value | Description | | -------------------------- | ----------- | | `QUERYFAILED` | | | `RESULTBUFFERSIZEEXCEEDED` | | ## BatchOperationItemResponseState State of the item. | Value | Description | | ----------- | ----------- | | `ACTIVE` | | | `COMPLETED` | | | `SKIPPED` | | | `CANCELED` | | | `FAILED` | | ## BatchOperationItemSearchQuerySortRequestField The field to sort by. | Value | Description | | -------------------- | ----------- | | `BatchOperationKey` | | | `ItemKey` | | | `ProcessInstanceKey` | | | `ProcessedDate` | | | `State` | | ## BatchOperationItemStateEnum The batch operation item state. | Value | Description | | ----------- | ----------- | | `ACTIVE` | | | `COMPLETED` | | | `CANCELED` | | | `FAILED` | | ## BatchOperationSearchQuerySortRequestField The field to sort by. | Value | Description | | ------------------- | ----------- | | `BatchOperationKey` | | | `OperationType` | | | `State` | | | `StartDate` | | | `EndDate` | | | `ActorType` | | | `ActorId` | | ## BatchOperationStateEnum The batch operation state. | Value | Description | | -------------------- | ----------- | | `ACTIVE` | | | `CANCELED` | | | `COMPLETED` | | | `CREATED` | | | `FAILED` | | | `PARTIALLYCOMPLETED` | | | `SUSPENDED` | | ## BatchOperationTypeEnum The type of the batch operation. | Value | Description | | -------------------------- | ----------- | | `ADDVARIABLE` | | | `CANCELPROCESSINSTANCE` | | | `DELETEDECISIONDEFINITION` | | | `DELETEDECISIONINSTANCE` | | | `DELETEPROCESSDEFINITION` | | | `DELETEPROCESSINSTANCE` | | | `MIGRATEPROCESSINSTANCE` | | | `MODIFYPROCESSINSTANCE` | | | `RESOLVEINCIDENT` | | | `UPDATEVARIABLE` | | ## CamundaAuthErrorCode Auth error codes matching the JS SDK. | Value | Description | | ------------------------- | ----------- | | `TokenFetchFailed` | | | `TokenParseFailed` | | | `TokenExpired` | | | `OAuthConfigMissing` | | | `BasicCredentialsMissing` | | ## CloudStage The cloud deployment stage. | Value | Description | | ------ | ----------- | | `Dev` | | | `Int` | | | `Prod` | | ## ClusterVariableScopeEnum The scope of a cluster variable. | Value | Description | | -------- | ----------- | | `GLOBAL` | | | `TENANT` | | ## ClusterVariableSearchQuerySortRequestField The field to sort by. | Value | Description | | ---------- | ----------- | | `Name` | | | `Value` | | | `TenantId` | | | `Scope` | | ## CorrelatedMessageSubscriptionSearchQuerySortRequestField The field to sort by. | Value | Description | | ---------------------- | ----------- | | `CorrelationKey` | | | `CorrelationTime` | | | `ElementId` | | | `ElementInstanceKey` | | | `MessageKey` | | | `MessageName` | | | `PartitionId` | | | `ProcessDefinitionId` | | | `ProcessDefinitionKey` | | | `ProcessInstanceKey` | | | `SubscriptionKey` | | | `TenantId` | | ## DecisionDefinitionSearchQuerySortRequestField The field to sort by. | Value | Description | | ----------------------------- | ----------- | | `DecisionDefinitionKey` | | | `DecisionDefinitionId` | | | `Name` | | | `Version` | | | `DecisionRequirementsId` | | | `DecisionRequirementsKey` | | | `DecisionRequirementsName` | | | `DecisionRequirementsVersion` | | | `TenantId` | | ## DecisionDefinitionTypeEnum The type of the decision. UNSPECIFIED is deprecated and should not be used anymore, for removal in 8.10 | Value | Description | | ------------------- | ----------- | | `DECISIONTABLE` | | | `LITERALEXPRESSION` | | | `UNSPECIFIED` | | | `UNKNOWN` | | ## DecisionInstanceSearchQuerySortRequestField The field to sort by. | Value | Description | | ------------------------------- | ----------- | | `DecisionDefinitionId` | | | `DecisionDefinitionKey` | | | `DecisionDefinitionName` | | | `DecisionDefinitionType` | | | `DecisionDefinitionVersion` | | | `DecisionEvaluationInstanceKey` | | | `DecisionEvaluationKey` | | | `ElementInstanceKey` | | | `EvaluationDate` | | | `EvaluationFailure` | | | `ProcessDefinitionKey` | | | `ProcessInstanceKey` | | | `RootDecisionDefinitionKey` | | | `State` | | | `TenantId` | | ## DecisionInstanceStateEnum The state of the decision instance. UNSPECIFIED and UNKNOWN are deprecated and should not be used anymore, for removal in 8.10 | Value | Description | | ------------- | ----------- | | `EVALUATED` | | | `FAILED` | | | `UNSPECIFIED` | | | `UNKNOWN` | | ## DecisionRequirementsSearchQuerySortRequestField The field to sort by. | Value | Description | | -------------------------- | ----------- | | `DecisionRequirementsKey` | | | `DecisionRequirementsName` | | | `Version` | | | `DecisionRequirementsId` | | | `TenantId` | | ## DocumentReferenceCamundaDocumentType Document discriminator. Always set to "camunda". | Value | Description | | --------- | ----------- | | `Camunda` | | ## ElementInstanceFilterType Type of element as defined set of values. | Value | Description | | ------------------------------ | ----------- | | `UNSPECIFIED` | | | `PROCESS` | | | `SUBPROCESS` | | | `EVENTSUBPROCESS` | | | `ADHOCSUBPROCESS` | | | `ADHOCSUBPROCESSINNERINSTANCE` | | | `STARTEVENT` | | | `INTERMEDIATECATCHEVENT` | | | `INTERMEDIATETHROWEVENT` | | | `BOUNDARYEVENT` | | | `ENDEVENT` | | | `SERVICETASK` | | | `RECEIVETASK` | | | `USERTASK` | | | `MANUALTASK` | | | `TASK` | | | `EXCLUSIVEGATEWAY` | | | `INCLUSIVEGATEWAY` | | | `PARALLELGATEWAY` | | | `EVENTBASEDGATEWAY` | | | `SEQUENCEFLOW` | | | `MULTIINSTANCEBODY` | | | `CALLACTIVITY` | | | `BUSINESSRULETASK` | | | `SCRIPTTASK` | | | `SENDTASK` | | | `UNKNOWN` | | ## ElementInstanceResultType Type of element as defined set of values. | Value | Description | | ------------------------------ | ----------- | | `UNSPECIFIED` | | | `PROCESS` | | | `SUBPROCESS` | | | `EVENTSUBPROCESS` | | | `ADHOCSUBPROCESS` | | | `ADHOCSUBPROCESSINNERINSTANCE` | | | `STARTEVENT` | | | `INTERMEDIATECATCHEVENT` | | | `INTERMEDIATETHROWEVENT` | | | `BOUNDARYEVENT` | | | `ENDEVENT` | | | `SERVICETASK` | | | `RECEIVETASK` | | | `USERTASK` | | | `MANUALTASK` | | | `TASK` | | | `EXCLUSIVEGATEWAY` | | | `INCLUSIVEGATEWAY` | | | `PARALLELGATEWAY` | | | `EVENTBASEDGATEWAY` | | | `SEQUENCEFLOW` | | | `MULTIINSTANCEBODY` | | | `CALLACTIVITY` | | | `BUSINESSRULETASK` | | | `SCRIPTTASK` | | | `SENDTASK` | | | `UNKNOWN` | | ## ElementInstanceSearchQuerySortRequestField The field to sort by. | Value | Description | | ---------------------- | ----------- | | `ElementInstanceKey` | | | `ProcessInstanceKey` | | | `ProcessDefinitionKey` | | | `ProcessDefinitionId` | | | `StartDate` | | | `EndDate` | | | `ElementId` | | | `ElementName` | | | `Type` | | | `State` | | | `IncidentKey` | | | `TenantId` | | ## ElementInstanceStateEnum Element states | Value | Description | | ------------ | ----------- | | `ACTIVE` | | | `COMPLETED` | | | `TERMINATED` | | ## GlobalListenerSourceEnum How the global listener was defined. | Value | Description | | --------------- | ----------- | | `CONFIGURATION` | | | `API` | | ## GlobalTaskListenerEventTypeEnum The event type that triggers the user task listener. | Value | Description | | ------------ | ----------- | | `All` | | | `Creating` | | | `Assigning` | | | `Updating` | | | `Completing` | | | `Canceling` | | ## GlobalTaskListenerSearchQuerySortRequestField The field to sort by. | Value | Description | | ---------------- | ----------- | | `Id` | | | `Type` | | | `AfterNonGlobal` | | | `Priority` | | | `Source` | | ## GroupClientSearchQuerySortRequestField The field to sort by. | Value | Description | | ---------- | ----------- | | `ClientId` | | ## GroupSearchQuerySortRequestField The field to sort by. | Value | Description | | --------- | ----------- | | `Name` | | | `GroupId` | | ## GroupUserSearchQuerySortRequestField The field to sort by. | Value | Description | | ---------- | ----------- | | `Username` | | ## IncidentErrorTypeEnum Incident error type with a defined set of values. | Value | Description | | ---------------------------- | ----------- | | `ADHOCSUBPROCESSNORETRIES` | | | `CALLEDDECISIONERROR` | | | `CALLEDELEMENTERROR` | | | `CONDITIONERROR` | | | `DECISIONEVALUATIONERROR` | | | `EXECUTIONLISTENERNORETRIES` | | | `EXTRACTVALUEERROR` | | | `FORMNOTFOUND` | | | `IOMAPPINGERROR` | | | `JOBNORETRIES` | | | `MESSAGESIZEEXCEEDED` | | | `RESOURCENOTFOUND` | | | `TASKLISTENERNORETRIES` | | | `UNHANDLEDERROREVENT` | | | `UNKNOWN` | | | `UNSPECIFIED` | | ## IncidentProcessInstanceStatisticsByDefinitionQuerySortRequestField The aggregated field by which the process instance statistics are sorted. | Value | Description | | ------------------------------- | ----------- | | `ActiveInstancesWithErrorCount` | | | `ProcessDefinitionKey` | | | `TenantId` | | ## IncidentProcessInstanceStatisticsByErrorQuerySortRequestField The field to sort the incident error statistics by. | Value | Description | | ------------------------------- | ----------- | | `ErrorMessage` | | | `ActiveInstancesWithErrorCount` | | ## IncidentSearchQuerySortRequestField The field to sort by. | Value | Description | | ---------------------- | ----------- | | `IncidentKey` | | | `ProcessDefinitionKey` | | | `ProcessDefinitionId` | | | `ProcessInstanceKey` | | | `ErrorType` | | | `ElementId` | | | `ElementInstanceKey` | | | `CreationTime` | | | `State` | | | `JobKey` | | | `TenantId` | | ## IncidentStateEnum Incident states with a defined set of values. | Value | Description | | ---------- | ----------- | | `ACTIVE` | | | `MIGRATED` | | | `PENDING` | | | `RESOLVED` | | | `UNKNOWN` | | ## JobKindEnum The job kind. | Value | Description | | ------------------- | ----------- | | `BPMNELEMENT` | | | `EXECUTIONLISTENER` | | | `TASKLISTENER` | | | `ADHOCSUBPROCESS` | | ## JobListenerEventTypeEnum The listener event type of the job. | Value | Description | | ------------- | ----------- | | `ASSIGNING` | | | `BEFOREALL` | | | `CANCELING` | | | `COMPLETING` | | | `CREATING` | | | `END` | | | `START` | | | `UNSPECIFIED` | | | `UPDATING` | | ## JobSearchQuerySortRequestField The field to sort by. | Value | Description | | -------------------------- | ----------- | | `Deadline` | | | `DeniedReason` | | | `ElementId` | | | `ElementInstanceKey` | | | `EndTime` | | | `ErrorCode` | | | `ErrorMessage` | | | `HasFailedWithRetriesLeft` | | | `IsDenied` | | | `JobKey` | | | `Kind` | | | `ListenerEventType` | | | `ProcessDefinitionId` | | | `ProcessDefinitionKey` | | | `ProcessInstanceKey` | | | `Retries` | | | `State` | | | `TenantId` | | | `Type` | | | `Worker` | | ## JobStateEnum The state of the job. | Value | Description | | ---------------- | ----------- | | `CANCELED` | | | `COMPLETED` | | | `CREATED` | | | `ERRORTHROWN` | | | `FAILED` | | | `MIGRATED` | | | `RETRIESUPDATED` | | | `TIMEDOUT` | | ## MappingRuleSearchQuerySortRequestField The field to sort by. | Value | Description | | --------------- | ----------- | | `MappingRuleId` | | | `ClaimName` | | | `ClaimValue` | | | `Name` | | ## MessageSubscriptionSearchQuerySortRequestField The field to sort by. | Value | Description | | -------------------------- | ----------- | | `MessageSubscriptionKey` | | | `ProcessDefinitionId` | | | `ProcessDefinitionName` | | | `ProcessDefinitionVersion` | | | `ProcessInstanceKey` | | | `ElementId` | | | `ElementInstanceKey` | | | `MessageSubscriptionState` | | | `MessageSubscriptionType` | | | `LastUpdatedDate` | | | `MessageName` | | | `CorrelationKey` | | | `TenantId` | | | `ToolName` | | | `InboundConnectorType` | | ## MessageSubscriptionStateEnum The state of message subscription. | Value | Description | | ------------ | ----------- | | `CORRELATED` | | | `CREATED` | | | `DELETED` | | | `MIGRATED` | | ## MessageSubscriptionTypeEnum The type of message subscription. `START_EVENT` is definition-scoped (process start events). Always has a value; only captured from Camunda 8.10 onwards. `PROCESS_EVENT` is instance-scoped (intermediate catch events). Pre-8.10 entries have no value stored; the API returns `PROCESS_EVENT` as a default for those entries. | Value | Description | | -------------- | ----------- | | `STARTEVENT` | | | `PROCESSEVENT` | | ## OwnerTypeEnum The type of the owner of permissions. | Value | Description | | ------------- | ----------- | | `USER` | | | `CLIENT` | | | `ROLE` | | | `GROUP` | | | `MAPPINGRULE` | | | `UNSPECIFIED` | | ## PartitionHealth Describes the current health of the partition. | Value | Description | | ----------- | ----------- | | `Healthy` | | | `Unhealthy` | | | `Dead` | | ## PartitionRole Describes the Raft role of the broker for a given partition. | Value | Description | | ---------- | ----------- | | `Leader` | | | `Follower` | | | `Inactive` | | ## PermissionTypeEnum Specifies the type of permissions. | Value | Description | | ---------------------------------------------- | ----------- | | `ACCESS` | | | `CANCELPROCESSINSTANCE` | | | `CLAIM` | | | `CLAIMUSERTASK` | | | `COMPLETE` | | | `COMPLETEUSERTASK` | | | `CREATE` | | | `CREATEBATCHOPERATIONCANCELPROCESSINSTANCE` | | | `CREATEBATCHOPERATIONDELETEDECISIONDEFINITION` | | | `CREATEBATCHOPERATIONDELETEDECISIONINSTANCE` | | | `CREATEBATCHOPERATIONDELETEPROCESSDEFINITION` | | | `CREATEBATCHOPERATIONDELETEPROCESSINSTANCE` | | | `CREATEBATCHOPERATIONMIGRATEPROCESSINSTANCE` | | | `CREATEBATCHOPERATIONMODIFYPROCESSINSTANCE` | | | `CREATEBATCHOPERATIONRESOLVEINCIDENT` | | | `CREATEDECISIONINSTANCE` | | | `CREATEPROCESSINSTANCE` | | | `CREATETASKLISTENER` | | | `DELETE` | | | `DELETEDECISIONINSTANCE` | | | `DELETEDRD` | | | `DELETEFORM` | | | `DELETEPROCESS` | | | `DELETEPROCESSINSTANCE` | | | `DELETERESOURCE` | | | `DELETETASKLISTENER` | | | `EVALUATE` | | | `MODIFYPROCESSINSTANCE` | | | `READ` | | | `READDECISIONDEFINITION` | | | `READDECISIONINSTANCE` | | | `READJOBMETRIC` | | | `READPROCESSDEFINITION` | | | `READPROCESSINSTANCE` | | | `READUSAGEMETRIC` | | | `READUSERTASK` | | | `READTASKLISTENER` | | | `UPDATE` | | | `UPDATEPROCESSINSTANCE` | | | `UPDATEUSERTASK` | | | `UPDATETASKLISTENER` | | ## ProcessDefinitionInstanceStatisticsQuerySortRequestField The field to sort by. | Value | Description | | ------------------------------------- | ----------- | | `ProcessDefinitionId` | | | `ActiveInstancesWithIncidentCount` | | | `ActiveInstancesWithoutIncidentCount` | | ## ProcessDefinitionInstanceVersionStatisticsQuerySortRequestField The field to sort by. | Value | Description | | ------------------------------------- | ----------- | | `ProcessDefinitionId` | | | `ProcessDefinitionKey` | | | `ProcessDefinitionName` | | | `ProcessDefinitionVersion` | | | `ActiveInstancesWithIncidentCount` | | | `ActiveInstancesWithoutIncidentCount` | | ## ProcessDefinitionSearchQuerySortRequestField The field to sort by. | Value | Description | | ---------------------- | ----------- | | `ProcessDefinitionKey` | | | `Name` | | | `ResourceName` | | | `Version` | | | `VersionTag` | | | `ProcessDefinitionId` | | | `TenantId` | | ## ProcessInstanceSearchQuerySortRequestField The field to sort by. | Value | Description | | ----------------------------- | ----------- | | `ProcessInstanceKey` | | | `ProcessDefinitionId` | | | `ProcessDefinitionName` | | | `ProcessDefinitionVersion` | | | `ProcessDefinitionVersionTag` | | | `ProcessDefinitionKey` | | | `ParentProcessInstanceKey` | | | `ParentElementInstanceKey` | | | `StartDate` | | | `EndDate` | | | `State` | | | `HasIncident` | | | `TenantId` | | | `BusinessId` | | ## ProcessInstanceStateEnum Process instance states | Value | Description | | ------------ | ----------- | | `ACTIVE` | | | `COMPLETED` | | | `TERMINATED` | | ## ResourceSearchQuerySortRequestField The field to sort by. | Value | Description | | --------------- | ----------- | | `ResourceKey` | | | `ResourceName` | | | `ResourceId` | | | `Version` | | | `VersionTag` | | | `DeploymentKey` | | | `TenantId` | | ## ResourceTypeEnum The type of resource to add/remove permissions to/from. | Value | Description | | -------------------------------- | ----------- | | `AUDITLOG` | | | `AUTHORIZATION` | | | `BATCH` | | | `CLUSTERVARIABLE` | | | `COMPONENT` | | | `DECISIONDEFINITION` | | | `DECISIONREQUIREMENTSDEFINITION` | | | `DOCUMENT` | | | `EXPRESSION` | | | `GLOBALLISTENER` | | | `GROUP` | | | `MAPPINGRULE` | | | `MESSAGE` | | | `PROCESSDEFINITION` | | | `RESOURCE` | | | `ROLE` | | | `SYSTEM` | | | `TENANT` | | | `USER` | | | `USERTASK` | | ## RoleClientSearchQuerySortRequestField The field to sort by. | Value | Description | | ---------- | ----------- | | `ClientId` | | ## RoleGroupSearchQuerySortRequestField The field to sort by. | Value | Description | | --------- | ----------- | | `GroupId` | | ## RoleSearchQuerySortRequestField The field to sort by. | Value | Description | | -------- | ----------- | | `Name` | | | `RoleId` | | ## RoleUserSearchQuerySortRequestField The field to sort by. | Value | Description | | ---------- | ----------- | | `Username` | | ## SortOrderEnum The order in which to sort the related field. | Value | Description | | ------ | ----------- | | `ASC` | | | `DESC` | | ## TenantClientSearchQuerySortRequestField The field to sort by. | Value | Description | | ---------- | ----------- | | `ClientId` | | ## TenantFilterEnum The tenant filtering strategy for job activation. Determines whether to use tenant IDs provided in the request or tenant IDs assigned to the authenticated principal. | Value | Description | | ---------- | ----------- | | `PROVIDED` | | | `ASSIGNED` | | ## TenantGroupSearchQuerySortRequestField The field to sort by. | Value | Description | | --------- | ----------- | | `GroupId` | | ## TenantSearchQuerySortRequestField The field to sort by. | Value | Description | | ---------- | ----------- | | `Key` | | | `Name` | | | `TenantId` | | ## TenantUserSearchQuerySortRequestField The field to sort by. | Value | Description | | ---------- | ----------- | | `Username` | | ## UserSearchQuerySortRequestField The field to sort by. | Value | Description | | ---------- | ----------- | | `Username` | | | `Name` | | | `Email` | | ## UserTaskSearchQuerySortRequestField The field to sort by. | Value | Description | | ---------------- | ----------- | | `CreationDate` | | | `CompletionDate` | | | `FollowUpDate` | | | `DueDate` | | | `Priority` | | | `Name` | | ## UserTaskStateEnum The state of the user task. Note: FAILED state is only for legacy job-worker-based tasks. | Value | Description | | ------------ | ----------- | | `CREATING` | | | `CREATED` | | | `ASSIGNING` | | | `UPDATING` | | | `COMPLETING` | | | `COMPLETED` | | | `CANCELING` | | | `CANCELED` | | | `FAILED` | | ## UserTaskVariableSearchQuerySortRequestField The field to sort by. | Value | Description | | -------------------- | ----------- | | `Value` | | | `Name` | | | `TenantId` | | | `VariableKey` | | | `ScopeKey` | | | `ProcessInstanceKey` | | ## VariableSearchQuerySortRequestField The field to sort by. | Value | Description | | -------------------- | ----------- | | `Value` | | | `Name` | | | `TenantId` | | | `VariableKey` | | | `ScopeKey` | | | `ProcessInstanceKey` | | ## WebappComponent A Camunda webapp component name. | Value | Description | | ---------- | ----------- | | `Operate` | | | `Tasklist` | | | `Admin` | | --- ## C# SDK API Reference :::caution Technical Preview The C# SDK is a **technical preview** available from Camunda 8.9. It will become fully supported in Camunda 8.10. Its API surface may change in future releases without following semver. ::: Auto-generated from the Camunda C# SDK source code. ## Sections - [CamundaClient](camunda-client.md) — Main client class with all API methods (3 types) - [Configuration](configuration.md) — SDK configuration, authentication, and options (16 types) - [Runtime](runtime.md) — Runtime infrastructure: job workers, backpressure, polling, errors (0 types) - [Models](models.md) — Request and response model classes (560 types) - [Enums](enums.md) — Enumeration types (77 types) - [Keys](keys.md) — Strongly-typed domain key types (26 types) --- ## Keys # Key Types :::caution Technical Preview The C# SDK is a **technical preview** available from Camunda 8.9. It will become fully supported in Camunda 8.10. Its API surface may change in future releases without following semver. ::: Strongly-typed domain key types provide compile-time safety for entity identifiers. Each key wraps a string value and ensures type-safe API calls. ## Overview | Key Type | Description | | ------------------------------- | ------------------------------------------------------------------------------------- | | `AgentInstanceKey` | System-generated key for an agent instance. | | `AuditLogEntityKey` | System-generated entity key for an audit log entry. | | `AuditLogKey` | System-generated key for an audit log entry. | | `AuthorizationKey` | System-generated key for an authorization. | | `BatchOperationKey` | System-generated key for an batch operation. | | `ConditionalEvaluationKey` | System-generated key for a conditional evaluation. | | `DecisionDefinitionKey` | System-generated key for a decision definition. | | `DecisionEvaluationInstanceKey` | System-generated identifier for a decision evaluation instance. It is composed of the | parent decision evaluation key and the 1-based index of the evaluated decision within that evaluation, joined by a hyphen (format: `-`). | | `DecisionEvaluationKey` | System-generated key for a decision evaluation. | | `DecisionInstanceKey` | System-generated key for a deployed decision instance. | | `DecisionRequirementsKey` | System-generated key for a deployed decision requirements definition. | | `DeploymentKey` | Key for a deployment. | | `ElementInstanceKey` | System-generated key for a element instance. | | `FormKey` | System-generated key for a deployed form. | | `IncidentKey` | System-generated key for a incident. | | `JobKey` | System-generated key for a job. | | `LongKey` | Zeebe Engine resource key (Java long serialized as string) | | `MessageKey` | System-generated key for an message. | | `MessageSubscriptionKey` | System-generated key for a message subscription. | | `ProcessDefinitionKey` | System-generated key for a deployed process definition. | | `ProcessInstanceKey` | System-generated key for a process instance. | | `ResourceKey` | The system-assigned key for this resource. | | `ScopeKey` | System-generated key for a scope. A scope can hold variables and represents either an element instance in a BPMN process or the process instance itself. | | `SignalKey` | System-generated key for an signal. | | `UserTaskKey` | System-generated key for a user task. | | `VariableKey` | System-generated key for a variable. | ## Common Methods All key types share these methods: | Method | Description | | ---------------------- | ------------------------------------------------ | | `AssumeExists(string)` | Creates a key from a known-valid string value. | | `IsValid(string)` | Validates whether a string is a valid key value. | | `Value` | Gets the underlying string value. | | `ToString()` | Returns the string representation. | ## Details ### AgentInstanceKey System-generated key for an agent instance. ```csharp public readonly record struct AgentInstanceKey : ICamundaKey, IEquatable ``` ### AuditLogEntityKey System-generated entity key for an audit log entry. ```csharp public readonly record struct AuditLogEntityKey : ICamundaKey, IEquatable ``` ### AuditLogKey System-generated key for an audit log entry. ```csharp public readonly record struct AuditLogKey : ICamundaKey, IEquatable ``` ### AuthorizationKey System-generated key for an authorization. ```csharp public readonly record struct AuthorizationKey : ICamundaKey, IEquatable ``` ### BatchOperationKey System-generated key for an batch operation. ```csharp public readonly record struct BatchOperationKey : ICamundaKey, IEquatable ``` ### ConditionalEvaluationKey System-generated key for a conditional evaluation. ```csharp public readonly record struct ConditionalEvaluationKey : ICamundaKey, IEquatable ``` ### DecisionDefinitionKey System-generated key for a decision definition. ```csharp public readonly record struct DecisionDefinitionKey : ICamundaKey, IEquatable ``` ### DecisionEvaluationInstanceKey System-generated identifier for a decision evaluation instance. It is composed of the parent decision evaluation key and the 1-based index of the evaluated decision within that evaluation, joined by a hyphen (format: `-`). ```csharp public readonly record struct DecisionEvaluationInstanceKey : ICamundaKey, IEquatable ``` ### DecisionEvaluationKey System-generated key for a decision evaluation. ```csharp public readonly record struct DecisionEvaluationKey : ICamundaKey, IEquatable ``` ### DecisionInstanceKey System-generated key for a deployed decision instance. ```csharp public readonly record struct DecisionInstanceKey : ICamundaKey, IEquatable ``` ### DecisionRequirementsKey System-generated key for a deployed decision requirements definition. ```csharp public readonly record struct DecisionRequirementsKey : ICamundaKey, IEquatable ``` ### DeploymentKey Key for a deployment. ```csharp public readonly record struct DeploymentKey : ICamundaKey, IEquatable ``` ### ElementInstanceKey System-generated key for a element instance. ```csharp public readonly record struct ElementInstanceKey : ICamundaKey, IEquatable ``` ### FormKey System-generated key for a deployed form. ```csharp public readonly record struct FormKey : ICamundaKey, IEquatable ``` ### IncidentKey System-generated key for a incident. ```csharp public readonly record struct IncidentKey : ICamundaKey, IEquatable ``` ### JobKey System-generated key for a job. ```csharp public readonly record struct JobKey : ICamundaKey, IEquatable ``` ### LongKey Zeebe Engine resource key (Java long serialized as string) ```csharp public readonly record struct LongKey : ICamundaKey, IEquatable ``` ### MessageKey System-generated key for an message. ```csharp public readonly record struct MessageKey : ICamundaKey, IEquatable ``` ### MessageSubscriptionKey System-generated key for a message subscription. ```csharp public readonly record struct MessageSubscriptionKey : ICamundaKey, IEquatable ``` ### ProcessDefinitionKey System-generated key for a deployed process definition. ```csharp public readonly record struct ProcessDefinitionKey : ICamundaKey, IEquatable ``` ### ProcessInstanceKey System-generated key for a process instance. ```csharp public readonly record struct ProcessInstanceKey : ICamundaKey, IEquatable ``` ### ResourceKey The system-assigned key for this resource. ```csharp public readonly record struct ResourceKey : ICamundaKey, IEquatable ``` ### ScopeKey System-generated key for a scope. A scope can hold variables and represents either an element instance in a BPMN process or the process instance itself. ```csharp public readonly record struct ScopeKey : ICamundaKey, IEquatable ``` ### SignalKey System-generated key for an signal. ```csharp public readonly record struct SignalKey : ICamundaKey, IEquatable ``` ### UserTaskKey System-generated key for a user task. ```csharp public readonly record struct UserTaskKey : ICamundaKey, IEquatable ``` ### VariableKey System-generated key for a variable. ```csharp public readonly record struct VariableKey : ICamundaKey, IEquatable ``` --- ## Models :::caution Technical Preview The C# SDK is a **technical preview** available from Camunda 8.9. It will become fully supported in Camunda 8.10. Its API surface may change in future releases without following semver. ::: Request and response model classes (560 types). ## Quick Reference - [ActivatedJob](#activatedjob) — An activated job received from the Camunda broker, with typed variable access - [ActivatedJobResult](#activatedjobresult) — ActivatedJobResult - [AdHocSubProcessActivateActivitiesInstruction](#adhocsubprocessactivateactivitiesinstruction) — AdHocSubProcessActivateActivitiesInstruction - [AdHocSubProcessActivateActivityReference](#adhocsubprocessactivateactivityreference) — AdHocSubProcessActivateActivityReference - [AdvancedActorTypeFilter](#advancedactortypefilter) — Advanced AuditLogActorTypeEnum filter - [AdvancedAgentInstanceKeyFilter](#advancedagentinstancekeyfilter) — Advanced AgentInstanceKey filter - [AdvancedAgentInstanceStatusFilter](#advancedagentinstancestatusfilter) — Advanced AgentInstanceStatusEnum filter - [AdvancedAuditLogEntityKeyFilter](#advancedauditlogentitykeyfilter) — Advanced entityKey filter - [AdvancedAuditLogKeyFilter](#advancedauditlogkeyfilter) — Advanced AuditLogKey filter - [AdvancedBatchOperationItemStateFilter](#advancedbatchoperationitemstatefilter) — Advanced BatchOperationItemStateEnum filter - [AdvancedBatchOperationStateFilter](#advancedbatchoperationstatefilter) — Advanced BatchOperationStateEnum filter - [AdvancedBatchOperationTypeFilter](#advancedbatchoperationtypefilter) — Advanced BatchOperationTypeEnum filter - [AdvancedCategoryFilter](#advancedcategoryfilter) — Advanced AuditLogCategoryEnum filter - [AdvancedClusterVariableScopeFilter](#advancedclustervariablescopefilter) — Advanced ClusterVariableScopeEnum filter - [AdvancedDateTimeFilter](#advanceddatetimefilter) — Advanced date-time filter - [AdvancedDecisionDefinitionKeyFilter](#advanceddecisiondefinitionkeyfilter) — Advanced DecisionDefinitionKey filter - [AdvancedDecisionEvaluationInstanceKeyFilter](#advanceddecisionevaluationinstancekeyfilter) — Advanced DecisionEvaluationInstanceKey filter - [AdvancedDecisionEvaluationKeyFilter](#advanceddecisionevaluationkeyfilter) — Advanced DecisionEvaluationKey filter - [AdvancedDecisionInstanceStateFilter](#advanceddecisioninstancestatefilter) — Advanced DecisionInstanceStateEnum filter - [AdvancedDecisionRequirementsKeyFilter](#advanceddecisionrequirementskeyfilter) — Advanced DecisionRequirementsKey filter - [AdvancedDeploymentKeyFilter](#advanceddeploymentkeyfilter) — Advanced DeploymentKey filter - [AdvancedElementIdFilter](#advancedelementidfilter) — Advanced ElementId filter - [AdvancedElementInstanceKeyFilter](#advancedelementinstancekeyfilter) — Advanced ElementInstanceKey filter - [AdvancedElementInstanceStateFilter](#advancedelementinstancestatefilter) — Advanced ElementInstanceStateEnum filter - [AdvancedEntityTypeFilter](#advancedentitytypefilter) — Advanced AuditLogEntityTypeEnum filter - [AdvancedFormKeyFilter](#advancedformkeyfilter) — Advanced FormKey filter - [AdvancedGlobalListenerSourceFilter](#advancedgloballistenersourcefilter) — Advanced global listener source filter - [AdvancedGlobalTaskListenerEventTypeFilter](#advancedglobaltasklistenereventtypefilter) — Advanced global listener event type filter - [AdvancedIncidentErrorTypeFilter](#advancedincidenterrortypefilter) — Advanced IncidentErrorTypeEnum filter - [AdvancedIncidentStateFilter](#advancedincidentstatefilter) — Advanced IncidentStateEnum filter - [AdvancedIntegerFilter](#advancedintegerfilter) — Advanced integer (int32) filter - [AdvancedJobKeyFilter](#advancedjobkeyfilter) — Advanced JobKey filter - [AdvancedJobKindFilter](#advancedjobkindfilter) — Advanced JobKindEnum filter - [AdvancedJobListenerEventTypeFilter](#advancedjoblistenereventtypefilter) — Advanced JobListenerEventTypeEnum filter - [AdvancedJobStateFilter](#advancedjobstatefilter) — Advanced JobStateEnum filter - [AdvancedMessageSubscriptionKeyFilter](#advancedmessagesubscriptionkeyfilter) — Advanced MessageSubscriptionKey filter - [AdvancedMessageSubscriptionStateFilter](#advancedmessagesubscriptionstatefilter) — Advanced MessageSubscriptionStateEnum filter - [AdvancedMessageSubscriptionTypeFilter](#advancedmessagesubscriptiontypefilter) — Advanced MessageSubscriptionTypeEnum filter - [AdvancedOperationTypeFilter](#advancedoperationtypefilter) — Advanced AuditLogOperationTypeEnum filter - [AdvancedProcessDefinitionIdFilter](#advancedprocessdefinitionidfilter) — Advanced ProcessDefinitionId filter - [AdvancedProcessDefinitionKeyFilter](#advancedprocessdefinitionkeyfilter) — Advanced ProcessDefinitionKey filter - [AdvancedProcessInstanceKeyFilter](#advancedprocessinstancekeyfilter) — Advanced ProcessInstanceKey filter - [AdvancedProcessInstanceStateFilter](#advancedprocessinstancestatefilter) — Advanced ProcessInstanceStateEnum filter - [AdvancedResourceKeyFilter](#advancedresourcekeyfilter) — Advanced ResourceKey filter - [AdvancedResultFilter](#advancedresultfilter) — Advanced AuditLogResultEnum filter - [AdvancedScopeKeyFilter](#advancedscopekeyfilter) — Advanced ScopeKey filter - [AdvancedStringFilter](#advancedstringfilter) — Advanced string filter - [AdvancedUserTaskStateFilter](#advancedusertaskstatefilter) — Advanced UserTaskStateEnum filter - [AdvancedVariableKeyFilter](#advancedvariablekeyfilter) — Advanced VariableKey filter - [AgentInstanceCreationRequest](#agentinstancecreationrequest) — Request to create a new agent instance - [AgentInstanceCreationResult](#agentinstancecreationresult) — Response returned after successfully creating an agent instance - [AgentInstanceDefinition](#agentinstancedefinition) — The static definition of an agent instance, set once at creation - [AgentInstanceFilter](#agentinstancefilter) — Agent instance search filter - [AgentInstanceKeyExactMatch](#agentinstancekeyexactmatch) — Matches the value exactly - [AgentInstanceKeyFilterProperty](#agentinstancekeyfilterproperty) — AgentInstanceKey property with full advanced search capabilities - [AgentInstanceLimits](#agentinstancelimits) — The configured limits for an agent instance, set once at creation - [AgentInstanceMetrics](#agentinstancemetrics) — Aggregated metrics for an agent instance across all model calls - [AgentInstanceMetricsDelta](#agentinstancemetricsdelta) — Metric increments to apply to the agent instance aggregate counters - [AgentInstanceResult](#agentinstanceresult) — AgentInstanceResult - [AgentInstanceSearchQuery](#agentinstancesearchquery) — Agent instance search request - [AgentInstanceSearchQueryResult](#agentinstancesearchqueryresult) — Agent instance search response - [AgentInstanceSearchQuerySortRequest](#agentinstancesearchquerysortrequest) — AgentInstanceSearchQuerySortRequest - [AgentInstanceStatusExactMatch](#agentinstancestatusexactmatch) — Matches the value exactly - [AgentInstanceStatusFilterProperty](#agentinstancestatusfilterproperty) — AgentInstanceStatusEnum property with full advanced search capabilities - [AgentInstanceUpdateRequest](#agentinstanceupdaterequest) — Request to update the mutable state of an agent instance - [AgentTool](#agenttool) — A tool available to the agent - [AncestorScopeInstruction](#ancestorscopeinstruction) — Defines the ancestor scope for the created element instances - [AuditLogActorTypeExactMatch](#auditlogactortypeexactmatch) — Matches the value exactly - [AuditLogActorTypeFilterProperty](#auditlogactortypefilterproperty) — AuditLogActorTypeEnum property with full advanced search capabilities - [AuditLogEntityKeyExactMatch](#auditlogentitykeyexactmatch) — Matches the value exactly - [AuditLogEntityKeyFilterProperty](#auditlogentitykeyfilterproperty) — EntityKey property with full advanced search capabilities - [AuditLogFilter](#auditlogfilter) — Audit log filter request - [AuditLogKeyExactMatch](#auditlogkeyexactmatch) — Matches the value exactly - [AuditLogKeyFilterProperty](#auditlogkeyfilterproperty) — AuditLogKey property with full advanced search capabilities - [AuditLogResult](#auditlogresult) — Audit log item - [AuditLogResultExactMatch](#auditlogresultexactmatch) — Matches the value exactly - [AuditLogResultFilterProperty](#auditlogresultfilterproperty) — AuditLogResultEnum property with full advanced search capabilities - [AuditLogSearchQueryRequest](#auditlogsearchqueryrequest) — Audit log search request - [AuditLogSearchQueryResult](#auditlogsearchqueryresult) — Audit log search response - [AuditLogSearchQuerySortRequest](#auditlogsearchquerysortrequest) — AuditLogSearchQuerySortRequest - [AuthenticationConfigurationResponse](#authenticationconfigurationresponse) — Configuration for authentication and session management - [AuthorizationCreateResult](#authorizationcreateresult) — AuthorizationCreateResult - [AuthorizationFilter](#authorizationfilter) — Authorization search filter - [AuthorizationIdBasedRequest](#authorizationidbasedrequest) — AuthorizationIdBasedRequest - [AuthorizationPropertyBasedRequest](#authorizationpropertybasedrequest) — AuthorizationPropertyBasedRequest - [AuthorizationRequest](#authorizationrequest) — Defines an authorization request - [AuthorizationResult](#authorizationresult) — AuthorizationResult - [AuthorizationSearchQuery](#authorizationsearchquery) — AuthorizationSearchQuery - [AuthorizationSearchQuerySortRequest](#authorizationsearchquerysortrequest) — AuthorizationSearchQuerySortRequest - [AuthorizationSearchResult](#authorizationsearchresult) — AuthorizationSearchResult - [BackpressureState](#backpressurestate) - [BaseProcessInstanceFilterFields](#baseprocessinstancefilterfields) — Base process instance search filter - [BasicStringFilter](#basicstringfilter) — Basic advanced string filter - [BasicStringFilterProperty](#basicstringfilterproperty) — String property with basic advanced search capabilities - [BatchOperationCreatedResult](#batchoperationcreatedresult) — The created batch operation - [BatchOperationError](#batchoperationerror) — BatchOperationError - [BatchOperationFilter](#batchoperationfilter) — Batch operation filter request - [BatchOperationItemFilter](#batchoperationitemfilter) — Batch operation item filter request - [BatchOperationItemResponse](#batchoperationitemresponse) — BatchOperationItemResponse - [BatchOperationItemSearchQuery](#batchoperationitemsearchquery) — Batch operation item search request - [BatchOperationItemSearchQueryResult](#batchoperationitemsearchqueryresult) — BatchOperationItemSearchQueryResult - [BatchOperationItemSearchQuerySortRequest](#batchoperationitemsearchquerysortrequest) — BatchOperationItemSearchQuerySortRequest - [BatchOperationItemStateExactMatch](#batchoperationitemstateexactmatch) — Matches the value exactly - [BatchOperationItemStateFilterProperty](#batchoperationitemstatefilterproperty) — BatchOperationItemStateEnum property with full advanced search capabilities - [BatchOperationResponse](#batchoperationresponse) — BatchOperationResponse - [BatchOperationSearchQuery](#batchoperationsearchquery) — Batch operation search request - [BatchOperationSearchQueryResult](#batchoperationsearchqueryresult) — The batch operation search query result - [BatchOperationSearchQuerySortRequest](#batchoperationsearchquerysortrequest) — BatchOperationSearchQuerySortRequest - [BatchOperationStateExactMatch](#batchoperationstateexactmatch) — Matches the value exactly - [BatchOperationStateFilterProperty](#batchoperationstatefilterproperty) — BatchOperationStateEnum property with full advanced search capabilities - [BatchOperationTypeExactMatch](#batchoperationtypeexactmatch) — Matches the value exactly - [BatchOperationTypeFilterProperty](#batchoperationtypefilterproperty) — BatchOperationTypeEnum property with full advanced search capabilities - [BpmnErrorException](#bpmnerrorexception) — Throw from a job handler to trigger a BPMN error boundary event on the job's task - [BrokerInfo](#brokerinfo) — Provides information on a broker node - [BusinessId](#businessid) — An optional, user-defined string identifier that identifies the process instance within the scope of a process definition (scoped by tenant) - [CamundaAuthException](#camundaauthexception) — Authentication-specific exception - [CamundaConfigurationException](#camundaconfigurationexception) — Thrown when configuration hydration encounters validation errors - [CamundaKeyJsonConverterFactory](#camundakeyjsonconverterfactory) — JSON converter factory that handles any struct - [CamundaKeyValidation](#camundakeyvalidation) — Validation helpers for domain key constraints - [CamundaLongKeyJsonConverterFactory](#camundalongkeyjsonconverterfactory) — JSON converter factory that handles any struct - [CamundaSdkException](#camundasdkexception) — SDK error types mirroring the JS SDK's error structure - [CamundaUserResult](#camundauserresult) — CamundaUserResult - [CancelProcessInstanceRequest](#cancelprocessinstancerequest) — CancelProcessInstanceRequest - [CancelSdkException](#cancelsdkexception) — Thrown when a cancellable operation is cancelled - [CategoryExactMatch](#categoryexactmatch) — Matches the value exactly - [CategoryFilterProperty](#categoryfilterproperty) — AuditLogCategoryEnum property with full advanced search capabilities - [Changeset](#changeset) — JSON object with changed task attribute values - [ClientId](#clientid) — The unique identifier of an OAuth client - [ClockPinRequest](#clockpinrequest) — ClockPinRequest - [CloudConfigurationResponse](#cloudconfigurationresponse) — Configuration for SaaS/cloud-specific settings - [ClusterVariableName](#clustervariablename) — The name of a cluster variable - [ClusterVariableResult](#clustervariableresult) — ClusterVariableResult - [ClusterVariableResultBase](#clustervariableresultbase) — Cluster variable response item - [ClusterVariableScopeExactMatch](#clustervariablescopeexactmatch) — Matches the value exactly - [ClusterVariableScopeFilterProperty](#clustervariablescopefilterproperty) — ClusterVariableScopeEnum property with full advanced search capabilities - [ClusterVariableSearchQueryFilterRequest](#clustervariablesearchqueryfilterrequest) — Cluster variable filter request - [ClusterVariableSearchQueryRequest](#clustervariablesearchqueryrequest) — Cluster variable search query request - [ClusterVariableSearchQueryResult](#clustervariablesearchqueryresult) — Cluster variable search query response - [ClusterVariableSearchQuerySortRequest](#clustervariablesearchquerysortrequest) — ClusterVariableSearchQuerySortRequest - [ClusterVariableSearchResult](#clustervariablesearchresult) — Cluster variable search response item - [ComponentsConfigurationResponse](#componentsconfigurationresponse) — Configuration for active Camunda components in the deployment - [ConditionalEvaluationInstruction](#conditionalevaluationinstruction) — ConditionalEvaluationInstruction - [ConsistencyOptions](#consistencyoptions) — Options for eventual consistency polling behavior - [CorrelatedMessageSubscriptionFilter](#correlatedmessagesubscriptionfilter) — Correlated message subscriptions search filter - [CorrelatedMessageSubscriptionResult](#correlatedmessagesubscriptionresult) — CorrelatedMessageSubscriptionResult - [CorrelatedMessageSubscriptionSearchQuery](#correlatedmessagesubscriptionsearchquery) — CorrelatedMessageSubscriptionSearchQuery - [CorrelatedMessageSubscriptionSearchQueryResult](#correlatedmessagesubscriptionsearchqueryresult) — CorrelatedMessageSubscriptionSearchQueryResult - [CorrelatedMessageSubscriptionSearchQuerySortRequest](#correlatedmessagesubscriptionsearchquerysortrequest) — CorrelatedMessageSubscriptionSearchQuerySortRequest - [CreateClusterVariableRequest](#createclustervariablerequest) — CreateClusterVariableRequest - [CreateGlobalTaskListenerRequest](#createglobaltasklistenerrequest) — CreateGlobalTaskListenerRequest - [CreateProcessInstanceResult](#createprocessinstanceresult) — CreateProcessInstanceResult - [CursorBackwardPagination](#cursorbackwardpagination) — CursorBackwardPagination - [CursorForwardPagination](#cursorforwardpagination) — CursorForwardPagination - [DateTimeFilterProperty](#datetimefilterproperty) — Date-time property with full advanced search capabilities - [DecisionDefinitionFilter](#decisiondefinitionfilter) — Decision definition search filter - [DecisionDefinitionId](#decisiondefinitionid) — Id of a decision definition, from the model - [DecisionDefinitionKeyExactMatch](#decisiondefinitionkeyexactmatch) — Matches the value exactly - [DecisionDefinitionKeyFilterProperty](#decisiondefinitionkeyfilterproperty) — DecisionDefinitionKey property with full advanced search capabilities - [DecisionDefinitionResult](#decisiondefinitionresult) — DecisionDefinitionResult - [DecisionDefinitionSearchQuery](#decisiondefinitionsearchquery) — DecisionDefinitionSearchQuery - [DecisionDefinitionSearchQueryResult](#decisiondefinitionsearchqueryresult) — DecisionDefinitionSearchQueryResult - [DecisionDefinitionSearchQuerySortRequest](#decisiondefinitionsearchquerysortrequest) — DecisionDefinitionSearchQuerySortRequest - [DecisionEvaluationById](#decisionevaluationbyid) — DecisionEvaluationById - [DecisionEvaluationByKey](#decisionevaluationbykey) — DecisionEvaluationByKey - [DecisionEvaluationInstanceKeyExactMatch](#decisionevaluationinstancekeyexactmatch) — Matches the value exactly - [DecisionEvaluationInstanceKeyFilterProperty](#decisionevaluationinstancekeyfilterproperty) — DecisionEvaluationInstanceKey property with full advanced search capabilities - [DecisionEvaluationInstruction](#decisionevaluationinstruction) — DecisionEvaluationInstruction - [DecisionEvaluationKeyExactMatch](#decisionevaluationkeyexactmatch) — Matches the value exactly - [DecisionEvaluationKeyFilterProperty](#decisionevaluationkeyfilterproperty) — DecisionEvaluationKey property with full advanced search capabilities - [DecisionInstanceDeletionBatchOperationRequest](#decisioninstancedeletionbatchoperationrequest) — The decision instance filter that defines which decision instances should be deleted - [DecisionInstanceFilter](#decisioninstancefilter) — Decision instance search filter - [DecisionInstanceGetQueryResult](#decisioninstancegetqueryresult) — DecisionInstanceGetQueryResult - [DecisionInstanceResult](#decisioninstanceresult) — DecisionInstanceResult - [DecisionInstanceSearchQuery](#decisioninstancesearchquery) — DecisionInstanceSearchQuery - [DecisionInstanceSearchQueryResult](#decisioninstancesearchqueryresult) — DecisionInstanceSearchQueryResult - [DecisionInstanceSearchQuerySortRequest](#decisioninstancesearchquerysortrequest) — DecisionInstanceSearchQuerySortRequest - [DecisionInstanceStateExactMatch](#decisioninstancestateexactmatch) — Matches the value exactly - [DecisionInstanceStateFilterProperty](#decisioninstancestatefilterproperty) — DecisionInstanceStateEnum property with full advanced search capabilities - [DecisionRequirementsFilter](#decisionrequirementsfilter) — Decision requirements search filter - [DecisionRequirementsKeyExactMatch](#decisionrequirementskeyexactmatch) — Matches the value exactly - [DecisionRequirementsKeyFilterProperty](#decisionrequirementskeyfilterproperty) — DecisionRequirementsKey property with full advanced search capabilities - [DecisionRequirementsResult](#decisionrequirementsresult) — DecisionRequirementsResult - [DecisionRequirementsSearchQuery](#decisionrequirementssearchquery) — DecisionRequirementsSearchQuery - [DecisionRequirementsSearchQueryResult](#decisionrequirementssearchqueryresult) — DecisionRequirementsSearchQueryResult - [DecisionRequirementsSearchQuerySortRequest](#decisionrequirementssearchquerysortrequest) — DecisionRequirementsSearchQuerySortRequest - [DeleteDecisionInstanceRequest](#deletedecisioninstancerequest) — DeleteDecisionInstanceRequest - [DeleteProcessInstanceRequest](#deleteprocessinstancerequest) — DeleteProcessInstanceRequest - [DeleteResourceRequest](#deleteresourcerequest) — DeleteResourceRequest - [DeleteResourceResponse](#deleteresourceresponse) — DeleteResourceResponse - [DeploymentConfigurationResponse](#deploymentconfigurationresponse) — Configuration for deployment characteristics - [DeploymentDecisionRequirementsResult](#deploymentdecisionrequirementsresult) — Deployed decision requirements - [DeploymentDecisionResult](#deploymentdecisionresult) — A deployed decision - [DeploymentFormResult](#deploymentformresult) — A deployed form - [DeploymentKeyExactMatch](#deploymentkeyexactmatch) — Matches the value exactly - [DeploymentKeyFilterProperty](#deploymentkeyfilterproperty) — DeploymentKey property with full advanced search capabilities - [DeploymentMetadataResult](#deploymentmetadataresult) — DeploymentMetadataResult - [DeploymentProcessResult](#deploymentprocessresult) — A deployed process - [DeploymentResourceResult](#deploymentresourceresult) — A deployed Resource - [DeploymentResult](#deploymentresult) — DeploymentResult - [DirectAncestorKeyInstruction](#directancestorkeyinstruction) — Provides a concrete key to use as ancestor scope for the created element instance - [DocumentCreationBatchResponse](#documentcreationbatchresponse) — DocumentCreationBatchResponse - [DocumentCreationFailureDetail](#documentcreationfailuredetail) — DocumentCreationFailureDetail - [DocumentId](#documentid) — Document Id that uniquely identifies a document - [DocumentLink](#documentlink) — DocumentLink - [DocumentLinkRequest](#documentlinkrequest) — DocumentLinkRequest - [DocumentMetadata](#documentmetadata) — Information about the document - [DocumentMetadataResponse](#documentmetadataresponse) — Information about the document that is returned in responses - [DocumentReference](#documentreference) — DocumentReference - [ElementId](#elementid) — The model-defined id of an element - [ElementIdExactMatch](#elementidexactmatch) — Matches the value exactly - [ElementIdFilterProperty](#elementidfilterproperty) — ElementId property with full advanced search capabilities - [ElementInstanceFilter](#elementinstancefilter) — Element instance filter - [ElementInstanceKeyExactMatch](#elementinstancekeyexactmatch) — Matches the value exactly - [ElementInstanceKeyFilterProperty](#elementinstancekeyfilterproperty) — ElementInstanceKey property with full advanced search capabilities - [ElementInstanceResult](#elementinstanceresult) — ElementInstanceResult - [ElementInstanceSearchQuery](#elementinstancesearchquery) — Element instance search request - [ElementInstanceSearchQueryResult](#elementinstancesearchqueryresult) — ElementInstanceSearchQueryResult - [ElementInstanceSearchQuerySortRequest](#elementinstancesearchquerysortrequest) — ElementInstanceSearchQuerySortRequest - [ElementInstanceStateExactMatch](#elementinstancestateexactmatch) — Matches the value exactly - [ElementInstanceStateFilterProperty](#elementinstancestatefilterproperty) — ElementInstanceStateEnum property with full advanced search capabilities - [EndCursor](#endcursor) — The end cursor in a search query result set - [EntityTypeExactMatch](#entitytypeexactmatch) — Matches the value exactly - [EntityTypeFilterProperty](#entitytypefilterproperty) — AuditLogEntityTypeEnum property with full advanced search capabilities - [EvaluateConditionalResult](#evaluateconditionalresult) — EvaluateConditionalResult - [EvaluateDecisionResult](#evaluatedecisionresult) — EvaluateDecisionResult - [EvaluatedDecisionInputItem](#evaluateddecisioninputitem) — A decision input that was evaluated within this decision evaluation - [EvaluatedDecisionOutputItem](#evaluateddecisionoutputitem) — The evaluated decision outputs - [EvaluatedDecisionResult](#evaluateddecisionresult) — A decision that was evaluated - [EventualConsistencyTimeoutException](#eventualconsistencytimeoutexception) — Thrown when an eventually consistent endpoint times out waiting for data - [ExpressionEvaluationRequest](#expressionevaluationrequest) — ExpressionEvaluationRequest - [ExpressionEvaluationResult](#expressionevaluationresult) — ExpressionEvaluationResult - [ExpressionEvaluationWarningItem](#expressionevaluationwarningitem) — ExpressionEvaluationWarningItem - [ExtendedDeploymentResponse](#extendeddeploymentresponse) — Extended deployment result with typed convenience properties for direct access to deployed artifacts by category (processes, decisions, forms, etc - [FormId](#formid) — The user-defined id for the form - [FormKeyExactMatch](#formkeyexactmatch) — Matches the value exactly - [FormKeyFilterProperty](#formkeyfilterproperty) — FormKey property with full advanced search capabilities - [FormResult](#formresult) — FormResult - [GlobalJobStatisticsQueryResult](#globaljobstatisticsqueryresult) — Global job statistics query result - [GlobalListenerBase](#globallistenerbase) — GlobalListenerBase - [GlobalListenerId](#globallistenerid) — The user-defined id for the global listener - [GlobalListenerSourceExactMatch](#globallistenersourceexactmatch) — Matches the value exactly - [GlobalListenerSourceFilterProperty](#globallistenersourcefilterproperty) — Global listener source property with full advanced search capabilities - [GlobalTaskListenerBase](#globaltasklistenerbase) — GlobalTaskListenerBase - [GlobalTaskListenerEventTypeExactMatch](#globaltasklistenereventtypeexactmatch) — Matches the value exactly - [GlobalTaskListenerEventTypeFilterProperty](#globaltasklistenereventtypefilterproperty) — Global listener event type property with full advanced search capabilities - [GlobalTaskListenerResult](#globaltasklistenerresult) — GlobalTaskListenerResult - [GlobalTaskListenerSearchQueryFilterRequest](#globaltasklistenersearchqueryfilterrequest) — Global listener filter request - [GlobalTaskListenerSearchQueryRequest](#globaltasklistenersearchqueryrequest) — Global listener search query request - [GlobalTaskListenerSearchQueryResult](#globaltasklistenersearchqueryresult) — Global listener search query response - [GlobalTaskListenerSearchQuerySortRequest](#globaltasklistenersearchquerysortrequest) — GlobalTaskListenerSearchQuerySortRequest - [GroupClientResult](#groupclientresult) — GroupClientResult - [GroupClientSearchQueryRequest](#groupclientsearchqueryrequest) — GroupClientSearchQueryRequest - [GroupClientSearchQuerySortRequest](#groupclientsearchquerysortrequest) — GroupClientSearchQuerySortRequest - [GroupClientSearchResult](#groupclientsearchresult) — GroupClientSearchResult - [GroupCreateRequest](#groupcreaterequest) — GroupCreateRequest - [GroupCreateResult](#groupcreateresult) — GroupCreateResult - [GroupFilter](#groupfilter) — Group filter request - [GroupId](#groupid) — The unique identifier of a group - [GroupMappingRuleSearchResult](#groupmappingrulesearchresult) — GroupMappingRuleSearchResult - [GroupResult](#groupresult) — Group search response item - [GroupRoleSearchResult](#grouprolesearchresult) — GroupRoleSearchResult - [GroupSearchQueryRequest](#groupsearchqueryrequest) — Group search request - [GroupSearchQueryResult](#groupsearchqueryresult) — Group search response - [GroupSearchQuerySortRequest](#groupsearchquerysortrequest) — GroupSearchQuerySortRequest - [GroupUpdateRequest](#groupupdaterequest) — GroupUpdateRequest - [GroupUpdateResult](#groupupdateresult) — GroupUpdateResult - [GroupUserResult](#groupuserresult) — GroupUserResult - [GroupUserSearchQueryRequest](#groupusersearchqueryrequest) — GroupUserSearchQueryRequest - [GroupUserSearchQuerySortRequest](#groupusersearchquerysortrequest) — GroupUserSearchQuerySortRequest - [GroupUserSearchResult](#groupusersearchresult) — GroupUserSearchResult - [HttpSdkException](#httpsdkexception) — HTTP-specific SDK error with RFC 7807 Problem Details - [ICamundaKey](#icamundakey) — Marker interface for all Camunda domain key types - [ICamundaLongKey](#icamundalongkey) — Marker interface for Camunda domain types backed by a long (int64) value - [ITenantIdSettable](#itenantidsettable) — Implemented by request body types that have an optional tenantId property - [ITenantIdsSettable](#itenantidssettable) — Implemented by request body types that have an optional tenantIds array property (e - [IncidentErrorTypeExactMatch](#incidenterrortypeexactmatch) — Matches the value exactly - [IncidentErrorTypeFilterProperty](#incidenterrortypefilterproperty) — IncidentErrorTypeEnum with full advanced search capabilities - [IncidentFilter](#incidentfilter) — Incident search filter - [IncidentProcessInstanceStatisticsByDefinitionFilter](#incidentprocessinstancestatisticsbydefinitionfilter) — Filter for the incident process instance statistics by definition query - [IncidentProcessInstanceStatisticsByDefinitionQuery](#incidentprocessinstancestatisticsbydefinitionquery) — IncidentProcessInstanceStatisticsByDefinitionQuery - [IncidentProcessInstanceStatisticsByDefinitionQueryResult](#incidentprocessinstancestatisticsbydefinitionqueryresult) — IncidentProcessInstanceStatisticsByDefinitionQueryResult - [IncidentProcessInstanceStatisticsByDefinitionQuerySortRequest](#incidentprocessinstancestatisticsbydefinitionquerysortrequest) — IncidentProcessInstanceStatisticsByDefinitionQuerySortRequest - [IncidentProcessInstanceStatisticsByDefinitionResult](#incidentprocessinstancestatisticsbydefinitionresult) — IncidentProcessInstanceStatisticsByDefinitionResult - [IncidentProcessInstanceStatisticsByErrorQuery](#incidentprocessinstancestatisticsbyerrorquery) — IncidentProcessInstanceStatisticsByErrorQuery - [IncidentProcessInstanceStatisticsByErrorQueryResult](#incidentprocessinstancestatisticsbyerrorqueryresult) — IncidentProcessInstanceStatisticsByErrorQueryResult - [IncidentProcessInstanceStatisticsByErrorQuerySortRequest](#incidentprocessinstancestatisticsbyerrorquerysortrequest) — IncidentProcessInstanceStatisticsByErrorQuerySortRequest - [IncidentProcessInstanceStatisticsByErrorResult](#incidentprocessinstancestatisticsbyerrorresult) — IncidentProcessInstanceStatisticsByErrorResult - [IncidentResolutionRequest](#incidentresolutionrequest) — IncidentResolutionRequest - [IncidentResult](#incidentresult) — IncidentResult - [IncidentSearchQuery](#incidentsearchquery) — IncidentSearchQuery - [IncidentSearchQueryResult](#incidentsearchqueryresult) — IncidentSearchQueryResult - [IncidentSearchQuerySortRequest](#incidentsearchquerysortrequest) — IncidentSearchQuerySortRequest - [IncidentStateExactMatch](#incidentstateexactmatch) — Matches the value exactly - [IncidentStateFilterProperty](#incidentstatefilterproperty) — IncidentStateEnum with full advanced search capabilities - [InferredAncestorKeyInstruction](#inferredancestorkeyinstruction) — Instructs the engine to derive the ancestor scope key from the source element's hierarchy - [IntegerFilterProperty](#integerfilterproperty) — Integer property with advanced search capabilities - [JobActivationRequest](#jobactivationrequest) — JobActivationRequest - [JobActivationResult](#jobactivationresult) — The list of activated jobs - [JobChangeset](#jobchangeset) — JSON object with changed job attribute values - [JobCompletionRequest](#jobcompletionrequest) — JobCompletionRequest - [JobErrorRequest](#joberrorrequest) — JobErrorRequest - [JobErrorStatisticsFilter](#joberrorstatisticsfilter) — Job error statistics search filter - [JobErrorStatisticsItem](#joberrorstatisticsitem) — Aggregated error metrics for a single error type and message combination - [JobErrorStatisticsQuery](#joberrorstatisticsquery) — Job error statistics query - [JobErrorStatisticsQueryResult](#joberrorstatisticsqueryresult) — Job error statistics query result - [JobFailRequest](#jobfailrequest) — JobFailRequest - [JobFailureException](#jobfailureexception) — Throw from a job handler to explicitly fail a job with custom retry settings - [JobFilter](#jobfilter) — Job search filter - [JobHandler](#jobhandler) — Delegate for job handler functions - [JobKeyExactMatch](#jobkeyexactmatch) — Matches the value exactly - [JobKeyFilterProperty](#jobkeyfilterproperty) — JobKey property with full advanced search capabilities - [JobKindExactMatch](#jobkindexactmatch) — Matches the value exactly - [JobKindFilterProperty](#jobkindfilterproperty) — JobKindEnum property with full advanced search capabilities - [JobListenerEventTypeExactMatch](#joblistenereventtypeexactmatch) — Matches the value exactly - [JobListenerEventTypeFilterProperty](#joblistenereventtypefilterproperty) — JobListenerEventTypeEnum property with full advanced search capabilities - [JobMetricsConfigurationResponse](#jobmetricsconfigurationresponse) — Configuration for job metrics collection and export - [JobResult](#jobresult) — The result of the completed job as determined by the worker - [JobResultActivateElement](#jobresultactivateelement) — Instruction to activate a single BPMN element within an ad‑hoc sub‑process, optionally providing variables scoped to that element - [JobResultAdHocSubProcess](#jobresultadhocsubprocess) — Job result details for an ad‑hoc sub‑process, including elements to activate and flags indicating completion or cancellation behavior - [JobResultCorrections](#jobresultcorrections) — JSON object with attributes that were corrected by the worker - [JobResultUserTask](#jobresultusertask) — Job result details for a user task completion, optionally including a denial reason and corrected task properties - [JobSearchQuery](#jobsearchquery) — Job search request - [JobSearchQueryResult](#jobsearchqueryresult) — Job search response - [JobSearchQuerySortRequest](#jobsearchquerysortrequest) — JobSearchQuerySortRequest - [JobSearchResult](#jobsearchresult) — JobSearchResult - [JobStateExactMatch](#jobstateexactmatch) — Matches the value exactly - [JobStateFilterProperty](#jobstatefilterproperty) — JobStateEnum property with full advanced search capabilities - [JobTimeSeriesStatisticsFilter](#jobtimeseriesstatisticsfilter) — Job time-series statistics search filter - [JobTimeSeriesStatisticsItem](#jobtimeseriesstatisticsitem) — Aggregated job metrics for a single time bucket - [JobTimeSeriesStatisticsQuery](#jobtimeseriesstatisticsquery) — Job time-series statistics query - [JobTimeSeriesStatisticsQueryResult](#jobtimeseriesstatisticsqueryresult) — Job time-series statistics query result - [JobTypeStatisticsFilter](#jobtypestatisticsfilter) — Job type statistics search filter - [JobTypeStatisticsItem](#jobtypestatisticsitem) — Statistics for a single job type - [JobTypeStatisticsQuery](#jobtypestatisticsquery) — Job type statistics query - [JobTypeStatisticsQueryResult](#jobtypestatisticsqueryresult) — Job type statistics query result - [JobUpdateRequest](#jobupdaterequest) — JobUpdateRequest - [JobWorker](#jobworker) — A long-running worker that polls the Camunda broker for jobs of a specific type, dispatches them to a handler, and auto-completes or auto-fails based on the outcome - [JobWorkerStatisticsFilter](#jobworkerstatisticsfilter) — Job worker statistics search filter - [JobWorkerStatisticsItem](#jobworkerstatisticsitem) — Statistics for a single worker within a job type - [JobWorkerStatisticsQuery](#jobworkerstatisticsquery) — Job worker statistics query - [JobWorkerStatisticsQueryResult](#jobworkerstatisticsqueryresult) — Job worker statistics query result - [LicenseResponse](#licenseresponse) — The response of a license request - [LikeFilter](#likefilter) — Checks if the property matches the provided like value - [LimitPagination](#limitpagination) — LimitPagination - [MappingRuleCreateRequest](#mappingrulecreaterequest) — MappingRuleCreateRequest - [MappingRuleCreateResult](#mappingrulecreateresult) — MappingRuleCreateResult - [MappingRuleCreateUpdateRequest](#mappingrulecreateupdaterequest) — MappingRuleCreateUpdateRequest - [MappingRuleCreateUpdateResult](#mappingrulecreateupdateresult) — MappingRuleCreateUpdateResult - [MappingRuleFilter](#mappingrulefilter) — Mapping rule search filter - [MappingRuleId](#mappingruleid) — The unique identifier of a mapping rule - [MappingRuleResult](#mappingruleresult) — MappingRuleResult - [MappingRuleSearchQueryRequest](#mappingrulesearchqueryrequest) — MappingRuleSearchQueryRequest - [MappingRuleSearchQueryResult](#mappingrulesearchqueryresult) — MappingRuleSearchQueryResult - [MappingRuleSearchQuerySortRequest](#mappingrulesearchquerysortrequest) — MappingRuleSearchQuerySortRequest - [MappingRuleUpdateRequest](#mappingruleupdaterequest) — MappingRuleUpdateRequest - [MappingRuleUpdateResult](#mappingruleupdateresult) — MappingRuleUpdateResult - [MatchedDecisionRuleItem](#matcheddecisionruleitem) — A decision rule that matched within this decision evaluation - [MessageCorrelationRequest](#messagecorrelationrequest) — MessageCorrelationRequest - [MessageCorrelationResult](#messagecorrelationresult) — The message key of the correlated message, as well as the first process instance key it correlated with - [MessagePublicationRequest](#messagepublicationrequest) — MessagePublicationRequest - [MessagePublicationResult](#messagepublicationresult) — The message key of the published message - [MessageSubscriptionFilter](#messagesubscriptionfilter) — Message subscription search filter - [MessageSubscriptionKeyExactMatch](#messagesubscriptionkeyexactmatch) — Matches the value exactly - [MessageSubscriptionKeyFilterProperty](#messagesubscriptionkeyfilterproperty) — MessageSubscriptionKey property with full advanced search capabilities - [MessageSubscriptionResult](#messagesubscriptionresult) — MessageSubscriptionResult - [MessageSubscriptionSearchQuery](#messagesubscriptionsearchquery) — MessageSubscriptionSearchQuery - [MessageSubscriptionSearchQueryResult](#messagesubscriptionsearchqueryresult) — MessageSubscriptionSearchQueryResult - [MessageSubscriptionSearchQuerySortRequest](#messagesubscriptionsearchquerysortrequest) — MessageSubscriptionSearchQuerySortRequest - [MessageSubscriptionStateExactMatch](#messagesubscriptionstateexactmatch) — Matches the value exactly - [MessageSubscriptionStateFilterProperty](#messagesubscriptionstatefilterproperty) — MessageSubscriptionStateEnum with full advanced search capabilities - [MessageSubscriptionTypeExactMatch](#messagesubscriptiontypeexactmatch) — Matches the value exactly - [MessageSubscriptionTypeFilterProperty](#messagesubscriptiontypefilterproperty) — MessageSubscriptionTypeEnum with full advanced search capabilities - [MigrateProcessInstanceMappingInstruction](#migrateprocessinstancemappinginstruction) — The mapping instructions describe how to map elements from the source process definition to the target process definition - [ModifyProcessInstanceVariableInstruction](#modifyprocessinstancevariableinstruction) — Instruction describing which variables to create or update - [OffsetPagination](#offsetpagination) — OffsetPagination - [OperationReference](#operationreference) — A reference key chosen by the user that will be part of all records resulting from this operation - [OperationTypeExactMatch](#operationtypeexactmatch) — Matches the value exactly - [OperationTypeFilterProperty](#operationtypefilterproperty) — AuditLogOperationTypeEnum property with full advanced search capabilities - [Partition](#partition) — Provides information on a partition within a broker node - [ProblemDetail](#problemdetail) — A Problem detail object as described in [RFC 9457](https://www - [ProcessDefinitionElementStatisticsQuery](#processdefinitionelementstatisticsquery) — Process definition element statistics request - [ProcessDefinitionElementStatisticsQueryResult](#processdefinitionelementstatisticsqueryresult) — Process definition element statistics query response - [ProcessDefinitionFilter](#processdefinitionfilter) — Process definition search filter - [ProcessDefinitionId](#processdefinitionid) — Id of a process definition, from the model - [ProcessDefinitionIdExactMatch](#processdefinitionidexactmatch) — Matches the value exactly - [ProcessDefinitionIdFilterProperty](#processdefinitionidfilterproperty) — ProcessDefinitionId property with full advanced search capabilities - [ProcessDefinitionInstanceStatisticsQuery](#processdefinitioninstancestatisticsquery) — ProcessDefinitionInstanceStatisticsQuery - [ProcessDefinitionInstanceStatisticsQueryResult](#processdefinitioninstancestatisticsqueryresult) — ProcessDefinitionInstanceStatisticsQueryResult - [ProcessDefinitionInstanceStatisticsQuerySortRequest](#processdefinitioninstancestatisticsquerysortrequest) — ProcessDefinitionInstanceStatisticsQuerySortRequest - [ProcessDefinitionInstanceStatisticsResult](#processdefinitioninstancestatisticsresult) — Process definition instance statistics response - [ProcessDefinitionInstanceVersionStatisticsFilter](#processdefinitioninstanceversionstatisticsfilter) — Process definition instance version statistics search filter - [ProcessDefinitionInstanceVersionStatisticsQuery](#processdefinitioninstanceversionstatisticsquery) — ProcessDefinitionInstanceVersionStatisticsQuery - [ProcessDefinitionInstanceVersionStatisticsQueryResult](#processdefinitioninstanceversionstatisticsqueryresult) — ProcessDefinitionInstanceVersionStatisticsQueryResult - [ProcessDefinitionInstanceVersionStatisticsQuerySortRequest](#processdefinitioninstanceversionstatisticsquerysortrequest) — ProcessDefinitionInstanceVersionStatisticsQuerySortRequest - [ProcessDefinitionInstanceVersionStatisticsResult](#processdefinitioninstanceversionstatisticsresult) — Process definition instance version statistics response - [ProcessDefinitionKeyExactMatch](#processdefinitionkeyexactmatch) — Matches the value exactly - [ProcessDefinitionKeyFilterProperty](#processdefinitionkeyfilterproperty) — ProcessDefinitionKey property with full advanced search capabilities - [ProcessDefinitionMessageSubscriptionStatisticsQuery](#processdefinitionmessagesubscriptionstatisticsquery) — ProcessDefinitionMessageSubscriptionStatisticsQuery - [ProcessDefinitionMessageSubscriptionStatisticsQueryResult](#processdefinitionmessagesubscriptionstatisticsqueryresult) — ProcessDefinitionMessageSubscriptionStatisticsQueryResult - [ProcessDefinitionMessageSubscriptionStatisticsResult](#processdefinitionmessagesubscriptionstatisticsresult) — ProcessDefinitionMessageSubscriptionStatisticsResult - [ProcessDefinitionResult](#processdefinitionresult) — ProcessDefinitionResult - [ProcessDefinitionSearchQuery](#processdefinitionsearchquery) — ProcessDefinitionSearchQuery - [ProcessDefinitionSearchQueryResult](#processdefinitionsearchqueryresult) — ProcessDefinitionSearchQueryResult - [ProcessDefinitionSearchQuerySortRequest](#processdefinitionsearchquerysortrequest) — ProcessDefinitionSearchQuerySortRequest - [ProcessDefinitionStatisticsFilter](#processdefinitionstatisticsfilter) — Process definition statistics search filter - [ProcessElementStatisticsResult](#processelementstatisticsresult) — Process element statistics response - [ProcessInstanceCallHierarchyEntry](#processinstancecallhierarchyentry) — ProcessInstanceCallHierarchyEntry - [ProcessInstanceCancellationBatchOperationRequest](#processinstancecancellationbatchoperationrequest) — The process instance filter that defines which process instances should be canceled - [ProcessInstanceCreationInstruction](#processinstancecreationinstruction) — Instructions for creating a process instance - [ProcessInstanceCreationInstructionById](#processinstancecreationinstructionbyid) — ProcessInstanceCreationInstructionById - [ProcessInstanceCreationInstructionByKey](#processinstancecreationinstructionbykey) — ProcessInstanceCreationInstructionByKey - [ProcessInstanceCreationRuntimeInstruction](#processinstancecreationruntimeinstruction) — ProcessInstanceCreationRuntimeInstruction - [ProcessInstanceCreationStartInstruction](#processinstancecreationstartinstruction) — ProcessInstanceCreationStartInstruction - [ProcessInstanceCreationTerminateInstruction](#processinstancecreationterminateinstruction) — Terminates the process instance after a specific BPMN element is completed or terminated - [ProcessInstanceDeletionBatchOperationRequest](#processinstancedeletionbatchoperationrequest) — The process instance filter that defines which process instances should be deleted - [ProcessInstanceElementStatisticsQueryResult](#processinstanceelementstatisticsqueryresult) — Process instance element statistics query response - [ProcessInstanceFilter](#processinstancefilter) — Process instance search filter - [ProcessInstanceFilterFields](#processinstancefilterfields) — Process instance search filter - [ProcessInstanceIncidentResolutionBatchOperationRequest](#processinstanceincidentresolutionbatchoperationrequest) — The process instance filter that defines which process instances should have their incidents resolved - [ProcessInstanceKeyExactMatch](#processinstancekeyexactmatch) — Matches the value exactly - [ProcessInstanceKeyFilterProperty](#processinstancekeyfilterproperty) — ProcessInstanceKey property with full advanced search capabilities - [ProcessInstanceMigrationBatchOperationPlan](#processinstancemigrationbatchoperationplan) — The migration instructions describe how to migrate a process instance from one process definition to another - [ProcessInstanceMigrationBatchOperationRequest](#processinstancemigrationbatchoperationrequest) — ProcessInstanceMigrationBatchOperationRequest - [ProcessInstanceMigrationInstruction](#processinstancemigrationinstruction) — The migration instructions describe how to migrate a process instance from one process definition to another - [ProcessInstanceModificationActivateInstruction](#processinstancemodificationactivateinstruction) — Instruction describing an element to activate - [ProcessInstanceModificationBatchOperationRequest](#processinstancemodificationbatchoperationrequest) — The process instance filter to define on which process instances tokens should be moved, and new element instances should be activated or terminated - [ProcessInstanceModificationInstruction](#processinstancemodificationinstruction) — ProcessInstanceModificationInstruction - [ProcessInstanceModificationMoveBatchOperationInstruction](#processinstancemodificationmovebatchoperationinstruction) — Instructions describing a move operation - [ProcessInstanceModificationMoveInstruction](#processinstancemodificationmoveinstruction) — Instruction describing a move operation - [ProcessInstanceModificationTerminateByIdInstruction](#processinstancemodificationterminatebyidinstruction) — Instruction describing which elements to terminate - [ProcessInstanceModificationTerminateByKeyInstruction](#processinstancemodificationterminatebykeyinstruction) — Instruction providing the key of the element instance to terminate - [ProcessInstanceModificationTerminateInstruction](#processinstancemodificationterminateinstruction) — Instruction describing which elements to terminate - [ProcessInstanceReference](#processinstancereference) — ProcessInstanceReference - [ProcessInstanceResult](#processinstanceresult) — Process instance search response item - [ProcessInstanceSearchQuery](#processinstancesearchquery) — Process instance search request - [ProcessInstanceSearchQueryResult](#processinstancesearchqueryresult) — Process instance search response - [ProcessInstanceSearchQuerySortRequest](#processinstancesearchquerysortrequest) — ProcessInstanceSearchQuerySortRequest - [ProcessInstanceSequenceFlowResult](#processinstancesequenceflowresult) — Process instance sequence flow result - [ProcessInstanceSequenceFlowsQueryResult](#processinstancesequenceflowsqueryresult) — Process instance sequence flows query response - [ProcessInstanceStateExactMatch](#processinstancestateexactmatch) — Matches the value exactly - [ProcessInstanceStateFilterProperty](#processinstancestatefilterproperty) — ProcessInstanceStateEnum property with full advanced search capabilities - [ResourceFilter](#resourcefilter) — Resource search filter - [ResourceKeyExactMatch](#resourcekeyexactmatch) — Matches the value exactly - [ResourceKeyFilterProperty](#resourcekeyfilterproperty) — ResourceKey property with full advanced search capabilities - [ResourceResult](#resourceresult) — ResourceResult - [ResourceSearchQuery](#resourcesearchquery) — ResourceSearchQuery - [ResourceSearchQueryResult](#resourcesearchqueryresult) — ResourceSearchQueryResult - [ResourceSearchQuerySortRequest](#resourcesearchquerysortrequest) — ResourceSearchQuerySortRequest - [RetryDecision](#retrydecision) - [RoleClientResult](#roleclientresult) — RoleClientResult - [RoleClientSearchQueryRequest](#roleclientsearchqueryrequest) — RoleClientSearchQueryRequest - [RoleClientSearchQuerySortRequest](#roleclientsearchquerysortrequest) — RoleClientSearchQuerySortRequest - [RoleClientSearchResult](#roleclientsearchresult) — RoleClientSearchResult - [RoleCreateRequest](#rolecreaterequest) — RoleCreateRequest - [RoleCreateResult](#rolecreateresult) — RoleCreateResult - [RoleFilter](#rolefilter) — Role filter request - [RoleGroupResult](#rolegroupresult) — RoleGroupResult - [RoleGroupSearchQueryRequest](#rolegroupsearchqueryrequest) — RoleGroupSearchQueryRequest - [RoleGroupSearchQuerySortRequest](#rolegroupsearchquerysortrequest) — RoleGroupSearchQuerySortRequest - [RoleGroupSearchResult](#rolegroupsearchresult) — RoleGroupSearchResult - [RoleId](#roleid) — The unique identifier of a role - [RoleMappingRuleSearchResult](#rolemappingrulesearchresult) — RoleMappingRuleSearchResult - [RoleResult](#roleresult) — Role search response item - [RoleSearchQueryRequest](#rolesearchqueryrequest) — Role search request - [RoleSearchQueryResult](#rolesearchqueryresult) — Role search response - [RoleSearchQuerySortRequest](#rolesearchquerysortrequest) — RoleSearchQuerySortRequest - [RoleUpdateRequest](#roleupdaterequest) — RoleUpdateRequest - [RoleUpdateResult](#roleupdateresult) — RoleUpdateResult - [RoleUserResult](#roleuserresult) — RoleUserResult - [RoleUserSearchQueryRequest](#roleusersearchqueryrequest) — RoleUserSearchQueryRequest - [RoleUserSearchQuerySortRequest](#roleusersearchquerysortrequest) — RoleUserSearchQuerySortRequest - [RoleUserSearchResult](#roleusersearchresult) — RoleUserSearchResult - [ScopeKeyExactMatch](#scopekeyexactmatch) — Matches the value exactly - [ScopeKeyFilterProperty](#scopekeyfilterproperty) — ScopeKey property with full advanced search capabilities - [SearchQueryPageRequest](#searchquerypagerequest) — Pagination criteria - [SearchQueryPageResponse](#searchquerypageresponse) — Pagination information about the search results - [SearchQueryRequest](#searchqueryrequest) — SearchQueryRequest - [SearchQueryResponse](#searchqueryresponse) — SearchQueryResponse - [SetVariableRequest](#setvariablerequest) — SetVariableRequest - [SignalBroadcastRequest](#signalbroadcastrequest) — SignalBroadcastRequest - [SignalBroadcastResult](#signalbroadcastresult) — SignalBroadcastResult - [SourceElementIdInstruction](#sourceelementidinstruction) — Defines an instruction with a sourceElementId - [SourceElementInstanceKeyInstruction](#sourceelementinstancekeyinstruction) — Defines an instruction with a sourceElementInstanceKey - [SourceElementInstruction](#sourceelementinstruction) — Defines the source element identifier for the move instruction - [StartCursor](#startcursor) — The start cursor in a search query result set - [StatusMetric](#statusmetric) — Metric for a single job status - [StopResult](#stopresult) — Result of a call - [StringFilterProperty](#stringfilterproperty) — String property with full advanced search capabilities - [SystemConfigurationResponse](#systemconfigurationresponse) — Envelope for all system configuration sections - [Tag](#tag) — A tag - [TenantClientResult](#tenantclientresult) — TenantClientResult - [TenantClientSearchQueryRequest](#tenantclientsearchqueryrequest) — TenantClientSearchQueryRequest - [TenantClientSearchQuerySortRequest](#tenantclientsearchquerysortrequest) — TenantClientSearchQuerySortRequest - [TenantClientSearchResult](#tenantclientsearchresult) — TenantClientSearchResult - [TenantCreateRequest](#tenantcreaterequest) — TenantCreateRequest - [TenantCreateResult](#tenantcreateresult) — TenantCreateResult - [TenantFilter](#tenantfilter) — Tenant filter request - [TenantGroupResult](#tenantgroupresult) — TenantGroupResult - [TenantGroupSearchQueryRequest](#tenantgroupsearchqueryrequest) — TenantGroupSearchQueryRequest - [TenantGroupSearchQuerySortRequest](#tenantgroupsearchquerysortrequest) — TenantGroupSearchQuerySortRequest - [TenantGroupSearchResult](#tenantgroupsearchresult) — TenantGroupSearchResult - [TenantId](#tenantid) — The unique identifier of the tenant - [TenantMappingRuleSearchResult](#tenantmappingrulesearchresult) — TenantMappingRuleSearchResult - [TenantResult](#tenantresult) — Tenant search response item - [TenantRoleSearchResult](#tenantrolesearchresult) — TenantRoleSearchResult - [TenantSearchQueryRequest](#tenantsearchqueryrequest) — Tenant search request - [TenantSearchQueryResult](#tenantsearchqueryresult) — Tenant search response - [TenantSearchQuerySortRequest](#tenantsearchquerysortrequest) — TenantSearchQuerySortRequest - [TenantUpdateRequest](#tenantupdaterequest) — TenantUpdateRequest - [TenantUpdateResult](#tenantupdateresult) — TenantUpdateResult - [TenantUserResult](#tenantuserresult) — TenantUserResult - [TenantUserSearchQueryRequest](#tenantusersearchqueryrequest) — TenantUserSearchQueryRequest - [TenantUserSearchQuerySortRequest](#tenantusersearchquerysortrequest) — TenantUserSearchQuerySortRequest - [TenantUserSearchResult](#tenantusersearchresult) — TenantUserSearchResult - [TlsConfig](#tlsconfig) — TLS / mTLS configuration for custom certificates - [TopologyResponse](#topologyresponse) — The response of a topology request - [TypedVariables](#typedvariables) — Extension methods for deserializing Camunda variable and custom header payloads from untyped object properties into strongly-typed DTOs - [UpdateClusterVariableRequest](#updateclustervariablerequest) — UpdateClusterVariableRequest - [UpdateGlobalTaskListenerRequest](#updateglobaltasklistenerrequest) — UpdateGlobalTaskListenerRequest - [UsageMetricsResponse](#usagemetricsresponse) — UsageMetricsResponse - [UsageMetricsResponseItem](#usagemetricsresponseitem) — UsageMetricsResponseItem - [UseSourceParentKeyInstruction](#usesourceparentkeyinstruction) — Instructs the engine to use the source's direct parent key as the ancestor scope key for the target element - [UserCreateResult](#usercreateresult) — UserCreateResult - [UserFilter](#userfilter) — User search filter - [UserRequest](#userrequest) — UserRequest - [UserResult](#userresult) — UserResult - [UserSearchQueryRequest](#usersearchqueryrequest) — UserSearchQueryRequest - [UserSearchQuerySortRequest](#usersearchquerysortrequest) — UserSearchQuerySortRequest - [UserSearchResult](#usersearchresult) — UserSearchResult - [UserTaskAssignmentRequest](#usertaskassignmentrequest) — UserTaskAssignmentRequest - [UserTaskAuditLogFilter](#usertaskauditlogfilter) — The user task audit log search filters - [UserTaskAuditLogSearchQueryRequest](#usertaskauditlogsearchqueryrequest) — User task search query request - [UserTaskCompletionRequest](#usertaskcompletionrequest) — UserTaskCompletionRequest - [UserTaskEffectiveVariableSearchQueryRequest](#usertaskeffectivevariablesearchqueryrequest) — User task effective variable search query request - [UserTaskFilter](#usertaskfilter) — User task filter request - [UserTaskProperties](#usertaskproperties) — Contains properties of a user task - [UserTaskResult](#usertaskresult) — UserTaskResult - [UserTaskSearchQuery](#usertasksearchquery) — User task search query request - [UserTaskSearchQueryResult](#usertasksearchqueryresult) — User task search query response - [UserTaskSearchQuerySortRequest](#usertasksearchquerysortrequest) — UserTaskSearchQuerySortRequest - [UserTaskStateExactMatch](#usertaskstateexactmatch) — Matches the value exactly - [UserTaskStateFilterProperty](#usertaskstatefilterproperty) — UserTaskStateEnum property with full advanced search capabilities - [UserTaskUpdateRequest](#usertaskupdaterequest) — UserTaskUpdateRequest - [UserTaskVariableFilter](#usertaskvariablefilter) — The user task variable search filters - [UserTaskVariableSearchQueryRequest](#usertaskvariablesearchqueryrequest) — User task search query request - [UserTaskVariableSearchQuerySortRequest](#usertaskvariablesearchquerysortrequest) — UserTaskVariableSearchQuerySortRequest - [UserUpdateRequest](#userupdaterequest) — UserUpdateRequest - [UserUpdateResult](#userupdateresult) — UserUpdateResult - [Username](#username) — The unique name of a user - [VariableFilter](#variablefilter) — Variable filter request - [VariableKeyExactMatch](#variablekeyexactmatch) — Matches the value exactly - [VariableKeyFilterProperty](#variablekeyfilterproperty) — VariableKey property with full advanced search capabilities - [VariableResult](#variableresult) — Variable search response item - [VariableResultBase](#variableresultbase) — Variable response item - [VariableSearchQuery](#variablesearchquery) — Variable search query request - [VariableSearchQueryResult](#variablesearchqueryresult) — Variable search query response - [VariableSearchQuerySortRequest](#variablesearchquerysortrequest) — VariableSearchQuerySortRequest - [VariableSearchResult](#variablesearchresult) — Variable search response item - [VariableValueFilterProperty](#variablevaluefilterproperty) — VariableValueFilterProperty - [WorkerDefaultsConfig](#workerdefaultsconfig) --- ## ActivatedJob An activated job received from the Camunda broker, with typed variable access. This is what job handler functions receive. ```csharp public sealed class ActivatedJob ``` | Property | Type | Description | | -------------------------- | -------------------------- | ----------------------------------------------------- | | `Type` | `String` | The job type (matches the BPMN task definition type). | | `ProcessDefinitionId` | `ProcessDefinitionId` | The BPMN process ID of the job's process definition. | | `ProcessDefinitionVersion` | `Int32` | The version of the job's process definition. | | `ElementId` | `ElementId` | The associated task element ID. | | `CustomHeaders` | `Object` | Raw custom headers (typically a at runtime). | | `Worker` | `String` | The name of the worker that activated this job. | | `Retries` | `Int32` | Retries remaining for this job. | | `Deadline` | `Int64` | UNIX epoch timestamp (ms) when the job lock expires. | | `Variables` | `Object` | Raw variables (typically a at runtime). | | `TenantId` | `TenantId` | The tenant that owns this job. | | `JobKey` | `JobKey` | Unique identifier for this job. | | `ProcessInstanceKey` | `ProcessInstanceKey` | The process instance this job belongs to. | | `ProcessDefinitionKey` | `ProcessDefinitionKey` | The process definition key. | | `ElementInstanceKey` | `ElementInstanceKey` | The element instance key. | | `Kind` | `JobKindEnum` | The job kind. | | `ListenerEventType` | `JobListenerEventTypeEnum` | The listener event type. | | `UserTask` | `UserTaskProperties` | User task properties (if this is a user task job). | | `Tags` | `List` | Tags associated with this job. | ## ActivatedJobResult ```csharp public sealed class ActivatedJobResult ``` | Property | Type | Description | | -------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Type` | `String` | The type of the job (should match what was requested). | | `ProcessDefinitionId` | `ProcessDefinitionId` | The bpmn process ID of the job's process definition. | | `ProcessDefinitionVersion` | `Int32` | The version of the job's process definition. | | `ElementId` | `ElementId` | The associated task element ID. | | `CustomHeaders` | `Object` | A set of custom headers defined during modelling; returned as a serialized JSON document. | | `Worker` | `String` | The name of the worker which activated this job. | | `Retries` | `Int32` | The amount of retries left to this job (should always be positive). | | `Deadline` | `Int64` | When the job can be activated again, sent as a UNIX epoch timestamp. | | `Variables` | `Object` | All variables visible to the task scope, computed at activation time. | | `TenantId` | `TenantId` | The ID of the tenant that owns the job. | | `JobKey` | `JobKey` | The key, a unique identifier for the job. | | `ProcessInstanceKey` | `ProcessInstanceKey` | The job's process instance key. | | `ProcessDefinitionKey` | `ProcessDefinitionKey` | The key of the job's process definition. | | `ElementInstanceKey` | `ElementInstanceKey` | The element instance key of the task. | | `Kind` | `JobKindEnum` | The job kind. | | `ListenerEventType` | `JobListenerEventTypeEnum` | The listener event type of the job. | | `UserTask` | `UserTaskProperties` | User task properties, if the job is a user task. This is `null` if the job is not a user task. | | `Tags` | `List` | List of tags. Tags need to start with a letter; then alphanumerics, `_`, `-`, `:`, or `.`; length ≤ 100. | | `RootProcessInstanceKey` | `Nullable` | The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. | ## AdHocSubProcessActivateActivitiesInstruction ```csharp public sealed class AdHocSubProcessActivateActivitiesInstruction ``` | Property | Type | Description | | -------------------------- | ------------------------------------------------ | ---------------------------------------------------------------- | | `Elements` | `List` | Activities to activate. | | `CancelRemainingInstances` | `Nullable` | Whether to cancel remaining instances of the ad-hoc sub-process. | ## AdHocSubProcessActivateActivityReference ```csharp public sealed class AdHocSubProcessActivateActivityReference ``` | Property | Type | Description | | ----------- | ----------- | ------------------------------------------------ | | `ElementId` | `ElementId` | The ID of the element that should be activated. | | `Variables` | `Object` | Variables to be set when activating the element. | ## AdvancedActorTypeFilter Advanced AuditLogActorTypeEnum filter. ```csharp public sealed class AdvancedActorTypeFilter ``` | Property | Type | Description | | -------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedAgentInstanceKeyFilter Advanced AgentInstanceKey filter. ```csharp public sealed class AdvancedAgentInstanceKeyFilter ``` | Property | Type | Description | | -------- | ---------------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## AdvancedAgentInstanceStatusFilter Advanced AgentInstanceStatusEnum filter. ```csharp public sealed class AdvancedAgentInstanceStatusFilter ``` | Property | Type | Description | | -------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedAuditLogEntityKeyFilter Advanced entityKey filter. ```csharp public sealed class AdvancedAuditLogEntityKeyFilter ``` | Property | Type | Description | | -------- | ----------------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## AdvancedAuditLogKeyFilter Advanced AuditLogKey filter. ```csharp public sealed class AdvancedAuditLogKeyFilter ``` | Property | Type | Description | | -------- | ----------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## AdvancedBatchOperationItemStateFilter Advanced BatchOperationItemStateEnum filter. ```csharp public sealed class AdvancedBatchOperationItemStateFilter ``` | Property | Type | Description | | -------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedBatchOperationStateFilter Advanced BatchOperationStateEnum filter. ```csharp public sealed class AdvancedBatchOperationStateFilter ``` | Property | Type | Description | | -------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedBatchOperationTypeFilter Advanced BatchOperationTypeEnum filter. ```csharp public sealed class AdvancedBatchOperationTypeFilter ``` | Property | Type | Description | | -------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedCategoryFilter Advanced AuditLogCategoryEnum filter. ```csharp public sealed class AdvancedCategoryFilter ``` | Property | Type | Description | | -------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedClusterVariableScopeFilter Advanced ClusterVariableScopeEnum filter. ```csharp public sealed class AdvancedClusterVariableScopeFilter ``` | Property | Type | Description | | -------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedDateTimeFilter Advanced date-time filter. ```csharp public sealed class AdvancedDateTimeFilter ``` | Property | Type | Description | | -------- | -------------------------- | ---------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `Gt` | `Nullable` | Greater than comparison with the provided value. | | `Gte` | `Nullable` | Greater than or equal comparison with the provided value. | | `Lt` | `Nullable` | Lower than comparison with the provided value. | | `Lte` | `Nullable` | Lower than or equal comparison with the provided value. | | `In` | `List` | Checks if the property matches any of the provided values. | ## AdvancedDecisionDefinitionKeyFilter Advanced DecisionDefinitionKey filter. ```csharp public sealed class AdvancedDecisionDefinitionKeyFilter ``` | Property | Type | Description | | -------- | --------------------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## AdvancedDecisionEvaluationInstanceKeyFilter Advanced DecisionEvaluationInstanceKey filter. ```csharp public sealed class AdvancedDecisionEvaluationInstanceKeyFilter ``` | Property | Type | Description | | -------- | ----------------------------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## AdvancedDecisionEvaluationKeyFilter Advanced DecisionEvaluationKey filter. ```csharp public sealed class AdvancedDecisionEvaluationKeyFilter ``` | Property | Type | Description | | -------- | --------------------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## AdvancedDecisionInstanceStateFilter Advanced DecisionInstanceStateEnum filter. ```csharp public sealed class AdvancedDecisionInstanceStateFilter ``` | Property | Type | Description | | -------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedDecisionRequirementsKeyFilter Advanced DecisionRequirementsKey filter. ```csharp public sealed class AdvancedDecisionRequirementsKeyFilter ``` | Property | Type | Description | | -------- | ----------------------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## AdvancedDeploymentKeyFilter Advanced DeploymentKey filter. ```csharp public sealed class AdvancedDeploymentKeyFilter ``` | Property | Type | Description | | -------- | ------------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## AdvancedElementIdFilter Advanced ElementId filter. ```csharp public sealed class AdvancedElementIdFilter ``` | Property | Type | Description | | -------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedElementInstanceKeyFilter Advanced ElementInstanceKey filter. ```csharp public sealed class AdvancedElementInstanceKeyFilter ``` | Property | Type | Description | | -------- | ------------------------------ | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## AdvancedElementInstanceStateFilter Advanced ElementInstanceStateEnum filter. ```csharp public sealed class AdvancedElementInstanceStateFilter ``` | Property | Type | Description | | -------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedEntityTypeFilter Advanced AuditLogEntityTypeEnum filter. ```csharp public sealed class AdvancedEntityTypeFilter ``` | Property | Type | Description | | -------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedFormKeyFilter Advanced FormKey filter. ```csharp public sealed class AdvancedFormKeyFilter ``` | Property | Type | Description | | -------- | ------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## AdvancedGlobalListenerSourceFilter Advanced global listener source filter. ```csharp public sealed class AdvancedGlobalListenerSourceFilter ``` | Property | Type | Description | | -------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedGlobalTaskListenerEventTypeFilter Advanced global listener event type filter. ```csharp public sealed class AdvancedGlobalTaskListenerEventTypeFilter ``` | Property | Type | Description | | -------- | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedIncidentErrorTypeFilter Advanced IncidentErrorTypeEnum filter ```csharp public sealed class AdvancedIncidentErrorTypeFilter ``` | Property | Type | Description | | -------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property does not match any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedIncidentStateFilter Advanced IncidentStateEnum filter ```csharp public sealed class AdvancedIncidentStateFilter ``` | Property | Type | Description | | -------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property does not match any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedIntegerFilter Advanced integer (int32) filter. ```csharp public sealed class AdvancedIntegerFilter ``` | Property | Type | Description | | -------- | ------------------- | ---------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `Gt` | `Nullable` | Greater than comparison with the provided value. | | `Gte` | `Nullable` | Greater than or equal comparison with the provided value. | | `Lt` | `Nullable` | Lower than comparison with the provided value. | | `Lte` | `Nullable` | Lower than or equal comparison with the provided value. | | `In` | `List` | Checks if the property matches any of the provided values. | ## AdvancedJobKeyFilter Advanced JobKey filter. ```csharp public sealed class AdvancedJobKeyFilter ``` | Property | Type | Description | | -------- | ------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## AdvancedJobKindFilter Advanced JobKindEnum filter. ```csharp public sealed class AdvancedJobKindFilter ``` | Property | Type | Description | | -------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedJobListenerEventTypeFilter Advanced JobListenerEventTypeEnum filter. ```csharp public sealed class AdvancedJobListenerEventTypeFilter ``` | Property | Type | Description | | -------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedJobStateFilter Advanced JobStateEnum filter. ```csharp public sealed class AdvancedJobStateFilter ``` | Property | Type | Description | | -------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedMessageSubscriptionKeyFilter Advanced MessageSubscriptionKey filter. ```csharp public sealed class AdvancedMessageSubscriptionKeyFilter ``` | Property | Type | Description | | -------- | ---------------------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for equality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## AdvancedMessageSubscriptionStateFilter Advanced MessageSubscriptionStateEnum filter ```csharp public sealed class AdvancedMessageSubscriptionStateFilter ``` | Property | Type | Description | | -------- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedMessageSubscriptionTypeFilter Advanced MessageSubscriptionTypeEnum filter ```csharp public sealed class AdvancedMessageSubscriptionTypeFilter ``` | Property | Type | Description | | -------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedOperationTypeFilter Advanced AuditLogOperationTypeEnum filter. ```csharp public sealed class AdvancedOperationTypeFilter ``` | Property | Type | Description | | -------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedProcessDefinitionIdFilter Advanced ProcessDefinitionId filter. ```csharp public sealed class AdvancedProcessDefinitionIdFilter ``` | Property | Type | Description | | -------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedProcessDefinitionKeyFilter Advanced ProcessDefinitionKey filter. ```csharp public sealed class AdvancedProcessDefinitionKeyFilter ``` | Property | Type | Description | | -------- | -------------------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## AdvancedProcessInstanceKeyFilter Advanced ProcessInstanceKey filter. ```csharp public sealed class AdvancedProcessInstanceKeyFilter ``` | Property | Type | Description | | -------- | ------------------------------ | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## AdvancedProcessInstanceStateFilter Advanced ProcessInstanceStateEnum filter. ```csharp public sealed class AdvancedProcessInstanceStateFilter ``` | Property | Type | Description | | -------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedResourceKeyFilter Advanced ResourceKey filter. ```csharp public sealed class AdvancedResourceKeyFilter ``` | Property | Type | Description | | -------- | ----------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## AdvancedResultFilter Advanced AuditLogResultEnum filter. ```csharp public sealed class AdvancedResultFilter ``` | Property | Type | Description | | -------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedScopeKeyFilter Advanced ScopeKey filter. ```csharp public sealed class AdvancedScopeKeyFilter ``` | Property | Type | Description | | -------- | -------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## AdvancedStringFilter Advanced string filter. ```csharp public sealed class AdvancedStringFilter ``` | Property | Type | Description | | -------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `String` | Checks for equality with the provided value. | | `Neq` | `String` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedUserTaskStateFilter Advanced UserTaskStateEnum filter. ```csharp public sealed class AdvancedUserTaskStateFilter ``` | Property | Type | Description | | -------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AdvancedVariableKeyFilter Advanced VariableKey filter. ```csharp public sealed class AdvancedVariableKeyFilter ``` | Property | Type | Description | | -------- | ----------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## AgentInstanceCreationRequest Request to create a new agent instance. ```csharp public sealed class AgentInstanceCreationRequest ``` | Property | Type | Description | | -------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ElementInstanceKey` | `ElementInstanceKey` | The key of the AHSP or AI Agent Task element instance. The engine uses this key to infer processInstanceKey, elementId, processDefinitionKey, and tenantId. | | `Definition` | `AgentInstanceDefinition` | Static definition set once at creation. | | `Limits` | `AgentInstanceLimits` | Limits for the agent execution. When omitted, all limits default to -1 (no limit). | ## AgentInstanceCreationResult Response returned after successfully creating an agent instance. ```csharp public sealed class AgentInstanceCreationResult ``` | Property | Type | Description | | ------------------ | ------------------ | -------------------------------------------------------- | | `AgentInstanceKey` | `AgentInstanceKey` | The system-generated key for the created agent instance. | ## AgentInstanceDefinition The static definition of an agent instance, set once at creation. ```csharp public sealed class AgentInstanceDefinition ``` | Property | Type | Description | | -------------- | -------- | ----------------------------------------------------- | | `Model` | `String` | The LLM model identifier (for example, gpt-4o). | | `Provider` | `String` | The LLM provider (for example, openai or anthropic). | | `SystemPrompt` | `String` | The system prompt configured for this agent instance. | ## AgentInstanceFilter Agent instance search filter. ```csharp public sealed class AgentInstanceFilter ``` | Property | Type | Description | | ---------------------- | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `AgentInstanceKey` | `AgentInstanceKeyFilterProperty` | The unique key of the agent instance. | | `Status` | `AgentInstanceStatusFilterProperty` | The current status of the agent instance. | | `ElementId` | `ElementIdFilterProperty` | The BPMN element ID of the agent task. | | `ProcessInstanceKey` | `ProcessInstanceKeyFilterProperty` | The key of the process instance that owns this agent instance. | | `ProcessDefinitionKey` | `ProcessDefinitionKeyFilterProperty` | The key of the process definition associated with this agent instance. | | `TenantId` | `StringFilterProperty` | The tenant ID of the agent instance. | | `CreationDate` | `DateTimeFilterProperty` | The creation date of the agent instance. | | `LastUpdatedDate` | `DateTimeFilterProperty` | The date the agent instance was last updated. | | `CompletionDate` | `DateTimeFilterProperty` | The completion date of the agent instance. | | `ElementInstanceKeys` | `List` | The keys of element instances associated with this agent instance. If multiple keys are provided, the filter matches agent instances associated with all of the provided keys at the same time. | ## AgentInstanceKeyExactMatch Matches the value exactly. ```csharp public readonly record struct AgentInstanceKeyExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## AgentInstanceKeyFilterProperty AgentInstanceKey property with full advanced search capabilities. ```csharp public sealed class AgentInstanceKeyFilterProperty ``` | Property | Type | Description | | -------- | ---------------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## AgentInstanceLimits The configured limits for an agent instance, set once at creation. ```csharp public sealed class AgentInstanceLimits ``` | Property | Type | Description | | --------------- | ------- | ----------------------------------------------------------- | | `MaxModelCalls` | `Int64` | Maximum LLM calls allowed. -1 if no limit is configured. | | `MaxToolCalls` | `Int64` | Maximum tool calls allowed. -1 if no limit is configured. | | `MaxTokens` | `Int64` | Maximum total tokens allowed. -1 if no limit is configured. | ## AgentInstanceMetrics Aggregated metrics for an agent instance across all model calls. ```csharp public sealed class AgentInstanceMetrics ``` | Property | Type | Description | | -------------- | ------- | ---------------------------------------------------- | | `InputTokens` | `Int64` | Total input tokens consumed across all model calls. | | `OutputTokens` | `Int64` | Total output tokens produced across all model calls. | | `ModelCalls` | `Int64` | Total number of LLM calls made. | | `ToolCalls` | `Int64` | Total number of tool calls made. | ## AgentInstanceMetricsDelta Metric increments to apply to the agent instance aggregate counters. The engine accumulates these deltas into running totals on each UPDATED event. All fields are optional; omit a field to leave the corresponding counter unchanged. ```csharp public sealed class AgentInstanceMetricsDelta ``` | Property | Type | Description | | -------------- | ----------------- | ----------------------------------------------------- | | `InputTokens` | `Nullable` | Increment to apply to the total input token counter. | | `OutputTokens` | `Nullable` | Increment to apply to the total output token counter. | | `ModelCalls` | `Nullable` | Increment to apply to the total model call counter. | | `ToolCalls` | `Nullable` | Increment to apply to the total tool call counter. | ## AgentInstanceResult ```csharp public sealed class AgentInstanceResult ``` | Property | Type | Description | | ---------------------- | -------------------------- | --------------------------------------------------------------------------------------------- | | `AgentInstanceKey` | `AgentInstanceKey` | The unique key for this agent instance. | | `Status` | `AgentInstanceStatusEnum` | The current status of an agent instance. | | `Definition` | `AgentInstanceDefinition` | The static definition of the agent, including model, provider, and system prompt. | | `Metrics` | `AgentInstanceMetrics` | Aggregated metrics across all iterations of this agent instance. | | `Limits` | `AgentInstanceLimits` | The configured limits for this agent instance, set once at creation. | | `Tools` | `List` | The tools available to the agent. | | `ElementId` | `ElementId` | The BPMN element ID of the ad-hoc sub-process or AI agent task that owns this agent instance. | | `ProcessInstanceKey` | `ProcessInstanceKey` | The key of the process instance that owns this agent instance. | | `ProcessDefinitionKey` | `ProcessDefinitionKey` | The key of the process definition associated with this agent instance. | | `TenantId` | `TenantId` | The tenant ID of this agent instance. | | `CreationDate` | `DateTimeOffset` | The date when this agent instance was created. | | `LastUpdatedDate` | `DateTimeOffset` | The date when this agent instance was last updated. | | `CompletionDate` | `Nullable` | The date when this agent instance completed. Null while the agent is still running. | | `ElementInstanceKeys` | `List` | The keys of all element instances associated with this agent instance. | ## AgentInstanceSearchQuery Agent instance search request. ```csharp public sealed class AgentInstanceSearchQuery ``` | Property | Type | Description | | -------- | ------------------------------------------- | ---------------------------------- | | `Sort` | `List` | Sort field criteria. | | `Filter` | `AgentInstanceFilter` | The agent instance search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## AgentInstanceSearchQueryResult Agent instance search response. ```csharp public sealed class AgentInstanceSearchQueryResult ``` | Property | Type | Description | | -------- | --------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching agent instances. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## AgentInstanceSearchQuerySortRequest ```csharp public sealed class AgentInstanceSearchQuerySortRequest ``` | Property | Type | Description | | -------- | ------------------------------------------ | --------------------------------------------- | | `Field` | `AgentInstanceSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## AgentInstanceStatusExactMatch Matches the value exactly. ```csharp public readonly record struct AgentInstanceStatusExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## AgentInstanceStatusFilterProperty AgentInstanceStatusEnum property with full advanced search capabilities. ```csharp public sealed class AgentInstanceStatusFilterProperty ``` | Property | Type | Description | | -------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AgentInstanceUpdateRequest Request to update the mutable state of an agent instance. At least one of status, metrics, or tools must be provided. ```csharp public sealed class AgentInstanceUpdateRequest ``` | Property | Type | Description | | --------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Status` | `Nullable` | The new status of the agent instance. | | `Metrics` | `AgentInstanceMetricsDelta` | Metric increments to apply to the aggregate counters. | | `Tools` | `List` | The complete list of tools available to the agent, replacing any previously stored tools. When provided, the engine replaces the existing tool list with this value. | ## AgentTool A tool available to the agent. ```csharp public sealed class AgentTool ``` | Property | Type | Description | | ------------- | -------- | ---------------------------------------------------------------------- | | `Name` | `String` | The tool name as visible to the LLM. | | `Description` | `String` | A human-readable description of the tool. | | `ElementId` | `String` | The BPMN element ID of the tool element within the ad-hoc sub-process. | ## AncestorScopeInstruction Defines the ancestor scope for the created element instances. The default behavior resembles a "direct" scope instruction with an `ancestorElementInstanceKey` of `"-1"`. ```csharp public abstract class AncestorScopeInstruction ``` ## AuditLogActorTypeExactMatch Matches the value exactly. ```csharp public readonly record struct AuditLogActorTypeExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## AuditLogActorTypeFilterProperty AuditLogActorTypeEnum property with full advanced search capabilities. ```csharp public sealed class AuditLogActorTypeFilterProperty ``` | Property | Type | Description | | -------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AuditLogEntityKeyExactMatch Matches the value exactly. ```csharp public readonly record struct AuditLogEntityKeyExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## AuditLogEntityKeyFilterProperty EntityKey property with full advanced search capabilities. ```csharp public sealed class AuditLogEntityKeyFilterProperty ``` | Property | Type | Description | | -------- | ----------------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## AuditLogFilter Audit log filter request ```csharp public sealed class AuditLogFilter ``` | Property | Type | Description | | ------------------------- | --------------------------------------- | -------------------------------------------- | | `AuditLogKey` | `AuditLogKeyFilterProperty` | The audit log key search filter. | | `ProcessDefinitionKey` | `ProcessDefinitionKeyFilterProperty` | The process definition key search filter. | | `ProcessInstanceKey` | `ProcessInstanceKeyFilterProperty` | The process instance key search filter. | | `ElementInstanceKey` | `ElementInstanceKeyFilterProperty` | The element instance key search filter. | | `OperationType` | `OperationTypeFilterProperty` | The operation type search filter. | | `Result` | `AuditLogResultFilterProperty` | The result search filter. | | `Timestamp` | `DateTimeFilterProperty` | The timestamp search filter. | | `ActorId` | `StringFilterProperty` | The actor ID search filter. | | `ActorType` | `AuditLogActorTypeFilterProperty` | The actor type search filter. | | `AgentElementId` | `StringFilterProperty` | The agent element ID search filter. | | `EntityKey` | `AuditLogEntityKeyFilterProperty` | The entity key search filter. | | `EntityType` | `EntityTypeFilterProperty` | The entity type search filter. | | `TenantId` | `StringFilterProperty` | The tenant ID search filter. | | `Category` | `CategoryFilterProperty` | The category search filter. | | `DeploymentKey` | `DeploymentKeyFilterProperty` | The deployment key search filter. | | `FormKey` | `FormKeyFilterProperty` | The form key search filter. | | `ResourceKey` | `ResourceKeyFilterProperty` | The resource key search filter. | | `BatchOperationType` | `BatchOperationTypeFilterProperty` | The batch operation type search filter. | | `ProcessDefinitionId` | `StringFilterProperty` | The process definition ID search filter. | | `JobKey` | `JobKeyFilterProperty` | The job key search filter. | | `UserTaskKey` | `BasicStringFilterProperty` | The user task key search filter. | | `DecisionRequirementsId` | `StringFilterProperty` | The decision requirements ID search filter. | | `DecisionRequirementsKey` | `DecisionRequirementsKeyFilterProperty` | The decision requirements key search filter. | | `DecisionDefinitionId` | `StringFilterProperty` | The decision definition ID search filter. | | `DecisionDefinitionKey` | `DecisionDefinitionKeyFilterProperty` | The decision definition key search filter. | | `DecisionEvaluationKey` | `DecisionEvaluationKeyFilterProperty` | The decision evaluation key search filter. | | `RelatedEntityKey` | `AuditLogEntityKeyFilterProperty` | The related entity key search filter. | | `RelatedEntityType` | `EntityTypeFilterProperty` | The related entity type search filter. | | `EntityDescription` | `StringFilterProperty` | The entity description filter. | ## AuditLogKeyExactMatch Matches the value exactly. ```csharp public readonly record struct AuditLogKeyExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## AuditLogKeyFilterProperty AuditLogKey property with full advanced search capabilities. ```csharp public sealed class AuditLogKeyFilterProperty ``` | Property | Type | Description | | -------- | ----------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## AuditLogResult Audit log item. ```csharp public sealed class AuditLogResult ``` | Property | Type | Description | | ------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `AuditLogKey` | `AuditLogKey` | The unique key of the audit log entry. | | `EntityKey` | `AuditLogEntityKey` | System-generated entity key for an audit log entry. | | `EntityType` | `AuditLogEntityTypeEnum` | The type of entity affected by the operation. | | `OperationType` | `AuditLogOperationTypeEnum` | The type of operation performed. | | `BatchOperationKey` | `Nullable` | Key of the batch operation. | | `BatchOperationType` | `Nullable` | The type of batch operation performed, if this is part of a batch. | | `Timestamp` | `DateTimeOffset` | The timestamp when the operation occurred. | | `ActorId` | `String` | The ID of the actor who performed the operation. | | `ActorType` | `Nullable` | The type of the actor who performed the operation. | | `AgentElementId` | `String` | The element ID of the agent that performed the operation (e.g. ad-hoc subprocess element ID). | | `TenantId` | `Nullable` | The tenant ID of the audit log. | | `Result` | `AuditLogResultEnum` | The result status of the operation. | | `Category` | `AuditLogCategoryEnum` | The category of the audit log operation. | | `ProcessDefinitionId` | `Nullable` | The process definition ID. | | `ProcessDefinitionKey` | `Nullable` | The key of the process definition. | | `ProcessInstanceKey` | `Nullable` | The key of the process instance. | | `RootProcessInstanceKey` | `Nullable` | The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. | | `ElementInstanceKey` | `Nullable` | The key of the element instance. | | `JobKey` | `Nullable` | The key of the job. | | `UserTaskKey` | `Nullable` | The key of the user task. | | `DecisionRequirementsId` | `String` | The decision requirements ID. | | `DecisionRequirementsKey` | `Nullable` | The assigned key of the decision requirements. | | `DecisionDefinitionId` | `Nullable` | The decision definition ID. | | `DecisionDefinitionKey` | `Nullable` | The key of the decision definition. | | `DecisionEvaluationKey` | `Nullable` | The key of the decision evaluation. | | `DeploymentKey` | `Nullable` | The key of the deployment. | | `FormKey` | `Nullable` | The key of the form. | | `ResourceKey` | `Nullable` | The system-assigned key for this resource. | | `RelatedEntityKey` | `Nullable` | The key of the related entity. The content depends on the operation type and entity type. For example, for authorization operations, this will contain the ID of the owner (e.g., user or group) the authorization belongs to. | | `RelatedEntityType` | `Nullable` | The type of the related entity. The content depends on the operation type and entity type. For example, for authorization operations, this will contain the type of the owner (e.g., USER or GROUP) the authorization belongs to. | | `EntityDescription` | `String` | Additional description of the entity affected by the operation. For example, for variable operations, this will contain the variable name. | ## AuditLogResultExactMatch Matches the value exactly. ```csharp public readonly record struct AuditLogResultExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## AuditLogResultFilterProperty AuditLogResultEnum property with full advanced search capabilities. ```csharp public sealed class AuditLogResultFilterProperty ``` | Property | Type | Description | | -------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## AuditLogSearchQueryRequest Audit log search request. ```csharp public sealed class AuditLogSearchQueryRequest ``` | Property | Type | Description | | -------- | -------------------------------------- | ----------------------------- | | `Sort` | `List` | Sort field criteria. | | `Filter` | `AuditLogFilter` | The audit log search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## AuditLogSearchQueryResult Audit log search response. ```csharp public sealed class AuditLogSearchQueryResult ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching audit logs. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## AuditLogSearchQuerySortRequest ```csharp public sealed class AuditLogSearchQuerySortRequest ``` | Property | Type | Description | | -------- | ------------------------------------- | --------------------------------------------- | | `Field` | `AuditLogSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## AuthenticationConfigurationResponse Configuration for authentication and session management. ```csharp public sealed class AuthenticationConfigurationResponse ``` | Property | Type | Description | | ------------------ | --------- | ------------------------------------------------------------ | | `CanLogout` | `Boolean` | Whether users can log out (false for SaaS deployments). | | `IsLoginDelegated` | `Boolean` | Whether login is delegated to an external identity provider. | ## AuthorizationCreateResult ```csharp public sealed class AuthorizationCreateResult ``` | Property | Type | Description | | ------------------ | ------------------ | ------------------------------------- | | `AuthorizationKey` | `AuthorizationKey` | The key of the created authorization. | ## AuthorizationFilter Authorization search filter. ```csharp public sealed class AuthorizationFilter ``` | Property | Type | Description | | ----------------------- | ---------------------------- | --------------------------------------------------------------- | | `OwnerId` | `String` | The ID of the owner of permissions. | | `OwnerType` | `Nullable` | The type of the owner of permissions. | | `ResourceIds` | `List` | The IDs of the resource to search permissions for. | | `ResourcePropertyNames` | `List` | The names of the resource properties to search permissions for. | | `ResourceType` | `Nullable` | The type of resource to search permissions for. | ## AuthorizationIdBasedRequest ```csharp public sealed class AuthorizationIdBasedRequest : AuthorizationRequest ``` | Property | Type | Description | | ----------------- | -------------------------- | --------------------------------------------- | | `OwnerId` | `String` | The ID of the owner of the permissions. | | `OwnerType` | `OwnerTypeEnum` | The type of the owner of permissions. | | `ResourceId` | `String` | The ID of the resource to add permissions to. | | `ResourceType` | `ResourceTypeEnum` | The type of resource to add permissions to. | | `PermissionTypes` | `List` | The permission types to add. | ## AuthorizationPropertyBasedRequest ```csharp public sealed class AuthorizationPropertyBasedRequest : AuthorizationRequest ``` | Property | Type | Description | | ---------------------- | -------------------------- | ----------------------------------------------------------------------- | | `OwnerId` | `String` | The ID of the owner of the permissions. | | `OwnerType` | `OwnerTypeEnum` | The type of the owner of permissions. | | `ResourcePropertyName` | `String` | The name of the resource property on which this authorization is based. | | `ResourceType` | `ResourceTypeEnum` | The type of resource to add permissions to. | | `PermissionTypes` | `List` | The permission types to add. | ## AuthorizationRequest Defines an authorization request. Either an id-based or a property-based authorization can be provided. ```csharp public abstract class AuthorizationRequest ``` ## AuthorizationResult ```csharp public sealed class AuthorizationResult ``` | Property | Type | Description | | ---------------------- | -------------------------- | --------------------------------------------------------------------------------------------------- | | `OwnerId` | `String` | The ID of the owner of permissions. | | `OwnerType` | `OwnerTypeEnum` | The type of the owner of permissions. | | `ResourceType` | `ResourceTypeEnum` | The type of resource that the permissions relate to. | | `ResourceId` | `String` | ID of the resource the permission relates to (mutually exclusive with `resourcePropertyName`). | | `ResourcePropertyName` | `String` | The name of the resource property the permission relates to (mutually exclusive with `resourceId`). | | `PermissionTypes` | `List` | Specifies the types of the permissions. | | `AuthorizationKey` | `AuthorizationKey` | The key of the authorization. | ## AuthorizationSearchQuery ```csharp public sealed class AuthorizationSearchQuery ``` | Property | Type | Description | | -------- | ------------------------------------------- | --------------------------------- | | `Sort` | `List` | Sort field criteria. | | `Filter` | `AuthorizationFilter` | The authorization search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## AuthorizationSearchQuerySortRequest ```csharp public sealed class AuthorizationSearchQuerySortRequest ``` | Property | Type | Description | | -------- | ------------------------------------------ | --------------------------------------------- | | `Field` | `AuthorizationSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## AuthorizationSearchResult ```csharp public sealed class AuthorizationSearchResult ``` | Property | Type | Description | | -------- | --------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching authorizations. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## BackpressureState ```csharp public sealed class BackpressureState ``` | Property | Type | Description | | ------------- | ----------------- | ----------- | | `Severity` | `String` | | | `PermitsMax` | `Nullable` | | | `Consecutive` | `Int32` | | ## BaseProcessInstanceFilterFields Base process instance search filter. ```csharp public sealed class BaseProcessInstanceFilterFields ``` | Property | Type | Description | | ---------------------------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `StartDate` | `DateTimeFilterProperty` | The start date. | | `EndDate` | `DateTimeFilterProperty` | The end date. | | `State` | `ProcessInstanceStateFilterProperty` | The process instance state. | | `HasIncident` | `Nullable` | Whether this process instance has a related incident or not. | | `TenantId` | `StringFilterProperty` | The tenant id. | | `Variables` | `List` | The process instance variables. | | `ProcessInstanceKey` | `ProcessInstanceKeyFilterProperty` | The key of this process instance. | | `ParentProcessInstanceKey` | `ProcessInstanceKeyFilterProperty` | The parent process instance key. | | `ParentElementInstanceKey` | `ElementInstanceKeyFilterProperty` | The parent element instance key. | | `BatchOperationId` | `StringFilterProperty` | The batch operation id. **Deprecated**: Use `batchOperationKey` instead. This field will be removed in a future release. If both `batchOperationId` and `batchOperationKey` are provided, the request will be rejected with a 400 error. | | `BatchOperationKey` | `StringFilterProperty` | The batch operation key. | | `ErrorMessage` | `StringFilterProperty` | The error message related to the process. | | `HasRetriesLeft` | `Nullable` | Whether the process has failed jobs with retries left. | | `ElementInstanceState` | `ElementInstanceStateFilterProperty` | The state of the element instances associated with the process instance. | | `ElementId` | `StringFilterProperty` | The element id associated with the process instance. | | `HasElementInstanceIncident` | `Nullable` | Whether the element instance has an incident or not. | | `IncidentErrorHashCode` | `IntegerFilterProperty` | The incident error hash code, associated with this process. | | `Tags` | `List` | List of tags. Tags need to start with a letter; then alphanumerics, `_`, `-`, `:`, or `.`; length ≤ 100. | | `BusinessId` | `StringFilterProperty` | The business id associated with the process instance. | ## BasicStringFilter Basic advanced string filter. ```csharp public sealed class BasicStringFilter ``` | Property | Type | Description | | -------- | ------------------- | ----------------------------------------------------------- | | `Eq` | `String` | Checks for equality with the provided value. | | `Neq` | `String` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## BasicStringFilterProperty String property with basic advanced search capabilities. ```csharp public sealed class BasicStringFilterProperty ``` | Property | Type | Description | | -------- | ------------------- | ----------------------------------------------------------- | | `Eq` | `String` | Checks for equality with the provided value. | | `Neq` | `String` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## BatchOperationCreatedResult The created batch operation. ```csharp public sealed class BatchOperationCreatedResult ``` | Property | Type | Description | | -------------------- | ------------------------ | -------------------------------- | | `BatchOperationKey` | `BatchOperationKey` | Key of the batch operation. | | `BatchOperationType` | `BatchOperationTypeEnum` | The type of the batch operation. | ## BatchOperationError ```csharp public sealed class BatchOperationError ``` | Property | Type | Description | | ------------- | ------------------------- | --------------------------------------------------------------- | | `PartitionId` | `Int32` | The partition ID where the error occurred. | | `Type` | `BatchOperationErrorType` | The type of the error that occurred during the batch operation. | | `Message` | `String` | The error message that occurred during the batch operation. | ## BatchOperationFilter Batch operation filter request. ```csharp public sealed class BatchOperationFilter ``` | Property | Type | Description | | ------------------- | ----------------------------------- | ------------------------------------------------------ | | `BatchOperationKey` | `BasicStringFilterProperty` | The key (or operate legacy ID) of the batch operation. | | `OperationType` | `BatchOperationTypeFilterProperty` | The type of the batch operation. | | `State` | `BatchOperationStateFilterProperty` | The state of the batch operation. | | `ActorType` | `Nullable` | The type of the actor who performed the operation. | | `ActorId` | `StringFilterProperty` | The ID of the actor who performed the operation. | ## BatchOperationItemFilter Batch operation item filter request. ```csharp public sealed class BatchOperationItemFilter ``` | Property | Type | Description | | -------------------- | ---------------------------------- | ------------------------------------------------------ | | `BatchOperationKey` | `BasicStringFilterProperty` | The key (or operate legacy ID) of the batch operation. | | `ItemKey` | `BasicStringFilterProperty` | The key of the item, e.g. a process instance key. | | `ProcessInstanceKey` | `ProcessInstanceKeyFilterProperty` | The process instance key of the processed item. | | `State` | `String` | The state of the batch operation. | | `OperationType` | `BatchOperationTypeFilterProperty` | The type of the batch operation. | ## BatchOperationItemResponse ```csharp public sealed class BatchOperationItemResponse ``` | Property | Type | Description | | ------------------------ | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `OperationType` | `BatchOperationTypeEnum` | The type of the batch operation. | | `BatchOperationKey` | `BatchOperationKey` | The key (or operate legacy ID) of the batch operation. | | `ItemKey` | `String` | Key of the item, e.g. a process instance key. | | `ProcessInstanceKey` | `Nullable` | The process instance key of the processed item. Null for batch-op types whose targets are not process instances (e.g. DELETE_DECISION_INSTANCE, DELETE_DECISION_DEFINITION, DELETE_PROCESS_DEFINITION). | | `RootProcessInstanceKey` | `Nullable` | The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. | | `State` | `BatchOperationItemResponseState` | State of the item. | | `ProcessedDate` | `Nullable` | The date this item was processed. This is `null` if the item has not yet been processed. | | `ErrorMessage` | `String` | The error message from the engine in case of a failed operation. | ## BatchOperationItemSearchQuery Batch operation item search request. ```csharp public sealed class BatchOperationItemSearchQuery ``` | Property | Type | Description | | -------- | ------------------------------------------------ | ---------------------------------------- | | `Sort` | `List` | Sort field criteria. | | `Filter` | `BatchOperationItemFilter` | The batch operation item search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## BatchOperationItemSearchQueryResult ```csharp public sealed class BatchOperationItemSearchQueryResult ``` | Property | Type | Description | | -------- | ---------------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching batch operation items. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## BatchOperationItemSearchQuerySortRequest ```csharp public sealed class BatchOperationItemSearchQuerySortRequest ``` | Property | Type | Description | | -------- | ----------------------------------------------- | --------------------------------------------- | | `Field` | `BatchOperationItemSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## BatchOperationItemStateExactMatch Matches the value exactly. ```csharp public readonly record struct BatchOperationItemStateExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## BatchOperationItemStateFilterProperty BatchOperationItemStateEnum property with full advanced search capabilities. ```csharp public sealed class BatchOperationItemStateFilterProperty ``` | Property | Type | Description | | -------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## BatchOperationResponse ```csharp public sealed class BatchOperationResponse ``` | Property | Type | Description | | -------------------------- | --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `BatchOperationKey` | `BatchOperationKey` | Key or (Operate Legacy ID = UUID) of the batch operation. | | `State` | `BatchOperationStateEnum` | The batch operation state. | | `BatchOperationType` | `BatchOperationTypeEnum` | The type of the batch operation. | | `StartDate` | `Nullable` | The start date of the batch operation. This is `null` if the batch operation has not yet started. | | `EndDate` | `Nullable` | The end date of the batch operation. This is `null` if the batch operation is still running. | | `ActorType` | `Nullable` | The type of the actor who performed the operation. This is `null` if the batch operation was created before 8.9, or if the actor information is not available. | | `ActorId` | `String` | The ID of the actor who performed the operation. Available for batch operations created since 8.9. | | `OperationsTotalCount` | `Int32` | The total number of items contained in this batch operation. | | `OperationsFailedCount` | `Int32` | The number of items which failed during execution of the batch operation. (e.g. because they are rejected by the Zeebe engine). | | `OperationsCompletedCount` | `Int32` | The number of successfully completed tasks. | | `Errors` | `List` | The errors that occurred per partition during the batch operation. | ## BatchOperationSearchQuery Batch operation search request. ```csharp public sealed class BatchOperationSearchQuery ``` | Property | Type | Description | | -------- | -------------------------------------------- | ----------------------------------- | | `Sort` | `List` | Sort field criteria. | | `Filter` | `BatchOperationFilter` | The batch operation search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## BatchOperationSearchQueryResult The batch operation search query result. ```csharp public sealed class BatchOperationSearchQueryResult ``` | Property | Type | Description | | -------- | ------------------------------ | ------------------------------------------------ | | `Items` | `List` | The matching batch operations. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## BatchOperationSearchQuerySortRequest ```csharp public sealed class BatchOperationSearchQuerySortRequest ``` | Property | Type | Description | | -------- | ------------------------------------------- | --------------------------------------------- | | `Field` | `BatchOperationSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## BatchOperationStateExactMatch Matches the value exactly. ```csharp public readonly record struct BatchOperationStateExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## BatchOperationStateFilterProperty BatchOperationStateEnum property with full advanced search capabilities. ```csharp public sealed class BatchOperationStateFilterProperty ``` | Property | Type | Description | | -------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## BatchOperationTypeExactMatch Matches the value exactly. ```csharp public readonly record struct BatchOperationTypeExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## BatchOperationTypeFilterProperty BatchOperationTypeEnum property with full advanced search capabilities. ```csharp public sealed class BatchOperationTypeFilterProperty ``` | Property | Type | Description | | -------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## BpmnErrorException Throw from a job handler to trigger a BPMN error boundary event on the job's task. The error code is matched against error catch events in the process model. ```csharp public sealed class BpmnErrorException : Exception, ISerializable ``` | Property | Type | Description | | -------------- | -------- | --------------------------------------------------------- | | `ErrorCode` | `String` | The error code matched against BPMN error catch events. | | `ErrorMessage` | `String` | Optional additional context message. | | `Variables` | `Object` | Optional variables to set at the error catch event scope. | ## BrokerInfo Provides information on a broker node. ```csharp public sealed class BrokerInfo ``` | Property | Type | Description | | ------------ | ----------------- | ---------------------------------------------------------- | | `NodeId` | `Int32` | The unique (within a cluster) node ID for the broker. | | `Host` | `String` | The hostname for reaching the broker. | | `Port` | `Int32` | The port for reaching the broker. | | `Partitions` | `List` | A list of partitions managed or replicated on this broker. | | `Version` | `String` | The broker version. | ## BusinessId An optional, user-defined string identifier that identifies the process instance within the scope of a process definition (scoped by tenant). If provided and uniqueness enforcement is enabled, the engine will reject creation if another root process instance with the same business id is already active for the same process definition. Note that any active child process instances with the same business id are not taken into account. ```csharp public readonly record struct BusinessId : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## CamundaAuthException Authentication-specific exception. ```csharp public sealed class CamundaAuthException : Exception, ISerializable ``` | Property | Type | Description | | -------- | ---------------------- | ----------- | | `Code` | `CamundaAuthErrorCode` | | ## CamundaConfigurationException Thrown when configuration hydration encounters validation errors. ```csharp public sealed class CamundaConfigurationException : Exception, ISerializable ``` | Property | Type | Description | | -------- | ---------------------------------- | ----------- | | `Errors` | `IReadOnlyList` | | ## CamundaKeyJsonConverterFactory JSON converter factory that handles any struct. Serializes as a plain JSON string; deserializes by calling the static AssumeExists factory. ```csharp public sealed class CamundaKeyJsonConverterFactory : JsonConverterFactory ``` ## CamundaKeyValidation Validation helpers for domain key constraints. ```csharp public static class CamundaKeyValidation ``` ## CamundaLongKeyJsonConverterFactory JSON converter factory that handles any struct. Serializes as a JSON number; deserializes by calling the static AssumeExists factory. ```csharp public sealed class CamundaLongKeyJsonConverterFactory : JsonConverterFactory ``` ## CamundaSdkException SDK error types mirroring the JS SDK's error structure. ```csharp public class CamundaSdkException : Exception, ISerializable ``` | Property | Type | Description | | ------------- | ----------------- | ----------- | | `OperationId` | `String` | | | `Status` | `Nullable` | | ## CamundaUserResult ```csharp public sealed class CamundaUserResult ``` | Property | Type | Description | | ---------------------- | -------------------- | ------------------------------------------------------------- | | `Username` | `Username` | The username of the user. | | `DisplayName` | `String` | The display name of the user. | | `Email` | `String` | The email of the user. | | `AuthorizedComponents` | `List` | The web components the user is authorized to use. | | `Tenants` | `List` | The tenants the user is a member of. | | `Groups` | `List` | The groups assigned to the user. | | `Roles` | `List` | The roles assigned to the user. | | `SalesPlanType` | `String` | The plan of the user. | | `C8Links` | `Dictionary` | The links to the components in the C8 stack. | | `CanLogout` | `Boolean` | Flag for understanding if the user is able to perform logout. | ## CancelProcessInstanceRequest ```csharp public sealed class CancelProcessInstanceRequest ``` | Property | Type | Description | | -------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | | `OperationReference` | `Nullable` | A reference key chosen by the user that will be part of all records resulting from this operation. Must be > 0 if provided. | ## CancelSdkException Thrown when a cancellable operation is cancelled. ```csharp public sealed class CancelSdkException : CamundaSdkException, ISerializable ``` ## CategoryExactMatch Matches the value exactly. ```csharp public readonly record struct CategoryExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## CategoryFilterProperty AuditLogCategoryEnum property with full advanced search capabilities. ```csharp public sealed class CategoryFilterProperty ``` | Property | Type | Description | | -------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## Changeset JSON object with changed task attribute values. The following attributes can be adjusted with this endpoint, additional attributes will be ignored: - `candidateGroups` - reset by providing an empty list - `candidateUsers` - reset by providing an empty list - `dueDate` - reset by providing an empty String - `followUpDate` - reset by providing an empty String - `priority` - minimum 0, maximum 100, default 50 Providing any of those attributes with a `null` value or omitting it preserves the persisted attribute's value. The assignee cannot be adjusted with this endpoint, use the Assign task endpoint. This ensures correct event emission for assignee changes. ```csharp public sealed class Changeset ``` | Property | Type | Description | | ----------------- | -------------------------- | --------------------------------------------------------------------------- | | `DueDate` | `Nullable` | The due date of the task. Reset by providing an empty String. | | `FollowUpDate` | `Nullable` | The follow-up date of the task. Reset by providing an empty String. | | `CandidateUsers` | `List` | The list of candidate users of the task. Reset by providing an empty list. | | `CandidateGroups` | `List` | The list of candidate groups of the task. Reset by providing an empty list. | | `Priority` | `Nullable` | The priority of the task. | ## ClientId The unique identifier of an OAuth client. Minted outside the Camunda REST API: in SaaS by Console, in Self-Managed with OIDC by the external identity provider (e.g. EntraID, Keycloak, Okta). In Self-Managed with Basic authentication, machine-to-machine applications are modelled as users instead — see the user identifier. ```csharp public readonly record struct ClientId : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## ClockPinRequest ```csharp public sealed class ClockPinRequest ``` | Property | Type | Description | | ----------- | ------- | ------------------------------------------------------------------------- | | `Timestamp` | `Int64` | The exact time in epoch milliseconds to which the clock should be pinned. | ## CloudConfigurationResponse Configuration for SaaS/cloud-specific settings. ```csharp public sealed class CloudConfigurationResponse ``` | Property | Type | Description | | ----------------- | ---------------------- | ---------------------------------------------- | | `OrganizationId` | `String` | The SaaS organization ID, if applicable. | | `ClusterId` | `String` | The SaaS cluster ID, if applicable. | | `Stage` | `Nullable` | The cloud deployment stage. | | `MixpanelToken` | `String` | The Mixpanel analytics token for the cloud UI. | | `MixpanelAPIHost` | `String` | The Mixpanel API host URL. | ## ClusterVariableName The name of a cluster variable. Unique within its scope (global or tenant-specific). ```csharp public readonly record struct ClusterVariableName : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## ClusterVariableResult ```csharp public sealed class ClusterVariableResult ``` | Property | Type | Description | | ---------- | -------------------------- | --------------------------------------------------------------------------------------- | | `Value` | `String` | Full value of this cluster variable. | | `Name` | `ClusterVariableName` | The name of the cluster variable. Unique within its scope (global or tenant-specific). | | `Scope` | `ClusterVariableScopeEnum` | The scope of a cluster variable. | | `TenantId` | `String` | Only provided if the cluster variable scope is TENANT. Null for global scope variables. | ## ClusterVariableResultBase Cluster variable response item. ```csharp public sealed class ClusterVariableResultBase ``` | Property | Type | Description | | ---------- | -------------------------- | --------------------------------------------------------------------------------------- | | `Name` | `ClusterVariableName` | The name of the cluster variable. Unique within its scope (global or tenant-specific). | | `Scope` | `ClusterVariableScopeEnum` | The scope of a cluster variable. | | `TenantId` | `String` | Only provided if the cluster variable scope is TENANT. Null for global scope variables. | ## ClusterVariableScopeExactMatch Matches the value exactly. ```csharp public readonly record struct ClusterVariableScopeExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## ClusterVariableScopeFilterProperty ClusterVariableScopeEnum property with full advanced search capabilities. ```csharp public sealed class ClusterVariableScopeFilterProperty ``` | Property | Type | Description | | -------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## ClusterVariableSearchQueryFilterRequest Cluster variable filter request. ```csharp public sealed class ClusterVariableSearchQueryFilterRequest ``` | Property | Type | Description | | ------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `Name` | `StringFilterProperty` | Name of the cluster variable. | | `Value` | `StringFilterProperty` | The value of the cluster variable. | | `Scope` | `ClusterVariableScopeFilterProperty` | The scope filter for cluster variables. | | `TenantId` | `StringFilterProperty` | Tenant ID of this variable. | | `IsTruncated` | `Nullable` | Filter cluster variables by truncation status of their stored values. When true, returns only variables whose stored values are truncated (i.e., the value exceeds the storage size limit and is truncated in storage). When false, returns only variables with non-truncated stored values. This filter is based on the underlying storage characteristic, not the response format. | ## ClusterVariableSearchQueryRequest Cluster variable search query request. ```csharp public sealed class ClusterVariableSearchQueryRequest ``` | Property | Type | Description | | -------- | --------------------------------------------- | ------------------------------------ | | `Sort` | `List` | Sort field criteria. | | `Filter` | `ClusterVariableSearchQueryFilterRequest` | The cluster variable search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## ClusterVariableSearchQueryResult Cluster variable search query response. ```csharp public sealed class ClusterVariableSearchQueryResult ``` | Property | Type | Description | | -------- | ----------------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching cluster variables. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## ClusterVariableSearchQuerySortRequest ```csharp public sealed class ClusterVariableSearchQuerySortRequest ``` | Property | Type | Description | | -------- | -------------------------------------------- | --------------------------------------------- | | `Field` | `ClusterVariableSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## ClusterVariableSearchResult Cluster variable search response item. ```csharp public sealed class ClusterVariableSearchResult ``` | Property | Type | Description | | ------------- | -------------------------- | --------------------------------------------------------------------------------------- | | `Value` | `String` | Value of this cluster variable. Can be truncated. | | `IsTruncated` | `Boolean` | Whether the value is truncated or not. | | `Name` | `ClusterVariableName` | The name of the cluster variable. Unique within its scope (global or tenant-specific). | | `Scope` | `ClusterVariableScopeEnum` | The scope of a cluster variable. | | `TenantId` | `String` | Only provided if the cluster variable scope is TENANT. Null for global scope variables. | ## ComponentsConfigurationResponse Configuration for active Camunda components in the deployment. ```csharp public sealed class ComponentsConfigurationResponse ``` | Property | Type | Description | | -------- | ----------------------- | ----------------------------------------------------------------- | | `Active` | `List` | List of webapp components whose UI is enabled in this deployment. | ## ConditionalEvaluationInstruction ```csharp public sealed class ConditionalEvaluationInstruction : ITenantIdSettable ``` | Property | Type | Description | | ---------------------- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `TenantId` | `Nullable` | Used to evaluate root-level conditional start events for a tenant with the given ID. This will only evaluate root-level conditional start events of process definitions which belong to the tenant. | | `ProcessDefinitionKey` | `Nullable` | Used to evaluate root-level conditional start events of the process definition with the given key. | | `Variables` | `Object` | JSON object representing the variables to use for evaluation of the conditions and to pass to the process instances that have been triggered. | ## ConsistencyOptions Options for eventual consistency polling behavior. ```csharp public sealed class ConsistencyOptions ``` | Property | Type | Description | | ---------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | `WaitUpToMs` | `Int32` | Maximum time to wait for the data to become consistent, in milliseconds. Set to 0 to skip eventual consistency handling. | | `PollIntervalMs` | `Int32` | Poll interval in milliseconds (default: 500). | | `IsConsistent` | `Boolean}` | Optional predicate: when true, the response is considered consistent. If not set, any non-null response with items (where applicable) is accepted. | ## CorrelatedMessageSubscriptionFilter Correlated message subscriptions search filter. ```csharp public sealed class CorrelatedMessageSubscriptionFilter ``` | Property | Type | Description | | ---------------------- | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `CorrelationKey` | `StringFilterProperty` | The correlation key of the message. | | `CorrelationTime` | `DateTimeFilterProperty` | The time when the message was correlated. | | `ElementId` | `StringFilterProperty` | The element ID that received the message. | | `ElementInstanceKey` | `ElementInstanceKeyFilterProperty` | The element instance key that received the message. | | `MessageKey` | `BasicStringFilterProperty` | The message key. | | `MessageName` | `StringFilterProperty` | The name of the message. | | `PartitionId` | `IntegerFilterProperty` | The partition ID that correlated the message. | | `ProcessDefinitionId` | `StringFilterProperty` | The process definition ID associated with this correlated message subscription. | | `ProcessDefinitionKey` | `ProcessDefinitionKeyFilterProperty` | The process definition key associated with this correlated message subscription. For intermediate message events, this only works for data created with 8.9 and later. | | `ProcessInstanceKey` | `ProcessInstanceKeyFilterProperty` | The process instance key associated with this correlated message subscription. | | `SubscriptionKey` | `MessageSubscriptionKeyFilterProperty` | The subscription key that received the message. | | `TenantId` | `StringFilterProperty` | The tenant ID associated with this correlated message subscription. | ## CorrelatedMessageSubscriptionResult ```csharp public sealed class CorrelatedMessageSubscriptionResult ``` | Property | Type | Description | | ------------------------ | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `CorrelationKey` | `String` | The correlation key of the message. | | `CorrelationTime` | `DateTimeOffset` | The time when the message was correlated. | | `ElementId` | `String` | The element ID that received the message. | | `ElementInstanceKey` | `Nullable` | The element instance key that received the message. It is `null` for start event subscriptions. | | `MessageKey` | `MessageKey` | The message key. | | `MessageName` | `String` | The name of the message. | | `PartitionId` | `Int32` | The partition ID that correlated the message. | | `ProcessDefinitionId` | `ProcessDefinitionId` | The process definition ID associated with this correlated message subscription. | | `ProcessDefinitionKey` | `ProcessDefinitionKey` | The process definition key associated with this correlated message subscription. | | `ProcessInstanceKey` | `ProcessInstanceKey` | The process instance key associated with this correlated message subscription. | | `RootProcessInstanceKey` | `Nullable` | The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. | | `SubscriptionKey` | `MessageSubscriptionKey` | The subscription key that received the message. | | `TenantId` | `TenantId` | The tenant ID associated with this correlated message subscription. | ## CorrelatedMessageSubscriptionSearchQuery ```csharp public sealed class CorrelatedMessageSubscriptionSearchQuery ``` | Property | Type | Description | | -------- | ----------------------------------------------------------- | ---------------------------------------------------- | | `Sort` | `List` | Sort field criteria. | | `Filter` | `CorrelatedMessageSubscriptionFilter` | The correlated message subscriptions search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## CorrelatedMessageSubscriptionSearchQueryResult ```csharp public sealed class CorrelatedMessageSubscriptionSearchQueryResult ``` | Property | Type | Description | | -------- | ------------------------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching correlated message subscriptions. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## CorrelatedMessageSubscriptionSearchQuerySortRequest ```csharp public sealed class CorrelatedMessageSubscriptionSearchQuerySortRequest ``` | Property | Type | Description | | -------- | ---------------------------------------------------------- | --------------------------------------------- | | `Field` | `CorrelatedMessageSubscriptionSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## CreateClusterVariableRequest ```csharp public sealed class CreateClusterVariableRequest ``` | Property | Type | Description | | -------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | `Name` | `ClusterVariableName` | The name of the cluster variable. Must be unique within its scope (global or tenant-specific). | | `Value` | `Object` | The value of the cluster variable. Can be any JSON object or primitive value. Will be serialized as a JSON string in responses. | ## CreateGlobalTaskListenerRequest ```csharp public sealed class CreateGlobalTaskListenerRequest ``` | Property | Type | Description | | ------------ | --------------------------------------- | -------------------------------------------------------- | | `Id` | `GlobalListenerId` | The user-defined id for the global listener | | `EventTypes` | `List` | List of user task event types that trigger the listener. | ## CreateProcessInstanceResult ```csharp public sealed class CreateProcessInstanceResult ``` | Property | Type | Description | | -------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | `ProcessDefinitionId` | `ProcessDefinitionId` | The BPMN process id of the process definition which was used to create the process. instance | | `ProcessDefinitionVersion` | `Int32` | The version of the process definition which was used to create the process instance. | | `TenantId` | `TenantId` | The tenant id of the created process instance. | | `Variables` | `Object` | All the variables visible in the root scope. | | `ProcessDefinitionKey` | `ProcessDefinitionKey` | The key of the process definition which was used to create the process instance. | | `ProcessInstanceKey` | `ProcessInstanceKey` | The unique identifier of the created process instance; to be used wherever a request needs a process instance key (e.g. CancelProcessInstanceRequest). | | `Tags` | `List` | List of tags. Tags need to start with a letter; then alphanumerics, `_`, `-`, `:`, or `.`; length ≤ 100. | | `BusinessId` | `Nullable` | Business id as provided on creation. | ## CursorBackwardPagination ```csharp public sealed class CursorBackwardPagination : SearchQueryPageRequest ``` | Property | Type | Description | | -------- | ----------------- | --------------------------------------------------------------------------------------------- | | `Before` | `StartCursor` | Use the `startCursor` value from the previous response to fetch the previous page of results. | | `Limit` | `Nullable` | The maximum number of items to return in one request. | ## CursorForwardPagination ```csharp public sealed class CursorForwardPagination : SearchQueryPageRequest ``` | Property | Type | Description | | -------- | ----------------- | --------------------------------------------------------------------------------------- | | `After` | `EndCursor` | Use the `endCursor` value from the previous response to fetch the next page of results. | | `Limit` | `Nullable` | The maximum number of items to return in one request. | ## DateTimeFilterProperty Date-time property with full advanced search capabilities. ```csharp public sealed class DateTimeFilterProperty ``` | Property | Type | Description | | -------- | -------------------------- | ---------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `Gt` | `Nullable` | Greater than comparison with the provided value. | | `Gte` | `Nullable` | Greater than or equal comparison with the provided value. | | `Lt` | `Nullable` | Lower than comparison with the provided value. | | `Lte` | `Nullable` | Lower than or equal comparison with the provided value. | | `In` | `List` | Checks if the property matches any of the provided values. | ## DecisionDefinitionFilter Decision definition search filter. ```csharp public sealed class DecisionDefinitionFilter ``` | Property | Type | Description | | ----------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `DecisionDefinitionId` | `Nullable` | The DMN ID of the decision definition. | | `Name` | `String` | The DMN name of the decision definition. | | `IsLatestVersion` | `Nullable` | Whether to only return the latest version of each decision definition. When using this filter, pagination functionality is limited, you can only paginate forward using `after` and `limit`. The response contains no `startCursor` in the `page`, and requests ignore the `from` and `before` in the `page`. | | `Version` | `Nullable` | The assigned version of the decision definition. | | `DecisionRequirementsId` | `String` | the DMN ID of the decision requirements graph that the decision definition is part of. | | `TenantId` | `Nullable` | The tenant ID of the decision definition. | | `DecisionDefinitionKey` | `Nullable` | The assigned key, which acts as a unique identifier for this decision definition. | | `DecisionRequirementsKey` | `Nullable` | The assigned key of the decision requirements graph that the decision definition is part of. | | `DecisionRequirementsName` | `String` | The DMN name of the decision requirements that the decision definition is part of. | | `DecisionRequirementsVersion` | `Nullable` | The assigned version of the decision requirements that the decision definition is part of. | ## DecisionDefinitionId Id of a decision definition, from the model. Only ids of decision definitions that are deployed are useful. ```csharp public readonly record struct DecisionDefinitionId : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## DecisionDefinitionKeyExactMatch Matches the value exactly. ```csharp public readonly record struct DecisionDefinitionKeyExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## DecisionDefinitionKeyFilterProperty DecisionDefinitionKey property with full advanced search capabilities. ```csharp public sealed class DecisionDefinitionKeyFilterProperty ``` | Property | Type | Description | | -------- | --------------------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## DecisionDefinitionResult ```csharp public sealed class DecisionDefinitionResult ``` | Property | Type | Description | | ----------------------------- | ------------------------- | -------------------------------------------------------------------------------------------- | | `DecisionDefinitionId` | `DecisionDefinitionId` | The DMN ID of the decision definition. | | `DecisionDefinitionKey` | `DecisionDefinitionKey` | The assigned key, which acts as a unique identifier for this decision definition. | | `DecisionRequirementsId` | `String` | the DMN ID of the decision requirements graph that the decision definition is part of. | | `DecisionRequirementsKey` | `DecisionRequirementsKey` | The assigned key of the decision requirements graph that the decision definition is part of. | | `DecisionRequirementsName` | `String` | The DMN name of the decision requirements that the decision definition is part of. | | `DecisionRequirementsVersion` | `Int32` | The assigned version of the decision requirements that the decision definition is part of. | | `Name` | `String` | The DMN name of the decision definition. | | `TenantId` | `TenantId` | The tenant ID of the decision definition. | | `Version` | `Int32` | The assigned version of the decision definition. | ## DecisionDefinitionSearchQuery ```csharp public sealed class DecisionDefinitionSearchQuery ``` | Property | Type | Description | | -------- | ------------------------------------------------ | --------------------------------------- | | `Sort` | `List` | Sort field criteria. | | `Filter` | `DecisionDefinitionFilter` | The decision definition search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## DecisionDefinitionSearchQueryResult ```csharp public sealed class DecisionDefinitionSearchQueryResult ``` | Property | Type | Description | | -------- | -------------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching decision definitions. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## DecisionDefinitionSearchQuerySortRequest ```csharp public sealed class DecisionDefinitionSearchQuerySortRequest ``` | Property | Type | Description | | -------- | ----------------------------------------------- | --------------------------------------------- | | `Field` | `DecisionDefinitionSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## DecisionEvaluationById ```csharp public sealed class DecisionEvaluationById : DecisionEvaluationInstruction, ITenantIdSettable ``` | Property | Type | Description | | ---------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------ | | `DecisionDefinitionId` | `DecisionDefinitionId` | The ID of the decision to be evaluated. When using the decision ID, the latest deployed version of the decision is used. | | `Variables` | `Object` | The decision evaluation variables as JSON document. | | `TenantId` | `Nullable` | The tenant ID of the decision. | ## DecisionEvaluationByKey ```csharp public sealed class DecisionEvaluationByKey : DecisionEvaluationInstruction, ITenantIdSettable ``` | Property | Type | Description | | ----------------------- | ----------------------- | --------------------------------------------------- | | `DecisionDefinitionKey` | `DecisionDefinitionKey` | System-generated key for a decision definition. | | `Variables` | `Object` | The decision evaluation variables as JSON document. | | `TenantId` | `Nullable` | The tenant ID of the decision. | ## DecisionEvaluationInstanceKeyExactMatch Matches the value exactly. ```csharp public readonly record struct DecisionEvaluationInstanceKeyExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## DecisionEvaluationInstanceKeyFilterProperty DecisionEvaluationInstanceKey property with full advanced search capabilities. ```csharp public sealed class DecisionEvaluationInstanceKeyFilterProperty ``` | Property | Type | Description | | -------- | ----------------------------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## DecisionEvaluationInstruction ```csharp public abstract class DecisionEvaluationInstruction ``` ## DecisionEvaluationKeyExactMatch Matches the value exactly. ```csharp public readonly record struct DecisionEvaluationKeyExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## DecisionEvaluationKeyFilterProperty DecisionEvaluationKey property with full advanced search capabilities. ```csharp public sealed class DecisionEvaluationKeyFilterProperty ``` | Property | Type | Description | | -------- | --------------------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## DecisionInstanceDeletionBatchOperationRequest The decision instance filter that defines which decision instances should be deleted. ```csharp public sealed class DecisionInstanceDeletionBatchOperationRequest ``` | Property | Type | Description | | -------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | | `Filter` | `DecisionInstanceFilter` | The decision instance filter. | | `OperationReference` | `Nullable` | A reference key chosen by the user that will be part of all records resulting from this operation. Must be > 0 if provided. | ## DecisionInstanceFilter Decision instance search filter. ```csharp public sealed class DecisionInstanceFilter ``` | Property | Type | Description | | ------------------------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `DecisionEvaluationInstanceKey` | `DecisionEvaluationInstanceKeyFilterProperty` | The key of the decision evaluation instance. | | `State` | `DecisionInstanceStateFilterProperty` | The state of the decision instance. | | `EvaluationFailure` | `String` | The evaluation failure of the decision instance. | | `EvaluationDate` | `DateTimeFilterProperty` | The evaluation date of the decision instance. | | `DecisionDefinitionId` | `Nullable` | The ID of the DMN decision. | | `DecisionDefinitionName` | `String` | The name of the DMN decision. | | `DecisionDefinitionVersion` | `Nullable` | The version of the decision. | | `DecisionDefinitionType` | `Nullable` | The type of the decision. UNSPECIFIED is deprecated and should not be used anymore, for removal in 8.10 | | `TenantId` | `Nullable` | The tenant ID of the decision instance. | | `DecisionEvaluationKey` | `Nullable` | The key of the parent decision evaluation. Note that this is not the identifier of an individual decision instance; the `decisionEvaluationInstanceKey` is the identifier for a decision instance. | | `ProcessDefinitionKey` | `Nullable` | The key of the process definition. | | `ProcessInstanceKey` | `Nullable` | The key of the process instance. | | `DecisionDefinitionKey` | `DecisionDefinitionKeyFilterProperty` | The key of the decision. | | `ElementInstanceKey` | `ElementInstanceKeyFilterProperty` | The key of the element instance this decision instance is linked to. | | `RootDecisionDefinitionKey` | `DecisionDefinitionKeyFilterProperty` | The key of the root decision definition. | | `DecisionRequirementsKey` | `DecisionRequirementsKeyFilterProperty` | The key of the decision requirements definition. | ## DecisionInstanceGetQueryResult ```csharp public sealed class DecisionInstanceGetQueryResult ``` | Property | Type | Description | | ------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `DecisionDefinitionId` | `DecisionDefinitionId` | The ID of the DMN decision. | | `DecisionDefinitionKey` | `DecisionDefinitionKey` | The key of the decision. | | `DecisionDefinitionName` | `String` | The name of the DMN decision. | | `DecisionDefinitionType` | `DecisionDefinitionTypeEnum` | The type of the decision. UNSPECIFIED is deprecated and should not be used anymore, for removal in 8.10 | | `DecisionDefinitionVersion` | `Int32` | The version of the decision. | | `DecisionEvaluationInstanceKey` | `DecisionEvaluationInstanceKey` | System-generated identifier for a decision evaluation instance. It is composed of the parent decision evaluation key and the 1-based index of the evaluated decision within that evaluation, joined by a hyphen (format: `-`). | | `DecisionEvaluationKey` | `DecisionEvaluationKey` | The key of the decision evaluation where this instance was created. | | `ElementInstanceKey` | `Nullable` | The key of the element instance this decision instance is linked to. | | `EvaluationDate` | `DateTimeOffset` | The evaluation date of the decision instance. | | `EvaluationFailure` | `String` | The evaluation failure of the decision instance. | | `ProcessDefinitionKey` | `Nullable` | The key of the process definition. | | `ProcessInstanceKey` | `Nullable` | The key of the process instance. | | `Result` | `String` | The result of the decision instance. | | `RootDecisionDefinitionKey` | `DecisionDefinitionKey` | The key of the root decision definition. | | `RootProcessInstanceKey` | `Nullable` | The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. | | `State` | `DecisionInstanceStateEnum` | The state of the decision instance. UNSPECIFIED and UNKNOWN are deprecated and should not be used anymore, for removal in 8.10 | | `TenantId` | `TenantId` | The tenant ID of the decision instance. | | `EvaluatedInputs` | `List` | The evaluated inputs of the decision instance. | | `MatchedRules` | `List` | The matched rules of the decision instance. | ## DecisionInstanceResult ```csharp public sealed class DecisionInstanceResult ``` | Property | Type | Description | | ------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `DecisionDefinitionId` | `DecisionDefinitionId` | The ID of the DMN decision. | | `DecisionDefinitionKey` | `DecisionDefinitionKey` | The key of the decision. | | `DecisionDefinitionName` | `String` | The name of the DMN decision. | | `DecisionDefinitionType` | `DecisionDefinitionTypeEnum` | The type of the decision. UNSPECIFIED is deprecated and should not be used anymore, for removal in 8.10 | | `DecisionDefinitionVersion` | `Int32` | The version of the decision. | | `DecisionEvaluationInstanceKey` | `DecisionEvaluationInstanceKey` | System-generated identifier for a decision evaluation instance. It is composed of the parent decision evaluation key and the 1-based index of the evaluated decision within that evaluation, joined by a hyphen (format: `-`). | | `DecisionEvaluationKey` | `DecisionEvaluationKey` | The key of the decision evaluation where this instance was created. | | `ElementInstanceKey` | `Nullable` | The key of the element instance this decision instance is linked to. | | `EvaluationDate` | `DateTimeOffset` | The evaluation date of the decision instance. | | `EvaluationFailure` | `String` | The evaluation failure of the decision instance. | | `ProcessDefinitionKey` | `Nullable` | The key of the process definition. | | `ProcessInstanceKey` | `Nullable` | The key of the process instance. | | `Result` | `String` | The result of the decision instance. | | `RootDecisionDefinitionKey` | `DecisionDefinitionKey` | The key of the root decision definition. | | `RootProcessInstanceKey` | `Nullable` | The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. | | `State` | `DecisionInstanceStateEnum` | The state of the decision instance. UNSPECIFIED and UNKNOWN are deprecated and should not be used anymore, for removal in 8.10 | | `TenantId` | `TenantId` | The tenant ID of the decision instance. | ## DecisionInstanceSearchQuery ```csharp public sealed class DecisionInstanceSearchQuery ``` | Property | Type | Description | | -------- | ---------------------------------------------- | ------------------------------------- | | `Sort` | `List` | Sort field criteria. | | `Filter` | `DecisionInstanceFilter` | The decision instance search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## DecisionInstanceSearchQueryResult ```csharp public sealed class DecisionInstanceSearchQueryResult ``` | Property | Type | Description | | -------- | ------------------------------ | ------------------------------------------------ | | `Items` | `List` | The matching decision instances. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## DecisionInstanceSearchQuerySortRequest ```csharp public sealed class DecisionInstanceSearchQuerySortRequest ``` | Property | Type | Description | | -------- | --------------------------------------------- | --------------------------------------------- | | `Field` | `DecisionInstanceSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## DecisionInstanceStateExactMatch Matches the value exactly. ```csharp public readonly record struct DecisionInstanceStateExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## DecisionInstanceStateFilterProperty DecisionInstanceStateEnum property with full advanced search capabilities. ```csharp public sealed class DecisionInstanceStateFilterProperty ``` | Property | Type | Description | | -------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## DecisionRequirementsFilter Decision requirements search filter. ```csharp public sealed class DecisionRequirementsFilter ``` | Property | Type | Description | | -------------------------- | ----------------------------------- | ------------------------------------------------------------------------- | | `DecisionRequirementsName` | `String` | The DMN name of the decision requirements. | | `DecisionRequirementsId` | `String` | the DMN ID of the decision requirements. | | `DecisionRequirementsKey` | `Nullable` | System-generated key for a deployed decision requirements definition. | | `Version` | `Nullable` | The assigned version of the decision requirements. | | `TenantId` | `Nullable` | The tenant ID of the decision requirements. | | `ResourceName` | `String` | The name of the resource from which the decision requirements were parsed | ## DecisionRequirementsKeyExactMatch Matches the value exactly. ```csharp public readonly record struct DecisionRequirementsKeyExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## DecisionRequirementsKeyFilterProperty DecisionRequirementsKey property with full advanced search capabilities. ```csharp public sealed class DecisionRequirementsKeyFilterProperty ``` | Property | Type | Description | | -------- | ----------------------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## DecisionRequirementsResult ```csharp public sealed class DecisionRequirementsResult ``` | Property | Type | Description | | -------------------------- | ------------------------- | ----------------------------------------------------------------------------------- | | `DecisionRequirementsId` | `String` | The DMN ID of the decision requirements. | | `DecisionRequirementsKey` | `DecisionRequirementsKey` | The assigned key, which acts as a unique identifier for this decision requirements. | | `DecisionRequirementsName` | `String` | The DMN name of the decision requirements. | | `ResourceName` | `String` | The name of the resource from which this decision requirements was parsed. | | `TenantId` | `TenantId` | The tenant ID of the decision requirements. | | `Version` | `Int32` | The assigned version of the decision requirements. | ## DecisionRequirementsSearchQuery ```csharp public sealed class DecisionRequirementsSearchQuery ``` | Property | Type | Description | | -------- | -------------------------------------------------- | --------------------------------------- | | `Sort` | `List` | Sort field criteria. | | `Filter` | `DecisionRequirementsFilter` | The decision definition search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## DecisionRequirementsSearchQueryResult ```csharp public sealed class DecisionRequirementsSearchQueryResult ``` | Property | Type | Description | | -------- | ---------------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching decision requirements. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## DecisionRequirementsSearchQuerySortRequest ```csharp public sealed class DecisionRequirementsSearchQuerySortRequest ``` | Property | Type | Description | | -------- | ------------------------------------------------- | --------------------------------------------- | | `Field` | `DecisionRequirementsSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## DeleteDecisionInstanceRequest ```csharp public sealed class DeleteDecisionInstanceRequest ``` | Property | Type | Description | | -------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | | `OperationReference` | `Nullable` | A reference key chosen by the user that will be part of all records resulting from this operation. Must be > 0 if provided. | ## DeleteProcessInstanceRequest ```csharp public sealed class DeleteProcessInstanceRequest ``` | Property | Type | Description | | -------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | | `OperationReference` | `Nullable` | A reference key chosen by the user that will be part of all records resulting from this operation. Must be > 0 if provided. | ## DeleteResourceRequest ```csharp public sealed class DeleteResourceRequest ``` | Property | Type | Description | | -------------------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `OperationReference` | `Nullable` | A reference key chosen by the user that will be part of all records resulting from this operation. Must be > 0 if provided. | | `DeleteHistory` | `Nullable` | Indicates if the historic data of a process resource should be deleted via a batch operation asynchronously. This flag is only effective for process resources. For other resource types (decisions, forms, generic resources), this flag is ignored and no history will be deleted. In those cases, the `batchOperation` field in the response will not be populated. | ## DeleteResourceResponse ```csharp public sealed class DeleteResourceResponse ``` | Property | Type | Description | | ---------------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ResourceKey` | `ResourceKey` | The system-assigned key for this resource, requested to be deleted. | | `BatchOperation` | `BatchOperationCreatedResult` | The batch operation created for asynchronously deleting the historic data. This field is only populated when the request `deleteHistory` is set to `true` and the resource is a process definition. For other resource types (decisions, forms, generic resources), this field will be `null`. | ## DeploymentConfigurationResponse Configuration for deployment characteristics. ```csharp public sealed class DeploymentConfigurationResponse ``` | Property | Type | Description | | ----------------------- | --------- | -------------------------------------------- | | `IsEnterprise` | `Boolean` | Whether this is an enterprise deployment. | | `IsMultiTenancyEnabled` | `Boolean` | Whether multi-tenancy is enabled. | | `ContextPath` | `String` | The servlet context path for the deployment. | | `MaxRequestSize` | `Int64` | The maximum HTTP request size in bytes. | ## DeploymentDecisionRequirementsResult Deployed decision requirements. ```csharp public sealed class DeploymentDecisionRequirementsResult ``` | Property | Type | Description | | -------------------------- | ------------------------- | --------------------------------------------------------------------------------------------------------- | | `DecisionRequirementsId` | `String` | The id of the deployed decision requirements. | | `DecisionRequirementsName` | `String` | The name of the deployed decision requirements. | | `Version` | `Int32` | The version of the deployed decision requirements. | | `ResourceName` | `String` | The name of the resource. | | `TenantId` | `TenantId` | The tenant ID of the deployed decision requirements. | | `DecisionRequirementsKey` | `DecisionRequirementsKey` | The assigned decision requirements key, which acts as a unique identifier for this decision requirements. | ## DeploymentDecisionResult A deployed decision. ```csharp public sealed class DeploymentDecisionResult ``` | Property | Type | Description | | ------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | | `DecisionDefinitionId` | `DecisionDefinitionId` | The dmn decision ID, as parsed during deployment, together with the version forms a unique identifier for a specific decision. | | `Version` | `Int32` | The assigned decision version. | | `Name` | `String` | The DMN name of the decision, as parsed during deployment. | | `TenantId` | `TenantId` | The tenant ID of the deployed decision. | | `DecisionRequirementsId` | `String` | The dmn ID of the decision requirements graph that this decision is part of, as parsed during deployment. | | `DecisionDefinitionKey` | `DecisionDefinitionKey` | The assigned decision key, which acts as a unique identifier for this decision. | | `DecisionRequirementsKey` | `DecisionRequirementsKey` | The assigned key of the decision requirements graph that this decision is part of. | ## DeploymentFormResult A deployed form. ```csharp public sealed class DeploymentFormResult ``` | Property | Type | Description | | -------------- | ---------- | ------------------------------------------------------------------------------------------------------------------ | | `FormId` | `FormId` | The form ID, as parsed during deployment, together with the version forms a unique identifier for a specific form. | | `Version` | `Int32` | The version of the deployed form. | | `ResourceName` | `String` | The name of the resource. | | `TenantId` | `TenantId` | The unique identifier of the tenant. | | `FormKey` | `FormKey` | The assigned key, which acts as a unique identifier for this form. | ## DeploymentKeyExactMatch Matches the value exactly. ```csharp public readonly record struct DeploymentKeyExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## DeploymentKeyFilterProperty DeploymentKey property with full advanced search capabilities. ```csharp public sealed class DeploymentKeyFilterProperty ``` | Property | Type | Description | | -------- | ------------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## DeploymentMetadataResult ```csharp public sealed class DeploymentMetadataResult ``` | Property | Type | Description | | ---------------------- | -------------------------------------- | ----------------------------------------- | | `ProcessDefinition` | `DeploymentProcessResult` | Deployed process. | | `DecisionDefinition` | `DeploymentDecisionResult` | Deployed decision. | | `DecisionRequirements` | `DeploymentDecisionRequirementsResult` | Deployed decision requirement definition. | | `Form` | `DeploymentFormResult` | Deployed form. | | `Resource` | `DeploymentResourceResult` | Deployed resource. | ## DeploymentProcessResult A deployed process. ```csharp public sealed class DeploymentProcessResult ``` | Property | Type | Description | | -------------------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | `ProcessDefinitionId` | `ProcessDefinitionId` | The bpmn process ID, as parsed during deployment, together with the version forms a unique identifier for a specific process definition. | | `ProcessDefinitionVersion` | `Int32` | The assigned process version. | | `ResourceName` | `String` | The resource name from which this process was parsed. | | `TenantId` | `TenantId` | The tenant ID of the deployed process. | | `ProcessDefinitionKey` | `ProcessDefinitionKey` | The assigned key, which acts as a unique identifier for this process. | ## DeploymentResourceResult A deployed Resource. ```csharp public sealed class DeploymentResourceResult ``` | Property | Type | Description | | -------------- | ------------- | ---------------------------------------------------------------------- | | `ResourceId` | `String` | The resource id of the deployed resource. | | `ResourceName` | `String` | The name of the deployed resource. | | `Version` | `Int32` | The description of the deployed resource. | | `TenantId` | `TenantId` | The unique identifier of the tenant. | | `ResourceKey` | `ResourceKey` | The assigned key, which acts as a unique identifier for this Resource. | ## DeploymentResult ```csharp public sealed class DeploymentResult ``` | Property | Type | Description | | --------------- | -------------------------------- | --------------------------------------------- | | `DeploymentKey` | `DeploymentKey` | The unique key identifying the deployment. | | `TenantId` | `TenantId` | The tenant ID associated with the deployment. | | `Deployments` | `List` | Items deployed by the request. | ## DirectAncestorKeyInstruction Provides a concrete key to use as ancestor scope for the created element instance. ```csharp public sealed class DirectAncestorKeyInstruction : AncestorScopeInstruction ``` | Property | Type | Description | | ---------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `AncestorElementInstanceKey` | `Object` | The key of the ancestor scope the element instance should be created in. Set to -1 to create the new element instance within an existing element instance of the flow scope. If multiple instances of the target element's flow scope exist, choose one specifically with this property by providing its key. | ## DocumentCreationBatchResponse ```csharp public sealed class DocumentCreationBatchResponse ``` | Property | Type | Description | | ------------------ | ------------------------------------- | ----------------------------------------- | | `FailedDocuments` | `List` | Documents that were successfully created. | | `CreatedDocuments` | `List` | Documents that failed creation. | ## DocumentCreationFailureDetail ```csharp public sealed class DocumentCreationFailureDetail ``` | Property | Type | Description | | ---------- | -------- | ------------------------------------------------------------------------ | | `FileName` | `String` | The name of the file that failed to upload. | | `Status` | `Int32` | The HTTP status code of the failure. | | `Title` | `String` | A short, human-readable summary of the problem type. | | `Detail` | `String` | A human-readable explanation specific to this occurrence of the problem. | ## DocumentId Document Id that uniquely identifies a document. ```csharp public readonly record struct DocumentId : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## DocumentLink ```csharp public sealed class DocumentLink ``` | Property | Type | Description | | ----------- | ---------------- | ---------------------------------------- | | `Url` | `String` | The link to the document. | | `ExpiresAt` | `DateTimeOffset` | The date and time when the link expires. | ## DocumentLinkRequest ```csharp public sealed class DocumentLinkRequest ``` | Property | Type | Description | | ------------ | ----------------- | -------------------------------------------- | | `TimeToLive` | `Nullable` | The time-to-live of the document link in ms. | ## DocumentMetadata Information about the document. ```csharp public sealed class DocumentMetadata ``` | Property | Type | Description | | --------------------- | ------------------------------- | ----------------------------------------------------------- | | `ContentType` | `String` | The content type of the document. | | `FileName` | `String` | The name of the file. | | `ExpiresAt` | `Nullable` | The date and time when the document expires. | | `Size` | `Nullable` | The size of the document in bytes. | | `ProcessDefinitionId` | `Nullable` | The ID of the process definition that created the document. | | `ProcessInstanceKey` | `Nullable` | The key of the process instance that created the document. | | `CustomProperties` | `Object` | Custom properties of the document. | ## DocumentMetadataResponse Information about the document that is returned in responses. ```csharp public sealed class DocumentMetadataResponse ``` | Property | Type | Description | | --------------------- | ------------------------------- | ----------------------------------------------------------- | | `ContentType` | `String` | The content type of the document. | | `FileName` | `String` | The name of the file. | | `ExpiresAt` | `Nullable` | The date and time when the document expires. | | `Size` | `Int64` | The size of the document in bytes. | | `ProcessDefinitionId` | `Nullable` | The ID of the process definition that created the document. | | `ProcessInstanceKey` | `Nullable` | The key of the process instance that created the document. | | `CustomProperties` | `Object` | Custom properties of the document. | ## DocumentReference ```csharp public sealed class DocumentReference ``` | Property | Type | Description | | --------------------- | -------------------------------------- | ------------------------------------------------------------- | | `CamundaDocumentType` | `DocumentReferenceCamundaDocumentType` | Document discriminator. Always set to "camunda". | | `StoreId` | `String` | The ID of the document store. | | `DocumentId` | `DocumentId` | The ID of the document. | | `ContentHash` | `String` | The hash of the document. | | `Metadata` | `DocumentMetadataResponse` | Information about the document that is returned in responses. | ## ElementId The model-defined id of an element. ```csharp public readonly record struct ElementId : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## ElementIdExactMatch Matches the value exactly. ```csharp public readonly record struct ElementIdExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## ElementIdFilterProperty ElementId property with full advanced search capabilities. ```csharp public sealed class ElementIdFilterProperty ``` | Property | Type | Description | | -------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## ElementInstanceFilter Element instance filter. ```csharp public sealed class ElementInstanceFilter ``` | Property | Type | Description | | ------------------------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ProcessDefinitionId` | `Nullable` | The process definition ID associated to this element instance. | | `State` | `ElementInstanceStateFilterProperty` | State of element instance as defined set of values. | | `Type` | `Nullable` | Type of element as defined set of values. | | `ElementId` | `ElementIdFilterProperty` | The element ID for this element instance. | | `ElementName` | `StringFilterProperty` | The element name. This only works for data created with 8.8 and onwards. Instances from prior versions don't contain this data and cannot be found. | | `HasIncident` | `Nullable` | Shows whether this element instance has an incident related to. | | `TenantId` | `Nullable` | The unique identifier of the tenant. | | `ElementInstanceKey` | `Nullable` | The assigned key, which acts as a unique identifier for this element instance. | | `ProcessInstanceKey` | `Nullable` | The process instance key associated to this element instance. | | `ProcessDefinitionKey` | `Nullable` | The process definition key associated to this element instance. | | `IncidentKey` | `Nullable` | The key of incident if field incident is true. | | `StartDate` | `DateTimeFilterProperty` | The start date of this element instance. | | `EndDate` | `DateTimeFilterProperty` | The end date of this element instance. | | `ElementInstanceScopeKey` | `String` | The scope key of this element instance. If provided with a process instance key it will return element instances that are immediate children of the process instance. If provided with an element instance key it will return element instances that are immediate children of the element instance. | ## ElementInstanceKeyExactMatch Matches the value exactly. ```csharp public readonly record struct ElementInstanceKeyExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## ElementInstanceKeyFilterProperty ElementInstanceKey property with full advanced search capabilities. ```csharp public sealed class ElementInstanceKeyFilterProperty ``` | Property | Type | Description | | -------- | ------------------------------ | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## ElementInstanceResult ```csharp public sealed class ElementInstanceResult ``` | Property | Type | Description | | ------------------------ | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ProcessDefinitionId` | `ProcessDefinitionId` | The process definition ID associated to this element instance. | | `StartDate` | `DateTimeOffset` | Date when element instance started. | | `EndDate` | `Nullable` | Date when element instance finished. | | `ElementId` | `ElementId` | The element ID for this element instance. | | `ElementName` | `String` | The element name for this element instance. | | `Type` | `ElementInstanceResultType` | Type of element as defined set of values. | | `State` | `ElementInstanceStateEnum` | State of element instance as defined set of values. | | `HasIncident` | `Boolean` | Shows whether this element instance has an incident. If true also an incidentKey is provided. | | `TenantId` | `TenantId` | The tenant ID of the incident. | | `ElementInstanceKey` | `ElementInstanceKey` | The assigned key, which acts as a unique identifier for this element instance. | | `ProcessInstanceKey` | `ProcessInstanceKey` | The process instance key associated to this element instance. | | `RootProcessInstanceKey` | `Nullable` | The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. | | `ProcessDefinitionKey` | `ProcessDefinitionKey` | The process definition key associated to this element instance. | | `IncidentKey` | `Nullable` | Incident key associated with this element instance. | ## ElementInstanceSearchQuery Element instance search request. ```csharp public sealed class ElementInstanceSearchQuery ``` | Property | Type | Description | | -------- | --------------------------------------------- | ------------------------------------ | | `Sort` | `List` | Sort field criteria. | | `Filter` | `ElementInstanceFilter` | The element instance search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## ElementInstanceSearchQueryResult ```csharp public sealed class ElementInstanceSearchQueryResult ``` | Property | Type | Description | | -------- | ----------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching element instances. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## ElementInstanceSearchQuerySortRequest ```csharp public sealed class ElementInstanceSearchQuerySortRequest ``` | Property | Type | Description | | -------- | -------------------------------------------- | --------------------------------------------- | | `Field` | `ElementInstanceSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## ElementInstanceStateExactMatch Matches the value exactly. ```csharp public readonly record struct ElementInstanceStateExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## ElementInstanceStateFilterProperty ElementInstanceStateEnum property with full advanced search capabilities. ```csharp public sealed class ElementInstanceStateFilterProperty ``` | Property | Type | Description | | -------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## EndCursor The end cursor in a search query result set. ```csharp public readonly record struct EndCursor : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## EntityTypeExactMatch Matches the value exactly. ```csharp public readonly record struct EntityTypeExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## EntityTypeFilterProperty AuditLogEntityTypeEnum property with full advanced search capabilities. ```csharp public sealed class EntityTypeFilterProperty ``` | Property | Type | Description | | -------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## EvaluateConditionalResult ```csharp public sealed class EvaluateConditionalResult ``` | Property | Type | Description | | -------------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | `ConditionalEvaluationKey` | `ConditionalEvaluationKey` | The unique key of the conditional evaluation operation. | | `TenantId` | `TenantId` | The tenant ID of the conditional evaluation operation. | | `ProcessInstances` | `List` | List of process instances created. If no root-level conditional start events evaluated to true, the list will be empty. | ## EvaluateDecisionResult ```csharp public sealed class EvaluateDecisionResult ``` | Property | Type | Description | | ---------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------ | | `DecisionDefinitionId` | `DecisionDefinitionId` | The ID of the decision which was evaluated. | | `DecisionDefinitionKey` | `DecisionDefinitionKey` | The unique key identifying the decision which was evaluated. | | `DecisionDefinitionName` | `String` | The name of the decision which was evaluated. | | `DecisionDefinitionVersion` | `Int32` | The version of the decision which was evaluated. | | `DecisionEvaluationKey` | `DecisionEvaluationKey` | The unique key identifying this decision evaluation. | | `DecisionInstanceKey` | `DecisionInstanceKey` | Deprecated, please refer to `decisionEvaluationKey`. | | `DecisionRequirementsId` | `String` | The ID of the decision requirements graph that the decision which was evaluated is part of. | | `DecisionRequirementsKey` | `DecisionRequirementsKey` | The unique key identifying the decision requirements graph that the decision which was evaluated is part of. | | `EvaluatedDecisions` | `List` | Decisions that were evaluated within the requested decision evaluation. | | `FailedDecisionDefinitionId` | `Nullable` | The ID of the decision which failed during evaluation. | | `FailureMessage` | `String` | Message describing why the decision which was evaluated failed. | | `Output` | `String` | JSON document that will instantiate the result of the decision which was evaluated. | | `TenantId` | `TenantId` | The tenant ID of the evaluated decision. | ## EvaluatedDecisionInputItem A decision input that was evaluated within this decision evaluation. ```csharp public sealed class EvaluatedDecisionInputItem ``` | Property | Type | Description | | ------------ | -------- | ------------------------------------- | | `InputId` | `String` | The identifier of the decision input. | | `InputName` | `String` | The name of the decision input. | | `InputValue` | `String` | The value of the decision input. | ## EvaluatedDecisionOutputItem The evaluated decision outputs. ```csharp public sealed class EvaluatedDecisionOutputItem ``` | Property | Type | Description | | ------------- | ----------------- | ----------------------------------------------------- | | `OutputId` | `String` | The ID of the evaluated decison output item. | | `OutputName` | `String` | The name of the of the evaluated decison output item. | | `OutputValue` | `String` | The value of the evaluated decison output item. | | `RuleId` | `String` | The ID of the matched rule. | | `RuleIndex` | `Nullable` | The index of the matched rule. | ## EvaluatedDecisionResult A decision that was evaluated. ```csharp public sealed class EvaluatedDecisionResult ``` | Property | Type | Description | | ------------------------------- | ---------------------------------- | ----------------------------------------------------------------------------------- | | `DecisionDefinitionId` | `DecisionDefinitionId` | The ID of the decision which was evaluated. | | `DecisionDefinitionName` | `String` | The name of the decision which was evaluated. | | `DecisionDefinitionVersion` | `Int32` | The version of the decision which was evaluated. | | `DecisionDefinitionType` | `String` | The type of the decision which was evaluated. | | `Output` | `String` | JSON document that will instantiate the result of the decision which was evaluated. | | `TenantId` | `TenantId` | The tenant ID of the evaluated decision. | | `MatchedRules` | `List` | The decision rules that matched within this decision evaluation. | | `EvaluatedInputs` | `List` | The decision inputs that were evaluated within this decision evaluation. | | `DecisionDefinitionKey` | `DecisionDefinitionKey` | The unique key identifying the decision which was evaluate. | | `DecisionEvaluationInstanceKey` | `DecisionEvaluationInstanceKey` | The unique key identifying this decision evaluation instance. | ## EventualConsistencyTimeoutException Thrown when an eventually consistent endpoint times out waiting for data. ```csharp public sealed class EventualConsistencyTimeoutException : CamundaSdkException, ISerializable ``` | Property | Type | Description | | ---------- | ------- | ----------- | | `WaitedMs` | `Int32` | | ## ExpressionEvaluationRequest ```csharp public sealed class ExpressionEvaluationRequest : ITenantIdSettable ``` | Property | Type | Description | | ------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------ | | `Expression` | `String` | The expression to evaluate (e.g., "=x + y") | | `TenantId` | `String` | Required when the expression references tenant-scoped cluster variables | | `Variables` | `Object` | Optional variables for expression evaluation. These variables are only used for the current evaluation and do not persist beyond it. | ## ExpressionEvaluationResult ```csharp public sealed class ExpressionEvaluationResult ``` | Property | Type | Description | | ------------ | --------------------------------------- | ------------------------------------------------------- | | `Expression` | `String` | The evaluated expression | | `Result` | `Object` | The result value. Its type can vary. | | `Warnings` | `List` | List of warnings generated during expression evaluation | ## ExpressionEvaluationWarningItem ```csharp public sealed class ExpressionEvaluationWarningItem ``` | Property | Type | Description | | --------- | -------- | ------------------- | | `Message` | `String` | The warning message | ## ExtendedDeploymentResponse Extended deployment result with typed convenience properties for direct access to deployed artifacts by category (processes, decisions, forms, etc.). ```csharp public sealed class ExtendedDeploymentResponse ``` | Property | Type | Description | | ---------------------- | -------------------------------------------- | --------------------------------------------- | | `Raw` | `DeploymentResult` | The underlying raw deployment response. | | `DeploymentKey` | `DeploymentKey` | The unique key identifying the deployment. | | `TenantId` | `TenantId` | The tenant ID associated with the deployment. | | `Deployments` | `List` | All items deployed by the request. | | `Processes` | `List` | Deployed process definitions. | | `Decisions` | `List` | Deployed decision definitions. | | `DecisionRequirements` | `List` | Deployed decision requirements. | | `Forms` | `List` | Deployed forms. | | `Resources` | `List` | Deployed resources. | ## FormId The user-defined id for the form ```csharp public readonly record struct FormId : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## FormKeyExactMatch Matches the value exactly. ```csharp public readonly record struct FormKeyExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## FormKeyFilterProperty FormKey property with full advanced search capabilities. ```csharp public sealed class FormKeyFilterProperty ``` | Property | Type | Description | | -------- | ------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## FormResult ```csharp public sealed class FormResult ``` | Property | Type | Description | | ---------- | ---------- | ------------------------------------------------------------------ | | `TenantId` | `TenantId` | The tenant ID of the form. | | `FormId` | `FormId` | The user-provided identifier of the form. | | `Schema` | `String` | The form schema as a JSON document serialized as a string. | | `Version` | `Int64` | The version of the the deployed form. | | `FormKey` | `FormKey` | The assigned key, which acts as a unique identifier for this form. | ## GlobalJobStatisticsQueryResult Global job statistics query result. ```csharp public sealed class GlobalJobStatisticsQueryResult ``` | Property | Type | Description | | -------------- | -------------- | ----------------------------------------------------------------------------------------------------- | | `Created` | `StatusMetric` | Metric for a single job status. | | `Completed` | `StatusMetric` | Metric for a single job status. | | `Failed` | `StatusMetric` | Metric for a single job status. | | `IsIncomplete` | `Boolean` | True if some data is missing because internal limits were reached and some metrics were not recorded. | ## GlobalListenerBase ```csharp public sealed class GlobalListenerBase ``` | Property | Type | Description | | ---------------- | ------------------- | --------------------------------------------------------------------------------------------------------------- | | `Type` | `String` | The name of the job type, used as a reference to specify which job workers request the respective listener job. | | `Retries` | `Nullable` | Number of retries for the listener job. | | `AfterNonGlobal` | `Nullable` | Whether the listener should run after model-level listeners. | | `Priority` | `Nullable` | The priority of the listener. Higher priority listeners are executed before lower priority ones. | ## GlobalListenerId The user-defined id for the global listener ```csharp public readonly record struct GlobalListenerId : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## GlobalListenerSourceExactMatch Matches the value exactly. ```csharp public readonly record struct GlobalListenerSourceExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## GlobalListenerSourceFilterProperty Global listener source property with full advanced search capabilities. ```csharp public sealed class GlobalListenerSourceFilterProperty ``` | Property | Type | Description | | -------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## GlobalTaskListenerBase ```csharp public sealed class GlobalTaskListenerBase ``` | Property | Type | Description | | ---------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | `EventTypes` | `List` | List of user task event types that trigger the listener. | | `Type` | `String` | The name of the job type, used as a reference to specify which job workers request the respective listener job. | | `Retries` | `Nullable` | Number of retries for the listener job. | | `AfterNonGlobal` | `Nullable` | Whether the listener should run after model-level listeners. | | `Priority` | `Nullable` | The priority of the listener. Higher priority listeners are executed before lower priority ones. | ## GlobalTaskListenerEventTypeExactMatch Matches the value exactly. ```csharp public readonly record struct GlobalTaskListenerEventTypeExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## GlobalTaskListenerEventTypeFilterProperty Global listener event type property with full advanced search capabilities. ```csharp public sealed class GlobalTaskListenerEventTypeFilterProperty ``` | Property | Type | Description | | -------- | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## GlobalTaskListenerResult ```csharp public sealed class GlobalTaskListenerResult ``` | Property | Type | Description | | ---------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | `Id` | `GlobalListenerId` | The user-defined id for the global listener | | `Source` | `GlobalListenerSourceEnum` | How the global listener was defined. | | `EventTypes` | `List` | List of user task event types that trigger the listener. | | `Type` | `String` | The name of the job type, used as a reference to specify which job workers request the respective listener job. | | `Retries` | `Int32` | Number of retries for the listener job. | | `AfterNonGlobal` | `Boolean` | Whether the listener should run after model-level listeners. | | `Priority` | `Int32` | The priority of the listener. Higher priority listeners are executed before lower priority ones. | ## GlobalTaskListenerSearchQueryFilterRequest Global listener filter request. ```csharp public sealed class GlobalTaskListenerSearchQueryFilterRequest ``` | Property | Type | Description | | ---------------- | ------------------------------------------------- | ------------------------------------------------------ | | `Id` | `StringFilterProperty` | Id of the global listener. | | `Type` | `StringFilterProperty` | Job type of the global listener. | | `Retries` | `IntegerFilterProperty` | Number of retries of the global listener. | | `EventTypes` | `List` | Event types of the global listener. | | `AfterNonGlobal` | `Nullable` | Whether the listener runs after model-level listeners. | | `Priority` | `IntegerFilterProperty` | Priority of the global listener. | | `Source` | `GlobalListenerSourceFilterProperty` | How the global listener was defined. | ## GlobalTaskListenerSearchQueryRequest Global listener search query request. ```csharp public sealed class GlobalTaskListenerSearchQueryRequest ``` | Property | Type | Description | | -------- | ------------------------------------------------ | ----------------------------------- | | `Sort` | `List` | Sort field criteria. | | `Filter` | `GlobalTaskListenerSearchQueryFilterRequest` | The global listener search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## GlobalTaskListenerSearchQueryResult Global listener search query response. ```csharp public sealed class GlobalTaskListenerSearchQueryResult ``` | Property | Type | Description | | -------- | -------------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching global listeners. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## GlobalTaskListenerSearchQuerySortRequest ```csharp public sealed class GlobalTaskListenerSearchQuerySortRequest ``` | Property | Type | Description | | -------- | ----------------------------------------------- | --------------------------------------------- | | `Field` | `GlobalTaskListenerSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## GroupClientResult ```csharp public sealed class GroupClientResult ``` | Property | Type | Description | | ---------- | ---------- | --------------------- | | `ClientId` | `ClientId` | The ID of the client. | ## GroupClientSearchQueryRequest ```csharp public sealed class GroupClientSearchQueryRequest ``` | Property | Type | Description | | -------- | ----------------------------------------- | -------------------- | | `Sort` | `List` | Sort field criteria. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## GroupClientSearchQuerySortRequest ```csharp public sealed class GroupClientSearchQuerySortRequest ``` | Property | Type | Description | | -------- | ---------------------------------------- | --------------------------------------------- | | `Field` | `GroupClientSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## GroupClientSearchResult ```csharp public sealed class GroupClientSearchResult ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching client IDs. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## GroupCreateRequest ```csharp public sealed class GroupCreateRequest ``` | Property | Type | Description | | ------------- | --------- | ---------------------------------- | | `GroupId` | `GroupId` | The ID of the new group. | | `Name` | `String` | The display name of the new group. | | `Description` | `String` | The description of the new group. | ## GroupCreateResult ```csharp public sealed class GroupCreateResult ``` | Property | Type | Description | | ------------- | --------- | -------------------------------------- | | `GroupId` | `GroupId` | The ID of the created group. | | `Name` | `String` | The display name of the created group. | | `Description` | `String` | The description of the created group. | ## GroupFilter Group filter request ```csharp public sealed class GroupFilter ``` | Property | Type | Description | | --------- | ---------------------- | ------------------------------ | | `GroupId` | `StringFilterProperty` | The group ID search filters. | | `Name` | `String` | The group name search filters. | ## GroupId The unique identifier of a group. ```csharp public readonly record struct GroupId : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## GroupMappingRuleSearchResult ```csharp public sealed class GroupMappingRuleSearchResult ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching mapping rules. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## GroupResult Group search response item. ```csharp public sealed class GroupResult ``` | Property | Type | Description | | ------------- | --------- | ---------------------- | | `Name` | `String` | The group name. | | `GroupId` | `GroupId` | The group ID. | | `Description` | `String` | The group description. | ## GroupRoleSearchResult ```csharp public sealed class GroupRoleSearchResult ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching roles. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## GroupSearchQueryRequest Group search request. ```csharp public sealed class GroupSearchQueryRequest ``` | Property | Type | Description | | -------- | ----------------------------------- | ------------------------- | | `Sort` | `List` | Sort field criteria. | | `Filter` | `GroupFilter` | The group search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## GroupSearchQueryResult Group search response. ```csharp public sealed class GroupSearchQueryResult ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching groups. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## GroupSearchQuerySortRequest ```csharp public sealed class GroupSearchQuerySortRequest ``` | Property | Type | Description | | -------- | ---------------------------------- | --------------------------------------------- | | `Field` | `GroupSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## GroupUpdateRequest ```csharp public sealed class GroupUpdateRequest ``` | Property | Type | Description | | ------------- | -------- | --------------------------------- | | `Name` | `String` | The new name of the group. | | `Description` | `String` | The new description of the group. | ## GroupUpdateResult ```csharp public sealed class GroupUpdateResult ``` | Property | Type | Description | | ------------- | --------- | ----------------------------- | | `GroupId` | `GroupId` | The unique group ID. | | `Name` | `String` | The name of the group. | | `Description` | `String` | The description of the group. | ## GroupUserResult ```csharp public sealed class GroupUserResult ``` | Property | Type | Description | | ---------- | ---------- | -------------------------- | | `Username` | `Username` | The unique name of a user. | ## GroupUserSearchQueryRequest ```csharp public sealed class GroupUserSearchQueryRequest ``` | Property | Type | Description | | -------- | --------------------------------------- | -------------------- | | `Sort` | `List` | Sort field criteria. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## GroupUserSearchQuerySortRequest ```csharp public sealed class GroupUserSearchQuerySortRequest ``` | Property | Type | Description | | -------- | -------------------------------------- | --------------------------------------------- | | `Field` | `GroupUserSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## GroupUserSearchResult ```csharp public sealed class GroupUserSearchResult ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching members. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## HttpSdkException HTTP-specific SDK error with RFC 7807 Problem Details. ```csharp public sealed class HttpSdkException : CamundaSdkException, ISerializable ``` | Property | Type | Description | | ---------------- | --------- | ----------- | | `Type` | `String` | | | `Title` | `String` | | | `Detail` | `String` | | | `Instance` | `String` | | | `IsBackpressure` | `Boolean` | | ## ICamundaKey Marker interface for all Camunda domain key types. Enables generic constraints and JSON converter discovery. ```csharp public interface ICamundaKey ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## ICamundaLongKey Marker interface for Camunda domain types backed by a long (int64) value. ```csharp public interface ICamundaLongKey ``` | Property | Type | Description | | -------- | ------- | -------------------------- | | `Value` | `Int64` | The underlying long value. | ## ITenantIdSettable Implemented by request body types that have an optional tenantId property. The SDK uses this to inject the configured default tenant ID when the caller does not supply one explicitly. ```csharp public interface ITenantIdSettable ``` ## ITenantIdsSettable Implemented by request body types that have an optional tenantIds array property (e.g. ). The SDK uses this to inject [DefaultTenantId] when the caller does not supply a tenant list explicitly. Mirrors for the plural array shape. ```csharp public interface ITenantIdsSettable ``` ## IncidentErrorTypeExactMatch Matches the value exactly. ```csharp public readonly record struct IncidentErrorTypeExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## IncidentErrorTypeFilterProperty IncidentErrorTypeEnum with full advanced search capabilities. ```csharp public sealed class IncidentErrorTypeFilterProperty ``` | Property | Type | Description | | -------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property does not match any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## IncidentFilter Incident search filter. ```csharp public sealed class IncidentFilter ``` | Property | Type | Description | | ---------------------- | ------------------------------------ | ---------------------------------------------------------------------- | | `ProcessDefinitionId` | `StringFilterProperty` | The process definition ID associated to this incident. | | `ErrorType` | `IncidentErrorTypeFilterProperty` | Incident error type with a defined set of values. | | `ErrorMessage` | `StringFilterProperty` | The error message of this incident. | | `ElementId` | `StringFilterProperty` | The element ID associated to this incident. | | `CreationTime` | `DateTimeFilterProperty` | Date of incident creation. | | `State` | `IncidentStateFilterProperty` | State of this incident with a defined set of values. | | `TenantId` | `StringFilterProperty` | The tenant ID of the incident. | | `IncidentKey` | `BasicStringFilterProperty` | The assigned key, which acts as a unique identifier for this incident. | | `ProcessDefinitionKey` | `ProcessDefinitionKeyFilterProperty` | The process definition key associated to this incident. | | `ProcessInstanceKey` | `ProcessInstanceKeyFilterProperty` | The process instance key associated to this incident. | | `ElementInstanceKey` | `ElementInstanceKeyFilterProperty` | The element instance key associated to this incident. | | `JobKey` | `JobKeyFilterProperty` | The job key, if exists, associated with this incident. | ## IncidentProcessInstanceStatisticsByDefinitionFilter Filter for the incident process instance statistics by definition query. ```csharp public sealed class IncidentProcessInstanceStatisticsByDefinitionFilter ``` | Property | Type | Description | | --------------- | ------- | ---------------------------------------------------------------------------------- | | `ErrorHashCode` | `Int32` | The error hash code of the incidents to filter the process instance statistics by. | ## IncidentProcessInstanceStatisticsByDefinitionQuery ```csharp public sealed class IncidentProcessInstanceStatisticsByDefinitionQuery ``` | Property | Type | Description | | -------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------- | | `Filter` | `IncidentProcessInstanceStatisticsByDefinitionFilter` | Filter criteria for the aggregated process instance statistics. | | `Page` | `OffsetPagination` | Pagination parameters for the aggregated process instance statistics. | | `Sort` | `List` | Sorting criteria for process instance statistics grouped by process definition. | ## IncidentProcessInstanceStatisticsByDefinitionQueryResult ```csharp public sealed class IncidentProcessInstanceStatisticsByDefinitionQueryResult ``` | Property | Type | Description | | -------- | ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | `Items` | `List` | Statistics of active process instances with incidents, grouped by process definition for the specified error hash code. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## IncidentProcessInstanceStatisticsByDefinitionQuerySortRequest ```csharp public sealed class IncidentProcessInstanceStatisticsByDefinitionQuerySortRequest ``` | Property | Type | Description | | -------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------- | | `Field` | `IncidentProcessInstanceStatisticsByDefinitionQuerySortRequestField` | The aggregated field by which the process instance statistics are sorted. | | `Order` | `Nullable` | The order in which to sort the related field. | ## IncidentProcessInstanceStatisticsByDefinitionResult ```csharp public sealed class IncidentProcessInstanceStatisticsByDefinitionResult ``` | Property | Type | Description | | ------------------------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------- | | `ProcessDefinitionId` | `ProcessDefinitionId` | Id of a process definition, from the model. Only ids of process definitions that are deployed are useful. | | `ProcessDefinitionKey` | `ProcessDefinitionKey` | System-generated key for a deployed process definition. | | `ProcessDefinitionName` | `String` | The name of the process definition. | | `ProcessDefinitionVersion` | `Int32` | The version of the process definition. | | `TenantId` | `TenantId` | The unique identifier of the tenant. | | `ActiveInstancesWithErrorCount` | `Int64` | The number of active process instances that currently have an incident with the specified error hash code. | ## IncidentProcessInstanceStatisticsByErrorQuery ```csharp public sealed class IncidentProcessInstanceStatisticsByErrorQuery ``` | Property | Type | Description | | -------- | ---------------------------------------------------------------- | -------------------------------------------------------------------------------- | | `Page` | `OffsetPagination` | Pagination parameters for process instance statistics grouped by incident error. | | `Sort` | `List` | Sorting criteria for process instance statistics grouped by incident error. | ## IncidentProcessInstanceStatisticsByErrorQueryResult ```csharp public sealed class IncidentProcessInstanceStatisticsByErrorQueryResult ``` | Property | Type | Description | | -------- | ------------------------------------------------------ | ----------------------------------------------------------------- | | `Items` | `List` | Statistics of active process instances grouped by incident error. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## IncidentProcessInstanceStatisticsByErrorQuerySortRequest ```csharp public sealed class IncidentProcessInstanceStatisticsByErrorQuerySortRequest ``` | Property | Type | Description | | -------- | --------------------------------------------------------------- | --------------------------------------------------- | | `Field` | `IncidentProcessInstanceStatisticsByErrorQuerySortRequestField` | The field to sort the incident error statistics by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## IncidentProcessInstanceStatisticsByErrorResult ```csharp public sealed class IncidentProcessInstanceStatisticsByErrorResult ``` | Property | Type | Description | | ------------------------------- | -------- | ---------------------------------------------------------------------------------------------- | | `ErrorHashCode` | `Int32` | The hash code identifying a specific incident error.. | | `ErrorMessage` | `String` | The error message associated with the incident error hash code. | | `ActiveInstancesWithErrorCount` | `Int64` | The number of active process instances that currently have an active incident with this error. | ## IncidentResolutionRequest ```csharp public sealed class IncidentResolutionRequest ``` | Property | Type | Description | | -------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | | `OperationReference` | `Nullable` | A reference key chosen by the user that will be part of all records resulting from this operation. Must be > 0 if provided. | ## IncidentResult ```csharp public sealed class IncidentResult ``` | Property | Type | Description | | ------------------------ | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ProcessDefinitionId` | `ProcessDefinitionId` | The process definition ID associated to this incident. | | `ErrorType` | `IncidentErrorTypeEnum` | The type of the incident error. | | `ErrorMessage` | `String` | Error message which describes the error in more detail. | | `ElementId` | `ElementId` | The element ID associated to this incident. | | `CreationTime` | `DateTimeOffset` | The creation time of the incident. | | `State` | `IncidentStateEnum` | The incident state. | | `TenantId` | `TenantId` | The tenant ID of the incident. | | `IncidentKey` | `IncidentKey` | The assigned key, which acts as a unique identifier for this incident. | | `ProcessDefinitionKey` | `ProcessDefinitionKey` | The process definition key associated to this incident. | | `ProcessInstanceKey` | `ProcessInstanceKey` | The process instance key associated to this incident. | | `RootProcessInstanceKey` | `Nullable` | The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. | | `ElementInstanceKey` | `ElementInstanceKey` | The element instance key associated to this incident. | | `JobKey` | `Nullable` | The job key, if exists, associated with this incident. | ## IncidentSearchQuery ```csharp public sealed class IncidentSearchQuery ``` | Property | Type | Description | | -------- | -------------------------------------- | ---------------------------- | | `Sort` | `List` | Sort field criteria. | | `Filter` | `IncidentFilter` | The incident search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## IncidentSearchQueryResult ```csharp public sealed class IncidentSearchQueryResult ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching incidents. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## IncidentSearchQuerySortRequest ```csharp public sealed class IncidentSearchQuerySortRequest ``` | Property | Type | Description | | -------- | ------------------------------------- | --------------------------------------------- | | `Field` | `IncidentSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## IncidentStateExactMatch Matches the value exactly. ```csharp public readonly record struct IncidentStateExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## IncidentStateFilterProperty IncidentStateEnum with full advanced search capabilities. ```csharp public sealed class IncidentStateFilterProperty ``` | Property | Type | Description | | -------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property does not match any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## InferredAncestorKeyInstruction Instructs the engine to derive the ancestor scope key from the source element's hierarchy. The engine traverses the source element's ancestry to find an instance that matches one of the target element's flow scopes, ensuring the target is activated in the correct scope. ```csharp public sealed class InferredAncestorKeyInstruction : AncestorScopeInstruction ``` ## IntegerFilterProperty Integer property with advanced search capabilities. ```csharp public sealed class IntegerFilterProperty ``` | Property | Type | Description | | -------- | ------------------- | ---------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `Gt` | `Nullable` | Greater than comparison with the provided value. | | `Gte` | `Nullable` | Greater than or equal comparison with the provided value. | | `Lt` | `Nullable` | Lower than comparison with the provided value. | | `Lte` | `Nullable` | Lower than or equal comparison with the provided value. | | `In` | `List` | Checks if the property matches any of the provided values. | ## JobActivationRequest ```csharp public sealed class JobActivationRequest : ITenantIdsSettable ``` | Property | Type | Description | | ------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Type` | `String` | The job type, as defined in the BPMN process (e.g. <zeebe:taskDefinition type="payment-service" />) | | `Worker` | `String` | The name of the worker activating the jobs, mostly used for logging purposes. | | `Timeout` | `Int64` | A job returned after this call will not be activated by another call until the timeout (in ms) has been reached. | | `MaxJobsToActivate` | `Int32` | The maximum jobs to activate by this request. | | `FetchVariable` | `List` | A list of variables to fetch as the job variables; if empty, all visible variables at the time of activation for the scope of the job will be returned. | | `RequestTimeout` | `Nullable` | The request will be completed when at least one job is activated or after the requestTimeout (in ms). If the requestTimeout = 0, a default timeout is used. If the requestTimeout < 0, long polling is disabled and the request is completed immediately, even when no job is activated. | | `TenantIds` | `List` | A list of IDs of tenants for which to activate jobs. | | `TenantFilter` | `Nullable` | The tenant filtering strategy - determines whether to use provided tenant IDs or assigned tenant IDs from the authenticated principal's authorized tenants. | ## JobActivationResult The list of activated jobs ```csharp public sealed class JobActivationResult ``` | Property | Type | Description | | -------- | -------------------------- | ------------------- | | `Jobs` | `List` | The activated jobs. | ## JobChangeset JSON object with changed job attribute values. The job cannot be completed or failed with this endpoint, use the complete job or fail job endpoints instead. ```csharp public sealed class JobChangeset ``` | Property | Type | Description | | --------- | ----------------- | -------------------------------------------- | | `Retries` | `Nullable` | The new number of retries for the job. | | `Timeout` | `Nullable` | The new timeout for the job in milliseconds. | ## JobCompletionRequest ```csharp public sealed class JobCompletionRequest ``` | Property | Type | Description | | ----------- | ----------- | ------------------------------------------------------------ | | `Variables` | `Object` | The variables to complete the job with. | | `Result` | `JobResult` | The result of the completed job as determined by the worker. | ## JobErrorRequest ```csharp public sealed class JobErrorRequest ``` | Property | Type | Description | | -------------- | -------- | -------------------------------------------------------------------------------------------------------------------------- | | `ErrorCode` | `String` | The error code that will be matched with an error catch event. | | `ErrorMessage` | `String` | An error message that provides additional context. | | `Variables` | `Object` | JSON object that will instantiate the variables at the local scope of the error catch event that catches the thrown error. | ## JobErrorStatisticsFilter Job error statistics search filter. ```csharp public sealed class JobErrorStatisticsFilter ``` | Property | Type | Description | | -------------- | ---------------------- | ---------------------------------------------------------------------- | | `From` | `DateTimeOffset` | Start of the time window to filter metrics. ISO 8601 date-time format. | | `To` | `DateTimeOffset` | End of the time window to filter metrics. ISO 8601 date-time format. | | `JobType` | `String` | Job type to return error metrics for. | | `ErrorCode` | `StringFilterProperty` | Optional error code filter with advanced search capabilities. | | `ErrorMessage` | `StringFilterProperty` | Optional error message filter with advanced search capabilities. | ## JobErrorStatisticsItem Aggregated error metrics for a single error type and message combination. ```csharp public sealed class JobErrorStatisticsItem ``` | Property | Type | Description | | -------------- | -------- | ------------------------------------------------------- | | `ErrorCode` | `String` | The error code identifier. | | `ErrorMessage` | `String` | The error message. | | `Workers` | `Int32` | Number of distinct workers that encountered this error. | ## JobErrorStatisticsQuery Job error statistics query. ```csharp public sealed class JobErrorStatisticsQuery ``` | Property | Type | Description | | -------- | -------------------------- | ----------------------------------- | | `Filter` | `JobErrorStatisticsFilter` | Job error statistics search filter. | | `Page` | `CursorForwardPagination` | Search cursor pagination. | ## JobErrorStatisticsQueryResult Job error statistics query result. ```csharp public sealed class JobErrorStatisticsQueryResult ``` | Property | Type | Description | | -------- | ------------------------------ | ------------------------------------------------ | | `Items` | `List` | The list of per-error statistics items. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## JobFailRequest ```csharp public sealed class JobFailRequest ``` | Property | Type | Description | | -------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Retries` | `Nullable` | The amount of retries the job should have left | | `ErrorMessage` | `String` | An optional error message describing why the job failed; if not provided, an empty string is used. | | `RetryBackOff` | `Nullable` | An optional retry back off for the failed job. The job will not be retryable before the current time plus the back off time. The default is 0 which means the job is retryable immediately. | | `Variables` | `Object` | JSON object that will instantiate the variables at the local scope of the job's associated task. | ## JobFailureException Throw from a job handler to explicitly fail a job with custom retry settings. ```csharp public sealed class JobFailureException : Exception, ISerializable ``` | Property | Type | Description | | ---------------- | ----------------- | ---------------------------------------------------------------------- | | `Retries` | `Nullable` | How many retries the job should have remaining. null = server decides. | | `RetryBackOffMs` | `Nullable` | Retry back-off in milliseconds. null = immediate retry. | ## JobFilter Job search filter. ```csharp public sealed class JobFilter ``` | Property | Type | Description | | -------------------------- | ------------------------------------ | --------------------------------------------------------------------------- | | `Deadline` | `DateTimeFilterProperty` | When the job can next be activated. | | `DeniedReason` | `StringFilterProperty` | The reason provided by the user task listener for denying the work. | | `ElementId` | `StringFilterProperty` | The element ID associated with the job. | | `ElementInstanceKey` | `ElementInstanceKeyFilterProperty` | The element instance key associated with the job. | | `EndTime` | `DateTimeFilterProperty` | When the job ended. | | `ErrorCode` | `StringFilterProperty` | The error code provided for the failed job. | | `ErrorMessage` | `StringFilterProperty` | The error message that provides additional context for a failed job. | | `HasFailedWithRetriesLeft` | `Nullable` | Indicates whether the job has failed with retries left. | | `IsDenied` | `Nullable` | Indicates whether the user task listener denies the work. | | `JobKey` | `JobKeyFilterProperty` | The key, a unique identifier for the job. | | `Kind` | `JobKindFilterProperty` | The kind of the job. | | `ListenerEventType` | `JobListenerEventTypeFilterProperty` | The listener event type of the job. | | `ProcessDefinitionId` | `StringFilterProperty` | The process definition ID associated with the job. | | `ProcessDefinitionKey` | `ProcessDefinitionKeyFilterProperty` | The process definition key associated with the job. | | `ProcessInstanceKey` | `ProcessInstanceKeyFilterProperty` | The process instance key associated with the job. | | `Retries` | `IntegerFilterProperty` | The number of retries left. | | `State` | `JobStateFilterProperty` | The state of the job. | | `TenantId` | `StringFilterProperty` | The tenant ID. | | `Type` | `StringFilterProperty` | The type of the job. | | `Worker` | `StringFilterProperty` | The name of the worker for this job. | | `CreationTime` | `DateTimeFilterProperty` | When the job was created. Field is present for jobs created after 8.9. | | `LastUpdateTime` | `DateTimeFilterProperty` | When the job was last updated. Field is present for jobs created after 8.9. | ## JobHandler Delegate for job handler functions. Return the output variables to complete the job with, or null to complete with no output variables. Return a to send a structured completion (e.g. with job corrections or a task denial). To signal a BPMN error, throw . To explicitly fail a job with custom retries, throw . Any other unhandled exception auto-fails the job with retries - 1. ```csharp public delegate Task JobHandler(ActivatedJob job, CancellationToken ct) ``` ## JobKeyExactMatch Matches the value exactly. ```csharp public readonly record struct JobKeyExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## JobKeyFilterProperty JobKey property with full advanced search capabilities. ```csharp public sealed class JobKeyFilterProperty ``` | Property | Type | Description | | -------- | ------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## JobKindExactMatch Matches the value exactly. ```csharp public readonly record struct JobKindExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## JobKindFilterProperty JobKindEnum property with full advanced search capabilities. ```csharp public sealed class JobKindFilterProperty ``` | Property | Type | Description | | -------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## JobListenerEventTypeExactMatch Matches the value exactly. ```csharp public readonly record struct JobListenerEventTypeExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## JobListenerEventTypeFilterProperty JobListenerEventTypeEnum property with full advanced search capabilities. ```csharp public sealed class JobListenerEventTypeFilterProperty ``` | Property | Type | Description | | -------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## JobMetricsConfigurationResponse Configuration for job metrics collection and export. ```csharp public sealed class JobMetricsConfigurationResponse ``` | Property | Type | Description | | --------------------- | --------- | ------------------------------------------------------------------------ | | `Enabled` | `Boolean` | Whether job metrics export is enabled. | | `ExportInterval` | `String` | The interval at which job metrics are exported, as an ISO 8601 duration. | | `MaxWorkerNameLength` | `Int32` | The maximum length of the worker name used in job metrics labels. | | `MaxJobTypeLength` | `Int32` | The maximum length of the job type used in job metrics labels. | | `MaxTenantIdLength` | `Int32` | The maximum length of the tenant ID used in job metrics labels. | | `MaxUniqueKeys` | `Int32` | The maximum number of unique metric keys tracked for job metrics. | ## JobResult The result of the completed job as determined by the worker. ```csharp public abstract class JobResult ``` ## JobResultActivateElement Instruction to activate a single BPMN element within an ad‑hoc sub‑process, optionally providing variables scoped to that element. ```csharp public sealed class JobResultActivateElement ``` | Property | Type | Description | | ----------- | --------------------- | --------------------------- | | `ElementId` | `Nullable` | The element ID to activate. | | `Variables` | `Object` | Variables for the element. | ## JobResultAdHocSubProcess Job result details for an ad‑hoc sub‑process, including elements to activate and flags indicating completion or cancellation behavior. ```csharp public sealed class JobResultAdHocSubProcess : JobResult ``` | Property | Type | Description | | -------------------------------- | -------------------------------- | -------------------------------------------------------------------------------------- | | `ActivateElements` | `List` | Indicates which elements need to be activated in the ad-hoc subprocess. | | `IsCompletionConditionFulfilled` | `Nullable` | Indicates whether the completion condition of the ad-hoc subprocess is fulfilled. | | `IsCancelRemainingInstances` | `Nullable` | Indicates whether the remaining instances of the ad-hoc subprocess should be canceled. | ## JobResultCorrections JSON object with attributes that were corrected by the worker. The following attributes can be corrected, additional attributes will be ignored: - `assignee` - clear by providing an empty String - `dueDate` - clear by providing an empty String - `followUpDate` - clear by providing an empty String - `candidateGroups` - clear by providing an empty list - `candidateUsers` - clear by providing an empty list - `priority` - minimum 0, maximum 100, default 50 Providing any of those attributes with a `null` value or omitting it preserves the persisted attribute's value. ```csharp public sealed class JobResultCorrections ``` | Property | Type | Description | | ----------------- | -------------------------- | ----------------------------------------- | | `Assignee` | `String` | Assignee of the task. | | `DueDate` | `Nullable` | The due date of the task. | | `FollowUpDate` | `Nullable` | The follow-up date of the task. | | `CandidateUsers` | `List` | The list of candidate users of the task. | | `CandidateGroups` | `List` | The list of candidate groups of the task. | | `Priority` | `Nullable` | The priority of the task. | ## JobResultUserTask Job result details for a user task completion, optionally including a denial reason and corrected task properties. ```csharp public sealed class JobResultUserTask : JobResult ``` | Property | Type | Description | | -------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Denied` | `Nullable` | Indicates whether the worker denies the work, i.e. explicitly doesn't approve it. For example, a user task listener can deny the completion of a task by setting this flag to true. In this example, the completion of a task is represented by a job that the worker can complete as denied. As a result, the completion request is rejected and the task remains active. Defaults to false. | | `DeniedReason` | `String` | The reason provided by the user task listener for denying the work. | | `Corrections` | `JobResultCorrections` | JSON object with attributes that were corrected by the worker. The following attributes can be corrected, additional attributes will be ignored: _ `assignee` - clear by providing an empty String _ `dueDate` - clear by providing an empty String _ `followUpDate` - clear by providing an empty String _ `candidateGroups` - clear by providing an empty list _ `candidateUsers` - clear by providing an empty list _ `priority` - minimum 0, maximum 100, default 50 Providing any of those attributes with a `null` value or omitting it preserves the persisted attribute's value. | ## JobSearchQuery Job search request. ```csharp public sealed class JobSearchQuery ``` | Property | Type | Description | | -------- | --------------------------------- | ----------------------- | | `Sort` | `List` | Sort field criteria. | | `Filter` | `JobFilter` | The job search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## JobSearchQueryResult Job search response. ```csharp public sealed class JobSearchQueryResult ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching jobs. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## JobSearchQuerySortRequest ```csharp public sealed class JobSearchQuerySortRequest ``` | Property | Type | Description | | -------- | -------------------------------- | --------------------------------------------- | | `Field` | `JobSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## JobSearchResult ```csharp public sealed class JobSearchResult ``` | Property | Type | Description | | -------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `CustomHeaders` | `Dictionary` | A set of custom headers defined during modelling. | | `Deadline` | `Nullable` | If the job has been activated, when it will next be available to be activated. | | `DeniedReason` | `String` | The reason provided by the user task listener for denying the work. | | `ElementId` | `Nullable` | The element ID associated with the job. May be missing on job failure. | | `ElementInstanceKey` | `ElementInstanceKey` | The element instance key associated with the job. | | `EndTime` | `Nullable` | End date of the job. This is `null` if the job is not in an end state yet. | | `ErrorCode` | `String` | The error code provided for a failed job. | | `ErrorMessage` | `String` | The error message that provides additional context for a failed job. | | `HasFailedWithRetriesLeft` | `Boolean` | Indicates whether the job has failed with retries left. | | `IsDenied` | `Nullable` | Indicates whether the user task listener denies the work. | | `JobKey` | `JobKey` | The key, a unique identifier for the job. | | `Kind` | `JobKindEnum` | The job kind. | | `ListenerEventType` | `JobListenerEventTypeEnum` | The listener event type of the job. | | `ProcessDefinitionId` | `ProcessDefinitionId` | The process definition ID associated with the job. | | `ProcessDefinitionKey` | `ProcessDefinitionKey` | The process definition key associated with the job. | | `ProcessInstanceKey` | `ProcessInstanceKey` | The process instance key associated with the job. | | `RootProcessInstanceKey` | `Nullable` | The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. | | `Retries` | `Int32` | The amount of retries left to this job. | | `State` | `JobStateEnum` | The state of the job. | | `TenantId` | `TenantId` | The unique identifier of the tenant. | | `Type` | `String` | The type of the job. | | `Worker` | `String` | The name of the worker of this job. | | `CreationTime` | `Nullable` | When the job was created. Field is present for jobs created after 8.9. | | `LastUpdateTime` | `Nullable` | When the job was last updated. Field is present for jobs created after 8.9. | ## JobStateExactMatch Matches the value exactly. ```csharp public readonly record struct JobStateExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## JobStateFilterProperty JobStateEnum property with full advanced search capabilities. ```csharp public sealed class JobStateFilterProperty ``` | Property | Type | Description | | -------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## JobTimeSeriesStatisticsFilter Job time-series statistics search filter. ```csharp public sealed class JobTimeSeriesStatisticsFilter ``` | Property | Type | Description | | ------------ | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | `From` | `DateTimeOffset` | Start of the time window to filter metrics. ISO 8601 date-time format. | | `To` | `DateTimeOffset` | End of the time window to filter metrics. ISO 8601 date-time format. | | `JobType` | `String` | Job type to return time-series metrics for. | | `Resolution` | `String` | Time bucket resolution as an ISO 8601 duration (for example `PT1M` for 1 minute, `PT1H` for 1 hour). If omitted, the server chooses a sensible default. | ## JobTimeSeriesStatisticsItem Aggregated job metrics for a single time bucket. ```csharp public sealed class JobTimeSeriesStatisticsItem ``` | Property | Type | Description | | ----------- | ---------------- | -------------------------------------------------------------- | | `Time` | `DateTimeOffset` | ISO 8601 timestamp representing the start of this time bucket. | | `Created` | `StatusMetric` | Metric for a single job status. | | `Completed` | `StatusMetric` | Metric for a single job status. | | `Failed` | `StatusMetric` | Metric for a single job status. | ## JobTimeSeriesStatisticsQuery Job time-series statistics query. ```csharp public sealed class JobTimeSeriesStatisticsQuery ``` | Property | Type | Description | | -------- | ------------------------------- | ----------------------------------------- | | `Filter` | `JobTimeSeriesStatisticsFilter` | Job time-series statistics search filter. | | `Page` | `CursorForwardPagination` | Search cursor pagination. | ## JobTimeSeriesStatisticsQueryResult Job time-series statistics query result. ```csharp public sealed class JobTimeSeriesStatisticsQueryResult ``` | Property | Type | Description | | -------- | ----------------------------------- | ---------------------------------------------------------------------- | | `Items` | `List` | The list of time-bucketed statistics items, ordered ascending by time. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## JobTypeStatisticsFilter Job type statistics search filter. ```csharp public sealed class JobTypeStatisticsFilter ``` | Property | Type | Description | | --------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------ | | `From` | `DateTimeOffset` | Start of the time window to filter metrics. ISO 8601 date-time format. | | `To` | `DateTimeOffset` | End of the time window to filter metrics. ISO 8601 date-time format. | | `JobType` | `StringFilterProperty` | Optional job type filter with advanced search capabilities. Supports exact match, pattern matching, and other operators. | ## JobTypeStatisticsItem Statistics for a single job type. ```csharp public sealed class JobTypeStatisticsItem ``` | Property | Type | Description | | ----------- | -------------- | ------------------------------------------------------ | | `JobType` | `String` | The job type identifier. | | `Created` | `StatusMetric` | Metric for a single job status. | | `Completed` | `StatusMetric` | Metric for a single job status. | | `Failed` | `StatusMetric` | Metric for a single job status. | | `Workers` | `Int32` | Number of distinct workers observed for this job type. | ## JobTypeStatisticsQuery Job type statistics query. ```csharp public sealed class JobTypeStatisticsQuery ``` | Property | Type | Description | | -------- | ------------------------- | ---------------------------------- | | `Filter` | `JobTypeStatisticsFilter` | Job type statistics search filter. | | `Page` | `CursorForwardPagination` | Search cursor pagination. | ## JobTypeStatisticsQueryResult Job type statistics query result. ```csharp public sealed class JobTypeStatisticsQueryResult ``` | Property | Type | Description | | -------- | ----------------------------- | ------------------------------------------------ | | `Items` | `List` | The list of job type statistics items. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## JobUpdateRequest ```csharp public sealed class JobUpdateRequest ``` | Property | Type | Description | | -------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `Changeset` | `JobChangeset` | JSON object with changed job attribute values. The job cannot be completed or failed with this endpoint, use the complete job or fail job endpoints instead. | | `OperationReference` | `Nullable` | A reference key chosen by the user that will be part of all records resulting from this operation. Must be > 0 if provided. | ## JobWorker A long-running worker that polls the Camunda broker for jobs of a specific type, dispatches them to a handler, and auto-completes or auto-fails based on the outcome. Concurrency model: jobs are dispatched as concurrent s on the .NET thread pool. controls how many jobs may be in-flight simultaneously. For async handlers (the typical case), the thread pool thread is released during await points, so many jobs can be handled by a small number of OS threads. For CPU-bound handlers, set MaxConcurrentJobs to to match available cores. ```csharp public sealed class JobWorker : IAsyncDisposable, IDisposable ``` | Property | Type | Description | | ------------ | --------- | -------------------------------------------------- | | `ActiveJobs` | `Int32` | Number of jobs currently being processed. | | `IsRunning` | `Boolean` | Whether the poll loop is currently running. | | `Name` | `String` | The worker's name (auto-generated or from config). | ## JobWorkerStatisticsFilter Job worker statistics search filter. ```csharp public sealed class JobWorkerStatisticsFilter ``` | Property | Type | Description | | --------- | ---------------- | ---------------------------------------------------------------------- | | `From` | `DateTimeOffset` | Start of the time window to filter metrics. ISO 8601 date-time format. | | `To` | `DateTimeOffset` | End of the time window to filter metrics. ISO 8601 date-time format. | | `JobType` | `String` | Job type to return worker metrics for. | ## JobWorkerStatisticsItem Statistics for a single worker within a job type. ```csharp public sealed class JobWorkerStatisticsItem ``` | Property | Type | Description | | ----------- | -------------- | ----------------------------------------------------------------------------- | | `Worker` | `String` | The name of the worker activating the jobs, mostly used for logging purposes. | | `Created` | `StatusMetric` | Metric for a single job status. | | `Completed` | `StatusMetric` | Metric for a single job status. | | `Failed` | `StatusMetric` | Metric for a single job status. | ## JobWorkerStatisticsQuery Job worker statistics query. ```csharp public sealed class JobWorkerStatisticsQuery ``` | Property | Type | Description | | -------- | --------------------------- | ------------------------------------ | | `Filter` | `JobWorkerStatisticsFilter` | Job worker statistics search filter. | | `Page` | `CursorForwardPagination` | Search cursor pagination. | ## JobWorkerStatisticsQueryResult Job worker statistics query result. ```csharp public sealed class JobWorkerStatisticsQueryResult ``` | Property | Type | Description | | -------- | ------------------------------- | ------------------------------------------------ | | `Items` | `List` | The list of per-worker statistics items. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## LicenseResponse The response of a license request. ```csharp public sealed class LicenseResponse ``` | Property | Type | Description | | -------------- | -------------------------- | -------------------------------------------------------------------- | | `ValidLicense` | `Boolean` | True if the Camunda license is valid, false if otherwise | | `LicenseType` | `String` | Will return the license type property of the Camunda license | | `IsCommercial` | `Boolean` | Will be false when a license contains a non-commerical=true property | | `ExpiresAt` | `Nullable` | The date when the Camunda license expires | ## LikeFilter Checks if the property matches the provided like value. Supported wildcard characters are: - `*`: matches zero, one, or multiple characters. - `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. ```csharp public readonly record struct LikeFilter : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## LimitPagination ```csharp public sealed class LimitPagination : SearchQueryPageRequest ``` | Property | Type | Description | | -------- | ----------------- | ----------------------------------------------------- | | `Limit` | `Nullable` | The maximum number of items to return in one request. | ## MappingRuleCreateRequest ```csharp public sealed class MappingRuleCreateRequest ``` | Property | Type | Description | | --------------- | --------------- | ---------------------------------- | | `MappingRuleId` | `MappingRuleId` | The unique ID of the mapping rule. | | `ClaimName` | `String` | The name of the claim to map. | | `ClaimValue` | `String` | The value of the claim to map. | | `Name` | `String` | The name of the mapping rule. | ## MappingRuleCreateResult ```csharp public sealed class MappingRuleCreateResult ``` | Property | Type | Description | | --------------- | --------------- | ---------------------------------- | | `ClaimName` | `String` | The name of the claim to map. | | `ClaimValue` | `String` | The value of the claim to map. | | `Name` | `String` | The name of the mapping rule. | | `MappingRuleId` | `MappingRuleId` | The unique ID of the mapping rule. | ## MappingRuleCreateUpdateRequest ```csharp public sealed class MappingRuleCreateUpdateRequest ``` | Property | Type | Description | | ------------ | -------- | ------------------------------ | | `ClaimName` | `String` | The name of the claim to map. | | `ClaimValue` | `String` | The value of the claim to map. | | `Name` | `String` | The name of the mapping rule. | ## MappingRuleCreateUpdateResult ```csharp public sealed class MappingRuleCreateUpdateResult ``` | Property | Type | Description | | --------------- | --------------- | ---------------------------------- | | `ClaimName` | `String` | The name of the claim to map. | | `ClaimValue` | `String` | The value of the claim to map. | | `Name` | `String` | The name of the mapping rule. | | `MappingRuleId` | `MappingRuleId` | The unique ID of the mapping rule. | ## MappingRuleFilter Mapping rule search filter. ```csharp public sealed class MappingRuleFilter ``` | Property | Type | Description | | --------------- | ------------------------- | ---------------------------------------- | | `ClaimName` | `String` | The claim name to match against a token. | | `ClaimValue` | `String` | The value of the claim to match. | | `Name` | `String` | The name of the mapping rule. | | `MappingRuleId` | `Nullable` | The ID of the mapping rule. | ## MappingRuleId The unique identifier of a mapping rule. ```csharp public readonly record struct MappingRuleId : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## MappingRuleResult ```csharp public sealed class MappingRuleResult ``` | Property | Type | Description | | --------------- | --------------- | ------------------------------ | | `ClaimName` | `String` | The name of the claim to map. | | `ClaimValue` | `String` | The value of the claim to map. | | `Name` | `String` | The name of the mapping rule. | | `MappingRuleId` | `MappingRuleId` | The ID of the mapping rule. | ## MappingRuleSearchQueryRequest ```csharp public sealed class MappingRuleSearchQueryRequest ``` | Property | Type | Description | | -------- | ----------------------------------------- | -------------------------------- | | `Sort` | `List` | Sort field criteria. | | `Filter` | `MappingRuleFilter` | The mapping rule search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## MappingRuleSearchQueryResult ```csharp public sealed class MappingRuleSearchQueryResult ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching mapping rules. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## MappingRuleSearchQuerySortRequest ```csharp public sealed class MappingRuleSearchQuerySortRequest ``` | Property | Type | Description | | -------- | ---------------------------------------- | --------------------------------------------- | | `Field` | `MappingRuleSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## MappingRuleUpdateRequest ```csharp public sealed class MappingRuleUpdateRequest ``` | Property | Type | Description | | ------------ | -------- | ------------------------------ | | `ClaimName` | `String` | The name of the claim to map. | | `ClaimValue` | `String` | The value of the claim to map. | | `Name` | `String` | The name of the mapping rule. | ## MappingRuleUpdateResult ```csharp public sealed class MappingRuleUpdateResult ``` | Property | Type | Description | | --------------- | --------------- | ---------------------------------- | | `ClaimName` | `String` | The name of the claim to map. | | `ClaimValue` | `String` | The value of the claim to map. | | `Name` | `String` | The name of the mapping rule. | | `MappingRuleId` | `MappingRuleId` | The unique ID of the mapping rule. | ## MatchedDecisionRuleItem A decision rule that matched within this decision evaluation. ```csharp public sealed class MatchedDecisionRuleItem ``` | Property | Type | Description | | ------------------ | ----------------------------------- | ------------------------------- | | `RuleId` | `String` | The ID of the matched rule. | | `RuleIndex` | `Int32` | The index of the matched rule. | | `EvaluatedOutputs` | `List` | The evaluated decision outputs. | ## MessageCorrelationRequest ```csharp public sealed class MessageCorrelationRequest : ITenantIdSettable ``` | Property | Type | Description | | ---------------- | -------------------- | ----------------------------------------------- | | `Name` | `String` | The message name as defined in the BPMN process | | `CorrelationKey` | `String` | The correlation key of the message. | | `Variables` | `Object` | The message variables as JSON document | | `TenantId` | `Nullable` | the tenant for which the message is published | ## MessageCorrelationResult The message key of the correlated message, as well as the first process instance key it correlated with. ```csharp public sealed class MessageCorrelationResult ``` | Property | Type | Description | | -------------------- | -------------------- | ----------------------------------------------------------------- | | `TenantId` | `TenantId` | The tenant ID of the correlated message | | `MessageKey` | `MessageKey` | The key of the correlated message. | | `ProcessInstanceKey` | `ProcessInstanceKey` | The key of the first process instance the message correlated with | ## MessagePublicationRequest ```csharp public sealed class MessagePublicationRequest : ITenantIdSettable ``` | Property | Type | Description | | ---------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Name` | `String` | The name of the message. | | `CorrelationKey` | `String` | The correlation key of the message. | | `TimeToLive` | `Nullable` | Timespan (in ms) to buffer the message on the broker. | | `MessageId` | `String` | The unique ID of the message. This is used to ensure only one message with the given ID will be published during the lifetime of the message (if `timeToLive` is set). | | `Variables` | `Object` | The message variables as JSON document. | | `TenantId` | `Nullable` | The tenant of the message sender. | ## MessagePublicationResult The message key of the published message. ```csharp public sealed class MessagePublicationResult ``` | Property | Type | Description | | ------------ | ------------ | --------------------------------- | | `TenantId` | `TenantId` | The tenant ID of the message. | | `MessageKey` | `MessageKey` | The key of the published message. | ## MessageSubscriptionFilter Message subscription search filter. ```csharp public sealed class MessageSubscriptionFilter ``` | Property | Type | Description | | -------------------------- | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `MessageSubscriptionKey` | `MessageSubscriptionKeyFilterProperty` | The message subscription key associated with this message subscription. | | `ProcessDefinitionKey` | `ProcessDefinitionKeyFilterProperty` | The process definition key associated with this correlated message subscription. This only works for data created with 8.9 and later. | | `ProcessDefinitionId` | `StringFilterProperty` | The process definition ID associated with this message subscription. | | `ProcessInstanceKey` | `ProcessInstanceKeyFilterProperty` | The process instance key associated with this message subscription. | | `ElementId` | `StringFilterProperty` | The element ID associated with this message subscription. | | `ElementInstanceKey` | `ElementInstanceKeyFilterProperty` | The element instance key associated with this message subscription. | | `MessageSubscriptionState` | `MessageSubscriptionStateFilterProperty` | The message subscription state. | | `LastUpdatedDate` | `DateTimeFilterProperty` | The last updated date of the message subscription. | | `MessageName` | `StringFilterProperty` | The name of the message associated with the message subscription. | | `CorrelationKey` | `StringFilterProperty` | The correlation key of the message subscription. | | `TenantId` | `StringFilterProperty` | The unique external tenant ID. | | `MessageSubscriptionType` | `MessageSubscriptionTypeFilterProperty` | The type of message subscription to filter by. When omitted, both `START_EVENT` and `PROCESS_EVENT` are returned. Only available for data created with Camunda 8.10 or later. | | `ProcessDefinitionName` | `StringFilterProperty` | The name of the process definition associated with this message subscription. | | `ProcessDefinitionVersion` | `IntegerFilterProperty` | The version of the process definition associated with this message subscription. | | `ToolName` | `StringFilterProperty` | Filter by tool name extracted from the `io.camunda.tool:name` zeebe:property. | | `InboundConnectorType` | `StringFilterProperty` | Filter by inbound connector type extracted from the `inbound.type` zeebe:property. | ## MessageSubscriptionKeyExactMatch Matches the value exactly. ```csharp public readonly record struct MessageSubscriptionKeyExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## MessageSubscriptionKeyFilterProperty MessageSubscriptionKey property with full advanced search capabilities. ```csharp public sealed class MessageSubscriptionKeyFilterProperty ``` | Property | Type | Description | | -------- | ---------------------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for equality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## MessageSubscriptionResult ```csharp public sealed class MessageSubscriptionResult ``` | Property | Type | Description | | -------------------------- | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `MessageSubscriptionKey` | `MessageSubscriptionKey` | The message subscription key associated with this message subscription. | | `ProcessDefinitionId` | `ProcessDefinitionId` | The process definition ID associated with this message subscription. | | `ProcessDefinitionKey` | `Nullable` | The process definition key associated with this message subscription. | | `ProcessInstanceKey` | `Nullable` | The process instance key associated with this message subscription. Only populated for intermediate event entities. | | `RootProcessInstanceKey` | `Nullable` | The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. | | `ElementId` | `ElementId` | The element ID associated with this message subscription. | | `ElementInstanceKey` | `Nullable` | The element instance key associated with this message subscription. Only populated for intermediate event entities. | | `MessageSubscriptionState` | `MessageSubscriptionStateEnum` | The state of message subscription. | | `LastUpdatedDate` | `DateTimeOffset` | The last updated date of the message subscription. | | `MessageName` | `String` | The name of the message associated with the message subscription. | | `CorrelationKey` | `String` | The correlation key of the message subscription. | | `MessageSubscriptionType` | `MessageSubscriptionTypeEnum` | The type of message subscription. `START_EVENT` is definition-scoped (process start events). Always has a value; only captured from Camunda 8.10 onwards. `PROCESS_EVENT` is instance-scoped (intermediate catch events). Pre-8.10 entries have no value stored; the API returns `PROCESS_EVENT` as a default for those entries. | | `ToolProperties` | `Dictionary` | The subset of `zeebe:properties` extension properties whose keys start with the `io.camunda.tool:` prefix, extracted from the BPMN element associated with this subscription. Empty object when no matching properties are defined. | | `ProcessDefinitionName` | `String` | The name of the process definition associated with this message subscription. | | `ProcessDefinitionVersion` | `Nullable` | The version of the process definition associated with this message subscription. | | `ToolName` | `String` | Tool name extracted from the `io.camunda.tool:name` zeebe:property. Null when the property is absent. | | `InboundConnectorType` | `String` | Inbound connector type extracted from the `inbound.type` zeebe:property. Null when the property is absent. | | `TenantId` | `TenantId` | The unique identifier of the tenant. | ## MessageSubscriptionSearchQuery ```csharp public sealed class MessageSubscriptionSearchQuery ``` | Property | Type | Description | | -------- | ------------------------------------------------- | ---------------------------- | | `Sort` | `List` | Sort field criteria. | | `Filter` | `MessageSubscriptionFilter` | The incident search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## MessageSubscriptionSearchQueryResult ```csharp public sealed class MessageSubscriptionSearchQueryResult ``` | Property | Type | Description | | -------- | --------------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching message subscriptions. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## MessageSubscriptionSearchQuerySortRequest ```csharp public sealed class MessageSubscriptionSearchQuerySortRequest ``` | Property | Type | Description | | -------- | ------------------------------------------------ | --------------------------------------------- | | `Field` | `MessageSubscriptionSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## MessageSubscriptionStateExactMatch Matches the value exactly. ```csharp public readonly record struct MessageSubscriptionStateExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## MessageSubscriptionStateFilterProperty MessageSubscriptionStateEnum with full advanced search capabilities. ```csharp public sealed class MessageSubscriptionStateFilterProperty ``` | Property | Type | Description | | -------- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## MessageSubscriptionTypeExactMatch Matches the value exactly. ```csharp public readonly record struct MessageSubscriptionTypeExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## MessageSubscriptionTypeFilterProperty MessageSubscriptionTypeEnum with full advanced search capabilities. ```csharp public sealed class MessageSubscriptionTypeFilterProperty ``` | Property | Type | Description | | -------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## MigrateProcessInstanceMappingInstruction The mapping instructions describe how to map elements from the source process definition to the target process definition. ```csharp public sealed class MigrateProcessInstanceMappingInstruction ``` | Property | Type | Description | | ----------------- | ----------- | ------------------------------- | | `SourceElementId` | `ElementId` | The element id to migrate from. | | `TargetElementId` | `ElementId` | The element id to migrate into. | ## ModifyProcessInstanceVariableInstruction Instruction describing which variables to create or update. ```csharp public sealed class ModifyProcessInstanceVariableInstruction ``` | Property | Type | Description | | ----------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Variables` | `Object` | JSON document that will instantiate the variables at the scope defined by the scopeId. It must be a JSON object, as variables will be mapped in a key-value fashion. | | `ScopeId` | `String` | The id of the element in which scope the variables should be created. Leave empty to create the variables in the global scope of the process instance. | ## OffsetPagination ```csharp public sealed class OffsetPagination : SearchQueryPageRequest ``` | Property | Type | Description | | -------- | ----------------- | ----------------------------------------------------- | | `From` | `Nullable` | The index of items to start searching from. | | `Limit` | `Nullable` | The maximum number of items to return in one request. | ## OperationReference A reference key chosen by the user that will be part of all records resulting from this operation. Must be > 0 if provided. ```csharp public readonly record struct OperationReference : ICamundaLongKey, IEquatable ``` | Property | Type | Description | | -------- | ------- | -------------------------- | | `Value` | `Int64` | The underlying long value. | ## OperationTypeExactMatch Matches the value exactly. ```csharp public readonly record struct OperationTypeExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## OperationTypeFilterProperty AuditLogOperationTypeEnum property with full advanced search capabilities. ```csharp public sealed class OperationTypeFilterProperty ``` | Property | Type | Description | | -------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## Partition Provides information on a partition within a broker node. ```csharp public sealed class Partition ``` | Property | Type | Description | | ------------- | ----------------- | ------------------------------------------------------------ | | `PartitionId` | `Int32` | The unique ID of this partition. | | `Role` | `PartitionRole` | Describes the Raft role of the broker for a given partition. | | `Health` | `PartitionHealth` | Describes the current health of the partition. | ## ProblemDetail A Problem detail object as described in [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457). There may be additional properties specific to the problem type. ```csharp public sealed class ProblemDetail ``` | Property | Type | Description | | ---------- | -------- | ------------------------------------------------- | | `Type` | `String` | A URI identifying the problem type. | | `Title` | `String` | A summary of the problem type. | | `Status` | `Int32` | The HTTP status code for this problem. | | `Detail` | `String` | An explanation of the problem in more detail. | | `Instance` | `String` | A URI path identifying the origin of the problem. | ## ProcessDefinitionElementStatisticsQuery Process definition element statistics request. ```csharp public sealed class ProcessDefinitionElementStatisticsQuery ``` | Property | Type | Description | | -------- | ----------------------------------- | ------------------------------------------------- | | `Filter` | `ProcessDefinitionStatisticsFilter` | The process definition statistics search filters. | ## ProcessDefinitionElementStatisticsQueryResult Process definition element statistics query response. ```csharp public sealed class ProcessDefinitionElementStatisticsQueryResult ``` | Property | Type | Description | | -------- | -------------------------------------- | ----------------------- | | `Items` | `List` | The element statistics. | ## ProcessDefinitionFilter Process definition search filter. ```csharp public sealed class ProcessDefinitionFilter ``` | Property | Type | Description | | ---------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `Name` | `StringFilterProperty` | Name of this process definition. | | `IsLatestVersion` | `Nullable` | Whether to only return the latest version of each process definition. When using this filter, pagination functionality is limited, you can only paginate forward using `after` and `limit`. The response contains no `startCursor` in the `page`, and requests ignore the `from` and `before` in the `page`. When using this filter, sorting is limited to `processDefinitionId` and `tenantId` fields only. | | `ResourceName` | `String` | Resource name of this process definition. | | `Version` | `Nullable` | Version of this process definition. | | `VersionTag` | `String` | Version tag of this process definition. | | `ProcessDefinitionId` | `StringFilterProperty` | Process definition ID of this process definition. | | `TenantId` | `Nullable` | Tenant ID of this process definition. | | `ProcessDefinitionKey` | `Nullable` | The key for this process definition. | | `HasStartForm` | `Nullable` | Indicates whether the start event of the process has an associated Form Key. | ## ProcessDefinitionId Id of a process definition, from the model. Only ids of process definitions that are deployed are useful. ```csharp public readonly record struct ProcessDefinitionId : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## ProcessDefinitionIdExactMatch Matches the value exactly. ```csharp public readonly record struct ProcessDefinitionIdExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## ProcessDefinitionIdFilterProperty ProcessDefinitionId property with full advanced search capabilities. ```csharp public sealed class ProcessDefinitionIdFilterProperty ``` | Property | Type | Description | | -------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## ProcessDefinitionInstanceStatisticsQuery ```csharp public sealed class ProcessDefinitionInstanceStatisticsQuery ``` | Property | Type | Description | | -------- | ----------------------------------------------------------- | ------------------------- | | `Page` | `OffsetPagination` | Search cursor pagination. | | `Sort` | `List` | Sort field criteria. | ## ProcessDefinitionInstanceStatisticsQueryResult ```csharp public sealed class ProcessDefinitionInstanceStatisticsQueryResult ``` | Property | Type | Description | | -------- | ------------------------------------------------- | -------------------------------------------------- | | `Items` | `List` | The process definition instance statistics result. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## ProcessDefinitionInstanceStatisticsQuerySortRequest ```csharp public sealed class ProcessDefinitionInstanceStatisticsQuerySortRequest ``` | Property | Type | Description | | -------- | ---------------------------------------------------------- | --------------------------------------------- | | `Field` | `ProcessDefinitionInstanceStatisticsQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## ProcessDefinitionInstanceStatisticsResult Process definition instance statistics response. ```csharp public sealed class ProcessDefinitionInstanceStatisticsResult ``` | Property | Type | Description | | ------------------------------------- | --------------------- | --------------------------------------------------------------------------------------------------------- | | `ProcessDefinitionId` | `ProcessDefinitionId` | Id of a process definition, from the model. Only ids of process definitions that are deployed are useful. | | `TenantId` | `TenantId` | The unique identifier of the tenant. | | `LatestProcessDefinitionName` | `String` | Name of the latest deployed process definition instance version. | | `HasMultipleVersions` | `Boolean` | Indicates whether multiple versions of this process definition instance are deployed. | | `ActiveInstancesWithoutIncidentCount` | `Int64` | Total number of currently active process instances of this definition that do not have incidents. | | `ActiveInstancesWithIncidentCount` | `Int64` | Total number of currently active process instances of this definition that have at least one incident. | ## ProcessDefinitionInstanceVersionStatisticsFilter Process definition instance version statistics search filter. ```csharp public sealed class ProcessDefinitionInstanceVersionStatisticsFilter ``` | Property | Type | Description | | --------------------- | --------------------- | -------------------------------------------------------------------- | | `ProcessDefinitionId` | `ProcessDefinitionId` | The ID of the process definition to retrieve version statistics for. | | `TenantId` | `Nullable` | Tenant ID of this process definition. | ## ProcessDefinitionInstanceVersionStatisticsQuery ```csharp public sealed class ProcessDefinitionInstanceVersionStatisticsQuery ``` | Property | Type | Description | | -------- | ------------------------------------------------------------------ | ------------------------------------------------------------------ | | `Page` | `OffsetPagination` | Pagination criteria. | | `Sort` | `List` | Sort field criteria. | | `Filter` | `ProcessDefinitionInstanceVersionStatisticsFilter` | The process definition instance version statistics search filters. | ## ProcessDefinitionInstanceVersionStatisticsQueryResult ```csharp public sealed class ProcessDefinitionInstanceVersionStatisticsQueryResult ``` | Property | Type | Description | | -------- | -------------------------------------------------------- | ---------------------------------------------------------- | | `Items` | `List` | The process definition instance version statistics result. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## ProcessDefinitionInstanceVersionStatisticsQuerySortRequest ```csharp public sealed class ProcessDefinitionInstanceVersionStatisticsQuerySortRequest ``` | Property | Type | Description | | -------- | ----------------------------------------------------------------- | --------------------------------------------- | | `Field` | `ProcessDefinitionInstanceVersionStatisticsQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## ProcessDefinitionInstanceVersionStatisticsResult Process definition instance version statistics response. ```csharp public sealed class ProcessDefinitionInstanceVersionStatisticsResult ``` | Property | Type | Description | | ------------------------------------- | ---------------------- | --------------------------------------------------------------------------------------- | | `ProcessDefinitionId` | `ProcessDefinitionId` | The ID associated with the process definition. | | `ProcessDefinitionKey` | `ProcessDefinitionKey` | The unique key of the process definition. | | `ProcessDefinitionName` | `String` | The name of the process definition. | | `TenantId` | `TenantId` | The tenant ID associated with the process definition. | | `ProcessDefinitionVersion` | `Int32` | The version number of the process definition. | | `ActiveInstancesWithIncidentCount` | `Int64` | The number of active process instances for this version that currently have incidents. | | `ActiveInstancesWithoutIncidentCount` | `Int64` | The number of active process instances for this version that do not have any incidents. | ## ProcessDefinitionKeyExactMatch Matches the value exactly. ```csharp public readonly record struct ProcessDefinitionKeyExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## ProcessDefinitionKeyFilterProperty ProcessDefinitionKey property with full advanced search capabilities. ```csharp public sealed class ProcessDefinitionKeyFilterProperty ``` | Property | Type | Description | | -------- | -------------------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## ProcessDefinitionMessageSubscriptionStatisticsQuery ```csharp public sealed class ProcessDefinitionMessageSubscriptionStatisticsQuery ``` | Property | Type | Description | | -------- | --------------------------- | --------------------------------- | | `Page` | `CursorForwardPagination` | Search cursor pagination. | | `Filter` | `MessageSubscriptionFilter` | The message subscription filters. | ## ProcessDefinitionMessageSubscriptionStatisticsQueryResult ```csharp public sealed class ProcessDefinitionMessageSubscriptionStatisticsQueryResult ``` | Property | Type | Description | | -------- | ------------------------------------------------------------ | ---------------------------------------------------------------- | | `Items` | `List` | The matching process definition message subscription statistics. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## ProcessDefinitionMessageSubscriptionStatisticsResult ```csharp public sealed class ProcessDefinitionMessageSubscriptionStatisticsResult ``` | Property | Type | Description | | ----------------------------------------- | ---------------------- | --------------------------------------------------------------------------------- | | `ProcessDefinitionId` | `ProcessDefinitionId` | The process definition ID associated with this message subscription. | | `TenantId` | `TenantId` | The tenant ID associated with this message subscription. | | `ProcessDefinitionKey` | `ProcessDefinitionKey` | The process definition key associated with this message subscription. | | `ProcessInstancesWithActiveSubscriptions` | `Int64` | The number of process instances with active message subscriptions. | | `ActiveSubscriptions` | `Int64` | The total number of active message subscriptions for this process definition key. | ## ProcessDefinitionResult ```csharp public sealed class ProcessDefinitionResult ``` | Property | Type | Description | | ---------------------- | ---------------------- | ---------------------------------------------------------------------------- | | `Name` | `String` | Name of this process definition. | | `ResourceName` | `String` | Resource name for this process definition. | | `Version` | `Int32` | Version of this process definition. | | `VersionTag` | `String` | Version tag of this process definition. | | `ProcessDefinitionId` | `ProcessDefinitionId` | Process definition ID of this process definition. | | `TenantId` | `TenantId` | Tenant ID of this process definition. | | `ProcessDefinitionKey` | `ProcessDefinitionKey` | The key for this process definition. | | `HasStartForm` | `Boolean` | Indicates whether the start event of the process has an associated Form Key. | ## ProcessDefinitionSearchQuery ```csharp public sealed class ProcessDefinitionSearchQuery ``` | Property | Type | Description | | -------- | ----------------------------------------------- | -------------------------------------- | | `Sort` | `List` | Sort field criteria. | | `Filter` | `ProcessDefinitionFilter` | The process definition search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## ProcessDefinitionSearchQueryResult ```csharp public sealed class ProcessDefinitionSearchQueryResult ``` | Property | Type | Description | | -------- | ------------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching process definitions. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## ProcessDefinitionSearchQuerySortRequest ```csharp public sealed class ProcessDefinitionSearchQuerySortRequest ``` | Property | Type | Description | | -------- | ---------------------------------------------- | --------------------------------------------- | | `Field` | `ProcessDefinitionSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## ProcessDefinitionStatisticsFilter Process definition statistics search filter. ```csharp public sealed class ProcessDefinitionStatisticsFilter ``` | Property | Type | Description | | ---------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `StartDate` | `DateTimeFilterProperty` | The start date. | | `EndDate` | `DateTimeFilterProperty` | The end date. | | `State` | `ProcessInstanceStateFilterProperty` | The process instance state. | | `HasIncident` | `Nullable` | Whether this process instance has a related incident or not. | | `TenantId` | `StringFilterProperty` | The tenant id. | | `Variables` | `List` | The process instance variables. | | `ProcessInstanceKey` | `ProcessInstanceKeyFilterProperty` | The key of this process instance. | | `ParentProcessInstanceKey` | `ProcessInstanceKeyFilterProperty` | The parent process instance key. | | `ParentElementInstanceKey` | `ElementInstanceKeyFilterProperty` | The parent element instance key. | | `BatchOperationId` | `StringFilterProperty` | The batch operation id. **Deprecated**: Use `batchOperationKey` instead. This field will be removed in a future release. If both `batchOperationId` and `batchOperationKey` are provided, the request will be rejected with a 400 error. | | `BatchOperationKey` | `StringFilterProperty` | The batch operation key. | | `ErrorMessage` | `StringFilterProperty` | The error message related to the process. | | `HasRetriesLeft` | `Nullable` | Whether the process has failed jobs with retries left. | | `ElementInstanceState` | `ElementInstanceStateFilterProperty` | The state of the element instances associated with the process instance. | | `ElementId` | `StringFilterProperty` | The element id associated with the process instance. | | `HasElementInstanceIncident` | `Nullable` | Whether the element instance has an incident or not. | | `IncidentErrorHashCode` | `IntegerFilterProperty` | The incident error hash code, associated with this process. | | `Tags` | `List` | List of tags. Tags need to start with a letter; then alphanumerics, `_`, `-`, `:`, or `.`; length ≤ 100. | | `BusinessId` | `StringFilterProperty` | The business id associated with the process instance. | | `Or` | `List` | Defines a list of alternative filter groups combined using OR logic. Each object in the array is evaluated independently, and the filter matches if any one of them is satisfied. Top-level fields and the `$or` clause are combined using AND logic — meaning: (top-level filters) AND (any of the `$or` filters) must match. <br> <em>Example:</em> `json { "state": "ACTIVE", "tenantId": 123, "$or": [ { "processDefinitionId": "process_v1" }, { "processDefinitionId": "process_v2", "hasIncident": true } ] } ` This matches process instances that: <ul style="padding-left: 20px; margin-left: 20px;"> <li style="list-style-type: disc;">are in <em>ACTIVE</em> state</li> <li style="list-style-type: disc;">have tenant id equal to <em>123</em></li> <li style="list-style-type: disc;">and match either: <ul style="padding-left: 20px; margin-left: 20px;"> <li style="list-style-type: circle;"><code>processDefinitionId</code> is <em>process_v1</em>, or</li> <li style="list-style-type: circle;"><code>processDefinitionId</code> is <em>process_v2</em> and <code>hasIncident</code> is <em>true</em></li> </ul> </li> </ul> <br> <p>Note: Using complex <code>$or</code> conditions may impact performance, use with caution in high-volume environments. | ## ProcessElementStatisticsResult Process element statistics response. ```csharp public sealed class ProcessElementStatisticsResult ``` | Property | Type | Description | | ----------- | ----------- | ------------------------------------------------------- | | `ElementId` | `ElementId` | The element ID for which the results are aggregated. | | `Active` | `Int64` | The total number of active instances of the element. | | `Canceled` | `Int64` | The total number of canceled instances of the element. | | `Incidents` | `Int64` | The total number of incidents for the element. | | `Completed` | `Int64` | The total number of completed instances of the element. | ## ProcessInstanceCallHierarchyEntry ```csharp public sealed class ProcessInstanceCallHierarchyEntry ``` | Property | Type | Description | | ----------------------- | ---------------------- | ---------------------------------------------------------------------------------------------- | | `ProcessInstanceKey` | `ProcessInstanceKey` | The key of the process instance. | | `ProcessDefinitionKey` | `ProcessDefinitionKey` | The key of the process definition. | | `ProcessDefinitionName` | `String` | The name of the process definition (fall backs to the process definition id if not available). | ## ProcessInstanceCancellationBatchOperationRequest The process instance filter that defines which process instances should be canceled. ```csharp public sealed class ProcessInstanceCancellationBatchOperationRequest ``` | Property | Type | Description | | -------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | | `Filter` | `ProcessInstanceFilter` | The process instance filter. | | `OperationReference` | `Nullable` | A reference key chosen by the user that will be part of all records resulting from this operation. Must be > 0 if provided. | ## ProcessInstanceCreationInstruction Instructions for creating a process instance. The process definition can be specified either by id or by key. ```csharp public abstract class ProcessInstanceCreationInstruction ``` ## ProcessInstanceCreationInstructionById ```csharp public sealed class ProcessInstanceCreationInstructionById : ProcessInstanceCreationInstruction, ITenantIdSettable ``` | Property | Type | Description | | -------------------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ProcessDefinitionId` | `ProcessDefinitionId` | The BPMN process id of the process definition to start an instance of. | | `ProcessDefinitionVersion` | `Nullable` | The version of the process. By default, the latest version of the process is used. | | `Variables` | `Object` | JSON object that will instantiate the variables for the root variable scope of the process instance. | | `TenantId` | `Nullable` | The tenant id of the process definition. If multi-tenancy is enabled, provide the tenant id of the process definition to start a process instance of. If multi-tenancy is disabled, don't provide this parameter. | | `OperationReference` | `Nullable` | A reference key chosen by the user that will be part of all records resulting from this operation. Must be > 0 if provided. | | `StartInstructions` | `List` | List of start instructions. By default, the process instance will start at the start event. If provided, the process instance will apply start instructions after it has been created. | | `RuntimeInstructions` | `List` | Runtime instructions (alpha). List of instructions that affect the runtime behavior of the process instance. Refer to specific instruction types for more details. This parameter is an alpha feature and may be subject to change in future releases. | | `AwaitCompletion` | `Nullable` | Wait for the process instance to complete. If the process instance does not complete within the request timeout limit, a 504 response status will be returned. The process instance will continue to run in the background regardless of the timeout. Disabled by default. | | `FetchVariables` | `List` | List of variables by name to be included in the response when awaitCompletion is set to true. If empty, all visible variables in the root scope will be returned. | | `RequestTimeout` | `Nullable` | Timeout (in ms) the request waits for the process to complete. By default or when set to 0, the generic request timeout configured in the cluster is applied. | | `Tags` | `List` | List of tags. Tags need to start with a letter; then alphanumerics, `_`, `-`, `:`, or `.`; length ≤ 100. | | `BusinessId` | `Nullable` | An optional, user-defined string identifier that identifies the process instance within the scope of a process definition (scoped by tenant). If provided and uniqueness enforcement is enabled, the engine will reject creation if another root process instance with the same business id is already active for the same process definition. Note that any active child process instances with the same business id are not taken into account. | ## ProcessInstanceCreationInstructionByKey ```csharp public sealed class ProcessInstanceCreationInstructionByKey : ProcessInstanceCreationInstruction, ITenantIdSettable ``` | Property | Type | Description | | -------------------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ProcessDefinitionKey` | `ProcessDefinitionKey` | The unique key identifying the process definition, for example, returned for a process in the deploy resources endpoint. | | `ProcessDefinitionVersion` | `Nullable` | As the version is already identified by the `processDefinitionKey`, the value of this field is ignored. It's here for backwards-compatibility only as previous releases accepted it in request bodies. | | `Variables` | `Object` | Set of variables as JSON object to instantiate in the root variable scope of the process instance. Can include nested complex objects. | | `StartInstructions` | `List` | List of start instructions. By default, the process instance will start at the start event. If provided, the process instance will apply start instructions after it has been created. | | `RuntimeInstructions` | `List` | Runtime instructions (alpha). List of instructions that affect the runtime behavior of the process instance. Refer to specific instruction types for more details. This parameter is an alpha feature and may be subject to change in future releases. | | `TenantId` | `Nullable` | The tenant id of the process definition. If multi-tenancy is enabled, provide the tenant id of the process definition to start a process instance of. If multi-tenancy is disabled, don't provide this parameter. | | `OperationReference` | `Nullable` | A reference key chosen by the user that will be part of all records resulting from this operation. Must be > 0 if provided. | | `AwaitCompletion` | `Nullable` | Wait for the process instance to complete. If the process instance does not complete within the request timeout limit, a 504 response status will be returned. The process instance will continue to run in the background regardless of the timeout. Disabled by default. | | `RequestTimeout` | `Nullable` | Timeout (in ms) the request waits for the process to complete. By default or when set to 0, the generic request timeout configured in the cluster is applied. | | `FetchVariables` | `List` | List of variables by name to be included in the response when awaitCompletion is set to true. If empty, all visible variables in the root scope will be returned. | | `Tags` | `List` | List of tags. Tags need to start with a letter; then alphanumerics, `_`, `-`, `:`, or `.`; length ≤ 100. | | `BusinessId` | `Nullable` | An optional, user-defined string identifier that identifies the process instance within the scope of a process definition (scoped by tenant). If provided and uniqueness enforcement is enabled, the engine will reject creation if another root process instance with the same business id is already active for the same process definition. Note that any active child process instances with the same business id are not taken into account. | ## ProcessInstanceCreationRuntimeInstruction ```csharp public abstract class ProcessInstanceCreationRuntimeInstruction ``` ## ProcessInstanceCreationStartInstruction ```csharp public sealed class ProcessInstanceCreationStartInstruction ``` | Property | Type | Description | | ----------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ElementId` | `ElementId` | Future extensions might include: - different types of start instructions - ability to set local variables for different flow scopes For now, however, the start instruction is implicitly a "startBeforeElement" instruction | ## ProcessInstanceCreationTerminateInstruction Terminates the process instance after a specific BPMN element is completed or terminated. ```csharp public sealed class ProcessInstanceCreationTerminateInstruction : ProcessInstanceCreationRuntimeInstruction ``` | Property | Type | Description | | ---------------- | ----------- | -------------------------------------------------------------------------------------------------- | | `AfterElementId` | `ElementId` | The id of the element that, once completed or terminated, will cause the process to be terminated. | ## ProcessInstanceDeletionBatchOperationRequest The process instance filter that defines which process instances should be deleted. ```csharp public sealed class ProcessInstanceDeletionBatchOperationRequest ``` | Property | Type | Description | | -------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | | `Filter` | `ProcessInstanceFilter` | The process instance filter. | | `OperationReference` | `Nullable` | A reference key chosen by the user that will be part of all records resulting from this operation. Must be > 0 if provided. | ## ProcessInstanceElementStatisticsQueryResult Process instance element statistics query response. ```csharp public sealed class ProcessInstanceElementStatisticsQueryResult ``` | Property | Type | Description | | -------- | -------------------------------------- | ----------------------- | | `Items` | `List` | The element statistics. | ## ProcessInstanceFilter Process instance search filter. ```csharp public sealed class ProcessInstanceFilter ``` | Property | Type | Description | | ----------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `ProcessDefinitionId` | `StringFilterProperty` | The process definition id. | | `ProcessDefinitionName` | `StringFilterProperty` | The process definition name. | | `ProcessDefinitionVersion` | `IntegerFilterProperty` | The process definition version. | | `ProcessDefinitionVersionTag` | `StringFilterProperty` | The process definition version tag. | | `ProcessDefinitionKey` | `ProcessDefinitionKeyFilterProperty` | The process definition key. | | `Or` | `List` | Defines a list of alternative filter groups combined using OR logic. Each object in the array is evaluated independently, and the filter matches if any one of them is satisfied. Top-level fields and the `$or` clause are combined using AND logic — meaning: (top-level filters) AND (any of the `$or` filters) must match. <br> <em>Example:</em> `json { "state": "ACTIVE", "tenantId": 123, "$or": [ { "processDefinitionId": "process_v1" }, { "processDefinitionId": "process_v2", "hasIncident": true } ] } ` This matches process instances that: <ul style="padding-left: 20px; margin-left: 20px;"> <li style="list-style-type: disc;">are in <em>ACTIVE</em> state</li> <li style="list-style-type: disc;">have tenant id equal to <em>123</em></li> <li style="list-style-type: disc;">and match either: <ul style="padding-left: 20px; margin-left: 20px;"> <li style="list-style-type: circle;"><code>processDefinitionId</code> is <em>process_v1</em>, or</li> <li style="list-style-type: circle;"><code>processDefinitionId</code> is <em>process_v2</em> and <code>hasIncident</code> is <em>true</em></li> </ul> </li> </ul> <br> <p>Note: Using complex <code>$or</code> conditions may impact performance, use with caution in high-volume environments. | ## ProcessInstanceFilterFields Process instance search filter. ```csharp public sealed class ProcessInstanceFilterFields ``` | Property | Type | Description | | ----------------------------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ProcessDefinitionId` | `StringFilterProperty` | The process definition id. | | `ProcessDefinitionName` | `StringFilterProperty` | The process definition name. | | `ProcessDefinitionVersion` | `IntegerFilterProperty` | The process definition version. | | `ProcessDefinitionVersionTag` | `StringFilterProperty` | The process definition version tag. | | `ProcessDefinitionKey` | `ProcessDefinitionKeyFilterProperty` | The process definition key. | | `StartDate` | `DateTimeFilterProperty` | The start date. | | `EndDate` | `DateTimeFilterProperty` | The end date. | | `State` | `ProcessInstanceStateFilterProperty` | The process instance state. | | `HasIncident` | `Nullable` | Whether this process instance has a related incident or not. | | `TenantId` | `StringFilterProperty` | The tenant id. | | `Variables` | `List` | The process instance variables. | | `ProcessInstanceKey` | `ProcessInstanceKeyFilterProperty` | The key of this process instance. | | `ParentProcessInstanceKey` | `ProcessInstanceKeyFilterProperty` | The parent process instance key. | | `ParentElementInstanceKey` | `ElementInstanceKeyFilterProperty` | The parent element instance key. | | `BatchOperationId` | `StringFilterProperty` | The batch operation id. **Deprecated**: Use `batchOperationKey` instead. This field will be removed in a future release. If both `batchOperationId` and `batchOperationKey` are provided, the request will be rejected with a 400 error. | | `BatchOperationKey` | `StringFilterProperty` | The batch operation key. | | `ErrorMessage` | `StringFilterProperty` | The error message related to the process. | | `HasRetriesLeft` | `Nullable` | Whether the process has failed jobs with retries left. | | `ElementInstanceState` | `ElementInstanceStateFilterProperty` | The state of the element instances associated with the process instance. | | `ElementId` | `StringFilterProperty` | The element id associated with the process instance. | | `HasElementInstanceIncident` | `Nullable` | Whether the element instance has an incident or not. | | `IncidentErrorHashCode` | `IntegerFilterProperty` | The incident error hash code, associated with this process. | | `Tags` | `List` | List of tags. Tags need to start with a letter; then alphanumerics, `_`, `-`, `:`, or `.`; length ≤ 100. | | `BusinessId` | `StringFilterProperty` | The business id associated with the process instance. | ## ProcessInstanceIncidentResolutionBatchOperationRequest The process instance filter that defines which process instances should have their incidents resolved. ```csharp public sealed class ProcessInstanceIncidentResolutionBatchOperationRequest ``` | Property | Type | Description | | -------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | | `Filter` | `ProcessInstanceFilter` | The process instance filter. | | `OperationReference` | `Nullable` | A reference key chosen by the user that will be part of all records resulting from this operation. Must be > 0 if provided. | ## ProcessInstanceKeyExactMatch Matches the value exactly. ```csharp public readonly record struct ProcessInstanceKeyExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## ProcessInstanceKeyFilterProperty ProcessInstanceKey property with full advanced search capabilities. ```csharp public sealed class ProcessInstanceKeyFilterProperty ``` | Property | Type | Description | | -------- | ------------------------------ | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## ProcessInstanceMigrationBatchOperationPlan The migration instructions describe how to migrate a process instance from one process definition to another. ```csharp public sealed class ProcessInstanceMigrationBatchOperationPlan ``` | Property | Type | Description | | ---------------------------- | ------------------------------------------------ | ---------------------------------- | | `TargetProcessDefinitionKey` | `ProcessDefinitionKey` | The target process definition key. | | `MappingInstructions` | `List` | The mapping instructions. | ## ProcessInstanceMigrationBatchOperationRequest ```csharp public sealed class ProcessInstanceMigrationBatchOperationRequest ``` | Property | Type | Description | | -------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | | `Filter` | `ProcessInstanceFilter` | The process instance filter. | | `MigrationPlan` | `ProcessInstanceMigrationBatchOperationPlan` | The migration plan. | | `OperationReference` | `Nullable` | A reference key chosen by the user that will be part of all records resulting from this operation. Must be > 0 if provided. | ## ProcessInstanceMigrationInstruction The migration instructions describe how to migrate a process instance from one process definition to another. ```csharp public sealed class ProcessInstanceMigrationInstruction ``` | Property | Type | Description | | ---------------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | | `TargetProcessDefinitionKey` | `ProcessDefinitionKey` | The key of process definition to migrate the process instance to. | | `MappingInstructions` | `List` | Element mappings from the source process instance to the target process instance. | | `OperationReference` | `Nullable` | A reference key chosen by the user that will be part of all records resulting from this operation. Must be > 0 if provided. | ## ProcessInstanceModificationActivateInstruction Instruction describing an element to activate. ```csharp public sealed class ProcessInstanceModificationActivateInstruction ``` | Property | Type | Description | | ---------------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ElementId` | `ElementId` | The id of the element to activate. | | `VariableInstructions` | `List` | Instructions describing which variables to create or update. | | `AncestorElementInstanceKey` | `Nullable` | The key of the ancestor scope the element instance should be created in. Set to -1 to create the new element instance within an existing element instance of the flow scope. If multiple instances of the target element's flow scope exist, choose one specifically with this property by providing its key. | ## ProcessInstanceModificationBatchOperationRequest The process instance filter to define on which process instances tokens should be moved, and new element instances should be activated or terminated. ```csharp public sealed class ProcessInstanceModificationBatchOperationRequest ``` | Property | Type | Description | | -------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | | `Filter` | `ProcessInstanceFilter` | The process instance filter. | | `MoveInstructions` | `List` | Instructions for moving tokens between elements. | | `OperationReference` | `Nullable` | A reference key chosen by the user that will be part of all records resulting from this operation. Must be > 0 if provided. | ## ProcessInstanceModificationInstruction ```csharp public sealed class ProcessInstanceModificationInstruction ``` | Property | Type | Description | | ----------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | | `OperationReference` | `Nullable` | A reference key chosen by the user that will be part of all records resulting from this operation. Must be > 0 if provided. | | `ActivateInstructions` | `List` | Instructions describing which elements to activate in which scopes and which variables to create or update. | | `MoveInstructions` | `List` | Instructions describing which elements to move from one scope to another. | | `TerminateInstructions` | `List` | Instructions describing which elements to terminate. | ## ProcessInstanceModificationMoveBatchOperationInstruction Instructions describing a move operation. This instruction will terminate all active element instances at `sourceElementId` and activate a new element instance for each terminated one at `targetElementId`. The new element instances are created in the parent scope of the source element instances. ```csharp public sealed class ProcessInstanceModificationMoveBatchOperationInstruction ``` | Property | Type | Description | | ----------------- | ----------- | ---------------------- | | `SourceElementId` | `ElementId` | The source element ID. | | `TargetElementId` | `ElementId` | The target element ID. | ## ProcessInstanceModificationMoveInstruction Instruction describing a move operation. This instruction will terminate active element instances based on the sourceElementInstruction and activate a new element instance for each terminated one at targetElementId. Note that, for multi-instance activities, only the multi-instance body instances will activate new element instances at the target id. ```csharp public sealed class ProcessInstanceModificationMoveInstruction ``` | Property | Type | Description | | -------------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `SourceElementInstruction` | `SourceElementInstruction` | Defines the source element identifier for the move instruction. It can either be a sourceElementId, or sourceElementInstanceKey. | | `TargetElementId` | `ElementId` | The target element id. | | `AncestorScopeInstruction` | `AncestorScopeInstruction` | Defines the ancestor scope for the created element instances. The default behavior resembles a "direct" scope instruction with an `ancestorElementInstanceKey` of `"-1"`. | | `VariableInstructions` | `List` | Instructions describing which variables to create or update. | ## ProcessInstanceModificationTerminateByIdInstruction Instruction describing which elements to terminate. The element instances are determined at runtime by the given id. ```csharp public sealed class ProcessInstanceModificationTerminateByIdInstruction : ProcessInstanceModificationTerminateInstruction ``` | Property | Type | Description | | ----------- | ----------- | ------------------------------------------------------------------------------------- | | `ElementId` | `ElementId` | The id of the elements to terminate. The element instances are determined at runtime. | ## ProcessInstanceModificationTerminateByKeyInstruction Instruction providing the key of the element instance to terminate. ```csharp public sealed class ProcessInstanceModificationTerminateByKeyInstruction : ProcessInstanceModificationTerminateInstruction ``` | Property | Type | Description | | -------------------- | -------------------- | --------------------------------------------- | | `ElementInstanceKey` | `ElementInstanceKey` | The key of the element instance to terminate. | ## ProcessInstanceModificationTerminateInstruction Instruction describing which elements to terminate. ```csharp public abstract class ProcessInstanceModificationTerminateInstruction ``` ## ProcessInstanceReference ```csharp public sealed class ProcessInstanceReference ``` | Property | Type | Description | | ---------------------- | ---------------------- | ---------------------------------------- | | `ProcessDefinitionKey` | `ProcessDefinitionKey` | The key of the process definition. | | `ProcessInstanceKey` | `ProcessInstanceKey` | The key of the created process instance. | ## ProcessInstanceResult Process instance search response item. ```csharp public sealed class ProcessInstanceResult ``` | Property | Type | Description | | ----------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ProcessDefinitionId` | `ProcessDefinitionId` | Id of a process definition, from the model. Only ids of process definitions that are deployed are useful. | | `ProcessDefinitionName` | `String` | The process definition name. | | `ProcessDefinitionVersion` | `Int32` | The process definition version. | | `ProcessDefinitionVersionTag` | `String` | The process definition version tag. | | `StartDate` | `DateTimeOffset` | The start time of the process instance. | | `EndDate` | `Nullable` | The completion or termination time of the process instance. | | `State` | `ProcessInstanceStateEnum` | Process instance states | | `HasIncident` | `Boolean` | Whether this process instance has a related incident or not. | | `TenantId` | `TenantId` | The unique identifier of the tenant. | | `ProcessInstanceKey` | `ProcessInstanceKey` | The key of this process instance. | | `ProcessDefinitionKey` | `ProcessDefinitionKey` | The process definition key. | | `ParentProcessInstanceKey` | `Nullable` | The parent process instance key. | | `ParentElementInstanceKey` | `Nullable` | The parent element instance key. | | `RootProcessInstanceKey` | `Nullable` | The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. | | `Tags` | `List` | List of tags. Tags need to start with a letter; then alphanumerics, `_`, `-`, `:`, or `.`; length ≤ 100. | | `BusinessId` | `Nullable` | The business id associated with this process instance. | ## ProcessInstanceSearchQuery Process instance search request. ```csharp public sealed class ProcessInstanceSearchQuery ``` | Property | Type | Description | | -------- | --------------------------------------------- | ------------------------------------ | | `Sort` | `List` | Sort field criteria. | | `Filter` | `ProcessInstanceFilter` | The process instance search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## ProcessInstanceSearchQueryResult Process instance search response. ```csharp public sealed class ProcessInstanceSearchQueryResult ``` | Property | Type | Description | | -------- | ----------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching process instances. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## ProcessInstanceSearchQuerySortRequest ```csharp public sealed class ProcessInstanceSearchQuerySortRequest ``` | Property | Type | Description | | -------- | -------------------------------------------- | --------------------------------------------- | | `Field` | `ProcessInstanceSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## ProcessInstanceSequenceFlowResult Process instance sequence flow result. ```csharp public sealed class ProcessInstanceSequenceFlowResult ``` | Property | Type | Description | | ------------------------ | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `SequenceFlowId` | `String` | The sequence flow id. | | `ProcessInstanceKey` | `ProcessInstanceKey` | The key of this process instance. | | `RootProcessInstanceKey` | `Nullable` | The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. | | `ProcessDefinitionKey` | `ProcessDefinitionKey` | The process definition key. | | `ProcessDefinitionId` | `ProcessDefinitionId` | The process definition id. | | `ElementId` | `ElementId` | The element id for this sequence flow, as provided in the BPMN process. | | `TenantId` | `TenantId` | The unique identifier of the tenant. | ## ProcessInstanceSequenceFlowsQueryResult Process instance sequence flows query response. ```csharp public sealed class ProcessInstanceSequenceFlowsQueryResult ``` | Property | Type | Description | | -------- | ----------------------------------------- | ------------------- | | `Items` | `List` | The sequence flows. | ## ProcessInstanceStateExactMatch Matches the value exactly. ```csharp public readonly record struct ProcessInstanceStateExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## ProcessInstanceStateFilterProperty ProcessInstanceStateEnum property with full advanced search capabilities. ```csharp public sealed class ProcessInstanceStateFilterProperty ``` | Property | Type | Description | | -------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## ResourceFilter Resource search filter. ```csharp public sealed class ResourceFilter ``` | Property | Type | Description | | --------------- | ----------------------------- | -------------------------------- | | `ResourceKey` | `ResourceKeyFilterProperty` | The key for this resource. | | `ResourceName` | `StringFilterProperty` | Resource name of this resource. | | `ResourceId` | `StringFilterProperty` | Resource ID of this resource. | | `Version` | `IntegerFilterProperty` | Version of this resource. | | `VersionTag` | `StringFilterProperty` | Version tag of this resource. | | `DeploymentKey` | `DeploymentKeyFilterProperty` | Deployment key of this resource. | | `TenantId` | `Nullable` | Tenant ID of this resource. | ## ResourceKeyExactMatch Matches the value exactly. ```csharp public readonly record struct ResourceKeyExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## ResourceKeyFilterProperty ResourceKey property with full advanced search capabilities. ```csharp public sealed class ResourceKeyFilterProperty ``` | Property | Type | Description | | -------- | ----------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## ResourceResult ```csharp public sealed class ResourceResult ``` | Property | Type | Description | | -------------- | ------------- | ------------------------------------------------------ | | `ResourceName` | `String` | The resource name from which this resource was parsed. | | `Version` | `Int32` | The assigned resource version. | | `VersionTag` | `String` | The version tag of this resource. | | `ResourceId` | `String` | The resource ID of this resource. | | `TenantId` | `TenantId` | The tenant ID of this resource. | | `ResourceKey` | `ResourceKey` | The unique key of this resource. | ## ResourceSearchQuery ```csharp public sealed class ResourceSearchQuery ``` | Property | Type | Description | | -------- | -------------------------------------- | ---------------------------- | | `Sort` | `List` | Sort field criteria. | | `Filter` | `ResourceFilter` | The resource search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## ResourceSearchQueryResult ```csharp public sealed class ResourceSearchQueryResult ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching resources. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## ResourceSearchQuerySortRequest ```csharp public sealed class ResourceSearchQuerySortRequest ``` | Property | Type | Description | | -------- | ------------------------------------- | --------------------------------------------- | | `Field` | `ResourceSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## RetryDecision ```csharp public readonly record struct RetryDecision : IEquatable ``` | Property | Type | Description | | ----------- | --------- | ----------- | | `Retryable` | `Boolean` | | | `Reason` | `String` | | ## RoleClientResult ```csharp public sealed class RoleClientResult ``` | Property | Type | Description | | ---------- | ---------- | --------------------- | | `ClientId` | `ClientId` | The ID of the client. | ## RoleClientSearchQueryRequest ```csharp public sealed class RoleClientSearchQueryRequest ``` | Property | Type | Description | | -------- | ---------------------------------------- | -------------------- | | `Sort` | `List` | Sort field criteria. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## RoleClientSearchQuerySortRequest ```csharp public sealed class RoleClientSearchQuerySortRequest ``` | Property | Type | Description | | -------- | --------------------------------------- | --------------------------------------------- | | `Field` | `RoleClientSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## RoleClientSearchResult ```csharp public sealed class RoleClientSearchResult ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching clients. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## RoleCreateRequest ```csharp public sealed class RoleCreateRequest ``` | Property | Type | Description | | ------------- | -------- | --------------------------------- | | `RoleId` | `RoleId` | The ID of the new role. | | `Name` | `String` | The display name of the new role. | | `Description` | `String` | The description of the new role. | ## RoleCreateResult ```csharp public sealed class RoleCreateResult ``` | Property | Type | Description | | ------------- | -------- | ------------------------------------- | | `RoleId` | `RoleId` | The ID of the created role. | | `Name` | `String` | The display name of the created role. | | `Description` | `String` | The description of the created role. | ## RoleFilter Role filter request ```csharp public sealed class RoleFilter ``` | Property | Type | Description | | -------- | ------------------ | ----------------------------- | | `RoleId` | `Nullable` | The role ID search filters. | | `Name` | `String` | The role name search filters. | ## RoleGroupResult ```csharp public sealed class RoleGroupResult ``` | Property | Type | Description | | --------- | --------- | -------------------- | | `GroupId` | `GroupId` | The id of the group. | ## RoleGroupSearchQueryRequest ```csharp public sealed class RoleGroupSearchQueryRequest ``` | Property | Type | Description | | -------- | --------------------------------------- | -------------------- | | `Sort` | `List` | Sort field criteria. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## RoleGroupSearchQuerySortRequest ```csharp public sealed class RoleGroupSearchQuerySortRequest ``` | Property | Type | Description | | -------- | -------------------------------------- | --------------------------------------------- | | `Field` | `RoleGroupSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## RoleGroupSearchResult ```csharp public sealed class RoleGroupSearchResult ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching groups. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## RoleId The unique identifier of a role. ```csharp public readonly record struct RoleId : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## RoleMappingRuleSearchResult ```csharp public sealed class RoleMappingRuleSearchResult ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching mapping rules. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## RoleResult Role search response item. ```csharp public sealed class RoleResult ``` | Property | Type | Description | | ------------- | -------- | ---------------------------- | | `Name` | `String` | The role name. | | `RoleId` | `RoleId` | The role id. | | `Description` | `String` | The description of the role. | ## RoleSearchQueryRequest Role search request. ```csharp public sealed class RoleSearchQueryRequest ``` | Property | Type | Description | | -------- | ---------------------------------- | ------------------------ | | `Sort` | `List` | Sort field criteria. | | `Filter` | `RoleFilter` | The role search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## RoleSearchQueryResult Role search response. ```csharp public sealed class RoleSearchQueryResult ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching roles. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## RoleSearchQuerySortRequest ```csharp public sealed class RoleSearchQuerySortRequest ``` | Property | Type | Description | | -------- | --------------------------------- | --------------------------------------------- | | `Field` | `RoleSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## RoleUpdateRequest ```csharp public sealed class RoleUpdateRequest ``` | Property | Type | Description | | ------------- | -------- | --------------------------------- | | `Name` | `String` | The display name of the new role. | | `Description` | `String` | The description of the new role. | ## RoleUpdateResult ```csharp public sealed class RoleUpdateResult ``` | Property | Type | Description | | ------------- | -------- | ------------------------------------- | | `Name` | `String` | The display name of the updated role. | | `Description` | `String` | The description of the updated role. | | `RoleId` | `RoleId` | The ID of the updated role. | ## RoleUserResult ```csharp public sealed class RoleUserResult ``` | Property | Type | Description | | ---------- | ---------- | -------------------------- | | `Username` | `Username` | The unique name of a user. | ## RoleUserSearchQueryRequest ```csharp public sealed class RoleUserSearchQueryRequest ``` | Property | Type | Description | | -------- | -------------------------------------- | -------------------- | | `Sort` | `List` | Sort field criteria. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## RoleUserSearchQuerySortRequest ```csharp public sealed class RoleUserSearchQuerySortRequest ``` | Property | Type | Description | | -------- | ------------------------------------- | --------------------------------------------- | | `Field` | `RoleUserSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## RoleUserSearchResult ```csharp public sealed class RoleUserSearchResult ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching users. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## ScopeKeyExactMatch Matches the value exactly. ```csharp public readonly record struct ScopeKeyExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## ScopeKeyFilterProperty ScopeKey property with full advanced search capabilities. Filter by the key of the element instance or process instance that defines the scope of a variable. ```csharp public sealed class ScopeKeyFilterProperty ``` | Property | Type | Description | | -------- | -------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## SearchQueryPageRequest Pagination criteria. Can use offset-based pagination (from/limit) OR cursor-based pagination (after/before + limit), but not both. ```csharp public abstract class SearchQueryPageRequest ``` ## SearchQueryPageResponse Pagination information about the search results. ```csharp public sealed class SearchQueryPageResponse ``` | Property | Type | Description | | ------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `TotalItems` | `Int64` | Total items matching the criteria. | | `HasMoreTotalItems` | `Boolean` | Indicates whether the `totalItems` value has been capped due to system limits. When true, `totalItems` is a lower bound and the actual number of matching items is greater than the reported value. | | `StartCursor` | `Nullable` | The cursor value for getting the previous page of results. Use this in the `before` field of an ensuing request. | | `EndCursor` | `Nullable` | The cursor value for getting the next page of results. Use this in the `after` field of an ensuing request. | ## SearchQueryRequest ```csharp public sealed class SearchQueryRequest ``` | Property | Type | Description | | -------- | ------------------------ | -------------------- | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## SearchQueryResponse ```csharp public sealed class SearchQueryResponse ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## SetVariableRequest ```csharp public sealed class SetVariableRequest ``` | Property | Type | Description | | -------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Variables` | `Object` | JSON object representing the variables to set in the element’s scope. | | `Local` | `Nullable` | If set to `true`, the variables are merged strictly into the local scope (as specified by the `elementInstanceKey`). Otherwise, the variables are propagated to upper scopes and set at the outermost one. Let's consider the following example: There are two scopes '1' and '2'. Scope '1' is the parent scope of '2'. The effective variables of the scopes are: 1 => { "foo" : 2 } 2 => { "bar" : 1 } An update request with elementInstanceKey as '2', variables { "foo": 5 }, and local set to `true` leaves scope '1' unchanged and adjusts scope '2' to { "bar": 1, "foo": 5 }. By default, with local set to `false`, scope '1' will be { "foo": 5 } and scope '2' will be { "bar": 1 }. | | `OperationReference` | `Nullable` | A reference key chosen by the user that will be part of all records resulting from this operation. Must be > 0 if provided. | ## SignalBroadcastRequest ```csharp public sealed class SignalBroadcastRequest : ITenantIdSettable ``` | Property | Type | Description | | ------------ | -------------------- | ------------------------------------------ | | `SignalName` | `String` | The name of the signal to broadcast. | | `Variables` | `Object` | The signal variables as a JSON object. | | `TenantId` | `Nullable` | The ID of the tenant that owns the signal. | ## SignalBroadcastResult ```csharp public sealed class SignalBroadcastResult ``` | Property | Type | Description | | ----------- | ----------- | ----------------------------------------------- | | `TenantId` | `TenantId` | The tenant ID of the signal that was broadcast. | | `SignalKey` | `SignalKey` | The key of the broadcasted signal. | ## SourceElementIdInstruction Defines an instruction with a sourceElementId. The move instruction with this sourceType will terminate all active element instances with the sourceElementId and activate a new element instance for each terminated one at targetElementId. ```csharp public sealed class SourceElementIdInstruction : SourceElementInstruction ``` | Property | Type | Description | | ----------------- | ----------- | ------------------------------------------------------ | | `SourceElementId` | `ElementId` | The id of the source element for the move instruction. | ## SourceElementInstanceKeyInstruction Defines an instruction with a sourceElementInstanceKey. The move instruction with this sourceType will terminate one active element instance with the sourceElementInstanceKey and activate a new element instance at targetElementId. ```csharp public sealed class SourceElementInstanceKeyInstruction : SourceElementInstruction ``` | Property | Type | Description | | -------------------------- | -------------------- | --------------------------------------------------------- | | `SourceElementInstanceKey` | `ElementInstanceKey` | The source element instance key for the move instruction. | ## SourceElementInstruction Defines the source element identifier for the move instruction. It can either be a sourceElementId, or sourceElementInstanceKey. ```csharp public abstract class SourceElementInstruction ``` ## StartCursor The start cursor in a search query result set. ```csharp public readonly record struct StartCursor : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## StatusMetric Metric for a single job status. ```csharp public sealed class StatusMetric ``` | Property | Type | Description | | --------------- | -------------------------- | ------------------------------------------------------ | | `Count` | `Int64` | Number of jobs in this status. | | `LastUpdatedAt` | `Nullable` | ISO 8601 timestamp of the last update for this status. | ## StopResult Result of a call. ```csharp public readonly record struct StopResult : IEquatable ``` | Property | Type | Description | | --------------- | --------- | ------------------------------------------------------------- | | `RemainingJobs` | `Int32` | Number of jobs still in-flight when stop completed. | | `TimedOut` | `Boolean` | Whether the grace period was exceeded with jobs still active. | ## StringFilterProperty String property with full advanced search capabilities. ```csharp public sealed class StringFilterProperty ``` | Property | Type | Description | | -------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `String` | Checks for equality with the provided value. | | `Neq` | `String` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## SystemConfigurationResponse Envelope for all system configuration sections. Each property represents a feature area. ```csharp public sealed class SystemConfigurationResponse ``` | Property | Type | Description | | ---------------- | ------------------------------------- | -------------------------------------------------------------- | | `JobMetrics` | `JobMetricsConfigurationResponse` | Configuration for job metrics collection and export. | | `Components` | `ComponentsConfigurationResponse` | Configuration for active Camunda components in the deployment. | | `Deployment` | `DeploymentConfigurationResponse` | Configuration for deployment characteristics. | | `Authentication` | `AuthenticationConfigurationResponse` | Configuration for authentication and session management. | | `Cloud` | `CloudConfigurationResponse` | Configuration for SaaS/cloud-specific settings. | ## Tag A tag. Needs to start with a letter; then alphanumerics, `_`, `-`, `:`, or `.`; length ≤ 100. ```csharp public readonly record struct Tag : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## TenantClientResult ```csharp public sealed class TenantClientResult ``` | Property | Type | Description | | ---------- | ---------- | --------------------- | | `ClientId` | `ClientId` | The ID of the client. | ## TenantClientSearchQueryRequest ```csharp public sealed class TenantClientSearchQueryRequest ``` | Property | Type | Description | | -------- | ------------------------------------------ | -------------------- | | `Sort` | `List` | Sort field criteria. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## TenantClientSearchQuerySortRequest ```csharp public sealed class TenantClientSearchQuerySortRequest ``` | Property | Type | Description | | -------- | ----------------------------------------- | --------------------------------------------- | | `Field` | `TenantClientSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## TenantClientSearchResult ```csharp public sealed class TenantClientSearchResult ``` | Property | Type | Description | | -------- | -------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching clients. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## TenantCreateRequest ```csharp public sealed class TenantCreateRequest ``` | Property | Type | Description | | ------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `TenantId` | `TenantId` | The unique ID for the tenant. Must be 31 characters or less and match `^[\w.-]{1,31}$` (word characters, `.`, `-`). The literal `` is also accepted as the default-tenant alias. | | `Name` | `String` | The name of the tenant. | | `Description` | `String` | The description of the tenant. | ## TenantCreateResult ```csharp public sealed class TenantCreateResult ``` | Property | Type | Description | | ------------- | ---------- | -------------------------------------------- | | `TenantId` | `TenantId` | The unique identifier of the created tenant. | | `Name` | `String` | The name of the tenant. | | `Description` | `String` | The description of the tenant. | ## TenantFilter Tenant filter request ```csharp public sealed class TenantFilter ``` | Property | Type | Description | | ---------- | -------------------- | ------------------------------------ | | `TenantId` | `Nullable` | The unique identifier of the tenant. | | `Name` | `String` | The name of the tenant. | ## TenantGroupResult ```csharp public sealed class TenantGroupResult ``` | Property | Type | Description | | --------- | --------- | ------------- | | `GroupId` | `GroupId` | The group ID. | ## TenantGroupSearchQueryRequest ```csharp public sealed class TenantGroupSearchQueryRequest ``` | Property | Type | Description | | -------- | ----------------------------------------- | -------------------- | | `Sort` | `List` | Sort field criteria. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## TenantGroupSearchQuerySortRequest ```csharp public sealed class TenantGroupSearchQuerySortRequest ``` | Property | Type | Description | | -------- | ---------------------------------------- | --------------------------------------------- | | `Field` | `TenantGroupSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## TenantGroupSearchResult ```csharp public sealed class TenantGroupSearchResult ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching groups. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## TenantId The unique identifier of the tenant. ```csharp public readonly record struct TenantId : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## TenantMappingRuleSearchResult ```csharp public sealed class TenantMappingRuleSearchResult ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching mapping rules. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## TenantResult Tenant search response item. ```csharp public sealed class TenantResult ``` | Property | Type | Description | | ------------- | ---------- | ------------------------------------ | | `Name` | `String` | The tenant name. | | `TenantId` | `TenantId` | The unique identifier of the tenant. | | `Description` | `String` | The tenant description. | ## TenantRoleSearchResult ```csharp public sealed class TenantRoleSearchResult ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching roles. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## TenantSearchQueryRequest Tenant search request ```csharp public sealed class TenantSearchQueryRequest ``` | Property | Type | Description | | -------- | ------------------------------------ | -------------------------- | | `Sort` | `List` | Sort field criteria. | | `Filter` | `TenantFilter` | The tenant search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## TenantSearchQueryResult Tenant search response. ```csharp public sealed class TenantSearchQueryResult ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching tenants. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## TenantSearchQuerySortRequest ```csharp public sealed class TenantSearchQuerySortRequest ``` | Property | Type | Description | | -------- | ----------------------------------- | --------------------------------------------- | | `Field` | `TenantSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## TenantUpdateRequest ```csharp public sealed class TenantUpdateRequest ``` | Property | Type | Description | | ------------- | -------- | ---------------------------------- | | `Name` | `String` | The new name of the tenant. | | `Description` | `String` | The new description of the tenant. | ## TenantUpdateResult ```csharp public sealed class TenantUpdateResult ``` | Property | Type | Description | | ------------- | ---------- | -------------------------------------------- | | `TenantId` | `TenantId` | The unique identifier of the updated tenant. | | `Name` | `String` | The name of the tenant. | | `Description` | `String` | The description of the tenant. | ## TenantUserResult ```csharp public sealed class TenantUserResult ``` | Property | Type | Description | | ---------- | ---------- | -------------------------- | | `Username` | `Username` | The unique name of a user. | ## TenantUserSearchQueryRequest ```csharp public sealed class TenantUserSearchQueryRequest ``` | Property | Type | Description | | -------- | ---------------------------------------- | -------------------- | | `Sort` | `List` | Sort field criteria. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## TenantUserSearchQuerySortRequest ```csharp public sealed class TenantUserSearchQuerySortRequest ``` | Property | Type | Description | | -------- | --------------------------------------- | --------------------------------------------- | | `Field` | `TenantUserSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## TenantUserSearchResult ```csharp public sealed class TenantUserSearchResult ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching users. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## TlsConfig TLS / mTLS configuration for custom certificates. ```csharp public sealed class TlsConfig ``` | Property | Type | Description | | --------------- | -------- | --------------------------------------------------- | | `Cert` | `String` | Inline PEM client certificate (overrides CertPath). | | `CertPath` | `String` | Path to PEM client certificate file. | | `Key` | `String` | Inline PEM client private key (overrides KeyPath). | | `KeyPath` | `String` | Path to PEM client private key file. | | `Ca` | `String` | Inline PEM CA bundle (overrides CaPath). | | `CaPath` | `String` | Path to PEM CA certificate bundle file. | | `KeyPassphrase` | `String` | Passphrase for an encrypted private key. | ## TopologyResponse The response of a topology request. ```csharp public sealed class TopologyResponse ``` | Property | Type | Description | | ----------------------- | ------------------ | ------------------------------------------------------- | | `Brokers` | `List` | A list of brokers that are part of this cluster. | | `ClusterId` | `String` | The cluster Id. | | `ClusterSize` | `Int32` | The number of brokers in the cluster. | | `PartitionsCount` | `Int32` | The number of partitions are spread across the cluster. | | `ReplicationFactor` | `Int32` | The configured replication factor for this cluster. | | `GatewayVersion` | `String` | The version of the Zeebe Gateway. | | `LastCompletedChangeId` | `String` | ID of the last completed change | ## TypedVariables Extension methods for deserializing Camunda variable and custom header payloads from untyped object properties into strongly-typed DTOs. Camunda API responses return variables and customHeaders as object properties which, at runtime, are values. These extensions let you opt in to typed deserialization: // Define your domain DTO public record OrderVars(string OrderId, decimal Amount); // Deserialize variables from a process instance result var result = await client.CreateProcessInstanceAsync( new ProcessInstanceCreationInstructionById { ProcessDefinitionId = ProcessDefinitionId.AssumeExists("order-process"), Variables = new OrderVars("ord-123", 99.99m), // input: just assign your DTO }); var vars = result.Variables.DeserializeAs<OrderVars>(); // output: typed extraction For input (sending variables), simply assign your DTO to the Variables property — System.Text.Json serializes the runtime type automatically. For output (receiving variables), call on the Variables or CustomHeaders property to deserialize the underlying into your DTO type. ```csharp public static class TypedVariables ``` ## UpdateClusterVariableRequest ```csharp public sealed class UpdateClusterVariableRequest ``` | Property | Type | Description | | -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------- | | `Value` | `Object` | The new value of the cluster variable. Can be any JSON object or primitive value. Will be serialized as a JSON string in responses. | ## UpdateGlobalTaskListenerRequest ```csharp public sealed class UpdateGlobalTaskListenerRequest ``` | Property | Type | Description | | ---------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | `EventTypes` | `List` | List of user task event types that trigger the listener. | | `Type` | `String` | The name of the job type, used as a reference to specify which job workers request the respective listener job. | | `Retries` | `Nullable` | Number of retries for the listener job. | | `AfterNonGlobal` | `Nullable` | Whether the listener should run after model-level listeners. | | `Priority` | `Nullable` | The priority of the listener. Higher priority listeners are executed before lower priority ones. | ## UsageMetricsResponse ```csharp public sealed class UsageMetricsResponse ``` | Property | Type | Description | | ------------------- | -------------------- | ------------------------------------------------------------------------------------------------- | | `ActiveTenants` | `Int64` | The amount of active tenants. | | `Tenants` | `Dictionary` | The usage metrics by tenants. Only available if request `withTenants` query parameter was `true`. | | `ProcessInstances` | `Int64` | The amount of created root process instances. | | `DecisionInstances` | `Int64` | The amount of executed decision instances. | | `Assignees` | `Int64` | The amount of unique active task users. | ## UsageMetricsResponseItem ```csharp public sealed class UsageMetricsResponseItem ``` | Property | Type | Description | | ------------------- | ------- | --------------------------------------------- | | `ProcessInstances` | `Int64` | The amount of created root process instances. | | `DecisionInstances` | `Int64` | The amount of executed decision instances. | | `Assignees` | `Int64` | The amount of unique active task users. | ## UseSourceParentKeyInstruction Instructs the engine to use the source's direct parent key as the ancestor scope key for the target element. This is a simpler alternative to `inferred` that skips hierarchy traversal and directly uses the source's parent key. This is useful when the source and target elements are siblings within the same flow scope. ```csharp public sealed class UseSourceParentKeyInstruction : AncestorScopeInstruction ``` ## UserCreateResult ```csharp public sealed class UserCreateResult ``` | Property | Type | Description | | ---------- | ---------- | --------------------------------- | | `Username` | `Username` | The username of the created user. | | `Name` | `String` | The name of the user. | | `Email` | `String` | The email of the user. | ## UserFilter User search filter. ```csharp public sealed class UserFilter ``` | Property | Type | Description | | ---------- | ---------------------- | ------------------------- | | `Username` | `StringFilterProperty` | The username of the user. | | `Name` | `StringFilterProperty` | The name of the user. | | `Email` | `StringFilterProperty` | The email of the user. | ## UserRequest ```csharp public sealed class UserRequest ``` | Property | Type | Description | | ---------- | ---------- | ----------------------------- | | `Password` | `String` | The password of the user. | | `Username` | `Username` | The username of the new user. | | `Name` | `String` | The name of the user. | | `Email` | `String` | The email of the user. | ## UserResult ```csharp public sealed class UserResult ``` | Property | Type | Description | | ---------- | ---------- | ------------------------- | | `Username` | `Username` | The username of the user. | | `Name` | `String` | The name of the user. | | `Email` | `String` | The email of the user. | ## UserSearchQueryRequest ```csharp public sealed class UserSearchQueryRequest ``` | Property | Type | Description | | -------- | ---------------------------------- | ------------------------ | | `Sort` | `List` | Sort field criteria. | | `Filter` | `UserFilter` | The user search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## UserSearchQuerySortRequest ```csharp public sealed class UserSearchQuerySortRequest ``` | Property | Type | Description | | -------- | --------------------------------- | --------------------------------------------- | | `Field` | `UserSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## UserSearchResult ```csharp public sealed class UserSearchResult ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching users. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## UserTaskAssignmentRequest ```csharp public sealed class UserTaskAssignmentRequest ``` | Property | Type | Description | | --------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Assignee` | `String` | The assignee for the user task. The assignee must not be empty or `null`. | | `AllowOverride` | `Nullable` | By default, the task is reassigned if it was already assigned. Set this to `false` to return an error in such cases. The task must then first be unassigned to be assigned again. Use this when you have users picking from group task queues to prevent race conditions. | | `Action` | `String` | A custom action value that will be accessible from user task events resulting from this endpoint invocation. If not provided, it will default to "assign". | ## UserTaskAuditLogFilter The user task audit log search filters. ```csharp public sealed class UserTaskAuditLogFilter ``` | Property | Type | Description | | --------------- | --------------------------------- | ------------------------------------------- | | `OperationType` | `OperationTypeFilterProperty` | The audit log operation type search filter. | | `Result` | `AuditLogResultFilterProperty` | The audit log result search filter. | | `Timestamp` | `DateTimeFilterProperty` | The audit log timestamp filter. | | `ActorType` | `AuditLogActorTypeFilterProperty` | The actor type search filter. | | `ActorId` | `StringFilterProperty` | The actor ID search filter. | ## UserTaskAuditLogSearchQueryRequest User task search query request. ```csharp public sealed class UserTaskAuditLogSearchQueryRequest ``` | Property | Type | Description | | -------- | -------------------------------------- | --------------------------------------- | | `Sort` | `List` | Sort field criteria. | | `Filter` | `UserTaskAuditLogFilter` | The user task audit log search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## UserTaskCompletionRequest ```csharp public sealed class UserTaskCompletionRequest ``` | Property | Type | Description | | ----------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `Variables` | `Object` | The variables to complete the user task with. | | `Action` | `String` | A custom action value that will be accessible from user task events resulting from this endpoint invocation. If not provided, it will default to "complete". | ## UserTaskEffectiveVariableSearchQueryRequest User task effective variable search query request. Uses offset-based pagination only. ```csharp public sealed class UserTaskEffectiveVariableSearchQueryRequest ``` | Property | Type | Description | | -------- | ---------------------------------------------- | -------------------------------------- | | `Page` | `OffsetPagination` | Pagination parameters. | | `Sort` | `List` | Sort field criteria. | | `Filter` | `UserTaskVariableFilter` | The user task variable search filters. | ## UserTaskFilter User task filter request. ```csharp public sealed class UserTaskFilter ``` | Property | Type | Description | | -------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | | `State` | `UserTaskStateFilterProperty` | The user task state. | | `Assignee` | `StringFilterProperty` | The assignee of the user task. | | `Priority` | `IntegerFilterProperty` | The priority of the user task. | | `ElementId` | `Nullable` | The element ID of the user task. | | `Name` | `StringFilterProperty` | The task name. This only works for data created with 8.8 and onwards. Instances from prior versions don't contain this data and cannot be found. | | `CandidateGroup` | `StringFilterProperty` | The candidate group for this user task. | | `CandidateUser` | `StringFilterProperty` | The candidate user for this user task. | | `TenantId` | `StringFilterProperty` | Tenant ID of this user task. | | `ProcessDefinitionId` | `ProcessDefinitionIdFilterProperty` | The ID of the process definition. | | `CreationDate` | `DateTimeFilterProperty` | The user task creation date. | | `CompletionDate` | `DateTimeFilterProperty` | The user task completion date. | | `FollowUpDate` | `DateTimeFilterProperty` | The user task follow-up date. | | `DueDate` | `DateTimeFilterProperty` | The user task due date. | | `ProcessInstanceVariables` | `List` | The variables of the process instance. | | `LocalVariables` | `List` | The local variables of the user task. | | `UserTaskKey` | `Nullable` | The key for this user task. | | `ProcessDefinitionKey` | `ProcessDefinitionKeyFilterProperty` | The key of the process definition. | | `ProcessInstanceKey` | `ProcessInstanceKeyFilterProperty` | The key of the process instance. | | `ElementInstanceKey` | `Nullable` | The key of the element instance. | | `Tags` | `List` | List of tags. Tags need to start with a letter; then alphanumerics, `_`, `-`, `:`, or `.`; length ≤ 100. | ## UserTaskProperties Contains properties of a user task. ```csharp public sealed class UserTaskProperties ``` | Property | Type | Description | | ------------------- | ----------------------- | ------------------------------------------------------- | | `Action` | `String` | The action performed on the user task. | | `Assignee` | `String` | The user assigned to the task. | | `CandidateGroups` | `List` | The groups eligible to claim the task. | | `CandidateUsers` | `List` | The users eligible to claim the task. | | `ChangedAttributes` | `List` | The attributes that were changed in the task. | | `DueDate` | `String` | The due date of the user task in ISO 8601 format. | | `FollowUpDate` | `String` | The follow-up date of the user task in ISO 8601 format. | | `FormKey` | `Nullable` | The key of the form associated with the user task. | | `Priority` | `Nullable` | The priority of the user task. | | `UserTaskKey` | `Nullable` | The unique key identifying the user task. | ## UserTaskResult ```csharp public sealed class UserTaskResult ``` | Property | Type | Description | | -------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Name` | `String` | The name for this user task. | | `State` | `UserTaskStateEnum` | The state of the user task. Note: FAILED state is only for legacy job-worker-based tasks. | | `Assignee` | `String` | The assignee of the user task. | | `ElementId` | `ElementId` | The element ID of the user task. | | `CandidateGroups` | `List` | The candidate groups for this user task. | | `CandidateUsers` | `List` | The candidate users for this user task. | | `ProcessDefinitionId` | `ProcessDefinitionId` | The ID of the process definition. | | `CreationDate` | `DateTimeOffset` | The creation date of a user task. | | `CompletionDate` | `Nullable` | The completion date of a user task. | | `FollowUpDate` | `Nullable` | The follow date of a user task. | | `DueDate` | `Nullable` | The due date of a user task. | | `TenantId` | `TenantId` | The unique identifier of the tenant. | | `ExternalFormReference` | `String` | The external form reference. | | `ProcessDefinitionVersion` | `Int32` | The version of the process definition. | | `CustomHeaders` | `Dictionary` | Custom headers for the user task. | | `Priority` | `Int32` | The priority of a user task. The higher the value the higher the priority. | | `UserTaskKey` | `UserTaskKey` | The key of the user task. | | `ElementInstanceKey` | `ElementInstanceKey` | The key of the element instance. | | `ProcessName` | `String` | The name of the process definition. This is `null` if the process has no name defined. | | `ProcessDefinitionKey` | `ProcessDefinitionKey` | The key of the process definition. | | `ProcessInstanceKey` | `ProcessInstanceKey` | The key of the process instance. | | `RootProcessInstanceKey` | `Nullable` | The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. | | `FormKey` | `Nullable` | The key of the form. | | `Tags` | `List` | List of tags. Tags need to start with a letter; then alphanumerics, `_`, `-`, `:`, or `.`; length ≤ 100. | ## UserTaskSearchQuery User task search query request. ```csharp public sealed class UserTaskSearchQuery ``` | Property | Type | Description | | -------- | -------------------------------------- | ----------------------------- | | `Sort` | `List` | Sort field criteria. | | `Filter` | `UserTaskFilter` | The user task search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## UserTaskSearchQueryResult User task search query response. ```csharp public sealed class UserTaskSearchQueryResult ``` | Property | Type | Description | | -------- | ------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching user tasks. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## UserTaskSearchQuerySortRequest ```csharp public sealed class UserTaskSearchQuerySortRequest ``` | Property | Type | Description | | -------- | ------------------------------------- | --------------------------------------------- | | `Field` | `UserTaskSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## UserTaskStateExactMatch Matches the value exactly. ```csharp public readonly record struct UserTaskStateExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## UserTaskStateFilterProperty UserTaskStateEnum property with full advanced search capabilities. ```csharp public sealed class UserTaskStateFilterProperty ``` | Property | Type | Description | | -------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `Like` | `Nullable` | Checks if the property matches the provided like value. Supported wildcard characters are: _ `_`: matches zero, one, or multiple characters. * `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. | ## UserTaskUpdateRequest ```csharp public sealed class UserTaskUpdateRequest ``` | Property | Type | Description | | ----------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Changeset` | `Changeset` | JSON object with changed task attribute values. The following attributes can be adjusted with this endpoint, additional attributes will be ignored: _ `candidateGroups` - reset by providing an empty list _ `candidateUsers` - reset by providing an empty list _ `dueDate` - reset by providing an empty String _ `followUpDate` - reset by providing an empty String \* `priority` - minimum 0, maximum 100, default 50 Providing any of those attributes with a `null` value or omitting it preserves the persisted attribute's value. The assignee cannot be adjusted with this endpoint, use the Assign task endpoint. This ensures correct event emission for assignee changes. | | `Action` | `String` | A custom action value that will be accessible from user task events resulting from this endpoint invocation. If not provided, it will default to "update". | ## UserTaskVariableFilter The user task variable search filters. ```csharp public sealed class UserTaskVariableFilter ``` | Property | Type | Description | | -------- | ---------------------- | --------------------- | | `Name` | `StringFilterProperty` | Name of the variable. | ## UserTaskVariableSearchQueryRequest User task search query request. ```csharp public sealed class UserTaskVariableSearchQueryRequest ``` | Property | Type | Description | | -------- | ---------------------------------------------- | -------------------------------------- | | `Sort` | `List` | Sort field criteria. | | `Filter` | `UserTaskVariableFilter` | The user task variable search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## UserTaskVariableSearchQuerySortRequest ```csharp public sealed class UserTaskVariableSearchQuerySortRequest ``` | Property | Type | Description | | -------- | --------------------------------------------- | --------------------------------------------- | | `Field` | `UserTaskVariableSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## UserUpdateRequest ```csharp public sealed class UserUpdateRequest ``` | Property | Type | Description | | ---------- | -------- | -------------------------------------------------------------- | | `Password` | `String` | The password of the user. If blank, the password is unchanged. | | `Name` | `String` | The name of the user. | | `Email` | `String` | The email of the user. | ## UserUpdateResult ```csharp public sealed class UserUpdateResult ``` | Property | Type | Description | | ---------- | ---------- | --------------------------------- | | `Username` | `Username` | The username of the updated user. | | `Name` | `String` | The name of the user. | | `Email` | `String` | The email of the user. | ## Username The unique name of a user. ```csharp public readonly record struct Username : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## VariableFilter Variable filter request. ```csharp public sealed class VariableFilter ``` | Property | Type | Description | | -------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `Name` | `StringFilterProperty` | Name of the variable. | | `Value` | `StringFilterProperty` | The value of the variable. Variable values in filters need to be in serialized JSON format. For example, a variable with string value `myValue` can be found with the filter value `"myValue"`. Consider appropriate escaping for special characters in JSON strings when constructing filter values. | | `TenantId` | `Nullable` | Tenant ID of this variable. | | `IsTruncated` | `Nullable` | Whether the value is truncated or not. | | `VariableKey` | `VariableKeyFilterProperty` | The key for this variable. | | `ScopeKey` | `ScopeKeyFilterProperty` | The key of the scope that defines where this variable is directly defined. This can be a process instance key (for process-level variables) or an element instance key (for local variables scoped to tasks, subprocesses, gateways, events, etc.). Use this filter to find variables directly defined in specific scopes. Note that this does not include variables from parent scopes that would be visible through the scope hierarchy. | | `ProcessInstanceKey` | `ProcessInstanceKeyFilterProperty` | The key of the process instance of this variable. | ## VariableKeyExactMatch Matches the value exactly. ```csharp public readonly record struct VariableKeyExactMatch : ICamundaKey, IEquatable ``` | Property | Type | Description | | -------- | -------- | ---------------------------- | | `Value` | `String` | The underlying string value. | ## VariableKeyFilterProperty VariableKey property with full advanced search capabilities. ```csharp public sealed class VariableKeyFilterProperty ``` | Property | Type | Description | | -------- | ----------------------- | ----------------------------------------------------------- | | `Eq` | `Nullable` | Checks for equality with the provided value. | | `Neq` | `Nullable` | Checks for inequality with the provided value. | | `Exists` | `Nullable` | Checks if the current property exists. | | `In` | `List` | Checks if the property matches any of the provided values. | | `NotIn` | `List` | Checks if the property matches none of the provided values. | ## VariableResult Variable search response item. ```csharp public sealed class VariableResult ``` | Property | Type | Description | | ------------------------ | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Value` | `String` | Full value of this variable. | | `Name` | `String` | Name of this variable. | | `TenantId` | `TenantId` | Tenant ID of this variable. | | `VariableKey` | `VariableKey` | The key for this variable. | | `ScopeKey` | `ScopeKey` | The key of the scope where this variable is directly defined. For process-level variables, this is the process instance key. For local variables, this is the key of the specific element instance (task, subprocess, gateway, event, etc.) where the variable is directly defined. | | `ProcessInstanceKey` | `ProcessInstanceKey` | The key of the process instance of this variable. | | `RootProcessInstanceKey` | `Nullable` | The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. | ## VariableResultBase Variable response item. ```csharp public sealed class VariableResultBase ``` | Property | Type | Description | | ------------------------ | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Name` | `String` | Name of this variable. | | `TenantId` | `TenantId` | Tenant ID of this variable. | | `VariableKey` | `VariableKey` | The key for this variable. | | `ScopeKey` | `ScopeKey` | The key of the scope where this variable is directly defined. For process-level variables, this is the process instance key. For local variables, this is the key of the specific element instance (task, subprocess, gateway, event, etc.) where the variable is directly defined. | | `ProcessInstanceKey` | `ProcessInstanceKey` | The key of the process instance of this variable. | | `RootProcessInstanceKey` | `Nullable` | The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. | ## VariableSearchQuery Variable search query request. ```csharp public sealed class VariableSearchQuery ``` | Property | Type | Description | | -------- | -------------------------------------- | ---------------------------- | | `Sort` | `List` | Sort field criteria. | | `Filter` | `VariableFilter` | The variable search filters. | | `Page` | `SearchQueryPageRequest` | Pagination criteria. | ## VariableSearchQueryResult Variable search query response. ```csharp public sealed class VariableSearchQueryResult ``` | Property | Type | Description | | -------- | ---------------------------- | ------------------------------------------------ | | `Items` | `List` | The matching variables. | | `Page` | `SearchQueryPageResponse` | Pagination information about the search results. | ## VariableSearchQuerySortRequest ```csharp public sealed class VariableSearchQuerySortRequest ``` | Property | Type | Description | | -------- | ------------------------------------- | --------------------------------------------- | | `Field` | `VariableSearchQuerySortRequestField` | The field to sort by. | | `Order` | `Nullable` | The order in which to sort the related field. | ## VariableSearchResult Variable search response item. ```csharp public sealed class VariableSearchResult ``` | Property | Type | Description | | ------------------------ | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Value` | `String` | Value of this variable. Can be truncated. | | `IsTruncated` | `Boolean` | Whether the value is truncated or not. | | `Name` | `String` | Name of this variable. | | `TenantId` | `TenantId` | Tenant ID of this variable. | | `VariableKey` | `VariableKey` | The key for this variable. | | `ScopeKey` | `ScopeKey` | The key of the scope where this variable is directly defined. For process-level variables, this is the process instance key. For local variables, this is the key of the specific element instance (task, subprocess, gateway, event, etc.) where the variable is directly defined. | | `ProcessInstanceKey` | `ProcessInstanceKey` | The key of the process instance of this variable. | | `RootProcessInstanceKey` | `Nullable` | The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. | ## VariableValueFilterProperty ```csharp public sealed class VariableValueFilterProperty ``` | Property | Type | Description | | -------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Name` | `String` | Name of the variable. | | `Value` | `StringFilterProperty` | The value of the variable. Variable values in filters need to be in serialized JSON format. For example, a variable with string value `myValue` can be found with the filter value `"myValue"`. Consider appropriate escaping for special characters in JSON strings when constructing filter values. | ## WorkerDefaultsConfig ```csharp public sealed class WorkerDefaultsConfig ``` | Property | Type | Description | | ------------------------- | ------------------ | ----------- | | `JobTimeoutMs` | `Nullable` | | | `MaxConcurrentJobs` | `Nullable` | | | `PollTimeoutMs` | `Nullable` | | | `WorkerName` | `String` | | | `StartupJitterMaxSeconds` | `Nullable` | | --- ## Runtime :::caution Technical Preview The C# SDK is a **technical preview** available from Camunda 8.9. It will become fully supported in Camunda 8.10. Its API surface may change in future releases without following semver. ::: Runtime infrastructure types: job workers, backpressure management, eventual consistency polling, error handling, and key serialization. --- ## Authentication(Csharp-sdk) :::caution Technical Preview The C# SDK is a **technical preview** available from Camunda 8.9. It will become fully supported in Camunda 8.10. Its API surface may change in future releases without following semver. ::: - **OAuth** — Automatic token management with singleflight refresh, caching, and retry - **Basic** — HTTP Basic Authentication - **None** — No authentication (local development) Auth strategy is auto-detected from environment variables when not explicitly set. --- ## Configuration Reference :::caution Technical Preview The C# SDK is a **technical preview** available from Camunda 8.9. It will become fully supported in Camunda 8.10. Its API surface may change in future releases without following semver. ::: The SDK uses environment variables for configuration, matching the [JS SDK](https://github.com/camunda/orchestration-cluster-api-js) conventions: | Variable | Description | Default | | -------------------------------------- | --------------------------------------------------------------- | -------------------- | | `CAMUNDA_REST_ADDRESS` | Cluster REST API address | — | | `CAMUNDA_AUTH_STRATEGY` | `NONE`, `OAUTH`, or `BASIC` | Auto-detected | | `CAMUNDA_CLIENT_ID` | OAuth client ID | — | | `CAMUNDA_CLIENT_SECRET` | OAuth client secret | — | | `CAMUNDA_OAUTH_URL` | OAuth token endpoint | — | | `CAMUNDA_TOKEN_AUDIENCE` | OAuth audience | — | | `CAMUNDA_OAUTH_GRANT_TYPE` | OAuth grant type | `client_credentials` | | `CAMUNDA_OAUTH_SCOPE` | OAuth scope | — | | `CAMUNDA_OAUTH_TIMEOUT_MS` | OAuth token request timeout (ms) | `5000` | | `CAMUNDA_OAUTH_RETRY_MAX` | Max OAuth token fetch retries | `5` | | `CAMUNDA_OAUTH_RETRY_BASE_DELAY_MS` | OAuth retry base delay (ms) | `1000` | | `CAMUNDA_BASIC_AUTH_USERNAME` | Basic auth username | — | | `CAMUNDA_BASIC_AUTH_PASSWORD` | Basic auth password | — | | `CAMUNDA_DEFAULT_TENANT_ID` | Default tenant ID | `` | | `CAMUNDA_SDK_LOG_LEVEL` | Log level (`error`, `warn`, `info`, `debug`, `trace`, `silent`) | `error` | | `CAMUNDA_SDK_VALIDATION` | Validation mode (see below) | `req:none,res:none` | | `CAMUNDA_SDK_HTTP_RETRY_MAX_ATTEMPTS` | Total HTTP retry attempts (initial + retries) | `3` | | `CAMUNDA_SDK_HTTP_RETRY_BASE_DELAY_MS` | HTTP retry base backoff (ms) | `100` | | `CAMUNDA_SDK_HTTP_RETRY_MAX_DELAY_MS` | HTTP retry max backoff cap (ms) | `2000` | | `CAMUNDA_SDK_EVENTUAL_POLL_DEFAULT_MS` | Default eventual consistency poll interval (ms) | `500` | | `ZEEBE_REST_ADDRESS` | Alias for `CAMUNDA_REST_ADDRESS` | — | | `CAMUNDA_MTLS_CERT` | Inline PEM client certificate | — | | `CAMUNDA_MTLS_KEY` | Inline PEM client private key | — | | `CAMUNDA_MTLS_CA` | Inline PEM CA bundle | — | | `CAMUNDA_MTLS_CERT_PATH` | Path to client certificate (PEM) | — | | `CAMUNDA_MTLS_KEY_PATH` | Path to client private key (PEM) | — | | `CAMUNDA_MTLS_CA_PATH` | Path to CA bundle (PEM) | — | | `CAMUNDA_MTLS_KEY_PASSPHRASE` | Passphrase for encrypted private key | — | For backpressure configuration variables, see [Global Backpressure](resilience.md#global-backpressure-adaptive-concurrency). --- ## Creating a Process Instance :::caution Technical Preview The C# SDK is a **technical preview** available from Camunda 8.9. It will become fully supported in Camunda 8.10. Its API surface may change in future releases without following semver. ::: The recommended pattern is to obtain keys from a prior API response (e.g. a deployment) and pass them directly — no manual conversion needed: ```csharp using Camunda.Orchestration.Sdk; using var client = CamundaClient.Create(); var deployment = await client.DeployResourcesFromFilesAsync(["process.bpmn"]); var processKey = deployment.Processes[0].ProcessDefinitionKey; var result = await client.CreateProcessInstanceAsync( new ProcessInstanceCreationInstructionByKey { ProcessDefinitionKey = processKey, }); Console.WriteLine($"Process instance key: {result.ProcessInstanceKey}"); ``` If you need to restore a key from external storage (database, message queue, config file), wrap the raw value with the domain key constructor: ```csharp using Camunda.Orchestration.Sdk; using var client = CamundaClient.Create(); var storedKey = "2251799813685249"; // from a DB row or config var result = await client.CreateProcessInstanceAsync( new ProcessInstanceCreationInstructionByKey { ProcessDefinitionKey = ProcessDefinitionKey.AssumeExists(storedKey), }); Console.WriteLine($"Process instance key: {result.ProcessInstanceKey}"); ``` You can also start a process instance by BPMN process ID (which uses the latest deployed version): ```csharp var result = await client.CreateProcessInstanceAsync( new ProcessInstanceCreationInstructionById { ProcessDefinitionId = ProcessDefinitionId.AssumeExists("my-process-id"), }); ``` --- ## Deploying Resources :::caution Technical Preview The C# SDK is a **technical preview** available from Camunda 8.9. It will become fully supported in Camunda 8.10. Its API surface may change in future releases without following semver. ::: Deploy BPMN, DMN, or Form files from disk: ```csharp using Camunda.Orchestration.Sdk; using var client = CamundaClient.Create(); var result = await client.DeployResourcesFromFilesAsync(["process.bpmn", "decision.dmn"]); Console.WriteLine($"Deployment key: {result.DeploymentKey}"); foreach (var process in result.Processes) { Console.WriteLine($" Process: {process.ProcessDefinitionId} (key: {process.ProcessDefinitionKey})"); } ``` --- ## Installation :::caution Technical Preview The C# SDK is a **technical preview** available from Camunda 8.9. It will become fully supported in Camunda 8.10. Its API surface may change in future releases without following semver. ::: ```bash dotnet add package Camunda.Orchestration.Sdk ``` ## Versioning This SDK has a different release cadence from the Camunda server. Features and fixes land in the SDK during a server release. The major version of the SDK signals a 1:1 type coherence with the server API for a Camunda minor release. SDK version `n.y.z` -> server version `8.n`, so the type surface of SDK version 9.y.z matches the API surface of Camunda 8.9. Using a later SDK version, for example: SDK version 10.y.z with Camunda 8.9, means that the SDK contains additive surfaces that are not guaranteed at runtime, and the compiler cannot warn of unsupported operations. Using an earlier SDK version, for example: SDK version 9.y.z with Camunda 8.10, results in slightly degraded compiler reasoning: exhaustiveness checks cannot be guaranteed by the compiler for any extended surfaces (principally, enums with added members). In the vast majority of use-cases, this will not be an issue; but you should be aware that using the matching SDK major version for the server minor version provides the strongest compiler guarantees about runtime reliability. **Recommended approach**: - Check the [CHANGELOG](https://github.com/camunda/orchestration-cluster-api-csharp/releases). - As a sanity check during server version upgrade, rebuild applications with the matching SDK major version to identify any affected runtime surfaces. --- ## Job Workers :::caution Technical Preview The C# SDK is a **technical preview** available from Camunda 8.9. It will become fully supported in Camunda 8.10. Its API surface may change in future releases without following semver. ::: Job workers subscribe to a specific job type and process jobs as they become available. The worker handles polling, concurrent dispatch, auto-completion, and error handling. ## Basic Worker ```csharp using Camunda.Orchestration.Sdk; // Define input/output DTOs public record OrderOutput(bool Processed, string InvoiceNumber); using var client = CamundaClient.Create(); client.CreateJobWorker( new JobWorkerConfig { JobType = "process-order", JobTimeoutMs = 30_000, }, async (job, ct) => { var input = job.GetVariables(); var invoice = await ProcessOrder(input!, ct); // Return value auto-completes the job with these output variables return new OrderOutput(true, invoice); }); // Block until Ctrl+C using var cts = new CancellationTokenSource(); Console.CancelKeyPress += (_, e) => { e.Cancel = true; cts.Cancel(); }; await client.RunWorkersAsync(ct: cts.Token); ``` ## Handler Contract The handler return value determines the job outcome: | Handler behavior | Job outcome | | ----------------------------- | ----------------------------------------------------- | | Return `object` | Auto-complete with those variables | | Return `null` | Auto-complete with no variables | | Return `JobCompletionRequest` | Complete with structured result (corrections, denial) | | Throw `BpmnErrorException` | Trigger a BPMN error boundary event | | Throw `JobFailureException` | Fail with custom retries / back-off | | Throw any other exception | Auto-fail with `retries - 1` | ```csharp // BPMN error — caught by error boundary events in the process model throw new BpmnErrorException("INVALID_ORDER", "Order not found"); // Explicit failure with retry control throw new JobFailureException("Service unavailable", retries: 2, retryBackOffMs: 5000); ``` ## Job Corrections (User Task Listeners) When handling jobs from [user task listeners](../../components/concepts/user-task-listeners.md), you can return a `JobCompletionRequest` to apply corrections to the task or deny the action. Return a `JobCompletionRequest` from the handler instead of a plain variables object: ```csharp client.CreateJobWorker(config, async (job, ct) => { // Apply corrections to the user task return new JobCompletionRequest { Variables = new { reviewed = true }, Result = new JobResultUserTask { Corrections = new JobResultCorrections { Assignee = "new-assignee", Priority = 75, CandidateGroups = new List { "managers" }, }, }, }; }); ``` To deny the user task action (e.g. reject a completion): ```csharp client.CreateJobWorker(config, async (job, ct) => { return new JobCompletionRequest { Result = new JobResultUserTask { Denied = true, DeniedReason = "Missing required fields", }, }; }); ``` ## Void Handler (No Output Variables) For handlers that don't return output variables, use the void overload: ```csharp public record NotificationInput(string Message); client.CreateJobWorker(config, async (job, ct) => { await SendNotification(job.GetVariables()!, ct); // Auto-completes with no variables }); ``` ## Configuration | Property | Default | Description | | ------------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------- | | `JobType` | _(required)_ | BPMN task type to subscribe to | | `JobTimeoutMs` | _(env / required)_ | Job lock duration (ms). Falls back to `CAMUNDA_WORKER_TIMEOUT` env var. | | `MaxConcurrentJobs` | `10` | Max in-flight jobs per worker. Falls back to `CAMUNDA_WORKER_MAX_CONCURRENT_JOBS` env var, then `10`. | | `PollIntervalMs` | `500` | Delay between polls when idle | | `PollTimeoutMs` | `null` | Long-poll timeout (null = broker default). Falls back to `CAMUNDA_WORKER_REQUEST_TIMEOUT` env var. | | `FetchVariables` | `null` | Variable names to fetch (null = all) | | `WorkerName` | auto | Worker name for logging. Falls back to `CAMUNDA_WORKER_NAME` env var. | | `AutoStart` | `true` | Start polling on creation | | `StartupJitterMaxSeconds` | `0` | Max random delay (seconds) before first poll. Falls back to `CAMUNDA_WORKER_STARTUP_JITTER_MAX_SECONDS` env var. | ## Heritable Worker Defaults When running many workers with the same base configuration, you can set global defaults via environment variables. These apply to every worker created by the client unless the individual `JobWorkerConfig` explicitly overrides them. | Environment Variable | Config Property | Type | | ------------------------------------------- | ------------------------- | ------ | | `CAMUNDA_WORKER_TIMEOUT` | `JobTimeoutMs` | long | | `CAMUNDA_WORKER_MAX_CONCURRENT_JOBS` | `MaxConcurrentJobs` | int | | `CAMUNDA_WORKER_REQUEST_TIMEOUT` | `PollTimeoutMs` | long | | `CAMUNDA_WORKER_NAME` | `WorkerName` | string | | `CAMUNDA_WORKER_STARTUP_JITTER_MAX_SECONDS` | `StartupJitterMaxSeconds` | int | **Precedence:** explicit `JobWorkerConfig` value > environment variable > hardcoded default. ```bash export CAMUNDA_WORKER_TIMEOUT=30000 export CAMUNDA_WORKER_MAX_CONCURRENT_JOBS=8 export CAMUNDA_WORKER_NAME=order-service ``` ```csharp // Workers inherit timeout, concurrency, and name from environment client.CreateJobWorker( new JobWorkerConfig { JobType = "validate-order" }, async (job, ct) => null); client.CreateJobWorker( new JobWorkerConfig { JobType = "ship-order" }, async (job, ct) => null); // Per-worker override: this worker uses 32 concurrent jobs instead of the global 8 client.CreateJobWorker( new JobWorkerConfig { JobType = "bulk-import", MaxConcurrentJobs = 32 }, async (job, ct) => null); ``` You can also pass defaults programmatically via the client constructor: ```csharp var client = CamundaClient.Create(new CamundaOptions { Config = new Dictionary { ["CAMUNDA_WORKER_TIMEOUT"] = "30000", ["CAMUNDA_WORKER_MAX_CONCURRENT_JOBS"] = "8", }, }); ``` ## Concurrency Jobs are dispatched as concurrent `Task`s on the .NET thread pool. `MaxConcurrentJobs` controls how many jobs may be in-flight simultaneously. - **I/O-bound handlers** (HTTP calls, database queries): higher values like 32–128 improve throughput because `async` handlers release threads during `await` points — many jobs, few OS threads. - **CPU-bound handlers**: set `MaxConcurrentJobs` to `Environment.ProcessorCount` to match cores. - **Sequential processing**: set `MaxConcurrentJobs = 1`. ## Lifecycle ```csharp // Manual start/stop var worker = client.CreateJobWorker(new JobWorkerConfig { JobType = "example", JobTimeoutMs = 30_000, AutoStart = false }, handler); worker.Start(); // Graceful stop — waits up to 10s for in-flight jobs to finish var result = await worker.StopAsync(gracePeriod: TimeSpan.FromSeconds(10)); // result.RemainingJobs, result.TimedOut // Or stop all workers at once await client.StopAllWorkersAsync(TimeSpan.FromSeconds(10)); // DisposeAsync stops workers automatically await using var disposableClient = CamundaClient.Create(); ``` --- ## Logging :::caution Technical Preview The C# SDK is a **technical preview** available from Camunda 8.9. It will become fully supported in Camunda 8.10. Its API surface may change in future releases without following semver. ::: The SDK uses `Microsoft.Extensions.Logging` — the standard .NET logging abstraction. This means it integrates with any logging framework that supports `ILoggerFactory` (Serilog, NLog, the built-in console logger, etc.). ## Default Behavior When no logger is injected, the SDK uses a built-in console logger filtered by `CAMUNDA_SDK_LOG_LEVEL`: | `CAMUNDA_SDK_LOG_LEVEL` | What is logged | | ----------------------- | ---------------------------------------------------------------- | | `error` (default) | Errors only | | `warn` | Errors + warnings | | `info` | + OAuth token events, worker start/stop | | `debug` | + HTTP requests/responses, retry decisions, backpressure changes | | `trace` | + tenant injection, internal diagnostics | | `silent` | Nothing (same as `NullLoggerFactory`) | Output uses a tagged format matching the JS SDK: ``` [camunda-sdk][info][CamundaClient] CamundaClient constructed with auth strategy OAuth [camunda-sdk][debug][CamundaClient] HTTP POST process-instances/search -> 200 [camunda-sdk][info][JobWorker.worker-process-order-1] JobWorker 'worker-process-order-1' started for type 'process-order' ``` ## Injecting Your Own Logger Pass an `ILoggerFactory` via `CamundaOptions` to integrate with your application's logging: ```csharp using Camunda.Orchestration.Sdk; using var loggerFactory = LoggerFactory.Create(builder => { builder .AddConsole() .SetMinimumLevel(LogLevel.Debug); }); using var client = CamundaClient.Create(new CamundaOptions { LoggerFactory = loggerFactory, }); ``` When an `ILoggerFactory` is provided, `CAMUNDA_SDK_LOG_LEVEL` is ignored — filtering is controlled entirely by the injected factory. ## ASP.NET Core / Dependency Injection When using `AddCamundaClient()`, the SDK automatically resolves `ILoggerFactory` from the DI container — no manual wiring needed: ```csharp using Camunda.Orchestration.Sdk; var builder = WebApplication.CreateBuilder(args); // Logging configuration builder.Logging.SetMinimumLevel(LogLevel.Debug); // SDK automatically uses the host's ILoggerFactory builder.Services.AddCamundaClient(builder.Configuration.GetSection("Camunda")); ``` All SDK log entries appear alongside your application logs with proper category names (`Camunda.Orchestration.Sdk.CamundaClient`, `Camunda.Orchestration.Sdk.JobWorker.*`, etc.). ## Serilog Integration ```csharp Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .WriteTo.Console() .CreateLogger(); using var loggerFactory = new SerilogLoggerFactory(); using var client = CamundaClient.Create(new CamundaOptions { LoggerFactory = loggerFactory, }); ``` ## What Gets Logged | Component | Level | Events | | --------------------- | ------- | ------------------------------------------------- | | `CamundaClient` | Debug | HTTP request method + path, response status codes | | `CamundaClient` | Warning | HTTP request failures (non-2xx) | | `CamundaClient` | Trace | Default tenant ID injection | | `OAuthManager` | Debug | Token request attempts | | `OAuthManager` | Info | Token acquired (with effective expiry) | | `BackpressureManager` | Debug | Permit reduction/recovery | | `HttpRetryExecutor` | Debug | Retry attempts with delay and reason | | `JobWorker.*` | Info | Worker started, worker stopped | | `JobWorker.*` | Debug | Job completed | | `JobWorker.*` | Error | Handler exceptions, poll failures | | `EventualPoller` | Debug | Consistency polling progress | --- ## Migration Guide: v9 → v10 :::caution Technical Preview The C# SDK is a **technical preview** available from Camunda 8.9. It will become fully supported in Camunda 8.10. Its API surface may change in future releases without following semver. ::: This section covers breaking changes and new features when upgrading from `Camunda.Orchestration.Sdk` v9 (Camunda 8.9) to v10 (Camunda 8.10). > **Note:** v10 is currently in alpha (`10.0.0-alpha.N` on NuGet). The changes listed here may evolve before the stable v10.0.0 release. ## Package update ```xml ``` Or via the CLI: ```bash dotnet add package Camunda.Orchestration.Sdk --version "10.*-*" ``` ## Breaking changes ### Type renames (bundler dedup) The v10 generator uses an upgraded spec bundler that correctly preserves upstream schema names instead of inventing inline names. Several types that were previously generated as standalone classes are now replaced by their canonical upstream equivalents. If your code references any of the old type names, update them to the new names: | Removed type (v9) | Replacement (v10) | | ----------------------------------------- | --------------------------------------------- | | `CreateMappingRuleResponse` | `MappingRuleCreateResult` | | `GetUserResponse` | `UserResult` | | `SearchClientsForGroupRequest` | `GroupClientSearchQueryRequest` | | `SearchClientsForGroupResponse` | `GroupClientSearchResult` | | `SearchClientsForRoleRequest` | `RoleClientSearchQueryRequest` | | `SearchClientsForRoleResponse` | `RoleClientSearchResult` | | `SearchClientsForTenantRequest` | `TenantClientSearchQueryRequest` | | `SearchClientsForTenantResponse` | `TenantClientSearchResult` | | `SearchMappingRuleResponse` | `MappingRuleSearchQueryResult` | | `SearchMappingRulesForGroupResponse` | `GroupMappingRuleSearchResult` | | `SearchMappingRulesForRoleResponse` | `RoleMappingRuleSearchResult` | | `SearchMappingRulesForTenantResponse` | `TenantMappingRuleSearchResult` | | `SearchRolesForGroupResponse` | `GroupRoleSearchResult` | | `SearchRolesForTenantResponse` | `TenantRoleSearchResult` | | `SearchUsersForGroupRequest` | `GroupUserSearchQueryRequest` | | `SearchUsersForGroupResponse` | `GroupUserSearchResult` | | `SearchUsersForRoleRequest` | `RoleUserSearchQueryRequest` | | `SearchUsersForRoleResponse` | `RoleUserSearchResult` | | `SearchUsersForTenantRequest` | `TenantUserSearchQueryRequest` | | `SearchUsersForTenantResponse` | `TenantUserSearchResult` | | `SearchUsersResponse` | `UserSearchResult` | | `SearchUserTaskEffectiveVariablesRequest` | `UserTaskEffectiveVariableSearchQueryRequest` | | `SearchUserTaskVariablesRequest` | `UserTaskVariableSearchQueryRequest` | | `SearchVariablesRequest` | `VariableSearchQuery` | | `UpdateMappingRuleResponse` | `MappingRuleUpdateResult` | | `UpdateUserResponse` | `UserUpdateResult` | The replacement types are structurally identical — only the names change. A find-and-replace across your codebase is sufficient. ### Method signature changes The following methods have updated parameter and/or return types to match the type renames above: | Method | Changed parameter / return type | | --------------------------------------- | --------------------------------------------------------------------------------------------------- | | `CreateMappingRuleAsync` | Returns `MappingRuleCreateResult` (was `CreateMappingRuleResponse`) | | `UpdateMappingRuleAsync` | Returns `MappingRuleUpdateResult` (was `UpdateMappingRuleResponse`) | | `SearchMappingRuleAsync` | Returns `MappingRuleSearchQueryResult` (was `SearchMappingRuleResponse`) | | `SearchClientsForGroupAsync` | Takes `GroupClientSearchQueryRequest`, returns `GroupClientSearchResult` | | `SearchClientsForRoleAsync` | Takes `RoleClientSearchQueryRequest`, returns `RoleClientSearchResult` | | `SearchClientsForTenantAsync` | Takes `TenantClientSearchQueryRequest`, returns `TenantClientSearchResult` | | `SearchMappingRulesForGroupAsync` | Returns `GroupMappingRuleSearchResult` | | `SearchMappingRulesForRoleAsync` | Returns `RoleMappingRuleSearchResult` | | `SearchMappingRulesForTenantAsync` | Returns `TenantMappingRuleSearchResult` | | `SearchRolesForGroupAsync` | Returns `GroupRoleSearchResult` | | `SearchRolesForTenantAsync` | Returns `TenantRoleSearchResult` | | `SearchUsersForGroupAsync` | Takes `GroupUserSearchQueryRequest`, returns `GroupUserSearchResult` | | `SearchUsersForRoleAsync` | Takes `RoleUserSearchQueryRequest`, returns `RoleUserSearchResult` | | `SearchUsersForTenantAsync` | Takes `TenantUserSearchQueryRequest`, returns `TenantUserSearchResult` | | `SearchUserTaskEffectiveVariablesAsync` | Takes `UserTaskEffectiveVariableSearchQueryRequest` (was `SearchUserTaskEffectiveVariablesRequest`) | | `SearchUserTaskVariablesAsync` | Takes `UserTaskVariableSearchQueryRequest` (was `SearchUserTaskVariablesRequest`) | | `SearchVariablesAsync` | Takes `VariableSearchQuery` (was `SearchVariablesRequest`) | | `GetUserAsync` | Returns `UserResult` (was `GetUserResponse`) | | `SearchUsersAsync` | Returns `UserSearchResult` (was `SearchUsersResponse`) | | `UpdateUserAsync` | Returns `UserUpdateResult` (was `UpdateUserResponse`) | | `GetDocumentAsync` | Returns `byte[]` (was `object`) | | `GetResourceContentBinaryAsync` | Returns `byte[]` (new in v10) | ### Binary response handling Operations that return `application/octet-stream` content (such as `GetDocumentAsync`) now correctly return `byte[]` instead of `object`. In v9, these methods attempted to JSON-deserialize the binary response body, which threw `JsonException` for non-JSON content and returned an unusable `JsonElement` for JSON content. No migration action is needed unless your code caught the `JsonException` and worked around it. ### Inline string enums 45 properties that were previously typed as bare `string` are now typed C# enums. This gives compile-time validation, IntelliSense, and parity with the JS and Python SDKs. The affected properties are mainly sort-request `Field` properties, plus a few `Type`, `State`, `Health`, and `Role` properties: ```csharp // Before (v9) — bare string, no compile-time checking var sort = new UserTaskSearchQuerySortRequest { Field = "completionTime", Order = SortOrderEnum.Asc, }; // After (v10) — typed enum with IntelliSense var sort = new UserTaskSearchQuerySortRequest { Field = UserTaskSearchQuerySortRequestField.CompletionTime, Order = SortOrderEnum.Asc, }; ``` Complete list of affected properties (45 total): | Type | Property | Enum | | --------------------------------------------------------------- | --------------------- | -------------------------------------------------------------------- | | `AgentInstanceSearchQuerySortRequest` | `Field` | `AgentInstanceSearchQuerySortRequestField` | | `AuditLogSearchQuerySortRequest` | `Field` | `AuditLogSearchQuerySortRequestField` | | `AuthorizationSearchQuerySortRequest` | `Field` | `AuthorizationSearchQuerySortRequestField` | | `BatchOperationError` | `Type` | `BatchOperationErrorType` | | `BatchOperationItemResponse` | `State` | `BatchOperationItemResponseState` | | `BatchOperationItemSearchQuerySortRequest` | `Field` | `BatchOperationItemSearchQuerySortRequestField` | | `BatchOperationSearchQuerySortRequest` | `Field` | `BatchOperationSearchQuerySortRequestField` | | `ClusterVariableSearchQuerySortRequest` | `Field` | `ClusterVariableSearchQuerySortRequestField` | | `CorrelatedMessageSubscriptionSearchQuerySortRequest` | `Field` | `CorrelatedMessageSubscriptionSearchQuerySortRequestField` | | `DecisionDefinitionSearchQuerySortRequest` | `Field` | `DecisionDefinitionSearchQuerySortRequestField` | | `DecisionInstanceSearchQuerySortRequest` | `Field` | `DecisionInstanceSearchQuerySortRequestField` | | `DecisionRequirementsSearchQuerySortRequest` | `Field` | `DecisionRequirementsSearchQuerySortRequestField` | | `DocumentReference` | `CamundaDocumentType` | `DocumentReferenceCamundaDocumentType` | | `ElementInstanceFilter` | `Type` | `ElementInstanceFilterType` | | `ElementInstanceResult` | `Type` | `ElementInstanceResultType` | | `ElementInstanceSearchQuerySortRequest` | `Field` | `ElementInstanceSearchQuerySortRequestField` | | `GlobalTaskListenerSearchQuerySortRequest` | `Field` | `GlobalTaskListenerSearchQuerySortRequestField` | | `GroupClientSearchQuerySortRequest` | `Field` | `GroupClientSearchQuerySortRequestField` | | `GroupSearchQuerySortRequest` | `Field` | `GroupSearchQuerySortRequestField` | | `GroupUserSearchQuerySortRequest` | `Field` | `GroupUserSearchQuerySortRequestField` | | `IncidentProcessInstanceStatisticsByDefinitionQuerySortRequest` | `Field` | `IncidentProcessInstanceStatisticsByDefinitionQuerySortRequestField` | | `IncidentProcessInstanceStatisticsByErrorQuerySortRequest` | `Field` | `IncidentProcessInstanceStatisticsByErrorQuerySortRequestField` | | `IncidentSearchQuerySortRequest` | `Field` | `IncidentSearchQuerySortRequestField` | | `JobSearchQuerySortRequest` | `Field` | `JobSearchQuerySortRequestField` | | `MappingRuleSearchQuerySortRequest` | `Field` | `MappingRuleSearchQuerySortRequestField` | | `MessageSubscriptionSearchQuerySortRequest` | `Field` | `MessageSubscriptionSearchQuerySortRequestField` | | `Partition` | `Health` | `PartitionHealth` | | `Partition` | `Role` | `PartitionRole` | | `ProcessDefinitionInstanceStatisticsQuerySortRequest` | `Field` | `ProcessDefinitionInstanceStatisticsQuerySortRequestField` | | `ProcessDefinitionInstanceVersionStatisticsQuerySortRequest` | `Field` | `ProcessDefinitionInstanceVersionStatisticsQuerySortRequestField` | | `ProcessDefinitionSearchQuerySortRequest` | `Field` | `ProcessDefinitionSearchQuerySortRequestField` | | `ProcessInstanceSearchQuerySortRequest` | `Field` | `ProcessInstanceSearchQuerySortRequestField` | | `ResourceSearchQuerySortRequest` | `Field` | `ResourceSearchQuerySortRequestField` | | `RoleClientSearchQuerySortRequest` | `Field` | `RoleClientSearchQuerySortRequestField` | | `RoleGroupSearchQuerySortRequest` | `Field` | `RoleGroupSearchQuerySortRequestField` | | `RoleSearchQuerySortRequest` | `Field` | `RoleSearchQuerySortRequestField` | | `RoleUserSearchQuerySortRequest` | `Field` | `RoleUserSearchQuerySortRequestField` | | `TenantClientSearchQuerySortRequest` | `Field` | `TenantClientSearchQuerySortRequestField` | | `TenantGroupSearchQuerySortRequest` | `Field` | `TenantGroupSearchQuerySortRequestField` | | `TenantSearchQuerySortRequest` | `Field` | `TenantSearchQuerySortRequestField` | | `TenantUserSearchQuerySortRequest` | `Field` | `TenantUserSearchQuerySortRequestField` | | `UserSearchQuerySortRequest` | `Field` | `UserSearchQuerySortRequestField` | | `UserTaskSearchQuerySortRequest` | `Field` | `UserTaskSearchQuerySortRequestField` | | `UserTaskVariableSearchQuerySortRequest` | `Field` | `UserTaskVariableSearchQuerySortRequestField` | | `VariableSearchQuerySortRequest` | `Field` | `VariableSearchQuerySortRequestField` | The naming convention is `{ParentClassName}{PascalCase(PropertyName)}` (e.g., `UserTaskSearchQuerySortRequestField`). ### Eventual consistency parameter types `GetResourceAsync` and `GetResourceContentAsync` now accept an optional `ConsistencyOptions` parameter, matching the pattern used by all other eventually-consistent endpoints. If you pass these methods by reference or use them in delegates, you may need to update the signature: ```csharp // Before (v9) var resource = await client.GetResourceAsync(resourceKey); // After (v10) — the call is unchanged, but the optional parameter exists var resource = await client.GetResourceAsync(resourceKey); // Or, with eventual consistency: var resource = await client.GetResourceAsync(resourceKey, new ConsistencyOptions { WaitUpToMs = 5000 }); ``` ## New features in v10 ### Resource search v10 adds a `SearchResourcesAsync` method with full search, filter, and sort support: ```csharp var result = await client.SearchResourcesAsync(new ResourceSearchQuery { Filter = new ResourceFilter { /* ... */ }, Sort = [new ResourceSearchQuerySortRequest { Field = ResourceSearchQuerySortRequestField.DeploymentKey, }], }); ``` Supporting types: `ResourceSearchQuery`, `ResourceFilter`, `ResourceSearchQueryResult`, `ResourceSearchQuerySortRequest`, `ResourceSearchQuerySortRequestField`. ### New filter properties The following filter properties are now available on existing search filters: - **`ElementIdFilterProperty`** — filter by element ID on flow node instance searches (`AdvancedElementIdFilter`, `ElementIdExactMatch`). - **`ProcessDefinitionIdFilterProperty`** — filter by process definition ID (`AdvancedProcessDefinitionIdFilter`, `ProcessDefinitionIdExactMatch`). - **`MessageSubscriptionTypeFilterProperty`** — filter message subscriptions by type (`AdvancedMessageSubscriptionTypeFilter`, `MessageSubscriptionTypeExactMatch`). ### New enum - **`MessageSubscriptionTypeEnum`** — discriminates message subscription types. ## No changes between v9 and v10 The following areas are **unchanged** between v9 and v10: - **Target framework**: .NET 8.0+ - **Runtime behavior**: Auth, retry, backpressure, job workers, eventual consistency polling - **Configuration**: All `CAMUNDA_*` environment variables and `CamundaOptions` properties - **Branded types**: All existing `ICamundaKey` types (`ProcessDefinitionKey`, `UserTaskKey`, etc.) retain the same API. v10 adds new branded types: `GroupId`, `RoleId`, `ClientId`, `MappingRuleId`, `AgentInstanceKey`, `ClusterVariableName` - **Enum handling**: `TolerantEnumConverter` continues to handle unknown enum values gracefully --- ## Quick Start (Zero-Config — Recommended) :::caution Technical Preview The C# SDK is a **technical preview** available from Camunda 8.9. It will become fully supported in Camunda 8.10. Its API surface may change in future releases without following semver. ::: Keep configuration out of application code. Let the factory read `CAMUNDA_*` variables from the environment (12-factor style). This makes rotation, secret management, and environment promotion safer and simpler. ```csharp using Camunda.Orchestration.Sdk; // Zero-config construction: reads CAMUNDA_* from environment variables. // If no configuration is present, defaults to Camunda 8 Run on localhost. using var client = CamundaClient.Create(); var topology = await client.GetTopologyAsync(); Console.WriteLine($"Brokers: {topology.Brokers?.Count ?? 0}"); ``` Typical environment (example): ```bash CAMUNDA_REST_ADDRESS=https://cluster.example # SDK appends /v2 automatically CAMUNDA_AUTH_STRATEGY=OAUTH CAMUNDA_CLIENT_ID=*** CAMUNDA_CLIENT_SECRET=*** CAMUNDA_OAUTH_URL=https://login.cloud.camunda.io/oauth/token CAMUNDA_DEFAULT_TENANT_ID= # optional: override default tenant ``` > **Why zero-config?** > > - **Separation of concerns**: business code depends on an interface, not on secrets/constants wiring. > - **12-Factor alignment**: config lives in the environment → simpler promotion (dev → staging → prod). > - **Secret rotation**: rotate credentials without a code change or redeploy. > - **Immutable start**: single hydration pass prevents drift / mid-request mutations. > - **Test ergonomics**: swap env vars per test without touching source; create multiple clients for multi-tenant tests. > - **Security review**: fewer code paths handling secrets; scanners & vault tooling work at the boundary. > - **Deploy portability**: same artifact runs everywhere; only the environment differs. > - **Cross-SDK consistency**: identical variable names across JavaScript, C#, and Python SDKs. ## Programmatic Overrides (Advanced) Use only when you must supply or mutate configuration dynamically (e.g. multi-tenant routing, tests, ephemeral preview environments). Keys mirror their `CAMUNDA_*` env names: ```csharp using Camunda.Orchestration.Sdk; using var client = CamundaClient.Create(new CamundaOptions { Config = new Dictionary { ["CAMUNDA_REST_ADDRESS"] = "https://my-cluster.camunda.io", ["CAMUNDA_AUTH_STRATEGY"] = "OAUTH", ["CAMUNDA_CLIENT_ID"] = "my-client-id", ["CAMUNDA_CLIENT_SECRET"] = "my-secret", ["CAMUNDA_OAUTH_URL"] = "https://login.cloud.camunda.io/oauth/token", ["CAMUNDA_TOKEN_AUDIENCE"] = "zeebe.camunda.io", }, }); ``` ## Configuration via `appsettings.json` The SDK can read configuration from any `IConfiguration` source (appsettings.json, user secrets, Azure Key Vault, etc.) using idiomatic .NET PascalCase section keys: ```json { "Camunda": { "RestAddress": "https://cluster.example.com", "Auth": { "Strategy": "OAUTH", "ClientId": "my-client-id", "ClientSecret": "my-secret" }, "OAuth": { "Url": "https://login.cloud.camunda.io/oauth/token" }, "Backpressure": { "Profile": "CONSERVATIVE" } } } ``` Pass the section to the client: ```csharp using Camunda.Orchestration.Sdk; var builder = WebApplication.CreateBuilder(args); using var client = CamundaClient.Create(new CamundaOptions { Configuration = builder.Configuration.GetSection("Camunda"), }); ``` Precedence (highest wins): `Config` dictionary > `IConfiguration` section > environment variables > defaults. This means you can set secrets via environment variables (or a vault) and non-sensitive settings via `appsettings.json` — they layer naturally: ```json // appsettings.json — non-sensitive, checked into source control { "Camunda": { "RestAddress": "https://cluster.example.com", "Backpressure": { "Profile": "CONSERVATIVE" } } } ``` ```bash # Secrets injected via environment (vault, CI, container orchestrator) CAMUNDA_CLIENT_ID=*** CAMUNDA_CLIENT_SECRET=*** CAMUNDA_OAUTH_URL=https://login.cloud.camunda.io/oauth/token ```
appsettings.json key reference | appsettings.json key | Maps to env var | | --------------------------------- | ----------------------------------------------- | | `RestAddress` | `CAMUNDA_REST_ADDRESS` | | `TokenAudience` | `CAMUNDA_TOKEN_AUDIENCE` | | `DefaultTenantId` | `CAMUNDA_DEFAULT_TENANT_ID` | | `LogLevel` | `CAMUNDA_SDK_LOG_LEVEL` | | `Validation` | `CAMUNDA_SDK_VALIDATION` | | `Auth:Strategy` | `CAMUNDA_AUTH_STRATEGY` | | `Auth:ClientId` | `CAMUNDA_CLIENT_ID` | | `Auth:ClientSecret` | `CAMUNDA_CLIENT_SECRET` | | `Auth:BasicUsername` | `CAMUNDA_BASIC_AUTH_USERNAME` | | `Auth:BasicPassword` | `CAMUNDA_BASIC_AUTH_PASSWORD` | | `OAuth:Url` | `CAMUNDA_OAUTH_URL` | | `OAuth:ClientId` | `CAMUNDA_CLIENT_ID` | | `OAuth:ClientSecret` | `CAMUNDA_CLIENT_SECRET` | | `OAuth:GrantType` | `CAMUNDA_OAUTH_GRANT_TYPE` | | `OAuth:Scope` | `CAMUNDA_OAUTH_SCOPE` | | `OAuth:TimeoutMs` | `CAMUNDA_OAUTH_TIMEOUT_MS` | | `OAuth:RetryMax` | `CAMUNDA_OAUTH_RETRY_MAX` | | `OAuth:RetryBaseDelayMs` | `CAMUNDA_OAUTH_RETRY_BASE_DELAY_MS` | | `HttpRetry:MaxAttempts` | `CAMUNDA_SDK_HTTP_RETRY_MAX_ATTEMPTS` | | `HttpRetry:BaseDelayMs` | `CAMUNDA_SDK_HTTP_RETRY_BASE_DELAY_MS` | | `HttpRetry:MaxDelayMs` | `CAMUNDA_SDK_HTTP_RETRY_MAX_DELAY_MS` | | `Backpressure:Profile` | `CAMUNDA_SDK_BACKPRESSURE_PROFILE` | | `Backpressure:InitialMax` | `CAMUNDA_SDK_BACKPRESSURE_INITIAL_MAX` | | `Backpressure:SoftFactor` | `CAMUNDA_SDK_BACKPRESSURE_SOFT_FACTOR` | | `Backpressure:SevereFactor` | `CAMUNDA_SDK_BACKPRESSURE_SEVERE_FACTOR` | | `Backpressure:RecoveryIntervalMs` | `CAMUNDA_SDK_BACKPRESSURE_RECOVERY_INTERVAL_MS` | | `Backpressure:RecoveryStep` | `CAMUNDA_SDK_BACKPRESSURE_RECOVERY_STEP` | | `Backpressure:DecayQuietMs` | `CAMUNDA_SDK_BACKPRESSURE_DECAY_QUIET_MS` | | `Backpressure:Floor` | `CAMUNDA_SDK_BACKPRESSURE_FLOOR` | | `Backpressure:SevereThreshold` | `CAMUNDA_SDK_BACKPRESSURE_SEVERE_THRESHOLD` | | `Eventual:PollDefaultMs` | `CAMUNDA_SDK_EVENTUAL_POLL_DEFAULT_MS` |
## Dependency Injection (`AddCamundaClient`) For ASP.NET Core and other DI-based applications, use the `AddCamundaClient()` extension method on `IServiceCollection`. The client is registered as a singleton and automatically picks up `ILoggerFactory` from the container. **Zero-config** (environment variables only): ```csharp using Camunda.Orchestration.Sdk; var builder = WebApplication.CreateBuilder(args); builder.Services.AddCamundaClient(); ``` **With `appsettings.json`**: ```csharp using Camunda.Orchestration.Sdk; var builder = WebApplication.CreateBuilder(args); builder.Services.AddCamundaClient(builder.Configuration.GetSection("Camunda")); ``` **With options callback** (full control): ```csharp using Camunda.Orchestration.Sdk; builder.Services.AddCamundaClient(options => { options.Configuration = builder.Configuration.GetSection("Camunda"); // or: options.Config = new Dictionary { ... }; }); ``` Inject the client anywhere via constructor injection: ```csharp public class OrderController(CamundaClient camunda) : ControllerBase { [HttpPost] public async Task StartProcess() { var result = await camunda.CreateProcessInstanceAsync( new ProcessInstanceCreationInstructionById { ProcessDefinitionId = ProcessDefinitionId.AssumeExists("order-process"), }); return Ok(result); } } ``` ## Custom HttpClient ```csharp using Camunda.Orchestration.Sdk; var httpClient = new HttpClient { BaseAddress = new Uri("https://my-cluster/v2/") }; using var client = CamundaClient.Create(new CamundaOptions { HttpClient = httpClient, }); ``` --- ## Resilience :::caution Technical Preview The C# SDK is a **technical preview** available from Camunda 8.9. It will become fully supported in Camunda 8.10. Its API surface may change in future releases without following semver. ::: ## HTTP Retry Automatic retry with exponential backoff and jitter for transient failures (429, 503, 500, timeouts). | Variable | Default | Description | | -------------------------------------- | ------- | ---------------------------------- | | `CAMUNDA_SDK_HTTP_RETRY_MAX_ATTEMPTS` | `3` | Total attempts (initial + retries) | | `CAMUNDA_SDK_HTTP_RETRY_BASE_DELAY_MS` | `100` | Base backoff delay (ms) | | `CAMUNDA_SDK_HTTP_RETRY_MAX_DELAY_MS` | `2000` | Maximum backoff cap (ms) | ## Global Backpressure (Adaptive Concurrency) The client includes an adaptive backpressure manager that throttles the number of in-flight operations when the cluster signals resource exhaustion. It complements (not replaces) per-request HTTP retry. ### Signals Considered An HTTP response is treated as a backpressure signal when it matches one of: - `429` (Too Many Requests) — always - `503` with `title === "RESOURCE_EXHAUSTED"` - `500` whose RFC 9457 / 7807 `detail` text contains `RESOURCE_EXHAUSTED` All other 5xx variants are treated as non-retryable (fail fast) and do **not** influence the adaptive gate. ### How It Works 1. Normal state starts with the concurrency cap from `CAMUNDA_SDK_BACKPRESSURE_INITIAL_MAX` (default 16). 2. On backpressure signals the manager reduces available permits using the soft factor (70% by default). 3. Repeated consecutive signals escalate severity to `severe`, applying a stronger reduction factor (50%). 4. Successful (non-backpressure) completions trigger passive recovery checks that gradually restore permits over time if the system stays quiet. 5. Quiet periods (no signals for a configurable decay interval) downgrade severity and reset the consecutive counter. The policy is intentionally conservative: it only engages after genuine pressure signals and recovers gradually to avoid oscillation. ### Configuration | Variable | Default | Description | | ----------------------------------------------- | ---------- | ------------------------------------------------------- | | `CAMUNDA_SDK_BACKPRESSURE_PROFILE` | `BALANCED` | Preset profile (see below) | | `CAMUNDA_SDK_BACKPRESSURE_INITIAL_MAX` | `16` | Bootstrap concurrency cap | | `CAMUNDA_SDK_BACKPRESSURE_SOFT_FACTOR` | `70` | Percentage multiplier on soft backpressure (70 → 0.70×) | | `CAMUNDA_SDK_BACKPRESSURE_SEVERE_FACTOR` | `50` | Percentage multiplier on severe backpressure | | `CAMUNDA_SDK_BACKPRESSURE_RECOVERY_INTERVAL_MS` | `1000` | Interval between passive recovery checks (ms) | | `CAMUNDA_SDK_BACKPRESSURE_RECOVERY_STEP` | `1` | Permits regained per recovery interval | | `CAMUNDA_SDK_BACKPRESSURE_DECAY_QUIET_MS` | `2000` | Quiet period to downgrade severity (ms) | | `CAMUNDA_SDK_BACKPRESSURE_FLOOR` | `1` | Minimum concurrency floor while degraded | | `CAMUNDA_SDK_BACKPRESSURE_SEVERE_THRESHOLD` | `3` | Consecutive signals required to enter severe state | ### Profiles Profiles supply coordinated defaults. Any explicitly set env var overrides the profile value. | Profile | initialMax | softFactor% | severeFactor% | recoveryMs | recoveryStep | quietDecayMs | floor | severeThreshold | Use case | | -------------- | ---------- | ----------- | ------------- | ---------- | ------------ | ------------ | ----- | --------------- | ---------------------------- | | `BALANCED` | 16 | 70 | 50 | 1000 | 1 | 2000 | 1 | 3 | General workloads | | `CONSERVATIVE` | 12 | 60 | 40 | 1200 | 1 | 2500 | 1 | 2 | Tighter capacity constraints | | `AGGRESSIVE` | 24 | 80 | 60 | 800 | 2 | 1500 | 2 | 4 | High throughput scenarios | | `LEGACY` | — | — | — | — | — | — | — | — | Observe-only (no gating) | Select via environment: ```bash CAMUNDA_SDK_BACKPRESSURE_PROFILE=AGGRESSIVE ``` Override individual knobs on top of a profile: ```bash CAMUNDA_SDK_BACKPRESSURE_PROFILE=AGGRESSIVE CAMUNDA_SDK_BACKPRESSURE_INITIAL_MAX=32 ``` The `LEGACY` profile disables adaptive gating entirely — signals are still tracked for observability but no concurrency limits are applied. Use this to opt out of backpressure management while retaining per-request retry. ### Inspecting State Programmatically ```csharp var state = client.GetBackpressureState(); // state.Severity: "healthy", "soft", or "severe" // state.Consecutive: consecutive backpressure signals observed // state.PermitsMax: current concurrency cap (null when LEGACY / not engaged) ``` ## Eventual Consistency Built-in polling for eventually consistent endpoints with configurable wait times and predicates. --- ## Self-signed TLS / mTLS :::caution Technical Preview The C# SDK is a **technical preview** available from Camunda 8.9. It will become fully supported in Camunda 8.10. Its API surface may change in future releases without following semver. ::: The SDK supports custom TLS certificates via environment variables. This is useful for: - **Self-signed server certificates** — trust a CA that signed your server's certificate, without presenting a client identity. - **Mutual TLS (mTLS)** — present a client certificate and key to prove the client's identity. - **Both** — trust a custom CA _and_ present client credentials. ## Trusting a self-signed server certificate Set only the CA certificate to trust the server's self-signed certificate: ```bash # Path to PEM file: CAMUNDA_MTLS_CA_PATH=/path/to/ca.pem # Or inline PEM: CAMUNDA_MTLS_CA="-----BEGIN CERTIFICATE-----\n..." ``` ## Mutual TLS (client certificate) To present a client certificate for mutual TLS, provide both the certificate and private key: ```bash CAMUNDA_MTLS_CERT_PATH=/path/to/client.crt CAMUNDA_MTLS_KEY_PATH=/path/to/client.key # Optional — passphrase if the key is encrypted: # CAMUNDA_MTLS_KEY_PASSPHRASE=secret ``` ## Full mTLS with custom CA Combine a custom CA with client credentials: ```bash CAMUNDA_MTLS_CA_PATH=/path/to/ca.pem CAMUNDA_MTLS_CERT_PATH=/path/to/client.crt CAMUNDA_MTLS_KEY_PATH=/path/to/client.key ``` Inline PEM values (`CAMUNDA_MTLS_CERT`, `CAMUNDA_MTLS_KEY`, `CAMUNDA_MTLS_CA`) take precedence over their `_PATH` counterparts. TLS is applied to all outbound calls, including OAuth token requests. No code changes are needed — the SDK picks up TLS configuration from environment variables automatically: ```csharp using Camunda.Orchestration.Sdk; var client = CamundaClient.Create(); // TLS configured from env vars ``` --- ## Strongly-Typed Domain Keys :::caution Technical Preview The C# SDK is a **technical preview** available from Camunda 8.9. It will become fully supported in Camunda 8.10. Its API surface may change in future releases without following semver. ::: All domain identifiers (process definition keys, job keys, user task keys, etc.) are `readonly record struct` types rather than plain strings. This prevents accidentally mixing different key types at compile time — the same pattern as the JS SDK's branded types. ```csharp using Camunda.Orchestration.Sdk; // Lift a raw value into the correct nominal type var defKey = ProcessDefinitionKey.AssumeExists("2251799813686749"); // Type safety — compiler prevents mixing key types var taskKey = UserTaskKey.AssumeExists("123456"); // await client.GetProcessDefinitionAsync(taskKey); // ← compile error // Validation — constraints (pattern, length) checked at construction ProcessDefinitionKey.IsValid("2251799813686749"); // true // Values returned from API calls are already typed var result = await client.GetProcessDefinitionAsync(defKey); // result.ProcessDefinitionKey is ProcessDefinitionKey, not string // Transparent JSON serialization — no special handling needed ``` Key types implement `ICamundaKey` (string-backed) or `ICamundaLongKey` (long-backed) and serialize as plain JSON values. Constraint validation (regex pattern, min/max length) is enforced in `AssumeExists()` and queryable via `IsValid()`. --- ## Support status :::caution Technical Preview The C# SDK is a **technical preview** available from Camunda 8.9. It will become fully supported in Camunda 8.10. Its API surface may change in future releases without following semver. ::: This is a technical preview of the C# client that will become fully supported in Camunda 8.10.0. The Technical Preview gives you a stable foundation to build on now, with a clear path to full support. We don't anticipate major changes — and [your feedback](https://github.com/camunda/orchestration-cluster-api-csharp/issues) between now and 8.10 is what closes that gap. --- ## Typed Variables with DTOs :::caution Technical Preview The C# SDK is a **technical preview** available from Camunda 8.9. It will become fully supported in Camunda 8.10. Its API surface may change in future releases without following semver. ::: Camunda API operations use dynamic `variables` and `customHeaders` payloads. By default these are untyped (`object`), but you can opt in to compile-time type safety using your own DTOs. ## Sending Variables (Input) Assign any DTO or dictionary to the `Variables` property — `System.Text.Json` serializes the runtime type automatically: ```csharp using Camunda.Orchestration.Sdk; // Define your application domain models public record OrderInput(string OrderId, decimal Amount); // Assign the DTO directly await client.CreateProcessInstanceAsync(new ProcessInstanceCreationInstructionById { ProcessDefinitionId = processDefinitionId, Variables = new OrderInput("ord-123", 99.99m), }); // Dictionaries also work — no DTO required await client.CompleteJobAsync(jobKey, new JobCompletionRequest { Variables = new Dictionary { ["processed"] = true }, }); ``` ## Receiving Variables (Output) Use `DeserializeAs()` to extract typed DTOs from API responses: ```csharp using Camunda.Orchestration.Sdk; public record OrderResult(bool Processed, string InvoiceNumber); // Deserialize variables from any API response var result = await client.CreateProcessInstanceAsync( new ProcessInstanceCreationInstructionById { ProcessDefinitionId = processDefinitionId, }); var output = result.Variables.DeserializeAs(); // output.Processed, output.InvoiceNumber — fully typed ``` `DeserializeAs()` handles the common runtime shapes: - `JsonElement` (standard API response) → deserialized via `System.Text.Json` - Already the target type → returned as-is (zero-copy) - `null` → returns `default(T)` Custom `JsonSerializerOptions` can be passed for non-standard naming conventions. --- ## C# SDK (Technical Preview) :::caution Technical Preview The C# SDK is a **technical preview** available from Camunda 8.9. It will become fully supported in Camunda 8.10. Its API surface may change in future releases without following semver. ::: Technical preview of the C# client SDK for the [Camunda 8 Orchestration Cluster REST API](../apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). Unified configuration, OAuth/Basic auth, automatic retry, backpressure management, strongly-typed domain keys, and opt-in typed variables. --- ## Introduction to task applications Task applications are the interface between humans and Camunda processes to orchestrate human work. Learn key concepts of the architecture of task applications before you build your own. ## What are task applications? Task applications are end-user applications that allow humans to perform work orchestrated with a process. A [user task](/components/modeler/bpmn/user-tasks/user-tasks.md#user-task-forms) (for [human task orchestration](/guides/getting-started-orchestrate-human-tasks.md)) represents a single **work item** to be performed by an individual or a group. The jobs of a task application include: - Listing available tasks and allowing users to select a task to work on. - Providing filter and search options for users so they can more easily find the right next task to work on. - Presenting the selected task and an interface for completing the task, usually via a form. - Providing an interface to create new tasks, e.g. by starting a new process. - Provide insight into the progress of work tasks, including processes and cases. - Aggregate information so users and their managers can assess the impact on process goals, such as KPIs and SLAs. - Ensure tasks are visible only to authorized users. Task applications play a key role in the orchestration of business processes. They enable the orchestration of processes that still contain manual work without automating each process step in advance. This unlocks the potential for continuous improvement and for identifying opportunities for process optimization and automation. :::tip Not sure if you should use Camunda Tasklist, build your custom task application, or use a third-party application? Read the [guide to understand human task management](/components/best-practices/architecture/understanding-human-tasks-management.md#deciding-about-your-task-list-frontend) first. ::: ## Tasklist layout Camunda 8 comes with a ready-to-use Tasklist UI that implements all key concepts of a task application. The Tasklist UI is a generic task application; your custom task application should probably be tailored to your specific use case and also include external data sources and tools. The Tasklist UI is split into two main pages: the [tasks page](#task-page) and the [processes page](#processes-page). ### Task page The task page lists all tasks pending for a user or user group, and allows users to pick and claim a task from that queue to work on. On the same page, the details of a selected task are displayed including the form that the user must submit in order to execute and complete the task. The task page is optimized for efficient workflows, where the most important tasks should be worked on first. The task page is divided into two main areas: - Left side showing the tasks queue. - Right side showing the details of the selected task. #### Tasks queue The **tasks queue side panel** lists all tasks pending for a user or user group. It comes with filter and sort options that allow users to identify the right task to work on next. The tasks can be sorted by the creation date, due date, or follow-up date. Learn more how to work with the task queue in the [Tasklist user guide](/components/tasklist/userguide/using-tasklist.md). #### Task details Task details are shown when a task is selected from the queue. A [form](/components/modeler/forms/utilizing-forms.md) is displayed as the task content, which must be filled out to complete the task. :::tip Typically, a task application utilizes forms to capture information from the user, to make a decision, to collect the results from a real-world task, or to provide task instructions to the user. However, a [user task](/components/modeler/bpmn/user-tasks/user-tasks.md#user-task-forms) is not limited to forms. A user task could also represent navigating to an external desktop or web application, where a task is to be performed, such as updating a record in a CRM. You can even use them to track physical work or actions using sensors, IoT devices, or any interface that can talk to the web, by using the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). For these cases, utilize the flexible [custom form key](/components/hub/workspace/modeler/modeling/advanced-modeling/form-linking.md#custom-form-key). ::: On the top of the form, a header shows the title of the task to work on, and the current assignee. Depending on the status of the assignment, a button allows you to assign the task to yourself or release it to the queue. At the bottom of the form there is a button with which you can complete the task. To the right of the task, you find additional information about the task, such as the [due date](/components/modeler/bpmn/user-tasks/user-tasks.md#scheduling) of the task, or the authorization data that controls who can work on the task. Potential extensions are dependent on your use case. You can consider adding more buttons to the bottom of the panel to indicate different task outcomes such as "approve" or "reject", or you could add a list of attachments to the right panel. Learn more how to work with the task details panel in the [Tasklist user guide](/components/tasklist/userguide/using-tasklist.md). ### Processes page The **Processes** page lists all processes available to the logged in user, and allows the user to start a process from there. Potential extensions are dependent on your use case. You can consider grouping processes by apps, domains, or teams, showing a process history, or adding a list of open process instances or cases. Learn more about the **Processes** page in the [Tasklist documentation](/components/tasklist/userguide/starting-processes.md). :::tip Alternative layouts There are many alternative layouts that you can choose for creating your task application. Design the layout based on the use case. For longer running processes and tasks with a lot of hierarchy between the tasks and the associated data, for example, tabular views together with multi-part detail views are more suitable. ::: ## Task lifecycle Every task follows a task life cycle. In the typical task life cycle, a task can, for example: - Be **created**, but not yet assigned - Be **assigned** and ready to work - Be **open** or **started** - Be **paused** and marked with a follow-up date - Be **delegated** to another user - Be **completed** or **canceled** Before you create your task application, you should be clear about [which task lifecycle is suitable for your use case](./02-user-task-lifecycle.md). ```mermaid flowchart subgraph Assignment Unassigned(Unassigned) -->|assign/claim| Assigned(fa:fa-user Assigned) Assigned -->|return| Unassigned Assigned -->|reassign| Assigned end subgraph Work state New(( )) -->|create| A(Open) A -->|start| B(In progress) B -->|complete| C(fa:fa-check Completed) B -->|pause| D(Paused) D -->|resume| B(In progress) B -->|return| A style New fill:black style C stroke-width:2px end ``` The lifecycle of human task orchestration is mostly a generic issue. There is no need to model common aspects into all your processes, as this often makes models unreadable. Use Camunda task management features or implement your requirements in a generic way. Learn how to define and implement your task lifecycle on the [user task lifecycle](./02-user-task-lifecycle.md) page. ## Task assignment Every task can be assigned to either a group of people, or a specific individual. An individual can **claim** a task, indicating that they are picking the task from the pool (to avoid multiple people working on the same task). As a general rule, you should assign user tasks in your business process to groups of people instead of specific individuals. This avoids bottlenecks (such as high workloads on single individuals or employees being on sick leave) and can greatly improve your process performance. In the [XML of a user task](/components/modeler/bpmn/user-tasks/user-tasks.md#xml-representations), this is represented as follows: ```xml ``` Then, require individual members of that group to explicitly claim tasks before working on them. This way, you avoid different people trying to work on the same task at the same time, which can cause a race condition. ## Additional elements and alternative use cases Often, task applications support the collaborative work on tasks, generally done using **comments**. **Document management** is a common use case of task applications, allowing users to upload, manage, and review **attachments**. Task applications are also the right place to browse, reference, and manage **case management data**. Task applications are not limited to web applications to be worked on desktops. Camunda has been used successfully for the the development of omnichannel customer-facing applications, such as **mobile banking apps**, often via a **backend-for-frontend** implementation. ## Next steps You learned the basic concepts of a task application. Your possible next steps are: - Learn how to [embed or customize Camunda Forms](/apis-tools/frontend-development/03-forms/01-introduction-to-forms.md) to render tailored forms that can be designed by business users. - Learn how to utilize the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) to query and execute tasks in your task application, and to enrich it with process execution data. --- ## User task life cycle The task life cycle defines how users can interact with tasks and how work on these tasks is performed. It defines the core actions of your task application, so you should define it before implementing your logic and user interface. ## Decide on your task life cycle The task life cycle ultimately depends on the use case you want to cover with your task application, the personas who interact with it, the content you want to track in reports and audit logs, and the flexibility your users need. Refer to the Camunda best practice task life cycle below as a starting point. ## Camunda best practice task life cycle The Camunda [Tasklist](/components/tasklist/introduction-to-tasklist.md) component implements a task life cycle optimized to track actual work on individual tasks via [forms](../03-forms/01-introduction-to-forms.md) on a desktop. It separates task assignment from task state to support collaborative ways of working and promote use cases for managers. On the happy path, task agents can: - `start` a task to indicate that they are working on it. - `complete` the task when the work is done. - When they cannot continue the work now, for example to gather more information on larger tasks, they can `pause` the work and `resume` it at a later time. - The data entered up to this point is preserved. If they are unable to continue, they can `return` a task to the queue for someone else to pick it up, which resets the task data. :::note The state of the task is derived via a CQRS pattern. [Zeebe](/components/zeebe/zeebe-overview.md), Camunda's process execution engine, manages a stream of events. There is no single status attribute in tasks. Instead, tasks derive their status from these events. ::: ```mermaid flowchart subgraph Work state New(( )) -->|create| A(Open) A -->|start| B(In progress) B -->|complete| C(fa:fa-check Completed) B -->|pause| D(Paused) D -->|resume| B(In progress) B -->|return| A style New fill:black style C stroke-width:2px end ``` :::tip To benefit from [out-of-the-box task performance reporting in Optimize](#task-lifecycle-reporting-in-optimize), your task life cycle should cover the outlined work state actions `start` and `complete`, and optionally `pause`, `resume`, and `return`. ::: ### Task assignment The assignment logic runs parallel to the work state. This ensures maximum flexibility. For example, a task may be assigned but be open for a while (lying in the inbox), indicating that the assigned user is not available to work on this task promptly and offering optimization potential for assignment rules for this task. Another example is the change of task assignee while working on a task, such as in collaborative environments. In the Tasklist user interface, a task can be claimed by the logged-in user, which assigns the task to that user. Managers can assign unassigned tasks to team members and reassign them as needed. ```mermaid flowchart subgraph Assignment Unassigned(Unassigned) -->|assign/claim| Assigned(fa:fa-user Assigned) Assigned -->|return| Unassigned Assigned -->|reassign| Assigned end ``` The execution engine does not validate whether a user is authorized to execute a task. Instead, this is a matter for the application that is built on top. For example, Tasklist checks that only the assigned user or an admin/manager is able to update and complete a task, but you are free to use different logic in your task application. This way, maximum flexibility is guaranteed when you create a custom application based on Camunda. For example, you can implement a substitute logic that allows a user to complete a task on behalf of another user. Our best practices, as implemented in Tasklist, are as follows: - `update` and `complete` operations can only be performed by the assigned user or an admin/manager. - Users can only see tasks to which they are assigned and tasks to which a user group of which they are a member is assigned as a candidate. - When a task is returned to the queue (i.e. the assignee is cleared), its data and status are reset to "open". - Only admins/managers can reassign tasks. - Task agents can return tasks, but must provide a comment as to why they are doing so. - Task agents can mark tasks with a follow-up date. These then disappear from their individual task list until the follow-up date is reached. The `open` status is preserved, or the task is moved to the `paused` status if it has already been processed. The task remains assigned to the user. Make sure that you create your own validation logic that matches your use case. ## Implement task life cycle with the Orchestration Cluster REST API Use the Orchestration Cluster REST API to implement task life cycle operations. You can find the API specifications [here](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). Task life cycle events can be tracked via the following API endpoints: - [`POST /user-tasks/:userTaskKey/assignment`](/apis-tools/orchestration-cluster-api-rest/specifications/assign-user-task.api.mdx) or [`DELETE /user-tasks/:userTaskKey/assignee`](/apis-tools/orchestration-cluster-api-rest/specifications/unassign-user-task.api.mdx) to change task assignment. - [`PATCH /user-tasks/:userTaskKey`](/apis-tools/orchestration-cluster-api-rest/specifications/update-user-task.api.mdx) to update a task. - [`POST /user-tasks/:userTaskKey/completion`](/apis-tools/orchestration-cluster-api-rest/specifications/complete-user-task.api.mdx) to complete a task. - Assign user task: - [`POST /user-tasks/:userTaskKey/assignment`](/apis-tools/orchestration-cluster-api-rest/specifications/assign-user-task.api.mdx) - [`DELETE /user-tasks/:userTaskKey/assignee`](/apis-tools/orchestration-cluster-api-rest/specifications/unassign-user-task.api.mdx) is used to unassing a user task. - Update user task: - [`PATCH /user-tasks/:taskKey`](/apis-tools/orchestration-cluster-api-rest/specifications/update-user-task.api.mdx) - Complete user task: - [`POST /user-tasks/:taskKey/completion`](/apis-tools/orchestration-cluster-api-rest/specifications/complete-user-task.api.mdx) #### [`POST /user-tasks/:userTaskKey/assignment`](/apis-tools/orchestration-cluster-api-rest/specifications/assign-user-task.api.mdx) Use the `assignment` endpoint to change the task assignment. Use the `action` attribute to indicate the cause of the change, including `claim`, `reassign`, or `assign`. #### [`PATCH /user-tasks/:userTaskKey`](/apis-tools/orchestration-cluster-api-rest/specifications/update-user-task.api.mdx) Use the `update` endpoint to change candidate users, groups, the due date, or the follow-up date by defining the `changeset`. You can also send it with an empty `changeset` and just pass an `action`. Use it to send `start`, `pause`, and `resume` actions. Additionally, you can send anything of interest or relevant for the audit log such as `escalate`, `requestFurtherInformation`, `uploadDocument`, or `openExternalApp`. An example request payload could look like this: ```js { "changeset": { "dueDate": "2024-03-18T20:47:20.340Z" } "action": "escalate" } ``` #### [`POST /user-tasks/:userTaskKey/completion`](/apis-tools/orchestration-cluster-api-rest/specifications/complete-user-task.api.mdx) Use the `completion` endpoint to complete a task. Pass along with it the outcome of the task via the `action` attribute, such as `approve` or `reject`. ### Listen to life cycle events To keep the life cycle customizable and flexible, there are four generic parent events that align with the API endpoints. These events contain an `action` attribute (except the `create` event) that contains the life cycle action that triggered this event. You can listen to these events by implementing a [custom exporter](/self-managed/concepts/exporters.md). Run your custom logic based on the `action` attribute and the payload in the events. To get started, use a pre-built exporter from the [Camunda community](https://github.com/orgs/camunda-community-hub/repositories?q=exporter) . #### `create` The `create` event is emitted when a task instance is successfully created by the engine. In case the `create` event does contain an assignee, no additional `assignment` event is fired. #### `assignment` The `assignment` event is emitted when task assignment changes. This includes actions such as `claim`, `assign`, `return`, or `unassign`. #### `update` The `update` event is emitted every time anything on the task changed except assignment, including candidate groups, candidate users, the due date, or the follow-up date. It is also emitted on custom life cycle actions that do not contain a payload, such as `start`, `pause`, or any other action of interest. #### `complete` The `complete` event is emitted on task completion. It can contain a custom action as well to indicate the outcome, such as `approved` or `rejected`. ## Task life cycle reporting You can use the stream of task life cycle events to populate an audit log or to build productivity reports. ### Task life cycle reporting in Optimize Optimize supports task productivity reports. However, it is currently limited to reports that measure assigned vs. unassigned time. In future versions, it is planned to calculate the net work and idle time of a task based on the following actions: - **Idle time:** Time a task was open, for example, time to `start`. - **Net working time:** Time during which a task was actually processed, for example, time from `start` to `complete`, minus the time during which it was paused (action `pause` until the next `resume` action). ### Export task life cycle events to external systems You can implement a [custom exporter](/self-managed/concepts/exporters.md) or use any from the [Camunda community](https://github.com/orgs/camunda-community-hub/repositories?q=exporter) to export task life cycle events to any external system. Use it to stream task events to BI or dashboarding tools, or use real-time stream processing frameworks like [Apache Spark](https://spark.apache.org/) for high-volume use cases. --- ## Task application architecture A typical task application architecture consists of a task application frontend, a backend-for-frontend, and one or more data sources or services that contain business data relevant for the application users to perform their work. The backend implements Camunda Zeebe and Tasklist clients to retrieve and interact with tasks via Camunda APIs. For historical process instance data, Operate is also required. Depending on the user task implementation type (job worker-based vs Camunda user task) you use in your processes, you need to run either the Tasklist or Zeebe client to run operations on tasks. Task, form, and variable retrieval happens via the API. Learn more about the differences of the task implementation types in the [migration guide for Camunda user tasks](/apis-tools/migration-manuals/migrate-to-camunda-user-tasks.md), and complete that migration before upgrading to 8.10 if you still use job worker-based user tasks. :::tip Starting a new project? Use Camunda user tasks to simplify your implementation. ::: Click on any element of this diagram to jump to the documentation page for the respective component: ```mermaid %%{init: {"flowchart": {"htmlLabels": true}} }%% flowchart LR subgraph Architecture direction LR subgraph Custom Task Application direction LR Frontend --- BFF B --- ExtData subgraph BFF[ ] direction TB B[BFF \n Backend for Frontend] ExtData[Business Data] end end subgraph Camunda 8 direction LR subgraph Tasklist Rest[Rest API] Forms end subgraph Zeebe ZeebeRest[Rest API] end Tasklist <-.-> Job[Job worker-based tasks] Tasklist <-.-> ZeebeTasks Rest <-.-> Forms Zeebe[Zeebe REST API] <-.-> ZeebeTasks[Zeebe-based tasks] end BFF -->|Query Tasks\nJob-Based or Zeebe| Tasklist BFF -->|Operation\nJob-based| Tasklist BFF -->|Operation\nZeebe| Zeebe end style Frontend fill:#2272c9,color:#fff style B fill:#2272c9,color:#fff style Rest fill:#10c95d,color:#fff style ZeebeRest fill:#ed7d31,color:#fff style Zeebe stroke:#ed7d31 style Job fill:#10c95d,color:#fff style ZeebeTasks fill:#ed7d31,color:#fff style subGraph1 fill:#e4eef8,stroke:#2272c9 style BFF fill:#c8dcf0,stroke:#2272c9 style ExtData fill:#a5caef,stroke:#2272c9 style Tasklist stroke:#10c95d,color:#000 click Forms "../../forms/introduction-to-forms" click Rest "../../../tasklist-api-rest/tasklist-api-rest-overview" click Job "../../../migration-manuals/migrate-to-camunda-user-tasks" click ZeebeTasks "../../../migration-manuals/migrate-to-camunda-user-tasks" click ZeebeRest "../../../zeebe-api-rest/zeebe-api-rest-overview" ``` Follow these resources to learn more about the individual components: - Learn how to use the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/specifications/assign-user-task.api.mdx) for task, variable, and form retrieval, and to run operations on Camunda user tasks. - If you still use older job worker-based user tasks, review [migrating to Camunda user tasks](/apis-tools/migration-manuals/migrate-to-camunda-user-tasks.md) and complete that migration before upgrading to 8.10, because this capability relied on the removed V1 Tasklist API. - Understand how to design, embed, and customize [forms](/apis-tools/frontend-development/03-forms/01-introduction-to-forms.md). - Understand how this architecture fits into the overall Camunda architecture with the [Java greenfield stack](/components/best-practices/architecture/deciding-about-your-stack.md). --- ## Introduction to forms Forms play a key role in giving work instructions, collecting information and making decisions within human task orchestration. Forms are lightweight user interfaces, tailored for focused data input in specific steps of a process, rendering the orchestration of human tasks more efficient than simply routing users to the applications that are orchestrated. Forms are commonly used in [user tasks](/components/modeler/bpmn/user-tasks/user-tasks.md#user-task-forms), but also as [start forms](/components/tasklist/userguide/starting-processes.md) to start a new process instance. ## Camunda Forms In Camunda 8, you can design forms using a drag'n'drop editor. The form editor is available in both Desktop and Web Modeler. Learn more about Camunda Forms and available components in the [Camunda Forms reference documentation](/components/modeler/forms/camunda-forms-reference.md), and learn how to design a human workflow with forms in the [getting started guide](/guides/getting-started-orchestrate-human-tasks.md). ## form-js Camunda Forms present a flexible, open solution to form creation. Camunda Forms are based on the [form-js library](https://github.com/bpmn-io/form-js) , maintained by Camunda. As a result, the form editor and renderer are non-proprietary, open-source technology, and can be used everywhere, also outside the context of Camunda 8. This unlocks a world of use cases, and eliminates any doubt around vendor lock-in or technical barriers. Form-js is a vanilla JavaScript library with [Preact](https://preactjs.com/) in the background, and can be used in any framework, from Angular to React. The resulting forms are serialized as a JSON document. The JSON document conforms to an open form schema that allows you to render the form using both the built-in form render, or even with a custom render. The form schema is extensible, allowing you to build your own extensions or custom components. :::tip Want to Contribute? We welcome your contributions to form-js! Whether it's fixing a bug, adding a feature or a new component, your input is valuable. You can also provide your ideas by opening issues for us or the community. **How to Contribute:** 1. 🌐 Visit our [GitHub Repository](https://github.com/bpmn-io/form-js). 2. 🛠️ Check out the issues or open a new one. 3. 💻 Fork the repository and submit a pull request. Let's make form-js better together! 👩‍💻👨‍💻 ::: Continue reading to learn how to setup, embed, and extend form-js to build form-based task applications for any use case. --- ## Concepts Use form-js, the open-source library that powers Camunda Forms, to embed forms anywhere from vanilla JavaScript to low-code application platforms. With form-js, you can view, visually edit, and simulate forms that are based on pure JSON. ## form-js basics The form-js project is made of three core libraries: the [form editor](https://github.com/bpmn-io/form-js/tree/develop/packages/form-js-editor) , the [form viewer](https://github.com/bpmn-io/form-js/tree/develop/packages/form-js-viewer) , and the [form playground](https://github.com/bpmn-io/form-js/tree/develop/packages/form-js-playground) . ### Form editor The [form editor](https://github.com/bpmn-io/form-js/tree/develop/packages/form-js-editor) allows to design forms with a drag'n'drop interface, and uses [FEEL expressions](/components/modeler/feel/what-is-feel.md) to execute form logic, such as visibility conditions, in realtime. Learn more about using the form editor in the [getting started guide](/components/modeler/forms/utilizing-forms.md). The form editor as it is shipped in Camunda 8 actually uses the [form playground](#form-playground), which provides realtime preview and validation functionality. ### Form viewer The [form viewer](https://github.com/bpmn-io/form-js/tree/develop/packages/form-js-viewer) renders a form built using the form editor. It is versatile and can be embedded in any JavaScript application to render a form and capture user interactions. Learn more about embedding the form viewer on the following pages. See the following example form using the form viewer, and interact with it: ### Form playground The [form playground](https://github.com/bpmn-io/form-js/tree/develop/packages/form-js-playground) is a tool to preview forms, simulate their behavior, and explore form-js in a playful manner. It combines the [editor](#form-editor) and the [viewer](#form-viewer) with mock data input and output panels to test a form and form editor features instantly. There is also a [Camunda-flavored version of the form playground](https://github.com/camunda/form-playground) , which closely resembles the form editor experience in Camunda Web and Desktop Modeler, and supports rapid development. The form playground mainly comprises the following areas: - The **component palette** to search and add components. - The **editor canvas**, allowing to compose a form by dragging components. - The **preview pane**, which shows an interactive preview of the form. The preview updates in real-time when a change happens in the editor, properties panel, or mock input data. - The **properties panel**, which is used to configure the properties of a component. - The **data input panel**, which allows to simulate the form preview using mock input data. - The **output panel**, which calculates and shows the current form output in real-time, based on the interactions with the preview. The input and output panel, together with the preview, come in handy to simulate the behavior of a form, and to validate or debug the configuration of one or multiple components, especially when using expressions extensively. Use the input data panel to simulate process variables, business objects, or static data used in your form. Try form playground Try the form playground yourself directly on the web, no log in needed. ## The form schema A form is serialized as plain JSON with a simple, flat structure to maximize flexibility and versatility. In the root, a form contains some metadata attributes. The main form is defined by a list of components, where the components carry their layout properties themselves (e.g. which row a component belongs to). This is in contrast to markup languages such as HTML, where the arrangement of the nodes determines the layout. This enables backward compatibility and compatibility with user-defined renderers. See this simple form schema for example, and the resulting form: ```json { "components": [ { "label": "First name", "type": "textfield", "layout": { "row": "Row_0hqc9xn", "columns": null }, "id": "Field_05l2s7c", "key": "firstName" }, { "label": "Last name", "type": "textfield", "layout": { "row": "Row_0hqc9xn", "columns": null }, "id": "Field_0nw7e1c", "key": "lastName" }, { "label": "Income", "type": "number", "layout": { "row": "Row_1ggwq2d", "columns": 8 }, "id": "Field_12yshuy", "key": "monthlyNetIncome", "description": "Monthly net income", "appearance": { "prefixAdorner": "USD" }, "increment": "100", "validate": { "required": true, "min": 0 } } ], "type": "default", "id": "ExampleForm", "executionPlatform": "Camunda Cloud", "executionPlatformVersion": "8.4.0", "exporter": { "name": "Camunda Modeler", "version": "5.18.0" }, "schemaVersion": 12 } ``` All form-js packages share the same [JSON schema](https://github.com/bpmn-io/form-js/tree/develop/packages/form-json-schema) for forms. This enables the interoperability of the created forms between the form editor and the viewer and possibly also between custom-made form renderers, or translating from a Camunda Form to another form. Using the form schema, you can write extensions to existing components, while still receiving benefits from updates made to the core form-js libraries. The schema abstracts the form model from the viewer, and allows you to inject another expression or templating language as an alternative to FEEL, since expressions are simply stored as strings. The schema is built on top of and validated by [`json-schema@draft-07`](https://json-schema.org/draft-07/json-schema-release-notes.html). :::tip You can use tools like this [JSON Schema Viewer](https://navneethg.github.io/jsonschemaviewer/) to explore the schema visually, or this [tool from Atlassian](https://json-schema.app/view/%23?url=https%3A%2F%2Funpkg.com%2F%40bpmn-io%2Fform-json-schema%401.6.0%2Fresources%2Fschema.json) to validate a form against the schema. ::: ### Schema variables Form-js comes with versatile methods to extract the expected input and output variables from a form schema. This makes it easy to validate the input and output of a form, and you can combine it with data validation libraries like [joi](https://github.com/hapijs/joi) to ensure type and schema safety. Learn more about schema variables in the [embedding guide](./02-embed-in-javascript.md). ## Examples Visit the [form-js examples repository](https://github.com/bpmn-io/form-js-examples) to explore form-js by playing with the toolkit. --- ## Embed forms in JavaScript Learn how to embed the form viewer in your own applications and web pages using JavaScript. ## Set up form-js Set up the [form viewer](https://github.com/bpmn-io/form-js/tree/develop/packages/form-js-viewer) in your own JavaScript projects by importing the library from NPM or a CDN. Alternatively, you can fork the code and [build it yourself](https://github.com/bpmn-io/form-js?tab=readme-ov-file#build-and-run) . ### NPM If you use [NPM](https://docs.npmjs.com/getting-started/what-is-npm), install the form viewer as follows: ```sh npm install @bpmn-io/form-js-viewer ``` ### CDN You can import the form viewer from a content delivery network (CDN), for example when you want to use the form viewer directly in a browser environment without bundling it with your application. Form-js is served via unpkg. Specify the version you want to reference in the URL. ```js ``` If you want to automatically use the latest version of the form viewer, you can specify only the major version. ```js ``` Make sure to import the stylesheets as well, and ensure that the version matches: ```js ``` ## Embed into your application Embedding a form with the form viewer requires only a few steps. 1. Import the library 2. Specify the render target div and render the form 3. Import the [form schema](./01-concepts.md#the-form-schema) ```js const form = new Form({ container: document.querySelector("#form"), }); // schema of the form to embed const schema = { type: "default", id: "TestForm", components: [ { key: "name", label: "Name", type: "textfield", validate: { required: true, }, }, ], }; await form.importSchema(schema); ``` This results in: You can also detach a form from a container and attach to another during form runtime. Learn more about that in the [API documentation](https://github.com/bpmn-io/form-js/tree/develop/packages/form-js-viewer#formattachtoparentnode-htmlelement--void) . ### Input form context data To provide data to your form, such as process variables or business objects, pass a JSON object containing this data to the `importSchema` function. ```js ... const schema = { ... }; // form context/input data const data = { name: 'ACME Corp' }; await form.importSchema(schema, data); ``` This results in: You can use context data not just to populate field values, but also to control form behavior, to provide options for select fields, or even to provide localization to your forms. You can fetch business data via an API first, and inject it via the data object. The following example demonstrates how to provide select options via context data, by using a `valuesExpression`. ```js ... const schema = { components: [ { label: "Business domain", type: "select", key: "domain", valuesExpression: "=businessDomains" } ], type: "default", id: "TestForm", schemaVersion: 12 }; // form context/input data const data = { businessDomains: ["Software development", "Consulting"] }; await form.importSchema(schema, data); ``` This results in: ### Validate a form Before you allow a user to submit a form, you can use the `validate` function to ensure that all validation rules of your form are met and that all required fields are completed. Learn more in the [form API documentation](https://github.com/bpmn-io/form-js/tree/develop/packages/form-js-viewer#formvalidate--errors) . ```js const errors = form.validate(); if (Object.keys(errors).length) { console.error("Form has errors", errors); } ``` ### Trigger and listen to form events Form-js provides a comprehensive set of events and APIs to react on form state changes. You can listen for - [form state changes](https://github.com/bpmn-io/form-js/tree/develop/packages/form-js-viewer#changed---data-errors-) , - [form submissions](https://github.com/bpmn-io/form-js/tree/develop/packages/form-js-viewer#submit---data-errors-) , - [form layout changes](https://github.com/bpmn-io/form-js/tree/develop/packages/form-js-viewer#layouting-events) . In addition, hook into [lifecycle events](https://github.com/bpmn-io/form-js/tree/develop/packages/form-js-viewer#lifecycle-events) to add custom logic (e.g. initialize listeners on form fields after the form loaded). Learn more about the full API in the [GitHub repository documentation](https://github.com/bpmn-io/form-js/tree/develop/packages/form-js-viewer#api) . ### Retrieve form output data To retrieve the current form output data on any form state change, listen to the [`changed`](https://github.com/bpmn-io/form-js/tree/develop/packages/form-js-viewer#changed---data-errors--) event. To retrieve the data on submit, listen to the [`submit`](https://github.com/bpmn-io/form-js/tree/develop/packages/form-js-viewer#formsubmit---data-data-errors-errors-) event. ### Retrieve schema variables Use the `getSchemaVariables` util to retrieve the [variables defined in a form schema](./01-concepts.md#schema-variables). This is useful to gather what data is consumed and produced by a form. ```javascript const variables = getSchemaVariables(schema); console.log("Schema variables", variables); ``` It is also possible to distinct between input and output variables: ```javascript const outputVariables = getSchemaVariables(schema, { inputs: false }); const inputVariables = getSchemaVariables(schema, { outputs: false }); ``` :::note form-js does not enforce typing. Retrieving schema variables returns the variable names, but not the type or whether the variable is optional (i.e. whether the field is required or not). To retrieve the expected type of the variable, parse the form schema manually. To enforce the typing of input variables, use validation libraries such as [joi](https://github.com/hapijs/joi) . ::: ### Next steps - [Style forms](../03-customize-and-extend/01-styling.md) using CSS and custom renderers. - [Integrate external data via APIs](../03-customize-and-extend/03-integrate-api-data.md) into your forms and task applications. - Create [custom form components](../03-customize-and-extend/02-custom-components.md) to design flexible forms tailored to your individual use case. --- ## Styling Forms can be easily styled by combining defining own CSS rules and overriding a set of CSS variables. If you want to go beyond CSS, you can fork the [form viewer](https://github.com/bpmn-io/form-js/tree/develop/packages/form-js-viewer) and change the HTML returned by the individual form component renderers. ## Styling via CSS ### CSS variables The variables are defined at the root of the form-js container: ```css .fjs-container { /** * Color settings. Specify color variables in the following schema: * 1 - use specified layer * 2 - use layer one * 3 - use fallback */ --color-background: var(--cds-field, var(--cds-field-01, var(--color-white))); --color-background-disabled: var( --cds-background, var(--color-grey-225-10-95) ); --color-background-readonly: var( --cds-background, var(--color-grey-225-10-95) ); --color-background-adornment: var( --cds-field, var(--cds-field-01, var(--color-grey-225-10-95)) ); --color-background-inverted: var( --cds-background-inverse, var(--color-grey-225-10-90) ); --color-background-inverted-hover: var( --cds-background-inverse-hover, var(--color-grey-225-10-93) ); --color-background-active: var( --cds-background-active, var(--color-grey-225-10-75) ); --color-layer: var(--cds-layer, var(--cds-layer-01, var(--color-white))); --color-layer-accent: var(--cds-layer-accent, var(--color-grey-0-0-88)); --color-icon-base: var(--cds-icon-primary, var(--color-black)); --color-icon-inverted: var(--cds-icon-inverse, var(--color-black)); --color-text: var(--cds-text-primary, var(--color-grey-225-10-15)); --color-text-light: var(--cds-text-secondary, var(--color-grey-225-10-35)); --color-text-lighter: var(--cds-text-secondary, var(--color-grey-225-10-45)); --color-text-lightest: var( --cds-text-placeholder, var(--color-grey-225-10-55) ); --color-text-inverted: var(--cds-text-inverse, var(--color-text)); --color-text-disabled: var(--cds-text-disabled, var(--color-text-light)); --color-borders: var( --cds-border-strong, var(--cds-border-strong-01, var(--color-grey-225-10-55)) ); --color-borders-group: var(--cds-border-subtle, var(--color-grey-225-10-85)); --color-borders-table: var(--color-borders-group); --color-borders-disabled: var( --cds-border-disabled, var(--color-grey-225-10-75) ); --color-borders-adornment: var( --cds-border-subtle, var(--cds-border-subtle-01, var(--color-grey-225-10-85)) ); --color-borders-readonly: var( --cds-border-subtle, var(--color-grey-225-10-75) ); --color-borders-inverted: var( --cds-border-inverse, var(--color-grey-225-10-90) ); --color-warning: var(--cds-text-error, var(--color-red-360-100-45)); --color-warning-light: var(--cds-text-error, var(--color-red-360-100-92)); --color-accent: var(--cds-link-primary, var(--color-blue-205-100-40)); --color-accent-readonly: var( --cds-border-strong, var(--cds-border-strong-01, var(--color-grey-225-10-55)) ); --color-datepicker-focused-day: var( --cds-button-primary, var(--color-grey-225-10-55) ); --color-shadow: var(--cds-shadow, var(--color-grey-225-10-85)); /* font + text settings */ --font-family: "IBM Plex Sans", sans-serif; --font-size-group: 15px; --font-size-base: 14px; --font-size-input: 14px; --font-size-label: 12px; --line-height-base: 20px; --line-height-input: 18px; --line-height-label: 16px; --letter-spacing-base: 0.16px; --letter-spacing-input: 0.16px; --letter-spacing-label: 0.32px; /* field settings */ --form-field-height: 36px; --border-definition: 1px solid var(--color-borders); --border-definition-adornment: 1px solid var(--color-borders-adornment); --outline-definition: 1px solid var(--cds-focus, var(--color-borders)); --button-warning-outline-definition: 2px solid var(--color-warning); --border-definition-disabled: 1px solid var(--color-borders-disabled); --border-definition-readonly: 1px solid var(--color-borders-readonly); } ``` ### Styleable classes The simplest way to find the right styleable elements to override is inspecting form-js using your browser's developer tools. Scope rules with the `.fjs-container` class to prevent CSS conflicts. For example, to override field borders for single-line fields: ```css .fjs-container .fjs-input-group { border-width: 0 0 1px 0; } ``` ### Example Camunda 8 web applications are built using the IBM Carbon design system. Forms rendered in Tasklist appear in this design system by default. Visit the [Carbon form-js styles repository](https://github.com/bpmn-io/form-js/tree/develop/packages/form-js-carbon-styles) to learn how to create your own form styles. Basic style Custom style (Material-like) ## Styling via form viewer customization The [form viewer](https://github.com/bpmn-io/form-js/tree/develop/packages/form-js-viewer) contains all [basic form components](https://github.com/bpmn-io/form-js/tree/develop/packages/form-js-viewer/src/render/components/form-fields) shipped in Camunda Forms. For full flexibility, fork the library and change the returned HTML of the individual components, or override existing components via [custom form components](02-custom-components.md). ### Example The following example demonstrates replacing the default rendering of the [text field component](https://github.com/bpmn-io/form-js/blob/develop/packages/form-js-viewer/src/render/components/form-fields/Textfield.js) with [Material UI](https://mui.com/material-ui/react-text-field/). ```js title="packages/form-js-viewer/src/render/components/form-fields/Textfield.js" ... export default function Textfield(props) { const { ... } = props; ... const onInputBlur = () => { ... }; return // using MUI TextField instead of default { ... }} /> ; ... } ``` --- ## Custom components Form-js comes with an extension point to hook in custom components. You can define the renderer, the configuration options of the component in the properties panel, and the palette entry. Custom components are built and distributed separately from the form viewer and renderer, and can be plugged in on demand by registering them as `additionalModules`. ```js new Form({ container, schema, data, additionalModules: [MyCustomComponent], }); ``` Read the [step-by-step guide](https://github.com/bpmn-io/form-js-examples/tree/master/custom-components) and inspect the example component to learn how to write your own custom components. :::note Custom components currently can not be imported into Camunda Web or Desktop Modeler. If you use custom components, you need to host the form editor yourself. ::: ## Use cases for custom components - **Integration with external APIs:** create components that integrate with external APIs to fetch real-time data or perform specific actions. For example, a location input component could connect to a mapping API to suggest locations as users type. - **Tailored services architecture:** write your own backend (micro-)services coupled to your components, and let the components communicate with these services to fetch domain-specifc or internal data in a secure fashion. - **File upload:** develop a component that allows users to upload a file, which is stored in a document storage system and returns the reference ID or URL of the document as the component/form output. - **Data visualization:** build components that visualize data directly within the form. This could include charts, graphs, or other visual representations of information relevant to the form's purpose. - **Geolocation services:** create components that leverage geolocation services to capture or display location-based information. This can be helpful for forms that require location-specific data. - **Payment processing:** develop secure components that integrate with payment gateways to handle financial transactions within a form. --- ## Integrate API data Read this page to learn how to integrate external business data into your forms via APIs. ## Load data on form initation Before you initiate your form with data, make sure to fetch external business data and merge it with the process variables first. Data that is not bound to a form field using a key will not be submitted, keeping process instance data clean. As an example, use a `valuesExpression` in your form to populate the options of a select field. ```js //... const schema = { components: [ { label: "Opportunities", type: "select", key: "opportunity", valuesExpression: "=external.salesforce.opportunities", }, ], type: "default", id: "TestForm", schemaVersion: 12, }; const response = await fetch(url, fetchOptions); const opportunities = await response.json(); //... // form context/input data const data = { ...processVariables, external: { salesforce: { opportunities, }, // ... }, }; await form.importSchema(schema, data); ``` ## Load data on runtime with form events :::info Workaround Currently, there is no built-in way to update a form's context data on runtime. However, a workaround exists. ::: To load and update data on runtime (e.g. when searching in a searchable select box, or entering a query in a text field), follow these steps: 1. Listen to the `changed`, `formField.blur`, or `formField.search` event. 2. Gather the current form state from the `changed` event, or call the `submit` function to retrieve the data. 3. Find the query term in the changed state that is relevant for your API calls. 4. Run your API call, e.g. fetch records based on the query term. 5. Re-import the form schema but with the updated data (the current form state you obtained earlier, merged with the API results). Don't forget to block the UI, e.g. using a loading spinner. ## Load data on runtime with a custom component A convenient way to provide realtime data fetching capabilities to your form designers is to design a custom component. For example, you can create a searcheable select that allows users to search and select a record from a CRM system. With custom components, you can create any logic for data retrieval without limitations. You could consider writing your own backend (micro-)services coupled to your components, and let the components communicate with these services to fetch domain-specifc or internal data in a secure fashion. Learn how to develop a custom component in the [custom component guide](./02-custom-components.md). :::note Custom components currently can not be imported into Camunda Web or Desktop Modeler. If you use custom components, you need to host the form editor yourself. ::: --- ## Java client ## About The Camunda Java Client is the official Java library for building process applications that integrate with Camunda 8. This client provides everything needed to interact with the Orchestration Cluster programmatically, such as orchestrating microservices, managing human tasks, or visualizing process data, and so on. For example, you can use it to build a job worker that handles polling for available jobs, use SLF4J for logging useful notes. :::info Public API The Camunda Java Client is part of the Camunda 8 [public API](/reference/public-api.md) and follows [Semantic Versioning](https://semver.org/) (except for alpha features). Minor and patch releases will not introduce breaking changes. ::: ## What is the Camunda Java Client? The Camunda Java Client is a comprehensive library enabling Java developers to: - **Deploy processes and decisions** to Camunda 8 clusters - **Start and manage processes** programmatically - **Implement job workers** to handle automated tasks within your processes - **Query and manage process data** via the Orchestration Cluster API It supports both REST and gRPC protocols, authentication setup, and provides robust error handling with retry mechanisms. :::info Migration from Zeebe Java Client **The Camunda Java Client replaces the Zeebe Java Client as of version 8.8.** - Provides improved structure and full Orchestration Cluster API support - Uses **REST** as default communication protocol (gRPC configurable) - The Zeebe Java Client will be **removed in version 8.10** - **Migrate before upgrading to 8.10** to avoid breaking changes See our [migration guide](../migration-manuals/migrate-to-camunda-java-client.md) for details. ::: ## What can you build with it? Use the Camunda Java Client to build: - **Job workers** that perform automated tasks and call external systems (APIs, databases, file systems) - **Integration services** that connect Camunda processes with existing systems or third-party services - **Data processing applications** that leverage process data for visualization, analytics, or business intelligence ## Get started ### Step 1: Add the dependency Add the Camunda Java Client to your project: **Maven:** ```xml io.camunda camunda-client-java ${camunda.version} ``` **Gradle:** ```groovy implementation 'io.camunda:camunda-client-java:${camunda.version}' ``` Use the latest version from [Maven Central](https://search.maven.org/artifact/io.camunda/camunda-client-java). ### Step 2a: Connect to a Self-Managed Orchestration Cluster Create a client instance to connect to your Self-Managed Camunda 8 cluster. Select the appropriate [authentication method](../orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md) for your environment: **Use for:** Local development when security is not required. ```java private static final String CAMUNDA_GRPC_ADDRESS = "[Address of Zeebe API (gRPC) - default: http://localhost:26500]"; private static final String CAMUNDA_REST_ADDRESS = "[Address of the Orchestration Cluster API - default: http://localhost:8080]"; public static void main(String[] args) { try (CamundaClient client = CamundaClient.newClientBuilder() .grpcAddress(URI.create(CAMUNDA_GRPC_ADDRESS)) .restAddress(URI.create(CAMUNDA_REST_ADDRESS)) .build()) { // Test the connection client.newTopologyRequest().execute(); System.out.println("Connected to Camunda 8!"); } } ``` **What this code does** 1. **Creates a no-authentication provider** – Configures the client to skip authentication. 2. **Builds a client using the protocol-specified transport** – Uses plaintext or TLS depending on whether the addresses use `http` or `https`. 3. **Connects to both APIs** – Configures access to the Zeebe gRPC and Orchestration Cluster REST APIs. 4. **Tests the connection** – Verifies connectivity by requesting cluster topology information. **Environment variables option** You can also configure the client using environment variables: ```bash export CAMUNDA_GRPC_ADDRESS='[Address of Zeebe API (gRPC) - default: http://localhost:26500]' export CAMUNDA_REST_ADDRESS='[Address of the Orchestration Cluster API - default: http://localhost:8080]' ``` ```java CamundaClient client = CamundaClient.newClientBuilder().build(); ``` The client will automatically read these environment variables and configure the appropriate authentication method. Ensure addresses are in absolute URI format: `scheme://host(:port)`. The protocol (`http` or `https`) determines whether the connection is encrypted. **Use for:** Development or testing environments with username/password protection. ```java private static final String CAMUNDA_GRPC_ADDRESS = "[Address of Zeebe API (gRPC) - default: http://localhost:26500]"; private static final String CAMUNDA_REST_ADDRESS = "[Address of the Orchestration Cluster API - default: http://localhost:8080]"; private static final String CAMUNDA_BASIC_AUTH_USERNAME = "[Your username - default: demo]"; private static final String CAMUNDA_BASIC_AUTH_PASSWORD = "[Your password - default: demo]"; public static void main(String[] args) { CredentialsProvider credentialsProvider = new BasicAuthCredentialsProviderBuilder() .username(CAMUNDA_BASIC_AUTH_USERNAME) .password(CAMUNDA_BASIC_AUTH_PASSWORD) .build(); try (CamundaClient client = CamundaClient.newClientBuilder() .grpcAddress(URI.create(CAMUNDA_GRPC_ADDRESS)) .restAddress(URI.create(CAMUNDA_REST_ADDRESS)) .credentialsProvider(credentialsProvider) .build()) { // Test the connection client.newTopologyRequest().execute(); System.out.println("Connected to Camunda 8!"); } } ``` **What this code does** 1. **Sets up username/password authentication** – Configures the client to use basic credentials. 2. **Builds a client using the protocol-specified transport** – Establishes an unencrypted connection if the addresses use `http` or an encrypted connection if they use `https`. 3. **Connects to both APIs** – Configures access to the Zeebe gRPC and Orchestration Cluster REST APIs. 4. **Tests the connection** – Verifies authentication by requesting cluster topology information. **Environment variables option** You can also set connection details via environment variables to create the client more simply: ```bash export CAMUNDA_GRPC_ADDRESS='[Address of Zeebe API (gRPC) - default: http://localhost:26500]' export CAMUNDA_REST_ADDRESS='[Address of the Orchestration Cluster API - default: http://localhost:8080]' export CAMUNDA_BASIC_AUTH_USERNAME='[Your username - default: demo]' export CAMUNDA_BASIC_AUTH_PASSWORD='[Your password - default: demo]' ``` ```java CamundaClient client = CamundaClient.newClientBuilder().build(); ``` The client will automatically read the environment variables and configure the appropriate authentication method. :::note - Ensure addresses use absolute URI format: `scheme://host(:port)`. - By default, environment variables override any values provided in Java code. To give Java code values precedence, use the `.applyEnvironmentOverrides(false)` method on `BasicAuthCredentialsProviderBuilder`. - The client adds an `Authorization` header to each request with the value `Basic username:password` (where `username:password` is base64 encoded). ::: **Use for:** Self-Managed production environments with OIDC-based authentication. Standard `client_secret_basic` authentication method. ```java private static final String CAMUNDA_GRPC_ADDRESS = "[Address of Zeebe API (gRPC) - default: http://localhost:26500]"; private static final String CAMUNDA_REST_ADDRESS = "[Address of the Orchestration Cluster API - default: http://localhost:8080]"; // There are three ways to define the authorization server URL private static final String CAMUNDA_AUTHORIZATION_SERVER_URL = "[OAuth URL e.g. http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token]"; private static final String CAMUNDA_WELL_KNOWN_CONFIGURATION_URL = "[OAuth URL e.g. http://localhost:18080/auth/realms/camunda-platform/.well-known/openid-configuration]"; private static final String CAMUNDA_ISSUER_URL = "[OAuth URL e.g. http://localhost:18080/auth/realms/camunda-platform]"; // Audience is the API that will receive the token, such as the Orchestration Cluster for example private static final String AUDIENCE = "[Orchestration Cluster audience]"; // Scope is the permission requested from the IdP (or leave empty) private static final String SCOPE = "[optional additional scopes]"; private static final String CLIENT_ID = "[Client ID registered in your IdP]"; private static final String CLIENT_SECRET = "[Client Secret]"; public static void main(String[] args) { CredentialsProvider credentialsProvider = new OAuthCredentialsProviderBuilder() // Select the authorization server configuration option according to your properties from above .authorizationServerUrl(CAMUNDA_AUTHORIZATION_SERVER_URL) .issuerUrl(CAMUNDA_ISSUER_URL) .wellKnownConfigurationUrl(CAMUNDA_WELL_KNOWN_CONFIGURATION_URL) // End authorization server .audience(AUDIENCE) .scope(SCOPE) .clientId(CLIENT_ID) .clientSecret(CLIENT_SECRET) .build(); try (CamundaClient client = CamundaClient.newClientBuilder() .grpcAddress(URI.create(CAMUNDA_GRPC_ADDRESS)) .restAddress(URI.create(CAMUNDA_REST_ADDRESS)) .credentialsProvider(credentialsProvider) .build()) { // Test the connection client.newTopologyRequest().execute(); System.out.println("Connected to Camunda 8!"); } } ``` **Notes for Microsoft Entra ID** - Use `scope=CLIENT_ID_OC + "/.default"` instead of `scope=CLIENT_ID_OC`. - The issuer URL is typically in the format: ``` https://login.microsoftonline.com//v2.0 ``` :::note Audience validation If you have [configured the audiences property for the Orchestration Cluster (`camunda.security.authentication.oidc.audiences`)](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#camunda.security.authentication.oidc), the Orchestration Cluster will validate the audience claim in the token against the configured audiences. Make sure your token includes the correct audience from the Orchestration Cluster configuration, or add your audience to the configuration. Often this is the client ID you used when setting up the Orchestration Cluster. ::: **What this code does** 1. **Sets up OAuth2 authentication** – Configures the client to use OAuth tokens from your identity provider. 2. **Builds a secure client** – Establishes an encrypted connection to your self-managed cluster (default). 3. **Connects to both APIs** – Configures access to the Zeebe gRPC and Orchestration Cluster REST APIs. 4. **Tests the connection** – Verifies OAuth authentication by requesting cluster topology information. **Environment variables option** You can also set connection details via environment variables to create the client more simply: ```bash export CAMUNDA_GRPC_ADDRESS='[Address of Zeebe API (gRPC) - default: http://localhost:26500]' export CAMUNDA_REST_ADDRESS='[Address of the Orchestration Cluster API - default: http://localhost:8080]' # There are three ways to define the authorization server URL export CAMUNDA_AUTHORIZATION_SERVER_URL='[OAuth URL e.g. http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token]' export CAMUNDA_WELL_KNOWN_CONFIGURATION_URL='[OAuth URL e.g. http://localhost:18080/auth/realms/camunda-platform/.well-known/openid-configuration]' export CAMUNDA_ISSUER_URL='[OAuth URL e.g. http://localhost:18080/auth/realms/camunda-platform]' export CAMUNDA_TOKEN_AUDIENCE='[Audience]' export CAMUNDA_CLIENT_ID='[Client ID]' export CAMUNDA_CLIENT_SECRET='[Client Secret]' ``` ```java CamundaClient client = CamundaClient.newClientBuilder().build(); ``` The client will automatically read the environment variables and configure the appropriate authentication method. :::note - Ensure addresses use absolute URI format: `scheme://host(:port)`. - By default, environment variables override any values provided in Java code. To give Java code values precedence, use the `.applyEnvironmentOverrides(false)` method on `OAuthCredentialsProviderBuilder`. - The client adds an `Authorization` header to each request with the value `Bearer `. The token is obtained from the authorization server, cached to avoid unnecessary requests, and refreshed lazily upon expiration. - There are three ways to define the token URL. They're prioritized as follows: 1. Provide the `camunda.client.auth.token-url`. 2. Provide the issuer's well-known configuration URL `camunda.client.auth.well-known-configuration-url`. This extracts the token URL from the `token_url` field in the loaded configuration. 3. Provide the issuer's URL `camunda.client.auth.issuer-url`. This generates the well-known configuration URL and extracts the token URL from the `token_url` field in the loaded configuration. ::: **Use for:** Production environments with mTLS certificate-based client authentication. Several identity providers, such as Keycloak, support client mTLS authentication as an alternative to `client_secret_basic`. **Prerequisites** - Properly configured KeyStore and TrustStore - Both your application and identity provider share the same CA trust certificates - Certificates for the identity provider are signed by a trusted CA - The application DN is registered in the identity provider client authorization details ```java private static final String CAMUNDA_GRPC_ADDRESS = "[Address of Zeebe API (gRPC) - default: http://localhost:26500]"; private static final String CAMUNDA_REST_ADDRESS = "[Address of the Orchestration Cluster API - default: http://localhost:8080]"; // There are three ways to define the authorization server URL private static final String CAMUNDA_AUTHORIZATION_SERVER_URL = "[OAuth URL e.g. http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token]"; private static final String CAMUNDA_WELL_KNOWN_CONFIGURATION_URL = "[OAuth URL e.g. http://localhost:18080/auth/realms/camunda-platform/.well-known/openid-configuration]", private static final String CAMUNDA_ISSUER_URL = "[OAuth URL e.g. http://localhost:18080/auth/realms/camunda-platform]"; private static final String AUDIENCE = "[Audience - default: zeebe-api]"; private static final String CLIENT_ID = "[Client ID]"; private static final Path KEYSTORE_PATH = Paths.get("/path/to/keystore.p12"); private static final String KEYSTORE_PASSWORD = "password"; private static final String KEYSTORE_KEY_PASSWORD = "password"; private static final Path TRUSTSTORE_PATH = Paths.get("/path/to/truststore.jks"); private static final String TRUSTSTORE_PASSWORD = "password"; public static void main(String[] args) { CredentialsProvider credentialsProvider = new OAuthCredentialsProviderBuilder() // Select the authorization server configuration option according to your properties from above .authorizationServerUrl(CAMUNDA_AUTHORIZATION_SERVER_URL) .issuerUrl(CAMUNDA_ISSUER_URL) .wellKnownConfigurationUrl(CAMUNDA_WELL_KNOWN_CONFIGURATION_URL) // End authorization server .audience(AUDIENCE) .clientId(CLIENT_ID) .keystorePath(KEYSTORE_PATH) .keystorePassword(KEYSTORE_PASSWORD) .keystoreKeyPassword(KEYSTORE_KEY_PASSWORD) .truststorePath(TRUSTSTORE_PATH) .truststorePassword(TRUSTSTORE_PASSWORD) .build(); try (CamundaClient client = CamundaClient.newClientBuilder() .grpcAddress(URI.create(CAMUNDA_GRPC_ADDRESS)) .restAddress(URI.create(CAMUNDA_REST_ADDRESS)) .credentialsProvider(credentialsProvider) .build()) { // Test the connection client.newTopologyRequest().execute(); System.out.println("Connected to Camunda 8!"); } } ``` **What this code does** 1. **Sets up mTLS certificate authentication** – Configures the client to authenticate using client certificates with OAuth. 2. **Builds a secure client** – Establishes an encrypted connection using mutual TLS authentication. 3. **Connects to both APIs** – Configures access to the Zeebe gRPC and Orchestration Cluster REST APIs. 4. **Tests the connection** – Verifies certificate authentication by requesting cluster topology information. **Environment variables option** You can also set connection details via environment variables to create the client more simply: ```bash export CAMUNDA_GRPC_ADDRESS='[Address of Zeebe API (gRPC) - default: http://localhost:26500]' export CAMUNDA_REST_ADDRESS='[Address of the Orchestration Cluster API - default: http://localhost:8080]' # There are three ways to define the authorization server URL export CAMUNDA_AUTHORIZATION_SERVER_URL='[OAuth URL e.g. http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token]' export CAMUNDA_WELL_KNOWN_CONFIGURATION_URL='[OAuth URL e.g. http://localhost:18080/auth/realms/camunda-platform/.well-known/openid-configuration]' export CAMUNDA_ISSUER_URL='[OAuth URL e.g. http://localhost:18080/auth/realms/camunda-platform]' export CAMUNDA_TOKEN_AUDIENCE='[Audience - default: zeebe-api]' export CAMUNDA_CLIENT_ID='[Client ID]' export CAMUNDA_CLIENT_SECRET='[Client Secret]' export CAMUNDA_SSL_CLIENT_KEYSTORE_PATH='[Keystore path]' export CAMUNDA_SSL_CLIENT_KEYSTORE_SECRET='[Keystore password]' export CAMUNDA_SSL_CLIENT_KEYSTORE_KEY_SECRET='[Keystore material password]' export CAMUNDA_SSL_CLIENT_TRUSTSTORE_PATH='[Truststore path]' export CAMUNDA_SSL_CLIENT_TRUSTSTORE_SECRET='[Truststore password]' ``` ```java CamundaClient client = CamundaClient.newClientBuilder().build(); ``` The client automatically reads environment variables and configures the appropriate authentication method. Refer to your identity provider documentation for configuring mutual TLS authentication. For example, see [Keycloak](https://www.keycloak.org/server/mutual-tls). :::note - Ensure addresses use absolute URI format: `scheme://host(:port)`. - By default, environment variables override any values provided in Java code. To give Java code values precedence, use the `.applyEnvironmentOverrides(false)` method on `OAuthCredentialsProviderBuilder`. - The client adds an `Authorization` header to each request with the value `Bearer `. The token is obtained from the authorization server, cached to avoid unnecessary requests, and refreshed lazily upon expiration. - There are three ways to define the token URL. They're prioritized as follows: 1. Provide the `camunda.client.auth.token-url`. 2. Provide the issuer's well-known configuration URL `camunda.client.auth.well-known-configuration-url`. This extracts the token URL from the `token_url` field in the loaded configuration. 3. Provide the issuer's URL `camunda.client.auth.issuer-url`. This generates the well-known configuration URL and extracts the token URL from the `token_url` field in the loaded configuration. ::: ### Step 2b: Configure the Orchestration Cluster connection for SaaS **Use for:** Camunda 8 SaaS environments. Get the values below from your [Camunda Console client credentials](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client). ```java private static final String CAMUNDA_CLUSTER_ID = "[Cluster ID from Console]"; private static final String CAMUNDA_CLIENT_ID = "[Client ID from Console]"; private static final String CAMUNDA_CLIENT_SECRET = "[Client Secret from Console]"; private static final String CAMUNDA_CLUSTER_REGION = "[Cluster Region from Console]"; public static void main(String[] args) { try (CamundaClient client = CamundaClient.newCloudClientBuilder() .withClusterId(CAMUNDA_CLUSTER_ID) .withClientId(CAMUNDA_CLIENT_ID) .withClientSecret(CAMUNDA_CLIENT_SECRET) .withRegion(CAMUNDA_CLUSTER_REGION) .build()) { // Test the connection client.newTopologyRequest().execute(); System.out.println("Connected to Camunda 8!"); } } ``` **What this code does** 1. **Sets up SaaS authentication** – Configures the client to connect to Camunda 8 SaaS using your cluster credentials. 2. **Builds a cloud client** – Creates a client optimized for SaaS with automatic endpoint discovery. 3. **Connects to your cluster** – Uses your cluster ID and region to locate and connect to the correct SaaS instance. 4. **Tests the connection** – Verifies SaaS authentication by requesting cluster topology information. **Environment variables option** You can also set connection details via environment variables to create the client more simply: ```bash export CAMUNDA_GRPC_ADDRESS='[Orchestration Cluster gRPC Address from Console]' export CAMUNDA_REST_ADDRESS='[Orchestration Cluster REST Address from Console]' export CAMUNDA_OAUTH_URL='[OAuth URL from Console]' export CAMUNDA_TOKEN_AUDIENCE='[Audience from Console - default: zeebe.camunda.io]' export CAMUNDA_CLIENT_ID='[Client ID from Console]' export CAMUNDA_CLIENT_SECRET='[Client Secret from Console]' ``` ```java CamundaClient client = CamundaClient.newClientBuilder().build(); ``` The client will automatically read the environment variables and configure the appropriate authentication method. :::note Ensure addresses are in absolute URI format: `scheme://host(:port)`. ::: ### Step 3: Start building your process application With a connected client, you are ready to build your process application. Below are the core operations you’ll typically perform, along with guidance on the next steps. #### Essential operations **Deploy a process:** ```java final DeploymentEvent deploymentEvent = client.newDeployResourceCommand() .addResourceFromClasspath("process.bpmn") .execute(); ``` This deploys your BPMN process definition to the cluster. Place your `.bpmn` files in `src/main/resources` and reference them by filename. **Start a process instance:** ```java final ProcessInstanceEvent processInstanceEvent = client.newCreateInstanceCommand() .bpmnProcessId("my-process") .latestVersion() .variables(Map.of("orderId", "12345", "amount", 100.0)) .execute(); ``` This creates a new instance of your process. The `bpmnProcessId` should match the Process ID from your BPMN file, and you can pass initial variables as a Map. For a comprehensive example demonstrating these steps, see the [DeployAndComplete example](https://github.com/camunda-community-hub/camunda-8-examples/blob/main/camunda-client-plain-java/src/main/java/io/camunda/example/e2e/process/DeployAndComplete.java) in the Camunda 8 examples repository. This example illustrates a complete workflow from process deployment to job completion. ## Key features and capabilities - **Full Orchestration Cluster 8 API support:** Access all Orchestration Cluster API capabilities, including process deployment, management, job handling, and querying process data. - **Multiple authentication methods:** Supports no authentication (development), basic authentication, and OIDC access tokens for production environments. - **Automatic token management:** Handles authentication token acquisition and renewal automatically—no manual token management required. - **Protocol flexibility:** Choose between REST and gRPC protocols depending on your requirements and infrastructure. ## Next steps and resources **Learn the fundamentals** - [Job worker implementation](job-worker.md) – Build workers to handle automated tasks - [Process testing](../testing/getting-started.md) – Test your processes with Camunda Process Test - [Getting Started Tutorial](../../guides/getting-started-example.md) – Complete walkthrough with Modeler, Operate, and Spring SDK **Advanced topics** - [Logging configuration](logging.md) – Set up proper logging for your application - [Client documentation](https://javadoc.io/doc/io.camunda/camunda-client-java) – Complete Javadoc reference. Make sure you select the relevant Javadoc version as the latest version is shown by default at this URL. **Need help?** - [Camunda Community Forum](https://forum.camunda.io/) – Get help from the community - [GitHub repository](https://github.com/camunda/camunda) – Report issues and contribute --- ## Job worker **Job workers are the backbone of process automation in Camunda 8.** They handle automated tasks (service tasks) in your BPMN processes by continuously polling for available jobs and executing your business logic when jobs become available. This guide covers everything you need to know about implementing and configuring job workers with the Camunda Java Client, from basic concepts to advanced features such as streaming, metrics, and multi-tenancy. ## Quick start Before diving into the details, here is a simple example of creating a job worker: ```java try (final JobWorker workerRegistration = client.newWorker() .jobType(jobType) .handler(new EmailJobHandler()) .open()) { System.out.println("Job worker opened and receiving jobs of type: " + jobType); // Keep the worker running Thread.sleep(Duration.ofMinutes(10)); } catch (InterruptedException e) { throw new RuntimeException(e); } private static class EmailJobHandler implements JobHandler { @Override public void handle(final JobClient client, final ActivatedJob job) { // Perform your business logic here System.out.println("Processing job: " + job.getKey() + " for a process instance: " + job.getProcessInstanceKey()); // Complete the job (or use client.newFailCommand() if something goes wrong) client.newCompleteCommand(job.getKey()) .variables(Map.of("emailSent", true)) .send() .join(); } } ``` For a complete walkthrough, see the [getting started guide](getting-started.md). ## What are job workers? A job worker is a service that: - **Polls for jobs** of a specific type from the Camunda cluster - **Executes your business logic** when jobs are activated - **Reports job completion or failure** back to the cluster - **Handles retries and error scenarios** automatically When you model a service task in your BPMN process and assign it a job type (e.g., `send-email`, `process-payment`), job workers subscribe to these job types and process them as they become available. ## Key benefits - **Decoupled architecture**: Workers run independently from the process engine - **Scalable processing**: Add more workers to handle increased load - **Fault tolerance**: Built-in retry mechanisms and error handling - **Language flexibility**: Implement workers in any language with a Camunda client ## Related resources - [Job worker basics](/components/concepts/job-workers.md) ## How job workers work The Java client provides a job worker that handles polling for available jobs. This allows you to focus on writing code to handle the activated jobs. :::caution REST API limitation The Java client cannot keep the long-lived polling connections required for job polling via the Orchestration Cluster REST API in the following cases: - Performing long-polling job activation when activating jobs larger than the [maximum message size](../../self-managed/components/orchestration-cluster/zeebe/configuration/gateway.md#zeebegatewaynetwork). - Issuing any additional job activation requests while a long-polling connection is open - whether from the same client instance, another client in the same JVM, or a client in a different JVM. When the cases above occurs, the open long-polling request will be interrupted. You may observe workers intermittently stop receiving jobs and cause reduced throughput due to wasted I/O and connection churn. If you encounter this issue, consider using job activation via the Orchestration Cluster REST API with long polling disabled, or switching to the Zeebe gRPC protocol for job activation. Additionally, the long-polling connection might still receive jobs after the Java client is closed. As the Java client does not process these jobs they will time out. This means some jobs are not processed and will become available again after their timeout has elapsed. ::: On `open`, the job worker waits `pollInterval` milliseconds and then polls for `maxJobsActive` jobs. It then continues with the following schedule: 1. If a poll did not activate any jobs, it waits for `pollInterval` milliseconds and then polls for more jobs. 2. If a poll activated jobs, the worker submits each job to the job handler. 3. Every time a job is handled, the worker checks whether the number of unhandled jobs have dropped below 30% (rounded up) of `maxJobsActive`. The first time that happens, it will poll for more jobs. 4. If a poll fails with an error response, a backoff strategy is applied. This strategy waits for the delay provided by the `backoffSupplier` and polls for more jobs. For example, imagine you have 10 process instances and a single job worker configured with `maxJobsActive = 3`. The job worker will first pull three jobs and begin executing them. The threshold to poll for new jobs is 1 (30% of 3 rounded up). After two jobs have completed, the threshold is reached and the job worker will poll for up to 2 additional jobs. This process repeats until the jobs from all 10 process instances are completed. If streaming is enabled (via `streamEnabled`), it will also open a long-living stream over which jobs will be pushed without having to be polled. In such cases, a worker will only buffer up to `maxJobsActive` jobs at the same time. You can then estimate its memory usage as `maxJobsActive` times the max message size. ## Backoff configuration When a poll fails with an error response, the job worker applies a backoff strategy. It waits for some time, after which it polls again for more jobs. This gives a Zeebe cluster some time to recover from a failure. In some cases, you may want to configure this backoff strategy to better fit your situation. The retry delay (i.e. the time the job worker waits after an error before the next poll for new jobs) is provided by the [`BackoffSupplier`](https://github.com/camunda/camunda/blob/main/clients/java/src/main/java/io/camunda/client/api/worker/BackoffSupplier.java). You can replace it using the `.backoffSupplier()` method on the [`JobWorkerBuilder`](https://github.com/camunda/camunda/blob/main/clients/java/src/main/java/io/camunda/client/api/worker/JobWorkerBuilderStep1.java). By default, the job worker uses an exponential backoff implementation, which you can configure using `BackoffSupplier.newBackoffBuilder()`. The backoff strategy is especially useful for dealing with the `GRPC_STATUS_RESOURCE_EXHAUSTED` error response (refer to [gRPC Technical Error Handling](/apis-tools/zeebe-api/technical-error-handling.md)). This error code indicates the Zeebe cluster is currently under too large of a load and has decided to reject this request. By backing off, the job worker helps Zeebe by reducing the load. :::note Zeebe's [backpressure mechanism](../../../self-managed/components/orchestration-cluster/zeebe/operations/backpressure) can also be configured. ::: ## Metrics The job worker exposes metrics through a custom interface: [JobWorkerMetrics](https://github.com/camunda/camunda/blob/main/clients/java/src/main/java/io/camunda/client/api/worker/JobWorkerMetrics.java). These represent specific callbacks used by the job worker to keep track of various internals, e.g. count of jobs activated, count of jobs handled, etc. :::note By default, job workers will not track any metrics, and it's up to the caller to specify an implementation if they wish to make use of this feature. ::: ### Available metrics The API currently supports two metrics: the count of jobs activated, and the count of jobs handled. - **The count of jobs activated** is incremented every time a worker activates new jobs. This is done by calling `JobWorkerMetrics#jobActivated(int)`, with the first argument being the count of jobs newly activated. The method is called before the job is passed to the job handler. - **The count of jobs handled** is incremented every time a worker's `JobHandler` (passed to the builder via `JobWorkerBuilderStep2#handler(JobHandler)`) returns (regardless of whether it was successful). This is done by calling `JobWorkerMetrics#jobHandled(int)`, with the first argument being the count of jobs newly handled. For both counters, the expectation is that implementations will simply increment an underlying counter, and track the rate or increase of this counter to derive the speed at which jobs are activated/handled by a given worker. Additionally, by subtracting both counters, you can derive the count of queued or buffered jobs - jobs which have yet to be handled by the worker. This can help you tune your workers, e.g. scaling in or out, tuning the amount of jobs activated, etc. ### Usage To use job worker metrics, create a new instance of a `JobWorkerMetrics` implementation, and pass it along to the builder: ```java public final JobWorker openWorker(final CamundaClient client, final JobHandler handler) { final JobWorkerMetrics metrics = new MyCustomJobWorkerMetrics(); return client.newJobWorker() .jobType("foo") .handler(handler) .metrics(metrics) .open(); } ``` #### Micrometer implementation The Java client comes with an optional, built-in [Micrometer](https://micrometer.io/) implementation of `JobWorkerMetrics`. :::note [Micrometer](https://micrometer.io/) is a popular metrics facade in the Java ecosystem - what SLF4J is to logging. It can be configured to export metrics to many other systems, such as OpenTelemetry, Prometheus, StatsD, Datadog, etc. ::: If your project does not yet use Micrometer, you need to add it to your dependencies, and wire it up to your metrics backend, [as described in the Micrometer docs](https://micrometer.io/docs). Once Micrometer is set up in your project, you can start using the implementation. For example: ```java public final JobWorker openWorker(final CamundaClient client, final JobHandler handler) { final MeterRegistry meterRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); final JobWorkerMetrics metrics = JobWorkerMetrics .micrometer() .withMeterRegistry(meterRegistry) .withTags(Tags.of("zeebe.client.worker.jobType", "foo", "zeebe.client.worker.name", "bee")) .build(); return client.newJobWorker() .jobType("foo") .handler(handler) .metrics(metrics) .name("bee") .open(); } ``` :::note There are currently no built-in tags, primarily because these are likely to be high cardinality, which can become an issue with some metric registries. If you want per-worker tags, create a different `JobWorkerMetrics` instance per worker. ::: This implementation creates four metrics: | Metric name | Description | Notes | | ----------------------------------- | ------------------------------------- | ------------------------------------------------------------------------- | | camunda.client.worker.job.activated | Counts the number of jobs activated | New | | camunda.client.worker.job.handled | Counts the number of jobs handled | New | | zeebe.client.worker.job.activated | Deprecated counter for jobs activated | Will be removed at 8.10. Use camunda.client.worker.job.activated instead. | | zeebe.client.worker.job.handled | Deprecated counter for jobs handled | Will be removed at 8.10. Use camunda.client.worker.job.handled instead. | :::warning Deprecated metrics The following metrics are deprecated and will be removed in version 8.10: - `zeebe.client.worker.job.activated`, replace with `camunda.client.worker.job.activated` - `zeebe.client.worker.job.handled`, replace with `camunda.client.worker.job.handled` Please update your monitoring integrations to use the new metrics before upgrading to version 8.10. ::: ### Workarounds for additional metrics The decision to track a small set of metrics directly in the client is a conscious one. The idea is we should only be tracking what is not possible for users to track themselves. If you believe a specific metric should be tracked by us, do open a feature request for it. In the meantime, here is a list of workarounds to help you track additional job worker-related metrics that you can already use: #### Job polling count You can use a gRPC [ClientInterceptor](https://grpc.github.io/grpc-java/javadoc/io/grpc/ClientInterceptor.html) or an Apache HttpClient [AsyncExecChainHandler](https://hc.apache.org/httpcomponents-client-5.3.x/current/httpclient5/apidocs/org/apache/hc/client5/http/async/AsyncExecChainHandler.html) to track any client calls, including the `ActivateJobsCommand` call that is sent every time a worker polls for more jobs. Here's an example using Micrometer APIs that integrate a gRPC [ClientInterceptor](https://javadoc.io/doc/io.micrometer/micrometer-core/1.7.2/io/micrometer/core/instrument/binder/grpc/MetricCollectingServerInterceptor.html) and Apache HttpClient [AsyncExecChainHandler](https://javadoc.io/doc/io.micrometer/micrometer-core/1.12.0/io/micrometer/core/instrument/binder/httpcomponents/hc5/ObservationExecChainHandler.html): ```java public CamundaClientBuilder configureClientMetrics(final CamundaClientBuilder builder, final MeterRegistry meterRegistry, final ObservationRegistry observationRegistry) { final ClientInterceptor monitoringInterceptor = new MetricCollectingClientInterceptor(meterRegistry); final AsyncExecChainHandler monitoringHandler = new ObservationExecChainHandler(observationRegistry); return builder.withInterceptors(monitoringInterceptor).withChainHandlers(monitoringHandler); } ``` #### Executor metrics If you wish to tune your job worker executor, you can pass a custom, instrumented executor to the client builder. For example, if we use Micrometer: ```java public CamundaClientBuilder configureClientMetrics( final CamundaClientBuilder builder, final ScheduledExecutorService executor, final MeterRegistry meterRegistry) { final ScheduledExecutorService instrumentedExecutor = ExecutorServiceMetrics.monitor(meterRegistry, executor, "job-worker-executor"); return builder.jobWorkerExecutor(instrumentedExecutor); } ``` ## Job streaming Job workers are designed to regularly poll and activate jobs. It's also possible to use them in a streaming fashion, such that jobs are automatically activated and pushed downstream to workers without requiring an extra round of polling. This greatly cuts down on overall activation latency by completely removing the poll request. ### Usage Enabling job streaming consists of toggling a single flag in the job worker builder: ```java public JobWorkerBuilderStep3 enableStreaming(final JobWorkerBuilderStep3 builder) { return builder.streamEnabled(true); } ``` This configures the job worker to open a long-living stream between itself and a gateway, through which activated jobs will be pushed. **If the stream is closed for any reason - e.g. the gateway crashed, there is a temporary network issue, etc. - it is automatically recreated.** :::note It's also possible to set an overall timeout - so called `streamTimeout` - which ensures the underlying long-living stream is refreshed once the timeout is reached. This is useful to trigger load balancing of your workers overtime, instead of having workers pinned to the same gateway. ::: #### Backfilling Even with streaming enabled, job workers still occasionally poll the cluster for jobs. Due to implementation constraints, when a job is made activate-able, it is pushed out only if there exists a stream for it; if not, it remains untouched. However, if a stream exists, then streaming is always prioritized over polling. This ensures polling will not activate any new jobs, and the worker will back off and poll less often as long as it receives empty responses overtime. #### Backpressure To avoid your workers being overloaded with too many jobs, e.g. running out of memory, the Java job worker relies on the [built-in gRPC flow control mechanism](https://grpc.io/docs/guides/flow-control/). If streaming is enabled, this means the worker will never work on more jobs than the configured `maxJobsActive` parameter. For example, if `maxJobsActive = 32`, then your worker will only work on at most 32 jobs concurrently. If this is already the case, and a 33rd job comes in, the gRPC thread will block, thus signaling the gateway to stop sending more jobs. **If streaming is enabled, back pressure applies to both pushing and polling**. You can then use `maxJobsActive` as a way to soft-bound the memory usage of your worker. For example, if your max message size is 4MB, and `maxJobsActive = 32`, then a single worker could use up to 128MB of memory in the worst case. :::note If the worker blocks longer than the job's deadline, the job will **not** be passed to the worker, but will be dropped. As it will time out on the broker side, it will be pushed again. ::: #### Proxying If you're using a reverse proxy or a load balancer between your worker and your gateway, you may need to configure additional parameters to ensure the job stream is not closed unexpectedly with an error. If you observe regular 504 timeouts, read our guide on [job streaming](../../../self-managed/components/orchestration-cluster/zeebe/zeebe-gateway/job-streaming). By default, the Java job workers have a stream timeout of one hour. You can overwrite this by calling the `streamTimeout` of the job worker builder: ```java final JobWorkerBuilderStep3 builder = ...; builder.streamTimeout(Duration.ofMinutes(30)); ``` ## Multi-tenancy You can configure a job worker to pick up jobs belonging to one or more tenants. The job worker builder provides two ways to control which tenants a worker retrieves jobs for: explicitly providing tenant IDs, or using the tenants assigned to the worker in the engine. ### Filtering by assigned tenants Use `.tenantFilter()` to control how the worker resolves tenants. It accepts a `TenantFilter` enum with two options: - `TenantFilter.PROVIDED` _(default)_: The worker retrieves jobs for the tenant IDs explicitly provided via `.tenantId()` or `.tenantIds()`. See [Filtering by provided tenant IDs](#filtering-by-provided-tenant-ids) below. - `TenantFilter.ASSIGNED`: The worker retrieves jobs for the tenants assigned to it in the engine. When this option is set, any tenant IDs configured via `.tenantId()` or `.tenantIds()` are ignored. Using `TenantFilter.ASSIGNED`: ```java client.newWorker() .jobType("myJobType") .handler(new MyJobTypeHandler()) .tenantFilter(TenantFilter.ASSIGNED) .open(); ``` ### Filtering by provided tenant IDs When using `TenantFilter.PROVIDED` (the default), you must also specify the tenant IDs the worker should retrieve jobs for. :::note The client must be authorized for **all** the provided tenants. If it is not, the job worker will not work on any jobs. ::: Opening a job worker for a single tenant: ```java client.newWorker() .jobType("myJobType") .handler(new MyJobTypeHandler()) .tenantId("myTenant") .open(); ``` Opening a job worker for multiple tenants: ```java client.newWorker() .jobType("myJobType") .handler(new MyJobTypeHandler()) .tenantIds("myTenant", "myOtherTenant") .open(); ``` ### Default tenant You can configure the default tenant(s) using environment variables or system properties. It's configured using `CAMUNDA_DEFAULT_JOB_WORKER_TENANT_IDS` or `camunda.client.worker.tenantIds` respectively. --- ## Logging(Java-client) The client uses SLF4J for logging useful notes, such as exception stack traces when a job handler fails execution. Using the SLF4J API, any SLF4J implementation can be plugged in. The following example uses Log4J 2: ## Maven dependencies ```xml org.apache.logging.log4j log4j-slf4j-impl 2.8.1 org.apache.logging.log4j log4j-core 2.8.1 ``` ## Configuration First, add a file called `log4j2.xml` to the classpath of your application. Then, add the following content: ```xml ``` This will log every log message to the console. ## MDC context Job workers include an MDC context that contains the following: - `processDefinitionKey` - `processInstanceKey` - `elementInstanceKey` - `jobKey` See [the example above](#configuration), which includes `%X` in the pattern to print the entire MDC context. --- ## Migrate Component V1 APIs :::note Have you already migrated? You do not need to perform this migration again if you already did this when upgrading to version 8.8. This guide is retained to help customers migrate before upgrading from 8.9 to 8.10. See [API and SDK changes to migrate before Camunda 8.10](../migration-manuals/migrate-to-89.md#api-and-sdk-changes-to-migrate-before-camunda-810). ::: ## About This document outlines the changes required to migrate from the component REST APIs before upgrading to Camunda 8.10, where the V1 component APIs are removed. Use it if migration to the new [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) was not yet possible during your 8.8 or 8.9 upgrade. In this context, **components** refer to the standalone Camunda applications **Operate** and **Tasklist**, each exposing its own V1 REST API. :::note As of version 8.8, the V1 component APIs are deprecated. They are removed in 8.10, so complete this migration before upgrading. We strongly recommend [migrating to the Orchestration Cluster REST API](/apis-tools/migration-manuals/migrate-to-camunda-api.md) where possible. ::: ## Migrate V1 APIs With Camunda 8.8, permissions for resource access have been reworked. For the V1 APIs, this means that access to endpoints now depends on specific read and write permissions for related resources. To continue using the V1 APIs, users and clients must be assigned the appropriate permissions under [the new authorization model](/components/concepts/access-control/authorizations.md). Users now require wildcard (`*`) permissions for the resource type and permission type being accessed. :::info For guidance on assigning permissions in Admin, see the [Admin authorization guide](../../components/admin/authorization.md). ::: ### Mapping Operate permissions to new authorizations To maintain the same access level for the Operate V1 API, apply the following authorizations: **`operate-api:read`** is replaced by: - `PROCESS_DEFINITION:*:READ_PROCESS_DEFINITION,READ_PROCESSINSTANCE` - `DECISION_DEFINITION:*:READ_DECISION_DEFINITION` - `DECISION_REQUIREMENTS_DEFINITION:*:READ` **`operate-api:write`** is replaced by: - `PROCESS_DEFINITION:*:DELETE_PROCESS_INSTANCES` ### Operate V1 API permission matrix To enable more fine-grained access control, the matrix below details the required permissions for each Operate V1 API endpoint. Ensure the user has general access (resource ID `*`) for each listed resource and permission type. | Endpoint | Resource Type | Permission type | | ----------------------------------------------- | -------------------------------- | ------------------------ | | `POST /v1/process-definitions/search` | PROCESS_DEFINITION | READ_PROCESS_DEFINITION | | `GET /v1/process-definitions/:key` | PROCESS_DEFINITION | READ_PROCESS_DEFINITION | | `GET v1/process-definitions/:key/xml` | PROCESS_DEFINITION | READ_PROCESS_DEFINITION | | `POST /v1/decision-definitions/search` | DECISION_DEFINITION | READ_DECISION_DEFINITION | | `GET /v1/decision-definitions/:key` | DECISION_DEFINITION | READ_DECISION_DEFINITION | | `POST /v1/decision-instances/search` | DECISION_DEFINITION | READ_DECISION_INSTANCE | | `GET /v1/decision-instances/:id` | DECISION_DEFINITION | READ_DECISION_INSTANCE | | `POST /v1/flownode-instances/search` | PROCESS_DEFINITION | READ_PROCESS_INSTANCE | | `GET /v1/flownode-instances/:key` | PROCESS_DEFINITION | READ_PROCESS_INSTANCE | | `POST /v1/variables/search` | PROCESS_DEFINITION | READ_PROCESS_INSTANCE | | `GET /v1/variables/:key` | PROCESS_DEFINITION | READ_PROCESS_INSTANCE | | `POST /v1/process-instances/search` | PROCESS_DEFINITION | READ_PROCESS_INSTANCE | | `GET /v1/process-instances/:key` | PROCESS_DEFINITION | READ_PROCESS_INSTANCE | | `GET /v1/process-instances/:key/statistics` | PROCESS_DEFINITION | READ_PROCESS_INSTANCE | | `GET /v1/process-instances/:key/sequence-flows` | PROCESS_DEFINITION | READ_PROCESS_INSTANCE | | `DEL /v1/process-instances/:key` | PROCESS_DEFINITION | DELETE_PROCESS_INSTANCE | | `POST /v1/drd/search` | DECISION_REQUIREMENTS_DEFINITION | READ | | `GET /v1/drd/:key` | DECISION_REQUIREMENTS_DEFINITION | READ | | `GET /v1/drd/:key/xml` | DECISION_REQUIREMENTS_DEFINITION | READ | | `POST /v1/incidents/search` | PROCESS_DEFINITION | READ_PROCESS_INSTANCE | | `GET /v1/incidents/:key` | PROCESS_DEFINITION | READ_PROCESS_INSTANCE | ### Mapping Tasklist permissions to new authorizations To maintain the same access level for the Tasklist V1 API, apply the following authorizations: **`tasklist-api:read`** is replaced by: - `PROCESS_DEFINITION:*:READ_PROCESS_DEFINITION,READ_USER_TASK` **`taslist-api:write`** is replaced by: - `PROCESS_DEFINITION:*:UPDATE_USER_TASK` ### Tasklist V1 API permission matrix To enable more fine-grained access control, the matrix below details the required permissions for each Tasklist V1 API endpoint. Ensure the user has general access (resource ID `*`) for each listed resource and permission type. | Endpoint | Resource Type | Permission type | | ----------------------------------------- | ------------------ | ---------------- | | `GET /v1/forms/:formId` | PROCESS_DEFINITION | READ_USER_TASK | | `POST /v1/tasks/search` | PROCESS_DEFINITION | READ_USER_TASK | | `GET /v1/tasks/:taskId` | PROCESS_DEFINITION | READ_USER_TASK | | `PATCH /v1/tasks/:taskId/assign` | PROCESS_DEFINITION | UPDATE_USER_TASK | | `PATCH /v1/tasks/:taskId/unassign` | PROCESS_DEFINITION | UPDATE_USER_TASK | | `PATCH /v1/tasks/:taskId/complete` | PROCESS_DEFINITION | UPDATE_USER_TASK | | `POST /v1/tasks/:taskId/variables` | PROCESS_DEFINTION | UPDATE_USER_TASK | | `POST /v1/tasks/:taskId/variables/search` | PROCESS_DEFINITION | READ_USER_TASK | | `GET /v1/variables/:variableId` | PROCESS_DEFINITION | READ_USER_TASK | --- ## Migrate from gRPC to the Orchestration Cluster API :::note Have you already migrated? You do not need to perform this migration again if you already did this when upgrading to version 8.8. This guide remains in the 8.9 documentation for customers who did not perform this migration during their 8.8 upgrade. See [API and SDK changes to migrate before Camunda 8.10](../migration-manuals/migrate-to-89.md#api-and-sdk-changes-to-migrate-before-camunda-810). ::: ## About This guide provides an overview of the process for migrating to the Orchestration Cluster REST API. The [Orchestration Cluster API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) is the official REST API for connecting to Orchestration Cluster, automating processes, and implementing job workers. ## Camunda Java Client In version 8.8.0, the [Camunda Java Client](/apis-tools/java-client/getting-started.md) changes to use the Orchestration Cluster API as a default cluster communication method. :::info Refer to the [Camunda Java Client migration guide](migrate-to-camunda-java-client.md#protocol-and-connection-rest-vs-grpc-selection) for details on how you can continue using gRPC. ::: ## gRPC vs REST mapping reference The following table provides a mapping reference between gRPC methods and their equivalent REST API endpoints in the Orchestration Cluster API. :::info For detailed information on each REST endpoint, see [Orchestration Cluster API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). ::: | gRPC Method Name (Gateway.proto) | Orchestration Cluster REST API Endpoint | Notes | | --------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------ | | `ActivateJobs` | `POST /v2/jobs/activation` | Batch job activation via long polling (streaming not available in REST). | | `BroadcastSignal` | `POST /v2/signals/broadcast` | Triggers signal events. | | `CancelProcessInstance` | `POST /v2/process-instances/{processInstanceKey}/cancellation` | Cancels a process instance. | | `CompleteJob` | `POST /v2/jobs/{jobKey}/completion` | Completes a job. | | `CreateProcessInstance` | `POST /v2/process-instances` | Starts a new process instance. | | `CreateProcessInstanceWithResult` | `POST /v2/process-instances?awaitCompletion=true` | Starts a process instance, waits for completion. | | `DeleteResource` | `POST /v2/resources/{resourceKey}/deletion` | Deletes a resource. | | `DeployResource` | `POST /v2/deployments` | Deploys BPMN, DMN, or form resources (multipart upload). | | `EvaluateDecision` | `POST /v2/decisions/evaluation` | Evaluates a DMN decision by key or id. | | `FailJob` | `POST /v2/jobs/{jobKey}/failure` | Marks a job as failed. | | `MigrateProcessInstance` | `POST /v2/process-instances/{processInstanceKey}/migration` | Migrates a process instance (phase 1 only). | | `ModifyProcessInstance` | `POST /v2/process-instances/{processInstanceKey}/modification` | Modifies a running process instance. | | `PublishMessage` | `POST /v2/messages/publication` | Publishes a message asynchronously. | | `ResolveIncident` | `POST /v2/incidents/{incidentKey}/resolution` | Resolves an incident. | | `SetVariables` | `PUT /v2/element-instances/{elementInstanceKey}/variables` | Sets variables (local/global by param). | | `ThrowError` | `POST /v2/jobs/{jobKey}/error` | Throws BPMN error from worker to engine. | | `Topology` | `GET /v2/topology` | Returns cluster info. | | `UpdateJobRetries` | `PATCH /v2/jobs/{jobKey}` | Updates job retries, PATCH can update multiple job properties. | | `UpdateJobTimeout` | `PATCH /v2/jobs/{jobKey}` | Updates job timeout, PATCH can update multiple job properties. | --- ## Camunda 8.10 APIs & Tools migration guide ## About This guide details the API and SDK changes introduced in Camunda 8.10 that require customer action, including breaking changes, deprecations, and step-by-step migration actions. Details are provided for each integration type, including what changed, why, and what action you must take. | Integration type | Description | | :--------------------- | :-------------------------------------------------------- | | Official SDK users | Java client, TypeScript SDK, Python SDK, and C# SDK. | | Generated-client users | Clients generated from the Camunda OpenAPI specification. | | Custom integrations | Custom code that calls the Camunda REST API directly. | ## Upgrade steps Complete the following steps in this guide: 1. Upgrade to the latest official Camunda SDK versions. 1. If you generate clients from OpenAPI, regenerate them from the 8.10 specification. 1. Re-run compilation/type checks and address any errors. 1. Review and apply fixes for the breaking changes, deprecations, and supported environment changes below. ### Camunda 8.10 breaking changes, deprecations, and supported environment changes Review the actions required for the following 8.10 changes: | Type | Change | | :---------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------- | | Breaking change | [Search filters: `UserTaskFilter` process filters converted into advanced search filters](#usertask-process-filter) | | Breaking change | [`POST /v2/message-subscriptions/search` returns start event subscriptions](#message-subscription-type) | | Behavioral change | [Element instance search: advanced filters on `elementId` / `elementName` and `$or` support](#element-instance-advanced-or) | | Behavioral change | [Resource API now uses eventual consistency](#resource-eventual-consistency) | | Deprecated | [Deprecated: GET resource content API](#deprecated-get-resource-content) | ## Breaking changes Review actions required for the following breaking changes: ### Search filters: `UserTaskFilter` process filters converted into advanced search filters {#usertask-process-filter} #### Change The search filter criteria for `processDefinitionKey`, `processInstanceKey`, and `bpmnProcessId` in `UserTaskFilter` have been converted into advanced search filters. #### Why As a result of the V1 API removal, advanced process filtering for user tasks was no longer supported. These changes let you use advanced process filters with the V2 User Tasks API again. #### Impact This affects the Java client because `io.camunda.client.api.search.filter.UserTaskFilter` now accepts advanced filters for `processDefinitionKey`, `processInstanceKey`, and `bpmnProcessId`. #### Action Update to the latest SDK version. The new SDK version includes advanced filters for `processDefinitionKey`, `processInstanceKey`, and `bpmnProcessId` in `UserTaskFilter`. Regenerate your client. No change is needed if your code already uses the exact-match filters for `processDefinitionKey`, `processInstanceKey`, and `bpmnProcessId` in `UserTaskFilter`. ### `POST /v2/message-subscriptions/search` returns start event subscriptions {#message-subscription-type} #### Change The `POST /v2/message-subscriptions/search` endpoint now returns both start event and intermediate event message subscriptions. Previously, only intermediate event subscriptions were returned. #### Why This change provides complete visibility into all active message subscriptions for a process, including start event subscriptions that were previously excluded. #### New field Each result includes a new `messageSubscriptionType` enum field: | Value | Description | | :-------------- | :------------------------------------------------ | | `START_EVENT` | A start event message subscription. | | `PROCESS_EVENT` | An intermediate catch event message subscription. | In existing legacy data, this field is `NULL`. #### Impact Integrations that consume results from `POST /v2/message-subscriptions/search` will now receive start event subscriptions in addition to intermediate event subscriptions. Code that assumes only intermediate events may produce unexpected behavior. #### Action Update to the latest SDK version. If your code relies on the endpoint returning only intermediate event subscriptions, add a filter to exclude start events when constructing your search query. Regenerate your client from the 8.10 OpenAPI specification to include the new `messageSubscriptionType` field. If your code expects only intermediate event subscriptions, add the filter shown in the **Custom integrations** tab to your request payload. If your code relies on the endpoint returning only intermediate event subscriptions, add the following filter to restore the previous behavior: ```json title="Before (no filter needed — endpoint returned only intermediate events)" { "filter": {} } ``` ```json title="After (filter required to exclude start events)" { "filter": { "messageSubscriptionType": { "$neq": "START_EVENT" } } } ``` This filter works correctly for both new data and legacy data (which has `NULL` in the `messageSubscriptionType` field). ## Behavioral changes ### Element instance search: advanced filters on `elementId` / `elementName` and `$or` support {#element-instance-advanced-or} #### Change The element instance search endpoint (`POST /v2/element-instances/search`) gained two filtering capabilities: - The `elementId` and `elementName` filter fields now accept [advanced search filter objects](../orchestration-cluster-api-rest/orchestration-cluster-api-rest-data-fetching.md#advanced-search-filters) in addition to plain string equality. Supported operators: `$eq`, `$neq`, `$exists`, `$in`, `$notIn`, `$like` (wildcard pattern with `*` and `?`). - The request body's `filter` object now accepts a top-level `$or` property that takes an array of alternative filter groups combined with OR logic. Top-level filter fields and `$or` are combined with AND logic. #### Why These additions let you express the queries the user interface (UI) needs (for example, "match any element whose name or ID contains a substring") in a single request, avoiding multiple round trips and client-side merging. #### Impact The change is additive and backward compatible — existing exact-match requests continue to work unchanged. New requests can now use advanced operators and `$or` to express richer queries: ```json { "filter": { "processInstanceKey": "2251799813685323", "$or": [ { "elementName": { "$like": "*Order*" } }, { "elementId": { "$like": "*Order*" } } ] } } ``` The example matches element instances where `processInstanceKey` equals the given value AND either `elementName` or `elementId` contains the substring `Order`. :::note Complex `$or` conditions may impact performance in high-volume environments; use them with care. ::: The `elementName` filter only matches instances created in 8.8 or later, since earlier runtimes did not persist this field on element instances. #### Action Update to the latest SDK version. The new SDK exposes advanced filters for `elementId` and `elementName`, and the `$or` filter on `ElementInstanceFilter`. Regenerate your client from the 8.10 OpenAPI specification to pick up the advanced filter and `$or` types on `ElementInstanceFilter`. No change is needed for existing requests. To use the new operators, send advanced filter objects on `elementId` / `elementName`, or a top-level `$or` array, as shown above. ### Resource API now uses eventual consistency {#resource-eventual-consistency} The [Get resource] and [Get resource content] APIs now retrieve from secondary storage, resulting in eventual consistency. After a resource is deployed, there may be a brief delay before it becomes retrievable via these endpoints. If your application assumes immediate resource retrieval after deployment, add retry logic or a short delay before querying resources. ## Deprecations Review the actions required for the following deprecations: ### Deprecated: GET resource content API {#deprecated-get-resource-content} The [Get resource content] endpoint is deprecated. Use [Get resource content binary] instead, which provides the same functionality and also returns generic resources. ## Next steps Once you have completed the [upgrade steps](#upgrade-steps) in this guide, you should: 1. Re-compile and run your test suite against the 8.10 API. [Get resource]: ../orchestration-cluster-api-rest/specifications/get-resource.api.mdx [Get resource content]: ../orchestration-cluster-api-rest/specifications/get-resource-content.api.mdx [Get resource content binary]: ../orchestration-cluster-api-rest/specifications/get-resource-content-binary.api.mdx --- ## Camunda 8.9 APIs & Tools migration guide ## About This guide details the API and SDK changes introduced in Camunda 8.9 that require customer action, including breaking changes, deprecations, and step-by-step migration actions. Details are provided for each integration type, including what changed, why, and what action you must take. | Integration type | Description | | :--------------------- | :-------------------------------------------------------- | | Official SDK users | Java client, TypeScript SDK, Python SDK, C# SDK. | | Generated-client users | Clients generated from the Camunda OpenAPI specification. | | Custom integrations | Custom code that calls the Camunda REST API directly. | :::info For a full list of changes, see the [8.9 release announcements](/reference/announcements-release-notes/890/890-announcements.md) and [release notes](/reference/announcements-release-notes/890/890-release-notes.md). ::: ## Upgrade steps Complete the following steps in this guide: 1. Upgrade to the latest official Camunda SDK versions. 1. If you generate clients from OpenAPI, regenerate them from the 8.9 specification. 1. Re-run compilation/type checks and address any errors. 1. Review and apply fixes for the breaking changes, deprecations, and supported environment changes below. ### API and SDK changes to migrate before Camunda 8.10 If you did not already migrate to the following APIs and SDKs during your 8.8 upgrade, Camunda recommends you perform these migrations before you upgrade to 8.9, as this must be performed before 8.10. If you already performed these migrations during your 8.8 upgrade, proceed to [Camunda 8.9 breaking changes, deprecations, and supported environment changes](#camunda-89-breaking-changes-deprecations-and-supported-environment-changes). | 8.9 status | Component/Use | Migrate to | Migrate by | | :--------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------- | :-------------------------- | :------------------ | | Deprecated | [V1 component APIs](../migration-manuals/migrate-to-camunda-api.md) | Orchestration Cluster API | Before Camunda 8.10 | | Deprecated | [ZeebeClient](../migration-manuals/migrate-to-camunda-java-client.md) | Camunda Java Client | Before Camunda 8.10 | | Deprecated | [Spring Zeebe SDK](../migration-manuals/migrate-to-camunda-spring-boot-starter.md) | Camunda Spring Boot Starter | Before Camunda 8.10 | | Deprecated | [Zeebe Process Test (ZPT)](../migration-manuals/migrate-to-camunda-process-test.md) | Camunda Process Test (CPT) | Before Camunda 8.10 | | Deprecated | [Job-based user tasks](../migration-manuals/migrate-to-camunda-user-tasks.md) | Camunda user tasks | Before Camunda 8.10 | :::tip Learn more about API changes in the blog post [Upcoming API Changes in Camunda 8: A Unified and Streamlined Experience](https://camunda.com/blog/2024/12/api-changes-in-camunda-8-a-unified-and-streamlined-experience/). ::: ### Camunda 8.9 breaking changes, deprecations, and supported environment changes Review the actions required for the following 8.9 changes: | Type | Change | | :----------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------ | | Breaking change | [Bug fix: `FormResult.schema` type corrected from object to string](#form-schema-type) | | Breaking change | [Document API response schemas now have explicit required and nullable annotations](#request-response-schema-split) | | Breaking change | [MCP Client and MCP Remote Client connectors](#mcp) | | Breaking change | [OpenAPI enum extensions](#enum-extensions) | | Breaking change | [OpenAPI type-safety enhancements](#type-safety-enhancements) | | Breaking change | [Resource deletion endpoint now returns a response body](#resource-deletion) | | Breaking change | [Search filter validation errors now return structured error collections](#search-filter-validation-errors) | | Breaking change | [Spring Boot 4.0 default for Camunda Spring Boot Starter](#spring-boot) | | Breaking change | [Type-safe pagination model in the Camunda Java client](#type-safe-pagination) | | Breaking change | [`versionTag` returns `null` instead of empty string when absent](#version-tag-null) | | Deprecated | [Deprecated: enum literals in Orchestration Cluster API v2](#deprecated-enum) | ## Breaking changes Review actions required for the following breaking changes: ### Bug fix: `FormResult.schema` type corrected from object to string {#form-schema-type} #### Change The `schema` property in `FormResult` was incorrectly specified as `type: object` in the OpenAPI contract. The server has always returned it as a JSON `string`. The specification is now corrected. #### Why This is a bug fix. The original specification was inaccurate and caused incorrect typing in generated clients. #### Impact This impacts the Java client as `io.camunda.client.api.search.response.Form::getSchema()` now returns `String` instead of `Object`. #### Action Update to the latest SDK version. If you are a Java client user, update any calls to `Form::getSchema()` that cast or process the return value as `Object` — it is now `String`. Regenerate your client. If your generated code relied on the incorrect `object` typing for `FormResult.schema`, update it to handle `string`. No change needed if your code was already handling the actual `string` response from the server. ### Document API response schemas now have explicit required and nullable annotations {#request-response-schema-split} #### Change The OpenAPI specification now uses distinct schemas for document request and response payloads, and adds explicit `required` / `nullable` annotations to document response types. #### Why A shared `DocumentMetadata` schema was used for both creating and reading documents. Because response fields like `customProperties` are always populated by the server but optional in requests, a single schema could not accurately express both contracts. This caused incorrect required/optional behavior in generated clients. #### Affected schemas | Schema | Change | | :----------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `DocumentMetadata` | Now request-only. Removed `required: [customProperties]` — `customProperties` is now optional in requests. | | `DocumentMetadataResponse` (new) | Response schema with required fields: `fileName`, `expiresAt`, `size`, `contentType`, `customProperties`, `processDefinitionId`, `processInstanceKey`. `expiresAt`, `processDefinitionId`, and `processInstanceKey` are nullable. | | `DocumentReference` | `metadata` now references `DocumentMetadataResponse`. Added `required`: `camunda.document.type`, `storeId`, `documentId`, `contentHash`, `metadata`. `contentHash` is now nullable. | | `DocumentLink` | `url` and `expiresAt` are now explicitly required. | | `UserTaskResult.candidateGroups` | Now marked as required in the response schema. | | `UserTaskProperties.candidateGroups` | Now marked as required in the response schema. | #### Impact The Java client is impacted as `DocumentMetadataImpl` (both `io.camunda.client` and the deprecated `io.camunda.zeebe.client`) now uses `DocumentMetadataResponse` instead of `DocumentMetadata` internally. #### Action Update to the latest SDK version. The updated response models are included automatically. Re-compile your application to verify. 1. Regenerate your client from the 8.9 OpenAPI specification. 2. Update any code that references `DocumentMetadata` in response handling — the response type is now `DocumentMetadataResponse`. 3. Review nullable annotations: `DocumentReference.contentHash`, `DocumentMetadataResponse.expiresAt`, `.processDefinitionId`, and `.processInstanceKey` can be `null`. 4. Code that reads `candidateGroups` from user task or job responses can now rely on the field being present without null checks. No request-side changes are needed. Response fields listed above are now guaranteed to be present (though some may be `null`). If your code reads document metadata responses and checks for `customProperties` or `candidateGroups` presence, those fields are now always included. ### MCP Client and MCP Remote Client connectors {#mcp} #### Change Breaking changes were introduced in alpha 2 to the element templates and runtime configuration of the MCP Client. #### Why This improves the stability and configuration model of the MCP connectors. #### Action Update both the MCP Client and MCP Remote Client connectors to use element template version 1. See the [MCP documentation](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-client.md) for details. ### OpenAPI enum extensions {#enum-extensions} #### Change New enum literals were added to support expanded 8.9 functionality. #### Why These additions enable new features such as decision instance deletion and user task authorization. #### Enum members added | Enum | New value | | :------------------------------------------------------------ | :------------------------- | | `BatchOperationTypeEnum` / `BatchOperationTypeFilterProperty` | `DELETE_DECISION_INSTANCE` | | `ResourceTypeEnum` | `USER_TASK` | | `PermissionTypeEnum` | `COMPLETE` | #### Action Update to the latest SDK version for full enum support. Re-compile your application — the compiler will signal any exhaustive match issues. 1. Regenerate your client from the 8.9 OpenAPI specification. 2. Add fallback/default handling in enum parsing and deserialization. 3. Ensure exhaustive `switch` or pattern matches include a `default` branch. Review all code paths that handle these enum values. Add handling for the new values and ensure you have a fallback for unknown values in `switch`/`if-else` chains. :::note In Java, the compiler does not signal incomplete enum handling at compile time. Search your codebase for references to `BatchOperationTypeEnum`, `ResourceTypeEnum`, and `PermissionTypeEnum` and verify coverage manually. ::: ### OpenAPI type-safety enhancements {#type-safety-enhancements} #### Change Several request properties in the OpenAPI contract now use stronger domain types instead of plain `string`, and one schema type was renamed. This completes the type-safety work that began in 8.8. #### Why This increases compile-time safety and helps prevent semantic substitution errors — for example, accidentally passing a `tenantId` where a `documentId` is expected. Compilers can now reason about semantic correctness in addition to structural correctness for these fields. #### Affected fields and types | Field | Old type | New type | | :------------------------------------------------------------------------------ | :------- | :------------------------------------------------ | | `CreateDeploymentData.body.tenantId` | `string` | `TenantId` | | `CreateDocumentData.query.documentId` | `string` | `DocumentId` | | `SearchCorrelatedMessageSubscriptionsData.body.filter.processDefinitionKey.$eq` | `string` | `ProcessDefinitionKey` | | `CorrelatedMessageSubscriptionFilter.processDefinitionKey` | `string` | `ProcessDefinitionKeyFilterProperty \| undefined` | | `CorrelatedMessageSubscriptionSearchQuery.filter.processDefinitionKey.$eq` | `string` | `ProcessDefinitionKey` | #### Schema rename | Old name | New name | | :----------------------------------- | :-------------------- | | `ProcessInstanceIncidentSearchQuery` | `IncidentSearchQuery` | **Example — message subscription filter payload**: ```json title="Before" { "processDefinitionKey": "2251799813685251" } ``` ```json title="After (for example, using $eq)" { "processDefinitionKey": { "$eq": "2251799813685251" } } ``` #### Action Update to the latest SDK version. The wire-type of these fields does not change, so most SDK users will not need code changes. Re-compile your application to verify. 1. Regenerate your client from the 8.9 OpenAPI specification. 2. Update type imports and references — in particular, rename `ProcessInstanceIncidentSearchQuery` to `IncidentSearchQuery`. 3. Update request payload construction for `processDefinitionKey` fields to use the new filter property type. 1. Update request payload construction for `processDefinitionKey` to use the filter object format. 2. Update any references to `ProcessInstanceIncidentSearchQuery` in your code. ### Resource deletion endpoint now returns a response body {#resource-deletion} #### Change The resource deletion endpoint `POST /resources/{resourceKey}/deletion` now returns a response body instead of an empty response. #### Why This provides explicit deletion feedback, making client-side confirmation, auditing, and follow-up workflow logic more reliable. #### Action Update to the latest SDK version. The updated response model is included automatically. Regenerate your client from the 8.9 OpenAPI specification. Update any code that previously expected an empty `204` response to handle the new response body. Update your HTTP client code to parse the new JSON response body from the deletion endpoint, rather than treating it as a `204 No Content`. ### Spring Boot 4.0 default for Camunda Spring Boot Starter {#spring-boot} #### Change Starting with 8.9.0, the default [Camunda Spring Boot Starter](/apis-tools/camunda-spring-boot-starter/getting-started.md) (`camunda-spring-boot-starter`) is bundled with and requires Spring Boot 4.0.x. A dedicated `camunda-spring-boot-3-starter` module is available for applications that are not yet ready to upgrade. #### Action - Migrate your application to Spring Boot 4.0.x and continue using `camunda-spring-boot-starter`. - If you cannot migrate yet, switch your dependency to `camunda-spring-boot-3-starter`, which is bundled with Spring Boot 3.5.x. Note that OSS support for Spring Boot 3.5.x ends in June 2026, so plan your migration accordingly. - See the [Spring Boot support timeline](https://spring.io/projects/spring-boot#support) for details. - See the [dedicated Spring Boot 3 and 4 modules](/apis-tools/camunda-spring-boot-starter/getting-started.md#dedicated-spring-boot-3-and-4-modules) documentation for more information. ### `versionTag` returns `null` instead of empty string when absent {#version-tag-null} #### Change API response fields for `versionTag` now return `null` instead of an empty string `""` when no version tag is set. #### Why This properly signals absence instead of leaking an internal empty-string default. It aligns `versionTag` with how other optional fields like `businessId` are handled, simplifying absence-detection logic. #### Action Update to the latest SDK version. Review any code that checks for an empty string (`""`) to detect a missing version tag, and update it to check for `null`. Regenerate your client to pick up the updated nullable annotation. Update absence-detection logic from empty-string checks to null checks. Update your response-handling code: ```java title="Before" if (versionTag != null && !versionTag.isEmpty()) { // version tag is present } ``` ```java title="After" if (versionTag != null) { // version tag is present } ``` ### Search filter validation errors now return structured error collections {#search-filter-validation-errors} #### Change REST API search endpoints now collect all filter validation errors and return them together in a single `400 Bad Request` response. Previously, only the first conversion error was returned. #### Why This is a bug fix that improves error handling consistency across the REST API. Collecting all validation errors in a single response makes debugging easier. #### Impact Search filter validation error responses now contain a list of all validation issues instead of stopping at the first error. The error detail format has changed: | Aspect | Before | After | Breaking? | | :--------------------- | :--------------------------------------- | :------------------------------------------------------------------------------------------------------- | :------------------------------------- | | HTTP status code | `400` | `400` | No | | ProblemDetail `title` | `"Bad Request"` | `"INVALID_ARGUMENT"` | Yes | | ProblemDetail `detail` | `"Failed to parse date-time: [invalid]"` | `"The provided evaluationDate 'invalid' cannot be parsed as a date according to RFC 3339, section 5.6."` | Yes | | Error collection | Fails on first error | Collects all validation errors | Yes (response may contain more errors) | Affected search endpoints include all endpoints that accept advanced search filters with key fields (such as `processInstanceKey`, `processDefinitionKey`, `scopeKey`) or date fields (such as `startDate`, `endDate`, `creationDate`). **Who is affected?** - Customers parsing error response bodies (specifically `title` or `detail` fields) for validation errors → **affected**. - Customers only checking HTTP status codes → **not affected**. - Customers sending valid requests → **not affected** (happy path is unchanged). #### Action If your code parses error response bodies from search endpoints for specific validation error messages, update it to handle: - The `title` field value changed from `"Bad Request"` to `"INVALID_ARGUMENT"`. - The `detail` field now contains more descriptive, structured messages. - A collection of validation errors in the response body (instead of a single error message). ### Type-safe pagination model in the Camunda Java client {#type-safe-pagination} #### Change The Camunda Java client now uses type-safe pagination interfaces (`AnyPage`, `OffsetPage`, `CursorForwardPage`, `CursorBackwardPage`) instead of the previous `SearchRequestPage` class. Each search or statistics endpoint exposes only the pagination methods it actually supports. Direction methods on `AnyPage` now return style-specific interfaces: `from()` returns `OffsetPage`, `after()` returns `CursorForwardPage`, and `before()` returns `CursorBackwardPage`. This prevents mixing incompatible pagination styles at compile time. #### Why The previous API allowed mixing incompatible pagination styles (for example, `.page(p -> p.from(10).after("cursor"))`), which always resulted in a `400 Bad Request` at runtime. This change surfaces that restriction at compile time. The pattern mirrors the existing sort polymorphism design (`TypedSortableRequest`). #### Impact This change is **not binary-compatible**. Code compiled against the previous API will fail at runtime without recompilation, because the method signature changed from `page(Consumer)` to `page(Consumer)`. All users must recompile their applications. Additionally, `TypedSearchRequest` now has 4 generic type parameters (previously 3) and `TypedPageableRequest` now has 2 (previously 1), which is a source-breaking change for custom implementations of these interfaces. #### Migration reference | Before (8.8) | After (8.9) | | :------------------------------------------------- | :---------------------------------------------------------- | | `import ...search.request.SearchRequestPage` | `import ...search.page.AnyPage` | | `import ...search.request.SearchRequestOffsetPage` | `import ...search.page.OffsetPage` | | `Consumer` | `Consumer` | | `Consumer` | `Consumer` | | `SearchRequestBuilders.searchRequestPage(fn)` | `SearchRequestBuilders.anyPage(fn)` (old method deprecated) | | `implements TypedSearchRequest` | `implements TypedSearchRequest` | | `implements TypedPageableRequest` | `implements TypedPageableRequest` | | `SearchRequestPage r = p.from(10)` | `OffsetPage r = p.from(10)` | | `SearchRequestPage r = p.after("c")` | `CursorForwardPage r = p.after("c")` | #### Action Update to the latest Java client version and **recompile your application**. If you use inline lambdas with valid pagination patterns (for example, `.page(p -> p.from(5).limit(10))`), your source code does not require changes — but recompilation is mandatory. If you have explicit references to `SearchRequestPage`, replace them with `AnyPage`. If you store the return value of direction methods (for example, `SearchRequestPage r = p.from(10)`), update the variable type to `OffsetPage`, `CursorForwardPage`, or `CursorBackwardPage` as appropriate. :::note This change is specific to the Camunda Java client. Generated clients and custom REST API integrations are not affected. ::: ## Deprecations Review the actions required for the following deprecations: ### Deprecated: enum literals in Orchestration Cluster API v2 {#deprecated-enum} The following enum literals are now marked as deprecated: - `UNSPECIFIED` in `DecisionDefinitionTypeEnum` - `UNKNOWN` in `DecisionInstanceStateFilterProperty` - `UNKNOWN` in `DecisionInstanceStateEnum` These values were reintroduced to preserve backward compatibility but are planned for removal in a future release. Removal will be signaled as a breaking change at that time. #### Action Avoid using these values in new integrations. If your code references them, plan to remove these references before the 8.10 release. ## Next steps Once you have completed the [upgrade steps](#upgrade-steps) in this guide, you should: 1. Re-compile and run your test suite against the 8.9 API. 1. Review [8.9 release announcements](/reference/announcements-release-notes/890/890-announcements.md) for additional context on each change. --- ## Migrate to the Orchestration Cluster API :::note Have you already migrated? You do not need to perform this migration again if you already did this when upgrading to version 8.8. This guide is retained to help customers migrate before upgrading from 8.9 to 8.10. See [API and SDK changes to migrate before Camunda 8.10](../migration-manuals/migrate-to-89.md#api-and-sdk-changes-to-migrate-before-camunda-810). ::: ## About This guide covers how to migrate to the V2 [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) before upgrading to Camunda 8.10, where the V1 component REST APIs are removed. It covers all public endpoints in the component REST APIs and their Orchestration Cluster API counterparts or required migration changes. - Camunda is streamlining the developer experience by creating a unified REST API for Zeebe, Operate, Tasklist, and the Identity components with endpoint parity. This is the single Orchestration Cluster REST API. - Individual component APIs (starting with the former Operate and Tasklist APIs) were deprecated before their removal in 8.10. Use this guide to complete the migration before upgrading. :::info To learn more about the unified REST API, see [the official blog announcement](https://camunda.com/blog/2024/12/api-changes-in-camunda-8-a-unified-and-streamlined-experience/). ::: :::note The Administration and Web Modeler APIs are not part of the Orchestration Cluster REST API, as these are platform APIs outside the cluster’s scope. ::: ## Migration steps To successfully migrate to the V2 Orchestration Cluster API, perform the following steps: 1. **Identify your current V1 endpoints**: Audit your application to catalog all V1 API calls currently in use. 1. **Map V1 endpoints to V2 equivalents**: Use the tables in this guide to find the corresponding V2 endpoints for each request. 1. **Update request and response structure**: Adapt your code to handle the new formats, renamed attributes, and data type changes as outlined in this guide. 1. **Update pagination logic**: Replace old pagination parameters with the new `page` object structure and cursor-based navigation. ## General endpoint changes - The new API can be found at `/v2/…>` instead of `/v1/…>`. - All endpoints are no longer separated by component concerns and all endpoints receive similar support. For example, process definitions, user tasks, and user authorizations were previously spread across separate Tasklist, Operate, and Identity APIs. - All endpoints support the [authorization-based access control model](../../components/concepts/access-control/authorizations.md). Component endpoints only support the configuration of full (wildcard) access or no access. - Naming, response codes, and type handling have been streamlined for all endpoints to provide a consistent UX. - Endpoints with similar concerns (variable search, for example) have been consolidated into single endpoints. - The request and response payload of every new endpoint might contain new attributes that are not necessarily needed for a migration from a V1 endpoint to V2 but might still be useful. Please consult the V2 API guides for access to all new attributes. - Unified search request structure. - Attributes `filter`, `page`, and `sort` on root level. - Endpoint-specific filter attributes in the filter object, not at the root level. - Pagination information in the `page` object. For example, the attributes `from`, `limit`, `before`, and `after`. - Sorting configuration in sort object array, each object containing the field name and order (descending or ascending). - Unified search response structure. - Attributes `items` and `page` on root level. - List of endpoint-specific response items in `items` attribute. - Page information in `page` attribute, for example the attributes `totalItems`, `startCursor`, and `endCursor` to use in `before` and `after` in follow-up requests. ## Name changes and mappings The following table shows key attribute name changes from V1 to V2: | **V1** | **V2** | **Notes** | | ---------------- | ----------------------- | ----------------------------------------------------------------------------------- | | `id` | `[entity]Id` | Keys now include entity prefix (for example, `userTaskKey`, `processDefinitionId`). | | `key` | `[entity]Key` | Converted from `int64` to `string` with entity prefix. | | `bpmnProcessId` | `processDefinitionId` | Unified naming convention. | | `processName` | `processDefinitionId` | Unified naming convention. | | `decisionKey` | `decisionDefinitionKey` | Unified naming convention. | | `dmnDecisionKey` | `decisionDefinitionKey` | Unified naming convention. | | `decisionId` | `decisionDefinitionId` | Unified naming convention. | | `dmnDecisionId` | `decisionDefinitionId` | Unified naming convention. | **General naming conventions:** - Keys and IDs contain the full entity name as prefix to avoid confusion (for example, `processDefinitionKey` instead of `processKey`). - Entity attributes have no prefix within their own entity, but use prefixes when referenced from other entities. - All key fields are now `string` type instead of `int64`. ## Tasklist REST API ### Form #### Get a form V1 V2 GET `/v1/forms/{formId}` GET [`/v2/user-tasks/{userTaskKey}/form`](../orchestration-cluster-api-rest/specifications/get-user-task-form.api.mdx) GET [`/v2/process-definitions/{processDefinitionKey}/form`](../orchestration-cluster-api-rest/specifications/get-start-process-form.api.mdx) - You cannot fetch forms directly anymore. Instead, fetch them by user task or process definition to get the respective form data. - The respective endpoint only takes the key of the resource the form is related to as input parameter. Embedded forms are no longer returned as Camunda user tasks don't support them. | **Field** | **Change Type** | **Notes** | | ---------------------- | --------------- | ----------------------------------------------------------- | | `id` | Renamed | Now `formKey` (unique system identifier of the form). | | `title` | Renamed | Now `formId` (aligns with form schema attribute). | | `isDeleted` | Removed | No longer provided by endpoint. | | `processDefinitionKey` | Removed | Can be identified from endpoint resource and key parameter. | ### Task #### Save task draft variables V1 V2 POST `/v1/tasks/{taskId}/variables` This feature is not supported in V2 anymore. Use [setting variables][] as `local` to the user task's `elementInstanceKey` as a replacement. #### Search task variables V1 V2 POST `/v1/tasks/{taskId}/variables/search` POST [`/v2/user-tasks/{userTaskKey}/variables/search`](../orchestration-cluster-api-rest/specifications/search-user-task-variables.api.mdx) Request structure changes as outlined in [general changes][]. | **Field** | **Change Type** | **Notes** | | ------------------ | --------------- | ---------------------------------------------------------------------------- | | `variableNames` | Renamed | Now `name` in `filter` object (plain string or `{ "$in": [ "xyz", ... ] }`). | | `includeVariables` | Removed | Endpoint returns all variables associated with the user task. | Response structure changes as outlined in [general changes][]. | **Field** | **Change Type** | **Notes** | | ------------------ | --------------- | ------------------------------------------------------------------------ | | `id` | Renamed | Now `variableKey` (unique system identifier of the variable). | | `previewValue` | Renamed | Now `value` (always represents variable value, may be truncated). | | `isValueTruncated` | Renamed | Now `isTruncated` (see get variable endpoint for full value if needed). | | `draft` | Removed | Draft variables not supported in V2 (see save draft variables endpoint). | For completed tasks, the V1 API returned snapshot variable values as they existed at completion time. The V2 API always returns the current runtime value of variables. #### Search tasks V1 V2 POST `/v1/tasks/search` POST [`/v2/user-tasks/search`](../orchestration-cluster-api-rest/specifications/search-user-tasks.api.mdx) Request structure changes as outlined in [general changes][]. | **Field** | **Change Type** | **Notes** | | ------------------------- | --------------- | --------------------------------------------------------------- | | `pageSize` | Renamed | Now `limit` in the `page` object. | | `searchAfter` | Renamed | Now `after` in the `page` object. | | `searchBefore` | Renamed | Now `before` in the `page` object. | | `taskDefinitionId` | Renamed | Now `elementId` (user-provided identifier of the BPMN element). | | `assigned` | Renamed | Now `assignee` with `{ "$exists": false }`. | | `assignees` | Renamed | Now `assignee` with `{ "$in": [ "xyz", ... ] }`. | | `candidateGroups` | Renamed | Now `candidateGroup` with `{ "$in": [ "xyz", ... ] }`. | | `candidateUsers` | Renamed | Now `candidateUser` with `{ "$in": [ "xyz", ... ] }`. | | `tenantIds` | Renamed | Now `tenantId` with `{ "$in": [ "xyz", ... ] }`. | | `followUpDate`, `dueDate` | Changed | Use `$gte` and `$lte` instead of `from` and `to`. | | `priority` | Changed | Filter keys need `$` prefix, supports new comparison options. | | `taskVariables` | Split | Now `localVariables` and `processInstanceVariables`. | | `searchAfterOrEqual` | Removed | No longer supported. | | `searchBeforeOrEqual` | Removed | No longer supported. | | `includeVariables` | Removed | Use separate search task variables endpoint. | | `implementation` | Removed | V2 API supports only Camunda user tasks. | Response structure changes as outlined in [general changes][]. | **Field** | **Change Type** | **Notes** | | ------------------ | --------------- | ------------------------------------------------------------------------------------- | | `sortValues` | Removed | No longer exist per result item - use `startCursor` and `endCursor` in `page` object. | | `id` | Renamed | Now `userTaskKey` (unique system identifier of the user task). | | `taskDefinitionId` | Renamed | Now `elementId` (user-provided identifier of the BPMN element). | | `taskState` | Renamed | Now `state` (user task's current state). | | `processName` | Renamed | Now `processDefinitionId` (user-provided identifier of the process). | | `formKey` | Changed | Now unique system identifier referencing linked Camunda form in specific version. | | `isFirst` | Removed | No longer identifies if task was first in process. | | `variables` | Removed | Use search user task variables endpoint. | | `implementation` | Removed | V2 API supports only Camunda user tasks. | | `isFormEmbedded` | Removed | V2 API does not support embedded forms. | | `formVersion` | Removed | Use get user task form endpoint. | | `formId` | Removed | Use get user task form endpoint. | #### Unassign a task V1 V2 PATCH `/v1/tasks/{taskId}/unassign` DELETE [`/v2/user-tasks/{userTaskKey}/assignee`](../orchestration-cluster-api-rest/specifications/unassign-user-task.api.mdx) - No input adjustments. - Response object removed - The V2 API returns a 204 status, indicating that the task was unassigned. Fetching the updated data of the user task should be done through the respective API since the data can change concurrently at any time. #### Complete a task V1 V2 PATCH `/v1/tasks/{taskId}/complete` POST [`/v2/user-tasks/{userTaskKey}/completion`](../orchestration-cluster-api-rest/specifications/complete-user-task.api.mdx) - Adjusted attributes - `variables` - Provide the variables as a proper JSON object instead of an array of objects with a `name` and a serialized JSON string `value`. - Response object removed - The V2 API returns a 204 status, indicating that the task was completed. Fetching the updated data of the user task should be done through the respective API since the data can change concurrently at any time. #### Assign a task V1 V2 PATCH `/v1/tasks/{taskId}/assign` POST [`/v2/user-tasks/{userTaskKey}/assignment`](../orchestration-cluster-api-rest/specifications/assign-user-task.api.mdx) - Renamed attributes - `allowOverrideAssignment` - Use `allowOverride`, this still refers to allowing to override any existing assignee. - Response object removed - The V2 API returns a 204 status, indicating that the task was assigned. Fetching the updated data of the user task should be done through the respective API since the data can change concurrently at any time. #### Get a task V1 V2 GET `/v1/tasks/{taskId}` GET [`/v2/user-tasks/{userTaskKey}`](../orchestration-cluster-api-rest/specifications/get-user-task.api.mdx) - No input adjustments. - Except for the response structure changes, all adjustments from [search tasks](#search-tasks) apply. ### Variables #### Get a variable V1 V2 GET `/v1/variables/{variableId}` GET [`/v2/variables/{variableKey}`](../orchestration-cluster-api-rest/specifications/get-variable.api.mdx) - `variableId` - Use `variableKey` as this refers to the unique system identifier of the variable. - Renamed attributes - `id` - Use `variableKey` as this refers to the unique system identifier of the variable. - Removed attributes - `draft` - Draft variables are not supported in V2 anymore, see also the [save draft variables](#save-task-draft-variables) endpoint for further details. ## Operate REST API ### Decision definition #### Search decision definitions V1 V2 POST `/v1/decision-definitions/search` POST [`/v2/decision-definitions/search`](../orchestration-cluster-api-rest/specifications/search-decision-definitions.api.mdx) Request structure changes as outlined in [general changes][]. | **Field** | **Change Type** | **Notes** | | ----------------------------- | --------------- | --------------------------------------------------------------- | | `searchAfter` | Renamed | Now `after` in the `page` object. | | `size` | Renamed | Now `limit` in the `page` object. | | `id` | Renamed | Now `decisionDefinitionKey` in filter object. | | `key` | Renamed | Now `decisionDefinitionKey` (changed from `int64` to `string`). | | `decisionId` | Renamed | Now `decisionDefinitionId` in filter object. | | `decisionRequirementsKey` | Changed | Now `string` type instead of `int64`. | | `decisionRequirementsName` | Removed | Can no longer be used for filtering. | | `decisionRequirementsVersion` | Removed | Can no longer be used for filtering. | Response structure changes as outlined in [general changes][]. | **Field** | **Change Type** | **Notes** | | ----------------------------- | --------------- | --------------------------------------------------------------- | | `total` | Moved | Now `totalItems` in `page` object. | | `sortValues` | Replaced | Now use `endCursor` in `page` object. | | `id` | Renamed | Now `decisionDefinitionKey`. | | `key` | Renamed | Now `decisionDefinitionKey` (changed from `int64` to `string`). | | `decisionId` | Renamed | Now `decisionDefinitionId`. | | `decisionRequirementsKey` | Changed | Now `string` type instead of `int64`. | | `decisionRequirementsName` | Removed | Fetch using get decision requirements endpoint. | | `decisionRequirementsVersion` | Removed | Fetch using get decision requirements endpoint. | #### Get decision definition by key V1 V2 GET `/v1/decision-definitions/{key}` GET [`/v2/decision-definitions/{decisionDefinitionKey}`](../orchestration-cluster-api-rest/specifications/get-decision-definition.api.mdx) - No input adjustments. - Except for the response structure changes, all adjustments from [search decision definitions](#search-decision-definitions) apply. #### Search decision instances V1 V2 POST `/v1/decision-instances/search` POST [`/v2/decision-instances/search`](../orchestration-cluster-api-rest/specifications/search-decision-instances.api.mdx) Request structure changes as outlined in [general changes][]. | **Field** | **Change Type** | **Notes** | | ---------------------- | --------------- | ------------------------------------------------------------- | | `searchAfter` | Renamed | Now `after` in the `page` object. | | `size` | Renamed | Now `limit` in the `page` object. | | `id` | Renamed | Now `decisionInstanceId` in filter object. | | `key` | Renamed | Now `decisionInstanceKey` (changed from `int64` to `string`). | | `processDefinitionKey` | Changed | Now `string` type instead of `int64`. | | `processInstanceKey` | Changed | Now `string` type instead of `int64`. | | `decisionId` | Renamed | Now `decisionDefinitionId`. | | `decisionName` | Renamed | Now `decisionDefinitionName`. | | `decisionVersion` | Renamed | Now `decisionDefinitionVersion`. | | `decisionType` | Renamed | Now `decisionDefinitionType`. | | `result` | Removed | Can no longer be used for filtering. | | `evaluatedInputs` | Removed | Can no longer be used for filtering. | | `evaluatedOutputs` | Removed | Can no longer be used for filtering. | Response structure changes as outlined in [general changes][]. | **Field** | **Change Type** | **Notes** | | ---------------------- | --------------- | ------------------------------------------------------------- | | `total` | Moved | Now `totalItems` in `page` object. | | `sortValues` | Replaced | Now use `endCursor` in `page` object. | | `id` | Renamed | Now `decisionInstanceId`. | | `key` | Renamed | Now `decisionInstanceKey` (changed from `int64` to `string`). | | `processDefinitionKey` | Changed | Now `string` type instead of `int64`. | | `processInstanceKey` | Changed | Now `string` type instead of `int64`. | | `decisionId` | Renamed | Now `decisionDefinitionId`. | | `decisionName` | Renamed | Now `decisionDefinitionName`. | | `decisionVersion` | Renamed | Now `decisionDefinitionVersion`. | | `decisionType` | Renamed | Now `decisionDefinitionType`. | | `evaluatedInputs` | Removed | No longer provided by endpoint. | | `evaluatedOutputs` | Removed | No longer provided by endpoint. | #### Get decision instance by id V1 V2 GET `/v1/decision-instances/{id}` GET [`/v2/decision-instances/{decisionInstanceId}`](../orchestration-cluster-api-rest/specifications/search-decision-instances.api.mdx) - No input adjustments. The adjustments from [search decision instances](#search-decision-instances) apply, with the following exceptions: `evaluatedInputs` and `evaluatedOutputs` are present in the response payload (with `evaluatedOutputs` moved under `matchedRules`). | **Field** | **Change Type** | **Notes** | | --------------------------- | --------------- | --------------------------------------- | | **evaluatedInputs object** | | | | `id` | Renamed | Now `inputId`. | | `name` | Renamed | Now `inputName`. | | `value` | Renamed | Now `inputValue`. | | **evaluatedOutputs object** | | | | `id` | Renamed | Now `outputId`. | | `name` | Renamed | Now `outputName`. | | `value` | Renamed | Now `outputValue`. | | `ruleId` | Moved | Now under `matchedRules` array objects. | | `ruleIndex` | Moved | Now under `matchedRules` array objects. | #### Search decision requirements V1 V2 POST `/v1/drd/search` POST [`/v2/decision-requirements/search`](../orchestration-cluster-api-rest/specifications/search-decision-requirements.api.mdx) Request structure changes as outlined in [general changes][]. | **Field** | **Change Type** | **Notes** | | ------------- | --------------- | ----------------------------------------------------------------- | | `searchAfter` | Renamed | Now `after` in the `page` object. | | `size` | Renamed | Now `limit` in the `page` object. | | `id` | Renamed | Now `decisionRequirementsKey` in filter object. | | `key` | Renamed | Now `decisionRequirementsKey` (changed from `int64` to `string`). | | `name` | Renamed | Now `decisionRequirementsName`. | Response structure changes as outlined in [general changes][]. | **Field** | **Change Type** | **Notes** | | ------------ | --------------- | ----------------------------------------------------------------- | | `total` | Moved | Now `totalItems` in `page` object. | | `sortValues` | Replaced | Now use `endCursor` in `page` object. | | `id` | Renamed | Now `decisionRequirementsKey`. | | `key` | Renamed | Now `decisionRequirementsKey` (changed from `int64` to `string`). | | `name` | Renamed | Now `decisionRequirementsName`. | #### Get decision requirements by key V1 V2 GET `/v1/drd/{key}` GET [`/v2/decision-requirements/{decisionRequirementsKey}`](../orchestration-cluster-api-rest/specifications/get-decision-requirements.api.mdx) - No input adjustments. - Except for the response structure changes, all adjustments from [search decision requirements](#search-decision-requirements) apply. #### Get decision requirements as XML by key V1 V2 GET `/v1/drd/{key}/xml` GET [`/v2/decision-requirements/{decisionRequirementsKey}/xml`](../orchestration-cluster-api-rest/specifications/get-decision-requirements-xml.api.mdx) - No input adjustments. - No output adjustments. ### Variable #### Search variables for process instances V1 V2 POST `/v1/variables/search` POST [`/v2/variables/search`](../orchestration-cluster-api-rest/specifications/search-variables.api.mdx) Request structure changes as outlined in [general changes][]. | **Field** | **Change Type** | **Notes** | | -------------------- | --------------- | ----------------------------------------------------- | | `searchAfter` | Renamed | Now `after` in the `page` object. | | `size` | Renamed | Now `limit` in the `page` object. | | `key` | Renamed | Now `variableKey` (changed from `int64` to `string`). | | `processInstanceKey` | Changed | Now `string` type instead of `int64`. | | `scopeKey` | Changed | Now `string` type instead of `int64`. | | `truncated` | Renamed | Now `isTruncated`. | Response structure changes as outlined in [general changes][]. | **Field** | **Change Type** | **Notes** | | -------------------- | --------------- | ----------------------------------------------------- | | `total` | Moved | Now `totalItems` in `page` object. | | `sortValues` | Replaced | Now use `endCursor` in `page` object. | | `key` | Renamed | Now `variableKey` (changed from `int64` to `string`). | | `processInstanceKey` | Changed | Now `string` type instead of `int64`. | | `scopeKey` | Changed | Now `string` type instead of `int64`. | | `truncated` | Renamed | Now `isTruncated`. | #### Get variable by key V1 V2 GET `/v1/variables/{key}` GET [`/v2/variables/{variableKey}`](../orchestration-cluster-api-rest/specifications/get-variable.api.mdx) - No input adjustments. - All adjustments from [search variables for process instances](#search-variables-for-process-instances) apply, with the following exceptions: - Response structure changes. - `truncated` is removed because this endpoint always returns the full variable value. [setting variables]: /apis-tools/orchestration-cluster-api-rest/specifications/create-element-instance-variables.api.mdx [general changes]: #general-endpoint-changes [multi-tenancy]: /components/concepts/multi-tenancy.md ### Process definition #### Search process definitions V1 V2 POST `/v1/process-definitions/search` POST [`/v2/process-definitions/search`](../orchestration-cluster-api-rest/specifications/search-process-definitions.api.mdx) Request structure changes as outlined in [general changes][]. | **Field** | **Change Type** | **Notes** | | --------------- | --------------- | -------------------------------------------------------------- | | `searchAfter` | Renamed | Now `after` in the `page` object. | | `size` | Renamed | Now `limit` in the `page` object. | | `key` | Renamed | Now `processDefinitionKey` (changed from `int64` to `string`). | | `bpmnProcessId` | Renamed | Now `processDefinitionId`. | Response structure changes as outlined in [general changes][]. | **Field** | **Change Type** | **Notes** | | --------------- | --------------- | -------------------------------------------------------------- | | `total` | Moved | Now `totalItems` in `page` object. | | `sortValues` | Replaced | Now use `endCursor` in `page` object. | | `key` | Renamed | Now `processDefinitionKey` (changed from `int64` to `string`). | | `bpmnProcessId` | Renamed | Now `processDefinitionId`. | #### Get process definition by key V1 V2 GET `/v1/process-definitions/{key}` GET [`/v2/process-definitions/{processDefinitionKey}`](../orchestration-cluster-api-rest/specifications/get-process-definition.api.mdx) - No input adjustments. - Except for the response structure changes, all adjustments from [search process definitions](#search-process-definitions) apply. #### Get process definition as XML by key V1 V2 GET `/v1/process-definitions/{key}/xml` GET [`/v2/process-definitions/{processDefinitionKey}/xml`](../orchestration-cluster-api-rest/specifications/get-process-definition-xml.api.mdx) - No input adjustments. - No output adjustments. ### Process instance #### Search process instances V1 V2 POST `/v1/process-instances/search` POST [`/v2/process-instances/search`](../orchestration-cluster-api-rest/specifications/search-process-instances.api.mdx) Request structure changes as outlined in [general changes][]. | **Field** | **Change Type** | **Notes** | | --------------------------- | --------------- | ------------------------------------------------------------ | | `searchAfter` | Renamed | Now `after` in the `page` object. | | `size` | Renamed | Now `limit` in the `page` object. | | `key` | Renamed | Now `processInstanceKey` (changed from `int64` to `string`). | | `processVersion` | Renamed | Now `processDefinitionVersion`. | | `processVersionTag` | Renamed | Now `processDefinitionVersionTag`. | | `bpmnProcessId` | Renamed | Now `processDefinitionId`. | | `parentFlowNodeInstanceKey` | Renamed | Now `parentElementInstanceKey` (changed to `string`). | | `parentKey` | Renamed | Now `parentProcessInstanceKey` (changed to `string`). | | `state` | Changed | Use `TERMINATED` instead of `CANCELED`. | | `incident` | Renamed | Now `hasIncident`. | | `parentProcessInstanceKey` | Changed | Now `string` type instead of `int64`. | | `processDefinitionKey` | Changed | Now `string` type instead of `int64`. | Response structure changes as outlined in [general changes][]. | **Field** | **Change Type** | **Notes** | | --------------------------- | --------------- | ------------------------------------------------------------ | | `total` | Moved | Now `totalItems` in `page` object. | | `sortValues` | Replaced | Now use `endCursor` in `page` object. | | `key` | Renamed | Now `processInstanceKey` (changed from `int64` to `string`). | | `processVersion` | Renamed | Now `processDefinitionVersion`. | | `processVersionTag` | Renamed | Now `processDefinitionVersionTag`. | | `bpmnProcessId` | Renamed | Now `processDefinitionId`. | | `parentFlowNodeInstanceKey` | Renamed | Now `parentElementInstanceKey` (changed to `string`). | | `parentKey` | Renamed | Now `parentProcessInstanceKey` (changed to `string`). | | `state` | Changed | Use `TERMINATED` instead of `CANCELED`. | | `incident` | Renamed | Now `hasIncident`. | | `parentProcessInstanceKey` | Changed | Now `string` type instead of `int64`. | | `processDefinitionKey` | Changed | Now `string` type instead of `int64`. | #### Get process instance by key V1 V2 GET `/v1/process-instances/{key}` GET [`/v2/process-instances/{processInstanceKey}`](../orchestration-cluster-api-rest/specifications/get-process-instance.api.mdx) - No input adjustments. - Except for the response structure changes, all adjustments from [search process instances](#search-process-instances) apply. #### Delete process instance and all dependant data by key V1 V2 DELETE `/v1/process-instances/{key}` This feature is not yet available in V2. It will be added in a future version. #### Get flow node statistic by process instance key V1 V2 GET `/v1/process-instances/{key}/statistics` GET [`/v2/process-instances/{processInstanceKey}/statistics/element-instances`](../orchestration-cluster-api-rest/specifications/get-process-instance-statistics.api.mdx) - No input adjustments. Response structure changes. | **Field** | **Change Type** | **Notes** | | -------------- | --------------- | ----------------------- | | Response items | Moved | Now under `items` array | | `activityId` | Renamed | Now `elementId` | #### Get sequence flows of process instance by key V1 V2 GET `/v1/process-instances/{key}/sequence-flows` GET [`/v2/process-instances/{processInstanceKey}/sequence-flows`](../orchestration-cluster-api-rest/specifications/get-process-instance-sequence-flows.api.mdx) - No input adjustments. Response structure changes. | **Field** | **Change Type** | **Notes** | | -------------- | --------------- | --------------------------------------------------------------------------------- | | Response items | Changed | Now type `object` instead of `string`. | | Response items | Moved | Now under `items` array. | | V1 recreation | Info | Collect `sequenceFlowId` of type `string` from all objects to recreate V1 result. | ### Flownode instances #### Search flownode instances V1 V2 POST `/v1/flownode-instances/search` POST [`/v2/element-instances/search`](../orchestration-cluster-api-rest/specifications/search-element-instances.api.mdx) Request structure changes as outlined in [general changes][]. | **Field** | **Change Type** | **Notes** | | ---------------------- | --------------- | ------------------------------------------------------------ | | `searchAfter` | Renamed | Now `after` in the `page` object. | | `size` | Renamed | Now `limit` in the `page` object. | | `key` | Renamed | Now `elementInstanceKey` (changed from `int64` to `string`). | | `flowNodeId` | Renamed | Now `elementId`. | | `flowNodeName` | Renamed | Now `elementName`. | | `incident` | Renamed | Now `hasIncident`. | | `processInstanceKey` | Changed | Now `string` type instead of `int64`. | | `processDefinitionKey` | Changed | Now `string` type instead of `int64`. | | `incidentKey` | Changed | Now `string` type instead of `int64`. | | `startDate` | Removed | Can no longer be used for filtering. | | `endDate` | Removed | Can no longer be used for filtering. | Response structure changes as outlined in [general changes][]. | **Field** | **Change Type** | **Notes** | | ---------------------- | --------------- | ------------------------------------------------------------ | | `total` | Moved | Now `totalItems` in `page` object. | | `sortValues` | Replaced | Now use `endCursor` in `page` object. | | `key` | Renamed | Now `elementInstanceKey` (changed from `int64` to `string`). | | `flowNodeId` | Renamed | Now `elementId`. | | `flowNodeName` | Renamed | Now `elementName`. | | `incident` | Renamed | Now `hasIncident`. | | `processInstanceKey` | Changed | Now `string` type instead of `int64`. | | `processDefinitionKey` | Changed | Now `string` type instead of `int64`. | | `incidentKey` | Changed | Now `string` type instead of `int64`. | #### Get flownode instance by key V1 V2 GET `/v1/flownode-instances/{key}` GET [`/v2/element-instances/{elementInstanceKey}`](../orchestration-cluster-api-rest/specifications/get-element-instance.api.mdx) - No input adjustments. - Except for the response structure changes, all adjustments from [search flownode instances](#search-flownode-instances) apply. ### Incidents #### Search incidents V1 V2 POST `/v1/incidents/search` POST [`/v2/incidents/search`](../orchestration-cluster-api-rest/specifications/search-incidents.api.mdx) Request structure changes as outlined in [general changes][]. | **Field** | **Change Type** | **Notes** | | ---------------------- | --------------- | ----------------------------------------------------- | | `searchAfter` | Renamed | Now `after` in the `page` object. | | `size` | Renamed | Now `limit` in the `page` object. | | `key` | Renamed | Now `incidentKey` (changed from `int64` to `string`). | | `type` | Renamed | Now `errorType`. | | `message` | Renamed | Now `errorMessage`. | | `processInstanceKey` | Changed | Now `string` type instead of `int64`. | | `processDefinitionKey` | Changed | Now `string` type instead of `int64`. | | `jobKey` | Changed | Now `string` type instead of `int64`. | Response structure changes as outlined in [general changes][]. | **Field** | **Change Type** | **Notes** | | ---------------------- | --------------- | ----------------------------------------------------- | | `total` | Moved | Now `totalItems` in `page` object. | | `sortValues` | Replaced | Now use `endCursor` in `page` object. | | `key` | Renamed | Now `incidentKey` (changed from `int64` to `string`). | | `type` | Renamed | Now `errorType`. | | `message` | Renamed | Now `errorMessage`. | | `processInstanceKey` | Changed | Now `string` type instead of `int64`. | | `processDefinitionKey` | Changed | Now `string` type instead of `int64`. | | `jobKey` | Changed | Now `string` type instead of `int64`. | #### Get incident by key V1 V2 GET `/v1/incidents/{key}` GET [`/v2/incidents/{incidentKey}`](../orchestration-cluster-api-rest/specifications/get-incident.api.mdx) - No input adjustments. - Except for the response structure changes, all adjustments from [search incidents](#search-incidents) apply. --- ## Migrate to the Camunda Java Client :::note Using Spring Boot? If you are migrating from the Spring Zeebe SDK, use [migrate to Camunda Spring Boot Starter](migrate-to-camunda-spring-boot-starter.md) instead. This guide covers migrations from the Zeebe Java Client to the Camunda Java Client. ::: :::note Have you already migrated? You do not need to perform this migration again if you already did this when upgrading to version 8.8. This guide remains in the 8.9 documentation for customers who did not perform this migration during their 8.8 upgrade. See [API and SDK changes to migrate before Camunda 8.10](../migration-manuals/migrate-to-89.md#api-and-sdk-changes-to-migrate-before-camunda-810). ::: ## About This guide provides an overview of the process for migrating to the Camunda Java Client. - The [Camunda Java Client](../java-client/getting-started.md) is the official Java library for connecting to Orchestration Cluster, automating processes, and implementing job workers. - The Zeebe Java Client remains available until Camunda 8.10. :::tip Plan and start your migration early to ensure compatibility, access to latest features, and future support. ::: ## Before you begin - Review project dependencies and identify where `io.camunda.zeebe:zeebe-client-java` is used. - Catalog code referencing Zeebe classes, interfaces, and APIs (for example, ZeebeClient, Zeebe workers). ## Update Maven/Gradle dependencies Replace the Zeebe Java Client dependency with the Camunda Java Client dependency in your `pom.xml` or `build.gradle` file. Maven: ```xml io.camunda camunda-client-java ${camunda.version} ``` Gradle: ```groovy implementation 'io.camunda:camunda-client-java:${camunda.version}' ``` ## Update imports Update all imports statement in your Java files to use the new Camunda Java Client package structure. Change from: ```java ``` to: ```java ``` ## Configuration and environment variable changes - All old Java client property names are refactored to more general ones. For example, `zeebe.client.tenantId` to `camunda.client.tenantId`. - Environment variables are also updated accordingly. For example, `ZEEBE_CLIENT_TENANT_ID` to `CAMUNDA_CLIENT_TENANT_ID`. - The former deprecated `gatewayAddress` property and `usePlainText` have been **removed and superseded by `restAddress` and `grpcAddress` which require explicit URI schemes** (for example, `http://` or `https://`). ## Update client initialization Update the client initialization code to use the new `CamundaClient` class. For example, change: ```java ZeebeClient client = ZeebeClient.newClientBuilder() .gatewayAddress("localhost:26500") .usePlaintext() .build(); ``` to: ```java CamundaClient client = CamundaClient.newClientBuilder() .grpcAddress(URI.create("http://localhost:26500")) .restAddress(URI.create("http://localhost:8080")) .build(); ``` :::info - Refer to the [CamundaClientBuilder documentation](https://javadoc.io/doc/io.camunda/camunda-client-java/latest/io/camunda/client/CamundaClientBuilder.html) for more details on available configuration options. - The construction for OAuth, Basic Auth, or custom providers remains conceptually the same, but you must ensure you use the classes from the new package. Refer to the [Camunda Java Client bootstrapping](../java-client/getting-started.md#bootstrapping) for more details. ::: ## Renamed API classes and commands The following API classes have been changed in the Camunda Java Client: | Old | New | | :------------------------------ | :-------------------------------- | | `ZeebeClientBuilder` | `CamundaClientBuilder` | | `ZeebeClientClouldBuilderStep1` | `CamundaClientClouldBuilderStep1` | | `ZeebeClientConfiguration` | `CamundaClientConfiguration` | | `ZeebeFuture` | `CamundaFuture` | The following commands have been renamed in the Camunda Java Client: | Old | New | | :----------------------------- | :----------------------------- | | `newClockPinCommand()` | `newPinClockCommand()` | | `newClockResetCommand()` | `newResetClockCommand()` | | `newUserCreateCommand()` | `newCreateUserCommand()` | | `newUserTaskAssignCommand()` | `newAssignUserTaskCommand()` | | `newUserTaskCompleteCommand()` | `newCompleteUserTaskCommand()` | | `newUserTaskUnassignCommand()` | `newUnassignUserTaskCommand()` | | `newUserTaskUpdateCommand()` | `newUpdateUserTaskCommand()` | ## Protocol and connection: REST vs gRPC selection Zeebe Java Client used **gRPC by default**. The Camunda Java Client uses **REST by default**. If you want to use gRPC, you need to explicitly set the `grpcAddress` in the client builder and configure `preferRestOverGrpc=false` to make gRPC the default. To use gRPC, add the following to your client builder: ```java CamundaClient client = CamundaClient.newClientBuilder() .grpcAddress(URI.create("http://localhost:26500")) .restAddress(URI.create("http://localhost:8080")) .preferRestOverGrpc(false) .build(); ``` --- ## Migrate to Camunda Process Test :::note Have you already migrated? You do not need to perform this migration again if you already did this when upgrading to version 8.8. This guide remains in the 8.9 documentation for customers who did not perform this migration during their 8.8 upgrade. See [API and SDK changes to migrate before Camunda 8.10](../migration-manuals/migrate-to-89.md#api-and-sdk-changes-to-migrate-before-camunda-810). ::: ## About [Camunda Process Test](/apis-tools/testing/getting-started.md) (CPT) is a library to test your BPMN processes and your process applications. - It is the successor to Zeebe Process Test (ZPT). - Starting with version **8.8**, ZPT is deprecated and was removed in version **8.10**. See [release announcement](https://camunda.com/blog/2025/04/camunda-process-test-the-next-generation-testing-library/). This guide walks you through migrating your existing test cases from ZPT to CPT step-by-step. ### Key differences There are key differences between ZPT and CPT in both API and behavior, which may increase migration effort depending on your existing test cases. | Aspect | Zeebe Process Test (ZPT) | Camunda Process Test (CPT) | | :--------------------------- | :------------------------------------------------------------------------------ | :---------------------------------------------------------------------------------------------------- | | **Underlying engine** | Uses only Camunda's workflow engine (Zeebe) with access to internal components. | Runs the full Camunda distribution and interacts with the Orchestration Cluster API. | | **Assertions and utilities** | Uses specific naming conventions. | Uses different names to align with the API; not all ZPT assertions/utilities have equivalents in CPT. | | **Startup time** | Faster startup. | Takes longer to start as it runs the full Orchestration Cluster distribution. | ### Key advantages of CPT - Access to Camunda’s Orchestration Cluster API and Connectors - Support for Camunda user tasks - Blocking assertions for asynchronous processing - Enhanced mocking utilities ## Update your dependency First, update your Maven dependency. - **If you use ZPT with Camunda Spring Boot Starter integration** (`artifactId: spring-boot-starter-camunda-test` or `spring-boot-starter-camunda-test-testcontainer`), replace it with **CPT’s Spring integration module**. - **If you use ZPT without Spring** (`artifactId: zeebe-process-test-extension` or `zeebe-process-test-extension-testcontainer`), replace it with **CPT’s Java module**. In your Maven `pom.xml`, add the dependency: ```xml io.camunda camunda-process-test-spring test ``` In your Maven `pom.xml`, add the dependency: ```xml io.camunda camunda-process-test-java test ``` ## Choose your runtime Next, choose how you want to run CPT, considering your environment. CPT can be used in two modes: - CPT with Testcontainers (as equivalent to ZPT with Testcontainers) - CPT with remote engine (as equivalent to ZPT's embedded runtime) ### ZPT with Testcontainers If you use ZPT with Testcontainers ( `artifactId: zeebe-process-test-extension-testcontainer` or `spring-boot-starter-camunda-test-testcontainer`), then you can use CPT's default [Testcontainers runtime](/apis-tools/testing/configuration.md#testcontainers-runtime) without additional changes. ### ZPT's embedded runtime If you use ZPT’s embedded runtime (`artifactId: zeebe-process-test-extension` or `spring-boot-starter-camunda-test`), switch to CPT’s [remote runtime](/apis-tools/testing/configuration.md#remote-runtime). Choose this option only if you cannot install a Docker-API compatible container runtime (e.g., Docker on Linux or Docker Desktop). In this mode, CPT connects to a remote runtime, such as a local Camunda 8 Run running on your machine. Prepare your remote runtime: 1. **Install Camunda 8 Run** Follow the [installation guide](/self-managed/quickstart/developer-quickstart/c8run/install-start.md#install-and-start-camunda-8-run) on your machine. 2. **Enable the management clock endpoint** See [prerequisites](/apis-tools/testing/configuration.md#prerequisites-1): - Create an `application.yaml` file in the root `/c8run` directory. - Add: ```yaml zeebe.clock.controlled: true ``` 3. **Start Camunda 8 Run**. 4. **Switch CPT’s runtime mode** to `remote` in your project configuration. In your `application.yml` (or `application.properties`): ```yaml camunda: process-test: runtime-mode: remote ``` In your `/camunda-container-runtime.properties` file: ``` runtimeMode=remote ``` ## Migrate your process tests Now, it's time to migrate your process tests. First, migrate the general test class structure: 1. **Replace annotations and types** - Replace `@ZeebeSpringTest` with `@CamundaSpringProcessTest` - Replace the type `ZeebeTestEngine` with `CamundaProcessTestContext` 2. **Remove record stream fields** CPT does not provide direct access to records. Instead, use the SDK to request data from the [API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). Below is an example of a ZPT test class: ```java @SpringBootTest @ZeebeSpringTest class MyProcessTest { @Autowired private CamundaClient client; @Autowired private ZeebeTestEngine engine; @Autowired private RecordStream recordStream; @Test void shouldCompleteProcess() { // given final ProcessInstanceEvent processInstance = client .newCreateInstanceCommand() .bpmnProcessId("my-process") .latestVersion() .send() .join(); // when: drive the process forward // then BpmnAssert.assertThat(processInstance) .hasPassedElementsInOrder("start", "task1", "task2", "task3", "end") .isCompleted(); } } ``` This is the equivalent CPT test class: ```java @SpringBootTest @CamundaSpringProcessTest class MyProcessTest { @Autowired private CamundaClient client; @Autowired private CamundaProcessTestContext processTestContext; @Test void shouldCompleteProcess() { // given final ProcessInstanceEvent processInstance = client .newCreateInstanceCommand() .bpmnProcessId("my-process") .latestVersion() .send() .join(); // when: drive the process forward // then CamundaAssert.assertThat(processInstance) .hasCompletedElementsInOrder("start", "task1", "task2", "task3", "end") .isCompleted(); } } ``` First, migrate the general test class structure: 1. **Replace annotations and types** - Replace `@ZeebeProcessTest` with `@CamundaProcessTest` - Replace the type `ZeebeTestEngine` with `CamundaProcessTestContext` 2. **Remove record stream fields** CPT does not provide direct access to records. Instead, use the SDK to request data from the [API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). Below is an example of a ZPT test class: ```java @ZeebeProcessTest class MyProcessTest { private CamundaClient client; private ZeebeTestEngine engine; private RecordStream recordStream; @Test void shouldCompleteProcess() { // given: the processes are deployed final ProcessInstanceEvent processInstance = client .newCreateInstanceCommand() .bpmnProcessId("my-process") .latestVersion() .send() .join(); // when: drive the process forward // then BpmnAssert.assertThat(processInstance) .hasPassedElementsInOrder("start", "task1", "task2", "task3", "end") .isCompleted(); } } ``` This is the equivalent CPT test class: ```java @CamundaProcessTest class MyProcessTest { private CamundaClient client; private CamundaProcessTestContext processTestContext; @Test void shouldCompleteProcess() { // given: the processes are deployed final ProcessInstanceEvent processInstance = client .newCreateInstanceCommand() .bpmnProcessId("my-process") .latestVersion() .send() .join(); // when: drive the process forward // then CamundaAssert.assertThat(processInstance) .hasCompletedElementsInOrder("start", "task1", "task2", "task3", "end") .isCompleted(); } } ``` Then, review all test methods and migrate the assertions and utilities. See the following sections for detailed instructions. ### Process instance assertions ZPT has assertions for a process instance using `BpmnAssert.assertThat()` with the `ProcessInstanceEvent` or the `ProcessInstanceResult`. CPT has equivalent assertions using `CamundaAssert.assertThat()`. ```java // given ProcessInstanceEvent processInstance = // // ZPT: BpmnAssert.assertThat(processInstance).isCompleted(); // CPT: CamundaAssert.assertThat(processInstance).isCompleted(); ``` Some of CPT's assertions have different method names or signatures. Check the following list for the equivalent CPT assertion: ZPT: BpmnAssert.assertThat(processInstance) CPT: CamundaAssert.assertThat(processInstance) isStarted() [isCreated()](/apis-tools/testing/assertions.md#iscreated) isActive() [isActive()](/apis-tools/testing/assertions.md#isactive) isCompleted() [isCompleted()](/apis-tools/testing/assertions.md#iscompleted) isNotCompleted() Not supported Instead, use [isActive()](/apis-tools/testing/assertions.md#isactive) or [isTerminated()](/apis-tools/testing/assertions.md#isterminated). isTerminated() [isTerminated()](/apis-tools/testing/assertions.md#isterminated) isNotTerminated() Not supported Instead, use [isActive()](/apis-tools/testing/assertions.md#isactive) or [isCompleted()](/apis-tools/testing/assertions.md#iscompleted). isWaitingAtElements() [hasActiveElements()](/apis-tools/testing/assertions.md#hasactiveelements) isWaitingExactlyAtElements() [hasActiveElementsExactly()](/apis-tools/testing/assertions.md#hasactiveelementsexactly) isNotWaitingAtElements() [hasNoActiveElements()](/apis-tools/testing/assertions.md#hasnoactiveelements) hasPassedElement() [hasCompletedElement()](/apis-tools/testing/assertions.md#hascompletedelement) hasPassedElementsInOrder() [hasCompletedElementsInOrder()](/apis-tools/testing/assertions.md#hascompletedelementsinorder) hasNotPassedElement() [hasNotActivatedElements()](/apis-tools/testing/assertions.md#hasnotactivatedelements) hasVariable() [hasVariableNames()](/apis-tools/testing/assertions.md#hasvariablenames) hasVariableWithValue() [hasVariable()](/apis-tools/testing/assertions.md#hasvariable) hasAnyIncidents() [hasActiveIncidents()](/apis-tools/testing/assertions.md#hasactiveincidents) hasNoIncidents() [hasNoActiveIncidents()](/apis-tools/testing/assertions.md#hasnoactiveincidents) isWaitingForMessages() [isWaitingForMessage()](/apis-tools/testing/assertions.md#iswaitingformessage) isNotWaitingForMessages() [isNotWaitingForMessage()](/apis-tools/testing/assertions.md#isnotwaitingformessage) hasCorrelatedMessageByName() [hasCorrelatedMessage()](/apis-tools/testing/assertions.md#hascorrelatedmessage) hasCorrelatedMessageByCorrelationKey() [hasCorrelatedMessage()](/apis-tools/testing/assertions.md#hascorrelatedmessage) hasCalledProcess() Not supported Instead, use a [ProcessInstanceSelector](/apis-tools/testing/assertions.md#with-process-instance-selector) to assert the child process instance. hasNotCalledProcess() Not supported Instead, assert the call activity or the child process instance. ### Deployment assertions ZPT has assertions for a deployment using `BpmnAssert.assertThat()` with the `DeploymentEvent`. CPT has no equivalent assertions. Instead, you could write a [custom assertion](/apis-tools/testing/assertions.md#custom-assertions) with AssertJ to verify the properties of the deployment event. ```java // given DeploymentEvent deploymentEvent = // // ZPT: BpmnAssert.assertThat(deploymentEvent).containsProcessesByResourceName("my-process.bpmn"); // CPT: Assertions.assertThat(deploymentEvent.getProcesses()) .extracting(Process::getResourceName) .contains("my-process.bpmn"); ``` ### Job assertions ZPT has assertions for an activated job using `BpmnAssert.assertThat()` with the `ActivatedJob`. CPT has no equivalent assertions. Instead, you could write a [custom assertion](/apis-tools/testing/assertions.md#custom-assertions) with AssertJ to verify the properties of the activated job. ```java // given ActivatedJob activatedJob = // // ZPT: BpmnAssert.assertThat(activatedJob).hasElementId("elementId"); // CPT: Assertions.assertThat(activatedJob.getElementId()).isEqualTo("elementId"); ``` ### Message assertions ZPT has assertions for a published message using `BpmnAssert.assertThat()` with the `PublishMessageResponse`. CPT has no equivalent assertions. Instead, you could use a [ProcessInstanceSelector](/apis-tools/testing/assertions.md#with-process-instance-selector) to find the correlated process instance and verify the correlation using a [message subscription assertion](/apis-tools/testing/assertions.md#hascorrelatedmessage). Alternatively, you could use the [correlate message API](/apis-tools/orchestration-cluster-api-rest/specifications/correlate-message.api.mdx) that returns the process instance key in the response. ```java // ZPT: final PublishMessageResponse publishMessageResponse = // BpmnAssert.assertThat(publishMessageResponse) .hasCreatedProcessInstance() .extractingProcessInstance() .isCompleted(); // CPT: final CorrelateMessageResponse correlateMessageResponse = // // The correlate command would fail if the message could not be correlated CamundaAssert.assertThatProcessInstance(byKey(correlateMessageResponse.getProcessInstanceKey())) .isCompleted(); ``` ### Inspection utilities ZPT provides the `InspectionUtility` to locate process instances and pass them to assertions. Some assertions also include methods to extract related entities, such as `extractingProcessInstance()` or `extractingLatestIncident()`. CPT offers a similar utility via the [ProcessInstanceSelector](/apis-tools/testing/assertions.md#with-process-instance-selector), which can be used with `CamundaAssert.assertThatProcessInstance()`. For other entities, you can use the Camunda client to search for the entity and implement a [custom assertion](/apis-tools/testing/assertions.md#custom-assertions). ```java // ZPT: InspectedProcessInstance childProcessInstance = InspectionUtility.findProcessInstances() .withBpmnProcessId("child-process") .findFirstProcessInstance() .get(); BpmnAssert.assertThat(childProcessInstance).isCompleted(); // CPT: CamundaAssert.assertThatProcessInstance(byProcessId("child-process")) .isCompleted(); ``` ### ZeebeTestEngine utilities ZPT provides the `ZeebeTestEngine` utilities to interact with the runtime, for example, to advance time. CPT offers a similar utility via the [CamundaProcessTestContext](/apis-tools/testing/utilities.md), but the following utilities are **not supported**: - `waitForIdleState(duration)` - `waitForBusyState(duration)` CPT does not require these utilities because it provides [blocking assertions](/apis-tools/testing/assertions.md) that wait until the expected condition is fulfilled. ```java // ZPT engine.waitForIdleState(duration); engine.increaseTime(Duration.ofDays(1)); // CPT: assertThat(processInstance).hasActiveElements("timer_event"); processTestContext.increaseTime(Duration.ofDays(1)); ``` ## Next steps Congratulations! Your process tests should now be fully migrated to CPT and running successfully. When you’re ready, take the next steps to continue your journey: - Explore new [assertions](/apis-tools/testing/assertions.md). - Simplify your tests with new [utilities](/apis-tools/testing/utilities.md). - Generate [process test coverage reports](/apis-tools/testing/getting-started.md#process-test-coverage). - Refer to the [API documentation](https://javadoc.io/doc/io.camunda/camunda-process-test-java/latest/io/camunda/process/test/api/package-summary.html) for details. ## Troubleshooting If you encounter issues with the migration or notice missing features, please report them in the [Camunda GitHub repository](https://github.com/camunda/camunda/issues). --- ## Migrate to Camunda Spring Boot Starter :::note Have you already migrated? You do not need to perform this migration again if you already did this when upgrading to version 8.8. This guide remains in the 8.9 documentation for customers who did not perform this migration during their 8.8 upgrade. See [API and SDK changes to migrate before Camunda 8.10](../migration-manuals/migrate-to-89.md#api-and-sdk-changes-to-migrate-before-camunda-810). ::: ## About This guide provides an overview of the process for migrating to the Camunda Spring Boot Starter. - The [Camunda Spring Boot Starter](../camunda-spring-boot-starter/getting-started.md) is the official Spring library for connecting to Orchestration Cluster, automating processes, and implementing job workers. :::tip Plan and start your migration early to ensure compatibility, access to latest features, and future support. ::: ## Maven/Gradle dependencies Replace the Zeebe Spring SDK dependency with the Camunda Spring Boot Starter dependency in your `pom.xml` or `build.gradle` file. Maven: ```xml io.camunda camunda-spring-boot-starter 8.8.x ``` Gradle: ```groovy implementation 'io.camunda:camunda-spring-boot-starter:${8.8.x}' ``` ## Deprecated classes and methods Please refer to the [Camunda Java Client migration guide](migrate-to-camunda-java-client.md) for details on deprecated classes and methods. --- ## Migrate to Camunda user tasks :::note Have you already migrated? You do not need to perform this migration again if you already did this when upgrading to version 8.8. This guide remains in the 8.9 documentation for customers who did not perform this migration during their 8.8 upgrade. See [API and SDK changes to migrate before Camunda 8.10](../migration-manuals/migrate-to-89.md#api-and-sdk-changes-to-migrate-before-camunda-810). ::: ## About Camunda 8.7 introduced a new [user task](/components/modeler/bpmn/user-tasks/user-tasks.md) implementation type: Camunda user task ([formerly named Zeebe user task](/reference/announcements-release-notes/870/870-release-notes.md#zeebe-user-tasks-modeling-migration-support-saasself-managedmodeler)). Camunda user tasks have several benefits compared to Job worked-based user tasks, including: - Running directly on the automation engine for high performance. - Removing dependencies and round trips to Tasklist. - A powerful API that supports the full task lifecycle. In this guide, you will learn: - Under which circumstances and when you should migrate. - How to estimate the impact on a project. - Steps you need to take for a successful migration without interrupting your operations. ## Decide on your migration path Camunda user tasks require migration of the user tasks in both your diagrams and the task API. With this in mind, you can migrate at your own pace. If you should migrate now or later, and what is required to migrate depends on your current setup and future plans. ### Task type differences To make an informed decision, you should understand the differences between both task types and the new capabilities of Camunda user tasks. Refer to this table for important high-level differences between the two task types: Camunda user tasks Recommended for new and existing projects Job worker-based user tasks Existing implementation Implementation location Zeebe Does not require Tasklist to run Tasklist Compatible versions 8.5 + 8.0 + Supports Tasklist UI API Supports Orchestration Cluster REST API Full support Supports Tasklist API (deprecated) Partially Queries, GET tasks, forms, variables ℹ You must use Zeebe and Tasklist APIs to manage Camunda user tasks Full support Supports job workers Supports task lifecycle events Full lifecycle events including custom actions Basic only: created/completed/canceled Supports task listeners Extras Custom actions/outcomes Custom actions can be defined on any operation excluding unassign (DELETE assignment, send update beforehand) Supports task reports in Optimize Recommendations Recommended for existing and new projects when you run Tasklist. Migrate existing projects and task applications/clients to this task type when you require one of the features above, or the following use cases: Implement a full task lifecycle React on any change/events in tasks, such as assignments, escalations, due date updates, or any custom actions Send notifications Track task or team performance Build an audit log on task events Enrich tasks with business data You can continue to use this task type on existing projects when you have a custom task application running on it and do not require any of the above features. ## Change the implementation type of user tasks We recommend you migrate process-by-process, allowing you to thoroughly test the processes in your test environments or via your [CI/CD](/components/hub/workspace/modeler/integrate-modeler-in-ci-cd.md). To do this, take the following steps: 1. Open a diagram you want to migrate. 2. Click on a user task. 3. Check if the task has an embedded form. - If a form is embedded, [transform it into a linked form](/components/modeler/bpmn/user-tasks/user-tasks.md#camunda-form-linked) before you change the task type implementation. Press `Ctrl+Z` or `⌘+Z` to undo if you accidentally removed your embedded form. 4. Open the **Implementation** section in the properties panel. 5. Click the **Type** dropdown and select **Camunda user task**. The linked form or external form reference will be preserved. Repeat these steps for all user tasks in the process. Then, deploy the process to your development cluster and test it by running the process and ensuring your custom task applications work. ## How Tasklist API (V1) compares to Orchestration Cluster REST API (V2) :::note The Tasklist REST API is [deprecated with the 8.8 release and will be deleted with the 8.10 release](/reference/announcements-release-notes/880/880-announcements.md#deprecated-operate-and-tasklist-v1-rest-apis). ::: The following table provides a breakdown of which operations are supported in which API, and for which user tasks. Operation Tasklist API Orchestration Cluster REST API Query tasks ✔ All types ✔ Camunda user tasks Get task ✔ All types ✔ Camunda user tasks Retrieve task variables ✔ All types ✔ Camunda user tasks Get task form ✔ All types ✔ Camunda user tasks Change task assignment ✔ Job worker-based tasks ✔ Camunda user tasks Complete task ✔ Job worker-based tasks ✔ Camunda user tasks Update task Not supported ✔ Camunda user tasks Safe and retrieve draft variables ✔ Job worker-based tasks Not supported The following table outlines the respective endpoints. Click the endpoints to follow to the API documentation and inspect the differences in the request and response objects. Operation Tasklist API Orchestration Cluster REST API Query user tasks POST /tasks/search POST /user-tasks/search Get user task GET /tasks/:taskId GET /user-tasks/:userTaskKey Retrieve task variables GET /variables/:variableId POST /tasks/:taskId/variables/search Get task form GET /forms/:formId GET /user-tasks/:userTaskKey/form Assign a task PATCH /tasks/:taskId/assign POST /user-tasks/:userTaskKey/assignment Unassign a task PATCH /tasks/:taskId/unassign DELETE /user-tasks/:userTaskKey/assignee Complete task PATCH /tasks/:taskId/complete POST /user-tasks/:userTaskKey/completion Update task Not supported PATCH /user-tasks/:userTaskKey Save and retrieve draft variables POST /tasks/:taskId/variables - ### Zeebe Java client Use the Zeebe Java client when you are building your task application in Java. The client assists with managing authentication and request/response objects. ### API differences Refer to the dedicated sections and API explorers to learn details about the APIs. ## Troubleshooting and common issues If your task application does not work properly after migration, check the following: - **The endpoints return specific error messages when you run them on the wrong task type**: Ensure to call the right endpoint for the right task type, c.f. above [table](#use-the-new-camunda-8-api). - **Forms do not appear**: Ensure you have extracted embedded forms, if any, and [transformed them into linked forms](/components/modeler/bpmn/user-tasks/user-tasks.md#camunda-form-linked), before you change the task type implementation. - **Task update operation does not work**: The update operation is only available to Camunda user tasks. --- ## SaaS orchestration architecture ## About Camunda 8.9 introduces a streamlined SaaS orchestration architecture. The runtime for Operate, Tasklist, Identity (Admin), and the Zeebe REST API is unified into a single orchestration service. Zeebe brokers continue to run separately and execute workflows as before. This is a topology change in SaaS only and does not affect Self-Managed deployments. What's new: - [Unified API domain for Orchestration Clusters](#unified-api-domain-for-orchestration-clusters). Legacy hostnames are deprecated but will remain available throughout 8.9 and are scheduled for removal in 8.10. - [Client credentials for new clusters use unified API URLs.](#client-credentials-and-legacy-hostnames) - [Cluster Metrics endpoint: `service` labels have changed on Orchestration Cluster metrics.](#service-label-changes) What didn't change: - The same UIs for Operate, Tasklist, and Admin/Identity and the REST API remain available as before. - The Zeebe gRPC endpoint is unchanged. ## Unified API domain for Orchestration Clusters Camunda 8.9 introduces a unified API domain for Orchestration Clusters. All services are now accessible under a single base URL: | Service | Unified URL (8.9) | | :------------------ | :------------------------------------------------------------ | | Base / REST API | `https://.api./` | | Operate UI | `https://.api.//operate` | | Tasklist UI | `https://.api.//tasklist` | | Admin (Identity) UI | `https://.api.//admin` | Legacy hostnames (`*.zeebe.`, `*.operate.`, `*.tasklist.`, and `*.identity.`) continue to work in 8.9 and are internally routed to the unified service, but are deprecated and scheduled for removal in 8.10. After the 8.10 release, only the Zeebe gRPC endpoint and the unified `*.api.*` endpoints will remain. ### Deprecation Legacy hostnames are deprecated as of 8.9 and will be removed in 8.10. Migrate any hard-coded URLs for Operate, Tasklist, or Identity to the new unified `*.api.*` URLs during the 8.9 lifecycle to ensure readiness before the 8.10 release. ## Client credentials and legacy hostnames Existing client credentials downloaded from Console for pre-8.9 clusters may reference legacy hostnames. Because those hostnames remain available in 8.9, existing credentials continue to work after a cluster is upgraded to 8.9. No immediate action is required. ### Recommended action For long-lived automation and CI/CD systems, Camunda recommends updating credentials to use the new unified `*.api.*` URLs and corresponding token audience values before 8.10. You have two options: - **Update existing credentials manually:** Edit the hostnames and token audience values in your existing credential configuration to reference the new unified URLs. For example: - **Legacy (pre-8.9):** - **Base URL:** `https://bru-2.operate.camunda.io/abc123-def456-ghi789` - **Audience:** `operate.camunda.io` - **New (from 8.9):** - **Base URL:** `https://bru-2.api.camunda.io/abc123-def456-ghi789/operate` - **Audience:** `api.camunda.io` - **Create new credentials:** Create a fresh set of client credentials in Camunda Console, which will be generated with the unified API URLs and correct audience values by default. ## Service label changes Breaking change Due to the streamlined orchestration architecture introduced in 8.9, metrics previously emitted by the individual Operate, Tasklist, and Identity services are now emitted by the unified orchestration service. As a result, the `service` label on affected metrics will change to reflect the new source service. This applies to Camunda 8 SaaS clusters using the [Cluster Metrics endpoint](/components/saas/monitoring/cluster-metrics-endpoint/set-up-cluster-metrics-endpoint.md). ### Impact Monitoring dashboards, alerting rules, or queries that filter or group by the `service` label on Orchestration Cluster metrics may stop matching expected values after upgrading to 8.9, as some metrics will now be emitted by a different source service. ### Action required After your cluster is upgraded to 8.9, review your dashboards and alerting rules and verify which `service` label values your metrics return. Update any Prometheus queries or alert definitions that no longer match. ## Upgrade behavior and expected downtime When a Camunda 8 SaaS Orchestration Cluster is upgraded from 8.8 to 8.9: 1. The existing Operate and Tasklist components are shut down. 2. The unified orchestration service starts in their place. 3. Endpoint routing is updated so legacy hostnames continue to resolve while the new `*.api.*` endpoints become active. ### Impact during upgrade - **Web UIs and REST API:** You should expect a short interruption while the old services are removed and the unified service becomes ready. This affects all services, but not the Zeebe brokers. - **Workflow execution (Zeebe brokers):** Designed to remain running throughout the upgrade. The primary impact is limited to UI and API availability, not to engine execution state. To upgrade, initiate the cluster upgrade to 8.9 from Camunda Console. No additional steps are required beyond the URL and credential recommendations above. --- ## Migrate from the removed Operate API :::warning The Operate API was removed in Camunda 8.10 and is no longer part of the current documentation set. ::: For the release-level summary of this removal, see the [8.10 release announcement](/reference/announcements-release-notes/8100/8100-announcements.md#removal-of-legacy-apis-tasklist-v1-dependent-features-and-zeebe-process-test). Use the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) for current integrations, and review [migrating to the Orchestration Cluster REST API](/apis-tools/migration-manuals/migrate-to-camunda-api.md) if you still have clients that call the removed Operate API. If you need legacy Operate API behavior as migration context, use the migration manuals in the current docs rather than building new integrations against the removed endpoints. --- ## Disable sharing This API allows users to disable the sharing functionality for all reports and dashboards in Optimize. Note that this setting will be permanently persisted in memory and will take precedence over any other previous configurations (e.g. configuration files). When sharing is disabled, previously shared URLs will no longer be accessible. Upon re-enabling sharing, the previously shared URLs will work once again under the same address as before. Calling this endpoint when sharing is already disabled will have no effect. ## Method & HTTP target resource POST `api/public/share/disable` ## Request headers The following request headers must be provided with every request: | Header | Constraints | Value | | -------------- | ----------- | ------------------------------------------------------- | | Authentication | REQUIRED | See [authentication](../optimize-api-authentication.md) | ## Query parameters No query parameters necessary. ## Request body An empty request body should be sent. ## Response codes Possible HTTP Response Status codes: | Code | Description | | ---- | --------------------------------------------------------------------------------------------------------------------------- | | 204 | Request successful. | | 401 | Secret incorrect or missing in HTTP Header. See [authentication](../optimize-api-authentication.md) on how to authenticate. | | 500 | Some error occurred while processing the request, best check the Optimize log. | ## Example ### Disable sharing POST `api/public/share/disable` #### Request header `Authorization: Bearer mySecret` #### Response Status 204 (Successful) #### Response content ``` no content ``` --- ## Enable sharing This API allows users to enable the sharing functionality for all reports and dashboards in Optimize. Note that this setting will be permanently persisted in memory and will take precedence over any other previous configurations (e.g. configuration files). If sharing had been previously enabled and then disabled, re-enabling sharing will allow users to access previously shared URLs under the same address as before. Calling this endpoint when sharing is already enabled will have no effect. ## Method & HTTP target resource POST `api/public/share/enable` ## Request headers The following request headers must be provided with every request: | Header | Constraints | Value | | -------------- | ----------- | ------------------------------------------------------- | | Authentication | REQUIRED | See [authentication](../optimize-api-authentication.md) | ## Query parameters No query parameters necessary. ## Request body An empty request body should be sent. ## Response codes Possible HTTP Response Status codes: | Code | Description | | ---- | --------------------------------------------------------------------------------------------------------------------------- | | 204 | Request successful. | | 401 | Secret incorrect or missing in HTTP Header. See [authentication](../optimize-api-authentication.md) on how to authenticate. | | 500 | Some error occurred while processing the request, best check the Optimize log. | ## Example ### Enable sharing POST `api/public/share/enable` #### Request header `Authorization: Bearer mySecret` #### Response Status 204 (Successful) #### Response content ``` no content ``` --- ## Delete dashboards The dashboards deletion API allows you to delete dashboards by ID from Optimize. :::note Heads up! The deletion of a dashboard does not affect the referenced reports. ::: ## Method & HTTP target resource DELETE `/api/public/dashboard/{dashboard-ID}` Where `dashboard-ID` is the ID of the dashboard you wish to delete. ## Request headers The following request headers have to be provided with every delete request: | Header | Constraints | Value | | -------------- | ----------- | ------------------------------------------------------- | | Authentication | REQUIRED | See [authentication](../optimize-api-authentication.md) | ## Query parameters No query parameters available. ## Request body No request body is required. ## Result No response body. ## Response codes Possible HTTP Response status codes: | Code | Description | | ---- | --------------------------------------------------------------------------------------------------------------------------- | | 204 | Request successful. | | 401 | Secret incorrect or missing in HTTP Header. See [authentication](../optimize-api-authentication.md) on how to authenticate. | | 404 | The requested dashboard was not found, please check the provided dashboard-ID. | | 500 | Some error occurred while processing the request, best check the Optimize log. | ## Example ### Delete a dashboard Let's assume you want to delete a dashboard with the ID `e6c5abb1-6a18-44e7-8480-d562d511ba62`, this is what it would look like: DELETE `/api/public/dashboard/e6c5aaa1-6a18-44e7-8480-d562d511ba62` #### Request header `Authorization: Bearer mySecret` #### Response Status 204. --- ## Export dashboard definitions This API allows users to export dashboard definitions which can later be imported into another Optimize system. Note that exporting a dashboard also exports all reports contained within the dashboard. The dashboards to be exported may be within a Collection or private entities, the API has access to both. The obtained list of entity exports can be imported into other Optimize systems either using the dedicated [import API](../import-entities.md) or [via UI](components/optimize/userguide/additional-features/export-import.md#importing-entities). ## Method & HTTP target resource POST `/api/public/export/dashboard/definition/json` ## Request headers The following request headers have to be provided with every request: | Header | Constraints | Value | | -------------- | ----------- | --------------------------------------------------- | | Authentication | REQUIRED | [Authentication](../optimize-api-authentication.md) | ## Query parameters No query parameters available. ## Request body The request body should contain a JSON array of dashboard IDs to be exported. ## Result The response contains a list of exported dashboard definitions as well as all report definitions contained within the dashboards. ## Response codes Possible HTTP response status codes: | Code | Description | | ---- | --------------------------------------------------------------------------------------------------------------------------- | | 204 | Request successful. | | 401 | Secret incorrect or missing in HTTP Header. See [authentication](../optimize-api-authentication.md) on how to authenticate. | | 404 | At least one of the given dashboard IDs does not exist. | | 500 | Some error occurred while processing the request, best check the Optimize log. | ## Example ### Export two dashboards Assuming you want to export the two dashboards with IDs `123` and `456` and have configured the accessToken `mySecret`, this is what it would look like: POST `/api/public/export/dashboard/definition/json` #### Request header `Authorization: Bearer mySecret` #### Request body ``` [ "123", "456" ] ``` #### Response Status 200. #### Response content The response contains the two exported dashboard definitions as well as all three process reports contained within the two dashboards. ``` [ { "id": "61ae2232-51e1-4c35-b72c-c7152ba264f9", "exportEntityType": "single_process_report", "name": "Number: Process instance duration", "description": "This report shows the average instance duration", "sourceIndexVersion": 11, "collectionId": null, "data": {...} }, { "id": "625c2411-b95f-4442-936b-1976b9511d4a", "exportEntityType": "single_process_report", "name": "Heatmap: Flownode count", "description": "This report shows a heatmap of the number of instances", "sourceIndexVersion": 11, "collectionId": null, "data": {...} }, { "id": "94a7252e-d5c3-45ea-9906-75271cc0cac2", "exportEntityType": "single_process_report", "name": "Data Table: User task count", "description": "This report shows number of user tasks", "sourceIndexVersion": 11, "collectionId": null, "data": {...} }, { "id": "123", "exportEntityType": "dashboard", "name": "Dashboard 1", "description": "A dashboard showing possible automation candidates", "sourceIndexVersion": 8, "reports": [ { "id": "61ae2232-51e1-4c35-b72c-c7152ba264f9", ... }, { "id": "625c2411-b95f-4442-936b-1976b9511d4a", ... } ], "availableFilters": [...], "collectionId": null }, { "id": "456", "exportEntityType": "dashboard", "name": "Dashboard 2", "description": "A dashboard showing user task data", "sourceIndexVersion": 8, "reports": [ { "id": "94a7252e-d5c3-45ea-9906-75271cc0cac2", ... } ], "availableFilters": [...], "collectionId": null } ] ``` --- ## Get dashboard IDs This API allows users to retrieve all dashboard IDs from a given collection. ## Method & HTTP target resource GET `/api/public/dashboard` ## Request headers The following request headers have to be provided with every request: | Header | Constraints | Value | | -------------- | ----------- | --------------------------------------------------- | | Authentication | REQUIRED | [Authentication](../optimize-api-authentication.md) | ## Query parameters The following query parameters have to be provided with every request: | Parameter | Constraints | Value | | ------------ | ----------- | ----------------------------------------------------------------- | | collectionId | REQUIRED | The ID of the collection for which to retrieve the dashboard IDs. | ## Request body No request body is required. ## Result The response contains a list of IDs of the dashboards existing in the collection with the given collection ID. ## Response codes Possible HTTP response status codes: | Code | Description | | ---- | --------------------------------------------------------------------------------------------------------------------------- | | 200 | Request successful. | | 401 | Secret incorrect or missing in HTTP Header. See [authentication](../optimize-api-authentication.md) on how to authenticate. | | 500 | Some error occurred while processing the request, best check the Optimize log. | ## Example ### Retrieve all dashboard IDs from a collection Assuming you want to retrieve all dashboard IDs in the collection with the ID `1234` and have configured the accessToken `mySecret`, this is what it would look like: GET `/api/public/dashboard?collectionId=1234` #### Request header `Authorization: Bearer mySecret` #### Response Status 200. #### Response content ``` [ { "id": "9b0eb845-e8ed-4824-bd85-8cd69038f2f5" }, { "id": "1a866c7c-563e-4f6b-adf1-c4648531f7d4" } ] ``` --- ## External variable ingestion With the external variable ingestion API, variable data held in external systems can be ingested into Optimize directly, without the need for these variables to be present in your Camunda platform data. This can be useful when external business data, which is relevant for process analysis in Optimize, is to be associated with specific process instances. Especially if this data changes over time, it is advisable to use this REST API to persist external variable updates to Optimize, as otherwise Optimize may not be aware of data changes in the external system. ## Functionality The external variable ingestion API allows users to ingest batches of variable data which Optimize stores in a dedicated index. All variable data includes a reference to the process instance each variable belongs to, this reference then enables Optimize to import external variable data from the dedicated index to their respective process instances at regular intervals. Once Optimize has updated the process instance data, the external variables are available for report evaluations in Optimize. ## Limitations Note that external variables should be treated as separate from engine variables. If you ingest variables that are already present in the engine, engine imports may override the ingested data and vice versa, leading to unreliable report results. Similarly, if the same ingested batch contains variables with duplicate IDs, you may experience unexpected report results because Optimize will assume only one of the updates per ID and batch to be the most up to date one. Additionally, ensure the reference information (process instance ID and process definition key) is accurate, as otherwise Optimize will not be able to correctly associate variables with instance data and may create new instance indices, resulting in data which will not be usable in reports. External variables can only be ingested for process instances and will not be affected by any configured variable plugin. ## Configuration Refer to the [configuration section](../../self-managed/components/optimize/configuration/system-configuration.md) to learn more about how to set up external variable ingestion. ## Method & HTTP target resource POST `/api/ingestion/variable` ## Request headers The following request headers have to be provided with every variable ingestion request: | Header | Constraints | Value | | -------------- | ----------- | ----------------------------------------------------- | | Authentication | REQUIRED\* | See [authentication](../optimize-api-authentication). | | Content-Type | REQUIRED | `application/json` | - Only required if not set as a query parameter ## Query parameters The following query parameters have to be provided with every delete request: | Parameter | Constraints | Value | | ------------ | ----------- | ---------------------------------------------------- | | access_token | REQUIRED\* | See [authentication](../optimize-api-authentication) | - Only required if not set as a request header ## Request body The request body contains an array of variable JSON Objects: | Name | Type | Constraints | Description | | -------------------- | ------ | ----------- | ------------------------------------------------------------------------------------------------- | | id | String | REQUIRED | The unique identifier of this variable. | | name | String | REQUIRED | The name of the variable. | | type | String | REQUIRED | The type of the variable. Must be one of: String, Short, Long, Double, Integer, Boolean, or Date. | | value | String | REQUIRED | The current value of the variable. | | processInstanceId | String | REQUIRED | The ID of the process instance this variable is to be associated with. | | processDefinitionKey | String | REQUIRED | The definition key of the process instance this variable is to be associated with. | ## Result This method returns no content. ## Response codes Possible HTTP response status codes: | Code | Description | | ---- | ------------------------------------------------------------------------------------------------------ | | 204 | Request successful. | | 400 | Returned if some properties in the request body are invalid or missing. | | 401 | Secret incorrect or missing. See [authentication](../optimize-api-authentication) on how to authorize. | ## Example ### Request POST `/api/ingestion/variable` Request Body: ``` [ { "id": "7689fced-2639-4408-9de1-cf8f72769f43", "name": "address", "type": "string", "value": "Main Street 1", "processInstanceId": "c6393461-02bb-4f62-a4b7-f2f8d9bbbac1", "processDefinitionKey": "shippingProcess" }, { "id": "993f4e73-7f6a-46a6-bd45-f4f8e3470ba1", "name": "amount", "type": "integer", "value": "500", "processInstanceId": "8282ed49-2243-44df-be5e-1bf893755d8f", "processDefinitionKey": "orderProcess" } ] ``` ### Response Status 204. --- ## Health readiness The purpose of Health-Readiness REST API is to return information indicating whether Optimize is ready to be used. :::note The Health-Readiness REST API does not require an [`Authorization` header](./optimize-api-authentication.md), and rejects requests that include one. ::: ## Method & HTTP target resource GET `/api/readyz` ## Response The response is an empty body with the status code indicating the readiness of Optimize. The following responses are available: - `200`: This indicates that Optimize is ready to use. It is connected to both Elasticsearch and at least one of its configured engines. - `503`: This indicates that Optimize is not ready to use. It cannot connect to either Elasticsearch or any of its configured engines. --- ## Import entities This API allows users to import entity definitions such as reports and dashboards into existing collections. These entity definitions may be obtained either using the [report](../report/export-report-definitions/) or [dashboard](../dashboard/export-dashboard-definitions) export API or [via the UI](components/optimize/userguide/additional-features/export-import.md#exporting-entities). ## Prerequisites For importing via API, the following prerequisites must be met: - All definitions the entities require exist in the target Optimize. - The target collection, identified using the `collectionId` query parameter, must exist in the target system. - The collection data sources must include all relevant definitions for the entities. - The entity data structures match. To ensure matching data structures, confirm that the Optimize version of the source is the same as the version of the target Optimize. If any of the above conditions are not met, the import will fail with an error response; refer to the error message in the response for more information. ## Method & HTTP target resource POST `/api/public/import` ## Request headers The following request headers have to be provided with every request: | Header | Constraints | Value | | -------------- | ----------- | -------------------------------------------------- | | Authentication | REQUIRED | [Authentication](./optimize-api-authentication.md) | ## Query parameters The following query parameters have to be provided with every request: | Parameter | Constraints | Value | | ------------ | ----------- | -------------------------------------------------------------- | | collectionId | REQUIRED | The ID of the collection for which to retrieve the report IDs. | ## Request body The request body should contain a JSON array of entity definitions to be imported. These entity definitions may be obtained by using the [report](../report/export-report-definitions) or [dashboard](../dashboard/export-dashboard-definitions) export APIs or by [manually exporting entities](components/optimize/userguide/additional-features/export-import.md#exporting-entities) via the Optimize UI. ## Result The response contains a list of DTOs that specify the ID and entity type (`report` or `dashboard`) of each newly created entity in the target system. ## Response codes Possible HTTP response status codes: | Code | Description | | ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 200 | Request successful. | | 400 | The provided list of entities is invalid. This can occur if any of the above listed [prerequisites](#prerequisites) are not met. Check the `detailedMessage` of the error response for more information. | | 401 | Secret incorrect or missing in HTTP header. See [authentication](./optimize-api-authentication.md) on how to authenticate. | | 404 | The given target collection ID does not exist. | | 500 | Some error occurred while processing the request, best check the Optimize log. | ## Example ### Import two entities Assuming you want to import a report and a dashboard into the collection with ID `123`, this is what it would look like: POST `/api/public/import?collectionId=123` #### Request header `Authorization: Bearer mySecret` #### Request body ``` [ { "id": "61ae2232-51e1-4c35-b72c-c7152ba264f9", "exportEntityType": "single_process_report", "name": "Number: Process instance duration", "description": "This report shows the average instance duration", "sourceIndexVersion": 11, "collectionId": null, "data": {...} }, { "id": "b0eb845-e8ed-4824-bd85-8cd69038f2f5", "exportEntityType": "dashboard", "name": "Dashboard 1", "description": "This dashboard displays reports relating to process durations", "sourceIndexVersion": 8, "reports": [ { "id": "61ae2232-51e1-4c35-b72c-c7152ba264f9", ... } ], "availableFilters": [...], "collectionId": null } ] ``` #### Response Status 200. #### Response Content ``` [ { "id": "e8ca18b9-e637-45c8-87da-0a2b08b34d6e", "entityType": "dashboard" }, { "id": "290b3425-ba33-4fbb-b20b-a4f236036847", "entityType": "report" } ] ``` --- ## Authentication(Optimize-api) All Optimize API requests except [the health readiness](./health-readiness.md) endpoint require authentication. To authenticate, generate a [JSON Web Token (JWT)](https://jwt.io/introduction/) and include it in each request. ## Generate a token 1. [Create client credentials](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client) in the **Clusters > Cluster name > API** tab of [Camunda Console](https://console.camunda.io/). 2. Add permissions to this client for **Optimize**. 3. Once you have created the client, capture the following values required to generate a token: | Name | Environment variable name | Default value | | ------------------------ | -------------------------------- | -------------------------------------------- | | Client ID | `ZEEBE_CLIENT_ID` | - | | Client Secret | `ZEEBE_CLIENT_SECRET` | - | | Authorization Server URL | `ZEEBE_AUTHORIZATION_SERVER_URL` | `https://login.cloud.camunda.io/oauth/token` | | Optimize REST Address | `CAMUNDA_OPTIMIZE_BASE_URL` | - | :::caution When client credentials are created, the `Client Secret` is only shown once. Save this `Client Secret` somewhere safe. ::: 4. Execute an authentication request to the token issuer: ```bash curl --request POST ${ZEEBE_AUTHORIZATION_SERVER_URL} \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'grant_type=client_credentials' \ --data-urlencode 'audience=optimize.camunda.io' \ --data-urlencode "client_id=${ZEEBE_CLIENT_ID}" \ --data-urlencode "client_secret=${ZEEBE_CLIENT_SECRET}" ``` A successful authentication response looks like the following: ```json { "access_token": "", "expires_in": 300, "refresh_expires_in": 0, "token_type": "Bearer", "not-before-policy": 0 } ``` 5. Capture the value of the `access_token` property and store it as your token. 1. [Configure the `api.audience` setting](/self-managed/components/optimize/configuration/system-configuration.md#public-api) in your Optimize installation to match the audience property of the **Optimize API** in [Management Identity](/self-managed/components/management-identity/access-management/access-management-overview.md). 2. [Add an M2M application in Management Identity](/self-managed/components/management-identity/application-user-group-role-management/applications.md). 3. [Add permissions to this application](/self-managed/components/management-identity/application-user-group-role-management/applications.md) for **Optimize API**. 4. Capture the `Client ID` and `Client Secret` from the application in Management Identity. 5. [Generate a token](/self-managed/components/management-identity/authentication.md) to access the Optimize REST API. Provide the `client_id` and `client_secret` from the values you previously captured in Management Identity. ```shell curl --location --request POST 'http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode "client_id=${CLIENT_ID}" \ --data-urlencode "client_secret=${CLIENT_SECRET}" \ --data-urlencode 'grant_type=client_credentials' ``` A successful authentication response looks like the following: ```json { "access_token": "", "expires_in": 300, "refresh_expires_in": 0, "token_type": "Bearer", "not-before-policy": 0 } ``` 6. Capture the value of the `access_token` property and store it as your token. :::note The Optimize API can also be configured in a Self-Managed environment to authenticate using a single shared access token. See [External API Configuration](/self-managed/components/optimize/configuration/system-configuration.md#external-api) for the configuration required to access the public API using a specific token. ::: ## Use a token Include the previously captured token as an authorization header in each request: `Authorization: Bearer `. For example, to send a request to the Optimize API's ["Get dashboard IDs"](./dashboard/get-dashboard-ids.md) endpoint: :::tip The `${CAMUNDA_TASKLIST_BASE_URL}` variable below represents the URL of the Optimize API. You can capture this URL when creating an API client. You can also construct it as `https://${REGION}.optimize.camunda.io/${CLUSTER_ID}`. ::: :::tip The `${CAMUNDA_OPTIMIZE_BASE_URL}` variable below represents the URL of the Optimize API. You can configure this value in your Self-Managed installation. The default value is `http://localhost:8083`. ::: ```shell curl --header "Authorization: Bearer ${TOKEN}" \ -G --data-urlencode "collectionId=${COLLECTION_ID}" \ ${CAMUNDA_OPTIMIZE_BASE_URL}/api/public/dashboard ``` A successful response includes [dashboard IDs](./dashboard/get-dashboard-ids.md). For example: ```json [ { "id": "11111111-1111-1111-1111-111111111111" }, { "id": "22222222-2222-2222-2222-222222222222" } ] ``` ## Token expiration Access tokens expire according to the `expires_in` property of a successful authentication response. After this duration, in seconds, you must request a new access token. --- ## Optimize API ## About You can use the Optimize API to: - Retrieve, create, update, and delete reports and dashboards - Export dashboards and reports for sharing or backup - Enable or disable sharing links ## Authentication All Optimize API requests, except [the health readiness](./health-readiness.md) endpoint, require authentication. To authenticate, generate a [JSON Web Token (JWT)](https://jwt.io/introduction/) and include it in each request. For more details, see the [Authentication](./optimize-api-authentication.md) section. ## API Postman collection To get started quickly, consider using the [Postman collection](https://www.postman.com/camundateam/workspace/camunda-8-postman/collection/24684262-a1103c05-7ed8-4fd4-8716-9005583ce23a?action=share&creator=11465105). ## Usage notes Deleting a file, folder, or project via the API is immediate and cannot be undone. Use caution. ## Further resources - [Authentication](./optimize-api-authentication.md) - [Camunda Optimize documentation](/components/optimize/what-is-optimize.md) - [Postman collection](https://www.postman.com/camundateam/workspace/camunda-8-postman/collection/24684262-a1103c05-7ed8-4fd4-8716-9005583ce23a?action=share&creator=11465105) --- ## Delete reports The report deletion API allows you to delete reports by ID from Optimize. :::note Heads up! During deletion a report will get removed from any dashboard or combined process report it is referenced by. In case a report is referenced by an alert, the corresponding alert will get deleted too. ::: ## Method & HTTP target resource DELETE `/api/public/report/{report-ID}` Where `report-ID` is the ID of the report you wish to delete. ## Request headers The following request headers have to be provided with every delete request: | Header | Constraints | Value | | -------------- | ----------- | ------------------------------------------------------- | | Authentication | REQUIRED | See [authentication](../optimize-api-authentication.md) | ## Query parameters No query parameters available. ## Request body No request body is required. ## Result No response body. ## Response codes Possible HTTP response status codes: | Code | Description | | ---- | --------------------------------------------------------------------------------------------------------------------------- | | 204 | Request successful. | | 401 | Secret incorrect or missing in HTTP Header. See [authentication](../optimize-api-authentication.md) on how to authenticate. | | 404 | The requested report was not found, please check the provided report-ID. | | 500 | Some error occurred while processing the request, best check the Optimize log. | ## Example ### Delete a report Let's assume you want to delete a report with the ID `e6c5abb1-6a18-44e7-8480-d562d511ba62`, this is what it would look like: DELETE `/api/public/report/e6c5aaa1-6a18-44e7-8480-d562d511ba62` #### Request header `Authorization: Bearer mySecret` #### Response Status 204. --- ## Export report definitions This API allows users to export report definitions which can later be imported into another Optimize system. The reports to be exported may be within a collection or private entities, the API has access to both. The obtained list of entity exports can be imported into other Optimize systems either using the dedicated [import API](../import-entities.md) or [via UI](components/optimize/userguide/additional-features/export-import.md#importing-entities). ## Method & HTTP target resource POST `/api/public/export/report/definition/json` ## Request headers The following request headers have to be provided with every request: | Header | Constraints | Value | | -------------- | ----------- | --------------------------------------------------- | | Authentication | REQUIRED | [Authentication](../optimize-api-authentication.md) | ## Query parameters No query parameters available. ## Request body The request body should contain a JSON array of report IDs to be exported. ## Result The response contains a list of exported report definitions. ## Response codes Possible HTTP response status codes: | Code | Description | | ---- | --------------------------------------------------------------------------------------------------------------------------- | | 204 | Request successful. | | 401 | Secret incorrect or missing in HTTP Header. See [authentication](../optimize-api-authentication.md) on how to authenticate. | | 404 | At least one of the given report IDs does not exist. | | 500 | Some error occurred while processing the request, best check the Optimize log. | ## Example ### Export two reports Assuming you want to export the two reports with IDs `123` and `456` and have configured the accessToken `mySecret`, this is what it would look like: POST `/api/public/export/report/definition/json` #### Request header `Authorization: Bearer mySecret` #### Request body ``` [ "123", "456" ] ``` #### Response Status 200. #### Response content ``` [ { "id": "123", "exportEntityType": "single_process_report", "name": "Number: Process instance duration", "sourceIndexVersion": 8, "collectionId": "40cb3657-bdcb-459d-93ce-06877ac7244a", "data": {...} }, { "id": "456", "exportEntityType": "single_process_report", "name": "Heatmap: Flownode count", "sourceIndexVersion": 8, "collectionId": "40cb3657-bdcb-459d-93ce-06877ac7244a", "data": {...} } ] ``` --- ## Export report result data The data export API allows users to export large amounts of data in a machine-readable format (JSON) from Optimize. ## Functionality Users can export all report types (except combined process reports) from `Optimize` using the Data Export API. Moreover, raw data reports will include additional data relating to the executed flow nodes and can be exported in a paginated fashion, so that large amounts of data can be consumed in chunks by the client. ### Pagination The simplest way to paginate through the results is to perform a search request with all the `REQUIRED` header/query parameters as described in the sections below (but without `searchRequestId`), then pass the `searchRequestId` returned in each response to the next request, until no more documents are returned. Note that it's often the case, but not guaranteed, that the `searchRequestId` remains stable through the entire pagination, so always use the `searchRequestId` from the most current response to make your next request. ## Method & HTTP target resource GET `/api/public/export/report/{report-ID}/result/json` Where `report-ID` is the ID of the report you wish to export. ## Request headers The following request headers have to be provided with every data export request: | Header | Constraints | Value | | -------------- | ----------- | --------------------------------------------------- | | Authentication | REQUIRED | [Authentication](../optimize-api-authentication.md) | ## Query parameters The following query parameters have to be provided with every data export request: | Parameter | Constraints | Value | | ----------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | limit | REQUIRED | Maximum number of records per page. Please note that the limit will only be considered when performing the request for the first page of a raw data report. The following requests for a given searchRequestId will have the same page size as the first request. | | paginationTimeout | REQUIRED | The amount of time (in seconds) for which a search context will be held in memory, so that the remaining pages of the result can be retrieved. For more information on how to paginate through the results, please refer to the section [Pagination](#pagination). | | searchRequestId | Optional | The ID of a previous search for which you wish to retrieve the next page of results. For more information on how to get and use a searchRequestId please refer to the section [Pagination](#pagination). | ## Request body No request body is required. ## Result | Content | Value | | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | searchRequestId | The ID of the performed search. The following pages from this search can be retrieved by using this ID. For more information please refer to the section [Pagination](#pagination). | | numberOfRecordsInResponse | Number of records in the JSON Response. This is a number between [0, limit] | | totalNumberOfRecords | The total number of records (from all pages) for this report export | | reportId | The ID of the exported report | | message | In case there is additional information relevant to this request, this field will contain a message describing it. The response will only contain this field if there is a message to be shown | | data [Array] | An array containing numberOfRecordsInResponse report data records in JSON Format | ## Response codes Possible HTTP response status codes: | Code | Description | | ---- | --------------------------------------------------------------------------------------------------------------------------- | | 200 | Request successful. | | 400 | Returned if some of the properties from the request are invalid or missing. | | 401 | Secret incorrect or missing in HTTP Header. See [authentication](../optimize-api-authentication.md) on how to authenticate. | | 404 | The requested report was not found, please check the provided report-ID. | | 500 | Some error occurred while processing the export request, best check the Optimize log. | ## Example ### Export a raw data report Let's assume you want to export a report with the ID `e6c5abb1-6a18-44e7-8480-d562d511ba62`, with a maximum of two records per page, an access token `mySecret` and a pagination timeout of 60s, this is what it would look like #### Initial API call GET `/api/public/export/report/e6c5aaa1-6a18-44e7-8480-d562d511ba62/result/json? paginationTimeout=60&limit=2` ##### Request header `Authorization: Bearer mySecret` ##### Response content ``` { "searchRequestId": "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ", "numberOfRecordsInResponse": 2, "totalNumberOfRecords": 11, "reportId": "e6c5abb1-6a18-44e7-8480-d562d511ba62", "data": [ { "processDefinitionKey": "aProcess", "processDefinitionId": "aProcess:1:1801", "processInstanceId": "1809", "businessKey": "aBusinessKey", "startDate": "2021-12-02T17:21:49.330+0200", "endDate": "2021-12-02T17:21:49.330+0200", "duration": 0, "engineName": "camunda-bpm", "tenantId": null, "variables": {} }, { "processDefinitionKey": "aProcess", "processDefinitionId": "aProcess:1:1801", "processInstanceId": "1804", "businessKey": "aBusinessKey", "startDate": "2021-12-02T17:21:49.297+0200", "endDate": "2021-12-02T17:21:49.298+0200", "duration": 1, "engineName": "camunda-bpm", "tenantId": null, "variables": {} } ] } ``` ##### Response Status 200. #### Subsequent API calls Note here the use of the query parameter `searchRequestId` to retrieve further pages from the initial search. `GET /api/public/export/report/e6c5aaa1-6a18-44e7-8480-d562d511ba62/result/json?paginationTimeout=60&searchRequestId=FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ&limit=2` ##### Request header `Authorization: Bearer mySecret` ##### Response content ``` { "searchRequestId": "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ", "numberOfRecordsInResponse": 2, "totalNumberOfRecords": 11, "reportId": "e6c5abb1-6a18-44e7-8480-d562d511ba62", "data": [ { "processDefinitionKey": "aProcess", "processDefinitionId": "aProcess:1:1bc9474d-5762-11ec-8b2c-0242ac120003", "processInstanceId": "1bdafab8-5762-11ec-8b2c-0242ac120003", "businessKey": "aBusinessKey", "startDate": "2021-12-07T15:32:22.739+0200", "endDate": "2021-12-07T15:32:22.740+0200", "duration": 1, "engineName": "camunda-bpm", "tenantId": null, "variables": {} }, { "processDefinitionKey": "aProcess", "processDefinitionId": "aProcess:1:1bc9474d-5762-11ec-8b2c-0242ac120003", "processInstanceId": "1bda3763-5762-11ec-8b2c-0242ac120003", "businessKey": "aBusinessKey", "startDate": "2021-12-07T15:32:22.735+0200", "endDate": "2021-12-07T15:32:22.735+0200", "duration": 0, "engineName": "camunda-bpm", "tenantId": null, "variables": {} } ] } ``` ##### Response Status 200. --- ## Get report IDs This API allows users to retrieve all report IDs from a given collection. ## Method & HTTP target resource GET `/api/public/report` ## Request headers The following request headers have to be provided with every request: | Header | Constraints | Value | | -------------- | ----------- | --------------------------------------------------- | | Authentication | REQUIRED | [Authentication](../optimize-api-authentication.md) | ## Query parameters The following query parameters have to be provided with every request: | Parameter | Constraints | Value | | ------------ | ----------- | -------------------------------------------------------------- | | collectionId | REQUIRED | The ID of the Collection for which to retrieve the report IDs. | ## Request body No request body is required. ## Result The response contains a list of IDs of the reports existing in the collection with the given collection ID. ## Response codes Possible HTTP response status codes: | Code | Description | | ---- | --------------------------------------------------------------------------------------------------------------------------- | | 200 | Request successful. | | 401 | Secret incorrect or missing in HTTP Header. See [authentication](../optimize-api-authentication.md) on how to authenticate. | | 500 | Some error occurred while processing the request, best check the Optimize log. | ## Example ### Retrieve all report IDs from a collection Assuming you want to retrieve all report IDs in the collection with the ID `1234` and have configured the accessToken `mySecret`, this is what it would look like: GET `/api/public/report?collectionId=1234` #### Request header `Authorization: Bearer mySecret` ##### Response Status 200. ##### Response content ``` [ { "id": "9b0eb845-e8ed-4824-bd85-8cd69038f2f5" }, { "id": "1a866c7c-563e-4f6b-adf1-c4648531f7d4" } ] ``` --- ## Tutorial(Optimize-api) In this tutorial, we'll step through examples to highlight the capabilities of the Optimize API, such as listing your existing dashboard IDs, or deleting a dashboard. ## Prerequisites - If you haven't done so already, [create a cluster](/components/hub/organization/manage-clusters/create-cluster.md). - Upon cluster creation, [create your first client](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client). Ensure you check the `Optimize` client scope box. :::note Make sure you keep the generated client credentials in a safe place. The **Client secret** will not be shown again. For your convenience, you can also download the client information to your computer. ::: - In this tutorial, we utilize a JavaScript-written [GitHub repository](https://github.com/camunda/camunda-api-tutorials) to write and run requests. Clone this repo before getting started. - Ensure you have [Node.js](https://nodejs.org/en/download) installed as this will be used for methods that can be called by the CLI (outlined later in this guide). Run `npm install` to ensure you have updated dependencies. ## Getting started - You need authentication to access the API endpoints. Find more information [here](./optimize-api-authentication.md). - To properly execute the commands to list existing dashboard IDs and delete a dashboard, ensure you have [created a collection](/components/optimize/userguide/collections-dashboards-reports.md) containing a [dashboard](/components/optimize/userguide/creating-dashboards.md). ## Set up authentication If you're interested in how we use a library to handle auth for our code, or to get started, examine the `auth.js` file in the GitHub repository. This file contains a function named `getAccessToken` which executes an OAuth 2.0 protocol to retrieve authentication credentials based on your client ID and client secret. Then, we return the actual token that can be passed as an authorization header in each request. To set up your credentials, create an `.env` file which will be protected by the `.gitignore` file. You will need to add your `OPTIMIZE_CLIENT_ID`, `OPTIMIZE_CLIENT_SECRET`, `OPTIMIZE_BASE_URL`, and `OPTIMIZE_AUDIENCE`, which is `optimize.camunda.io` in a Camunda 8 SaaS environment. For example, your audience may be defined as `OPTIMIZE_AUDIENCE=optimize.camunda.io`. These keys will be consumed by the `auth.js` file to execute the OAuth protocol, and should be saved when you generate your client credentials in [prerequisites](#prerequisites). :::tip Can't find your environment variables? When you create new client credentials as a [prerequisite](#prerequisites), your environment variables appear in a pop-up window. Your environment variables may appear as `CAMUNDA_CLIENT_ID`, `CAMUNDA_CLIENT_SECRET`, and `CAMUNDA_OPTIMIZE_BASE_URL`. ::: Examine the existing `.env.example` file for an example of how your `.env` file should look upon completion. Do not place your credentials in the `.env.example` file, as this example file is not protected by the `.gitignore`. :::note In this tutorial, we will execute arguments to list existing dashboard IDs and delete a dashboard. You can examine the framework for processing these arguments in the `cli.js` file before getting started. ::: ## GET a list of existing dashboard IDs First, let's script an API call to list our existing dashboard IDs. To do this, take the following steps: 1. In the file named `optimize.js`, outline the authentication and authorization configuration in the first few lines. This will pull in your `.env` variables to obtain an access token before making any API calls: ```javascript const authorizationConfiguration = { clientId: process.env.OPTIMIZE_CLIENT_ID, clientSecret: process.env.OPTIMIZE_CLIENT_SECRET, audience: process.env.OPTIMIZE_AUDIENCE, }; ``` 2. Examine the function `async function listDashboards([collectionId])` below this configuration. This is where you will script out your API call. 3. Within the function, you must first apply an access token for this request, so your function should now look like the following: ```javascript async function listDashboards([collectionId]) { const accessToken = await getAccessToken(authorizationConfiguration); } ``` 4. Using your generated client credentials from [prerequisites](#prerequisites), capture your Optimize API URL beneath your call for an access token by defining `optimizeApiUrl`: `const optimizeApiUrl = process.env.OPTIMIZE_BASE_URL;` 5. On the next line, script the API endpoint to list your existing dashboard IDs for a particular collection: ```javascript const url = `${optimizeApiUrl}/api/public/dashboard?collectionId=${collectionId}`; ``` 6. Configure your GET request to the appropriate endpoint, including an authorization header based on the previously acquired `accessToken`: ```javascript const options = { method: "GET", url, headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, }, }; ``` 7. Call the collection's endpoint, process the results from the API call, emit the dashboard IDs to output, and emit an error message from the server if necessary: ```javascript try { const response = await axios(options); const results = response.data; results.forEach((x) => console.log(`ID: ${x.id}`)); } catch (error) { // Emit an error from the server. console.error(error.message); } ``` 8. In your terminal, run `node cli.js optimize list `, where `` is where you can paste the ID of your collection for a list of your existing dashboard IDs within this particular collection. If you have any existing dashboards within a collection, you will see an output similar to the following: `ID: 12345` :::note This `list` command is connected to the `listDashboards` function at the bottom of the `optimize.js` file, and executed by the `cli.js` file. While we will view dashboard IDs and delete a dashboard in this tutorial, you may add additional arguments depending on the API calls you would like to make. ::: If you have any existing dashboards, the `ID: ${x.id}` will now output. If you have an invalid API name or action name, or no arguments provided, or improper/insufficient credentials configured, an error message will output as outlined in the `cli.js` file. ## DELETE a dashboard To delete a dashboard, capture its ID from the previous exercise and take the following steps: 1. Outline your function, similar to the steps above. Note that the URL endpoint will look different, as you are accessing a different endpoint in this request (using a dashboard ID) than in the prior request (using a collection ID): ```javascript async function deleteDashboard([dashboardId]) { console.log(`deleting dashboard ${dashboardId}`); const accessToken = await getAccessToken(authorizationConfiguration); const optimizeApiUrl = process.env.OPTIMIZE_BASE_URL; const url = `${optimizeApiUrl}/api/public/dashboard/${dashboardId}`; } ``` 2. Configure the API call using the DELETE method: ```javascript const options = { method: "DELETE", url, headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, }, }; ``` 3. Process the results from the API call. For example: ```javascript try { // Call the delete endpoint. const response = await axios(options); // Process the results from the API call. if (response.status === 204) { console.log(`Dashboard ${clientId} was deleted!`); } else { // Emit an unexpected error message. console.error("Unable to delete dashboard!"); } } catch (error) { // Emit an error from the server. console.error(error.message); } ``` 4. In your terminal, run `node cli.js optimize delete `, where `` is where you can paste the ID of the dashboard you would like to delete. You will see a response similar to the following: `Dashboard 12345 was deleted!` ## If you get stuck Having trouble configuring your API calls or want to examine an example of the completed tutorial? Navigate to the `completed` folder in the [GitHub repository](https://github.com/camunda/camunda-api-tutorials/tree/main/completed), where you can view an example `optimize.js` file. ## Next steps You can script several additional API calls as outlined in the [Optimize API reference material](./overview.md). --- ## Variable labeling With the variable labeling endpoint, variable labels can be added, updated, and deleted from Optimize. ## Functionality The variable labeling API allows users to add, update, and delete batches of variable label data, which Optimize stores in a dedicated index. All variable label data includes a reference to the process definition each variable belongs to, which allows Optimize to display a variable's label instead of its original name anywhere the given process definition is being used. Some examples of that would be in reports, configuring filters, report grouping, dashboard filters, and event-based processes. ## Limitations Note that this feature is currently not supported in task analysis. This means that during task analysis, the original name of a variable will be displayed. ## Authentication Every request requires [authentication](./optimize-api-authentication.md). ## Method & HTTP target resource POST `/api/public/variables/labels` ## Request headers The following request headers must be provided with every variable labeling request: | Header | Constraints | Value | | -------------- | ----------- | -------------------------------------------------- | | Authentication | REQUIRED\* | [Authentication](./optimize-api-authentication.md) | ## Request body The request body should contain a reference to the process definition using its key, as well as an array of variable labels. Each variable label object in the array must specify the name and type of the variable for which a label is being added, as well as the value of the label itself. ## Result This method returns no content. ## Response codes Possible HTTP Response Status codes: | Code | Description | | ---- | --------------------------------------------------------------------------------------- | | 204 | Request successful. | | 400 | Returned if some of the properties in the request body are invalid or missing. | | 401 | Secret incorrect or missing. See [authentication](#authentication) on how to authorize. | | 404 | The process definition with the given definition key doesn't exist. | ## Example 1 Insert three labels for three variable for a given process definition :::note If the label exists already in the index, its value will be overridden. ::: ### Request POST `/api/public/variables/labels` Request Body: ``` { "definitionKey": "bookrequest-1-tenant", "labels" : [ { "variableName": "bookAvailable", "variableType": "Boolean", "variableLabel": "book availability" }, { "variableName": "person.name", "variableType": "String", "variableLabel": "first and last name" }, { "variableName": "person.hobbies._listSize", "variableType": "Long", "variableLabel": "amount of hobbies" } ] } ``` ### Response Status 204. ## Example 2 Delete a label for a variable belonging to a given process definition by inputting an empty string for its value. If there is no label for the given variable in Elasticsearch, no operation is being conducted. ### Request POST `/api/public/variables/labels` Request Body: ``` { "definitionKey": "bookrequest-1-tenant", "labels" : [ { "variableName": "bookAvailable", "variableType": "Boolean", "variableLabel": "" } ] } ``` ### Response Status 204. ## Example 3 Insert and delete labels for two variables belonging to a given process definition. The following example adds a label for the variable with name **bookAvailable** and deletes a label for the variable with name **person.name**. ### Request POST `/api/public/variables/labels` Request Body: ``` { "definitionKey": "bookrequest-1-tenant", "labels" : [ { "variableName": "bookAvailable", "variableType": "Boolean", "variableLabel": "book availability" }, { "variableName": "person.name", "variableType": "String", "variableLabel": "" }, ] } ``` ### Response Status 204. ## Example 4 Attempting to insert multiple labels for the same variable will result to a 400 response code. ### Request POST `/api/public/variables/labels` Request Body: ``` { "definitionKey": "someProcessDefinitionKey", "labels" : [ { "variableName": "bookAvailable", "variableType": "Boolean", "variableLabel": "book availability" }, { "variableName": "bookAvailable", "variableType": "Boolean", "variableLabel": "is book available" }, ] } ``` ### Response Status 400. --- ## Orchestration Cluster MCP Server ## About The Orchestration Cluster MCP Server is an API surface of the Orchestration Cluster that exposes Camunda's operational capabilities through the [Model Context Protocol](https://modelcontextprotocol.io/) (MCP). - It enables AI agents and LLM-powered applications to discover and invoke Camunda tools using a standardized interface, without custom API integration code. - Similar to the [Orchestration Cluster API](../orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md), the MCP server is built into the Orchestration Cluster and shares the same [authentication](../orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md) and [authorization](/components/concepts/access-control/authorizations.md) model. It can be enabled independently. :::important Camunda 8 public API The Orchestration Cluster MCP Server is not part of the [Camunda 8 public API](/reference/public-api.md). ::: :::note This is the Orchestration Cluster MCP Server documentation. If you are looking to: - Expose your own BPMN processes as callable MCP tools for AI agents, see the [Processes MCP Server](../processes-mcp/processes-mcp-overview.md). - Connect an AI agent running in a BPMN process to an external MCP server, see the [MCP Client connector](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-client.md). ::: ### Key features Building AI-powered applications that interact with Camunda traditionally requires writing custom client code to call REST APIs, handle authentication, parse responses, and format data for AI consumption. The MCP server removes this by providing: | Benefit | Description | | :------------------ | :------------------------------------------------------------------------------------------------------------------------ | | Standardized access | AI agents discover and invoke Camunda capabilities through the MCP protocol, without bespoke integration code. | | Tool discovery | MCP clients automatically discover available tools and their schemas at runtime. | | Broad compatibility | Works with any MCP-compliant client, including VS Code (GitHub Copilot), Claude Code, Cursor, and custom AI applications. | | Consistent security | Inherits the same authentication and authorization model as the REST API. | ### Authentication The MCP server uses the same authentication model as the [Orchestration Cluster REST API](../orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md). OAuth tokens obtained for the REST API work without changes. For SaaS environments: 1. [Create API client credentials](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client) in the Camunda Console. Ensure the **Orchestration Cluster API** scope is enabled. 2. Use the generated **Client ID**, **Client secret**, **OAuth token endpoint**, and **audience** to obtain an access token via the [OAuth 2.0 client credentials flow](../orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md#using-a-token-oidcjwt). 3. Pass the token in the `Authorization: Bearer ` header, or use [`c8ctl mcp-proxy`](./orchestration-cluster-api-mcp-setup.md#using-c8ctl-mcp-proxy) to handle this automatically. For the full authentication reference, including Self-Managed OIDC and basic authentication setup, see [Authentication](../orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md). ### Transport The MCP server uses the [Streamable HTTP](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#streamable-http) transport and is served at the `/mcp/cluster` endpoint. It is stateless and no session management is required. ## Get started :::important Camunda 8.9 The MCP server is only available from Camunda 8.9 onwards. ::: If you have a local Orchestration Cluster running with [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) or [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md), the MCP server is enabled by default. Connect any MCP client using this configuration: ```json { "servers": { "camunda": { "type": "http", "url": "http://localhost:8080/mcp/cluster" } } } ``` For production environments and other deployment types, the MCP server must be explicitly enabled on your cluster before use. See [Enable and connect](./orchestration-cluster-api-mcp-setup.md) for more details. ## Available tools The MCP server exposes tools across the following domains: | Domain | Capabilities | | :------------------ | :-------------------------------------------------------------- | | Cluster | Check cluster health and retrieve topology information. | | Incidents | Search, retrieve, and resolve incidents. | | Process definitions | Search process definitions and retrieve BPMN XML. | | Process instances | Search, retrieve, and create process instances. | | User tasks | Search, retrieve, and assign user tasks. Search task variables. | | Variables | Search and retrieve variables. | For the full list of available tools, see [Available tools](./orchestration-cluster-api-mcp-tools.md). --- ## Enable and connect Enable the Orchestration Cluster MCP Server and configure MCP clients to connect. ## Enable the Orchestration Cluster MCP Server The MCP server is enabled by default in [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) and [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md). For other deployment types, it must be explicitly enabled before MCP clients can connect. Depending on your deployment, enable it as follows: The MCP server is **enabled by default** in Camunda 8 Run. No additional configuration is needed. The MCP server is **enabled by default** in the Docker Compose distribution. No additional configuration is needed. Set the following [`extraConfiguration`](/self-managed/deployment/helm/configure/application-configs.md#configuration-options) value in your Helm chart values: ```yaml orchestration: extraConfiguration: - file: mcp-gateway.yaml content: | camunda: mcp: enabled: true ``` In the Camunda Console, navigate to your cluster, open **Cluster Settings**, and enable **MCP Support**. :::info MCP server support is available on SaaS clusters running Camunda 8.9.0 or later. ::: For a full reference of MCP configuration properties, see [Property reference](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#api---mcp). ## Connect an MCP client Once the MCP server is enabled, you can connect any MCP-compliant client. The approach depends on your client's capabilities and authentication requirements. :::important When you [create API client credentials](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client) in the Camunda Console, all required connection details, including the base URL, OAuth endpoint, client ID, and audience, are displayed on the credentials page. ::: ### MCP endpoint URL The MCP server is served at `/mcp/cluster` on the Orchestration Cluster. The full endpoint URL depends on your deployment type: | Deployment | MCP endpoint URL | | :------------------------- | :------------------------------------------------------------------------------ | | Camunda 8 Run | `http://localhost:8080/mcp/cluster` | | Docker Compose | `http://localhost:8080/mcp/cluster` | | SaaS – public connectivity | `https://${REGION_ID}.api.camunda.io/${CLUSTER_ID}/mcp/cluster` | | SaaS – secure connectivity | `https://${CLUSTER_ID}.${REGION_ID}.privateconnectivity.camunda.io/mcp/cluster` | | Self-Managed (custom) | `https:///mcp/cluster` | For SaaS, find your **Region Id** and **Cluster Id** in the Camunda Console under **Cluster Details**. ### Direct HTTP connection If your Orchestration Cluster does not require authentication, for example, when running locally with [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) or [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md), you can connect directly to the MCP server endpoint without any additional tooling. Any MCP client that supports [Streamable HTTP](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#streamable-http) can be used. For authenticated environments, [use c8ctl `mcp-proxy`](#use-c8ctl-mcp-proxy) instead. ```json { "servers": { "camunda": { "type": "http", "url": "http://localhost:8080/mcp/cluster" } } } ``` ### Use c8ctl `mcp-proxy` Many MCP clients, such as VS Code (GitHub Copilot) and Claude Code, do not natively support the OAuth 2.0 client credentials flow required for authenticated environments. The [c8ctl](https://github.com/camunda/c8ctl) `mcp-proxy` command bridges this gap by providing a local STDIO-to-Remote HTTP proxy that handles authentication transparently. The proxy authenticates to the MCP server using OAuth 2.0 client credentials, and exposes a local STDIO MCP interface that your client connects to. #### Prerequisites - [Node.js](https://nodejs.org/) 18 or later. - [Client credentials](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client) for your Camunda cluster with the **Orchestration Cluster API** scope enabled. #### Configuration Add the following to your MCP client configuration. For example, use the following in `claude_desktop_config.json` for Claude Code: ```json { "mcpServers": { "camunda-mcp": { "type": "stdio", "command": "npx", "args": ["-y", "@camunda8/cli", "mcp-proxy"], "env": { "CAMUNDA_BASE_URL": "https://", "CAMUNDA_CLIENT_ID": "", "CAMUNDA_CLIENT_SECRET": "", "CAMUNDA_OAUTH_URL": "https:///oauth/token", "CAMUNDA_TOKEN_AUDIENCE": "" } } } } ``` | Variable | Description | | :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `CAMUNDA_BASE_URL` | Base URL of your Orchestration Cluster, **without** the `/mcp/cluster` path (for example, the public `api.camunda.io` URL or the private `privateconnectivity.camunda.io` URL when using Secure connectivity). | | `CAMUNDA_CLIENT_ID` | OAuth client ID from your API client credentials. | | `CAMUNDA_CLIENT_SECRET` | OAuth client secret from your API client credentials. | | `CAMUNDA_OAUTH_URL` | OAuth token endpoint URL. | | `CAMUNDA_TOKEN_AUDIENCE` | Token audience for the Orchestration Cluster API. | :::tip Where to find these values When you [create API client credentials](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client) in the Camunda Console, all required connection details are displayed on the credentials page. You can also copy a ready-to-use c8ctl configuration snippet directly from the **MCP** tab on the credentials screen. ::: For the full list of supported environment variables, see the [c8ctl documentation](https://github.com/camunda/c8ctl). ### Use with the MCP Client connectors You can also connect to the MCP server from within a BPMN process using Camunda's [MCP Client connectors](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-client.md). This allows an AI agent running in an agentic orchestration workflow to interact with Camunda's own operational data. For example, you can query incidents or start processes as part of an automated workflow. The [MCP Remote Client connector](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-remote-client-connector.md) connects to remote MCP servers over HTTP. Configure it in the properties panel with the following settings: - **Transport type**: Streamable HTTP. - **URL**: Your MCP endpoint URL (see [above](#mcp-endpoint-url)). - **Authentication**: OAuth 2.0. | Field | Value | | :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------ | | OAuth 2.0 token endpoint | Your OAuth token endpoint (`https://login.cloud.camunda.io/oauth/token` for SaaS). | | Client ID | Your OAuth client ID. | | Client secret | Your OAuth client secret. Use [secrets](/components/hub/organization/manage-clusters/manage-secrets.md) (for example, `{{secrets.MCP_CLIENT_SECRET}}`). | | Audience | The audience for your cluster API (`zeebe.camunda.io` for SaaS). | | Client authentication | Send client credentials in body. | For more details, see [MCP Remote Client connector](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-remote-client-connector.md). The [MCP Client connector](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-client-connector.md) manages persistent MCP connections through the connector runtime. Configure the Camunda MCP server as a remote HTTP client in your connector runtime configuration (for example, `application.yml`): ```yaml camunda: connector: agenticai: mcp: client: enabled: true clients: camunda-mcp: type: http http: url: https://${REGION_ID}.api.camunda.io/${CLUSTER_ID}/mcp/cluster authentication: type: oauth oauth: oauth-token-endpoint: https://login.cloud.camunda.io/oauth/token client-id: client-secret: audience: zeebe.camunda.io client-authentication: credentials-body ``` The example above shows a SaaS configuration using the public endpoint. For clusters with Secure connectivity (AWS PrivateLink), set `url` to the private MCP endpoint URL shown in Camunda Console instead of the public `zeebe.camunda.io` host (the path still ends with `/mcp/cluster`). For local unauthenticated setups, you can omit the `authentication` block and use `http://localhost:8080/mcp/cluster` as the URL. Reference the client ID `camunda-mcp` in the MCP Client connector element template in your BPMN process. For more details, see [MCP Client connector](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-client-connector.md). --- ## Available tools The following tools are available through the Orchestration Cluster MCP server, grouped by domain. :::info Tool names, parameters, and response schemas are fully discoverable by MCP clients at runtime. The exact tool signatures may evolve across versions. ::: ## Cluster | Tool | Description | | :----------------- | :------------------------------------------------------------------------------------ | | `getClusterStatus` | Returns whether the cluster is healthy (at least one partition has a healthy leader). | | `getTopology` | Returns cluster topology including brokers, partitions, roles, health, and versions. | ## Incidents | Tool | Description | | :---------------- | :------------------------------------------------------------------------------------------------------------------------------------------ | | `searchIncidents` | Search incidents with filters such as state, error type, element ID, creation time range, process definition key, and process instance key. | | `getIncident` | Retrieve an incident by key. | | `resolveIncident` | Resolve an incident. For job-related incidents, job retries are automatically updated. | ## Process definitions | Tool | Description | | :------------------------- | :------------------------------------------------------------ | | `searchProcessDefinitions` | Search process definitions with filters such as name and key. | | `getProcessDefinition` | Retrieve a process definition by key. | | `getProcessDefinitionXml` | Retrieve the BPMN XML of a process definition. | ## Process instances | Tool | Description | | :----------------------- | :------------------------------------------------------------------------------- | | `searchProcessInstances` | Search process instances with filters. | | `getProcessInstance` | Retrieve a process instance by key. | | `createProcessInstance` | Create a new process instance, optionally with variables or awaiting completion. | ## User tasks | Tool | Description | | :------------------------ | :---------------------------------------------------------------------------------- | | `searchUserTasks` | Search user tasks with filters such as assignee, state, and process definition key. | | `getUserTask` | Retrieve a user task by key. | | `assignUserTask` | Update the assignment of a user task. | | `searchUserTaskVariables` | Search variables scoped to a specific user task. | ## Variables | Tool | Description | | :---------------- | :----------------------------- | | `searchVariables` | Search variables with filters. | | `getVariable` | Retrieve a variable by key. | --- ## Intermediate tutorial Intermediate In this tutorial, we'll step through examples to highlight the capabilities of the Orchestration Cluster REST API, such as deploying resources, creating and starting a process instance, and viewing a process instance by its key. This tutorial is intended for intermediate users of the Orchestration Cluster REST API, using more sophisticated API calls and multipart requests. If you are new to the Orchestration Cluster REST API, we recommend starting with the [beginner tutorial](/apis-tools/orchestration-cluster-api-rest/tutorial.md). ## Prerequisites | Requirement | Description | | :------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [Create a cluster](/components/hub/organization/manage-clusters/create-cluster.md) | If you haven't done so already, create a cluster. | | [Create your first client](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client) | Upon cluster creation, create your first client. Ensure you check the `Orchestration Cluster API` client scope box. Make sure you keep the generated client credentials in a safe place. The **Client secret** will not be shown again. For your convenience, you can also download the client information to your computer. | | Clone the [GitHub repository](https://github.com/camunda/camunda-api-tutorials) | In this tutorial, we use a JavaScript-written [GitHub repository](https://github.com/camunda/camunda-api-tutorials) to write and run requests. Clone this repo before getting started. | | Prepare resources | The first request we will run is a request to deploy one or more resources (such as processes, decision models, or forms). For the purposes of this tutorial, we have preconfigured a BPMN diagram and converted this into XML. This diagram, `calculate-sales-tax.bpmn`, can be found in the GitHub repository above within the `resources` folder. The BPMN diagram itself represents a process to calculate the total sales tax for a given purchase. You can take a closer look at this diagram by opening it in [Modeler](/components/modeler/about-modeler.md). | | [Node.js](https://nodejs.org/en/download) | Ensure you have [Node.js](https://nodejs.org/en/download) installed as this will be used for methods that can be called by the CLI (outlined later in this guide). Run `npm install` to ensure you have updated dependencies. | | Authenticate | You need authentication to access the API endpoints. Find more information in [Camunda 8 authentication](./orchestration-cluster-api-rest-authentication.md), and the section below. | ### Set up authentication If you're interested in how we use a library to handle auth for our code, or to get started, examine the `auth.js` file in the GitHub repository. This file contains a function named `getAccessToken` which executes an OAuth 2.0 protocol to retrieve authentication credentials based on your client ID and client secret. Then, we return the actual token that can be passed as an authorization header in each request. To set up your credentials, create an `.env` file which will be protected by the `.gitignore` file. Add the following environment variables: - `CAMUNDA_CLIENT_ID` - `CAMUNDA_CLIENT_SECRET` - `CAMUNDA_REST_ADDRESS` (after creating a client and downloading the .env variables, this is reflected in the Console UI as `ZEEBE_REST_ADDRESS`) - `CAMUNDA_TOKEN_AUDIENCE` (represented as `ZEEBE_TOKEN_AUDIENCE` in the Console UI), which is `zeebe.camunda.io` in a Camunda 8 SaaS environment. For example, your audience may be defined as `CAMUNDA_TOKEN_AUDIENCE=zeebe.camunda.io`. These keys will be consumed by the `auth.js` file to execute the OAuth protocol, and should be saved when you generate your client credentials in [prerequisites](#prerequisites). See the existing `.env.example` file for an example of how your `.env` file should look upon completion. Do not place your credentials in the `.env.example` file, as this example file is not protected by the `.gitignore`. :::note In this tutorial, we will execute arguments to deploy a resource, create and start a process instance, and view a process instance by its key. You can examine the framework for processing these arguments in the `cli.js` file before getting started. ::: ## Deploy resources (POST) First, let's script an API call to deploy a resource. To do this, take the following steps: 1. In the file named `camunda-process-instances.js`, outline the authentication and authorization configuration in the first few lines. This will pull in your `.env` variables to obtain an access token before making any API calls: ```javascript const authorizationConfiguration = { clientId: process.env.CAMUNDA_CLIENT_ID, clientSecret: process.env.CAMUNDA_CLIENT_SECRET, // These settings come from your .env file. Note that CAMUNDA_TOKEN_AUDIENCE is represented by ZEEBE_TOKEN_AUDIENCE in the Console UI. audience: process.env.CAMUNDA_TOKEN_AUDIENCE, }; ``` 2. Examine the function `async function deployResources()` below this configuration. This is where you will script out your API call. 3. Within the function, you must first generate an access token for this request, so your function should now look like the following: ```javascript async function deployResources() { const accessToken = await getAccessToken(authorizationConfiguration); } ``` 4. Using your generated client credentials from [prerequisites](#prerequisites), capture your Orchestration Cluster REST API URL beneath your call for an access token by defining `camundaApiUrl`: ```javascript const camundaApiUrl = process.env.CAMUNDA_REST_ADDRESS; ``` 5. On the next line, script the API endpoint to deploy the resources: ```javascript const url = `${camundaApiUrl}/deployments`; ``` 6. We will now configure the variables representing the BPMN file and its form data. This may look different depending on which resources you choose to deploy, but reflects the block-scoped local variables and append method to insert a set of objects for the BPMN resource of this tutorial: ```javascript const formData = new FormData(); // Read the BPMN file and add it to the form data const bpmnFilePath = path.resolve("resources/calculate-sales-tax.bpmn"); const fileContent = fs.readFileSync(bpmnFilePath); formData.append("resources", fileContent, { filename: "calculate-sales-tax.bpmn", contentType: "application/xml", }); ``` :::note The `resources` name must be exact according to the API requirements, the path to the file (`const bpmnFilePath = path.resolve("resources/calculate-sales-tax.bpmn");`) must be correct, and `contentType` must be `application/xml` to ensure the upload will not fail. ::: 7. Call the endpoint, process the results from the API call, and emit an error message from the server if necessary: ```javascript try { const response = await axios.post(url, formData, { headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, ...formData.getHeaders(), }, }); const deployedResources = response.data.deployments || []; // Emit deployed resources deployedResources.forEach((x) => console.log( `Process Definition Key: ${x.processDefinition.processDefinitionKey}; Process Definition Id: ${x.processDefinition.processDefinitionId}` ) ); } catch (error) { // Emit an error from the server. console.error(`Error deploying resources: ${error.message}`); } ``` 8. In your terminal, run `node cli.js processInstances deploy`. :::note This `deploy` command is connected to the `deployResources` function at the bottom of the `camunda-process-instances.js` file, and executed by the `cli.js` file. While we will work with process instances in this tutorial, you may add additional arguments depending on the API requests you want to make. ::: The existing process definition key and ID will now output. If you have an invalid API name or action name, or no arguments provided, or improper/insufficient credentials configured, an error message will output as outlined in the `cli.js` file. ## Create and start a process instance (POST) To create and start a process instance based on the process instance key obtained in the request above, take the following steps: 1. Outline your function, similar to the steps above: ```javascript async function createInstance([processDefinitionKey]) { const accessToken = await getAccessToken(authorizationConfiguration); const camundaApiUrl = process.env.CAMUNDA_REST_ADDRESS; const url = `${camundaApiUrl}/process-instances`; } ``` 2. Build the payload you will send to the endpoint: ```javascript const payload = { processDefinitionKey, variables: { total: 90.0, }, }; ``` :::note The request will succeed if the variable names are different, but the process instance itself will not function as expected. ::: 3. Call the endpoint, process the results from the API call, and emit an error message from the server if necessary: ```javascript try { const response = await axios.post(url, payload, { headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, }, }); const processInstance = response.data; console.log(`Process Instance Key: ${processInstance.processInstanceKey}`); } catch (error) { console.error(`Error creating process instance: ${error.message}`); } ``` 4. In your terminal, run `node cli.js processInstances create `, where `` is the process definition key. The `processInstanceKey` will now display in the output. Capture this key for a future method. ## Retrieve a process instance (GET) To retrieve a process instance by the process instance key, take the following steps: 1. Outline your function, similar to the steps above: ```javascript async function viewInstance([processInstanceKey]) { const accessToken = await getAccessToken(authorizationConfiguration); const camundaApiUrl = process.env.CAMUNDA_REST_ADDRESS; const url = `${camundaApiUrl}/process-instances/${processInstanceKey}`; } ``` 2. Call the endpoint, process the results from the API call, and emit an error message from the server if necessary: ```javascript try { const response = await axios.get(url, { headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, }, }); const results = response.data; console.log( `Process instance name: ${results.processDefinitionName}; State: ${results.state};` ); } catch (error) { console.error(`Error retrieving process instance: ${error.message}`); } ``` 3. In your terminal, run `node cli.js processInstances view `, where `` is the process instance key. The `processDefinitionName` and `state` will then display in the output. ## Troubleshooting Having trouble configuring your API calls or want to examine an example of the completed tutorial? Navigate to the `completed` folder in the [GitHub repository](https://github.com/camunda/camunda-api-tutorials/tree/main/completed), where you can view an example `camunda-process-instances.js` file. ## Next steps You can script several additional API calls as outlined in the [Orchestration Cluster REST API reference material](./orchestration-cluster-api-rest-overview.md). --- ## Authentication(Orchestration-cluster-api-rest) This page explains how to authenticate requests to the Orchestration Cluster REST API across different deployment environments. ## Authentication support matrix | Distribution | Default Authentication | No auth support | Basic auth support | OIDC-based auth support | | --------------------------------------------------------------------------------- | ---------------------- | ----------------------- | ------------------ | ----------------------- | | [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) | None | ✅ (default) | ✅ (when enabled) | ✅ (when configured) | | [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md) | None | ✅ (default) | ✅ (when enabled) | ✅ (when configured) | | [Helm](/self-managed/deployment/helm/install/quick-install.md) | Basic Auth | ✅ (when auth disabled) | ✅ (default) | ✅ (when configured) | | SaaS | OIDC-based Auth | ❌ | ❌ | ✅ (required) | :::info Authentication vs. authorization Authentication establishes who is calling the Orchestration Cluster REST API (for example, using basic authentication or an OIDC access token). Authorization determines what that caller can do, based on authorizations configured in Admin. To learn more about authorization resources, permissions, and precedence (including user task permissions), see [Orchestration Cluster authorization](../../components/concepts/access-control/authorizations.md). ::: ## Authenticate API calls ### No authentication (local development) By default, Camunda 8 Run and Docker Compose expose the Orchestration Cluster REST API without authentication for local development. You can make API requests directly: ```shell curl http://localhost:8080/v2/topology ``` ### Basic Authentication Basic Authentication uses username and password credentials. **For Camunda 8 Run:** Enable Basic Auth by configuring authentication in your `application.yaml`. See [Camunda 8 Run documentation](/self-managed/quickstart/developer-quickstart/c8run/configuration.md#enable-authentication-and-authorization) for details. **For Helm:** Basic Auth is enabled by default for the Orchestration Cluster API. Include your username and password in each API request: ```shell curl --user username:password \ http://localhost:8080/v2/topology ``` :::note Basic Authentication checks the password with every request, limiting the number of requests per second. It may not be suitable for production. See [Camunda components troubleshooting](/self-managed/operational-guides/troubleshooting.md) ::: ## Using a token (OIDC/JWT) OIDC-based authentication is recommended for production and required for SaaS. Obtain an access token and pass it as an OAuth 2.0 Bearer Token in the `Authorization` header of each request. The token's subject (user or client) must also have the required authorizations. Otherwise, requests fail with `403 Forbidden` even if authentication succeeds. 1. [Create client credentials](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client) in the Camunda Console. 2. Request an access token using the credentials: ```shell curl --request POST ${CAMUNDA_OAUTH_URL} \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'grant_type=client_credentials' \ --data-urlencode "audience=${CAMUNDA_TOKEN_AUDIENCE}" \ --data-urlencode "client_id=${CAMUNDA_CLIENT_ID}" \ --data-urlencode "client_secret=${CAMUNDA_CLIENT_SECRET}" ``` 3. Use the access token from the response in your API requests: ```shell curl --header "Authorization: Bearer ${ACCESS_TOKEN}" \ ${BASE_URL}/topology ``` **Prerequisites for OIDC-based authentication** - Your Orchestration Cluster must already be configured with your Identity Provider. See [Set up OIDC-based Authentication](/self-managed/components/orchestration-cluster/admin/connect-external-identity-provider.md). - You must have a registered client in your IdP with a **client ID**, **client secret**, and authorization endpoint. - Note the configured **audience** and **scope** for token requests (variables `OC_AUDIENCE` and `SCOPE`). Depends on IdP configuration. **Request an access token using client credentials** Example for Keycloak; adjust the authorization URI and parameters for your IdP: ```shell curl --location --request POST 'http:///auth/realms//protocol/openid-connect/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode "client_id=${CLIENT_ID}" \ --data-urlencode "client_secret=${CLIENT_SECRET}" \ --data-urlencode "audience=${OC_AUDIENCE}" \ --data-urlencode "scope=${SCOPE}" \ --data-urlencode 'grant_type=client_credentials' ``` > **Microsoft Entra ID**: Use `scope=${SCOPE}/.default` instead of `scope=${SCOPE}`. The Authorization URI is typically `https://login.microsoftonline.com//oauth2/v2.0/token`. **Use the access token in API requests** ```shell curl --header "Authorization: Bearer ${ACCESS_TOKEN}" \ ${BASE_URL}/topology ``` ### OIDC-based authentication using X.509 client certificates For advanced security scenarios, you can obtain OIDC access tokens using X.509 client certificates. This is typically required in Self-Managed environments where your IdP enforces mutual TLS (mTLS). **For Java applications** The Java client supports automatic OIDC access token retrieval using X.509 client certificates. Configure the necessary keystore and truststore via code or environment variables. See [Java client authentication](../java-client/getting-started.md#oidc-access-token-authentication-with-x509-client-certificate) for details. **For other clients** Refer to your IdP documentation for obtaining tokens using X.509 certificates. ### Automatic token management in official clients Official Camunda clients (Java client or Spring Boot Starter) handle token acquisition and renewal automatically. You do not need to manually obtain or refresh tokens. ### Troubleshooting - Check logs for authentication errors. - Verify your access token includes the correct audience if audience validation is enabled. ### Learn more - [Camunda Java client authentication and token management](../java-client/getting-started.md) - [Camunda Spring Boot Starter: Configuring the Camunda 8 connection](../camunda-spring-boot-starter/getting-started.md#configuring-the-camunda-8-connection) - [Orchestration Cluster authorization: Resources, permissions, and configuration](../../components/concepts/access-control/authorizations.md) --- ## Data fetching The Orchestration Cluster REST API allows you to retrieve data from key resources like process definitions, user tasks, users, and tenants. Each search-enabled endpoint supports rich filtering, sorting, and pagination so you can quickly find the data that matters most. The sections below explain how to structure a search request and interpret the response format. ## Searchable resources The following examples support search via POST endpoints, each with its own set of filterable fields: - Process instances (`POST /v2/process-instances/search`) - User tasks (`POST /v2/user-tasks/search`) - Users (`POST /v2/users/search`) - Batch operations (`POST /v2/batch-operations/search`) Refer to the [interactive Orchestration Cluster REST API Explorer](./specifications/orchestration-cluster-api.info.mdx) for the full attribute lists. ## Supported operations Most searchable resources allow: - Filtering based on properties or variables - Sorting results - Paginating with either offset or cursor methods - Accessing nested resources (e.g., group users) > Example: You can search for groups using `POST /v2/groups/search`, and for the users in a group using `POST /v2/groups/:groupId/users/search`. You can also fetch single resources using `GET` endpoints with unique identifiers, such as: ```shell GET /v2/user-tasks/:userTaskKey ``` ## Data consistency Endpoints in the Orchestration Cluster API are classified as either **strongly consistent** or **eventually consistent**. This distinction applies to the _data behind the endpoint_, not the endpoint's functionality itself. - **Strongly consistent endpoints** return data that reflects the real-time state of the system. - **Eventually consistent endpoints** return data exported by the [Camunda Exporter](../../self-managed/components/orchestration-cluster/zeebe/exporters/camunda-exporter.md). This data may lag behind the real-time state until the exporter processes it, so it becomes consistent only after a delay. Each endpoint is clearly labeled with its consistency type so you can account for this behavior in your applications. ### Why consistency matters If eventual consistency is not handled properly, it can lead to unexpected results. For example: 1. A resource is created using a strongly consistent endpoint. 2. An _immediate_ request to an eventually consistent endpoint for the same resource might return: - `404 Not Found` for a `GET` request, or - an empty result set for a search request. This happens because the eventually consistent endpoint has not yet synced the new data. A later request will return the correct result once the data export completes. If your application does not account for eventual consistency, you may encounter **non-deterministic runtime behavior**. Code paths that work reliably during development or testing may fail intermittently in production, especially under load, if this characteristic is ignored. ## User task support The Orchestration Cluster REST API only supports Camunda user tasks (previously referred to as [Zeebe user tasks](../migration-manuals/migrate-to-camunda-user-tasks.md), which may still appear as `zeebe:userTask` in your XML content). ## Search requests Search requests consist of the components for **filter**, **sort**, and **page**. ### Filter The filter object defines which fields should match. Only items that match the given fields will be returned. The available fields vary by object and are described in the respective search endpoint. Filtering by a unique identifier is usually available in filtering options. Beyond that, the filter options don’t have to comprise all the returned items’ attributes.
Example ``` POST /v2/user-tasks/search { "filter": { "assignee": "demo", "processInstanceKey": "22456786958" } } ``` This filters by the attributes `assignee` and `processInstanceKey`, looking for exact matches with the provided values.
### Sort The sort array specifies by which `field`s to sort the result items and whether this happens in ascending (ASC) or descending (DESC) `order`.
Example ``` POST /v2/user-tasks/search { "sort": [ { "field": "state", "order": "ASC" } ] } ``` This sorts the overall result set by the `state` attribute in ascending order.
### Page The page object details how to slice the result set. An initial search request can omit the page object or define the `limit`. This specifies the maximum number of results to retrieve per request. Subsequent requests can either use **cursor** or **offset pagination** to iterate through the result set. Cursor pagination bases on the value of the [search response's](#search-responses) `startCursor` and `endCursor`. Copy `startCursor` into `before` or `endCursor` into `after` to page through results respectively. The [search example](#search-example) showcases how to use these attributes for cursor pagination. Offset pagination uses the `from` attribute to define the starting point of the next set of items in the overall result set. :::note Choosing the right pagination type depends on the specific use case. The expected result set size and intended usage of the results have the biggest influence. The expected reliability and performance of the search request affect this decision as well. Consider using cursor pagination for larger result sets and displaying result list that scroll infinitely. Paged result sets can be realized with offset pagination in a straightforward way but come with performance penalties for larger result sets. :::
Example ``` POST /v2/user-tasks/search { "page": { "limit": 3 } } ``` This limits the result set returned in the response to 3 items, no matter how many overall results exist.
### Advanced search filters To provide an easy yet expressive way for users to search for and filter resources, search requests can contain more advanced filter criteria than fields being _equal_ to a target value. For example, this allows searching using logical (and, in) and comparison operators (greater than, less than). The list of generally supported advanced filter operators is described below. The supported operators depend on the endpoint and the type of the filter attribute. All endpoints document available operators for each attribute in the Orchestration Cluster REST API specification. #### Conditional Operators | Operator | Syntax | Description | | --------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `$eq` | `field: { "$eq": value }` | Filter where `field` is equal to `value`. Abbreviated form `field: value` is also allowed. | | `$neq` | `field: { "$neq": value }` | Filter where `field` is not equal to `value`. | | `$exists` | `field: { "$exists": value }` | Filter where `field` does or does not exist. The `value` is a boolean and can be either `true` or `false`. | | `$gt` | `field: { "$gt": value }` | Filter where `field` is greater than `value`. | | `$gte` | `field: { "$gte": value }` | Filter where `field` is greater than or equal to `value`. | | `$lt` | `field: { "$lt": value }` | Filter where `field` is less than `value`. | | `$lte` | `field: { "$lte": value }` | Filter where `field` is less than or equal to `value`. | | `$like` | `field: { "$like": value }` | Filter where `field` contains a string like `value`. The wildcard characters `*` (zero, one, or multiple characters) and `?` (a single character) are allowed in `value`. They can be escaped with a backslash, like in `my \*`. | | `$in` | `field: { "$in": [ value1, value2, ... ] }` | Filter where `field` is equal to at least one of the `value`s in the provided array. | | `$notIn` | `field: { "$notIn": [ value1, value2, ... ] }` | Filter where `field` is not equal to any one of the `value`s in the provided array. |
Example ``` POST /v2/user-tasks/search { "filter": { "candidateGroups": { "$like": "external-*", "$neq": "external-supervisor" } } } ``` This filters by `candidateGroups` that start with `"external-"` but do not match `"external-supervisor"`.
#### Logical Operators | Operator | Syntax | Description | | -------- | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | `$or` | `"$or": [ { condition1 }, { condition2 }, ... ]` | Filter where at least one of the conditions is true. | | and | `{ field: { "$lt": value1 }, field: { "$gt": value2 }, ... }` | All conditions outside of `$or` operators will be considered as combined by an `AND` operator. There is no explicit operator. |
Example ``` POST /v2/user-tasks/search { "filter": { "assignee": "demo", "processInstanceKey": "22456786958", "candidateGroups": { "$neq": "external-supervisor", "$like": "external-*" } } } ``` The top-level filters `assignee`, `processInstanceKey`, and `candidateGroups` are connected by an AND operator. Likewise, the `$neq` and `$like` advanced filter operators inside the top-level `candidateGroups` filter are combined by an AND operator.
#### Variables Search endpoints can support filtering by variable values. This allows querying for process-related resources based on the values of specific variables that exist in their respective scope. For example, user task search supports filtering using the `localVariables` array and defining filter criteria for specific variables. For variable values, the advanced filter criteria outlined above for fields apply.
Example ``` POST /v2/user-tasks/search { "filter": { "localVariables" : [ { "name": "orderVolume", "value": "10000" }, { "name": "price", "value": { "$lt": "500" } }, { "name": "skipped", "value": { "$exists": false } } ] } } ``` This filters for user tasks containing at least the variables `orderVolume` with a value of `10000` and `price` with a value lower than `500`, not containing variable `skipped`.
## Search responses Search responses consist of two components: **`items`** and **`page`**. - The **`items`** array contains instances of the respective endpoint’s resource. The structure and attributes of these instances vary by endpoint and are detailed in the corresponding endpoint documentation. - The **`page`** object includes pagination details for navigating through results in subsequent search requests: - **`totalItems`**: Indicates the total number of results for the query. > **Note:** In Elasticsearch/OpenSearch, this value is capped at **10,000**, even if more results are available. - **`startCursor`**: A reference to the **first** entry on the current page. Use this value in the `before` parameter to page **backward** in a subsequent [search request](#search-requests). - **`endCursor`**: A reference to the **last** entry on the current page. Use this value in the `after` parameter to page **forward** in a subsequent [search request](#search-requests).
Example ``` { "items": [ { "state": "CREATED", "processInstanceKey": "22456786958", "userTaskKey": "22456786345", ... }, { "state": "CREATED", "processInstanceKey": "22456786958", "userTaskKey": "22456786456", ... }, { "state": "COMPLETED", "processInstanceKey": "22456786958", "userTaskKey": "22456786678", ... } ], "page": { "totalItems": 345, "startCursor": "jfenj8vhekgj98uzfafhu7", "endCursor": "negbkjeh84tzh4gk0kwegj" } } ```
## Search example Querying for the first three user tasks with certain criteria and sorted by state could look as follows: ``` POST /v2/user-tasks/search { "filter": { "assignee": "demo", "processInstanceKey": "22456786958", "candidateGroups": { "$like": "external-*", "$neq": "external-supervisor" }, "localVariables" : [ { "name": "orderVolume", "value": "10000" }, { "name": "price", "value": { "$lt": "500" } }, { "name": "skipped", "value": { "$exists": false } } ], }, "sort": [ { "field": "state", "order": "ASC" } ], "page": { "limit": 3 } } ``` This could yield the following example result: ``` 200 OK { "items": [ { "state": "CREATED", "processInstanceKey": "22456786958", "userTaskKey": "22456786345", ... }, { "state": "CREATED", "processInstanceKey": "22456786958", "userTaskKey": "22456786456", ... }, { "state": "COMPLETED", "processInstanceKey": "22456786958", "userTaskKey": "22456786678", ... } ], "page": { "totalItems": 345, "startCursor": "jfenj8vhekgj98uzfafhu7", "endCursor": "negbkjeh84tzh4gk0kwegj" } } ``` A follow-up request to receive the next three items could then look as follows: ``` POST /v2/user-tasks/search { "filter": { "assignee": "demo", "processInstanceKey": "22456786958", "candidateGroups": { "$like": "external-*", "$neq": "external-supervisor" }, "localVariables" : [ { "name": "orderVolume", "value": "10000" }, { "name": "price", "value": { "$lt": "500" } }, { "name": "skipped", "value": { "$exists": false } } ], }, "sort": [ { "field": "state", "order": "ASC" } ], "page": { "limit": 3, "after": "negbkjeh84tzh4gk0kwegj" } } ``` This yields the next three user task instances after the last one from the first search request’s result. --- ## Orchestration Cluster REST API ## About You can use the Orchestration Cluster REST API to interact programmatically with process orchestration capabilities in Camunda 8. For example, you can start, manage, and query process instances, complete user tasks, resolve incidents, and manage variables. Use this API to: | Use case | Description | | :------------------------------------------ | :-------------------------------------------------------------------------------- | | Build process-driven applications | Create applications to orchestrate processes and integrate with existing systems. | | Integrate user tasks into custom UIs | Build task management interfaces that connect to Camunda's user task engine. | | Start and monitor external system processes | Trigger process instances and track progress from any application or service. | ## Key features This API is designed to make it easy to [find resources](./orchestration-cluster-api-rest-data-fetching.md#advanced-search-filters) with a consistent experience, while ensuring all endpoints are secure with [authentication](./orchestration-cluster-api-rest-authentication.md) and fine-grained [resource authorization](/components/concepts/access-control/authorizations.md). Key features include: | Feature | Description | | :-------------------------------- | :---------------------------------------------------- | | Full process lifecycle management | Deploy, start, and monitor BPMN processes. | | User task operations | Claim, complete, and manage human tasks. | | Variable management | Read and update process variables. | | Incident resolution | Handle and resolve process incidents. | | Advanced search and filtering | Query process data with powerful search capabilities. | :::info - This API is part of the Camunda 8 [public API](/reference/public-api.md) and is covered by our SemVer stability guarantees (except for clearly marked alpha endpoints). You can rely on backward compatibility for production use. - To learn more about the Orchestration Cluster, see [Orchestration Cluster](/components/orchestration-cluster.md). ::: ## Getting started This section helps you get up and running in minutes. ### Prerequisites - **A Camunda 8 Orchestration Cluster** - For local development, use [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) or [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md), which expose the API without requiring credentials or tokens by default. - For production or advanced development, use [Helm/Kubernetes](/self-managed/deployment/helm/install/quick-install.md) or [manual installation](/self-managed/deployment/manual/install.md). - Alternatively, sign up for a free [Camunda 8 SaaS trial](https://accounts.camunda.io/signup) to get a managed cluster with the API enabled. - **A client to send API requests** - Quick testing: Use the [Swagger](../orchestration-cluster-api-rest-swagger) interface - Programmatic access: Use the [Java client](/apis-tools/java-client/getting-started.md) or [Camunda Spring Boot Starter](/apis-tools/camunda-spring-boot-starter/getting-started.md) - Custom client: [Download the OpenAPI spec](https://github.com/camunda/camunda/blob/main/zeebe/gateway-protocol/src/main/proto/rest-api.yaml) to generate your own client - Universal client: [Postman collection](https://www.postman.com/camundateam/camunda-8-postman/collection/apl78x9/camunda-8-api-rest) ### Authentication Authentication for the Orchestration Cluster REST API depends on your environment and how you deploy Camunda 8. Authenticate your Client requests based on your setup. **Supported authentication methods** - No authentication – For local development only - Basic authentication – Username/password for simple setups - OIDC-based authentication – Use OAuth2/OIDC tokens for production environments **Quick reference** - See the [authentication support matrix](./orchestration-cluster-api-rest-authentication.md#authentication-support-matrix) for details on supported methods by deployment type - If you're using the Java or Spring clients, token management is handled automatically. See [client authentication configuration](../camunda-spring-boot-starter/getting-started.md#configuring-the-camunda-8-connection) For detailed authentication setup, follow the step-by-step guide in [Authentication](./orchestration-cluster-api-rest-authentication.md) based on your deployment type. ### Test your connection Once you're set up, verify your connection works by making your first API call: #### Using curl Local (Camunda 8 Run / Docker Compose): ```bash curl http://localhost:8080/v2/topology ``` SaaS, public connectivity: ```bash curl https://${REGION_ID}.api.camunda.io/${CLUSTER_ID}/v2/topology ``` SaaS, secure connectivity (AWS PrivateLink): ```bash curl https://${CLUSTER_ID}.${REGION_ID}.privateconnectivity.camunda.io/api/v2/topology ``` Replace the placeholders with the values for your environment. See [Base URLs](#base-urls) for details on SaaS (public and secure connectivity) and self-managed setups. #### Using Postman Try the [get cluster topology](https://www.postman.com/camundateam/camunda-8-postman/request/en495q6/get-cluster-typology) request or browse the full collection. This request returns information about your cluster topology, confirming that your setup is working correctly. ### Try your first workflow If you're just getting started with process automation, try this simple workflow: 1. **Model a process** – Create a simple BPMN process with a user task using [Camunda Modeler](https://camunda.com/download/modeler/) 2. **Deploy the process** – Use [`POST /deployments`](./specifications/create-deployment.api.mdx) to deploy your BPMN file 3. **Start a process instance** – Use [`POST /process-instances`](./specifications/create-process-instance.api.mdx) to create a new process instance 4. **Complete a user task** – Use [`POST /user-tasks/{userTaskKey}/completion`](./specifications/complete-user-task.api.mdx) to complete the task For a complete walkthrough with code examples, see our [Getting Started Tutorial](/guides/getting-started-example.md). ### Explore the API ## API reference This section covers the technical details and conventions you need to understand when working with the Orchestration Cluster REST API. ### Base URLs #### SaaS In the Camunda Console, go to your cluster, and in the Cluster Details, find your **Region Id** and **Cluster Id**. - For public connectivity (default), use this pattern as your `${BASE_URL}`: `https://${REGION_ID}.api.camunda.io/${CLUSTER_ID}/v2/` - For secure connectivity (AWS PrivateLink), use the private base URL shown in Console. For the Orchestration Cluster REST API, the pattern is: `${BASE_URL} = https://${CLUSTER_ID}.${REGION_ID}.privateconnectivity.camunda.io/api/v2/` For example: `https://b4102386-6818-43c6-a880-d21c968a883f.ork-1.privateconnectivity.camunda.io/api/v2/topology` #### Self-Managed Use the host and path defined for your [Zeebe Gateway](/reference/glossary.md#zeebe-gateway). For Ingress and routing details, see the [configuration guide](/self-managed/deployment/helm/configure/ingress/ingress-setup.md). If you're using the default setup, the `${BASE_URL}` is `http://localhost:8080/v2/`. ### Versioning Camunda uses semantic versioning (SemVer) to ensure API changes are predictable and compatible. This helps you upgrade safely without unexpected breaking changes. The API version is determined by the API version number (`v2`) and the product version—for example, `POST /v2/user-tasks/search` in Camunda 8.8.0. Camunda versions the entire API rather than individual endpoints. If a breaking change occurs in any endpoint, the entire API is versioned. During migration periods, multiple API versions may coexist—for example, both `v2` and `v3` versions of `/user-tasks/search` may be available in the same release. :::note Adding new endpoints or attributes to existing responses is **not** considered a breaking change. ::: ### Request size limits The default maximum request size for all requests (for example, to `POST /v2/deployments`) is 4MB. It can be configured in the Zeebe Gateway configuration using the `maxMessageSize` property. For more information, see the [Zeebe Gateway configuration reference](/self-managed/components/orchestration-cluster/zeebe/configuration/configuration.md#gateway-configuration). ### Naming conventions Naming across the Orchestration Cluster REST API is simple, intuitive, and consistent to reduce friction when working with multiple endpoints. The conventions include: - **Nouns over verbs** – e.g., `assignment` instead of `assign` - **Plural terms** for top-level resources – e.g., `user-tasks` - **Kebab-case** for multiple words in path parameters – e.g., `user-tasks` - **camelCase** for multiple words in query parameters – e.g., `userTaskKey` These conventions are illustrated in the following endpoint example: `POST /user-tasks/{userTaskKey}/assignment` For IDs or similar short 2- or 3-letter words or acronyms, Camunda only capitalizes the first letter. If standalone, all letters are lowercase. | Term | Usage | | ---- | ------------------------------------------ | | ID | `id` (standalone) or `processDefinitionId` | | URL | `url` (standalone) or `externalUrl` | | UUID | `uuid` (standalone) or `clusterUuid` | Identifiers follow a naming convention for parameters and data attributes alike: - Unique technical identifiers are suffixed with **key**, for example, `userTaskKey`, `processInstanceKey`, or `userKey`. These are usually numeric values. - Other identifiers, such as those copied from the BPMN XML, are typically suffixed with **id**, for example, `processDefinitionId`. - Key and id fields contain the entity as a prefix, for example, `userTaskKey` or `processDefinitionId`. This applies when referencing other resources like `formKey` in the user task entity and the respective entities themselves like `userTaskKey` in the user task entity. - The full entity name is used as the prefix to avoid confusion, for example, `processDefinitionKey` instead of `processKey`, which could be interpreted as a process instance or process definition. - Other entity attributes do not have a prefix to avoid clutter, such as `version` in the process definition entity. However, references to other resources require a prefix, like `processDefinitionVersion` in the process instance entity. ### HTTP status codes & error handling The Orchestration Cluster REST API uses standard HTTP status codes and returns error responses following the [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) format. Each error response includes the following fields: - `type`: A URI identifier for the error type - `status`: The HTTP status code (e.g., 400, 404) - `title`: A short, human-readable summary of the error - `detail`: A detailed explanation of the issue - `instance`: A URI reference identifying the specific occurrence of the problem #### Common error codes | Error code | Meaning | | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 200 | OK | | 204 | No content | | 400 | Bad request. Generic error with further description in the problem detail. | | 401 | Unauthorized. The client is not authenticated. Retry with a modified authorization header. | | 403 | Forbidden. The client has insufficient permissions for the request. | | 404 | Not found | | 409 | Conflict. The request attempts to modify a resource that is not in the correct state. | | 412 | Precondition failed. The client should check the cluster status. | | 500 | Internal server error. Generic error with further description in the problem detail. | | 503 | The service is currently unavailable. This may happen when the system signals backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism [here](../../components/zeebe/technical-concepts/internal-processing.md) | ### Date formats Date values in the Orchestration Cluster REST API follow the [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339) notation. This applies to all requests and responses. The endpoints validate requests and transform responses accordingly. ### Variables Variables in the Orchestration Cluster REST API are JSON objects, where `key` defines the variable name and `value` specifies the variable value. For full details on variable filtering and structure, see [search requests](orchestration-cluster-api-rest-data-fetching.md#variables). ## What's next? Now that you're familiar with the Orchestration Cluster REST API, here are some useful next steps: - [Build a job worker using the Spring SDK](../camunda-spring-boot-starter/getting-started.md) - [Test your process definitions using Camunda Process Test](../testing/getting-started.md) - [Migrate from v1 component REST APIs to the v2 Orchestration Cluster REST API](../migration-manuals/migrate-to-camunda-api.md) - [Download the OpenAPI spec](https://github.com/camunda/camunda/blob/main/zeebe/gateway-protocol/src/main/proto/rest-api.yaml) to generate a client or explore the raw schema --- ## Try with Swagger The Orchestration Cluster REST API is documented using the [OpenAPI specification](https://github.com/camunda/camunda/blob/main/zeebe/gateway-protocol/src/main/proto/rest-api.yaml). You can explore, test, and interact with all API endpoints directly using [Swagger UI](https://swagger.io/tools/swagger-ui/) - an interactive documentation interface. **Use Swagger UI to:** - Explore available endpoints - Browse all REST API operations with detailed parameter descriptions - Test API calls interactively - Execute real API requests directly from your browser - Understand request/response formats - See example payloads and response schemas - Authenticate and authorise - Test with your actual credentials in a secure environment Swagger UI is particularly useful for API discovery, development, and testing workflows before integrating the API into your applications. ## Prerequisites Before using Swagger UI, ensure you have: - **A running Camunda 8 Orchestration Cluster:** SaaS or Self-Managed - **Appropriate [access permissions](../../components/concepts/access-control/authorizations.md)** to the resources you want to manage via the API (if authorizations are enabled). ## Accessing Swagger UI ### SaaS For SaaS clusters, Swagger UI is accessible through your cluster's dedicated endpoint. 1. In the Camunda Console, go to your cluster 2. In **Cluster Details**, find your **Region ID** and **Cluster ID** 3. Use this URL format: `https://${REGION_ID}.api.camunda.io/${CLUSTER_ID}/swagger` :::note Swagger UI is protected with CSRF. If you are logged into the Camunda Console, you can access Swagger UI directly. If not, you may need to log in first. ::: **Example:** If your Region ID is `bru-2` and Cluster ID is `abc123-def456-ghi789`, your Swagger UI URL would be: `https://bru-2.api.camunda.io/abc123-def456-ghi789/swagger` ### Self-Managed For Self-Managed deployments, Swagger UI is available at your configured [Zeebe Gateway](/reference/glossary.md#zeebe-gateway) endpoint. **Default setup:** `http://localhost:8080/swagger` **Custom configuration:** Use the host and path defined for your Zeebe Gateway in the [configuration guide](/self-managed/deployment/helm/configure/ingress/ingress-setup.md), then append `/swagger`. **Example with custom domain:** `https://your-zeebe-gateway.company.com/swagger` ## Authentication in Swagger UI Swagger UI supports the same authentication methods as the REST API. Choose the method that matches your deployment: ### Automatic authentication - **Session-based authentication**: If you're already logged into Camunda (for example, through Operate), Swagger UI automatically authenticates you using your session cookie ### Manual authentication Click the **Authorize** button in Swagger UI to manually configure authentication: #### Bearer Token (Recommended for production) 1. Click **Authorize** in Swagger UI 2. In the **Bearer** section, enter your JWT access token 3. Click **Authorize** to apply **To obtain a Bearer token:** - **SaaS**: Follow the [OIDC-based Authentication guide](./orchestration-cluster-api-rest-authentication.md#oidc-access-token-authentication-using-client-credentials) for SaaS - **Self-Managed**: Follow the [OIDC-based Authentication guide](./orchestration-cluster-api-rest-authentication.md#oidc-access-token-authentication-using-client-credentials) for Self-Managed #### Basic Authentication 1. Click **Authorize** in Swagger UI 2. In the **Basic** section, enter your username and password 3. Click **Authorize** to apply **Note:** Basic Authentication is only available for Self-Managed deployments. For detailed authentication setup instructions, see the [Authentication guide](./orchestration-cluster-api-rest-authentication.md). ## Using Swagger UI effectively ### Making your first API call 1. **Test connectivity**: Try the `GET /topology` endpoint to verify your connection and authentication 2. **Explore endpoints**: Browse the available operations organized by category (processes, user tasks, variables, etc.) 3. **Try sample requests**: Click "Try it out" on any endpoint to see the request form 4. **Execute requests**: Fill in parameters and click "Execute" to see real responses ### Understanding the interface - **Endpoints are grouped by resource type** - Find process-related operations under "Process Instances", task operations under "User Tasks", etc. - **Required parameters are marked** - Look for the red asterisk (\*) next to required fields - **Example values are provided** - Use the "Example Value" links to populate request bodies quickly - **Response schemas are documented** - Expand the response sections to understand the data structure ### Testing workflows Use Swagger UI to test complete workflows: 1. **Deploy a process** - Use `POST /deployments` to upload a BPMN file 2. **Start a process instance** - Use `POST /process-instances` with your process definition 3. **Query and manage** - Use search endpoints to find and interact with your data 4. **Complete tasks** - Use `POST /user-tasks/{userTaskKey}/completion` to progress workflows ## Managing Swagger UI availability ### SaaS Control Swagger UI access through the Camunda Console: 1. Navigate to your cluster in the Camunda Console 2. Go to **Cluster Settings** 3. Toggle **Enable Swagger** on or off 4. Changes apply automatically to your orchestration cluster ### Self-Managed Configure Swagger UI availability using environment variables: **Enable Swagger UI (default):** ```bash CAMUNDA_REST_SWAGGER_ENABLED=true ``` **Disable Swagger UI:** ```bash CAMUNDA_REST_SWAGGER_ENABLED=false ``` **Alternative property format:** ```yaml camunda: rest: swagger: enabled: true ``` **Security consideration:** In production environments, consider disabling Swagger UI and using it only in development environments. ## Next steps After exploring the API with Swagger UI: - **Build production integrations** using the [Java client](/apis-tools/java-client/getting-started.md) or [Camunda Spring Boot Starter](/apis-tools/camunda-spring-boot-starter/getting-started.md) - **Review the complete API reference** in the [Overview](./orchestration-cluster-api-rest-overview.md) - **Set up proper authentication** following the [Authentication guide](./orchestration-cluster-api-rest-authentication.md) - **Learn advanced querying** with [Data Fetching and Search](./orchestration-cluster-api-rest-data-fetching.md) - **Download the OpenAPI specification** for [custom client generation](https://github.com/camunda/camunda/blob/main/zeebe/gateway-protocol/src/main/proto/rest-api.yaml) **Need help?** Try the [Getting Started Tutorial](/guides/getting-started-example.md) for a complete workflow walkthrough, or browse the [Postman collection](https://www.postman.com/camundateam/camunda-8-postman/collection/apl78x9/camunda-8-api-rest) for additional examples. --- ## Beginner tutorial Beginner In this tutorial, we'll step through examples to highlight the capabilities of the Orchestration Cluster REST API, such as listing all roles, creating a role, retrieving a role, and deleting a role. ## Prerequisites - If you haven't done so already, [create a cluster](/components/hub/organization/manage-clusters/create-cluster.md). - Upon cluster creation, [create your first client](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client). Ensure you check the `Orchestration Cluster API` client scope box. :::note Make sure you keep the generated client credentials in a safe place. The **Client secret** will not be shown again. For your convenience, you can download the client information to your computer. ::: - In this tutorial, we utilize a JavaScript-written [GitHub repository](https://github.com/camunda/camunda-api-tutorials) to write and run requests. Clone this repo before getting started. - Ensure you have [Node.js](https://nodejs.org/en/download) installed as this will be used for methods that can be called by the CLI (outlined later in this guide). Run `npm install` to ensure you have updated dependencies. ## Getting started - You need authentication to access the API endpoints. Find more information [here](./orchestration-cluster-api-rest-authentication.md). ## Set up authentication If you're interested in how we use a library to handle auth for our code, or to get started, examine the `auth.js` file in the GitHub repository. This file contains a function named `getAccessToken` which executes an OAuth 2.0 protocol to retrieve authentication credentials based on your client ID and client secret. Then, we return the actual token that can be passed as an authorization header in each request. To set up your credentials, create an `.env` file which will be protected by the `.gitignore` file. You will need to add the following: - `CAMUNDA_CLIENT_ID` - `CAMUNDA_CLIENT_SECRET` - `CAMUNDA_REST_ADDRESS` (after creating a client and downloading the .env variables, this is reflected in the Console UI as `ZEEBE_REST_ADDRESS`) - `CAMUNDA_TOKEN_AUDIENCE` (represented as `ZEEBE_TOKEN_AUDIENCE` in the Console UI), which is `zeebe.camunda.io` in a Camunda 8 SaaS environment. For example, your audience may be defined as `CAMUNDA_TOKEN_AUDIENCE=zeebe.camunda.io`. These keys will be consumed by the `auth.js` file to execute the OAuth protocol, and should be saved when you generate your client credentials in [prerequisites](#prerequisites). Examine the existing `.env.example` file for an example of how your `.env` file should look upon completion. Do not place your credentials in the `.env.example` file, as this example file is not protected by the `.gitignore`. :::note In this tutorial, we will execute arguments to list all roles, create a role, retrieve a role, and delete a role. You can examine the framework for processing these arguments in the `cli.js` file before getting started. ::: ## List all roles (POST) First, let's script an API call to list all existing roles. To do this, take the following steps: 1. In the file named `camunda-8.js`, outline the authentication configuration in the first few lines. This will pull in your `.env` variables to obtain an access token before making any API calls: ```javascript const authorizationConfiguration = { clientId: process.env.CAMUNDA_CLIENT_ID, clientSecret: process.env.CAMUNDA_CLIENT_SECRET, audience: process.env.CAMUNDA_TOKEN_AUDIENCE, }; ``` 2. Examine the function `async function listRoles()` below this configuration. This is where you will script out your API call. 3. Within the function, you must first generate an access token for this request, so your function should now look like the following: ```javascript async function listRoles() { const accessToken = await getAccessToken(authorizationConfiguration); } ``` 4. Using your generated client credentials from [prerequisites](#prerequisites), capture your Orchestration Cluster REST API URL beneath your call for an access token by defining `camundaApiUrl`: ```javascript const camundaApiUrl = process.env.CAMUNDA_REST_ADDRESS; ``` On the next line, script the API endpoint to list the existing roles: ```javascript const url = `${camundaApiUrl}/roles/search`; ``` 5. Configure your POST request to the appropriate endpoint, including an authorization header based on the previously acquired `accessToken`: ```javascript const options = { method: "POST", url, headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, }, data: {}, }; ``` 6. Call the endpoint, process the results from the API call, and emit an error message from the server if necessary: ```javascript try { const response = await axios(options); const results = response.data; results.items.forEach((x) => console.log(`Role Name: ${x.name}; key: ${x.key}`) ); } catch (error) { console.error(error.message); } ``` 7. In your terminal, run `node cli.js camunda8 list`. :::note This `list` command is connected to the `listRoles` function at the bottom of the `camunda-8.js` file, and executed by the `cli.js` file. While we will work with roles in this tutorial, you may add additional arguments depending on the API calls you would like to make. ::: The existing roles (if any) will now output. If you have an invalid API name or action name, or no arguments provided, or improper/insufficient credentials configured, an error message will output as outlined in the `cli.js` file. If no action is provided, it will default to "assign" everywhere, except when unassigning a user. ## Create a role (POST) 1. Outline your function: ```javascript async function createRole([roleName]) { const accessToken = await getAccessToken(authorizationConfiguration); const camundaApiUrl = process.env.CAMUNDA_REST_ADDRESS; const url = `${camundaApiUrl}/roles`; } ``` 2. Configure the API call: ```javascript const options = { method: "POST", url, headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, }, data: { name: roleName, }, }; ``` 3. Process the results: ```javascript try { const response = await axios(options); const newRole = response.data; console.log(`Role added! Name: ${roleName}. Key: ${newRole.roleKey}.`); } catch (error) { console.error(error.message); } ``` 4. Run in your terminal `node cli.js camunda8 create `. ## Retrieve a role (GET) 1. Outline your function: ```javascript async function getRole([roleKey]) { const accessToken = await getAccessToken(authorizationConfiguration); const camundaApiUrl = process.env.CAMUNDA_REST_ADDRESS; const url = `${camundaApiUrl}/roles/${roleKey}`; } ``` 2. Configure the API call. ```javascript const options = { method: "GET", url, headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, }, }; ``` 3. Process the results: ```javascript try { const response = await axios(options); const results = response.data; console.log(`Role Name: ${results.name}; Key: ${results.key};`); } catch (error) { console.error(error.message); } ``` 4. Run in your terminal `node cli.js camunda8 view `. ## Delete a role (DELETE) 1. Outline your function: ```javascript async function deleteRole([roleKey]) { const accessToken = await getAccessToken(authorizationConfiguration); const camundaApiUrl = process.env.CAMUNDA_REST_ADDRESS; const url = `${camundaApiUrl}/roles/${roleKey}`; } ``` 2. Configure the API call: ```javascript const options = { method: "DELETE", url, headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, }, }; ``` 3. Process the results: ```javascript try { const response = await axios(options); if (response.status === 204) { console.log("Role deleted!"); } else { console.error("Unable to delete this role!"); } } catch (error) { console.error(error.message); } ``` 4. Run in your terminal `node cli.js camunda8 delete `. ## If you get stuck Having trouble configuring your API calls or want to examine an example of the completed tutorial? Navigate to the `completed` folder in the [GitHub repository](https://github.com/camunda/camunda-api-tutorials/tree/main/completed), where you can view an example `camunda-8.js` file. ## Next steps You can script several additional API calls as outlined in the [Orchestration Cluster REST API reference material](./orchestration-cluster-api-rest-overview.md). --- ## Processes MCP Server ## About The Processes MCP Server is a capability of the Orchestration Cluster that exposes your deployed BPMN processes as callable tools through the [Model Context Protocol](https://modelcontextprotocol.io/) (MCP). - Any process equipped with an [MCP start event](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-start-event.md) element template is automatically registered as an MCP tool when deployed. - MCP clients discover these tools at runtime and invoke them by name. Each invocation starts a new process instance and returns the started process instance key immediately. - The server shares the same [authentication](../orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md) and [authorization](/components/concepts/access-control/authorizations.md) model as the Orchestration Cluster REST API. :::important Camunda 8 public API The Processes MCP Server is not part of the [Camunda 8 public API](/reference/public-api.md). ::: :::note This is the Processes MCP Server documentation. If you are looking to: - Give AI agents access to Camunda's operational capabilities, such as incidents, user tasks, and process instances, see the [Orchestration Cluster MCP Server](../orchestration-cluster-api-mcp/orchestration-cluster-api-mcp-overview.md). - Connect an AI agent running inside a BPMN process to an external MCP server, see the [MCP Client connector](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-client.md). ::: ### Key features | Feature | Description | | :------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Process tool registration | Processes with an MCP start event are automatically registered as MCP tools on deployment. | | Tool discovery | MCP clients discover available process tools and their schemas at runtime. | | Static tools | The server also exposes a set of [static tools](#static-tools) for inspecting running process instances. | | Version binding | Only the latest deployed version of a process is exposed. See [Version binding](./processes-mcp-version-binding.md). | | Standard transport | Uses [Streamable HTTP](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#streamable-http), compatible with any MCP-compliant client. | ### Authentication The Processes MCP Server uses the same authentication model as the [Orchestration Cluster REST API](../orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md). OAuth tokens obtained for the REST API work without changes. For the full authentication reference, including SaaS and Self-Managed setup, see [Authentication](../orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md). ### Transport The Processes MCP Server uses [Streamable HTTP](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#streamable-http) and is served at the `/mcp/processes` endpoint on the Orchestration Cluster. It is stateless; no session management is required. ## Get started :::important Camunda 8.10 The Processes MCP Server is only available from Camunda 8.10 onwards. ::: To expose a BPMN process as an MCP tool, see [Expose a process as an MCP tool](/components/agentic-orchestration/expose-process-as-mcp-tool.md). If you have a local Orchestration Cluster running with [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) or [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md), the Processes MCP Server is enabled by default. Connect any MCP client using this configuration: ```json { "servers": { "camunda-processes": { "type": "http", "url": "http://localhost:8080/mcp/processes" } } } ``` For production environments and other deployment types, the Processes MCP Server must be explicitly enabled before use. See [Enable and connect](./processes-mcp-setup.md) for more details. ## Static tools In addition to dynamically registered process tools, the Processes MCP Server exposes a set of static tools for inspecting the process instances it starts. See [static tools](./processes-mcp-static-tools.md) for more details. --- ## Enable and connect(Processes-mcp) Enable the Processes MCP Server and configure MCP clients to connect. ## Enable the Processes MCP Server The Processes MCP Server is enabled by default in [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) and [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md). For other deployment types, it must be explicitly enabled before MCP clients can connect. Depending on your deployment, enable it as follows: The Processes MCP Server is **enabled by default** in Camunda 8 Run. No additional configuration is needed. The Processes MCP Server is **enabled by default** in the Docker Compose distribution. No additional configuration is needed. Set the following [`extraConfiguration`](/self-managed/deployment/helm/configure/application-configs.md#configuration-options) value in your Helm chart values: ```yaml orchestration: extraConfiguration: - file: mcp-gateway.yaml content: | camunda: mcp: enabled: true ``` In the Camunda Console, navigate to your cluster, open **Cluster Settings**, and enable **MCP Support**. :::info MCP server support is available on SaaS clusters running Camunda 8.10.0 or later. ::: For a full reference of MCP configuration properties, see [Property reference](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#api---mcp). ## Connect an MCP client Once the Processes MCP Server is enabled, you can connect any MCP-compliant client. The approach depends on your client's capabilities and authentication requirements. :::important When you [create API client credentials](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client) in the Camunda Console, all required connection details, including the base URL, OAuth endpoint, client ID, and audience, are displayed on the credentials page. ::: ### MCP endpoint URL The Processes MCP Server is served at `/mcp/processes` on the Orchestration Cluster. The full endpoint URL depends on your deployment type: | Deployment | MCP endpoint URL | | :------------------------- | :-------------------------------------------------------------------------------- | | Camunda 8 Run | `http://localhost:8080/mcp/processes` | | Docker Compose | `http://localhost:8080/mcp/processes` | | SaaS – public connectivity | `https://${REGION_ID}.api.camunda.io/${CLUSTER_ID}/mcp/processes` | | SaaS – secure connectivity | `https://${CLUSTER_ID}.${REGION_ID}.privateconnectivity.camunda.io/mcp/processes` | | Self-Managed (custom) | `https:///mcp/processes` | For SaaS, find your **Region Id** and **Cluster Id** in the Camunda Console under **Cluster Details**. ### Direct HTTP connection If your Orchestration Cluster does not require authentication, for example, when running locally with [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) or [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md), you can connect directly to the MCP server endpoint without any additional tooling. Any MCP client that supports [Streamable HTTP](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#streamable-http) can be used. For authenticated environments, [use c8ctl `mcp-proxy`](#use-c8ctl-mcp-proxy) instead. ```json { "servers": { "camunda-processes": { "type": "http", "url": "http://localhost:8080/mcp/processes" } } } ``` ### Use c8ctl `mcp-proxy` Many MCP clients, such as VS Code (GitHub Copilot) and Claude Code, do not natively support the OAuth 2.0 client credentials flow required for authenticated environments. The [c8ctl](https://github.com/camunda/c8ctl) `mcp-proxy` command bridges this gap by providing a local STDIO-to-Remote HTTP proxy that handles authentication transparently. The proxy authenticates to the MCP server using OAuth 2.0 client credentials, and exposes a local STDIO MCP interface that your client connects to. #### Prerequisites - [Node.js](https://nodejs.org/) 18 or later. - [Client credentials](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client) for your Camunda cluster with the **Orchestration Cluster API** scope enabled. #### Configuration Add the following to your MCP client configuration. For example, use the following in `claude_desktop_config.json` for Claude Code: ```json { "mcpServers": { "camunda-processes": { "type": "stdio", "command": "npx", "args": ["-y", "@camunda8/cli", "mcp-proxy"], "env": { "CAMUNDA_BASE_URL": "https://", "CAMUNDA_CLIENT_ID": "", "CAMUNDA_CLIENT_SECRET": "", "CAMUNDA_OAUTH_URL": "https:///oauth/token", "CAMUNDA_TOKEN_AUDIENCE": "" } } } } ``` | Variable | Description | | :----------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `CAMUNDA_BASE_URL` | Base URL of your Orchestration Cluster, **without** the `/mcp/processes` path (for example, the public `api.camunda.io` URL or the private `privateconnectivity.camunda.io` URL when using Secure connectivity). | | `CAMUNDA_CLIENT_ID` | OAuth client ID from your API client credentials. | | `CAMUNDA_CLIENT_SECRET` | OAuth client secret from your API client credentials. | | `CAMUNDA_OAUTH_URL` | OAuth token endpoint URL. | | `CAMUNDA_TOKEN_AUDIENCE` | Token audience for the Orchestration Cluster API. | :::tip Where to find these values When you [create API client credentials](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client) in the Camunda Console, all required connection details are displayed on the credentials page. You can also copy a ready-to-use c8ctl configuration snippet directly from the **MCP** tab on the credentials screen. ::: For the full list of supported environment variables, see the [c8ctl documentation](https://github.com/camunda/c8ctl). ### Use with the MCP Client connectors You can also connect to the Processes MCP Server from within a BPMN process using Camunda's [MCP Client connectors](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-client.md). This allows an AI agent running in an agentic orchestration workflow to invoke your deployed processes as MCP tools. The [MCP Remote Client connector](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-remote-client-connector.md) connects to remote MCP servers over HTTP. Configure it in the properties panel with the following settings: - **Transport type**: Streamable HTTP. - **URL**: Your MCP endpoint URL (see [above](#mcp-endpoint-url)). - **Authentication**: OAuth 2.0. | Field | Value | | :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------ | | OAuth 2.0 token endpoint | Your OAuth token endpoint (`https://login.cloud.camunda.io/oauth/token` for SaaS). | | Client ID | Your OAuth client ID. | | Client secret | Your OAuth client secret. Use [secrets](/components/hub/organization/manage-clusters/manage-secrets.md) (for example, `{{secrets.MCP_CLIENT_SECRET}}`). | | Audience | The audience for your cluster API (`zeebe.camunda.io` for SaaS). | | Client authentication | Send client credentials in body. | For more details, see [MCP Remote Client connector](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-remote-client-connector.md). The [MCP Client connector](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-client-connector.md) manages persistent MCP connections through the connector runtime. Configure the Processes MCP Server as a remote HTTP client in your connector runtime configuration (for example, `application.yml`): ```yaml camunda: connector: agenticai: mcp: client: enabled: true clients: camunda-processes: type: http http: url: https://${REGION_ID}.api.camunda.io/${CLUSTER_ID}/mcp/processes authentication: type: oauth oauth: oauth-token-endpoint: https://login.cloud.camunda.io/oauth/token client-id: client-secret: audience: zeebe.camunda.io client-authentication: credentials-body ``` The example above shows a SaaS configuration using the public endpoint. For clusters with Secure connectivity (AWS PrivateLink), set `url` to the private MCP endpoint URL shown in Camunda Console instead of the public `zeebe.camunda.io` host (the path still ends with `/mcp/processes`). For local unauthenticated setups, you can omit the `authentication` block and use `http://localhost:8080/mcp/processes` as the URL. Reference the client ID `camunda-processes` in the MCP Client connector element template in your BPMN process. For more details, see [MCP Client connector](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-client-connector.md). --- ## Static tools The Processes MCP Server exposes the following static tools alongside dynamically registered process tools. These tools let an MCP agent inspect the process instance it just started, including its variables, state, and incidents, without switching to a different MCP server. | Tool | Description | | :------------------- | :---------------------------------------------------------- | | `searchVariables` | Search and inspect variables of a running process instance. | | `getProcessInstance` | Retrieve the state and details of a process instance. | | `searchIncidents` | Inspect incidents raised on a running process instance. | :::note These tools are a subset of the [Orchestration Cluster MCP Server tools](../orchestration-cluster-api-mcp/orchestration-cluster-api-mcp-tools.md). Refer to that page for full parameter and response schema details. ::: --- ## Version binding Understand how the Processes MCP Server handles process version binding, stale tool references after redeployment, and best practices for managing breaking changes. ## Which version is exposed The Processes MCP Server always exposes only the **latest deployed version** of a process. When you deploy a new version of a process, it replaces the previous version's tool registration. There is no mechanism to pin an MCP client to a specific process version. ## Stale tool references MCP clients typically cache the tool list after connecting. If a client cached the tool list before a redeployment and then attempts to call the now-replaced tool, it receives an error instructing it to refresh its tool list by running tool discovery again. MCP clients must therefore: 1. Handle the stale-tool error gracefully. 2. Re-fetch the tool list by running tool discovery again before retrying. ## Implications for process owners Redeploying a process with a changed interface, such as a different tool name, input parameters, or output variables, is a breaking change for any MCP client currently holding a reference to that tool. To reduce disruption: - Communicate planned redeployments to teams operating MCP clients that use your process tool. - When making a significant interface change, consider deploying a new process with a different tool name rather than redeploying over the existing one. This lets existing clients continue using the old version while new clients adopt the updated tool. --- ## CamundaAsyncClient ## CamundaAsyncClient ```python class CamundaAsyncClient(configuration=None, auth_provider=None, logger=None, **kwargs) ``` Bases: `object` - **Parameters:** - **configuration** ([_CamundaSdkConfiguration_](runtime.md#camunda_orchestration_sdk.runtime.configuration_resolver.CamundaSdkConfiguration)) - **auth_provider** ([_AuthProvider_](runtime.md#camunda_orchestration_sdk.runtime.auth.AuthProvider)) - **logger** ([_CamundaLogger_](runtime.md#camunda_orchestration_sdk.runtime.logging.CamundaLogger) _|_ _None_) - **kwargs** (_Any_) ### aclose() ```python async def aclose() ``` Close underlying HTTP clients. This closes both the API client’s async httpx client and, when available, the auth provider’s token client. - **Return type:** None ### activate_ad_hoc_sub_process_activities() ```python async def activate_ad_hoc_sub_process_activities(ad_hoc_sub_process_instance_key, , data, **kwargs) ``` Activate activities within an ad-hoc sub-process > Activates selected activities within an ad-hoc sub-process identified by element ID. The provided element IDs must exist within the ad-hoc sub-process instance identified by the provided adHocSubProcessInstanceKey. - **Parameters:** - **ad_hoc_sub_process_instance_key** (_str_) – System-generated key for a element instance. Example: 2251799813686789. - **body** (_AdHocSubProcessActivateActivitiesInstruction_) - **data** (_AdHocSubProcessActivateActivitiesInstruction_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The ad-hoc sub-process instance is not found or the provided key does not identify an ad-hoc sub-process. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Activate ad-hoc sub-process activities:** ```python def activate_ad_hoc_sub_process_activities_example(element_id: ElementId) -> None: client = CamundaClient() client.activate_ad_hoc_sub_process_activities( ad_hoc_sub_process_instance_key="123456", data=AdHocSubProcessActivateActivitiesInstruction( elements=[ AdHocSubProcessActivateActivityReference(element_id=element_id), AdHocSubProcessActivateActivityReference(element_id=element_id), ], ), ) ``` ### activate_jobs() ```python async def activate_jobs(, data, **kwargs) ``` Activate jobs > Iterate through all known partitions and activate jobs up to the requested maximum. - **Parameters:** - **body** (_JobActivationRequest_) - **data** (_JobActivationRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** JobActivationResult - **Return type:** JobActivationResult #### Examples **Activate and process jobs:** ```python async def activate_jobs_example() -> None: async with CamundaAsyncClient() as client: result = await client.activate_jobs( data=JobActivationRequest( type_="payment-processing", timeout=30000, max_jobs_to_activate=5, ) ) for job in result.jobs: print(f"Job {job.job_key}: {job.type_}") ``` ### assign_client_to_group() ```python async def assign_client_to_group(group_id, client_id, **kwargs) ``` Assign a client to a group > Assigns a client to a group, making it a member of the group. Members of the group inherit the group authorizations, roles, and tenant assignments. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **client_id** (_str_) – The unique identifier of an OAuth client. Minted outside the Camunda REST API: in SaaS by Console, in Self-Managed with OIDC by the external identity provider (e.g. EntraID, Keycloak, Okta). In Self-Managed with Basic authentication, machine-to-machine applications are modelled as users instead — see the user identifier. > Example: my-application. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The group with the given ID was not found. - **errors.ConflictError** – If the response status code is 409. The client with the given ID is already assigned to the group. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a client to a group:** ```python def assign_client_to_group_example(group_id: GroupId, client_id: ClientId) -> None: client = CamundaClient() client.assign_client_to_group( group_id=group_id, client_id=client_id, ) ``` ### assign_client_to_tenant() ```python async def assign_client_to_tenant(tenant_id, client_id, **kwargs) ``` Assign a client to a tenant > Assign the client to the specified tenant. The client can then access tenant data and perform authorized actions. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **client_id** (_str_) – The unique identifier of an OAuth client. Minted outside the Camunda REST API: in SaaS by Console, in Self-Managed with OIDC by the external identity provider (e.g. EntraID, Keycloak, Okta). In Self-Managed with Basic authentication, machine-to-machine applications are modelled as users instead — see the user identifier. > Example: my-application. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The tenant was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a client to a tenant:** ```python def assign_client_to_tenant_example(tenant_id: TenantId, client_id: ClientId) -> None: client = CamundaClient() client.assign_client_to_tenant( tenant_id=tenant_id, client_id=client_id, ) ``` ### assign_group_to_tenant() ```python async def assign_group_to_tenant(tenant_id, group_id, **kwargs) ``` Assign a group to a tenant > Assigns a group to a specified tenant. Group members (users, clients) can then access tenant data and perform authorized actions. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The tenant or group was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a group to a tenant:** ```python def assign_group_to_tenant_example(tenant_id: TenantId, group_id: GroupId) -> None: client = CamundaClient() client.assign_group_to_tenant( tenant_id=tenant_id, group_id=group_id, ) ``` ### assign_mapping_rule_to_group() ```python async def assign_mapping_rule_to_group(group_id, mapping_rule_id, **kwargs) ``` Assign a mapping rule to a group > Assigns a mapping rule to a group. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **mapping_rule_id** (_str_) – The unique identifier of a mapping rule. Example: my-mapping-rule. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The group or mapping rule with the given ID was not found. - **errors.ConflictError** – If the response status code is 409. The mapping rule with the given ID is already assigned to the group. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a mapping rule to a group:** ```python def assign_mapping_rule_to_group_example(group_id: GroupId, mapping_rule_id: MappingRuleId) -> None: client = CamundaClient() client.assign_mapping_rule_to_group( group_id=group_id, mapping_rule_id=mapping_rule_id, ) ``` ### assign_mapping_rule_to_tenant() ```python async def assign_mapping_rule_to_tenant(tenant_id, mapping_rule_id, **kwargs) ``` Assign a mapping rule to a tenant > Assign a single mapping rule to a specified tenant. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **mapping_rule_id** (_str_) – The unique identifier of a mapping rule. Example: my-mapping-rule. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The tenant or mapping rule was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a mapping rule to a tenant:** ```python def assign_mapping_rule_to_tenant_example(tenant_id: TenantId, mapping_rule_id: MappingRuleId) -> None: client = CamundaClient() client.assign_mapping_rule_to_tenant( tenant_id=tenant_id, mapping_rule_id=mapping_rule_id, ) ``` ### assign_role_to_client() ```python async def assign_role_to_client(role_id, client_id, **kwargs) ``` Assign a role to a client > Assigns the specified role to the client. The client will inherit the authorizations associated with this role. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **client_id** (_str_) – The unique identifier of an OAuth client. Minted outside the Camunda REST API: in SaaS by Console, in Self-Managed with OIDC by the external identity provider (e.g. EntraID, Keycloak, Okta). In Self-Managed with Basic authentication, machine-to-machine applications are modelled as users instead — see the user identifier. > Example: my-application. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role with the given ID was not found. - **errors.ConflictError** – If the response status code is 409. The role was already assigned to the client with the given ID. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a role to a client:** ```python def assign_role_to_client_example(role_id: RoleId, client_id: ClientId) -> None: client = CamundaClient() client.assign_role_to_client( role_id=role_id, client_id=client_id, ) ``` ### assign_role_to_group() ```python async def assign_role_to_group(role_id, group_id, **kwargs) ``` Assign a role to a group > Assigns the specified role to the group. Every member of the group (user or client) will inherit the authorizations associated with this role. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role or group with the given ID was not found. - **errors.ConflictError** – If the response status code is 409. The role is already assigned to the group with the given ID. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a role to a group:** ```python def assign_role_to_group_example(role_id: RoleId, group_id: GroupId) -> None: client = CamundaClient() client.assign_role_to_group( role_id=role_id, group_id=group_id, ) ``` ### assign_role_to_mapping_rule() ```python async def assign_role_to_mapping_rule(role_id, mapping_rule_id, **kwargs) ``` Assign a role to a mapping rule > Assigns a role to a mapping rule. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **mapping_rule_id** (_str_) – The unique identifier of a mapping rule. Example: my-mapping-rule. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role or mapping rule with the given ID was not found. - **errors.ConflictError** – If the response status code is 409. The role is already assigned to the mapping rule with the given ID. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a role to a mapping rule:** ```python def assign_role_to_mapping_rule_example(role_id: RoleId, mapping_rule_id: MappingRuleId) -> None: client = CamundaClient() client.assign_role_to_mapping_rule( role_id=role_id, mapping_rule_id=mapping_rule_id, ) ``` ### assign_role_to_tenant() ```python async def assign_role_to_tenant(tenant_id, role_id, **kwargs) ``` Assign a role to a tenant > Assigns a role to a specified tenant. Users, Clients or Groups, that have the role assigned, will get access to the tenant’s data and can perform actions according to their authorizations. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The tenant or role was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a role to a tenant:** ```python def assign_role_to_tenant_example(tenant_id: TenantId, role_id: RoleId) -> None: client = CamundaClient() client.assign_role_to_tenant( tenant_id=tenant_id, role_id=role_id, ) ``` ### assign_role_to_user() ```python async def assign_role_to_user(role_id, username, **kwargs) ``` Assign a role to a user > Assigns the specified role to the user. The user will inherit the authorizations associated with this role. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **username** (_str_) – The unique name of a user. Example: swillis. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role or user with the given ID or username was not found. - **errors.ConflictError** – If the response status code is 409. The role is already assigned to the user with the given ID. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a role to a user:** ```python def assign_role_to_user_example(role_id: RoleId, username: Username) -> None: client = CamundaClient() client.assign_role_to_user( role_id=role_id, username=username, ) ``` ### assign_user_task() ```python async def assign_user_task(user_task_key, , data, **kwargs) ``` Assign user task > Assigns a user task with the given key to the given assignee. Assignment waits for blocking task listeners on this lifecycle transition. If listener processing is delayed beyond the request timeout, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. - **Parameters:** - **user_task_key** (_str_) – System-generated key for a user task. - **body** (_UserTaskAssignmentRequest_) - **data** (_UserTaskAssignmentRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The user task with the given key was not found. - **errors.ConflictError** – If the response status code is 409. The user task with the given key is in the wrong state currently. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.GatewayTimeoutError** – If the response status code is 504. The request timed out between the gateway and the broker. For these endpoints, this often happens when user task listeners are configured and the corresponding listener job is not completed within the request timeout. Common causes include no available job workers for the listener type, busy or crashed job workers, or delayed job completion. As with any gateway timeout, general timeout causes (for example transient network issues) can also result in a 504 response. Troubleshooting: - verify that job workers for the listener type are running and healthy - check worker logs for crashes, retries, and completion failures - check network connectivity between workers, gateway, and broker - retry with backoff after transient failures - fail without retries if a problem persists - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a user task:** ```python def assign_user_task_example(user_task_key: UserTaskKey) -> None: client = CamundaClient() client.assign_user_task( user_task_key=user_task_key, data=UserTaskAssignmentRequest( assignee="user@example.com", ), ) ``` ### assign_user_to_group() ```python async def assign_user_to_group(group_id, username, **kwargs) ``` Assign a user to a group > Assigns a user to a group, making the user a member of the group. Group members inherit the group authorizations, roles, and tenant assignments. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **username** (_str_) – The unique name of a user. Example: swillis. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The group or user with the given ID or username was not found. - **errors.ConflictError** – If the response status code is 409. The user with the given ID is already assigned to the group. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a user to a group:** ```python def assign_user_to_group_example(group_id: GroupId, username: Username) -> None: client = CamundaClient() client.assign_user_to_group( group_id=group_id, username=username, ) ``` ### assign_user_to_tenant() ```python async def assign_user_to_tenant(tenant_id, username, **kwargs) ``` Assign a user to a tenant > Assign a single user to a specified tenant. The user can then access tenant data and perform authorized actions. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **username** (_str_) – The unique name of a user. Example: swillis. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The tenant or user was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a user to a tenant:** ```python def assign_user_to_tenant_example(tenant_id: TenantId, username: Username) -> None: client = CamundaClient() client.assign_user_to_tenant( tenant_id=tenant_id, username=username, ) ``` ### auth_provider ```python auth_provider: [AuthProvider](runtime.md#camunda_orchestration_sdk.runtime.auth.AuthProvider) ``` ### broadcast_signal() ```python async def broadcast_signal(, data, **kwargs) ``` Broadcast signal > Broadcasts a signal. - **Parameters:** - **body** (_SignalBroadcastRequest_) - **data** (_SignalBroadcastRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The signal is not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** SignalBroadcastResult - **Return type:** SignalBroadcastResult #### Examples **Broadcast a signal:** ```python def broadcast_signal_example() -> None: client = CamundaClient() result = client.broadcast_signal( data=SignalBroadcastRequest( signal_name="order-cancelled", ) ) print(f"Signal key: {result.signal_key}") ``` ### cancel_batch_operation() ```python async def cancel_batch_operation(batch_operation_key, *, data=, **kwargs) ``` Cancel Batch operation > Cancels a running batch operation. This is done asynchronously, the progress can be tracked using the batch operation status endpoint (/batch-operations/{batchOperationKey}). - **Parameters:** - **batch_operation_key** (_str_) – System-generated key for an batch operation. Example: 2251799813684321. - **body** (_Any_ _|_ _Unset_) - **data** (_Any_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The batch operation was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Cancel a batch operation:** ```python def cancel_batch_operation_example(batch_operation_key: BatchOperationKey) -> None: client = CamundaClient() client.cancel_batch_operation( batch_operation_key=batch_operation_key, ) ``` ### cancel_process_instance() ```python async def cancel_process_instance(process_instance_key, *, data=, **kwargs) ``` Cancel process instance > Cancels a running process instance. As a cancellation includes more than just the removal of the process instance resource, the cancellation resource must be posted. Cancellation can wait on listener-related processing; when that processing does not complete in time, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. - **Parameters:** - **process_instance_key** (_str_) – System-generated key for a process instance. Example: 2251799813690746. - **body** (_CancelProcessInstanceRequest_ _|_ _None_ _|_ _Unset_) - **data** (_CancelProcessInstanceRequest_ _|_ _None_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The process instance is not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.GatewayTimeoutError** – If the response status code is 504. The request timed out between the gateway and the broker. For these endpoints, this often happens when user task listeners are configured and the corresponding listener job is not completed within the request timeout. Common causes include no available job workers for the listener type, busy or crashed job workers, or delayed job completion. As with any gateway timeout, general timeout causes (for example transient network issues) can also result in a 504 response. Troubleshooting: - verify that job workers for the listener type are running and healthy - check worker logs for crashes, retries, and completion failures - check network connectivity between workers, gateway, and broker - retry with backoff after transient failures - fail without retries if a problem persists - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Cancel a process instance:** ```python def cancel_process_instance_example(process_definition_id: ProcessDefinitionId) -> None: client = CamundaClient() # Create a process instance and get its key from the response created = client.create_process_instance( data=ProcessCreationById(process_definition_id=process_definition_id) ) # Cancel it using the key from the creation response client.cancel_process_instance( process_instance_key=created.process_instance_key, ) ``` ### cancel_process_instances_batch_operation() ```python async def cancel_process_instances_batch_operation(, data, **kwargs) ``` Cancel process instances (batch) > Cancels multiple running process instances. Since only ACTIVE root instances can be cancelled, any given filters for state and parentProcessInstanceKey are ignored and overridden during this batch operation. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). - **Parameters:** - **body** (_ProcessInstanceCancellationBatchOperationRequest_) – The process instance filter that defines which process instances should be canceled. - **data** (_ProcessInstanceCancellationBatchOperationRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The process instance batch operation failed. More details are provided in the response body. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** BatchOperationCreatedResult - **Return type:** BatchOperationCreatedResult #### Examples **Cancel process instances in batch:** ```python def cancel_process_instances_batch_operation_example() -> None: client = CamundaClient() result = client.cancel_process_instances_batch_operation( data=ProcessInstanceCancellationBatchOperationRequest( filter_=ProcessInstanceCancellationBatchOperationRequestFilter(), ), ) print(f"Batch operation key: {result.batch_operation_key}") ``` ### client ```python client: [Client](configuration.md#camunda_orchestration_sdk.Client) | [AuthenticatedClient](configuration.md#camunda_orchestration_sdk.AuthenticatedClient) ``` ### complete_job() ```python async def complete_job(job_key, *, data=, **kwargs) ``` Complete job > Complete a job with the given payload, which allows completing the associated service task. - **Parameters:** - **job_key** (_str_) – System-generated key for a job. Example: 2251799813653498. - **body** (_JobCompletionRequest_ _|_ _Unset_) - **data** (_JobCompletionRequest_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The job with the given key was not found. - **errors.ConflictError** – If the response status code is 409. The job with the given key is in the wrong state currently. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Complete a job:** ```python def complete_job_example(job_key: JobKey) -> None: client = CamundaClient() client.complete_job( job_key=job_key, data=JobCompletionRequest( variables=JobCompletionRequestVariables.from_dict( {"paymentId": "PAY-123", "status": "completed"} ) ), ) ``` ### complete_user_task() ```python async def complete_user_task(user_task_key, *, data=, **kwargs) ``` Complete user task > Completes a user task with the given key. Completion waits for blocking task listeners on this lifecycle transition. If listener processing is delayed beyond the request timeout, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. - **Parameters:** - **user_task_key** (_str_) – System-generated key for a user task. - **body** (_UserTaskCompletionRequest_ _|_ _Unset_) - **data** (_UserTaskCompletionRequest_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The user task with the given key was not found. - **errors.ConflictError** – If the response status code is 409. The user task with the given key is in the wrong state currently. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.GatewayTimeoutError** – If the response status code is 504. The request timed out between the gateway and the broker. For these endpoints, this often happens when user task listeners are configured and the corresponding listener job is not completed within the request timeout. Common causes include no available job workers for the listener type, busy or crashed job workers, or delayed job completion. As with any gateway timeout, general timeout causes (for example transient network issues) can also result in a 504 response. Troubleshooting: - verify that job workers for the listener type are running and healthy - check worker logs for crashes, retries, and completion failures - check network connectivity between workers, gateway, and broker - retry with backoff after transient failures - fail without retries if a problem persists - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Complete a user task:** ```python def complete_user_task_example(user_task_key: UserTaskKey) -> None: client = CamundaClient() variables = UserTaskCompletionRequestVariables() variables["approved"] = True client.complete_user_task( user_task_key=user_task_key, data=UserTaskCompletionRequest( variables=variables, ), ) ``` ### configuration ```python configuration: [CamundaSdkConfiguration](runtime.md#camunda_orchestration_sdk.runtime.configuration_resolver.CamundaSdkConfiguration) ``` ### correlate_message() ```python async def correlate_message(, data, **kwargs) ``` Correlate message > Publishes a message and correlates it to a subscription. If correlation is successful it will return the first process instance key the message correlated with. The message is not buffered. Use the publish message endpoint to send messages that can be buffered. - **Parameters:** - **body** (_MessageCorrelationRequest_) - **data** (_MessageCorrelationRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** MessageCorrelationResult - **Return type:** MessageCorrelationResult #### Examples **Correlate a message:** ```python def correlate_message_example() -> None: client = CamundaClient() result = client.correlate_message( data=MessageCorrelationRequest( name="payment-received", correlation_key="order-12345", ) ) print(f"Message key: {result.message_key}") ``` ### create_admin_user() ```python async def create_admin_user(, data, **kwargs) ``` Create admin user > Creates a new user and assigns the admin role to it. This endpoint is only usable when users are managed in the Orchestration Cluster and while no user is assigned to the admin role. - **Parameters:** - **body** (_UserRequest_) - **data** (_UserRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** UserCreateResult - **Return type:** UserCreateResult #### Examples **Create an admin user:** ```python def create_admin_user_example(username: Username) -> None: client = CamundaClient() result = client.create_admin_user( data=UserRequest( username=username, name="Admin User", email="admin@example.com", password="admin-password", ), ) print(f"Admin user: {result.username}") ``` ### create_agent_instance() ```python async def create_agent_instance(, data, **kwargs) ``` Create agent instance > Creates a new agent instance. The returned key identifies the instance and must be used in subsequent update and query calls. - **Parameters:** - **body** (_AgentInstanceCreationRequest_) – Request to create a new agent instance. - **data** (_AgentInstanceCreationRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The elementInstanceKey does not correspond to an active element instance. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** AgentInstanceCreationResult - **Return type:** AgentInstanceCreationResult #### Examples **Create an agent instance:** ```python def create_agent_instance_example(element_instance_key: ElementInstanceKey) -> None: client = CamundaClient() result = client.create_agent_instance( data=AgentInstanceCreationRequest( element_instance_key=element_instance_key, definition=AgentInstanceCreationRequestDefinition( model="gpt-4o", provider="openai", system_prompt="You are a helpful assistant.", ), ), ) print(f"Created agent instance: {result.agent_instance_key}") ``` ### create_authorization() ```python async def create_authorization(, data, **kwargs) ``` Create authorization > Create the authorization. - **Parameters:** - **body** (_AuthorizationIdBasedRequest_ _|_ _AuthorizationPropertyBasedRequest_) - **data** (_AuthorizationIdBasedRequest_ _|_ _AuthorizationPropertyBasedRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The owner was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** AuthorizationCreateResult - **Return type:** AuthorizationCreateResult #### Examples **Create an authorization:** ```python def create_authorization_example() -> None: client = CamundaClient() result = client.create_authorization( data=AuthorizationIdBasedRequest( resource_type=AuthorizationIdBasedRequestResourceType.PROCESS_DEFINITION, permission_types=[ AuthorizationIdBasedRequestPermissionTypesItem.READ, AuthorizationIdBasedRequestPermissionTypesItem.UPDATE, ], resource_id="my-process", owner_type=OwnerTypeEnum.USER, owner_id="user@example.com", ), ) print(f"Authorization key: {result.authorization_key}") ``` ### create_deployment() ```python async def create_deployment(, data, **kwargs) ``` Deploy resources > Deploys one or more resources (e.g. processes, decision models, or forms). This is an atomic call, i.e. either all resources are deployed or none of them are. - **Parameters:** - **body** (_CreateDeploymentData_) - **data** (_CreateDeploymentData_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** DeploymentResult - **Return type:** DeploymentResult #### Examples **From files:** ```python def deploy_resources_example() -> None: client = CamundaClient() result = client.deploy_resources_from_files( ["order-process.bpmn", "decision.dmn"] ) print(f"Deployment key: {result.deployment_key}") for process in result.processes: print( f" Process: {process.process_definition_id} v{process.process_definition_version}" ) for decision in result.decisions: print(f" Decision: {decision.decision_definition_id}") ``` **With tenant ID:** ```python def deploy_resources_with_tenant_example() -> None: client = CamundaClient() result = client.deploy_resources_from_files( ["order-process.bpmn"], tenant_id="my-tenant", ) print(f"Deployment key: {result.deployment_key}") print(f"Tenant: {result.tenant_id}") ``` ### create_document() ```python async def create_document(*, data, store_id=, document_id=, **kwargs) ``` Upload document > Upload a document to the Camunda 8 cluster. Note that this is currently supported for document stores of type: AWS, Azure, GCP, in-memory (non- production), local (non-production) - **Parameters:** - **store_id** (_str_ _|_ _Unset_) - **document_id** (_str_ _|_ _Unset_) – Document Id that uniquely identifies a document. - **body** (_CreateDocumentData_) - **data** (_CreateDocumentData_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnsupportedMediaTypeError** – If the response status code is 415. The server cannot process the request because the media type (Content-Type) of the request payload is not supported by the server for the requested resource and method. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** DocumentReference - **Return type:** DocumentReference #### Examples **Create a document:** ```python def create_document_example() -> None: client = CamundaClient() result = client.create_document( data=CreateDocumentData( file=File(payload=io.BytesIO(b"hello world"), file_name="example.txt"), ), ) print(f"Document ID: {result.document_id}") ``` ### create_document_link() ```python async def create_document_link(document_id, *, data=, store_id=, content_hash=, **kwargs) ``` Create document link > Create a link to a document in the Camunda 8 cluster. Note that this is currently supported for document stores of type: AWS, Azure, GCP - **Parameters:** - **document_id** (_str_) – Document Id that uniquely identifies a document. - **store_id** (_str_ _|_ _Unset_) - **content_hash** (_str_ _|_ _Unset_) - **body** (_DocumentLinkRequest_ _|_ _Unset_) - **data** (_DocumentLinkRequest_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** DocumentLink - **Return type:** DocumentLink #### Examples **Create a document link:** ```python def create_document_link_example(document_id: DocumentId) -> None: client = CamundaClient() result = client.create_document_link( document_id=document_id, data=DocumentLinkRequest(), ) print(f"Document link: {result.url}") ``` ### create_documents() ```python async def create_documents(*, data, store_id=, **kwargs) ``` Upload multiple documents > Upload multiple documents to the Camunda 8 cluster. The caller must provide a file name for each document, which will be used in case of a multi-status response to identify which documents failed to upload. The file name can be provided in the Content- Disposition header of the file part or in the fileName field of the metadata. You can add a parallel array of metadata objects. These are matched with the files based on index, and must have the same length as the files array. To pass homogenous metadata for all files, spread the metadata over the metadata array. A filename value provided explicitly via the metadata array in the request overrides the Content- Disposition header of the file part. In case of a multi-status response, the response body will contain a list of DocumentBatchProblemDetail objects, each of which contains the file name of the document that failed to upload and the reason for the failure. The client can choose to retry the whole batch or individual documents based on the response. Note that this is currently supported for document stores of type: AWS, Azure, GCP, in-memory (non- production), local (non-production) - **Parameters:** - **store_id** (_str_ _|_ _Unset_) - **body** (_CreateDocumentsData_) - **data** (_CreateDocumentsData_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnsupportedMediaTypeError** – If the response status code is 415. The server cannot process the request because the media type (Content-Type) of the request payload is not supported by the server for the requested resource and method. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** DocumentCreationBatchResponse - **Return type:** DocumentCreationBatchResponse #### Examples **Create documents:** ```python def create_documents_example() -> None: client = CamundaClient() result = client.create_documents( data=CreateDocumentsData( files=[ File(payload=io.BytesIO(b"file one"), file_name="one.txt"), File(payload=io.BytesIO(b"file two"), file_name="two.txt"), ], ), ) if not isinstance(result.created_documents, Unset): for doc in result.created_documents: print(f"Created document: {doc.document_id}") ``` ### create_element_instance_variables() ```python async def create_element_instance_variables(element_instance_key, , data, **kwargs) ``` Update element instance variables > Updates all the variables of a particular scope (for example, process instance, element instance) with the given variable data. Specify the element instance in the elementInstanceKey parameter. Variable updates can be delayed by listener-related processing; if processing exceeds the request timeout, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. - **Parameters:** - **element_instance_key** (_str_) – System-generated key for a element instance. Example: 2251799813686789. - **body** (_SetVariableRequest_) - **data** (_SetVariableRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.GatewayTimeoutError** – If the response status code is 504. The request timed out between the gateway and the broker. For these endpoints, this often happens when user task listeners are configured and the corresponding listener job is not completed within the request timeout. Common causes include no available job workers for the listener type, busy or crashed job workers, or delayed job completion. As with any gateway timeout, general timeout causes (for example transient network issues) can also result in a 504 response. Troubleshooting: - verify that job workers for the listener type are running and healthy - check worker logs for crashes, retries, and completion failures - check network connectivity between workers, gateway, and broker - retry with backoff after transient failures - fail without retries if a problem persists - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Create element instance variables:** ```python def create_element_instance_variables_example(element_instance_key: ElementInstanceKey) -> None: client = CamundaClient() variables = SetVariableRequestVariables.from_dict({"myVar": "myValue"}) client.create_element_instance_variables( element_instance_key=element_instance_key, data=SetVariableRequest( variables=variables, ), ) ``` ### create_global_cluster_variable() ```python async def create_global_cluster_variable(, data, **kwargs) ``` Create a global-scoped cluster variable > Create a global-scoped cluster variable. - **Parameters:** - **body** (_CreateClusterVariableRequest_) - **data** (_CreateClusterVariableRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ClusterVariableResult - **Return type:** ClusterVariableResult #### Examples **Create a global cluster variable:** ```python def create_global_cluster_variable_example(name: ClusterVariableName) -> None: client = CamundaClient() result = client.create_global_cluster_variable( data=CreateClusterVariableRequest( name=name, value=CreateClusterVariableRequestValue.from_dict({"key": "my-value"}), ), ) print(f"Created variable: {result.name}") ``` ### create_global_task_listener() ```python async def create_global_task_listener(, data, **kwargs) ``` Create global user task listener > Create a new global user task listener. - **Parameters:** - **body** (_CreateGlobalTaskListenerRequest_) - **data** (_CreateGlobalTaskListenerRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.ConflictError** – If the response status code is 409. A global listener with this id already exists. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GlobalTaskListenerResult - **Return type:** GlobalTaskListenerResult #### Examples **Create a global task listener:** ```python def create_global_task_listener_example() -> None: client = CamundaClient() result = client.create_global_task_listener( data=CreateGlobalTaskListenerRequest( id="audit-log-listener", event_types=[GlobalTaskListenerEventTypeEnum.COMPLETING], type_="my-task-listener", ), ) print(f"Task listener: {result.id}") ``` ### create_group() ```python async def create_group(*, data=, **kwargs) ``` Create group > Create a new group. The supplied groupId is validated against ^[a-zA-Z0-9_~@.+-]+$ (max 256 characters) by IdentifierValidator.validateId in the runtime. This strict validation applies wherever the Groups API is available: in OIDC deployments that set camunda.security.authentication.oidc.groupsClaim the Groups API (including this endpoint) is disabled entirely, so group CRUD never sees externally-minted IdP IDs. The BYOG relaxation only loosens validation when a group is referenced _as a member_ of a role or tenant (assignRoleToGroup, assignGroupToTenant); group CRUD itself always uses the strict default-id regex. The constraint is not advertised on the GroupId schema so that the same schema can be reused at member-reference sites without falsely rejecting externally-minted IdP group IDs there. - **Parameters:** - **body** (_GroupCreateRequest_ _|_ _Unset_) - **data** (_GroupCreateRequest_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GroupCreateResult - **Return type:** GroupCreateResult #### Examples **Create a group:** ```python def create_group_example(group_id: GroupId) -> None: client = CamundaClient() result = client.create_group( data=GroupCreateRequest(group_id=group_id, name="Engineering"), ) print(f"Group: {result.group_id}") ``` ### create_job_worker(config: [WorkerConfig](runtime.md#camunda_orchestration_sdk.runtime.job_worker.WorkerConfig), callback: Callable[[[ConnectedJobContext](runtime.md#camunda_orchestration_sdk.runtime.job_worker.ConnectedJobContext)], Coroutine[Any, Any, dict[str, Any] | JobCompletionRequest | None]] | Callable[[[SyncJobContext](runtime.md#camunda_orchestration_sdk.runtime.job_worker.SyncJobContext)], dict[str, Any] | JobCompletionRequest | None], auto_start: bool = True, , execution_strategy: Literal['auto', 'async', 'thread'] = 'auto', startup_jitter_max_seconds: float | None = None) → [JobWorker](runtime.md#camunda_orchestration_sdk.runtime.job_worker.JobWorker) ### create_job_worker(config: [WorkerConfig](runtime.md#camunda_orchestration_sdk.runtime.job_worker.WorkerConfig), callback: Callable[[[JobContext](runtime.md#camunda_orchestration_sdk.runtime.job_worker.JobContext)], Coroutine[Any, Any, dict[str, Any] | JobCompletionRequest | None]] | Callable[[[JobContext](runtime.md#camunda_orchestration_sdk.runtime.job_worker.JobContext)], dict[str, Any] | JobCompletionRequest | None], auto_start: bool = True, , execution_strategy: Literal['process'], startup_jitter_max_seconds: float | None = None) → [JobWorker](runtime.md#camunda_orchestration_sdk.runtime.job_worker.JobWorker) - **Parameters:** - **config** ([_WorkerConfig_](runtime.md#camunda_orchestration_sdk.runtime.job_worker.WorkerConfig)) - **callback** (_Callable_ _[_ _[_[_ConnectedJobContext_](runtime.md#camunda_orchestration_sdk.runtime.job_worker.ConnectedJobContext) _]_ _,_ _Coroutine_ _[\*\*Any_ _,_ _Any_ _,_ _dict_ _[\*\*str_ _,_ _Any_ _]_ _|_ _JobCompletionRequest_ _|_ _None_ _]_ _]_ _|_ _Callable_ _[_ _[_[_SyncJobContext_](runtime.md#camunda_orchestration_sdk.runtime.job_worker.SyncJobContext) _]_ _,_ _dict_ _[\*\*str_ _,_ _Any_ _]_ _|_ _JobCompletionRequest_ _|_ _None_ _]_ _|_ _Callable_ _[_ _[_[_JobContext_](runtime.md#camunda_orchestration_sdk.runtime.job_worker.JobContext) _]_ _,_ _Coroutine_ _[\*\*Any_ _,_ _Any_ _,_ _dict_ _[\*\*str_ _,_ _Any_ _]_ _|_ _JobCompletionRequest_ _|_ _None_ _]_ _]_ _|_ _Callable_ _[_ _[_[_JobContext_](runtime.md#camunda_orchestration_sdk.runtime.job_worker.JobContext) _]_ _,_ _dict_ _[\*\*str_ _,_ _Any_ _]_ _|_ _JobCompletionRequest_ _|_ _None_ _]_) - **auto_start** (_bool_) - **execution_strategy** (_Literal_ _[_ _'auto'_ _,_ _'async'_ _,_ _'thread'_ _,_ _'process'_ _]_) - **startup_jitter_max_seconds** (_float_ _|_ _None_) - **Return type:** [_JobWorker_](runtime.md#camunda_orchestration_sdk.runtime.job_worker.JobWorker) ### create_mapping_rule() ```python async def create_mapping_rule(*, data=, **kwargs) ``` Create mapping rule > Create a new mapping rule - **Parameters:** - **body** (_MappingRuleCreateRequest_ _|_ _Unset_) - **data** (_MappingRuleCreateRequest_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. The request to create a mapping rule was denied. More details are provided in the response body. - **errors.NotFoundError** – If the response status code is 404. The request to create a mapping rule was denied. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** MappingRuleCreateResult - **Return type:** MappingRuleCreateResult #### Examples **Create a mapping rule:** ```python def create_mapping_rule_example(mapping_rule_id: MappingRuleId) -> None: client = CamundaClient() result = client.create_mapping_rule( data=MappingRuleCreateRequest( mapping_rule_id=mapping_rule_id, claim_name="groups", claim_value="engineering", name="Engineering Group Mapping", ), ) print(f"Mapping rule: {result.mapping_rule_id}") ``` ### create_process_instance() ```python async def create_process_instance(, data, **kwargs) ``` Create process instance > Creates and starts an instance of the specified process. The process definition to use to create the instance can be specified either using its unique key (as returned by Deploy resources), or using the BPMN process id and a version. Waits for the completion of the process instance before returning a result when awaitCompletion is enabled. - **Parameters:** - **body** (_ProcessCreationById_ _|_ _ProcessCreationByKey_) – Instructions for creating a process instance. The process definition can be specified either by id or by key. - **data** (_ProcessCreationById_ _|_ _ProcessCreationByKey_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ConflictError** – If the response status code is 409. The process instance creation was rejected due to a business ID uniqueness conflict. This can happen only when Business ID Uniqueness Control is enabled and an active root process instance with the provided business ID already exists for the same process definition and tenant. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.GatewayTimeoutError** – If the response status code is 504. The process instance creation request timed out in the gateway. This can happen if the awaitCompletion request parameter is set to true and the created process instance did not complete within the defined request timeout. This often happens when the created instance is not fully automated or contains wait states. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** CreateProcessInstanceResult - **Return type:** CreateProcessInstanceResult #### Examples **By key:** ```python def create_process_instance_by_key_example() -> None: client = CamundaClient() # Deploy a process and obtain the typed key from the response deployment = client.deploy_resources_from_files(["order-process.bpmn"]) process_key = deployment.processes[0].process_definition_key # Use the typed key directly — no manual string lifting needed result = client.create_process_instance( data=ProcessCreationByKey( process_definition_key=process_key, ) ) print(f"Process instance key: {result.process_instance_key}") ``` **By stored key:** ```python def create_process_instance_by_key_from_storage_example() -> None: client = CamundaClient() # When restoring a key from a database or message queue, # wrap the raw string with the semantic type constructor: stored_key = "2251799813685249" # e.g. from a DB row result = client.create_process_instance( data=ProcessCreationByKey( process_definition_key=ProcessDefinitionKey(stored_key), ) ) print(f"Process instance key: {result.process_instance_key}") ``` **By ID:** ```python def create_process_instance_by_id_example(process_definition_id: ProcessDefinitionId) -> None: client = CamundaClient() result = client.create_process_instance( data=ProcessCreationById( process_definition_id=process_definition_id, ) ) print(f"Process instance key: {result.process_instance_key}") ``` ### create_role() ```python async def create_role(*, data=, **kwargs) ``` Create role > Create a new role. - **Parameters:** - **body** (_RoleCreateRequest_ _|_ _Unset_) - **data** (_RoleCreateRequest_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** RoleCreateResult - **Return type:** RoleCreateResult #### Examples **Create a role:** ```python def create_role_example(role_id: RoleId) -> None: client = CamundaClient() result = client.create_role( data=RoleCreateRequest(role_id=role_id, name="Developer"), ) print(f"Role: {result.role_id}") ``` ### create_tenant() ```python async def create_tenant(, data, **kwargs) ``` Create tenant > Creates a new tenant. - **Parameters:** - **body** (_TenantCreateRequest_) - **data** (_TenantCreateRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The resource was not found. - **errors.ConflictError** – If the response status code is 409. Tenant with this id already exists. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** TenantCreateResult - **Return type:** TenantCreateResult #### Examples **Create a tenant:** ```python def create_tenant_example(tenant_id: TenantId) -> None: client = CamundaClient() result = client.create_tenant( data=TenantCreateRequest( tenant_id=tenant_id, name="Acme Corporation", ), ) print(f"Tenant: {result.tenant_id}") ``` ### create_tenant_cluster_variable() ```python async def create_tenant_cluster_variable(tenant_id, , data, **kwargs) ``` Create a tenant-scoped cluster variable > Create a new cluster variable for the given tenant. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **body** (_CreateClusterVariableRequest_) - **data** (_CreateClusterVariableRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The tenant with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ClusterVariableResult - **Return type:** ClusterVariableResult #### Examples **Create a tenant cluster variable:** ```python def create_tenant_cluster_variable_example(tenant_id: TenantId, name: ClusterVariableName) -> None: client = CamundaClient() result = client.create_tenant_cluster_variable( tenant_id=tenant_id, data=CreateClusterVariableRequest( name=name, value=CreateClusterVariableRequestValue.from_dict({"key": "tenant-value"}), ), ) print(f"Created variable: {result.name}") ``` ### create_user() ```python async def create_user(, data, **kwargs) ``` Create user > Create a new user. - **Parameters:** - **body** (_UserRequest_) - **data** (_UserRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.ConflictError** – If the response status code is 409. A user with this username already exists. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** UserCreateResult - **Return type:** UserCreateResult #### Examples **Create a user:** ```python def create_user_example(username: Username) -> None: client = CamundaClient() result = client.create_user( data=UserRequest( username=username, name="Jane Doe", email="jdoe@example.com", password="secure-password", ), ) print(f"Created user: {result.username}") ``` ### delete_authorization() ```python async def delete_authorization(authorization_key, **kwargs) ``` Delete authorization > Deletes the authorization with the given key. - **Parameters:** - **authorization_key** (_str_) – System-generated key for an authorization. Example: 2251799813684332. - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.NotFoundError** – If the response status code is 404. The authorization with the authorizationKey was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete an authorization:** ```python def delete_authorization_example(authorization_key: AuthorizationKey) -> None: client = CamundaClient() client.delete_authorization( authorization_key=authorization_key, ) ``` ### delete_decision_instance() ```python async def delete_decision_instance(decision_evaluation_key, *, data=, **kwargs) ``` Delete decision instance > Delete all associated decision evaluations based on provided key. - **Parameters:** - **decision_evaluation_key** (_str_) – System-generated key for a decision evaluation. Example: 2251792362345323. - **body** (_DeleteDecisionInstanceRequest_ _|_ _None_ _|_ _Unset_) - **data** (_DeleteDecisionInstanceRequest_ _|_ _None_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The decision instance is not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete a decision instance:** ```python def delete_decision_instance_example(decision_evaluation_key: DecisionEvaluationKey) -> None: client = CamundaClient() client.delete_decision_instance( decision_evaluation_key=decision_evaluation_key, ) ``` ### delete_decision_instances_batch_operation() ```python async def delete_decision_instances_batch_operation(, data, **kwargs) ``` Delete decision instances (batch) > Delete multiple decision instances. This will delete the historic data from secondary storage. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). - **Parameters:** - **body** (_DecisionInstanceDeletionBatchOperationRequest_) – The decision instance filter that defines which decision instances should be deleted. - **data** (_DecisionInstanceDeletionBatchOperationRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The decision instance batch operation failed. More details are provided in the response body. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** BatchOperationCreatedResult - **Return type:** BatchOperationCreatedResult #### Examples **Delete decision instances in batch:** ```python def delete_decision_instances_batch_operation_example() -> None: client = CamundaClient() result = client.delete_decision_instances_batch_operation( data=DecisionInstanceDeletionBatchOperationRequest( filter_=DecisionInstanceDeletionBatchOperationRequestFilter(), ), ) print(f"Batch operation key: {result.batch_operation_key}") ``` ### delete_document() ```python async def delete_document(document_id, *, store_id=, **kwargs) ``` Delete document > Delete a document from the Camunda 8 cluster. Note that this is currently supported for document stores of type: AWS, Azure, GCP, in-memory (non- production), local (non-production) - **Parameters:** - **document_id** (_str_) – Document Id that uniquely identifies a document. - **store_id** (_str_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.NotFoundError** – If the response status code is 404. The document with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete a document:** ```python def delete_document_example(document_id: DocumentId) -> None: client = CamundaClient() client.delete_document(document_id=document_id) ``` ### delete_global_cluster_variable() ```python async def delete_global_cluster_variable(name, **kwargs) ``` Delete a global-scoped cluster variable > Delete a global-scoped cluster variable. - **Parameters:** - **name** (_str_) – The name of a cluster variable. Unique within its scope (global or tenant- specific). Example: feature-flag-checkout. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Cluster variable not found - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete a global cluster variable:** ```python def delete_global_cluster_variable_example(name: ClusterVariableName) -> None: client = CamundaClient() client.delete_global_cluster_variable(name=name) ``` ### delete_global_task_listener() ```python async def delete_global_task_listener(id, **kwargs) ``` Delete global user task listener > Deletes a global user task listener. - **Parameters:** - **id** (_str_) – The user-defined id for the global listener Example: GlobalListener_1. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The global user task listener was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete a global task listener:** ```python def delete_global_task_listener_example(listener_id: GlobalListenerId) -> None: client = CamundaClient() client.delete_global_task_listener(id=listener_id) ``` ### delete_group() ```python async def delete_group(group_id, **kwargs) ``` Delete group > Deletes the group with the given ID. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.NotFoundError** – If the response status code is 404. The group with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete a group:** ```python def delete_group_example(group_id: GroupId) -> None: client = CamundaClient() client.delete_group(group_id=group_id) ``` ### delete_mapping_rule() ```python async def delete_mapping_rule(mapping_rule_id, **kwargs) ``` Delete a mapping rule > Deletes the mapping rule with the given ID. - **Parameters:** - **mapping_rule_id** (_str_) – The unique identifier of a mapping rule. Example: my-mapping-rule. - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.NotFoundError** – If the response status code is 404. The mapping rule with the mappingRuleId was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete a mapping rule:** ```python def delete_mapping_rule_example(mapping_rule_id: MappingRuleId) -> None: client = CamundaClient() client.delete_mapping_rule(mapping_rule_id=mapping_rule_id) ``` ### delete_process_instance() ```python async def delete_process_instance(process_instance_key, *, data=, **kwargs) ``` Delete process instance > Deletes a process instance. Only instances that are completed or terminated can be deleted. - **Parameters:** - **process_instance_key** (_str_) – System-generated key for a process instance. Example: 2251799813690746. - **body** (_DeleteProcessInstanceRequest_ _|_ _None_ _|_ _Unset_) - **data** (_DeleteProcessInstanceRequest_ _|_ _None_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The process instance is not found. - **errors.ConflictError** – If the response status code is 409. The process instance is not in a completed or terminated state and cannot be deleted. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete a process instance:** ```python def delete_process_instance_example(process_instance_key: ProcessInstanceKey) -> None: client = CamundaClient() client.delete_process_instance( process_instance_key=process_instance_key, ) ``` ### delete_process_instances_batch_operation() ```python async def delete_process_instances_batch_operation(, data, **kwargs) ``` Delete process instances (batch) > Delete multiple process instances. This will delete the historic data from secondary storage. Only process instances in a final state (COMPLETED or TERMINATED) can be deleted. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). - **Parameters:** - **body** (_ProcessInstanceDeletionBatchOperationRequest_) – The process instance filter that defines which process instances should be deleted. - **data** (_ProcessInstanceDeletionBatchOperationRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The process instance batch operation failed. More details are provided in the response body. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** BatchOperationCreatedResult - **Return type:** BatchOperationCreatedResult #### Examples **Delete process instances in batch:** ```python def delete_process_instances_batch_operation_example() -> None: client = CamundaClient() result = client.delete_process_instances_batch_operation( data=ProcessInstanceDeletionBatchOperationRequest( filter_=ProcessInstanceCancellationBatchOperationRequestFilter(), ), ) print(f"Batch operation key: {result.batch_operation_key}") ``` ### delete_resource() ```python async def delete_resource(resource_key, *, data=, **kwargs) ``` Delete resource > Deletes a deployed resource. This can be a process definition, decision requirements definition, or form definition deployed using the deploy resources endpoint. Specify the resource you want to delete in the resourceKey parameter. Once a resource has been deleted it cannot be recovered. If the resource needs to be available again, a new deployment of the resource is required. By default, only the resource itself is deleted from the runtime state. To also delete the historic data associated with a resource, set the deleteHistory flag in the request body to true. The historic data is deleted asynchronously via a batch operation. The details of the created batch operation are included in the response. Note that history deletion is only supported for process resources; for other resource types this flag is ignored and no history will be deleted. - **Parameters:** - **resource_key** (_str_) – The system-assigned key for this resource. - **body** (_DeleteResourceRequest_ _|_ _None_ _|_ _Unset_) - **data** (_DeleteResourceRequest_ _|_ _None_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The resource is not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** DeleteResourceResponse - **Return type:** DeleteResourceResponse #### Examples **Delete a resource:** ```python def delete_resource_example() -> None: client = CamundaClient() # Use a resource key from a previous deployment response client.delete_resource(resource_key="2251799813685249") ``` ### delete_role() ```python async def delete_role(role_id, **kwargs) ``` Delete role > Deletes the role with the given ID. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.NotFoundError** – If the response status code is 404. The role with the ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete a role:** ```python def delete_role_example(role_id: RoleId) -> None: client = CamundaClient() client.delete_role(role_id=role_id) ``` ### delete_tenant() ```python async def delete_tenant(tenant_id, **kwargs) ``` Delete tenant > Deletes an existing tenant. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The tenant was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete a tenant:** ```python def delete_tenant_example(tenant_id: TenantId) -> None: client = CamundaClient() client.delete_tenant(tenant_id=tenant_id) ``` ### delete_tenant_cluster_variable() ```python async def delete_tenant_cluster_variable(tenant_id, name, **kwargs) ``` Delete a tenant-scoped cluster variable > Delete a tenant-scoped cluster variable. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **name** (_str_) – The name of a cluster variable. Unique within its scope (global or tenant- specific). Example: feature-flag-checkout. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Cluster variable not found - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete a tenant cluster variable:** ```python def delete_tenant_cluster_variable_example(tenant_id: TenantId, name: ClusterVariableName) -> None: client = CamundaClient() client.delete_tenant_cluster_variable( tenant_id=tenant_id, name=name, ) ``` ### delete_user() ```python async def delete_user(username, **kwargs) ``` Delete user > Deletes a user. - **Parameters:** - **username** (_str_) – The unique name of a user. Example: swillis. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The user is not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete a user:** ```python def delete_user_example(username: Username) -> None: client = CamundaClient() client.delete_user(username=username) ``` ### deploy_resources_from_files() ```python async def deploy_resources_from_files(files, tenant_id=None) ``` Deploy BPMN/DMN/Form resources from local files. Async variant of [`CamundaClient.deploy_resources_from_files()`](client.md#camunda_orchestration_sdk.CamundaClient.deploy_resources_from_files). This reads each file path in `files` as bytes, wraps them into `camunda_orchestration_sdk.types.File`, calls [`create_deployment()`](#create_deployment), and returns an `ExtendedDeploymentResult`. Note: file reads are currently performed using blocking I/O (`open(...).read()`). If you need fully non-blocking file access, load the bytes yourself and call [`create_deployment()`](#create_deployment). - **Parameters:** - **files** (_list_ _[\*\*str_ _|_ _Path_ _]_) – File paths (`str` or `Path`) to deploy. - **tenant_id** (_str_ _|_ _None_) – Optional tenant identifier. If not provided, the default tenant is used. - **Returns:** The deployment result with extracted resource lists. - **Return type:** ExtendedDeploymentResult - **Raises:** - **FileNotFoundError** – If any file path does not exist. - **PermissionError** – If any file path cannot be read. - **IsADirectoryError** – If any file path is a directory. - **OSError** – For other I/O failures while reading files. - **Exception** – Propagates any exception raised by [`create_deployment()`](#create_deployment) (including typed API errors in `camunda_orchestration_sdk.errors` and `httpx.TimeoutException`). ### evaluate_conditionals() ```python async def evaluate_conditionals(, data, **kwargs) ``` Evaluate root level conditional start events > Evaluates root-level conditional start events for process definitions. If the evaluation is successful, it will return the keys of all created process instances, along with their associated process definition key. Multiple root-level conditional start events of the same process definition can trigger if their conditions evaluate to true. - **Parameters:** - **body** (_ConditionalEvaluationInstruction_) - **data** (_ConditionalEvaluationInstruction_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. The client is not authorized to start process instances for the specified process definition. If a processDefinitionKey is not provided, this indicates that the client is not authorized to start process instances for at least one of the matched process definitions. - **errors.NotFoundError** – If the response status code is 404. The process definition was not found for the given processDefinitionKey. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** EvaluateConditionalResult - **Return type:** EvaluateConditionalResult #### Examples **Evaluate conditionals:** ```python def evaluate_conditionals_example() -> None: client = CamundaClient() result = client.evaluate_conditionals( data=ConditionalEvaluationInstruction( variables=ConditionalEvaluationInstructionVariables.from_dict({"orderReady": True}), ), ) print(f"Result: {result}") ``` ### evaluate_decision() ```python async def evaluate_decision(, data, **kwargs) ``` Evaluate decision > Evaluates a decision. You specify the decision to evaluate either by using its unique key (as returned by DeployResource), or using the decision ID. When using the decision ID, the latest deployed version of the decision is used. - **Parameters:** - **body** (_DecisionEvaluationByID_ _|_ _DecisionEvaluationByKey_) - **data** (_DecisionEvaluationByID_ _|_ _DecisionEvaluationByKey_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The decision is not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** EvaluateDecisionResult - **Return type:** EvaluateDecisionResult #### Examples **By key:** ```python def evaluate_decision_by_key_example(decision_definition_key: DecisionDefinitionKey) -> None: client = CamundaClient() result = client.evaluate_decision( data=DecisionEvaluationByKey( decision_definition_key=decision_definition_key, ) ) print(f"Decision key: {result.decision_definition_key}") ``` **By ID:** ```python def evaluate_decision_by_id_example(decision_definition_id: DecisionDefinitionId) -> None: client = CamundaClient() result = client.evaluate_decision( data=DecisionEvaluationByID( decision_definition_id=decision_definition_id, ) ) print(f"Decision key: {result.decision_definition_key}") ``` ### evaluate_expression() ```python async def evaluate_expression(, data, **kwargs) ``` Evaluate an expression > Evaluates a FEEL expression and returns the result. Supports references to tenant scoped cluster variables when a tenant ID is provided. - **Parameters:** - **body** (_ExpressionEvaluationRequest_) - **data** (_ExpressionEvaluationRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ExpressionEvaluationResult - **Return type:** ExpressionEvaluationResult #### Examples **Evaluate an expression:** ```python def evaluate_expression_example() -> None: client = CamundaClient() result = client.evaluate_expression( data=ExpressionEvaluationRequest( expression="= 1 + 2", ), ) print(f"Result: {result.result}") ``` ### fail_job() ```python async def fail_job(job_key, *, data=, **kwargs) ``` Fail job > Mark the job as failed. - **Parameters:** - **job_key** (_str_) – System-generated key for a job. Example: 2251799813653498. - **body** (_JobFailRequest_ _|_ _Unset_) - **data** (_JobFailRequest_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The job with the given jobKey is not found. It was completed by another worker, or the process instance itself was canceled. - **errors.ConflictError** – If the response status code is 409. The job with the given key is in the wrong state (i.e: not ACTIVATED or ACTIVATABLE). The job was failed by another worker with retries = 0, and the process is now in an incident state. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Fail a job with retry:** ```python def fail_job_example(job_key: JobKey) -> None: client = CamundaClient() client.fail_job( job_key=job_key, data=JobFailRequest( retries=2, error_message="Payment gateway timeout", retry_back_off=5000, ), ) ``` ### get_agent_instance() ```python async def get_agent_instance(agent_instance_key, , consistency=None, **kwargs) ``` Get agent instance > Returns agent instance as JSON. - **Parameters:** - **agent_instance_key** (_str_) – System-generated key for an agent instance. Example: 4503599627370496. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The agent instance with the given key was not found. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** AgentInstanceResult - **Return type:** AgentInstanceResult #### Examples **Get an agent instance:** ```python def get_agent_instance_example(agent_instance_key: AgentInstanceKey) -> None: client = CamundaClient() agent_instance = client.get_agent_instance(agent_instance_key=agent_instance_key) print(f"Agent instance status: {agent_instance.status}") ``` ### get_audit_log() ```python async def get_audit_log(audit_log_key, , consistency=None, **kwargs) ``` Get audit log > Get an audit log entry by auditLogKey. - **Parameters:** - **audit_log_key** (_str_) – System-generated key for an audit log entry. Example: 22517998136843567. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The audit log with the given key was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** AuditLogResult - **Return type:** AuditLogResult #### Examples **Get an audit log entry:** ```python def get_audit_log_example(audit_log_key: AuditLogKey) -> None: client = CamundaClient() result = client.get_audit_log(audit_log_key=audit_log_key) print(f"Audit log: {result.audit_log_key}") ``` ### get_authentication() ```python async def get_authentication(**kwargs) ``` Get current user > Retrieves the current authenticated user. - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** CamundaUserResult - **Parameters:** **kwargs** (_Any_) - **Return type:** CamundaUserResult #### Examples **Get authentication info:** ```python def get_authentication_example() -> None: client = CamundaClient() result = client.get_authentication() print(f"Authenticated user: {result.username}") ``` ### get_authorization() ```python async def get_authorization(authorization_key, , consistency=None, **kwargs) ``` Get authorization > Get authorization by the given key. - **Parameters:** - **authorization_key** (_str_) – System-generated key for an authorization. Example: 2251799813684332. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The authorization with the given key was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** AuthorizationResult - **Return type:** AuthorizationResult #### Examples **Get an authorization:** ```python def get_authorization_example(authorization_key: AuthorizationKey) -> None: client = CamundaClient() result = client.get_authorization( authorization_key=authorization_key, ) print(f"Resource type: {result.resource_type}") ``` ### get_batch_operation() ```python async def get_batch_operation(batch_operation_key, , consistency=None, **kwargs) ``` Get batch operation > Get batch operation by key. - **Parameters:** - **batch_operation_key** (_str_) – System-generated key for an batch operation. Example: 2251799813684321. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The batch operation is not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** BatchOperationResponse - **Return type:** BatchOperationResponse #### Examples **Get a batch operation:** ```python def get_batch_operation_example(batch_operation_key: BatchOperationKey) -> None: client = CamundaClient() result = client.get_batch_operation( batch_operation_key=batch_operation_key, ) print(f"Batch operation: {result.batch_operation_key}") ``` ### get_decision_definition() ```python async def get_decision_definition(decision_definition_key, , consistency=None, **kwargs) ``` Get decision definition > Returns a decision definition by key. - **Parameters:** - **decision_definition_key** (_str_) – System-generated key for a decision definition. Example: 2251799813326547. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The decision definition with the given key was not found. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** DecisionDefinitionResult - **Return type:** DecisionDefinitionResult #### Examples **Get a decision definition:** ```python def get_decision_definition_example(decision_definition_key: DecisionDefinitionKey) -> None: client = CamundaClient() definition = client.get_decision_definition( decision_definition_key=decision_definition_key, ) print(f"Decision: {definition.decision_definition_id}") ``` ### get_decision_definition_xml() ```python async def get_decision_definition_xml(decision_definition_key, , consistency=None, **kwargs) ``` Get decision definition XML > Returns decision definition as XML. - **Parameters:** - **decision_definition_key** (_str_) – System-generated key for a decision definition. Example: 2251799813326547. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The decision definition with the given key was not found. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** str - **Return type:** str #### Examples **Get decision definition XML:** ```python def get_decision_definition_xml_example(decision_definition_key: DecisionDefinitionKey) -> None: client = CamundaClient() xml = client.get_decision_definition_xml( decision_definition_key=decision_definition_key, ) print(f"XML length: {len(xml)}") ``` ### get_decision_instance() ```python async def get_decision_instance(decision_evaluation_instance_key, , consistency=None, **kwargs) ``` Get decision instance > Returns a decision instance. - **Parameters:** - **decision_evaluation_instance_key** (_str_) – System-generated identifier for a decision evaluation instance. It is composed of the parent decision evaluation key and the 1-based index of the evaluated decision within that evaluation, joined by a hyphen (format: -). > Example: 2251799813684367-1. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The decision instance with the given key was not found. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** DecisionInstanceGetQueryResult - **Return type:** DecisionInstanceGetQueryResult #### Examples **Get a decision instance:** ```python def get_decision_instance_example(decision_evaluation_instance_key: DecisionEvaluationInstanceKey) -> None: client = CamundaClient() result = client.get_decision_instance( decision_evaluation_instance_key=decision_evaluation_instance_key, ) print(f"Decision instance: {result.decision_definition_id}") ``` ### get_decision_requirements() ```python async def get_decision_requirements(decision_requirements_key, , consistency=None, **kwargs) ``` Get decision requirements > Returns Decision Requirements as JSON. - **Parameters:** - **decision_requirements_key** (_str_) – System-generated key for a deployed decision requirements definition. Example: 2251799813683346. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The decision requirements with the given key was not found. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** DecisionRequirementsResult - **Return type:** DecisionRequirementsResult #### Examples **Get decision requirements:** ```python def get_decision_requirements_example(decision_requirements_key: DecisionRequirementsKey) -> None: client = CamundaClient() result = client.get_decision_requirements( decision_requirements_key=decision_requirements_key, ) print(f"DRD: {result.decision_requirements_name}") ``` ### get_decision_requirements_xml() ```python async def get_decision_requirements_xml(decision_requirements_key, , consistency=None, **kwargs) ``` Get decision requirements XML > Returns decision requirements as XML. - **Parameters:** - **decision_requirements_key** (_str_) – System-generated key for a deployed decision requirements definition. Example: 2251799813683346. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The decision requirements with the given key was not found. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** str - **Return type:** str #### Examples **Get decision requirements XML:** ```python def get_decision_requirements_xml_example(decision_requirements_key: DecisionRequirementsKey) -> None: client = CamundaClient() xml = client.get_decision_requirements_xml( decision_requirements_key=decision_requirements_key, ) print(f"XML length: {len(xml)}") ``` ### get_document() ```python async def get_document(document_id, *, store_id=, content_hash=, **kwargs) ``` Download document > Download a document from the Camunda 8 cluster. Note that this is currently supported for document stores of type: AWS, Azure, GCP, in-memory (non- production), local (non-production) - **Parameters:** - **document_id** (_str_) – Document Id that uniquely identifies a document. - **store_id** (_str_ _|_ _Unset_) - **content_hash** (_str_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.NotFoundError** – If the response status code is 404. The document with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** File - **Return type:** File #### Examples **Get a document:** ```python def get_document_example(document_id: DocumentId) -> None: client = CamundaClient() result = client.get_document(document_id=document_id) print(f"File name: {result.file_name}") ``` ### get_element_instance() ```python async def get_element_instance(element_instance_key, , consistency=None, **kwargs) ``` Get element instance > Returns element instance as JSON. - **Parameters:** - **element_instance_key** (_str_) – System-generated key for a element instance. Example: 2251799813686789. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The element instance with the given key was not found. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ElementInstanceResult - **Return type:** ElementInstanceResult #### Examples **Get an element instance:** ```python def get_element_instance_example(element_instance_key: ElementInstanceKey) -> None: client = CamundaClient() result = client.get_element_instance( element_instance_key=element_instance_key, ) print(f"Element: {result.element_id}") ``` ### get_form_by_key() ```python async def get_form_by_key(form_key, , consistency=None, **kwargs) ``` Get form by key > Get a form by its unique form key. - **Parameters:** - **form_key** (_str_) – System-generated key for a deployed form. Example: 2251799813684365. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The form with the given key was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** FormResult - **Return type:** FormResult #### Examples **Get a form by key:** ```python def get_form_by_key_example(form_key: FormKey) -> None: client = CamundaClient() result = client.get_form_by_key(form_key=form_key) print(f"Form: {result.form_id}") ``` ### get_global_cluster_variable() ```python async def get_global_cluster_variable(name, , consistency=None, **kwargs) ``` Get a global-scoped cluster variable > Get a global-scoped cluster variable. - **Parameters:** - **name** (_str_) – The name of a cluster variable. Unique within its scope (global or tenant- specific). Example: feature-flag-checkout. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Cluster variable not found - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ClusterVariableResult - **Return type:** ClusterVariableResult #### Examples **Get a global cluster variable:** ```python def get_global_cluster_variable_example(name: ClusterVariableName) -> None: client = CamundaClient() result = client.get_global_cluster_variable(name=name) print(f"Variable: {result.name} = {result.value}") ``` ### get_global_job_statistics() ```python async def get_global_job_statistics(*, from_, to, job_type=, consistency=None, **kwargs) ``` Global job statistics > Returns global aggregated counts for jobs. Filter by the creation time window (required) and optionally by jobType. - **Parameters:** - **from** (_datetime.datetime_) - **to** (_datetime.datetime_) - **job_type** (_str_ _|_ _Unset_) - **from\_** (_datetime.datetime_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GlobalJobStatisticsQueryResult - **Return type:** GlobalJobStatisticsQueryResult #### Examples **Get global job statistics:** ```python def get_global_job_statistics_example() -> None: client = CamundaClient() result = client.get_global_job_statistics( from_=datetime.datetime(2024, 1, 1), to=datetime.datetime(2024, 12, 31), ) print(f"Global job stats: {result}") ``` ### get_global_task_listener() ```python async def get_global_task_listener(id, , consistency=None, **kwargs) ``` Get global user task listener > Get a global user task listener by its id. - **Parameters:** - **id** (_str_) – The user-defined id for the global listener Example: GlobalListener_1. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The global user task listener with the given id was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GlobalTaskListenerResult - **Return type:** GlobalTaskListenerResult #### Examples **Get a global task listener:** ```python def get_global_task_listener_example(listener_id: GlobalListenerId) -> None: client = CamundaClient() result = client.get_global_task_listener(id=listener_id) print(f"Task listener: {result.event_types}") ``` ### get_group() ```python async def get_group(group_id, , consistency=None, **kwargs) ``` Get group > Get a group by its ID. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The group with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GroupResult - **Return type:** GroupResult #### Examples **Get a group:** ```python def get_group_example(group_id: GroupId) -> None: client = CamundaClient() result = client.get_group(group_id=group_id) print(f"Group: {result.name}") ``` ### get_incident() ```python async def get_incident(incident_key, , consistency=None, **kwargs) ``` Get incident > Returns incident as JSON. - **Parameters:** - **incident_key** (_str_) – System-generated key for a incident. Example: 2251799813689432. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The incident with the given key was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** IncidentResult - **Return type:** IncidentResult #### Examples **Get an incident:** ```python def get_incident_example(incident_key: IncidentKey) -> None: client = CamundaClient() incident = client.get_incident(incident_key=incident_key) print(f"Incident error type: {incident.error_type}") ``` ### get_job_error_statistics() ```python async def get_job_error_statistics(, data, consistency=None, **kwargs) ``` Get error metrics for a job type > Returns aggregated metrics per error for the given jobType. - **Parameters:** - **body** (_JobErrorStatisticsQuery_) – Job error statistics query. - **data** (_JobErrorStatisticsQuery_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** JobErrorStatisticsQueryResult - **Return type:** JobErrorStatisticsQueryResult #### Examples **Get job error statistics:** ```python def get_job_error_statistics_example() -> None: client = CamundaClient() result = client.get_job_error_statistics( data=JobErrorStatisticsQuery( filter_=JobErrorStatisticsFilter( from_=datetime.datetime(2024, 1, 1), to=datetime.datetime(2024, 12, 31), job_type="payment-processing", ), ), ) if not isinstance(result.items, Unset): for stat in result.items: print(f"Error: {stat.error_code}") ``` ### get_job_time_series_statistics() ```python async def get_job_time_series_statistics(, data, consistency=None, **kwargs) ``` Get time-series metrics for a job type > Returns a list of time-bucketed metrics ordered ascending by time. The from and to fields select the time window of interest. Each item in the response corresponds to one time bucket of the requested resolution. - **Parameters:** - **body** (_JobTimeSeriesStatisticsQuery_) – Job time-series statistics query. - **data** (_JobTimeSeriesStatisticsQuery_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** JobTimeSeriesStatisticsQueryResult - **Return type:** JobTimeSeriesStatisticsQueryResult #### Examples **Get job time series statistics:** ```python def get_job_time_series_statistics_example() -> None: client = CamundaClient() result = client.get_job_time_series_statistics( data=JobTimeSeriesStatisticsQuery( filter_=JobTimeSeriesStatisticsFilter( from_=datetime.datetime(2024, 1, 1), to=datetime.datetime(2024, 12, 31), job_type="payment-processing", ), ), ) if not isinstance(result.items, Unset): for stat in result.items: print(f"Time series: {stat}") ``` ### get_job_type_statistics() ```python async def get_job_type_statistics(, data, consistency=None, **kwargs) ``` Get job statistics by type > Get statistics about jobs, grouped by job type. - **Parameters:** - **body** (_JobTypeStatisticsQuery_) – Job type statistics query. - **data** (_JobTypeStatisticsQuery_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** JobTypeStatisticsQueryResult - **Return type:** JobTypeStatisticsQueryResult #### Examples **Get job type statistics:** ```python def get_job_type_statistics_example() -> None: client = CamundaClient() result = client.get_job_type_statistics( data=JobTypeStatisticsQuery(), ) if not isinstance(result.items, Unset): for stat in result.items: print(f"Job type: {stat.job_type}") ``` ### get_job_worker_statistics() ```python async def get_job_worker_statistics(, data, consistency=None, **kwargs) ``` Get job statistics by worker > Get statistics about jobs, grouped by worker, for a given job type. - **Parameters:** - **body** (_JobWorkerStatisticsQuery_) – Job worker statistics query. - **data** (_JobWorkerStatisticsQuery_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** JobWorkerStatisticsQueryResult - **Return type:** JobWorkerStatisticsQueryResult #### Examples **Get job worker statistics:** ```python def get_job_worker_statistics_example() -> None: client = CamundaClient() result = client.get_job_worker_statistics( data=JobWorkerStatisticsQuery( filter_=JobWorkerStatisticsFilter( from_=datetime.datetime(2024, 1, 1), to=datetime.datetime(2024, 12, 31), job_type="payment-processing", ), ), ) if not isinstance(result.items, Unset): for stat in result.items: print(f"Worker: {stat.worker}") ``` ### get_license() ```python async def get_license(**kwargs) ``` Get license status > Obtains the status of the current Camunda license. - **Raises:** - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** LicenseResponse - **Parameters:** **kwargs** (_Any_) - **Return type:** LicenseResponse #### Examples **Get license information:** ```python def get_license_example() -> None: client = CamundaClient() result = client.get_license() print(f"License type: {result.license_type}") ``` ### get_mapping_rule() ```python async def get_mapping_rule(mapping_rule_id, , consistency=None, **kwargs) ``` Get a mapping rule > Gets the mapping rule with the given ID. - **Parameters:** - **mapping_rule_id** (_str_) – The unique identifier of a mapping rule. Example: my-mapping-rule. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.NotFoundError** – If the response status code is 404. The mapping rule with the mappingRuleId was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** MappingRuleResult - **Return type:** MappingRuleResult #### Examples **Get a mapping rule:** ```python def get_mapping_rule_example(mapping_rule_id: MappingRuleId) -> None: client = CamundaClient() result = client.get_mapping_rule(mapping_rule_id=mapping_rule_id) print(f"Mapping rule: {result.name}") ``` ### get_process_definition() ```python async def get_process_definition(process_definition_key, , consistency=None, **kwargs) ``` Get process definition > Returns process definition as JSON. - **Parameters:** - **process_definition_key** (_str_) – System-generated key for a deployed process definition. Example: 2251799813686749. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The process definition with the given key was not found. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ProcessDefinitionResult - **Return type:** ProcessDefinitionResult #### Examples **Get a process definition:** ```python def get_process_definition_example(process_definition_key: ProcessDefinitionKey) -> None: client = CamundaClient() result = client.get_process_definition( process_definition_key=process_definition_key, ) print(f"Process definition: {result.name}") ``` ### get_process_definition_instance_statistics() ```python async def get_process_definition_instance_statistics(*, data=, consistency=None, **kwargs) ``` Get process instance statistics > Get statistics about process instances, grouped by process definition and tenant. - **Parameters:** - **body** (_ProcessDefinitionInstanceStatisticsQuery_ _|_ _Unset_) - **data** (_ProcessDefinitionInstanceStatisticsQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ProcessDefinitionInstanceStatisticsQueryResult - **Return type:** ProcessDefinitionInstanceStatisticsQueryResult #### Examples **Get process definition instance statistics:** ```python def get_process_definition_instance_statistics_example() -> None: client = CamundaClient() result = client.get_process_definition_instance_statistics( data=ProcessDefinitionInstanceStatisticsQuery(), ) if not isinstance(result.items, Unset): for stat in result.items: print(f"Definition: {stat.process_definition_id}") ``` ### get_process_definition_instance_version_statistics() ```python async def get_process_definition_instance_version_statistics(, data, consistency=None, **kwargs) ``` Get process instance statistics by version > Get statistics about process instances, grouped by version for a given process definition. The process definition ID must be provided as a required field in the request body filter. - **Parameters:** - **body** (_ProcessDefinitionInstanceVersionStatisticsQuery_) - **data** (_ProcessDefinitionInstanceVersionStatisticsQuery_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ProcessDefinitionInstanceVersionStatisticsQueryResult - **Return type:** ProcessDefinitionInstanceVersionStatisticsQueryResult #### Examples **Get version statistics:** ```python def get_process_definition_instance_version_statistics_example(process_definition_id: ProcessDefinitionId) -> None: client = CamundaClient() result = client.get_process_definition_instance_version_statistics( data=ProcessDefinitionInstanceVersionStatisticsQuery( filter_=ProcessDefinitionInstanceVersionStatisticsQueryFilter( process_definition_id=process_definition_id, ), ), ) if not isinstance(result.items, Unset): for stat in result.items: print(f"Version: {stat.process_definition_version}") ``` ### get_process_definition_message_subscription_statistics() ```python async def get_process_definition_message_subscription_statistics(*, data=, consistency=None, **kwargs) ``` Get message subscription statistics > Get message subscription statistics, grouped by process definition. - **Parameters:** - **body** (_ProcessDefinitionMessageSubscriptionStatisticsQuery_ _|_ _Unset_) - **data** (_ProcessDefinitionMessageSubscriptionStatisticsQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ProcessDefinitionMessageSubscriptionStatisticsQueryResult - **Return type:** ProcessDefinitionMessageSubscriptionStatisticsQueryResult #### Examples **Get message subscription statistics:** ```python def get_process_definition_message_subscription_statistics_example() -> None: client = CamundaClient() result = client.get_process_definition_message_subscription_statistics( data=ProcessDefinitionMessageSubscriptionStatisticsQuery(), ) if not isinstance(result.items, Unset): for stat in result.items: print(f"Definition: {stat.process_definition_id}, subscriptions: {stat.active_subscriptions}") ``` ### get_process_definition_statistics() ```python async def get_process_definition_statistics(process_definition_key, *, data=, consistency=None, **kwargs) ``` Get process definition statistics > Get statistics about elements in currently running process instances by process definition key and search filter. - **Parameters:** - **process_definition_key** (_str_) – System-generated key for a deployed process definition. Example: 2251799813686749. - **body** (_ProcessDefinitionElementStatisticsQuery_ _|_ _Unset_) – Process definition element statistics request. - **data** (_ProcessDefinitionElementStatisticsQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ProcessDefinitionElementStatisticsQueryResult - **Return type:** ProcessDefinitionElementStatisticsQueryResult #### Examples **Get process definition element statistics:** ```python def get_process_definition_statistics_example(process_definition_key: ProcessDefinitionKey) -> None: client = CamundaClient() result = client.get_process_definition_statistics( process_definition_key=process_definition_key, ) if not isinstance(result.items, Unset): for stat in result.items: print(f"Element: {stat.element_id}") ``` ### get_process_definition_xml() ```python async def get_process_definition_xml(process_definition_key, , consistency=None, **kwargs) ``` Get process definition XML > Returns process definition as XML. - **Parameters:** - **process_definition_key** (_str_) – System-generated key for a deployed process definition. Example: 2251799813686749. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The process definition with the given key was not found. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** str - **Return type:** str #### Examples **Get process definition XML:** ```python def get_process_definition_xml_example(process_definition_key: ProcessDefinitionKey) -> None: client = CamundaClient() xml = client.get_process_definition_xml( process_definition_key=process_definition_key, ) print(f"XML length: {len(xml)}") ``` ### get_process_instance() ```python async def get_process_instance(process_instance_key, , consistency=None, **kwargs) ``` Get process instance > Get the process instance by the process instance key. - **Parameters:** - **process_instance_key** (_str_) – System-generated key for a process instance. Example: 2251799813690746. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The process instance with the given key was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ProcessInstanceResult - **Return type:** ProcessInstanceResult #### Examples **Get a process instance:** ```python def get_process_instance_example(process_instance_key: ProcessInstanceKey) -> None: client = CamundaClient() result = client.get_process_instance( process_instance_key=process_instance_key, ) print(f"Process instance: {result.process_definition_id}") ``` ### get_process_instance_call_hierarchy() ```python async def get_process_instance_call_hierarchy(process_instance_key, , consistency=None, **kwargs) ``` Get call hierarchy > Returns the call hierarchy for a given process instance, showing its ancestry up to the root instance. - **Parameters:** - **process_instance_key** (_str_) – System-generated key for a process instance. Example: 2251799813690746. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The process instance is not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** list[Any] - **Return type:** list[Any] #### Examples **Get process instance call hierarchy:** ```python def get_process_instance_call_hierarchy_example(process_instance_key: ProcessInstanceKey) -> None: client = CamundaClient() result = client.get_process_instance_call_hierarchy( process_instance_key=process_instance_key, ) for entry in result: print(f"Call hierarchy entry: {entry}") ``` ### get_process_instance_sequence_flows() ```python async def get_process_instance_sequence_flows(process_instance_key, , consistency=None, **kwargs) ``` Get sequence flows > Get sequence flows taken by the process instance. - **Parameters:** - **process_instance_key** (_str_) – System-generated key for a process instance. Example: 2251799813690746. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ProcessInstanceSequenceFlowsQueryResult - **Return type:** ProcessInstanceSequenceFlowsQueryResult #### Examples **Get process instance sequence flows:** ```python def get_process_instance_sequence_flows_example(process_instance_key: ProcessInstanceKey) -> None: client = CamundaClient() result = client.get_process_instance_sequence_flows( process_instance_key=process_instance_key, ) if not isinstance(result.items, Unset): for flow in result.items: print(f"Sequence flow: {flow}") ``` ### get_process_instance_statistics() ```python async def get_process_instance_statistics(process_instance_key, , consistency=None, **kwargs) ``` Get element instance statistics > Get statistics about elements by the process instance key. - **Parameters:** - **process_instance_key** (_str_) – System-generated key for a process instance. Example: 2251799813690746. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ProcessInstanceElementStatisticsQueryResult - **Return type:** ProcessInstanceElementStatisticsQueryResult #### Examples **Get process instance statistics:** ```python def get_process_instance_statistics_example(process_instance_key: ProcessInstanceKey) -> None: client = CamundaClient() result = client.get_process_instance_statistics( process_instance_key=process_instance_key, ) if not isinstance(result.items, Unset): for stat in result.items: print(f"Element: {stat.element_id}, Active: {stat.active}") ``` ### get_process_instance_statistics_by_definition() ```python async def get_process_instance_statistics_by_definition(, data, consistency=None, **kwargs) ``` Get process instance statistics by definition > Returns statistics for active process instances with incidents, grouped by process definition. The result set is scoped to a specific incident error hash code, which must be provided as a filter in the request body. - **Parameters:** - **body** (_IncidentProcessInstanceStatisticsByDefinitionQuery_) - **data** (_IncidentProcessInstanceStatisticsByDefinitionQuery_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** IncidentProcessInstanceStatisticsByDefinitionQueryResult - **Return type:** IncidentProcessInstanceStatisticsByDefinitionQueryResult #### Examples **Get instance statistics by definition:** ```python def get_process_instance_statistics_by_definition_example() -> None: client = CamundaClient() result = client.get_process_instance_statistics_by_definition( data=IncidentProcessInstanceStatisticsByDefinitionQuery( filter_=IncidentProcessInstanceStatisticsByDefinitionQueryFilter( error_hash_code=12345, ), ), ) if not isinstance(result.items, Unset): for stat in result.items: print(f"Definition: {stat.process_definition_key}") ``` ### get_process_instance_statistics_by_error() ```python async def get_process_instance_statistics_by_error(*, data=, consistency=None, **kwargs) ``` Get process instance statistics by error > Returns statistics for active process instances that currently have active incidents, grouped by incident error hash code. - **Parameters:** - **body** (_IncidentProcessInstanceStatisticsByErrorQuery_ _|_ _Unset_) - **data** (_IncidentProcessInstanceStatisticsByErrorQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** IncidentProcessInstanceStatisticsByErrorQueryResult - **Return type:** IncidentProcessInstanceStatisticsByErrorQueryResult #### Examples **Get instance statistics by error:** ```python def get_process_instance_statistics_by_error_example() -> None: client = CamundaClient() result = client.get_process_instance_statistics_by_error( data=IncidentProcessInstanceStatisticsByErrorQuery(), ) if not isinstance(result.items, Unset): for stat in result.items: print(f"Error: {stat.error_message}") ``` ### get_resource() ```python async def get_resource(resource_key, , consistency=None, **kwargs) ``` Get resource > Returns a deployed resource. :::info This endpoint does not return BPMN process definitions, DMN decision definitions, or form resources. To query BPMN process definitions or DMN decision definitions, use their respective APIs. ::: - **resource_key**: The system-assigned key for this resource. ```` * **Raises:** * **errors.NotFoundError** – If the response status code is 404. A resource with the given key was not found. * **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. * **errors.UnexpectedStatus** – If the response status code is not documented. * **httpx.TimeoutException** – If the request takes longer than Client.timeout. * **Returns:** ResourceResult * **Parameters:** * **resource_key** (*str*) * **consistency** (*ConsistencyOptions* *|* *None*) * **kwargs** (*Any*) * **Return type:** ResourceResult #### Examples **Get a resource:** ```python def get_resource_example() -> None: client = CamundaClient() result = client.get_resource(resource_key="123456") print(f"Resource: {result.resource_name}") ```` ### get_resource_content() ```python async def get_resource_content(resource_key, , consistency=None, **kwargs) ``` Get RPA resource content (deprecated) > **Deprecated** — use /resources/{resourceKey}/content/binary instead, which supports all resource types and returns content as binary (octet-stream). Returns the content of a deployed RPA resource as JSON. :::info This endpoint only supports RPA resources. For generic resource content in binary format, use the /resources/{resourceKey}/content/binary endpoint. ::: - **resource_key**: The system-assigned key for this resource. ```` * **Raises:** * **errors.NotFoundError** – If the response status code is 404. A resource with the given key was not found. * **errors.NotAcceptableError** – If the response status code is 406. The resource exists but is not an RPA resource. * **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. * **errors.UnexpectedStatus** – If the response status code is not documented. * **httpx.TimeoutException** – If the request takes longer than Client.timeout. * **Returns:** GetResourceContentResponse200 * **Parameters:** * **resource_key** (*str*) * **consistency** (*ConsistencyOptions* *|* *None*) * **kwargs** (*Any*) * **Return type:** GetResourceContentResponse200 #### Examples **Get resource content:** ```python def get_resource_content_example() -> None: client = CamundaClient() content = client.get_resource_content(resource_key="123456") print(f"Content: {content}") ```` ### get_resource_content_binary() ```python async def get_resource_content_binary(resource_key, , consistency=None, **kwargs) ``` Get resource content as binary > Returns the content of a deployed resource in binary format (octet-stream). :::info This endpoint does not return BPMN process definitions, DMN decision definitions, or form resources. To query BPMN process definitions or DMN decision definitions, use their respective APIs. ::: - **resource_key**: The system-assigned key for this resource. ```` * **Raises:** * **errors.NotFoundError** – If the response status code is 404. A resource with the given key was not found. * **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. * **errors.UnexpectedStatus** – If the response status code is not documented. * **httpx.TimeoutException** – If the request takes longer than Client.timeout. * **Returns:** File * **Parameters:** * **resource_key** (*str*) * **consistency** (*ConsistencyOptions* *|* *None*) * **kwargs** (*Any*) * **Return type:** File #### Examples **Get resource content as binary:** ```python def get_resource_content_binary_example() -> None: client = CamundaClient() content = client.get_resource_content_binary(resource_key="123456") print(f"Binary content size: {len(content.payload.read())}") ```` ### get_role() ```python async def get_role(role_id, , consistency=None, **kwargs) ``` Get role > Get a role by its ID. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** RoleResult - **Return type:** RoleResult #### Examples **Get a role:** ```python def get_role_example(role_id: RoleId) -> None: client = CamundaClient() result = client.get_role(role_id=role_id) print(f"Role: {result.name}") ``` ### get_start_process_form() ```python async def get_start_process_form(process_definition_key, , consistency=None, **kwargs) ``` Get process start form > Get the start form of a process. Note that this endpoint will only return linked forms. This endpoint does not support embedded forms. - **Parameters:** - **process_definition_key** (_str_) – System-generated key for a deployed process definition. Example: 2251799813686749. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** FormResult - **Return type:** FormResult #### Examples **Get start process form:** ```python def get_start_process_form_example(process_definition_key: ProcessDefinitionKey) -> None: client = CamundaClient() result = client.get_start_process_form( process_definition_key=process_definition_key, ) print(f"Form: {result.form_key}") ``` ### get_status() ```python async def get_status(**kwargs) ``` Get cluster status - **Raises:** - **errors.ServiceUnavailableError** – If the response status code is 503. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Parameters:** **kwargs** (_Any_) - **Return type:** None #### Examples **Check cluster status:** ```python def get_status_example() -> None: client = CamundaClient() client.get_status() print("Cluster is healthy") ``` ### get_system_configuration() ```python async def get_system_configuration(**kwargs) ``` System configuration (alpha) > Returns the current system configuration. The response is an envelope that groups settings by feature area. This endpoint is an alpha feature and may be subject to change in future releases. - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** SystemConfigurationResponse - **Parameters:** **kwargs** (_Any_) - **Return type:** SystemConfigurationResponse #### Examples **Get system configuration:** ```python def get_system_configuration_example() -> None: client = CamundaClient() result = client.get_system_configuration() print(f"System config: {result}") ``` ### get_tenant() ```python async def get_tenant(tenant_id, , consistency=None, **kwargs) ``` Get tenant > Retrieves a single tenant by tenant ID. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Tenant not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** TenantResult - **Return type:** TenantResult #### Examples **Get a tenant:** ```python def get_tenant_example(tenant_id: TenantId) -> None: client = CamundaClient() result = client.get_tenant(tenant_id=tenant_id) print(f"Tenant: {result.name}") ``` ### get_tenant_cluster_variable() ```python async def get_tenant_cluster_variable(tenant_id, name, , consistency=None, **kwargs) ``` Get a tenant-scoped cluster variable > Get a tenant-scoped cluster variable. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **name** (_str_) – The name of a cluster variable. Unique within its scope (global or tenant- specific). Example: feature-flag-checkout. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Cluster variable not found - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ClusterVariableResult - **Return type:** ClusterVariableResult #### Examples **Get a tenant cluster variable:** ```python def get_tenant_cluster_variable_example(tenant_id: TenantId, name: ClusterVariableName) -> None: client = CamundaClient() result = client.get_tenant_cluster_variable( tenant_id=tenant_id, name=name, ) print(f"Variable: {result.name} = {result.value}") ``` ### get_topology() ```python async def get_topology(**kwargs) ``` Get cluster topology > Obtains the current topology of the cluster the gateway is part of. - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** TopologyResponse - **Parameters:** **kwargs** (_Any_) - **Return type:** TopologyResponse #### Examples **Get cluster topology:** ```python def get_topology_example() -> None: client = CamundaClient() result = client.get_topology() print(f"Topology: {result}") ``` ### get_usage_metrics() ```python async def get_usage_metrics(*, start_time, end_time, tenant_id=, with_tenants=, consistency=None, **kwargs) ``` Get usage metrics > Retrieve the usage metrics based on given criteria. - **Parameters:** - **start_time** (_datetime.datetime_) – Example: 2025-06-07T13:14:15Z. - **end_time** (_datetime.datetime_) – Example: 2025-06-07T13:14:15Z. - **tenant_id** (_str_ _|_ _Unset_) – The unique identifier of the tenant. Example: customer-service. - **with_tenants** (_bool_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** UsageMetricsResponse - **Return type:** UsageMetricsResponse #### Examples **Get usage metrics:** ```python def get_usage_metrics_example() -> None: client = CamundaClient() result = client.get_usage_metrics( start_time=datetime.datetime(2024, 1, 1), end_time=datetime.datetime(2024, 12, 31), ) print(f"Metrics: {result}") ``` ### get_user() ```python async def get_user(username, , consistency=None, **kwargs) ``` Get user > Get a user by its username. - **Parameters:** - **username** (_str_) – The unique name of a user. Example: swillis. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The user with the given username was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** UserResult - **Return type:** UserResult #### Examples **Get a user:** ```python def get_user_example(username: Username) -> None: client = CamundaClient() result = client.get_user(username=username) print(f"User: {result.username}") ``` ### get_user_task() ```python async def get_user_task(user_task_key, , consistency=None, **kwargs) ``` Get user task > Get the user task by the user task key. - **Parameters:** - **user_task_key** (_str_) – System-generated key for a user task. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The user task with the given key was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** UserTaskResult - **Return type:** UserTaskResult #### Examples **Get a user task:** ```python def get_user_task_example(user_task_key: UserTaskKey) -> None: client = CamundaClient() task = client.get_user_task(user_task_key=user_task_key) print(f"Task: {task.user_task_key}") ``` ### get_user_task_form() ```python async def get_user_task_form(user_task_key, , consistency=None, **kwargs) ``` Get user task form > Get the form of a user task. Note that this endpoint will only return linked forms. This endpoint does not support embedded forms. - **Parameters:** - **user_task_key** (_str_) – System-generated key for a user task. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** FormResult - **Return type:** FormResult #### Examples **Get a user task form:** ```python def get_user_task_form_example(user_task_key: UserTaskKey) -> None: client = CamundaClient() result = client.get_user_task_form( user_task_key=user_task_key, ) print(f"Form: {result.form_key}") ``` ### get_variable() ```python async def get_variable(variable_key, , consistency=None, **kwargs) ``` Get variable > Get a variable by its key. This endpoint returns both process-level and local (element-scoped) variables. The variable’s scopeKey indicates whether it’s a process-level variable or scoped to a specific element instance. - **Parameters:** - **variable_key** (_str_) – System-generated key for a variable. Example: 2251799813683287. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** VariableResult - **Return type:** VariableResult #### Examples **Get a variable:** ```python def get_variable_example(variable_key: VariableKey) -> None: client = CamundaClient() result = client.get_variable( variable_key=variable_key, ) print(f"Variable: {result.name} = {result.value}") ``` ### migrate_process_instance() ```python async def migrate_process_instance(process_instance_key, , data, **kwargs) ``` Migrate process instance > Migrates a process instance to a new process definition. This request can contain multiple mapping instructions to define mapping between the active process instance’s elements and target process definition elements. Use this to upgrade a process instance to a new version of a process or to a different process definition, e.g. to keep your running instances up-to-date with the latest process improvements. - **Parameters:** - **process_instance_key** (_str_) – System-generated key for a process instance. Example: 2251799813690746. - **body** (_ProcessInstanceMigrationInstruction_) – The migration instructions describe how to migrate a process instance from one process definition to another. - **data** (_ProcessInstanceMigrationInstruction_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The process instance is not found. - **errors.ConflictError** – If the response status code is 409. The process instance migration failed. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Migrate a process instance:** ```python def migrate_process_instance_example(process_instance_key: ProcessInstanceKey, target_process_definition_key: ProcessDefinitionKey, source_element_id: ElementId, target_element_id: ElementId) -> None: client = CamundaClient() client.migrate_process_instance( process_instance_key=process_instance_key, data=ProcessInstanceMigrationInstruction( target_process_definition_key=target_process_definition_key, mapping_instructions=[ MigrateProcessInstanceMappingInstruction( source_element_id=source_element_id, target_element_id=target_element_id, ), ], ), ) ``` ### migrate_process_instances_batch_operation() ```python async def migrate_process_instances_batch_operation(, data, **kwargs) ``` Migrate process instances (batch) > Migrate multiple process instances. Since only process instances with ACTIVE state can be migrated, any given filters for state are ignored and overridden during this batch operation. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). - **Parameters:** - **body** (_ProcessInstanceMigrationBatchOperationRequest_) - **data** (_ProcessInstanceMigrationBatchOperationRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The process instance batch operation failed. More details are provided in the response body. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** BatchOperationCreatedResult - **Return type:** BatchOperationCreatedResult #### Examples **Migrate process instances in batch:** ```python def migrate_process_instances_batch_operation_example(target_process_definition_key: ProcessDefinitionKey, source_element_id: ElementId, target_element_id: ElementId) -> None: client = CamundaClient() result = client.migrate_process_instances_batch_operation( data=ProcessInstanceMigrationBatchOperationRequest( filter_=ProcessInstanceCancellationBatchOperationRequestFilter(), migration_plan=ProcessInstanceMigrationBatchOperationRequestMigrationPlan( target_process_definition_key=target_process_definition_key, mapping_instructions=[ MigrateProcessInstanceMappingInstruction( source_element_id=source_element_id, target_element_id=target_element_id, ), ], ), ), ) print(f"Batch operation key: {result.batch_operation_key}") ``` ### modify_process_instance() ```python async def modify_process_instance(process_instance_key, , data, **kwargs) ``` Modify process instance > Modifies a running process instance. This request can contain multiple instructions to activate an element of the process or to terminate an active instance of an element. Use this to repair a process instance that is stuck on an element or took an unintended path. For example, because an external system is not available or doesn’t respond as expected. - **Parameters:** - **process_instance_key** (_str_) – System-generated key for a process instance. Example: 2251799813690746. - **body** (_ProcessInstanceModificationInstruction_) - **data** (_ProcessInstanceModificationInstruction_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The process instance is not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Modify a process instance:** ```python def modify_process_instance_example(process_instance_key: ProcessInstanceKey) -> None: client = CamundaClient() client.modify_process_instance( process_instance_key=process_instance_key, data=ProcessInstanceModificationInstruction(), ) ``` ### modify_process_instances_batch_operation() ```python async def modify_process_instances_batch_operation(, data, **kwargs) ``` Modify process instances (batch) > Modify multiple process instances. Since only process instances with ACTIVE state can be modified, any given filters for state are ignored and overridden during this batch operation. In contrast to single modification operation, it is not possible to add variable instructions or modify by element key. It is only possible to use the element id of the source and target. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). - **Parameters:** - **body** (_ProcessInstanceModificationBatchOperationRequest_) – The process instance filter to define on which process instances tokens should be moved, and new element instances should be activated or terminated. - **data** (_ProcessInstanceModificationBatchOperationRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The process instance batch operation failed. More details are provided in the response body. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** BatchOperationCreatedResult - **Return type:** BatchOperationCreatedResult #### Examples **Modify process instances in batch:** ```python def modify_process_instances_batch_operation_example(source_element_id: ElementId, target_element_id: ElementId) -> None: client = CamundaClient() result = client.modify_process_instances_batch_operation( data=ProcessInstanceModificationBatchOperationRequest( filter_=ProcessInstanceCancellationBatchOperationRequestFilter(), move_instructions=[ ProcessInstanceModificationMoveBatchOperationInstruction( source_element_id=source_element_id, target_element_id=target_element_id, ), ], ), ) print(f"Batch operation key: {result.batch_operation_key}") ``` ### pin_clock() ```python async def pin_clock(, data, **kwargs) ``` Pin internal clock (alpha) > Set a precise, static time for the Zeebe engine’s internal clock. When the clock is pinned, it remains at the specified time and does not advance. To change the time, the clock must be pinned again with a new timestamp. This endpoint is an alpha feature and may be subject to change in future releases. - **Parameters:** - **body** (_ClockPinRequest_) - **data** (_ClockPinRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Pin the cluster clock:** ```python def pin_clock_example() -> None: client = CamundaClient() client.pin_clock( data=ClockPinRequest( timestamp=1700000000000, ), ) ``` ### publish_message() ```python async def publish_message(, data, **kwargs) ``` Publish message > Publishes a single message. Messages are published to specific partitions computed from their correlation keys. Messages can be buffered. The endpoint does not wait for a correlation result. Use the message correlation endpoint for such use cases. - **Parameters:** - **body** (_MessagePublicationRequest_) - **data** (_MessagePublicationRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** MessagePublicationResult - **Return type:** MessagePublicationResult #### Examples **Publish a message:** ```python def publish_message_example() -> None: client = CamundaClient() result = client.publish_message( data=MessagePublicationRequest( name="order-created", correlation_key="order-12345", time_to_live=60000, ) ) print(f"Message key: {result.message_key}") ``` ### reset_clock() ```python async def reset_clock(**kwargs) ``` Reset internal clock (alpha) > Resets the Zeebe engine’s internal clock to the current system time, enabling it to tick in real- time. This operation is useful for returning the clock to normal behavior after it has been pinned to a specific time. This endpoint is an alpha feature and may be subject to change in future releases. - **Raises:** - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Parameters:** **kwargs** (_Any_) - **Return type:** None #### Examples **Reset the cluster clock:** ```python def reset_clock_example() -> None: client = CamundaClient() client.reset_clock() ``` ### resolve_incident() ```python async def resolve_incident(incident_key, *, data=, **kwargs) ``` Resolve incident > Marks the incident as resolved; most likely a call to Update job will be necessary to reset the job’s retries, followed by this call. - **Parameters:** - **incident_key** (_str_) – System-generated key for a incident. Example: 2251799813689432. - **body** (_IncidentResolutionRequest_ _|_ _Unset_) - **data** (_IncidentResolutionRequest_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The incident with the incidentKey is not found. - **errors.ConflictError** – If the response status code is 409. The incident cannot be resolved due to an invalid state. For example, the associated job may have no retries left. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Resolve an incident:** ```python def resolve_incident_example(incident_key: IncidentKey) -> None: client = CamundaClient() client.resolve_incident(incident_key=incident_key) ``` ### resolve_incidents_batch_operation() ```python async def resolve_incidents_batch_operation(*, data=, **kwargs) ``` Resolve related incidents (batch) > Resolves multiple instances of process instances. Since only process instances with ACTIVE state can have unresolved incidents, any given filters for state are ignored and overridden during this batch operation. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). - **Parameters:** - **body** (_ProcessInstanceIncidentResolutionBatchOperationRequest_ _|_ _Unset_) – The process instance filter that defines which process instances should have their incidents resolved. - **data** (_ProcessInstanceIncidentResolutionBatchOperationRequest_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The process instance batch operation failed. More details are provided in the response body. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** BatchOperationCreatedResult - **Return type:** BatchOperationCreatedResult #### Examples **Resolve incidents in batch:** ```python def resolve_incidents_batch_operation_example() -> None: client = CamundaClient() result = client.resolve_incidents_batch_operation( data=ProcessInstanceIncidentResolutionBatchOperationRequest( filter_=ProcessInstanceCancellationBatchOperationRequestFilter(), ), ) print(f"Batch operation key: {result.batch_operation_key}") ``` ### resolve_process_instance_incidents() ```python async def resolve_process_instance_incidents(process_instance_key, **kwargs) ``` Resolve related incidents > Creates a batch operation to resolve multiple incidents of a process instance. - **Parameters:** - **process_instance_key** (_str_) – System-generated key for a process instance. Example: 2251799813690746. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.NotFoundError** – If the response status code is 404. The process instance is not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** BatchOperationCreatedResult - **Return type:** BatchOperationCreatedResult #### Examples **Resolve process instance incidents:** ```python def resolve_process_instance_incidents_example(process_instance_key: ProcessInstanceKey) -> None: client = CamundaClient() result = client.resolve_process_instance_incidents( process_instance_key=process_instance_key, ) print(f"Batch operation key: {result.batch_operation_key}") ``` ### resume_batch_operation() ```python async def resume_batch_operation(batch_operation_key, *, data=, **kwargs) ``` Resume Batch operation > Resumes a suspended batch operation. This is done asynchronously, the progress can be tracked using the batch operation status endpoint (/batch-operations/{batchOperationKey}). - **Parameters:** - **batch_operation_key** (_str_) – System-generated key for an batch operation. Example: 2251799813684321. - **body** (_Any_ _|_ _Unset_) - **data** (_Any_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The batch operation was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Resume a batch operation:** ```python def resume_batch_operation_example(batch_operation_key: BatchOperationKey) -> None: client = CamundaClient() client.resume_batch_operation( batch_operation_key=batch_operation_key, ) ``` ### run_workers() ```python async def run_workers() ``` ### search_agent_instances() ```python async def search_agent_instances(*, data=, consistency=None, **kwargs) ``` Search agent instances > Search for agent instances based on given criteria. - **Parameters:** - **body** (_AgentInstanceSearchQuery_ _|_ _Unset_) – Agent instance search request. - **data** (_AgentInstanceSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** AgentInstanceSearchQueryResult - **Return type:** AgentInstanceSearchQueryResult #### Examples **Search agent instances:** ```python def search_agent_instances_example() -> None: client = CamundaClient() result = client.search_agent_instances( data=AgentInstanceSearchQuery() ) if not isinstance(result.items, Unset): for agent_instance in result.items: print(f"Agent instance key: {agent_instance.agent_instance_key}") ``` ### search_audit_logs() ```python async def search_audit_logs(*, data=, consistency=None, **kwargs) ``` Search audit logs > Search for audit logs based on given criteria. - **Parameters:** - **body** (_AuditLogSearchQueryRequest_ _|_ _Unset_) – Audit log search request. - **data** (_AuditLogSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** AuditLogSearchQueryResult - **Return type:** AuditLogSearchQueryResult #### Examples **Search audit logs:** ```python def search_audit_logs_example() -> None: client = CamundaClient() result = client.search_audit_logs( data=AuditLogSearchQueryRequest(), ) if not isinstance(result.items, Unset): for log in result.items: print(f"Audit log: {log.audit_log_key}") ``` ### search_authorizations() ```python async def search_authorizations(*, data=, consistency=None, **kwargs) ``` Search authorizations > Search for authorizations based on given criteria. - **Parameters:** - **body** (_AuthorizationSearchQuery_ _|_ _Unset_) - **data** (_AuthorizationSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** AuthorizationSearchResult - **Return type:** AuthorizationSearchResult #### Examples **Search authorizations:** ```python def search_authorizations_example() -> None: client = CamundaClient() result = client.search_authorizations( data=AuthorizationSearchQuery(), ) if not isinstance(result.items, Unset): for auth in result.items: print(f"Authorization: {auth.authorization_key}") ``` ### search_batch_operation_items() ```python async def search_batch_operation_items(*, data=, consistency=None, **kwargs) ``` Search batch operation items > Search for batch operation items based on given criteria. - **Parameters:** - **body** (_BatchOperationItemSearchQuery_ _|_ _Unset_) – Batch operation item search request. - **data** (_BatchOperationItemSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** BatchOperationItemSearchQueryResult - **Return type:** BatchOperationItemSearchQueryResult #### Examples **Search batch operation items:** ```python def search_batch_operation_items_example(batch_operation_key: BatchOperationKey) -> None: client = CamundaClient() result = client.search_batch_operation_items( batch_operation_key=batch_operation_key, data=BatchOperationItemSearchQuery(), ) if not isinstance(result.items, Unset): for item in result.items: print(f"Item: {item.item_key}") ``` ### search_batch_operations() ```python async def search_batch_operations(*, data=, consistency=None, **kwargs) ``` Search batch operations > Search for batch operations based on given criteria. - **Parameters:** - **body** (_BatchOperationSearchQuery_ _|_ _Unset_) – Batch operation search request. - **data** (_BatchOperationSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** BatchOperationSearchQueryResult - **Return type:** BatchOperationSearchQueryResult #### Examples **Search batch operations:** ```python def search_batch_operations_example() -> None: client = CamundaClient() result = client.search_batch_operations( data=BatchOperationSearchQuery(), ) if not isinstance(result.items, Unset): for op in result.items: print(f"Batch operation: {op.batch_operation_key}") ``` ### search_clients_for_group() ```python async def search_clients_for_group(group_id, *, data=, consistency=None, **kwargs) ``` Search group clients > Search clients assigned to a group. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **body** (_GroupClientSearchQueryRequest_ _|_ _Unset_) - **data** (_GroupClientSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The group with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GroupClientSearchResult - **Return type:** GroupClientSearchResult #### Examples **Search clients in a group:** ```python def search_clients_for_group_example(group_id: GroupId) -> None: client = CamundaClient() result = client.search_clients_for_group( group_id=group_id, ) if not isinstance(result.items, Unset): for c in result.items: print(f"Client: {c.client_id}") ``` ### search_clients_for_role() ```python async def search_clients_for_role(role_id, *, data=, consistency=None, **kwargs) ``` Search role clients > Search clients with assigned role. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **body** (_RoleClientSearchQueryRequest_ _|_ _Unset_) - **data** (_RoleClientSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** RoleClientSearchResult - **Return type:** RoleClientSearchResult #### Examples **Search clients for a role:** ```python def search_clients_for_role_example(role_id: RoleId) -> None: client = CamundaClient() result = client.search_clients_for_role( role_id=role_id, ) if not isinstance(result.items, Unset): for c in result.items: print(f"Client: {c.client_id}") ``` ### search_clients_for_tenant() ```python async def search_clients_for_tenant(tenant_id, *, data=, consistency=None, **kwargs) ``` Search clients for tenant > Retrieves a filtered and sorted list of clients for a specified tenant. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **body** (_TenantClientSearchQueryRequest_ _|_ _Unset_) - **data** (_TenantClientSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** TenantClientSearchResult - **Return type:** TenantClientSearchResult #### Examples **Search clients for a tenant:** ```python def search_clients_for_tenant_example(tenant_id: TenantId) -> None: client = CamundaClient() result = client.search_clients_for_tenant( tenant_id=tenant_id, ) if not isinstance(result.items, Unset): for c in result.items: print(f"Client: {c.client_id}") ``` ### search_cluster_variables() ```python async def search_cluster_variables(*, data=, truncate_values=, consistency=None, **kwargs) ``` Search for cluster variables based on given criteria. By default, long variable values in the response are truncated. - **Parameters:** - **truncate_values** (_bool_ _|_ _Unset_) - **body** (_ClusterVariableSearchQueryRequest_ _|_ _Unset_) – Cluster variable search query request. - **data** (_ClusterVariableSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ClusterVariableSearchQueryResult - **Return type:** ClusterVariableSearchQueryResult #### Examples **Search cluster variables:** ```python def search_cluster_variables_example() -> None: client = CamundaClient() result = client.search_cluster_variables( data=ClusterVariableSearchQueryRequest(), ) if not isinstance(result.items, Unset): for var in result.items: print(f"Variable: {var.name}") ``` ### search_correlated_message_subscriptions() ```python async def search_correlated_message_subscriptions(*, data=, consistency=None, **kwargs) ``` Search correlated message subscriptions > Search correlated message subscriptions based on given criteria. - **Parameters:** - **body** (_CorrelatedMessageSubscriptionSearchQuery_ _|_ _Unset_) - **data** (_CorrelatedMessageSubscriptionSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** CorrelatedMessageSubscriptionSearchQueryResult - **Return type:** CorrelatedMessageSubscriptionSearchQueryResult #### Examples **Search correlated message subscriptions:** ```python def search_correlated_message_subscriptions_example() -> None: client = CamundaClient() result = client.search_correlated_message_subscriptions( data=CorrelatedMessageSubscriptionSearchQuery(), ) if not isinstance(result.items, Unset): for sub in result.items: print(f"Correlated subscription: {sub.message_name}") ``` ### search_decision_definitions() ```python async def search_decision_definitions(*, data=, consistency=None, **kwargs) ``` Search decision definitions > Search for decision definitions based on given criteria. - **Parameters:** - **body** (_DecisionDefinitionSearchQuery_ _|_ _Unset_) - **data** (_DecisionDefinitionSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** DecisionDefinitionSearchQueryResult - **Return type:** DecisionDefinitionSearchQueryResult #### Examples **Search decision definitions:** ```python def search_decision_definitions_example() -> None: client = CamundaClient() result = client.search_decision_definitions( data=DecisionDefinitionSearchQuery() ) if not isinstance(result.items, Unset): for definition in result.items: print(f"Decision: {definition.decision_definition_id}") ``` ### search_decision_instances() ```python async def search_decision_instances(*, data=, consistency=None, **kwargs) ``` Search decision instances > Search for decision instances based on given criteria. - **Parameters:** - **body** (_DecisionInstanceSearchQuery_ _|_ _Unset_) - **data** (_DecisionInstanceSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** DecisionInstanceSearchQueryResult - **Return type:** DecisionInstanceSearchQueryResult #### Examples **Search decision instances:** ```python def search_decision_instances_example() -> None: client = CamundaClient() result = client.search_decision_instances( data=DecisionInstanceSearchQuery(), ) if not isinstance(result.items, Unset): for di in result.items: print(f"Decision instance: {di.decision_definition_id}") ``` ### search_decision_requirements() ```python async def search_decision_requirements(*, data=, consistency=None, **kwargs) ``` Search decision requirements > Search for decision requirements based on given criteria. - **Parameters:** - **body** (_DecisionRequirementsSearchQuery_ _|_ _Unset_) - **data** (_DecisionRequirementsSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** DecisionRequirementsSearchQueryResult - **Return type:** DecisionRequirementsSearchQueryResult #### Examples **Search decision requirements:** ```python def search_decision_requirements_example() -> None: client = CamundaClient() result = client.search_decision_requirements( data=DecisionRequirementsSearchQuery(), ) if not isinstance(result.items, Unset): for drd in result.items: print(f"DRD: {drd.decision_requirements_name}") ``` ### search_element_instance_incidents() ```python async def search_element_instance_incidents(element_instance_key, , data, consistency=None, **kwargs) ``` Search for incidents of a specific element instance > Search for incidents caused by the specified element instance, including incidents of any child instances created from this element instance. Although the elementInstanceKey is provided as a path parameter to indicate the root element instance, you may also include an elementInstanceKey within the filter object to narrow results to specific child element instances. This is useful, for example, if you want to isolate incidents associated with nested or subordinate elements within the given element instance while excluding incidents directly tied to the root element itself. - **Parameters:** - **element_instance_key** (_str_) – System-generated key for a element instance. Example: 2251799813686789. - **body** (_IncidentSearchQuery_) - **data** (_IncidentSearchQuery_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The element instance with the given key was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** IncidentSearchQueryResult - **Return type:** IncidentSearchQueryResult #### Examples **Search element instance incidents:** ```python def search_element_instance_incidents_example(element_instance_key: ElementInstanceKey) -> None: client = CamundaClient() result = client.search_element_instance_incidents( element_instance_key=element_instance_key, data=IncidentSearchQuery(), ) if not isinstance(result.items, Unset): for incident in result.items: print(f"Incident: {incident.incident_key}") ``` ### search_element_instances() ```python async def search_element_instances(*, data=, consistency=None, **kwargs) ``` Search element instances > Search for element instances based on given criteria. - **Parameters:** - **body** (_ElementInstanceSearchQuery_ _|_ _Unset_) – Element instance search request. - **data** (_ElementInstanceSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ElementInstanceSearchQueryResult - **Return type:** ElementInstanceSearchQueryResult #### Examples **Search element instances:** ```python def search_element_instances_example() -> None: client = CamundaClient() result = client.search_element_instances( data=ElementInstanceSearchQuery(), ) if not isinstance(result.items, Unset): for ei in result.items: print(f"Element instance: {ei.element_instance_key}") ``` ### search_global_task_listeners() ```python async def search_global_task_listeners(*, data=, consistency=None, **kwargs) ``` Search global user task listeners > Search for global user task listeners based on given criteria. - **Parameters:** - **body** (_GlobalTaskListenerSearchQueryRequest_ _|_ _Unset_) – Global listener search query request. - **data** (_GlobalTaskListenerSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GlobalTaskListenerSearchQueryResult - **Return type:** GlobalTaskListenerSearchQueryResult #### Examples **Search global task listeners:** ```python def search_global_task_listeners_example() -> None: client = CamundaClient() result = client.search_global_task_listeners( data=GlobalTaskListenerSearchQueryRequest(), ) if not isinstance(result.items, Unset): for listener in result.items: print(f"Listener: {listener.id}") ``` ### search_group_ids_for_tenant() ```python async def search_group_ids_for_tenant(tenant_id, *, data=, consistency=None, **kwargs) ``` Search groups for tenant > Retrieves a filtered and sorted list of groups for a specified tenant. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **body** (_TenantGroupSearchQueryRequest_ _|_ _Unset_) - **data** (_TenantGroupSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** TenantGroupSearchResult - **Return type:** TenantGroupSearchResult #### Examples **Search groups for a tenant:** ```python def search_group_ids_for_tenant_example(tenant_id: TenantId) -> None: client = CamundaClient() result = client.search_group_ids_for_tenant( tenant_id=tenant_id, data=TenantGroupSearchQueryRequest(), ) if not isinstance(result.items, Unset): for group in result.items: print(f"Group: {group.group_id}") ``` ### search_groups() ```python async def search_groups(*, data=, consistency=None, **kwargs) ``` Search groups > Search for groups based on given criteria. - **Parameters:** - **body** (_GroupSearchQueryRequest_ _|_ _Unset_) – Group search request. - **data** (_GroupSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GroupSearchQueryResult - **Return type:** GroupSearchQueryResult #### Examples **Search groups:** ```python def search_groups_example() -> None: client = CamundaClient() result = client.search_groups( data=GroupSearchQueryRequest(), ) if not isinstance(result.items, Unset): for group in result.items: print(f"Group: {group.name}") ``` ### search_groups_for_role() ```python async def search_groups_for_role(role_id, *, data=, consistency=None, **kwargs) ``` Search role groups > Search groups with assigned role. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **body** (_RoleGroupSearchQueryRequest_ _|_ _Unset_) - **data** (_RoleGroupSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** RoleGroupSearchResult - **Return type:** RoleGroupSearchResult #### Examples **Search groups for a role:** ```python def search_groups_for_role_example(role_id: RoleId) -> None: client = CamundaClient() result = client.search_groups_for_role( role_id=role_id, data=RoleGroupSearchQueryRequest(), ) if not isinstance(result.items, Unset): for group in result.items: print(f"Group: {group.group_id}") ``` ### search_incidents() ```python async def search_incidents(*, data=, consistency=None, **kwargs) ``` Search incidents > Search for incidents based on given criteria. - **Parameters:** - **body** (_IncidentSearchQuery_ _|_ _Unset_) - **data** (_IncidentSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** IncidentSearchQueryResult - **Return type:** IncidentSearchQueryResult #### Examples **Search incidents:** ```python def search_incidents_example() -> None: client = CamundaClient() result = client.search_incidents( data=IncidentSearchQuery() ) if not isinstance(result.items, Unset): for incident in result.items: print(f"Incident key: {incident.incident_key}") ``` ### search_jobs() ```python async def search_jobs(*, data=, consistency=None, **kwargs) ``` Search jobs > Search for jobs based on given criteria. - **Parameters:** - **body** (_JobSearchQuery_ _|_ _Unset_) – Job search request. - **data** (_JobSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** JobSearchQueryResult - **Return type:** JobSearchQueryResult #### Examples **Search jobs:** ```python def search_jobs_example() -> None: client = CamundaClient() result = client.search_jobs( data=JobSearchQuery(), ) if not isinstance(result.items, Unset): for job in result.items: print(f"Job: {job.job_key}") ``` ### search_mapping_rule() ```python async def search_mapping_rule(*, data=, consistency=None, **kwargs) ``` Search mapping rules > Search for mapping rules based on given criteria. - **Parameters:** - **body** (_MappingRuleSearchQueryRequest_ _|_ _Unset_) - **data** (_MappingRuleSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** MappingRuleSearchQueryResult - **Return type:** MappingRuleSearchQueryResult #### Examples **Search mapping rules:** ```python def search_mapping_rule_example() -> None: client = CamundaClient() result = client.search_mapping_rule( data=MappingRuleSearchQueryRequest(), ) if not isinstance(result.items, Unset): for rule in result.items: print(f"Mapping rule: {rule.name}") ``` ### search_mapping_rules_for_group() ```python async def search_mapping_rules_for_group(group_id, *, data=, consistency=None, **kwargs) ``` Search group mapping rules > Search mapping rules assigned to a group. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **body** (_MappingRuleSearchQueryRequest_ _|_ _Unset_) - **data** (_MappingRuleSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The group with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GroupMappingRuleSearchResult - **Return type:** GroupMappingRuleSearchResult #### Examples **Search mapping rules for a group:** ```python def search_mapping_rules_for_group_example(group_id: GroupId) -> None: client = CamundaClient() result = client.search_mapping_rules_for_group( group_id=group_id, data=MappingRuleSearchQueryRequest(), ) if not isinstance(result.items, Unset): for rule in result.items: print(f"Mapping rule: {rule.mapping_rule_id}") ``` ### search_mapping_rules_for_role() ```python async def search_mapping_rules_for_role(role_id, *, data=, consistency=None, **kwargs) ``` Search role mapping rules > Search mapping rules with assigned role. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **body** (_MappingRuleSearchQueryRequest_ _|_ _Unset_) - **data** (_MappingRuleSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** RoleMappingRuleSearchResult - **Return type:** RoleMappingRuleSearchResult #### Examples **Search mapping rules for a role:** ```python def search_mapping_rules_for_role_example(role_id: RoleId) -> None: client = CamundaClient() result = client.search_mapping_rules_for_role( role_id=role_id, data=MappingRuleSearchQueryRequest(), ) if not isinstance(result.items, Unset): for rule in result.items: print(f"Mapping rule: {rule.mapping_rule_id}") ``` ### search_mapping_rules_for_tenant() ```python async def search_mapping_rules_for_tenant(tenant_id, *, data=, consistency=None, **kwargs) ``` Search mapping rules for tenant > Retrieves a filtered and sorted list of MappingRules for a specified tenant. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **body** (_MappingRuleSearchQueryRequest_ _|_ _Unset_) - **data** (_MappingRuleSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** TenantMappingRuleSearchResult - **Return type:** TenantMappingRuleSearchResult #### Examples **Search mapping rules for a tenant:** ```python def search_mapping_rules_for_tenant_example(tenant_id: TenantId) -> None: client = CamundaClient() result = client.search_mapping_rules_for_tenant( tenant_id=tenant_id, data=MappingRuleSearchQueryRequest(), ) if not isinstance(result.items, Unset): for rule in result.items: print(f"Mapping rule: {rule.mapping_rule_id}") ``` ### search_message_subscriptions() ```python async def search_message_subscriptions(*, data=, consistency=None, **kwargs) ``` Search message subscriptions > Search for message subscriptions based on given criteria. By default, both start and intermediate event subscriptions are returned. Use the messageSubscriptionType filter to restrict results to a single type. **Version notes:** - Start event subscriptions are only captured for deployments made with 8.10 or later. - The messageSubscriptionType field is only populated for data created > with Camunda 8.10 or later. For pre-8.10 data, intermediate event entries have no > messageSubscriptionType value stored. For convenience, the API returns PROCESS_EVENT > as a default for such search results, though. - Searching for intermediate event subscriptions **including legacy data** can be achieved by filtering for messageSubscriptionType not matching START_EVENT. * **Parameters:** - **body** (_MessageSubscriptionSearchQuery_ _|_ _Unset_) - **data** (_MessageSubscriptionSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) * **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. * **Returns:** MessageSubscriptionSearchQueryResult * **Return type:** MessageSubscriptionSearchQueryResult #### Examples **Search message subscriptions:** ```python def search_message_subscriptions_example() -> None: client = CamundaClient() result = client.search_message_subscriptions( data=MessageSubscriptionSearchQuery(), ) if not isinstance(result.items, Unset): for sub in result.items: print(f"Subscription: {sub.message_name}") ``` ### search_process_definitions() ```python async def search_process_definitions(*, data=, consistency=None, **kwargs) ``` Search process definitions > Search for process definitions based on given criteria. - **Parameters:** - **body** (_ProcessDefinitionSearchQuery_ _|_ _Unset_) - **data** (_ProcessDefinitionSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ProcessDefinitionSearchQueryResult - **Return type:** ProcessDefinitionSearchQueryResult #### Examples **Search process definitions:** ```python def search_process_definitions_example() -> None: client = CamundaClient() result = client.search_process_definitions( data=ProcessDefinitionSearchQuery(), ) if not isinstance(result.items, Unset): for pd in result.items: print(f"Process definition: {pd.name}") ``` ### search_process_instance_incidents() ```python async def search_process_instance_incidents(process_instance_key, *, data=, consistency=None, **kwargs) ``` Search related incidents > Search for incidents caused by the process instance or any of its called process or decision instances. Although the processInstanceKey is provided as a path parameter to indicate the root process instance, you may also include a processInstanceKey within the filter object to narrow results to specific child process instances. This is useful, for example, if you want to isolate incidents associated with subprocesses or called processes under the root instance while excluding incidents directly tied to the root. - **Parameters:** - **process_instance_key** (_str_) – System-generated key for a process instance. Example: 2251799813690746. - **body** (_IncidentSearchQuery_ _|_ _Unset_) - **data** (_IncidentSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The process instance with the given key was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** IncidentSearchQueryResult - **Return type:** IncidentSearchQueryResult #### Examples **Search process instance incidents:** ```python def search_process_instance_incidents_example(process_instance_key: ProcessInstanceKey) -> None: client = CamundaClient() result = client.search_process_instance_incidents( process_instance_key=process_instance_key, data=IncidentSearchQuery(), ) if not isinstance(result.items, Unset): for incident in result.items: print(f"Incident: {incident.incident_key}") ``` ### search_process_instances() ```python async def search_process_instances(*, data=, consistency=None, **kwargs) ``` Search process instances > Search for process instances based on given criteria. - **Parameters:** - **body** (_ProcessInstanceSearchQuery_ _|_ _Unset_) – Process instance search request. - **data** (_ProcessInstanceSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ProcessInstanceSearchQueryResult - **Return type:** ProcessInstanceSearchQueryResult #### Examples **Search process instances:** ```python def search_process_instances_example() -> None: client = CamundaClient() result = client.search_process_instances( data=ProcessInstanceSearchQuery( filter_=ProcessInstanceSearchQueryFilter( process_definition_id="order-process", ), sort=[ ProcessInstanceSearchQuerySortRequest( field=ProcessInstanceSearchQuerySortRequestField.STARTDATE, order=SortOrderEnum.DESC, ) ], page=LimitBasedPagination(limit=10), ) ) for instance in result.items: print(f"{instance.process_instance_key}: {instance.state}") print(f"Total: {result.page.total_items}") ``` ### search_resources() ```python async def search_resources(*, data=, consistency=None, **kwargs) ``` Search resources > Search for deployed resources based on given criteria. :::info This endpoint does not return BPMN process definitions, DMN decision definitions, or form resources. To query BPMN process definitions or DMN decision definitions, use their respective search APIs. ::: - **body**: :type body: ResourceSearchQuery | Unset ```` * **Raises:** * **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. * **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. * **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. * **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. * **errors.UnexpectedStatus** – If the response status code is not documented. * **httpx.TimeoutException** – If the request takes longer than Client.timeout. * **Returns:** ResourceSearchQueryResult * **Parameters:** * **data** (*ResourceSearchQuery* *|* *Unset*) * **consistency** (*ConsistencyOptions* *|* *None*) * **kwargs** (*Any*) * **Return type:** ResourceSearchQueryResult #### Examples **Search resources:** ```python def search_resources_example() -> None: client = CamundaClient() result = client.search_resources( data=ResourceSearchQuery(), ) if not isinstance(result.items, Unset): for resource in result.items: print(f"Resource: {resource.resource_name}") ```` ### search_roles() ```python async def search_roles(*, data=, consistency=None, **kwargs) ``` Search roles > Search for roles based on given criteria. - **Parameters:** - **body** (_RoleSearchQueryRequest_ _|_ _Unset_) – Role search request. - **data** (_RoleSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** RoleSearchQueryResult - **Return type:** RoleSearchQueryResult #### Examples **Search roles:** ```python def search_roles_example() -> None: client = CamundaClient() result = client.search_roles( data=RoleSearchQueryRequest(), ) if not isinstance(result.items, Unset): for role in result.items: print(f"Role: {role.name}") ``` ### search_roles_for_group() ```python async def search_roles_for_group(group_id, *, data=, consistency=None, **kwargs) ``` Search group roles > Search roles assigned to a group. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **body** (_RoleSearchQueryRequest_ _|_ _Unset_) – Role search request. - **data** (_RoleSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The group with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GroupRoleSearchResult - **Return type:** GroupRoleSearchResult #### Examples **Search roles for a group:** ```python def search_roles_for_group_example(group_id: GroupId) -> None: client = CamundaClient() result = client.search_roles_for_group( group_id=group_id, data=RoleSearchQueryRequest(), ) if not isinstance(result.items, Unset): for role in result.items: print(f"Role: {role.name}") ``` ### search_roles_for_tenant() ```python async def search_roles_for_tenant(tenant_id, *, data=, consistency=None, **kwargs) ``` Search roles for tenant > Retrieves a filtered and sorted list of roles for a specified tenant. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **body** (_RoleSearchQueryRequest_ _|_ _Unset_) – Role search request. - **data** (_RoleSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** TenantRoleSearchResult - **Return type:** TenantRoleSearchResult #### Examples **Search roles for a tenant:** ```python def search_roles_for_tenant_example(tenant_id: TenantId) -> None: client = CamundaClient() result = client.search_roles_for_tenant( tenant_id=tenant_id, data=RoleSearchQueryRequest(), ) if not isinstance(result.items, Unset): for role in result.items: print(f"Role: {role.name}") ``` ### search_tenants() ```python async def search_tenants(*, data=, consistency=None, **kwargs) ``` Search tenants > Retrieves a filtered and sorted list of tenants. - **Parameters:** - **body** (_TenantSearchQueryRequest_ _|_ _Unset_) – Tenant search request - **data** (_TenantSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** TenantSearchQueryResult - **Return type:** TenantSearchQueryResult #### Examples **Search tenants:** ```python def search_tenants_example() -> None: client = CamundaClient() result = client.search_tenants( data=TenantSearchQueryRequest(), ) if not isinstance(result.items, Unset): for tenant in result.items: print(f"Tenant: {tenant.name}") ``` ### search_user_task_audit_logs() ```python async def search_user_task_audit_logs(user_task_key, *, data=, consistency=None, **kwargs) ``` Search user task audit logs > Search for user task audit logs based on given criteria. - **Parameters:** - **user_task_key** (_str_) – System-generated key for a user task. - **body** (_UserTaskAuditLogSearchQueryRequest_ _|_ _Unset_) – User task search query request. - **data** (_UserTaskAuditLogSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** AuditLogSearchQueryResult - **Return type:** AuditLogSearchQueryResult #### Examples **Search user task audit logs:** ```python def search_user_task_audit_logs_example(user_task_key: UserTaskKey) -> None: client = CamundaClient() result = client.search_user_task_audit_logs( user_task_key=user_task_key, data=UserTaskAuditLogSearchQueryRequest(), ) if not isinstance(result.items, Unset): for log in result.items: print(f"Audit log: {log.audit_log_key}") ``` ### search_user_task_effective_variables() ```python async def search_user_task_effective_variables(user_task_key, *, data=, truncate_values=, consistency=None, **kwargs) ``` Search user task effective variables > Search for the effective variables of a user task. This endpoint returns deduplicated variables where each variable name appears at most once. When the same variable name exists at multiple scope levels in the scope hierarchy, the value from the innermost scope (closest to the user task) takes precedence. This is useful for retrieving the actual runtime state of variables as seen by the user task. By default, long variable values in the response are truncated. - **Parameters:** - **user_task_key** (_str_) – System-generated key for a user task. - **truncate_values** (_bool_ _|_ _Unset_) - **body** (_UserTaskEffectiveVariableSearchQueryRequest_ _|_ _Unset_) – User task effective variable search query request. Uses offset-based pagination only. - **data** (_UserTaskEffectiveVariableSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** VariableSearchQueryResult - **Return type:** VariableSearchQueryResult #### Examples **Search user task effective variables:** ```python def search_user_task_effective_variables_example(user_task_key: UserTaskKey) -> None: client = CamundaClient() result = client.search_user_task_effective_variables( user_task_key=user_task_key, ) if not isinstance(result.items, Unset): for var in result.items: print(f"Variable: {var.name}") ``` ### search_user_task_variables() ```python async def search_user_task_variables(user_task_key, *, data=, truncate_values=, consistency=None, **kwargs) ``` Search user task variables > Search for user task variables based on given criteria. This endpoint returns all variable documents visible from the user task’s scope, including variables from parent scopes in the scope hierarchy. If the same variable name exists at multiple scope levels, each scope’s variable is returned as a separate result. Use the /user-tasks/{userTaskKey}/effective-variables/search endpoint to get deduplicated variables where the innermost scope takes precedence. By default, long variable values in the response are truncated. - **Parameters:** - **user_task_key** (_str_) – System-generated key for a user task. - **truncate_values** (_bool_ _|_ _Unset_) - **body** (_UserTaskVariableSearchQueryRequest_ _|_ _Unset_) – User task search query request. - **data** (_UserTaskVariableSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** VariableSearchQueryResult - **Return type:** VariableSearchQueryResult #### Examples **Search user task variables:** ```python def search_user_task_variables_example(user_task_key: UserTaskKey) -> None: client = CamundaClient() result = client.search_user_task_variables( user_task_key=user_task_key, ) if not isinstance(result.items, Unset): for var in result.items: print(f"Variable: {var.name}") ``` ### search_user_tasks() ```python async def search_user_tasks(*, data=, consistency=None, **kwargs) ``` Search user tasks > Search for user tasks based on given criteria. - **Parameters:** - **body** (_UserTaskSearchQuery_ _|_ _Unset_) – User task search query request. - **data** (_UserTaskSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** UserTaskSearchQueryResult - **Return type:** UserTaskSearchQueryResult #### Examples **Search user tasks:** ```python def search_user_tasks_example() -> None: client = CamundaClient() result = client.search_user_tasks( data=UserTaskSearchQuery() ) if not isinstance(result.items, Unset): for task in result.items: print(f"Task: {task.user_task_key}") ``` ### search_users() ```python async def search_users(*, data=, consistency=None, **kwargs) ``` Search users > Search for users based on given criteria. - **Parameters:** - **body** (_UserSearchQueryRequest_ _|_ _Unset_) - **data** (_UserSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** UserSearchResult - **Return type:** UserSearchResult #### Examples **Search users:** ```python def search_users_example() -> None: client = CamundaClient() result = client.search_users( data=UserSearchQueryRequest(), ) if not isinstance(result.items, Unset): for user in result.items: print(f"User: {user.username}") ``` ### search_users_for_group() ```python async def search_users_for_group(group_id, *, data=, consistency=None, **kwargs) ``` Search group users > Search users assigned to a group. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **body** (_GroupUserSearchQueryRequest_ _|_ _Unset_) - **data** (_GroupUserSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The group with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GroupUserSearchResult - **Return type:** GroupUserSearchResult #### Examples **Search users in a group:** ```python def search_users_for_group_example(group_id: GroupId) -> None: client = CamundaClient() result = client.search_users_for_group( group_id=group_id, ) if not isinstance(result.items, Unset): for user in result.items: print(f"User: {user.username}") ``` ### search_users_for_role() ```python async def search_users_for_role(role_id, *, data=, consistency=None, **kwargs) ``` Search role users > Search users with assigned role. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **body** (_RoleUserSearchQueryRequest_ _|_ _Unset_) - **data** (_RoleUserSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** RoleUserSearchResult - **Return type:** RoleUserSearchResult #### Examples **Search users for a role:** ```python def search_users_for_role_example(role_id: RoleId) -> None: client = CamundaClient() result = client.search_users_for_role( role_id=role_id, ) if not isinstance(result.items, Unset): for user in result.items: print(f"User: {user.username}") ``` ### search_users_for_tenant() ```python async def search_users_for_tenant(tenant_id, *, data=, consistency=None, **kwargs) ``` Search users for tenant > Retrieves a filtered and sorted list of users for a specified tenant. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **body** (_TenantUserSearchQueryRequest_ _|_ _Unset_) - **data** (_TenantUserSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** TenantUserSearchResult - **Return type:** TenantUserSearchResult #### Examples **Search users for a tenant:** ```python def search_users_for_tenant_example(tenant_id: TenantId) -> None: client = CamundaClient() result = client.search_users_for_tenant( tenant_id=tenant_id, ) if not isinstance(result.items, Unset): for user in result.items: print(f"User: {user.username}") ``` ### search_variables() ```python async def search_variables(*, data=, truncate_values=, consistency=None, **kwargs) ``` Search variables > Search for variables based on given criteria. This endpoint returns variables that exist directly at the specified scopes - it does not include variables from parent scopes that would be visible through the scope hierarchy. Variables can be process-level (scoped to the process instance) or local (scoped to specific BPMN elements like tasks, subprocesses, etc.). By default, long variable values in the response are truncated. - **Parameters:** - **truncate_values** (_bool_ _|_ _Unset_) - **body** (_VariableSearchQuery_ _|_ _Unset_) – Variable search query request. - **data** (_VariableSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** VariableSearchQueryResult - **Return type:** VariableSearchQueryResult #### Examples **Search variables:** ```python def search_variables_example() -> None: client = CamundaClient() result = client.search_variables() if not isinstance(result.items, Unset): for var in result.items: print(f"Variable: {var.name}") ``` ### suspend_batch_operation() ```python async def suspend_batch_operation(batch_operation_key, *, data=, **kwargs) ``` Suspend Batch operation > Suspends a running batch operation. This is done asynchronously, the progress can be tracked using the batch operation status endpoint (/batch-operations/{batchOperationKey}). - **Parameters:** - **batch_operation_key** (_str_) – System-generated key for an batch operation. Example: 2251799813684321. - **body** (_Any_ _|_ _Unset_) - **data** (_Any_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The batch operation was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Suspend a batch operation:** ```python def suspend_batch_operation_example(batch_operation_key: BatchOperationKey) -> None: client = CamundaClient() client.suspend_batch_operation( batch_operation_key=batch_operation_key, ) ``` ### throw_job_error() ```python async def throw_job_error(job_key, , data, **kwargs) ``` Throw error for job > Reports a business error (i.e. non-technical) that occurs while processing a job. - **Parameters:** - **job_key** (_str_) – System-generated key for a job. Example: 2251799813653498. - **body** (_JobErrorRequest_) - **data** (_JobErrorRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The job with the given key was not found or is not activated. - **errors.ConflictError** – If the response status code is 409. The job with the given key is in the wrong state currently. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Throw a job error:** ```python def throw_job_error_example(job_key: JobKey) -> None: client = CamundaClient() client.throw_job_error( job_key=job_key, data=JobErrorRequest( error_code="VALIDATION_ERROR", error_message="Input validation failed", ), ) ``` ### unassign_client_from_group() ```python async def unassign_client_from_group(group_id, client_id, **kwargs) ``` Unassign a client from a group > Unassigns a client from a group. The client is removed as a group member, with associated authorizations, roles, and tenant assignments no longer applied. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **client_id** (_str_) – The unique identifier of an OAuth client. Minted outside the Camunda REST API: in SaaS by Console, in Self-Managed with OIDC by the external identity provider (e.g. EntraID, Keycloak, Okta). In Self-Managed with Basic authentication, machine-to-machine applications are modelled as users instead — see the user identifier. > Example: my-application. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The group with the given ID was not found, or the client is not assigned to this group. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a client from a group:** ```python def unassign_client_from_group_example(group_id: GroupId, client_id: ClientId) -> None: client = CamundaClient() client.unassign_client_from_group( group_id=group_id, client_id=client_id, ) ``` ### unassign_client_from_tenant() ```python async def unassign_client_from_tenant(tenant_id, client_id, **kwargs) ``` Unassign a client from a tenant > Unassigns the client from the specified tenant. The client can no longer access tenant data. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **client_id** (_str_) – The unique identifier of an OAuth client. Minted outside the Camunda REST API: in SaaS by Console, in Self-Managed with OIDC by the external identity provider (e.g. EntraID, Keycloak, Okta). In Self-Managed with Basic authentication, machine-to-machine applications are modelled as users instead — see the user identifier. > Example: my-application. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The tenant does not exist or the client was not assigned to it. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a client from a tenant:** ```python def unassign_client_from_tenant_example(tenant_id: TenantId, client_id: ClientId) -> None: client = CamundaClient() client.unassign_client_from_tenant( tenant_id=tenant_id, client_id=client_id, ) ``` ### unassign_group_from_tenant() ```python async def unassign_group_from_tenant(tenant_id, group_id, **kwargs) ``` Unassign a group from a tenant > Unassigns a group from a specified tenant. Members of the group (users, clients) will no longer have access to the tenant’s data - except they are assigned directly to the tenant. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The tenant or group was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a group from a tenant:** ```python def unassign_group_from_tenant_example(tenant_id: TenantId, group_id: GroupId) -> None: client = CamundaClient() client.unassign_group_from_tenant( tenant_id=tenant_id, group_id=group_id, ) ``` ### unassign_mapping_rule_from_group() ```python async def unassign_mapping_rule_from_group(group_id, mapping_rule_id, **kwargs) ``` Unassign a mapping rule from a group > Unassigns a mapping rule from a group. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **mapping_rule_id** (_str_) – The unique identifier of a mapping rule. Example: my-mapping-rule. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The group or mapping rule with the given ID was not found, or the mapping rule is not assigned to this group. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a mapping rule from a group:** ```python def unassign_mapping_rule_from_group_example(group_id: GroupId, mapping_rule_id: MappingRuleId) -> None: client = CamundaClient() client.unassign_mapping_rule_from_group( group_id=group_id, mapping_rule_id=mapping_rule_id, ) ``` ### unassign_mapping_rule_from_tenant() ```python async def unassign_mapping_rule_from_tenant(tenant_id, mapping_rule_id, **kwargs) ``` Unassign a mapping rule from a tenant > Unassigns a single mapping rule from a specified tenant without deleting the rule. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **mapping_rule_id** (_str_) – The unique identifier of a mapping rule. Example: my-mapping-rule. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The tenant or mapping rule was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a mapping rule from a tenant:** ```python def unassign_mapping_rule_from_tenant_example(tenant_id: TenantId, mapping_rule_id: MappingRuleId) -> None: client = CamundaClient() client.unassign_mapping_rule_from_tenant( tenant_id=tenant_id, mapping_rule_id=mapping_rule_id, ) ``` ### unassign_role_from_client() ```python async def unassign_role_from_client(role_id, client_id, **kwargs) ``` Unassign a role from a client > Unassigns the specified role from the client. The client will no longer inherit the authorizations associated with this role. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **client_id** (_str_) – The unique identifier of an OAuth client. Minted outside the Camunda REST API: in SaaS by Console, in Self-Managed with OIDC by the external identity provider (e.g. EntraID, Keycloak, Okta). In Self-Managed with Basic authentication, machine-to-machine applications are modelled as users instead — see the user identifier. > Example: my-application. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role or client with the given ID or username was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a role from a client:** ```python def unassign_role_from_client_example(role_id: RoleId, client_id: ClientId) -> None: client = CamundaClient() client.unassign_role_from_client( role_id=role_id, client_id=client_id, ) ``` ### unassign_role_from_group() ```python async def unassign_role_from_group(role_id, group_id, **kwargs) ``` Unassign a role from a group > Unassigns the specified role from the group. All group members (user or client) no longer inherit the authorizations associated with this role. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role or group with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a role from a group:** ```python def unassign_role_from_group_example(role_id: RoleId, group_id: GroupId) -> None: client = CamundaClient() client.unassign_role_from_group( role_id=role_id, group_id=group_id, ) ``` ### unassign_role_from_mapping_rule() ```python async def unassign_role_from_mapping_rule(role_id, mapping_rule_id, **kwargs) ``` Unassign a role from a mapping rule > Unassigns a role from a mapping rule. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **mapping_rule_id** (_str_) – The unique identifier of a mapping rule. Example: my-mapping-rule. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role or mapping rule with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a role from a mapping rule:** ```python def unassign_role_from_mapping_rule_example(role_id: RoleId, mapping_rule_id: MappingRuleId) -> None: client = CamundaClient() client.unassign_role_from_mapping_rule( role_id=role_id, mapping_rule_id=mapping_rule_id, ) ``` ### unassign_role_from_tenant() ```python async def unassign_role_from_tenant(tenant_id, role_id, **kwargs) ``` Unassign a role from a tenant > Unassigns a role from a specified tenant. Users, Clients or Groups, that have the role assigned, will no longer have access to the tenant’s data - unless they are assigned directly to the tenant. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The tenant or role was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a role from a tenant:** ```python def unassign_role_from_tenant_example(tenant_id: TenantId, role_id: RoleId) -> None: client = CamundaClient() client.unassign_role_from_tenant( tenant_id=tenant_id, role_id=role_id, ) ``` ### unassign_role_from_user() ```python async def unassign_role_from_user(role_id, username, **kwargs) ``` Unassign a role from a user > Unassigns a role from a user. The user will no longer inherit the authorizations associated with this role. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **username** (_str_) – The unique name of a user. Example: swillis. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role or user with the given ID or username was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a role from a user:** ```python def unassign_role_from_user_example(role_id: RoleId, username: Username) -> None: client = CamundaClient() client.unassign_role_from_user( role_id=role_id, username=username, ) ``` ### unassign_user_from_group() ```python async def unassign_user_from_group(group_id, username, **kwargs) ``` Unassign a user from a group > Unassigns a user from a group. The user is removed as a group member, with associated authorizations, roles, and tenant assignments no longer applied. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **username** (_str_) – The unique name of a user. Example: swillis. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The group or user with the given ID was not found, or the user is not assigned to this group. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a user from a group:** ```python def unassign_user_from_group_example(group_id: GroupId, username: Username) -> None: client = CamundaClient() client.unassign_user_from_group( group_id=group_id, username=username, ) ``` ### unassign_user_from_tenant() ```python async def unassign_user_from_tenant(tenant_id, username, **kwargs) ``` Unassign a user from a tenant > Unassigns the user from the specified tenant. The user can no longer access tenant data. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **username** (_str_) – The unique name of a user. Example: swillis. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The tenant or user was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a user from a tenant:** ```python def unassign_user_from_tenant_example(tenant_id: TenantId, username: Username) -> None: client = CamundaClient() client.unassign_user_from_tenant( tenant_id=tenant_id, username=username, ) ``` ### unassign_user_task() ```python async def unassign_user_task(user_task_key, **kwargs) ``` Unassign user task > Removes the assignee of a task with the given key. Unassignment waits for blocking task listeners on this lifecycle transition. If listener processing is delayed beyond the request timeout, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. - **Parameters:** - **user_task_key** (_str_) – System-generated key for a user task. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The user task with the given key was not found. - **errors.ConflictError** – If the response status code is 409. The user task with the given key is in the wrong state currently. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.GatewayTimeoutError** – If the response status code is 504. The request timed out between the gateway and the broker. For these endpoints, this often happens when user task listeners are configured and the corresponding listener job is not completed within the request timeout. Common causes include no available job workers for the listener type, busy or crashed job workers, or delayed job completion. As with any gateway timeout, general timeout causes (for example transient network issues) can also result in a 504 response. Troubleshooting: - verify that job workers for the listener type are running and healthy - check worker logs for crashes, retries, and completion failures - check network connectivity between workers, gateway, and broker - retry with backoff after transient failures - fail without retries if a problem persists - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a user task:** ```python def unassign_user_task_example(user_task_key: UserTaskKey) -> None: client = CamundaClient() client.unassign_user_task(user_task_key=user_task_key) ``` ### update_agent_instance() ```python async def update_agent_instance(agent_instance_key, , data, **kwargs) ``` Update agent instance > Updates the mutable fields of an agent instance: status, metric counters, and tools. Metric values are treated as deltas and applied immediately to the aggregate counters. Tool updates replace the existing tool list. At least one of status, metrics, or tools must be provided. - **Parameters:** - **agent_instance_key** (_str_) – System-generated key for an agent instance. Example: 4503599627370496. - **body** (_AgentInstanceUpdateRequest_) – Request to update the mutable state of an agent instance. At least one of status, metrics, or tools must be provided. - **data** (_AgentInstanceUpdateRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The agent instance with the given key was not found. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Update an agent instance:** ```python def update_agent_instance_example(agent_instance_key: AgentInstanceKey) -> None: client = CamundaClient() client.update_agent_instance( agent_instance_key=agent_instance_key, data=AgentInstanceUpdateRequest( status=AgentInstanceUpdateRequestStatus.THINKING, ), ) ``` ### update_authorization() ```python async def update_authorization(authorization_key, , data, **kwargs) ``` Update authorization > Update the authorization with the given key. - **Parameters:** - **authorization_key** (_str_) – System-generated key for an authorization. Example: 2251799813684332. - **body** (_AuthorizationIdBasedRequest_ _|_ _AuthorizationPropertyBasedRequest_) - **data** (_AuthorizationIdBasedRequest_ _|_ _AuthorizationPropertyBasedRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.NotFoundError** – If the response status code is 404. The authorization with the authorizationKey was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Update an authorization:** ```python def update_authorization_example(authorization_key: AuthorizationKey) -> None: client = CamundaClient() client.update_authorization( authorization_key=authorization_key, data=AuthorizationIdBasedRequest( resource_type=AuthorizationIdBasedRequestResourceType.PROCESS_DEFINITION, permission_types=[ AuthorizationIdBasedRequestPermissionTypesItem.READ, AuthorizationIdBasedRequestPermissionTypesItem.UPDATE, AuthorizationIdBasedRequestPermissionTypesItem.DELETE, ], resource_id="my-process", owner_type=OwnerTypeEnum.USER, owner_id="user@example.com", ), ) ``` ### update_global_cluster_variable() ```python async def update_global_cluster_variable(name, , data, **kwargs) ``` Update a global-scoped cluster variable > Updates the value of an existing global cluster variable. The variable must exist, otherwise a 404 error is returned. - **Parameters:** - **name** (_str_) – The name of a cluster variable. Unique within its scope (global or tenant- specific). Example: feature-flag-checkout. - **body** (_UpdateClusterVariableRequest_) - **data** (_UpdateClusterVariableRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Cluster variable not found - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ClusterVariableResult - **Return type:** ClusterVariableResult #### Examples **Update a global cluster variable:** ```python def update_global_cluster_variable_example(name: ClusterVariableName) -> None: client = CamundaClient() result = client.update_global_cluster_variable( name=name, data=UpdateClusterVariableRequest( value=UpdateClusterVariableRequestValue.from_dict({"key": "updated-value"}), ), ) print(f"Updated variable: {result.name}") ``` ### update_global_task_listener() ```python async def update_global_task_listener(id, , data, **kwargs) ``` Update global user task listener > Updates a global user task listener. - **Parameters:** - **id** (_str_) – The user-defined id for the global listener Example: GlobalListener_1. - **body** (_UpdateGlobalTaskListenerRequest_) - **data** (_UpdateGlobalTaskListenerRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The global user task listener was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GlobalTaskListenerResult - **Return type:** GlobalTaskListenerResult #### Examples **Update a global task listener:** ```python def update_global_task_listener_example(listener_id: GlobalListenerId) -> None: client = CamundaClient() result = client.update_global_task_listener( id=listener_id, data=UpdateGlobalTaskListenerRequest( event_types=[GlobalTaskListenerEventTypeEnum.COMPLETING], type_="updated-task-listener", ), ) print(f"Updated listener: {result.id}") ``` ### update_group() ```python async def update_group(group_id, , data, **kwargs) ``` Update group > Update a group with the given ID. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **body** (_GroupUpdateRequest_) - **data** (_GroupUpdateRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.NotFoundError** – If the response status code is 404. The group with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GroupUpdateResult - **Return type:** GroupUpdateResult #### Examples **Update a group:** ```python def update_group_example(group_id: GroupId) -> None: client = CamundaClient() client.update_group( group_id=group_id, data=GroupUpdateRequest(name="engineering-team"), ) ``` ### update_job() ```python async def update_job(job_key, , data, **kwargs) ``` Update job > Update a job with the given key. - **Parameters:** - **job_key** (_str_) – System-generated key for a job. Example: 2251799813653498. - **body** (_JobUpdateRequest_) - **data** (_JobUpdateRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The job with the jobKey is not found. - **errors.ConflictError** – If the response status code is 409. The job with the given key is in the wrong state currently. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Update a job:** ```python def update_job_example(job_key: JobKey) -> None: client = CamundaClient() client.update_job( job_key=job_key, data=JobUpdateRequest( changeset=JobChangeset( retries=3, ), ), ) ``` ### update_mapping_rule() ```python async def update_mapping_rule(mapping_rule_id, *, data=, **kwargs) ``` Update mapping rule > Update a mapping rule. - **Parameters:** - **mapping_rule_id** (_str_) – The unique identifier of a mapping rule. Example: my-mapping-rule. - **body** (_MappingRuleUpdateRequest_ _|_ _Unset_) - **data** (_MappingRuleUpdateRequest_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. The request to update a mapping rule was denied. More details are provided in the response body. - **errors.NotFoundError** – If the response status code is 404. The request to update a mapping rule was denied. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** MappingRuleUpdateResult - **Return type:** MappingRuleUpdateResult #### Examples **Update a mapping rule:** ```python def update_mapping_rule_example(mapping_rule_id: MappingRuleId) -> None: client = CamundaClient() client.update_mapping_rule( mapping_rule_id=mapping_rule_id, data=MappingRuleUpdateRequest( claim_name="groups", claim_value="senior-engineering", name="Senior Engineering Mapping", ), ) ``` ### update_role() ```python async def update_role(role_id, , data, **kwargs) ``` Update role > Update a role with the given ID. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **body** (_RoleUpdateRequest_) - **data** (_RoleUpdateRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.NotFoundError** – If the response status code is 404. The role with the ID is not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** RoleUpdateResult - **Return type:** RoleUpdateResult #### Examples **Update a role:** ```python def update_role_example(role_id: RoleId) -> None: client = CamundaClient() client.update_role( role_id=role_id, data=RoleUpdateRequest(name="senior-developer"), ) ``` ### update_tenant() ```python async def update_tenant(tenant_id, , data, **kwargs) ``` Update tenant > Updates an existing tenant. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **body** (_TenantUpdateRequest_) - **data** (_TenantUpdateRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The tenant was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** TenantUpdateResult - **Return type:** TenantUpdateResult #### Examples **Update a tenant:** ```python def update_tenant_example(tenant_id: TenantId) -> None: client = CamundaClient() client.update_tenant( tenant_id=tenant_id, data=TenantUpdateRequest(name="Acme Corp International"), ) ``` ### update_tenant_cluster_variable() ```python async def update_tenant_cluster_variable(tenant_id, name, , data, **kwargs) ``` Update a tenant-scoped cluster variable > Updates the value of an existing tenant-scoped cluster variable. The variable must exist, otherwise a 404 error is returned. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **name** (_str_) – The name of a cluster variable. Unique within its scope (global or tenant- specific). Example: feature-flag-checkout. - **body** (_UpdateClusterVariableRequest_) - **data** (_UpdateClusterVariableRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Cluster variable not found - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ClusterVariableResult - **Return type:** ClusterVariableResult #### Examples **Update a tenant cluster variable:** ```python def update_tenant_cluster_variable_example(tenant_id: TenantId, name: ClusterVariableName) -> None: client = CamundaClient() result = client.update_tenant_cluster_variable( tenant_id=tenant_id, name=name, data=UpdateClusterVariableRequest( value=UpdateClusterVariableRequestValue.from_dict({"key": "updated-tenant-value"}), ), ) print(f"Updated variable: {result.name}") ``` ### update_user() ```python async def update_user(username, , data, **kwargs) ``` Update user > Updates a user. - **Parameters:** - **username** (_str_) – The unique name of a user. Example: swillis. - **body** (_UserUpdateRequest_) - **data** (_UserUpdateRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The user was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** UserUpdateResult - **Return type:** UserUpdateResult #### Examples **Update a user:** ```python def update_user_example(username: Username) -> None: client = CamundaClient() client.update_user( username=username, data=UserUpdateRequest( name="Jane Smith", email="jsmith@example.com", ), ) ``` ### update_user_task() ```python async def update_user_task(user_task_key, *, data=, **kwargs) ``` Update user task > Update a user task with the given key. Updates wait for blocking task listeners on this lifecycle transition. If listener processing is delayed beyond the request timeout, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. - **Parameters:** - **user_task_key** (_str_) – System-generated key for a user task. - **body** (_UserTaskUpdateRequest_ _|_ _Unset_) - **data** (_UserTaskUpdateRequest_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The user task with the given key was not found. - **errors.ConflictError** – If the response status code is 409. The user task with the given key is in the wrong state currently. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.GatewayTimeoutError** – If the response status code is 504. The request timed out between the gateway and the broker. For these endpoints, this often happens when user task listeners are configured and the corresponding listener job is not completed within the request timeout. Common causes include no available job workers for the listener type, busy or crashed job workers, or delayed job completion. As with any gateway timeout, general timeout causes (for example transient network issues) can also result in a 504 response. Troubleshooting: - verify that job workers for the listener type are running and healthy - check worker logs for crashes, retries, and completion failures - check network connectivity between workers, gateway, and broker - retry with backoff after transient failures - fail without retries if a problem persists - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Update a user task:** ```python def update_user_task_example(user_task_key: UserTaskKey) -> None: client = CamundaClient() client.update_user_task( user_task_key=user_task_key, data=UserTaskUpdateRequest( changeset=Changeset( due_date=datetime.datetime(2025, 12, 31, 23, 59, 59), ), ), ) ``` --- ## CamundaClient(Api-reference) ## CamundaClient ```python class CamundaClient(configuration=None, auth_provider=None, logger=None, **kwargs) ``` Bases: `object` - **Parameters:** - **configuration** ([_CamundaSdkConfiguration_](runtime.md#camunda_orchestration_sdk.runtime.configuration_resolver.CamundaSdkConfiguration)) - **auth_provider** ([_AuthProvider_](runtime.md#camunda_orchestration_sdk.runtime.auth.AuthProvider)) - **logger** ([_CamundaLogger_](runtime.md#camunda_orchestration_sdk.runtime.logging.CamundaLogger) _|_ _None_) - **kwargs** (_Any_) ### activate_ad_hoc_sub_process_activities() ```python def activate_ad_hoc_sub_process_activities(ad_hoc_sub_process_instance_key, , data, **kwargs) ``` Activate activities within an ad-hoc sub-process > Activates selected activities within an ad-hoc sub-process identified by element ID. The provided element IDs must exist within the ad-hoc sub-process instance identified by the provided adHocSubProcessInstanceKey. - **Parameters:** - **ad_hoc_sub_process_instance_key** (_str_) – System-generated key for a element instance. Example: 2251799813686789. - **body** (_AdHocSubProcessActivateActivitiesInstruction_) - **data** (_AdHocSubProcessActivateActivitiesInstruction_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The ad-hoc sub-process instance is not found or the provided key does not identify an ad-hoc sub-process. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Activate ad-hoc sub-process activities:** ```python def activate_ad_hoc_sub_process_activities_example(element_id: ElementId) -> None: client = CamundaClient() client.activate_ad_hoc_sub_process_activities( ad_hoc_sub_process_instance_key="123456", data=AdHocSubProcessActivateActivitiesInstruction( elements=[ AdHocSubProcessActivateActivityReference(element_id=element_id), AdHocSubProcessActivateActivityReference(element_id=element_id), ], ), ) ``` ### activate_jobs() ```python def activate_jobs(, data, **kwargs) ``` Activate jobs > Iterate through all known partitions and activate jobs up to the requested maximum. - **Parameters:** - **body** (_JobActivationRequest_) - **data** (_JobActivationRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** JobActivationResult - **Return type:** JobActivationResult #### Examples **Activate and process jobs:** ```python async def activate_jobs_example() -> None: async with CamundaAsyncClient() as client: result = await client.activate_jobs( data=JobActivationRequest( type_="payment-processing", timeout=30000, max_jobs_to_activate=5, ) ) for job in result.jobs: print(f"Job {job.job_key}: {job.type_}") ``` ### assign_client_to_group() ```python def assign_client_to_group(group_id, client_id, **kwargs) ``` Assign a client to a group > Assigns a client to a group, making it a member of the group. Members of the group inherit the group authorizations, roles, and tenant assignments. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **client_id** (_str_) – The unique identifier of an OAuth client. Minted outside the Camunda REST API: in SaaS by Console, in Self-Managed with OIDC by the external identity provider (e.g. EntraID, Keycloak, Okta). In Self-Managed with Basic authentication, machine-to-machine applications are modelled as users instead — see the user identifier. > Example: my-application. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The group with the given ID was not found. - **errors.ConflictError** – If the response status code is 409. The client with the given ID is already assigned to the group. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a client to a group:** ```python def assign_client_to_group_example(group_id: GroupId, client_id: ClientId) -> None: client = CamundaClient() client.assign_client_to_group( group_id=group_id, client_id=client_id, ) ``` ### assign_client_to_tenant() ```python def assign_client_to_tenant(tenant_id, client_id, **kwargs) ``` Assign a client to a tenant > Assign the client to the specified tenant. The client can then access tenant data and perform authorized actions. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **client_id** (_str_) – The unique identifier of an OAuth client. Minted outside the Camunda REST API: in SaaS by Console, in Self-Managed with OIDC by the external identity provider (e.g. EntraID, Keycloak, Okta). In Self-Managed with Basic authentication, machine-to-machine applications are modelled as users instead — see the user identifier. > Example: my-application. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The tenant was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a client to a tenant:** ```python def assign_client_to_tenant_example(tenant_id: TenantId, client_id: ClientId) -> None: client = CamundaClient() client.assign_client_to_tenant( tenant_id=tenant_id, client_id=client_id, ) ``` ### assign_group_to_tenant() ```python def assign_group_to_tenant(tenant_id, group_id, **kwargs) ``` Assign a group to a tenant > Assigns a group to a specified tenant. Group members (users, clients) can then access tenant data and perform authorized actions. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The tenant or group was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a group to a tenant:** ```python def assign_group_to_tenant_example(tenant_id: TenantId, group_id: GroupId) -> None: client = CamundaClient() client.assign_group_to_tenant( tenant_id=tenant_id, group_id=group_id, ) ``` ### assign_mapping_rule_to_group() ```python def assign_mapping_rule_to_group(group_id, mapping_rule_id, **kwargs) ``` Assign a mapping rule to a group > Assigns a mapping rule to a group. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **mapping_rule_id** (_str_) – The unique identifier of a mapping rule. Example: my-mapping-rule. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The group or mapping rule with the given ID was not found. - **errors.ConflictError** – If the response status code is 409. The mapping rule with the given ID is already assigned to the group. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a mapping rule to a group:** ```python def assign_mapping_rule_to_group_example(group_id: GroupId, mapping_rule_id: MappingRuleId) -> None: client = CamundaClient() client.assign_mapping_rule_to_group( group_id=group_id, mapping_rule_id=mapping_rule_id, ) ``` ### assign_mapping_rule_to_tenant() ```python def assign_mapping_rule_to_tenant(tenant_id, mapping_rule_id, **kwargs) ``` Assign a mapping rule to a tenant > Assign a single mapping rule to a specified tenant. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **mapping_rule_id** (_str_) – The unique identifier of a mapping rule. Example: my-mapping-rule. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The tenant or mapping rule was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a mapping rule to a tenant:** ```python def assign_mapping_rule_to_tenant_example(tenant_id: TenantId, mapping_rule_id: MappingRuleId) -> None: client = CamundaClient() client.assign_mapping_rule_to_tenant( tenant_id=tenant_id, mapping_rule_id=mapping_rule_id, ) ``` ### assign_role_to_client() ```python def assign_role_to_client(role_id, client_id, **kwargs) ``` Assign a role to a client > Assigns the specified role to the client. The client will inherit the authorizations associated with this role. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **client_id** (_str_) – The unique identifier of an OAuth client. Minted outside the Camunda REST API: in SaaS by Console, in Self-Managed with OIDC by the external identity provider (e.g. EntraID, Keycloak, Okta). In Self-Managed with Basic authentication, machine-to-machine applications are modelled as users instead — see the user identifier. > Example: my-application. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role with the given ID was not found. - **errors.ConflictError** – If the response status code is 409. The role was already assigned to the client with the given ID. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a role to a client:** ```python def assign_role_to_client_example(role_id: RoleId, client_id: ClientId) -> None: client = CamundaClient() client.assign_role_to_client( role_id=role_id, client_id=client_id, ) ``` ### assign_role_to_group() ```python def assign_role_to_group(role_id, group_id, **kwargs) ``` Assign a role to a group > Assigns the specified role to the group. Every member of the group (user or client) will inherit the authorizations associated with this role. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role or group with the given ID was not found. - **errors.ConflictError** – If the response status code is 409. The role is already assigned to the group with the given ID. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a role to a group:** ```python def assign_role_to_group_example(role_id: RoleId, group_id: GroupId) -> None: client = CamundaClient() client.assign_role_to_group( role_id=role_id, group_id=group_id, ) ``` ### assign_role_to_mapping_rule() ```python def assign_role_to_mapping_rule(role_id, mapping_rule_id, **kwargs) ``` Assign a role to a mapping rule > Assigns a role to a mapping rule. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **mapping_rule_id** (_str_) – The unique identifier of a mapping rule. Example: my-mapping-rule. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role or mapping rule with the given ID was not found. - **errors.ConflictError** – If the response status code is 409. The role is already assigned to the mapping rule with the given ID. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a role to a mapping rule:** ```python def assign_role_to_mapping_rule_example(role_id: RoleId, mapping_rule_id: MappingRuleId) -> None: client = CamundaClient() client.assign_role_to_mapping_rule( role_id=role_id, mapping_rule_id=mapping_rule_id, ) ``` ### assign_role_to_tenant() ```python def assign_role_to_tenant(tenant_id, role_id, **kwargs) ``` Assign a role to a tenant > Assigns a role to a specified tenant. Users, Clients or Groups, that have the role assigned, will get access to the tenant’s data and can perform actions according to their authorizations. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The tenant or role was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a role to a tenant:** ```python def assign_role_to_tenant_example(tenant_id: TenantId, role_id: RoleId) -> None: client = CamundaClient() client.assign_role_to_tenant( tenant_id=tenant_id, role_id=role_id, ) ``` ### assign_role_to_user() ```python def assign_role_to_user(role_id, username, **kwargs) ``` Assign a role to a user > Assigns the specified role to the user. The user will inherit the authorizations associated with this role. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **username** (_str_) – The unique name of a user. Example: swillis. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role or user with the given ID or username was not found. - **errors.ConflictError** – If the response status code is 409. The role is already assigned to the user with the given ID. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a role to a user:** ```python def assign_role_to_user_example(role_id: RoleId, username: Username) -> None: client = CamundaClient() client.assign_role_to_user( role_id=role_id, username=username, ) ``` ### assign_user_task() ```python def assign_user_task(user_task_key, , data, **kwargs) ``` Assign user task > Assigns a user task with the given key to the given assignee. Assignment waits for blocking task listeners on this lifecycle transition. If listener processing is delayed beyond the request timeout, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. - **Parameters:** - **user_task_key** (_str_) – System-generated key for a user task. - **body** (_UserTaskAssignmentRequest_) - **data** (_UserTaskAssignmentRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The user task with the given key was not found. - **errors.ConflictError** – If the response status code is 409. The user task with the given key is in the wrong state currently. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.GatewayTimeoutError** – If the response status code is 504. The request timed out between the gateway and the broker. For these endpoints, this often happens when user task listeners are configured and the corresponding listener job is not completed within the request timeout. Common causes include no available job workers for the listener type, busy or crashed job workers, or delayed job completion. As with any gateway timeout, general timeout causes (for example transient network issues) can also result in a 504 response. Troubleshooting: - verify that job workers for the listener type are running and healthy - check worker logs for crashes, retries, and completion failures - check network connectivity between workers, gateway, and broker - retry with backoff after transient failures - fail without retries if a problem persists - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a user task:** ```python def assign_user_task_example(user_task_key: UserTaskKey) -> None: client = CamundaClient() client.assign_user_task( user_task_key=user_task_key, data=UserTaskAssignmentRequest( assignee="user@example.com", ), ) ``` ### assign_user_to_group() ```python def assign_user_to_group(group_id, username, **kwargs) ``` Assign a user to a group > Assigns a user to a group, making the user a member of the group. Group members inherit the group authorizations, roles, and tenant assignments. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **username** (_str_) – The unique name of a user. Example: swillis. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The group or user with the given ID or username was not found. - **errors.ConflictError** – If the response status code is 409. The user with the given ID is already assigned to the group. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a user to a group:** ```python def assign_user_to_group_example(group_id: GroupId, username: Username) -> None: client = CamundaClient() client.assign_user_to_group( group_id=group_id, username=username, ) ``` ### assign_user_to_tenant() ```python def assign_user_to_tenant(tenant_id, username, **kwargs) ``` Assign a user to a tenant > Assign a single user to a specified tenant. The user can then access tenant data and perform authorized actions. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **username** (_str_) – The unique name of a user. Example: swillis. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The tenant or user was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Assign a user to a tenant:** ```python def assign_user_to_tenant_example(tenant_id: TenantId, username: Username) -> None: client = CamundaClient() client.assign_user_to_tenant( tenant_id=tenant_id, username=username, ) ``` ### auth_provider ```python auth_provider: [AuthProvider](runtime.md#camunda_orchestration_sdk.runtime.auth.AuthProvider) ``` ### broadcast_signal() ```python def broadcast_signal(, data, **kwargs) ``` Broadcast signal > Broadcasts a signal. - **Parameters:** - **body** (_SignalBroadcastRequest_) - **data** (_SignalBroadcastRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The signal is not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** SignalBroadcastResult - **Return type:** SignalBroadcastResult #### Examples **Broadcast a signal:** ```python def broadcast_signal_example() -> None: client = CamundaClient() result = client.broadcast_signal( data=SignalBroadcastRequest( signal_name="order-cancelled", ) ) print(f"Signal key: {result.signal_key}") ``` ### cancel_batch_operation() ```python def cancel_batch_operation(batch_operation_key, *, data=, **kwargs) ``` Cancel Batch operation > Cancels a running batch operation. This is done asynchronously, the progress can be tracked using the batch operation status endpoint (/batch-operations/{batchOperationKey}). - **Parameters:** - **batch_operation_key** (_str_) – System-generated key for an batch operation. Example: 2251799813684321. - **body** (_Any_ _|_ _Unset_) - **data** (_Any_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The batch operation was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Cancel a batch operation:** ```python def cancel_batch_operation_example(batch_operation_key: BatchOperationKey) -> None: client = CamundaClient() client.cancel_batch_operation( batch_operation_key=batch_operation_key, ) ``` ### cancel_process_instance() ```python def cancel_process_instance(process_instance_key, *, data=, **kwargs) ``` Cancel process instance > Cancels a running process instance. As a cancellation includes more than just the removal of the process instance resource, the cancellation resource must be posted. Cancellation can wait on listener-related processing; when that processing does not complete in time, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. - **Parameters:** - **process_instance_key** (_str_) – System-generated key for a process instance. Example: 2251799813690746. - **body** (_CancelProcessInstanceRequest_ _|_ _None_ _|_ _Unset_) - **data** (_CancelProcessInstanceRequest_ _|_ _None_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The process instance is not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.GatewayTimeoutError** – If the response status code is 504. The request timed out between the gateway and the broker. For these endpoints, this often happens when user task listeners are configured and the corresponding listener job is not completed within the request timeout. Common causes include no available job workers for the listener type, busy or crashed job workers, or delayed job completion. As with any gateway timeout, general timeout causes (for example transient network issues) can also result in a 504 response. Troubleshooting: - verify that job workers for the listener type are running and healthy - check worker logs for crashes, retries, and completion failures - check network connectivity between workers, gateway, and broker - retry with backoff after transient failures - fail without retries if a problem persists - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Cancel a process instance:** ```python def cancel_process_instance_example(process_definition_id: ProcessDefinitionId) -> None: client = CamundaClient() # Create a process instance and get its key from the response created = client.create_process_instance( data=ProcessCreationById(process_definition_id=process_definition_id) ) # Cancel it using the key from the creation response client.cancel_process_instance( process_instance_key=created.process_instance_key, ) ``` ### cancel_process_instances_batch_operation() ```python def cancel_process_instances_batch_operation(, data, **kwargs) ``` Cancel process instances (batch) > Cancels multiple running process instances. Since only ACTIVE root instances can be cancelled, any given filters for state and parentProcessInstanceKey are ignored and overridden during this batch operation. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). - **Parameters:** - **body** (_ProcessInstanceCancellationBatchOperationRequest_) – The process instance filter that defines which process instances should be canceled. - **data** (_ProcessInstanceCancellationBatchOperationRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The process instance batch operation failed. More details are provided in the response body. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** BatchOperationCreatedResult - **Return type:** BatchOperationCreatedResult #### Examples **Cancel process instances in batch:** ```python def cancel_process_instances_batch_operation_example() -> None: client = CamundaClient() result = client.cancel_process_instances_batch_operation( data=ProcessInstanceCancellationBatchOperationRequest( filter_=ProcessInstanceCancellationBatchOperationRequestFilter(), ), ) print(f"Batch operation key: {result.batch_operation_key}") ``` ### client ```python client: [Client](configuration.md#camunda_orchestration_sdk.Client) | [AuthenticatedClient](configuration.md#camunda_orchestration_sdk.AuthenticatedClient) ``` ### close() ```python def close() ``` Close underlying HTTP clients. This closes both the API client’s httpx client and, when available, the auth provider’s token client. - **Return type:** None ### complete_job() ```python def complete_job(job_key, *, data=, **kwargs) ``` Complete job > Complete a job with the given payload, which allows completing the associated service task. - **Parameters:** - **job_key** (_str_) – System-generated key for a job. Example: 2251799813653498. - **body** (_JobCompletionRequest_ _|_ _Unset_) - **data** (_JobCompletionRequest_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The job with the given key was not found. - **errors.ConflictError** – If the response status code is 409. The job with the given key is in the wrong state currently. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Complete a job:** ```python def complete_job_example(job_key: JobKey) -> None: client = CamundaClient() client.complete_job( job_key=job_key, data=JobCompletionRequest( variables=JobCompletionRequestVariables.from_dict( {"paymentId": "PAY-123", "status": "completed"} ) ), ) ``` ### complete_user_task() ```python def complete_user_task(user_task_key, *, data=, **kwargs) ``` Complete user task > Completes a user task with the given key. Completion waits for blocking task listeners on this lifecycle transition. If listener processing is delayed beyond the request timeout, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. - **Parameters:** - **user_task_key** (_str_) – System-generated key for a user task. - **body** (_UserTaskCompletionRequest_ _|_ _Unset_) - **data** (_UserTaskCompletionRequest_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The user task with the given key was not found. - **errors.ConflictError** – If the response status code is 409. The user task with the given key is in the wrong state currently. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.GatewayTimeoutError** – If the response status code is 504. The request timed out between the gateway and the broker. For these endpoints, this often happens when user task listeners are configured and the corresponding listener job is not completed within the request timeout. Common causes include no available job workers for the listener type, busy or crashed job workers, or delayed job completion. As with any gateway timeout, general timeout causes (for example transient network issues) can also result in a 504 response. Troubleshooting: - verify that job workers for the listener type are running and healthy - check worker logs for crashes, retries, and completion failures - check network connectivity between workers, gateway, and broker - retry with backoff after transient failures - fail without retries if a problem persists - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Complete a user task:** ```python def complete_user_task_example(user_task_key: UserTaskKey) -> None: client = CamundaClient() variables = UserTaskCompletionRequestVariables() variables["approved"] = True client.complete_user_task( user_task_key=user_task_key, data=UserTaskCompletionRequest( variables=variables, ), ) ``` ### configuration ```python configuration: [CamundaSdkConfiguration](runtime.md#camunda_orchestration_sdk.runtime.configuration_resolver.CamundaSdkConfiguration) ``` ### correlate_message() ```python def correlate_message(, data, **kwargs) ``` Correlate message > Publishes a message and correlates it to a subscription. If correlation is successful it will return the first process instance key the message correlated with. The message is not buffered. Use the publish message endpoint to send messages that can be buffered. - **Parameters:** - **body** (_MessageCorrelationRequest_) - **data** (_MessageCorrelationRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** MessageCorrelationResult - **Return type:** MessageCorrelationResult #### Examples **Correlate a message:** ```python def correlate_message_example() -> None: client = CamundaClient() result = client.correlate_message( data=MessageCorrelationRequest( name="payment-received", correlation_key="order-12345", ) ) print(f"Message key: {result.message_key}") ``` ### create_admin_user() ```python def create_admin_user(, data, **kwargs) ``` Create admin user > Creates a new user and assigns the admin role to it. This endpoint is only usable when users are managed in the Orchestration Cluster and while no user is assigned to the admin role. - **Parameters:** - **body** (_UserRequest_) - **data** (_UserRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** UserCreateResult - **Return type:** UserCreateResult #### Examples **Create an admin user:** ```python def create_admin_user_example(username: Username) -> None: client = CamundaClient() result = client.create_admin_user( data=UserRequest( username=username, name="Admin User", email="admin@example.com", password="admin-password", ), ) print(f"Admin user: {result.username}") ``` ### create_agent_instance() ```python def create_agent_instance(, data, **kwargs) ``` Create agent instance > Creates a new agent instance. The returned key identifies the instance and must be used in subsequent update and query calls. - **Parameters:** - **body** (_AgentInstanceCreationRequest_) – Request to create a new agent instance. - **data** (_AgentInstanceCreationRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The elementInstanceKey does not correspond to an active element instance. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** AgentInstanceCreationResult - **Return type:** AgentInstanceCreationResult #### Examples **Create an agent instance:** ```python def create_agent_instance_example(element_instance_key: ElementInstanceKey) -> None: client = CamundaClient() result = client.create_agent_instance( data=AgentInstanceCreationRequest( element_instance_key=element_instance_key, definition=AgentInstanceCreationRequestDefinition( model="gpt-4o", provider="openai", system_prompt="You are a helpful assistant.", ), ), ) print(f"Created agent instance: {result.agent_instance_key}") ``` ### create_authorization() ```python def create_authorization(, data, **kwargs) ``` Create authorization > Create the authorization. - **Parameters:** - **body** (_AuthorizationIdBasedRequest_ _|_ _AuthorizationPropertyBasedRequest_) - **data** (_AuthorizationIdBasedRequest_ _|_ _AuthorizationPropertyBasedRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The owner was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** AuthorizationCreateResult - **Return type:** AuthorizationCreateResult #### Examples **Create an authorization:** ```python def create_authorization_example() -> None: client = CamundaClient() result = client.create_authorization( data=AuthorizationIdBasedRequest( resource_type=AuthorizationIdBasedRequestResourceType.PROCESS_DEFINITION, permission_types=[ AuthorizationIdBasedRequestPermissionTypesItem.READ, AuthorizationIdBasedRequestPermissionTypesItem.UPDATE, ], resource_id="my-process", owner_type=OwnerTypeEnum.USER, owner_id="user@example.com", ), ) print(f"Authorization key: {result.authorization_key}") ``` ### create_deployment() ```python def create_deployment(, data, **kwargs) ``` Deploy resources > Deploys one or more resources (e.g. processes, decision models, or forms). This is an atomic call, i.e. either all resources are deployed or none of them are. - **Parameters:** - **body** (_CreateDeploymentData_) - **data** (_CreateDeploymentData_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** DeploymentResult - **Return type:** DeploymentResult #### Examples **From files:** ```python def deploy_resources_example() -> None: client = CamundaClient() result = client.deploy_resources_from_files( ["order-process.bpmn", "decision.dmn"] ) print(f"Deployment key: {result.deployment_key}") for process in result.processes: print( f" Process: {process.process_definition_id} v{process.process_definition_version}" ) for decision in result.decisions: print(f" Decision: {decision.decision_definition_id}") ``` **With tenant ID:** ```python def deploy_resources_with_tenant_example() -> None: client = CamundaClient() result = client.deploy_resources_from_files( ["order-process.bpmn"], tenant_id="my-tenant", ) print(f"Deployment key: {result.deployment_key}") print(f"Tenant: {result.tenant_id}") ``` ### create_document() ```python def create_document(*, data, store_id=, document_id=, **kwargs) ``` Upload document > Upload a document to the Camunda 8 cluster. Note that this is currently supported for document stores of type: AWS, Azure, GCP, in-memory (non- production), local (non-production) - **Parameters:** - **store_id** (_str_ _|_ _Unset_) - **document_id** (_str_ _|_ _Unset_) – Document Id that uniquely identifies a document. - **body** (_CreateDocumentData_) - **data** (_CreateDocumentData_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnsupportedMediaTypeError** – If the response status code is 415. The server cannot process the request because the media type (Content-Type) of the request payload is not supported by the server for the requested resource and method. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** DocumentReference - **Return type:** DocumentReference #### Examples **Create a document:** ```python def create_document_example() -> None: client = CamundaClient() result = client.create_document( data=CreateDocumentData( file=File(payload=io.BytesIO(b"hello world"), file_name="example.txt"), ), ) print(f"Document ID: {result.document_id}") ``` ### create_document_link() ```python def create_document_link(document_id, *, data=, store_id=, content_hash=, **kwargs) ``` Create document link > Create a link to a document in the Camunda 8 cluster. Note that this is currently supported for document stores of type: AWS, Azure, GCP - **Parameters:** - **document_id** (_str_) – Document Id that uniquely identifies a document. - **store_id** (_str_ _|_ _Unset_) - **content_hash** (_str_ _|_ _Unset_) - **body** (_DocumentLinkRequest_ _|_ _Unset_) - **data** (_DocumentLinkRequest_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** DocumentLink - **Return type:** DocumentLink #### Examples **Create a document link:** ```python def create_document_link_example(document_id: DocumentId) -> None: client = CamundaClient() result = client.create_document_link( document_id=document_id, data=DocumentLinkRequest(), ) print(f"Document link: {result.url}") ``` ### create_documents() ```python def create_documents(*, data, store_id=, **kwargs) ``` Upload multiple documents > Upload multiple documents to the Camunda 8 cluster. The caller must provide a file name for each document, which will be used in case of a multi-status response to identify which documents failed to upload. The file name can be provided in the Content- Disposition header of the file part or in the fileName field of the metadata. You can add a parallel array of metadata objects. These are matched with the files based on index, and must have the same length as the files array. To pass homogenous metadata for all files, spread the metadata over the metadata array. A filename value provided explicitly via the metadata array in the request overrides the Content- Disposition header of the file part. In case of a multi-status response, the response body will contain a list of DocumentBatchProblemDetail objects, each of which contains the file name of the document that failed to upload and the reason for the failure. The client can choose to retry the whole batch or individual documents based on the response. Note that this is currently supported for document stores of type: AWS, Azure, GCP, in-memory (non- production), local (non-production) - **Parameters:** - **store_id** (_str_ _|_ _Unset_) - **body** (_CreateDocumentsData_) - **data** (_CreateDocumentsData_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnsupportedMediaTypeError** – If the response status code is 415. The server cannot process the request because the media type (Content-Type) of the request payload is not supported by the server for the requested resource and method. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** DocumentCreationBatchResponse - **Return type:** DocumentCreationBatchResponse #### Examples **Create documents:** ```python def create_documents_example() -> None: client = CamundaClient() result = client.create_documents( data=CreateDocumentsData( files=[ File(payload=io.BytesIO(b"file one"), file_name="one.txt"), File(payload=io.BytesIO(b"file two"), file_name="two.txt"), ], ), ) if not isinstance(result.created_documents, Unset): for doc in result.created_documents: print(f"Created document: {doc.document_id}") ``` ### create_element_instance_variables() ```python def create_element_instance_variables(element_instance_key, , data, **kwargs) ``` Update element instance variables > Updates all the variables of a particular scope (for example, process instance, element instance) with the given variable data. Specify the element instance in the elementInstanceKey parameter. Variable updates can be delayed by listener-related processing; if processing exceeds the request timeout, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. - **Parameters:** - **element_instance_key** (_str_) – System-generated key for a element instance. Example: 2251799813686789. - **body** (_SetVariableRequest_) - **data** (_SetVariableRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.GatewayTimeoutError** – If the response status code is 504. The request timed out between the gateway and the broker. For these endpoints, this often happens when user task listeners are configured and the corresponding listener job is not completed within the request timeout. Common causes include no available job workers for the listener type, busy or crashed job workers, or delayed job completion. As with any gateway timeout, general timeout causes (for example transient network issues) can also result in a 504 response. Troubleshooting: - verify that job workers for the listener type are running and healthy - check worker logs for crashes, retries, and completion failures - check network connectivity between workers, gateway, and broker - retry with backoff after transient failures - fail without retries if a problem persists - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Create element instance variables:** ```python def create_element_instance_variables_example(element_instance_key: ElementInstanceKey) -> None: client = CamundaClient() variables = SetVariableRequestVariables.from_dict({"myVar": "myValue"}) client.create_element_instance_variables( element_instance_key=element_instance_key, data=SetVariableRequest( variables=variables, ), ) ``` ### create_global_cluster_variable() ```python def create_global_cluster_variable(, data, **kwargs) ``` Create a global-scoped cluster variable > Create a global-scoped cluster variable. - **Parameters:** - **body** (_CreateClusterVariableRequest_) - **data** (_CreateClusterVariableRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ClusterVariableResult - **Return type:** ClusterVariableResult #### Examples **Create a global cluster variable:** ```python def create_global_cluster_variable_example(name: ClusterVariableName) -> None: client = CamundaClient() result = client.create_global_cluster_variable( data=CreateClusterVariableRequest( name=name, value=CreateClusterVariableRequestValue.from_dict({"key": "my-value"}), ), ) print(f"Created variable: {result.name}") ``` ### create_global_task_listener() ```python def create_global_task_listener(, data, **kwargs) ``` Create global user task listener > Create a new global user task listener. - **Parameters:** - **body** (_CreateGlobalTaskListenerRequest_) - **data** (_CreateGlobalTaskListenerRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.ConflictError** – If the response status code is 409. A global listener with this id already exists. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GlobalTaskListenerResult - **Return type:** GlobalTaskListenerResult #### Examples **Create a global task listener:** ```python def create_global_task_listener_example() -> None: client = CamundaClient() result = client.create_global_task_listener( data=CreateGlobalTaskListenerRequest( id="audit-log-listener", event_types=[GlobalTaskListenerEventTypeEnum.COMPLETING], type_="my-task-listener", ), ) print(f"Task listener: {result.id}") ``` ### create_group() ```python def create_group(*, data=, **kwargs) ``` Create group > Create a new group. The supplied groupId is validated against ^[a-zA-Z0-9_~@.+-]+$ (max 256 characters) by IdentifierValidator.validateId in the runtime. This strict validation applies wherever the Groups API is available: in OIDC deployments that set camunda.security.authentication.oidc.groupsClaim the Groups API (including this endpoint) is disabled entirely, so group CRUD never sees externally-minted IdP IDs. The BYOG relaxation only loosens validation when a group is referenced _as a member_ of a role or tenant (assignRoleToGroup, assignGroupToTenant); group CRUD itself always uses the strict default-id regex. The constraint is not advertised on the GroupId schema so that the same schema can be reused at member-reference sites without falsely rejecting externally-minted IdP group IDs there. - **Parameters:** - **body** (_GroupCreateRequest_ _|_ _Unset_) - **data** (_GroupCreateRequest_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GroupCreateResult - **Return type:** GroupCreateResult #### Examples **Create a group:** ```python def create_group_example(group_id: GroupId) -> None: client = CamundaClient() result = client.create_group( data=GroupCreateRequest(group_id=group_id, name="Engineering"), ) print(f"Group: {result.group_id}") ``` ### create_mapping_rule() ```python def create_mapping_rule(*, data=, **kwargs) ``` Create mapping rule > Create a new mapping rule - **Parameters:** - **body** (_MappingRuleCreateRequest_ _|_ _Unset_) - **data** (_MappingRuleCreateRequest_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. The request to create a mapping rule was denied. More details are provided in the response body. - **errors.NotFoundError** – If the response status code is 404. The request to create a mapping rule was denied. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** MappingRuleCreateResult - **Return type:** MappingRuleCreateResult #### Examples **Create a mapping rule:** ```python def create_mapping_rule_example(mapping_rule_id: MappingRuleId) -> None: client = CamundaClient() result = client.create_mapping_rule( data=MappingRuleCreateRequest( mapping_rule_id=mapping_rule_id, claim_name="groups", claim_value="engineering", name="Engineering Group Mapping", ), ) print(f"Mapping rule: {result.mapping_rule_id}") ``` ### create_process_instance() ```python def create_process_instance(, data, **kwargs) ``` Create process instance > Creates and starts an instance of the specified process. The process definition to use to create the instance can be specified either using its unique key (as returned by Deploy resources), or using the BPMN process id and a version. Waits for the completion of the process instance before returning a result when awaitCompletion is enabled. - **Parameters:** - **body** (_ProcessCreationById_ _|_ _ProcessCreationByKey_) – Instructions for creating a process instance. The process definition can be specified either by id or by key. - **data** (_ProcessCreationById_ _|_ _ProcessCreationByKey_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ConflictError** – If the response status code is 409. The process instance creation was rejected due to a business ID uniqueness conflict. This can happen only when Business ID Uniqueness Control is enabled and an active root process instance with the provided business ID already exists for the same process definition and tenant. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.GatewayTimeoutError** – If the response status code is 504. The process instance creation request timed out in the gateway. This can happen if the awaitCompletion request parameter is set to true and the created process instance did not complete within the defined request timeout. This often happens when the created instance is not fully automated or contains wait states. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** CreateProcessInstanceResult - **Return type:** CreateProcessInstanceResult #### Examples **By key:** ```python def create_process_instance_by_key_example() -> None: client = CamundaClient() # Deploy a process and obtain the typed key from the response deployment = client.deploy_resources_from_files(["order-process.bpmn"]) process_key = deployment.processes[0].process_definition_key # Use the typed key directly — no manual string lifting needed result = client.create_process_instance( data=ProcessCreationByKey( process_definition_key=process_key, ) ) print(f"Process instance key: {result.process_instance_key}") ``` **By stored key:** ```python def create_process_instance_by_key_from_storage_example() -> None: client = CamundaClient() # When restoring a key from a database or message queue, # wrap the raw string with the semantic type constructor: stored_key = "2251799813685249" # e.g. from a DB row result = client.create_process_instance( data=ProcessCreationByKey( process_definition_key=ProcessDefinitionKey(stored_key), ) ) print(f"Process instance key: {result.process_instance_key}") ``` **By ID:** ```python def create_process_instance_by_id_example(process_definition_id: ProcessDefinitionId) -> None: client = CamundaClient() result = client.create_process_instance( data=ProcessCreationById( process_definition_id=process_definition_id, ) ) print(f"Process instance key: {result.process_instance_key}") ``` ### create_role() ```python def create_role(*, data=, **kwargs) ``` Create role > Create a new role. - **Parameters:** - **body** (_RoleCreateRequest_ _|_ _Unset_) - **data** (_RoleCreateRequest_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** RoleCreateResult - **Return type:** RoleCreateResult #### Examples **Create a role:** ```python def create_role_example(role_id: RoleId) -> None: client = CamundaClient() result = client.create_role( data=RoleCreateRequest(role_id=role_id, name="Developer"), ) print(f"Role: {result.role_id}") ``` ### create_tenant() ```python def create_tenant(, data, **kwargs) ``` Create tenant > Creates a new tenant. - **Parameters:** - **body** (_TenantCreateRequest_) - **data** (_TenantCreateRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The resource was not found. - **errors.ConflictError** – If the response status code is 409. Tenant with this id already exists. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** TenantCreateResult - **Return type:** TenantCreateResult #### Examples **Create a tenant:** ```python def create_tenant_example(tenant_id: TenantId) -> None: client = CamundaClient() result = client.create_tenant( data=TenantCreateRequest( tenant_id=tenant_id, name="Acme Corporation", ), ) print(f"Tenant: {result.tenant_id}") ``` ### create_tenant_cluster_variable() ```python def create_tenant_cluster_variable(tenant_id, , data, **kwargs) ``` Create a tenant-scoped cluster variable > Create a new cluster variable for the given tenant. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **body** (_CreateClusterVariableRequest_) - **data** (_CreateClusterVariableRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The tenant with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ClusterVariableResult - **Return type:** ClusterVariableResult #### Examples **Create a tenant cluster variable:** ```python def create_tenant_cluster_variable_example(tenant_id: TenantId, name: ClusterVariableName) -> None: client = CamundaClient() result = client.create_tenant_cluster_variable( tenant_id=tenant_id, data=CreateClusterVariableRequest( name=name, value=CreateClusterVariableRequestValue.from_dict({"key": "tenant-value"}), ), ) print(f"Created variable: {result.name}") ``` ### create_user() ```python def create_user(, data, **kwargs) ``` Create user > Create a new user. - **Parameters:** - **body** (_UserRequest_) - **data** (_UserRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.ConflictError** – If the response status code is 409. A user with this username already exists. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** UserCreateResult - **Return type:** UserCreateResult #### Examples **Create a user:** ```python def create_user_example(username: Username) -> None: client = CamundaClient() result = client.create_user( data=UserRequest( username=username, name="Jane Doe", email="jdoe@example.com", password="secure-password", ), ) print(f"Created user: {result.username}") ``` ### delete_authorization() ```python def delete_authorization(authorization_key, **kwargs) ``` Delete authorization > Deletes the authorization with the given key. - **Parameters:** - **authorization_key** (_str_) – System-generated key for an authorization. Example: 2251799813684332. - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.NotFoundError** – If the response status code is 404. The authorization with the authorizationKey was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete an authorization:** ```python def delete_authorization_example(authorization_key: AuthorizationKey) -> None: client = CamundaClient() client.delete_authorization( authorization_key=authorization_key, ) ``` ### delete_decision_instance() ```python def delete_decision_instance(decision_evaluation_key, *, data=, **kwargs) ``` Delete decision instance > Delete all associated decision evaluations based on provided key. - **Parameters:** - **decision_evaluation_key** (_str_) – System-generated key for a decision evaluation. Example: 2251792362345323. - **body** (_DeleteDecisionInstanceRequest_ _|_ _None_ _|_ _Unset_) - **data** (_DeleteDecisionInstanceRequest_ _|_ _None_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The decision instance is not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete a decision instance:** ```python def delete_decision_instance_example(decision_evaluation_key: DecisionEvaluationKey) -> None: client = CamundaClient() client.delete_decision_instance( decision_evaluation_key=decision_evaluation_key, ) ``` ### delete_decision_instances_batch_operation() ```python def delete_decision_instances_batch_operation(, data, **kwargs) ``` Delete decision instances (batch) > Delete multiple decision instances. This will delete the historic data from secondary storage. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). - **Parameters:** - **body** (_DecisionInstanceDeletionBatchOperationRequest_) – The decision instance filter that defines which decision instances should be deleted. - **data** (_DecisionInstanceDeletionBatchOperationRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The decision instance batch operation failed. More details are provided in the response body. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** BatchOperationCreatedResult - **Return type:** BatchOperationCreatedResult #### Examples **Delete decision instances in batch:** ```python def delete_decision_instances_batch_operation_example() -> None: client = CamundaClient() result = client.delete_decision_instances_batch_operation( data=DecisionInstanceDeletionBatchOperationRequest( filter_=DecisionInstanceDeletionBatchOperationRequestFilter(), ), ) print(f"Batch operation key: {result.batch_operation_key}") ``` ### delete_document() ```python def delete_document(document_id, *, store_id=, **kwargs) ``` Delete document > Delete a document from the Camunda 8 cluster. Note that this is currently supported for document stores of type: AWS, Azure, GCP, in-memory (non- production), local (non-production) - **Parameters:** - **document_id** (_str_) – Document Id that uniquely identifies a document. - **store_id** (_str_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.NotFoundError** – If the response status code is 404. The document with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete a document:** ```python def delete_document_example(document_id: DocumentId) -> None: client = CamundaClient() client.delete_document(document_id=document_id) ``` ### delete_global_cluster_variable() ```python def delete_global_cluster_variable(name, **kwargs) ``` Delete a global-scoped cluster variable > Delete a global-scoped cluster variable. - **Parameters:** - **name** (_str_) – The name of a cluster variable. Unique within its scope (global or tenant- specific). Example: feature-flag-checkout. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Cluster variable not found - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete a global cluster variable:** ```python def delete_global_cluster_variable_example(name: ClusterVariableName) -> None: client = CamundaClient() client.delete_global_cluster_variable(name=name) ``` ### delete_global_task_listener() ```python def delete_global_task_listener(id, **kwargs) ``` Delete global user task listener > Deletes a global user task listener. - **Parameters:** - **id** (_str_) – The user-defined id for the global listener Example: GlobalListener_1. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The global user task listener was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete a global task listener:** ```python def delete_global_task_listener_example(listener_id: GlobalListenerId) -> None: client = CamundaClient() client.delete_global_task_listener(id=listener_id) ``` ### delete_group() ```python def delete_group(group_id, **kwargs) ``` Delete group > Deletes the group with the given ID. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.NotFoundError** – If the response status code is 404. The group with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete a group:** ```python def delete_group_example(group_id: GroupId) -> None: client = CamundaClient() client.delete_group(group_id=group_id) ``` ### delete_mapping_rule() ```python def delete_mapping_rule(mapping_rule_id, **kwargs) ``` Delete a mapping rule > Deletes the mapping rule with the given ID. - **Parameters:** - **mapping_rule_id** (_str_) – The unique identifier of a mapping rule. Example: my-mapping-rule. - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.NotFoundError** – If the response status code is 404. The mapping rule with the mappingRuleId was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete a mapping rule:** ```python def delete_mapping_rule_example(mapping_rule_id: MappingRuleId) -> None: client = CamundaClient() client.delete_mapping_rule(mapping_rule_id=mapping_rule_id) ``` ### delete_process_instance() ```python def delete_process_instance(process_instance_key, *, data=, **kwargs) ``` Delete process instance > Deletes a process instance. Only instances that are completed or terminated can be deleted. - **Parameters:** - **process_instance_key** (_str_) – System-generated key for a process instance. Example: 2251799813690746. - **body** (_DeleteProcessInstanceRequest_ _|_ _None_ _|_ _Unset_) - **data** (_DeleteProcessInstanceRequest_ _|_ _None_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The process instance is not found. - **errors.ConflictError** – If the response status code is 409. The process instance is not in a completed or terminated state and cannot be deleted. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete a process instance:** ```python def delete_process_instance_example(process_instance_key: ProcessInstanceKey) -> None: client = CamundaClient() client.delete_process_instance( process_instance_key=process_instance_key, ) ``` ### delete_process_instances_batch_operation() ```python def delete_process_instances_batch_operation(, data, **kwargs) ``` Delete process instances (batch) > Delete multiple process instances. This will delete the historic data from secondary storage. Only process instances in a final state (COMPLETED or TERMINATED) can be deleted. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). - **Parameters:** - **body** (_ProcessInstanceDeletionBatchOperationRequest_) – The process instance filter that defines which process instances should be deleted. - **data** (_ProcessInstanceDeletionBatchOperationRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The process instance batch operation failed. More details are provided in the response body. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** BatchOperationCreatedResult - **Return type:** BatchOperationCreatedResult #### Examples **Delete process instances in batch:** ```python def delete_process_instances_batch_operation_example() -> None: client = CamundaClient() result = client.delete_process_instances_batch_operation( data=ProcessInstanceDeletionBatchOperationRequest( filter_=ProcessInstanceCancellationBatchOperationRequestFilter(), ), ) print(f"Batch operation key: {result.batch_operation_key}") ``` ### delete_resource() ```python def delete_resource(resource_key, *, data=, **kwargs) ``` Delete resource > Deletes a deployed resource. This can be a process definition, decision requirements definition, or form definition deployed using the deploy resources endpoint. Specify the resource you want to delete in the resourceKey parameter. Once a resource has been deleted it cannot be recovered. If the resource needs to be available again, a new deployment of the resource is required. By default, only the resource itself is deleted from the runtime state. To also delete the historic data associated with a resource, set the deleteHistory flag in the request body to true. The historic data is deleted asynchronously via a batch operation. The details of the created batch operation are included in the response. Note that history deletion is only supported for process resources; for other resource types this flag is ignored and no history will be deleted. - **Parameters:** - **resource_key** (_str_) – The system-assigned key for this resource. - **body** (_DeleteResourceRequest_ _|_ _None_ _|_ _Unset_) - **data** (_DeleteResourceRequest_ _|_ _None_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The resource is not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** DeleteResourceResponse - **Return type:** DeleteResourceResponse #### Examples **Delete a resource:** ```python def delete_resource_example() -> None: client = CamundaClient() # Use a resource key from a previous deployment response client.delete_resource(resource_key="2251799813685249") ``` ### delete_role() ```python def delete_role(role_id, **kwargs) ``` Delete role > Deletes the role with the given ID. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.NotFoundError** – If the response status code is 404. The role with the ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete a role:** ```python def delete_role_example(role_id: RoleId) -> None: client = CamundaClient() client.delete_role(role_id=role_id) ``` ### delete_tenant() ```python def delete_tenant(tenant_id, **kwargs) ``` Delete tenant > Deletes an existing tenant. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The tenant was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete a tenant:** ```python def delete_tenant_example(tenant_id: TenantId) -> None: client = CamundaClient() client.delete_tenant(tenant_id=tenant_id) ``` ### delete_tenant_cluster_variable() ```python def delete_tenant_cluster_variable(tenant_id, name, **kwargs) ``` Delete a tenant-scoped cluster variable > Delete a tenant-scoped cluster variable. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **name** (_str_) – The name of a cluster variable. Unique within its scope (global or tenant- specific). Example: feature-flag-checkout. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Cluster variable not found - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete a tenant cluster variable:** ```python def delete_tenant_cluster_variable_example(tenant_id: TenantId, name: ClusterVariableName) -> None: client = CamundaClient() client.delete_tenant_cluster_variable( tenant_id=tenant_id, name=name, ) ``` ### delete_user() ```python def delete_user(username, **kwargs) ``` Delete user > Deletes a user. - **Parameters:** - **username** (_str_) – The unique name of a user. Example: swillis. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The user is not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Delete a user:** ```python def delete_user_example(username: Username) -> None: client = CamundaClient() client.delete_user(username=username) ``` ### deploy_resources_from_files() ```python def deploy_resources_from_files(files, tenant_id=None) ``` Deploy BPMN/DMN/Form resources from local files. This is a convenience wrapper around [`create_deployment()`](#create_deployment) that: - Reads each path in `files` as bytes. - Wraps the bytes in `camunda_orchestration_sdk.types.File` using the file’s basename as `file_name`. - Builds `camunda_orchestration_sdk.models.CreateDeploymentData` and calls [`create_deployment()`](#create_deployment). - Returns an `ExtendedDeploymentResult`, which is the deployment response plus convenience lists (`processes`, `decisions`, `decision_requirements`, `forms`). * **Parameters:** - **files** (_list_ _[\*\*str_ _|_ _Path_ _]_) – File paths (`str` or `Path`) to deploy. - **tenant_id** (_str_ _|_ _None_) – Optional tenant identifier. If not provided, the default tenant is used. * **Returns:** The deployment result with extracted resource lists. * **Return type:** ExtendedDeploymentResult * **Raises:** - **FileNotFoundError** – If any file path does not exist. - **PermissionError** – If any file path cannot be read. - **IsADirectoryError** – If any file path is a directory. - **OSError** – For other I/O failures while reading files. - **Exception** – Propagates any exception raised by [`create_deployment()`](#create_deployment) (including typed API errors in `camunda_orchestration_sdk.errors` and `httpx.TimeoutException`). ### evaluate_conditionals() ```python def evaluate_conditionals(, data, **kwargs) ``` Evaluate root level conditional start events > Evaluates root-level conditional start events for process definitions. If the evaluation is successful, it will return the keys of all created process instances, along with their associated process definition key. Multiple root-level conditional start events of the same process definition can trigger if their conditions evaluate to true. - **Parameters:** - **body** (_ConditionalEvaluationInstruction_) - **data** (_ConditionalEvaluationInstruction_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. The client is not authorized to start process instances for the specified process definition. If a processDefinitionKey is not provided, this indicates that the client is not authorized to start process instances for at least one of the matched process definitions. - **errors.NotFoundError** – If the response status code is 404. The process definition was not found for the given processDefinitionKey. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** EvaluateConditionalResult - **Return type:** EvaluateConditionalResult #### Examples **Evaluate conditionals:** ```python def evaluate_conditionals_example() -> None: client = CamundaClient() result = client.evaluate_conditionals( data=ConditionalEvaluationInstruction( variables=ConditionalEvaluationInstructionVariables.from_dict({"orderReady": True}), ), ) print(f"Result: {result}") ``` ### evaluate_decision() ```python def evaluate_decision(, data, **kwargs) ``` Evaluate decision > Evaluates a decision. You specify the decision to evaluate either by using its unique key (as returned by DeployResource), or using the decision ID. When using the decision ID, the latest deployed version of the decision is used. - **Parameters:** - **body** (_DecisionEvaluationByID_ _|_ _DecisionEvaluationByKey_) - **data** (_DecisionEvaluationByID_ _|_ _DecisionEvaluationByKey_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The decision is not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** EvaluateDecisionResult - **Return type:** EvaluateDecisionResult #### Examples **By key:** ```python def evaluate_decision_by_key_example(decision_definition_key: DecisionDefinitionKey) -> None: client = CamundaClient() result = client.evaluate_decision( data=DecisionEvaluationByKey( decision_definition_key=decision_definition_key, ) ) print(f"Decision key: {result.decision_definition_key}") ``` **By ID:** ```python def evaluate_decision_by_id_example(decision_definition_id: DecisionDefinitionId) -> None: client = CamundaClient() result = client.evaluate_decision( data=DecisionEvaluationByID( decision_definition_id=decision_definition_id, ) ) print(f"Decision key: {result.decision_definition_key}") ``` ### evaluate_expression() ```python def evaluate_expression(, data, **kwargs) ``` Evaluate an expression > Evaluates a FEEL expression and returns the result. Supports references to tenant scoped cluster variables when a tenant ID is provided. - **Parameters:** - **body** (_ExpressionEvaluationRequest_) - **data** (_ExpressionEvaluationRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ExpressionEvaluationResult - **Return type:** ExpressionEvaluationResult #### Examples **Evaluate an expression:** ```python def evaluate_expression_example() -> None: client = CamundaClient() result = client.evaluate_expression( data=ExpressionEvaluationRequest( expression="= 1 + 2", ), ) print(f"Result: {result.result}") ``` ### fail_job() ```python def fail_job(job_key, *, data=, **kwargs) ``` Fail job > Mark the job as failed. - **Parameters:** - **job_key** (_str_) – System-generated key for a job. Example: 2251799813653498. - **body** (_JobFailRequest_ _|_ _Unset_) - **data** (_JobFailRequest_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The job with the given jobKey is not found. It was completed by another worker, or the process instance itself was canceled. - **errors.ConflictError** – If the response status code is 409. The job with the given key is in the wrong state (i.e: not ACTIVATED or ACTIVATABLE). The job was failed by another worker with retries = 0, and the process is now in an incident state. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Fail a job with retry:** ```python def fail_job_example(job_key: JobKey) -> None: client = CamundaClient() client.fail_job( job_key=job_key, data=JobFailRequest( retries=2, error_message="Payment gateway timeout", retry_back_off=5000, ), ) ``` ### get_agent_instance() ```python def get_agent_instance(agent_instance_key, , consistency=None, **kwargs) ``` Get agent instance > Returns agent instance as JSON. - **Parameters:** - **agent_instance_key** (_str_) – System-generated key for an agent instance. Example: 4503599627370496. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The agent instance with the given key was not found. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** AgentInstanceResult - **Return type:** AgentInstanceResult #### Examples **Get an agent instance:** ```python def get_agent_instance_example(agent_instance_key: AgentInstanceKey) -> None: client = CamundaClient() agent_instance = client.get_agent_instance(agent_instance_key=agent_instance_key) print(f"Agent instance status: {agent_instance.status}") ``` ### get_audit_log() ```python def get_audit_log(audit_log_key, , consistency=None, **kwargs) ``` Get audit log > Get an audit log entry by auditLogKey. - **Parameters:** - **audit_log_key** (_str_) – System-generated key for an audit log entry. Example: 22517998136843567. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The audit log with the given key was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** AuditLogResult - **Return type:** AuditLogResult #### Examples **Get an audit log entry:** ```python def get_audit_log_example(audit_log_key: AuditLogKey) -> None: client = CamundaClient() result = client.get_audit_log(audit_log_key=audit_log_key) print(f"Audit log: {result.audit_log_key}") ``` ### get_authentication() ```python def get_authentication(**kwargs) ``` Get current user > Retrieves the current authenticated user. - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** CamundaUserResult - **Parameters:** **kwargs** (_Any_) - **Return type:** CamundaUserResult #### Examples **Get authentication info:** ```python def get_authentication_example() -> None: client = CamundaClient() result = client.get_authentication() print(f"Authenticated user: {result.username}") ``` ### get_authorization() ```python def get_authorization(authorization_key, , consistency=None, **kwargs) ``` Get authorization > Get authorization by the given key. - **Parameters:** - **authorization_key** (_str_) – System-generated key for an authorization. Example: 2251799813684332. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The authorization with the given key was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** AuthorizationResult - **Return type:** AuthorizationResult #### Examples **Get an authorization:** ```python def get_authorization_example(authorization_key: AuthorizationKey) -> None: client = CamundaClient() result = client.get_authorization( authorization_key=authorization_key, ) print(f"Resource type: {result.resource_type}") ``` ### get_batch_operation() ```python def get_batch_operation(batch_operation_key, , consistency=None, **kwargs) ``` Get batch operation > Get batch operation by key. - **Parameters:** - **batch_operation_key** (_str_) – System-generated key for an batch operation. Example: 2251799813684321. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The batch operation is not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** BatchOperationResponse - **Return type:** BatchOperationResponse #### Examples **Get a batch operation:** ```python def get_batch_operation_example(batch_operation_key: BatchOperationKey) -> None: client = CamundaClient() result = client.get_batch_operation( batch_operation_key=batch_operation_key, ) print(f"Batch operation: {result.batch_operation_key}") ``` ### get_decision_definition() ```python def get_decision_definition(decision_definition_key, , consistency=None, **kwargs) ``` Get decision definition > Returns a decision definition by key. - **Parameters:** - **decision_definition_key** (_str_) – System-generated key for a decision definition. Example: 2251799813326547. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The decision definition with the given key was not found. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** DecisionDefinitionResult - **Return type:** DecisionDefinitionResult #### Examples **Get a decision definition:** ```python def get_decision_definition_example(decision_definition_key: DecisionDefinitionKey) -> None: client = CamundaClient() definition = client.get_decision_definition( decision_definition_key=decision_definition_key, ) print(f"Decision: {definition.decision_definition_id}") ``` ### get_decision_definition_xml() ```python def get_decision_definition_xml(decision_definition_key, , consistency=None, **kwargs) ``` Get decision definition XML > Returns decision definition as XML. - **Parameters:** - **decision_definition_key** (_str_) – System-generated key for a decision definition. Example: 2251799813326547. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The decision definition with the given key was not found. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** str - **Return type:** str #### Examples **Get decision definition XML:** ```python def get_decision_definition_xml_example(decision_definition_key: DecisionDefinitionKey) -> None: client = CamundaClient() xml = client.get_decision_definition_xml( decision_definition_key=decision_definition_key, ) print(f"XML length: {len(xml)}") ``` ### get_decision_instance() ```python def get_decision_instance(decision_evaluation_instance_key, , consistency=None, **kwargs) ``` Get decision instance > Returns a decision instance. - **Parameters:** - **decision_evaluation_instance_key** (_str_) – System-generated identifier for a decision evaluation instance. It is composed of the parent decision evaluation key and the 1-based index of the evaluated decision within that evaluation, joined by a hyphen (format: -). > Example: 2251799813684367-1. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The decision instance with the given key was not found. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** DecisionInstanceGetQueryResult - **Return type:** DecisionInstanceGetQueryResult #### Examples **Get a decision instance:** ```python def get_decision_instance_example(decision_evaluation_instance_key: DecisionEvaluationInstanceKey) -> None: client = CamundaClient() result = client.get_decision_instance( decision_evaluation_instance_key=decision_evaluation_instance_key, ) print(f"Decision instance: {result.decision_definition_id}") ``` ### get_decision_requirements() ```python def get_decision_requirements(decision_requirements_key, , consistency=None, **kwargs) ``` Get decision requirements > Returns Decision Requirements as JSON. - **Parameters:** - **decision_requirements_key** (_str_) – System-generated key for a deployed decision requirements definition. Example: 2251799813683346. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The decision requirements with the given key was not found. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** DecisionRequirementsResult - **Return type:** DecisionRequirementsResult #### Examples **Get decision requirements:** ```python def get_decision_requirements_example(decision_requirements_key: DecisionRequirementsKey) -> None: client = CamundaClient() result = client.get_decision_requirements( decision_requirements_key=decision_requirements_key, ) print(f"DRD: {result.decision_requirements_name}") ``` ### get_decision_requirements_xml() ```python def get_decision_requirements_xml(decision_requirements_key, , consistency=None, **kwargs) ``` Get decision requirements XML > Returns decision requirements as XML. - **Parameters:** - **decision_requirements_key** (_str_) – System-generated key for a deployed decision requirements definition. Example: 2251799813683346. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The decision requirements with the given key was not found. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** str - **Return type:** str #### Examples **Get decision requirements XML:** ```python def get_decision_requirements_xml_example(decision_requirements_key: DecisionRequirementsKey) -> None: client = CamundaClient() xml = client.get_decision_requirements_xml( decision_requirements_key=decision_requirements_key, ) print(f"XML length: {len(xml)}") ``` ### get_document() ```python def get_document(document_id, *, store_id=, content_hash=, **kwargs) ``` Download document > Download a document from the Camunda 8 cluster. Note that this is currently supported for document stores of type: AWS, Azure, GCP, in-memory (non- production), local (non-production) - **Parameters:** - **document_id** (_str_) – Document Id that uniquely identifies a document. - **store_id** (_str_ _|_ _Unset_) - **content_hash** (_str_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.NotFoundError** – If the response status code is 404. The document with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** File - **Return type:** File #### Examples **Get a document:** ```python def get_document_example(document_id: DocumentId) -> None: client = CamundaClient() result = client.get_document(document_id=document_id) print(f"File name: {result.file_name}") ``` ### get_element_instance() ```python def get_element_instance(element_instance_key, , consistency=None, **kwargs) ``` Get element instance > Returns element instance as JSON. - **Parameters:** - **element_instance_key** (_str_) – System-generated key for a element instance. Example: 2251799813686789. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The element instance with the given key was not found. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ElementInstanceResult - **Return type:** ElementInstanceResult #### Examples **Get an element instance:** ```python def get_element_instance_example(element_instance_key: ElementInstanceKey) -> None: client = CamundaClient() result = client.get_element_instance( element_instance_key=element_instance_key, ) print(f"Element: {result.element_id}") ``` ### get_form_by_key() ```python def get_form_by_key(form_key, , consistency=None, **kwargs) ``` Get form by key > Get a form by its unique form key. - **Parameters:** - **form_key** (_str_) – System-generated key for a deployed form. Example: 2251799813684365. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The form with the given key was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** FormResult - **Return type:** FormResult #### Examples **Get a form by key:** ```python def get_form_by_key_example(form_key: FormKey) -> None: client = CamundaClient() result = client.get_form_by_key(form_key=form_key) print(f"Form: {result.form_id}") ``` ### get_global_cluster_variable() ```python def get_global_cluster_variable(name, , consistency=None, **kwargs) ``` Get a global-scoped cluster variable > Get a global-scoped cluster variable. - **Parameters:** - **name** (_str_) – The name of a cluster variable. Unique within its scope (global or tenant- specific). Example: feature-flag-checkout. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Cluster variable not found - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ClusterVariableResult - **Return type:** ClusterVariableResult #### Examples **Get a global cluster variable:** ```python def get_global_cluster_variable_example(name: ClusterVariableName) -> None: client = CamundaClient() result = client.get_global_cluster_variable(name=name) print(f"Variable: {result.name} = {result.value}") ``` ### get_global_job_statistics() ```python def get_global_job_statistics(*, from_, to, job_type=, consistency=None, **kwargs) ``` Global job statistics > Returns global aggregated counts for jobs. Filter by the creation time window (required) and optionally by jobType. - **Parameters:** - **from** (_datetime.datetime_) - **to** (_datetime.datetime_) - **job_type** (_str_ _|_ _Unset_) - **from\_** (_datetime.datetime_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GlobalJobStatisticsQueryResult - **Return type:** GlobalJobStatisticsQueryResult #### Examples **Get global job statistics:** ```python def get_global_job_statistics_example() -> None: client = CamundaClient() result = client.get_global_job_statistics( from_=datetime.datetime(2024, 1, 1), to=datetime.datetime(2024, 12, 31), ) print(f"Global job stats: {result}") ``` ### get_global_task_listener() ```python def get_global_task_listener(id, , consistency=None, **kwargs) ``` Get global user task listener > Get a global user task listener by its id. - **Parameters:** - **id** (_str_) – The user-defined id for the global listener Example: GlobalListener_1. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The global user task listener with the given id was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GlobalTaskListenerResult - **Return type:** GlobalTaskListenerResult #### Examples **Get a global task listener:** ```python def get_global_task_listener_example(listener_id: GlobalListenerId) -> None: client = CamundaClient() result = client.get_global_task_listener(id=listener_id) print(f"Task listener: {result.event_types}") ``` ### get_group() ```python def get_group(group_id, , consistency=None, **kwargs) ``` Get group > Get a group by its ID. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The group with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GroupResult - **Return type:** GroupResult #### Examples **Get a group:** ```python def get_group_example(group_id: GroupId) -> None: client = CamundaClient() result = client.get_group(group_id=group_id) print(f"Group: {result.name}") ``` ### get_incident() ```python def get_incident(incident_key, , consistency=None, **kwargs) ``` Get incident > Returns incident as JSON. - **Parameters:** - **incident_key** (_str_) – System-generated key for a incident. Example: 2251799813689432. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The incident with the given key was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** IncidentResult - **Return type:** IncidentResult #### Examples **Get an incident:** ```python def get_incident_example(incident_key: IncidentKey) -> None: client = CamundaClient() incident = client.get_incident(incident_key=incident_key) print(f"Incident error type: {incident.error_type}") ``` ### get_job_error_statistics() ```python def get_job_error_statistics(, data, consistency=None, **kwargs) ``` Get error metrics for a job type > Returns aggregated metrics per error for the given jobType. - **Parameters:** - **body** (_JobErrorStatisticsQuery_) – Job error statistics query. - **data** (_JobErrorStatisticsQuery_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** JobErrorStatisticsQueryResult - **Return type:** JobErrorStatisticsQueryResult #### Examples **Get job error statistics:** ```python def get_job_error_statistics_example() -> None: client = CamundaClient() result = client.get_job_error_statistics( data=JobErrorStatisticsQuery( filter_=JobErrorStatisticsFilter( from_=datetime.datetime(2024, 1, 1), to=datetime.datetime(2024, 12, 31), job_type="payment-processing", ), ), ) if not isinstance(result.items, Unset): for stat in result.items: print(f"Error: {stat.error_code}") ``` ### get_job_time_series_statistics() ```python def get_job_time_series_statistics(, data, consistency=None, **kwargs) ``` Get time-series metrics for a job type > Returns a list of time-bucketed metrics ordered ascending by time. The from and to fields select the time window of interest. Each item in the response corresponds to one time bucket of the requested resolution. - **Parameters:** - **body** (_JobTimeSeriesStatisticsQuery_) – Job time-series statistics query. - **data** (_JobTimeSeriesStatisticsQuery_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** JobTimeSeriesStatisticsQueryResult - **Return type:** JobTimeSeriesStatisticsQueryResult #### Examples **Get job time series statistics:** ```python def get_job_time_series_statistics_example() -> None: client = CamundaClient() result = client.get_job_time_series_statistics( data=JobTimeSeriesStatisticsQuery( filter_=JobTimeSeriesStatisticsFilter( from_=datetime.datetime(2024, 1, 1), to=datetime.datetime(2024, 12, 31), job_type="payment-processing", ), ), ) if not isinstance(result.items, Unset): for stat in result.items: print(f"Time series: {stat}") ``` ### get_job_type_statistics() ```python def get_job_type_statistics(, data, consistency=None, **kwargs) ``` Get job statistics by type > Get statistics about jobs, grouped by job type. - **Parameters:** - **body** (_JobTypeStatisticsQuery_) – Job type statistics query. - **data** (_JobTypeStatisticsQuery_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** JobTypeStatisticsQueryResult - **Return type:** JobTypeStatisticsQueryResult #### Examples **Get job type statistics:** ```python def get_job_type_statistics_example() -> None: client = CamundaClient() result = client.get_job_type_statistics( data=JobTypeStatisticsQuery(), ) if not isinstance(result.items, Unset): for stat in result.items: print(f"Job type: {stat.job_type}") ``` ### get_job_worker_statistics() ```python def get_job_worker_statistics(, data, consistency=None, **kwargs) ``` Get job statistics by worker > Get statistics about jobs, grouped by worker, for a given job type. - **Parameters:** - **body** (_JobWorkerStatisticsQuery_) – Job worker statistics query. - **data** (_JobWorkerStatisticsQuery_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** JobWorkerStatisticsQueryResult - **Return type:** JobWorkerStatisticsQueryResult #### Examples **Get job worker statistics:** ```python def get_job_worker_statistics_example() -> None: client = CamundaClient() result = client.get_job_worker_statistics( data=JobWorkerStatisticsQuery( filter_=JobWorkerStatisticsFilter( from_=datetime.datetime(2024, 1, 1), to=datetime.datetime(2024, 12, 31), job_type="payment-processing", ), ), ) if not isinstance(result.items, Unset): for stat in result.items: print(f"Worker: {stat.worker}") ``` ### get_license() ```python def get_license(**kwargs) ``` Get license status > Obtains the status of the current Camunda license. - **Raises:** - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** LicenseResponse - **Parameters:** **kwargs** (_Any_) - **Return type:** LicenseResponse #### Examples **Get license information:** ```python def get_license_example() -> None: client = CamundaClient() result = client.get_license() print(f"License type: {result.license_type}") ``` ### get_mapping_rule() ```python def get_mapping_rule(mapping_rule_id, , consistency=None, **kwargs) ``` Get a mapping rule > Gets the mapping rule with the given ID. - **Parameters:** - **mapping_rule_id** (_str_) – The unique identifier of a mapping rule. Example: my-mapping-rule. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.NotFoundError** – If the response status code is 404. The mapping rule with the mappingRuleId was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** MappingRuleResult - **Return type:** MappingRuleResult #### Examples **Get a mapping rule:** ```python def get_mapping_rule_example(mapping_rule_id: MappingRuleId) -> None: client = CamundaClient() result = client.get_mapping_rule(mapping_rule_id=mapping_rule_id) print(f"Mapping rule: {result.name}") ``` ### get_process_definition() ```python def get_process_definition(process_definition_key, , consistency=None, **kwargs) ``` Get process definition > Returns process definition as JSON. - **Parameters:** - **process_definition_key** (_str_) – System-generated key for a deployed process definition. Example: 2251799813686749. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The process definition with the given key was not found. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ProcessDefinitionResult - **Return type:** ProcessDefinitionResult #### Examples **Get a process definition:** ```python def get_process_definition_example(process_definition_key: ProcessDefinitionKey) -> None: client = CamundaClient() result = client.get_process_definition( process_definition_key=process_definition_key, ) print(f"Process definition: {result.name}") ``` ### get_process_definition_instance_statistics() ```python def get_process_definition_instance_statistics(*, data=, consistency=None, **kwargs) ``` Get process instance statistics > Get statistics about process instances, grouped by process definition and tenant. - **Parameters:** - **body** (_ProcessDefinitionInstanceStatisticsQuery_ _|_ _Unset_) - **data** (_ProcessDefinitionInstanceStatisticsQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ProcessDefinitionInstanceStatisticsQueryResult - **Return type:** ProcessDefinitionInstanceStatisticsQueryResult #### Examples **Get process definition instance statistics:** ```python def get_process_definition_instance_statistics_example() -> None: client = CamundaClient() result = client.get_process_definition_instance_statistics( data=ProcessDefinitionInstanceStatisticsQuery(), ) if not isinstance(result.items, Unset): for stat in result.items: print(f"Definition: {stat.process_definition_id}") ``` ### get_process_definition_instance_version_statistics() ```python def get_process_definition_instance_version_statistics(, data, consistency=None, **kwargs) ``` Get process instance statistics by version > Get statistics about process instances, grouped by version for a given process definition. The process definition ID must be provided as a required field in the request body filter. - **Parameters:** - **body** (_ProcessDefinitionInstanceVersionStatisticsQuery_) - **data** (_ProcessDefinitionInstanceVersionStatisticsQuery_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ProcessDefinitionInstanceVersionStatisticsQueryResult - **Return type:** ProcessDefinitionInstanceVersionStatisticsQueryResult #### Examples **Get version statistics:** ```python def get_process_definition_instance_version_statistics_example(process_definition_id: ProcessDefinitionId) -> None: client = CamundaClient() result = client.get_process_definition_instance_version_statistics( data=ProcessDefinitionInstanceVersionStatisticsQuery( filter_=ProcessDefinitionInstanceVersionStatisticsQueryFilter( process_definition_id=process_definition_id, ), ), ) if not isinstance(result.items, Unset): for stat in result.items: print(f"Version: {stat.process_definition_version}") ``` ### get_process_definition_message_subscription_statistics() ```python def get_process_definition_message_subscription_statistics(*, data=, consistency=None, **kwargs) ``` Get message subscription statistics > Get message subscription statistics, grouped by process definition. - **Parameters:** - **body** (_ProcessDefinitionMessageSubscriptionStatisticsQuery_ _|_ _Unset_) - **data** (_ProcessDefinitionMessageSubscriptionStatisticsQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ProcessDefinitionMessageSubscriptionStatisticsQueryResult - **Return type:** ProcessDefinitionMessageSubscriptionStatisticsQueryResult #### Examples **Get message subscription statistics:** ```python def get_process_definition_message_subscription_statistics_example() -> None: client = CamundaClient() result = client.get_process_definition_message_subscription_statistics( data=ProcessDefinitionMessageSubscriptionStatisticsQuery(), ) if not isinstance(result.items, Unset): for stat in result.items: print(f"Definition: {stat.process_definition_id}, subscriptions: {stat.active_subscriptions}") ``` ### get_process_definition_statistics() ```python def get_process_definition_statistics(process_definition_key, *, data=, consistency=None, **kwargs) ``` Get process definition statistics > Get statistics about elements in currently running process instances by process definition key and search filter. - **Parameters:** - **process_definition_key** (_str_) – System-generated key for a deployed process definition. Example: 2251799813686749. - **body** (_ProcessDefinitionElementStatisticsQuery_ _|_ _Unset_) – Process definition element statistics request. - **data** (_ProcessDefinitionElementStatisticsQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ProcessDefinitionElementStatisticsQueryResult - **Return type:** ProcessDefinitionElementStatisticsQueryResult #### Examples **Get process definition element statistics:** ```python def get_process_definition_statistics_example(process_definition_key: ProcessDefinitionKey) -> None: client = CamundaClient() result = client.get_process_definition_statistics( process_definition_key=process_definition_key, ) if not isinstance(result.items, Unset): for stat in result.items: print(f"Element: {stat.element_id}") ``` ### get_process_definition_xml() ```python def get_process_definition_xml(process_definition_key, , consistency=None, **kwargs) ``` Get process definition XML > Returns process definition as XML. - **Parameters:** - **process_definition_key** (_str_) – System-generated key for a deployed process definition. Example: 2251799813686749. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The process definition with the given key was not found. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** str - **Return type:** str #### Examples **Get process definition XML:** ```python def get_process_definition_xml_example(process_definition_key: ProcessDefinitionKey) -> None: client = CamundaClient() xml = client.get_process_definition_xml( process_definition_key=process_definition_key, ) print(f"XML length: {len(xml)}") ``` ### get_process_instance() ```python def get_process_instance(process_instance_key, , consistency=None, **kwargs) ``` Get process instance > Get the process instance by the process instance key. - **Parameters:** - **process_instance_key** (_str_) – System-generated key for a process instance. Example: 2251799813690746. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The process instance with the given key was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ProcessInstanceResult - **Return type:** ProcessInstanceResult #### Examples **Get a process instance:** ```python def get_process_instance_example(process_instance_key: ProcessInstanceKey) -> None: client = CamundaClient() result = client.get_process_instance( process_instance_key=process_instance_key, ) print(f"Process instance: {result.process_definition_id}") ``` ### get_process_instance_call_hierarchy() ```python def get_process_instance_call_hierarchy(process_instance_key, , consistency=None, **kwargs) ``` Get call hierarchy > Returns the call hierarchy for a given process instance, showing its ancestry up to the root instance. - **Parameters:** - **process_instance_key** (_str_) – System-generated key for a process instance. Example: 2251799813690746. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The process instance is not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** list[Any] - **Return type:** list[Any] #### Examples **Get process instance call hierarchy:** ```python def get_process_instance_call_hierarchy_example(process_instance_key: ProcessInstanceKey) -> None: client = CamundaClient() result = client.get_process_instance_call_hierarchy( process_instance_key=process_instance_key, ) for entry in result: print(f"Call hierarchy entry: {entry}") ``` ### get_process_instance_sequence_flows() ```python def get_process_instance_sequence_flows(process_instance_key, , consistency=None, **kwargs) ``` Get sequence flows > Get sequence flows taken by the process instance. - **Parameters:** - **process_instance_key** (_str_) – System-generated key for a process instance. Example: 2251799813690746. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ProcessInstanceSequenceFlowsQueryResult - **Return type:** ProcessInstanceSequenceFlowsQueryResult #### Examples **Get process instance sequence flows:** ```python def get_process_instance_sequence_flows_example(process_instance_key: ProcessInstanceKey) -> None: client = CamundaClient() result = client.get_process_instance_sequence_flows( process_instance_key=process_instance_key, ) if not isinstance(result.items, Unset): for flow in result.items: print(f"Sequence flow: {flow}") ``` ### get_process_instance_statistics() ```python def get_process_instance_statistics(process_instance_key, , consistency=None, **kwargs) ``` Get element instance statistics > Get statistics about elements by the process instance key. - **Parameters:** - **process_instance_key** (_str_) – System-generated key for a process instance. Example: 2251799813690746. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ProcessInstanceElementStatisticsQueryResult - **Return type:** ProcessInstanceElementStatisticsQueryResult #### Examples **Get process instance statistics:** ```python def get_process_instance_statistics_example(process_instance_key: ProcessInstanceKey) -> None: client = CamundaClient() result = client.get_process_instance_statistics( process_instance_key=process_instance_key, ) if not isinstance(result.items, Unset): for stat in result.items: print(f"Element: {stat.element_id}, Active: {stat.active}") ``` ### get_process_instance_statistics_by_definition() ```python def get_process_instance_statistics_by_definition(, data, consistency=None, **kwargs) ``` Get process instance statistics by definition > Returns statistics for active process instances with incidents, grouped by process definition. The result set is scoped to a specific incident error hash code, which must be provided as a filter in the request body. - **Parameters:** - **body** (_IncidentProcessInstanceStatisticsByDefinitionQuery_) - **data** (_IncidentProcessInstanceStatisticsByDefinitionQuery_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** IncidentProcessInstanceStatisticsByDefinitionQueryResult - **Return type:** IncidentProcessInstanceStatisticsByDefinitionQueryResult #### Examples **Get instance statistics by definition:** ```python def get_process_instance_statistics_by_definition_example() -> None: client = CamundaClient() result = client.get_process_instance_statistics_by_definition( data=IncidentProcessInstanceStatisticsByDefinitionQuery( filter_=IncidentProcessInstanceStatisticsByDefinitionQueryFilter( error_hash_code=12345, ), ), ) if not isinstance(result.items, Unset): for stat in result.items: print(f"Definition: {stat.process_definition_key}") ``` ### get_process_instance_statistics_by_error() ```python def get_process_instance_statistics_by_error(*, data=, consistency=None, **kwargs) ``` Get process instance statistics by error > Returns statistics for active process instances that currently have active incidents, grouped by incident error hash code. - **Parameters:** - **body** (_IncidentProcessInstanceStatisticsByErrorQuery_ _|_ _Unset_) - **data** (_IncidentProcessInstanceStatisticsByErrorQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** IncidentProcessInstanceStatisticsByErrorQueryResult - **Return type:** IncidentProcessInstanceStatisticsByErrorQueryResult #### Examples **Get instance statistics by error:** ```python def get_process_instance_statistics_by_error_example() -> None: client = CamundaClient() result = client.get_process_instance_statistics_by_error( data=IncidentProcessInstanceStatisticsByErrorQuery(), ) if not isinstance(result.items, Unset): for stat in result.items: print(f"Error: {stat.error_message}") ``` ### get_resource() ```python def get_resource(resource_key, , consistency=None, **kwargs) ``` Get resource > Returns a deployed resource. :::info This endpoint does not return BPMN process definitions, DMN decision definitions, or form resources. To query BPMN process definitions or DMN decision definitions, use their respective APIs. ::: - **resource_key**: The system-assigned key for this resource. ```` * **Raises:** * **errors.NotFoundError** – If the response status code is 404. A resource with the given key was not found. * **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. * **errors.UnexpectedStatus** – If the response status code is not documented. * **httpx.TimeoutException** – If the request takes longer than Client.timeout. * **Returns:** ResourceResult * **Parameters:** * **resource_key** (*str*) * **consistency** (*ConsistencyOptions* *|* *None*) * **kwargs** (*Any*) * **Return type:** ResourceResult #### Examples **Get a resource:** ```python def get_resource_example() -> None: client = CamundaClient() result = client.get_resource(resource_key="123456") print(f"Resource: {result.resource_name}") ```` ### get_resource_content() ```python def get_resource_content(resource_key, , consistency=None, **kwargs) ``` Get RPA resource content (deprecated) > **Deprecated** — use /resources/{resourceKey}/content/binary instead, which supports all resource types and returns content as binary (octet-stream). Returns the content of a deployed RPA resource as JSON. :::info This endpoint only supports RPA resources. For generic resource content in binary format, use the /resources/{resourceKey}/content/binary endpoint. ::: - **resource_key**: The system-assigned key for this resource. ```` * **Raises:** * **errors.NotFoundError** – If the response status code is 404. A resource with the given key was not found. * **errors.NotAcceptableError** – If the response status code is 406. The resource exists but is not an RPA resource. * **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. * **errors.UnexpectedStatus** – If the response status code is not documented. * **httpx.TimeoutException** – If the request takes longer than Client.timeout. * **Returns:** GetResourceContentResponse200 * **Parameters:** * **resource_key** (*str*) * **consistency** (*ConsistencyOptions* *|* *None*) * **kwargs** (*Any*) * **Return type:** GetResourceContentResponse200 #### Examples **Get resource content:** ```python def get_resource_content_example() -> None: client = CamundaClient() content = client.get_resource_content(resource_key="123456") print(f"Content: {content}") ```` ### get_resource_content_binary() ```python def get_resource_content_binary(resource_key, , consistency=None, **kwargs) ``` Get resource content as binary > Returns the content of a deployed resource in binary format (octet-stream). :::info This endpoint does not return BPMN process definitions, DMN decision definitions, or form resources. To query BPMN process definitions or DMN decision definitions, use their respective APIs. ::: - **resource_key**: The system-assigned key for this resource. ```` * **Raises:** * **errors.NotFoundError** – If the response status code is 404. A resource with the given key was not found. * **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. * **errors.UnexpectedStatus** – If the response status code is not documented. * **httpx.TimeoutException** – If the request takes longer than Client.timeout. * **Returns:** File * **Parameters:** * **resource_key** (*str*) * **consistency** (*ConsistencyOptions* *|* *None*) * **kwargs** (*Any*) * **Return type:** File #### Examples **Get resource content as binary:** ```python def get_resource_content_binary_example() -> None: client = CamundaClient() content = client.get_resource_content_binary(resource_key="123456") print(f"Binary content size: {len(content.payload.read())}") ```` ### get_role() ```python def get_role(role_id, , consistency=None, **kwargs) ``` Get role > Get a role by its ID. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** RoleResult - **Return type:** RoleResult #### Examples **Get a role:** ```python def get_role_example(role_id: RoleId) -> None: client = CamundaClient() result = client.get_role(role_id=role_id) print(f"Role: {result.name}") ``` ### get_start_process_form() ```python def get_start_process_form(process_definition_key, , consistency=None, **kwargs) ``` Get process start form > Get the start form of a process. Note that this endpoint will only return linked forms. This endpoint does not support embedded forms. - **Parameters:** - **process_definition_key** (_str_) – System-generated key for a deployed process definition. Example: 2251799813686749. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** FormResult - **Return type:** FormResult #### Examples **Get start process form:** ```python def get_start_process_form_example(process_definition_key: ProcessDefinitionKey) -> None: client = CamundaClient() result = client.get_start_process_form( process_definition_key=process_definition_key, ) print(f"Form: {result.form_key}") ``` ### get_status() ```python def get_status(**kwargs) ``` Get cluster status - **Raises:** - **errors.ServiceUnavailableError** – If the response status code is 503. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Parameters:** **kwargs** (_Any_) - **Return type:** None #### Examples **Check cluster status:** ```python def get_status_example() -> None: client = CamundaClient() client.get_status() print("Cluster is healthy") ``` ### get_system_configuration() ```python def get_system_configuration(**kwargs) ``` System configuration (alpha) > Returns the current system configuration. The response is an envelope that groups settings by feature area. This endpoint is an alpha feature and may be subject to change in future releases. - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** SystemConfigurationResponse - **Parameters:** **kwargs** (_Any_) - **Return type:** SystemConfigurationResponse #### Examples **Get system configuration:** ```python def get_system_configuration_example() -> None: client = CamundaClient() result = client.get_system_configuration() print(f"System config: {result}") ``` ### get_tenant() ```python def get_tenant(tenant_id, , consistency=None, **kwargs) ``` Get tenant > Retrieves a single tenant by tenant ID. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Tenant not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** TenantResult - **Return type:** TenantResult #### Examples **Get a tenant:** ```python def get_tenant_example(tenant_id: TenantId) -> None: client = CamundaClient() result = client.get_tenant(tenant_id=tenant_id) print(f"Tenant: {result.name}") ``` ### get_tenant_cluster_variable() ```python def get_tenant_cluster_variable(tenant_id, name, , consistency=None, **kwargs) ``` Get a tenant-scoped cluster variable > Get a tenant-scoped cluster variable. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **name** (_str_) – The name of a cluster variable. Unique within its scope (global or tenant- specific). Example: feature-flag-checkout. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Cluster variable not found - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ClusterVariableResult - **Return type:** ClusterVariableResult #### Examples **Get a tenant cluster variable:** ```python def get_tenant_cluster_variable_example(tenant_id: TenantId, name: ClusterVariableName) -> None: client = CamundaClient() result = client.get_tenant_cluster_variable( tenant_id=tenant_id, name=name, ) print(f"Variable: {result.name} = {result.value}") ``` ### get_topology() ```python def get_topology(**kwargs) ``` Get cluster topology > Obtains the current topology of the cluster the gateway is part of. - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** TopologyResponse - **Parameters:** **kwargs** (_Any_) - **Return type:** TopologyResponse #### Examples **Get cluster topology:** ```python def get_topology_example() -> None: client = CamundaClient() result = client.get_topology() print(f"Topology: {result}") ``` ### get_usage_metrics() ```python def get_usage_metrics(*, start_time, end_time, tenant_id=, with_tenants=, consistency=None, **kwargs) ``` Get usage metrics > Retrieve the usage metrics based on given criteria. - **Parameters:** - **start_time** (_datetime.datetime_) – Example: 2025-06-07T13:14:15Z. - **end_time** (_datetime.datetime_) – Example: 2025-06-07T13:14:15Z. - **tenant_id** (_str_ _|_ _Unset_) – The unique identifier of the tenant. Example: customer-service. - **with_tenants** (_bool_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** UsageMetricsResponse - **Return type:** UsageMetricsResponse #### Examples **Get usage metrics:** ```python def get_usage_metrics_example() -> None: client = CamundaClient() result = client.get_usage_metrics( start_time=datetime.datetime(2024, 1, 1), end_time=datetime.datetime(2024, 12, 31), ) print(f"Metrics: {result}") ``` ### get_user() ```python def get_user(username, , consistency=None, **kwargs) ``` Get user > Get a user by its username. - **Parameters:** - **username** (_str_) – The unique name of a user. Example: swillis. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The user with the given username was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** UserResult - **Return type:** UserResult #### Examples **Get a user:** ```python def get_user_example(username: Username) -> None: client = CamundaClient() result = client.get_user(username=username) print(f"User: {result.username}") ``` ### get_user_task() ```python def get_user_task(user_task_key, , consistency=None, **kwargs) ``` Get user task > Get the user task by the user task key. - **Parameters:** - **user_task_key** (_str_) – System-generated key for a user task. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The user task with the given key was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** UserTaskResult - **Return type:** UserTaskResult #### Examples **Get a user task:** ```python def get_user_task_example(user_task_key: UserTaskKey) -> None: client = CamundaClient() task = client.get_user_task(user_task_key=user_task_key) print(f"Task: {task.user_task_key}") ``` ### get_user_task_form() ```python def get_user_task_form(user_task_key, , consistency=None, **kwargs) ``` Get user task form > Get the form of a user task. Note that this endpoint will only return linked forms. This endpoint does not support embedded forms. - **Parameters:** - **user_task_key** (_str_) – System-generated key for a user task. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** FormResult - **Return type:** FormResult #### Examples **Get a user task form:** ```python def get_user_task_form_example(user_task_key: UserTaskKey) -> None: client = CamundaClient() result = client.get_user_task_form( user_task_key=user_task_key, ) print(f"Form: {result.form_key}") ``` ### get_variable() ```python def get_variable(variable_key, , consistency=None, **kwargs) ``` Get variable > Get a variable by its key. This endpoint returns both process-level and local (element-scoped) variables. The variable’s scopeKey indicates whether it’s a process-level variable or scoped to a specific element instance. - **Parameters:** - **variable_key** (_str_) – System-generated key for a variable. Example: 2251799813683287. - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** VariableResult - **Return type:** VariableResult #### Examples **Get a variable:** ```python def get_variable_example(variable_key: VariableKey) -> None: client = CamundaClient() result = client.get_variable( variable_key=variable_key, ) print(f"Variable: {result.name} = {result.value}") ``` ### migrate_process_instance() ```python def migrate_process_instance(process_instance_key, , data, **kwargs) ``` Migrate process instance > Migrates a process instance to a new process definition. This request can contain multiple mapping instructions to define mapping between the active process instance’s elements and target process definition elements. Use this to upgrade a process instance to a new version of a process or to a different process definition, e.g. to keep your running instances up-to-date with the latest process improvements. - **Parameters:** - **process_instance_key** (_str_) – System-generated key for a process instance. Example: 2251799813690746. - **body** (_ProcessInstanceMigrationInstruction_) – The migration instructions describe how to migrate a process instance from one process definition to another. - **data** (_ProcessInstanceMigrationInstruction_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The process instance is not found. - **errors.ConflictError** – If the response status code is 409. The process instance migration failed. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Migrate a process instance:** ```python def migrate_process_instance_example(process_instance_key: ProcessInstanceKey, target_process_definition_key: ProcessDefinitionKey, source_element_id: ElementId, target_element_id: ElementId) -> None: client = CamundaClient() client.migrate_process_instance( process_instance_key=process_instance_key, data=ProcessInstanceMigrationInstruction( target_process_definition_key=target_process_definition_key, mapping_instructions=[ MigrateProcessInstanceMappingInstruction( source_element_id=source_element_id, target_element_id=target_element_id, ), ], ), ) ``` ### migrate_process_instances_batch_operation() ```python def migrate_process_instances_batch_operation(, data, **kwargs) ``` Migrate process instances (batch) > Migrate multiple process instances. Since only process instances with ACTIVE state can be migrated, any given filters for state are ignored and overridden during this batch operation. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). - **Parameters:** - **body** (_ProcessInstanceMigrationBatchOperationRequest_) - **data** (_ProcessInstanceMigrationBatchOperationRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The process instance batch operation failed. More details are provided in the response body. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** BatchOperationCreatedResult - **Return type:** BatchOperationCreatedResult #### Examples **Migrate process instances in batch:** ```python def migrate_process_instances_batch_operation_example(target_process_definition_key: ProcessDefinitionKey, source_element_id: ElementId, target_element_id: ElementId) -> None: client = CamundaClient() result = client.migrate_process_instances_batch_operation( data=ProcessInstanceMigrationBatchOperationRequest( filter_=ProcessInstanceCancellationBatchOperationRequestFilter(), migration_plan=ProcessInstanceMigrationBatchOperationRequestMigrationPlan( target_process_definition_key=target_process_definition_key, mapping_instructions=[ MigrateProcessInstanceMappingInstruction( source_element_id=source_element_id, target_element_id=target_element_id, ), ], ), ), ) print(f"Batch operation key: {result.batch_operation_key}") ``` ### modify_process_instance() ```python def modify_process_instance(process_instance_key, , data, **kwargs) ``` Modify process instance > Modifies a running process instance. This request can contain multiple instructions to activate an element of the process or to terminate an active instance of an element. Use this to repair a process instance that is stuck on an element or took an unintended path. For example, because an external system is not available or doesn’t respond as expected. - **Parameters:** - **process_instance_key** (_str_) – System-generated key for a process instance. Example: 2251799813690746. - **body** (_ProcessInstanceModificationInstruction_) - **data** (_ProcessInstanceModificationInstruction_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The process instance is not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Modify a process instance:** ```python def modify_process_instance_example(process_instance_key: ProcessInstanceKey) -> None: client = CamundaClient() client.modify_process_instance( process_instance_key=process_instance_key, data=ProcessInstanceModificationInstruction(), ) ``` ### modify_process_instances_batch_operation() ```python def modify_process_instances_batch_operation(, data, **kwargs) ``` Modify process instances (batch) > Modify multiple process instances. Since only process instances with ACTIVE state can be modified, any given filters for state are ignored and overridden during this batch operation. In contrast to single modification operation, it is not possible to add variable instructions or modify by element key. It is only possible to use the element id of the source and target. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). - **Parameters:** - **body** (_ProcessInstanceModificationBatchOperationRequest_) – The process instance filter to define on which process instances tokens should be moved, and new element instances should be activated or terminated. - **data** (_ProcessInstanceModificationBatchOperationRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The process instance batch operation failed. More details are provided in the response body. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** BatchOperationCreatedResult - **Return type:** BatchOperationCreatedResult #### Examples **Modify process instances in batch:** ```python def modify_process_instances_batch_operation_example(source_element_id: ElementId, target_element_id: ElementId) -> None: client = CamundaClient() result = client.modify_process_instances_batch_operation( data=ProcessInstanceModificationBatchOperationRequest( filter_=ProcessInstanceCancellationBatchOperationRequestFilter(), move_instructions=[ ProcessInstanceModificationMoveBatchOperationInstruction( source_element_id=source_element_id, target_element_id=target_element_id, ), ], ), ) print(f"Batch operation key: {result.batch_operation_key}") ``` ### pin_clock() ```python def pin_clock(, data, **kwargs) ``` Pin internal clock (alpha) > Set a precise, static time for the Zeebe engine’s internal clock. When the clock is pinned, it remains at the specified time and does not advance. To change the time, the clock must be pinned again with a new timestamp. This endpoint is an alpha feature and may be subject to change in future releases. - **Parameters:** - **body** (_ClockPinRequest_) - **data** (_ClockPinRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Pin the cluster clock:** ```python def pin_clock_example() -> None: client = CamundaClient() client.pin_clock( data=ClockPinRequest( timestamp=1700000000000, ), ) ``` ### publish_message() ```python def publish_message(, data, **kwargs) ``` Publish message > Publishes a single message. Messages are published to specific partitions computed from their correlation keys. Messages can be buffered. The endpoint does not wait for a correlation result. Use the message correlation endpoint for such use cases. - **Parameters:** - **body** (_MessagePublicationRequest_) - **data** (_MessagePublicationRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** MessagePublicationResult - **Return type:** MessagePublicationResult #### Examples **Publish a message:** ```python def publish_message_example() -> None: client = CamundaClient() result = client.publish_message( data=MessagePublicationRequest( name="order-created", correlation_key="order-12345", time_to_live=60000, ) ) print(f"Message key: {result.message_key}") ``` ### reset_clock() ```python def reset_clock(**kwargs) ``` Reset internal clock (alpha) > Resets the Zeebe engine’s internal clock to the current system time, enabling it to tick in real- time. This operation is useful for returning the clock to normal behavior after it has been pinned to a specific time. This endpoint is an alpha feature and may be subject to change in future releases. - **Raises:** - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Parameters:** **kwargs** (_Any_) - **Return type:** None #### Examples **Reset the cluster clock:** ```python def reset_clock_example() -> None: client = CamundaClient() client.reset_clock() ``` ### resolve_incident() ```python def resolve_incident(incident_key, *, data=, **kwargs) ``` Resolve incident > Marks the incident as resolved; most likely a call to Update job will be necessary to reset the job’s retries, followed by this call. - **Parameters:** - **incident_key** (_str_) – System-generated key for a incident. Example: 2251799813689432. - **body** (_IncidentResolutionRequest_ _|_ _Unset_) - **data** (_IncidentResolutionRequest_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The incident with the incidentKey is not found. - **errors.ConflictError** – If the response status code is 409. The incident cannot be resolved due to an invalid state. For example, the associated job may have no retries left. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Resolve an incident:** ```python def resolve_incident_example(incident_key: IncidentKey) -> None: client = CamundaClient() client.resolve_incident(incident_key=incident_key) ``` ### resolve_incidents_batch_operation() ```python def resolve_incidents_batch_operation(*, data=, **kwargs) ``` Resolve related incidents (batch) > Resolves multiple instances of process instances. Since only process instances with ACTIVE state can have unresolved incidents, any given filters for state are ignored and overridden during this batch operation. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). - **Parameters:** - **body** (_ProcessInstanceIncidentResolutionBatchOperationRequest_ _|_ _Unset_) – The process instance filter that defines which process instances should have their incidents resolved. - **data** (_ProcessInstanceIncidentResolutionBatchOperationRequest_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The process instance batch operation failed. More details are provided in the response body. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** BatchOperationCreatedResult - **Return type:** BatchOperationCreatedResult #### Examples **Resolve incidents in batch:** ```python def resolve_incidents_batch_operation_example() -> None: client = CamundaClient() result = client.resolve_incidents_batch_operation( data=ProcessInstanceIncidentResolutionBatchOperationRequest( filter_=ProcessInstanceCancellationBatchOperationRequestFilter(), ), ) print(f"Batch operation key: {result.batch_operation_key}") ``` ### resolve_process_instance_incidents() ```python def resolve_process_instance_incidents(process_instance_key, **kwargs) ``` Resolve related incidents > Creates a batch operation to resolve multiple incidents of a process instance. - **Parameters:** - **process_instance_key** (_str_) – System-generated key for a process instance. Example: 2251799813690746. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.NotFoundError** – If the response status code is 404. The process instance is not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** BatchOperationCreatedResult - **Return type:** BatchOperationCreatedResult #### Examples **Resolve process instance incidents:** ```python def resolve_process_instance_incidents_example(process_instance_key: ProcessInstanceKey) -> None: client = CamundaClient() result = client.resolve_process_instance_incidents( process_instance_key=process_instance_key, ) print(f"Batch operation key: {result.batch_operation_key}") ``` ### resume_batch_operation() ```python def resume_batch_operation(batch_operation_key, *, data=, **kwargs) ``` Resume Batch operation > Resumes a suspended batch operation. This is done asynchronously, the progress can be tracked using the batch operation status endpoint (/batch-operations/{batchOperationKey}). - **Parameters:** - **batch_operation_key** (_str_) – System-generated key for an batch operation. Example: 2251799813684321. - **body** (_Any_ _|_ _Unset_) - **data** (_Any_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The batch operation was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Resume a batch operation:** ```python def resume_batch_operation_example(batch_operation_key: BatchOperationKey) -> None: client = CamundaClient() client.resume_batch_operation( batch_operation_key=batch_operation_key, ) ``` ### search_agent_instances() ```python def search_agent_instances(*, data=, consistency=None, **kwargs) ``` Search agent instances > Search for agent instances based on given criteria. - **Parameters:** - **body** (_AgentInstanceSearchQuery_ _|_ _Unset_) – Agent instance search request. - **data** (_AgentInstanceSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** AgentInstanceSearchQueryResult - **Return type:** AgentInstanceSearchQueryResult #### Examples **Search agent instances:** ```python def search_agent_instances_example() -> None: client = CamundaClient() result = client.search_agent_instances( data=AgentInstanceSearchQuery() ) if not isinstance(result.items, Unset): for agent_instance in result.items: print(f"Agent instance key: {agent_instance.agent_instance_key}") ``` ### search_audit_logs() ```python def search_audit_logs(*, data=, consistency=None, **kwargs) ``` Search audit logs > Search for audit logs based on given criteria. - **Parameters:** - **body** (_AuditLogSearchQueryRequest_ _|_ _Unset_) – Audit log search request. - **data** (_AuditLogSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** AuditLogSearchQueryResult - **Return type:** AuditLogSearchQueryResult #### Examples **Search audit logs:** ```python def search_audit_logs_example() -> None: client = CamundaClient() result = client.search_audit_logs( data=AuditLogSearchQueryRequest(), ) if not isinstance(result.items, Unset): for log in result.items: print(f"Audit log: {log.audit_log_key}") ``` ### search_authorizations() ```python def search_authorizations(*, data=, consistency=None, **kwargs) ``` Search authorizations > Search for authorizations based on given criteria. - **Parameters:** - **body** (_AuthorizationSearchQuery_ _|_ _Unset_) - **data** (_AuthorizationSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** AuthorizationSearchResult - **Return type:** AuthorizationSearchResult #### Examples **Search authorizations:** ```python def search_authorizations_example() -> None: client = CamundaClient() result = client.search_authorizations( data=AuthorizationSearchQuery(), ) if not isinstance(result.items, Unset): for auth in result.items: print(f"Authorization: {auth.authorization_key}") ``` ### search_batch_operation_items() ```python def search_batch_operation_items(*, data=, consistency=None, **kwargs) ``` Search batch operation items > Search for batch operation items based on given criteria. - **Parameters:** - **body** (_BatchOperationItemSearchQuery_ _|_ _Unset_) – Batch operation item search request. - **data** (_BatchOperationItemSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** BatchOperationItemSearchQueryResult - **Return type:** BatchOperationItemSearchQueryResult #### Examples **Search batch operation items:** ```python def search_batch_operation_items_example(batch_operation_key: BatchOperationKey) -> None: client = CamundaClient() result = client.search_batch_operation_items( batch_operation_key=batch_operation_key, data=BatchOperationItemSearchQuery(), ) if not isinstance(result.items, Unset): for item in result.items: print(f"Item: {item.item_key}") ``` ### search_batch_operations() ```python def search_batch_operations(*, data=, consistency=None, **kwargs) ``` Search batch operations > Search for batch operations based on given criteria. - **Parameters:** - **body** (_BatchOperationSearchQuery_ _|_ _Unset_) – Batch operation search request. - **data** (_BatchOperationSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** BatchOperationSearchQueryResult - **Return type:** BatchOperationSearchQueryResult #### Examples **Search batch operations:** ```python def search_batch_operations_example() -> None: client = CamundaClient() result = client.search_batch_operations( data=BatchOperationSearchQuery(), ) if not isinstance(result.items, Unset): for op in result.items: print(f"Batch operation: {op.batch_operation_key}") ``` ### search_clients_for_group() ```python def search_clients_for_group(group_id, *, data=, consistency=None, **kwargs) ``` Search group clients > Search clients assigned to a group. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **body** (_GroupClientSearchQueryRequest_ _|_ _Unset_) - **data** (_GroupClientSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The group with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GroupClientSearchResult - **Return type:** GroupClientSearchResult #### Examples **Search clients in a group:** ```python def search_clients_for_group_example(group_id: GroupId) -> None: client = CamundaClient() result = client.search_clients_for_group( group_id=group_id, ) if not isinstance(result.items, Unset): for c in result.items: print(f"Client: {c.client_id}") ``` ### search_clients_for_role() ```python def search_clients_for_role(role_id, *, data=, consistency=None, **kwargs) ``` Search role clients > Search clients with assigned role. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **body** (_RoleClientSearchQueryRequest_ _|_ _Unset_) - **data** (_RoleClientSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** RoleClientSearchResult - **Return type:** RoleClientSearchResult #### Examples **Search clients for a role:** ```python def search_clients_for_role_example(role_id: RoleId) -> None: client = CamundaClient() result = client.search_clients_for_role( role_id=role_id, ) if not isinstance(result.items, Unset): for c in result.items: print(f"Client: {c.client_id}") ``` ### search_clients_for_tenant() ```python def search_clients_for_tenant(tenant_id, *, data=, consistency=None, **kwargs) ``` Search clients for tenant > Retrieves a filtered and sorted list of clients for a specified tenant. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **body** (_TenantClientSearchQueryRequest_ _|_ _Unset_) - **data** (_TenantClientSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** TenantClientSearchResult - **Return type:** TenantClientSearchResult #### Examples **Search clients for a tenant:** ```python def search_clients_for_tenant_example(tenant_id: TenantId) -> None: client = CamundaClient() result = client.search_clients_for_tenant( tenant_id=tenant_id, ) if not isinstance(result.items, Unset): for c in result.items: print(f"Client: {c.client_id}") ``` ### search_cluster_variables() ```python def search_cluster_variables(*, data=, truncate_values=, consistency=None, **kwargs) ``` Search for cluster variables based on given criteria. By default, long variable values in the response are truncated. - **Parameters:** - **truncate_values** (_bool_ _|_ _Unset_) - **body** (_ClusterVariableSearchQueryRequest_ _|_ _Unset_) – Cluster variable search query request. - **data** (_ClusterVariableSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ClusterVariableSearchQueryResult - **Return type:** ClusterVariableSearchQueryResult #### Examples **Search cluster variables:** ```python def search_cluster_variables_example() -> None: client = CamundaClient() result = client.search_cluster_variables( data=ClusterVariableSearchQueryRequest(), ) if not isinstance(result.items, Unset): for var in result.items: print(f"Variable: {var.name}") ``` ### search_correlated_message_subscriptions() ```python def search_correlated_message_subscriptions(*, data=, consistency=None, **kwargs) ``` Search correlated message subscriptions > Search correlated message subscriptions based on given criteria. - **Parameters:** - **body** (_CorrelatedMessageSubscriptionSearchQuery_ _|_ _Unset_) - **data** (_CorrelatedMessageSubscriptionSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** CorrelatedMessageSubscriptionSearchQueryResult - **Return type:** CorrelatedMessageSubscriptionSearchQueryResult #### Examples **Search correlated message subscriptions:** ```python def search_correlated_message_subscriptions_example() -> None: client = CamundaClient() result = client.search_correlated_message_subscriptions( data=CorrelatedMessageSubscriptionSearchQuery(), ) if not isinstance(result.items, Unset): for sub in result.items: print(f"Correlated subscription: {sub.message_name}") ``` ### search_decision_definitions() ```python def search_decision_definitions(*, data=, consistency=None, **kwargs) ``` Search decision definitions > Search for decision definitions based on given criteria. - **Parameters:** - **body** (_DecisionDefinitionSearchQuery_ _|_ _Unset_) - **data** (_DecisionDefinitionSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** DecisionDefinitionSearchQueryResult - **Return type:** DecisionDefinitionSearchQueryResult #### Examples **Search decision definitions:** ```python def search_decision_definitions_example() -> None: client = CamundaClient() result = client.search_decision_definitions( data=DecisionDefinitionSearchQuery() ) if not isinstance(result.items, Unset): for definition in result.items: print(f"Decision: {definition.decision_definition_id}") ``` ### search_decision_instances() ```python def search_decision_instances(*, data=, consistency=None, **kwargs) ``` Search decision instances > Search for decision instances based on given criteria. - **Parameters:** - **body** (_DecisionInstanceSearchQuery_ _|_ _Unset_) - **data** (_DecisionInstanceSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** DecisionInstanceSearchQueryResult - **Return type:** DecisionInstanceSearchQueryResult #### Examples **Search decision instances:** ```python def search_decision_instances_example() -> None: client = CamundaClient() result = client.search_decision_instances( data=DecisionInstanceSearchQuery(), ) if not isinstance(result.items, Unset): for di in result.items: print(f"Decision instance: {di.decision_definition_id}") ``` ### search_decision_requirements() ```python def search_decision_requirements(*, data=, consistency=None, **kwargs) ``` Search decision requirements > Search for decision requirements based on given criteria. - **Parameters:** - **body** (_DecisionRequirementsSearchQuery_ _|_ _Unset_) - **data** (_DecisionRequirementsSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** DecisionRequirementsSearchQueryResult - **Return type:** DecisionRequirementsSearchQueryResult #### Examples **Search decision requirements:** ```python def search_decision_requirements_example() -> None: client = CamundaClient() result = client.search_decision_requirements( data=DecisionRequirementsSearchQuery(), ) if not isinstance(result.items, Unset): for drd in result.items: print(f"DRD: {drd.decision_requirements_name}") ``` ### search_element_instance_incidents() ```python def search_element_instance_incidents(element_instance_key, , data, consistency=None, **kwargs) ``` Search for incidents of a specific element instance > Search for incidents caused by the specified element instance, including incidents of any child instances created from this element instance. Although the elementInstanceKey is provided as a path parameter to indicate the root element instance, you may also include an elementInstanceKey within the filter object to narrow results to specific child element instances. This is useful, for example, if you want to isolate incidents associated with nested or subordinate elements within the given element instance while excluding incidents directly tied to the root element itself. - **Parameters:** - **element_instance_key** (_str_) – System-generated key for a element instance. Example: 2251799813686789. - **body** (_IncidentSearchQuery_) - **data** (_IncidentSearchQuery_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The element instance with the given key was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** IncidentSearchQueryResult - **Return type:** IncidentSearchQueryResult #### Examples **Search element instance incidents:** ```python def search_element_instance_incidents_example(element_instance_key: ElementInstanceKey) -> None: client = CamundaClient() result = client.search_element_instance_incidents( element_instance_key=element_instance_key, data=IncidentSearchQuery(), ) if not isinstance(result.items, Unset): for incident in result.items: print(f"Incident: {incident.incident_key}") ``` ### search_element_instances() ```python def search_element_instances(*, data=, consistency=None, **kwargs) ``` Search element instances > Search for element instances based on given criteria. - **Parameters:** - **body** (_ElementInstanceSearchQuery_ _|_ _Unset_) – Element instance search request. - **data** (_ElementInstanceSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ElementInstanceSearchQueryResult - **Return type:** ElementInstanceSearchQueryResult #### Examples **Search element instances:** ```python def search_element_instances_example() -> None: client = CamundaClient() result = client.search_element_instances( data=ElementInstanceSearchQuery(), ) if not isinstance(result.items, Unset): for ei in result.items: print(f"Element instance: {ei.element_instance_key}") ``` ### search_global_task_listeners() ```python def search_global_task_listeners(*, data=, consistency=None, **kwargs) ``` Search global user task listeners > Search for global user task listeners based on given criteria. - **Parameters:** - **body** (_GlobalTaskListenerSearchQueryRequest_ _|_ _Unset_) – Global listener search query request. - **data** (_GlobalTaskListenerSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GlobalTaskListenerSearchQueryResult - **Return type:** GlobalTaskListenerSearchQueryResult #### Examples **Search global task listeners:** ```python def search_global_task_listeners_example() -> None: client = CamundaClient() result = client.search_global_task_listeners( data=GlobalTaskListenerSearchQueryRequest(), ) if not isinstance(result.items, Unset): for listener in result.items: print(f"Listener: {listener.id}") ``` ### search_group_ids_for_tenant() ```python def search_group_ids_for_tenant(tenant_id, *, data=, consistency=None, **kwargs) ``` Search groups for tenant > Retrieves a filtered and sorted list of groups for a specified tenant. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **body** (_TenantGroupSearchQueryRequest_ _|_ _Unset_) - **data** (_TenantGroupSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** TenantGroupSearchResult - **Return type:** TenantGroupSearchResult #### Examples **Search groups for a tenant:** ```python def search_group_ids_for_tenant_example(tenant_id: TenantId) -> None: client = CamundaClient() result = client.search_group_ids_for_tenant( tenant_id=tenant_id, data=TenantGroupSearchQueryRequest(), ) if not isinstance(result.items, Unset): for group in result.items: print(f"Group: {group.group_id}") ``` ### search_groups() ```python def search_groups(*, data=, consistency=None, **kwargs) ``` Search groups > Search for groups based on given criteria. - **Parameters:** - **body** (_GroupSearchQueryRequest_ _|_ _Unset_) – Group search request. - **data** (_GroupSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GroupSearchQueryResult - **Return type:** GroupSearchQueryResult #### Examples **Search groups:** ```python def search_groups_example() -> None: client = CamundaClient() result = client.search_groups( data=GroupSearchQueryRequest(), ) if not isinstance(result.items, Unset): for group in result.items: print(f"Group: {group.name}") ``` ### search_groups_for_role() ```python def search_groups_for_role(role_id, *, data=, consistency=None, **kwargs) ``` Search role groups > Search groups with assigned role. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **body** (_RoleGroupSearchQueryRequest_ _|_ _Unset_) - **data** (_RoleGroupSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** RoleGroupSearchResult - **Return type:** RoleGroupSearchResult #### Examples **Search groups for a role:** ```python def search_groups_for_role_example(role_id: RoleId) -> None: client = CamundaClient() result = client.search_groups_for_role( role_id=role_id, data=RoleGroupSearchQueryRequest(), ) if not isinstance(result.items, Unset): for group in result.items: print(f"Group: {group.group_id}") ``` ### search_incidents() ```python def search_incidents(*, data=, consistency=None, **kwargs) ``` Search incidents > Search for incidents based on given criteria. - **Parameters:** - **body** (_IncidentSearchQuery_ _|_ _Unset_) - **data** (_IncidentSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** IncidentSearchQueryResult - **Return type:** IncidentSearchQueryResult #### Examples **Search incidents:** ```python def search_incidents_example() -> None: client = CamundaClient() result = client.search_incidents( data=IncidentSearchQuery() ) if not isinstance(result.items, Unset): for incident in result.items: print(f"Incident key: {incident.incident_key}") ``` ### search_jobs() ```python def search_jobs(*, data=, consistency=None, **kwargs) ``` Search jobs > Search for jobs based on given criteria. - **Parameters:** - **body** (_JobSearchQuery_ _|_ _Unset_) – Job search request. - **data** (_JobSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** JobSearchQueryResult - **Return type:** JobSearchQueryResult #### Examples **Search jobs:** ```python def search_jobs_example() -> None: client = CamundaClient() result = client.search_jobs( data=JobSearchQuery(), ) if not isinstance(result.items, Unset): for job in result.items: print(f"Job: {job.job_key}") ``` ### search_mapping_rule() ```python def search_mapping_rule(*, data=, consistency=None, **kwargs) ``` Search mapping rules > Search for mapping rules based on given criteria. - **Parameters:** - **body** (_MappingRuleSearchQueryRequest_ _|_ _Unset_) - **data** (_MappingRuleSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** MappingRuleSearchQueryResult - **Return type:** MappingRuleSearchQueryResult #### Examples **Search mapping rules:** ```python def search_mapping_rule_example() -> None: client = CamundaClient() result = client.search_mapping_rule( data=MappingRuleSearchQueryRequest(), ) if not isinstance(result.items, Unset): for rule in result.items: print(f"Mapping rule: {rule.name}") ``` ### search_mapping_rules_for_group() ```python def search_mapping_rules_for_group(group_id, *, data=, consistency=None, **kwargs) ``` Search group mapping rules > Search mapping rules assigned to a group. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **body** (_MappingRuleSearchQueryRequest_ _|_ _Unset_) - **data** (_MappingRuleSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The group with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GroupMappingRuleSearchResult - **Return type:** GroupMappingRuleSearchResult #### Examples **Search mapping rules for a group:** ```python def search_mapping_rules_for_group_example(group_id: GroupId) -> None: client = CamundaClient() result = client.search_mapping_rules_for_group( group_id=group_id, data=MappingRuleSearchQueryRequest(), ) if not isinstance(result.items, Unset): for rule in result.items: print(f"Mapping rule: {rule.mapping_rule_id}") ``` ### search_mapping_rules_for_role() ```python def search_mapping_rules_for_role(role_id, *, data=, consistency=None, **kwargs) ``` Search role mapping rules > Search mapping rules with assigned role. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **body** (_MappingRuleSearchQueryRequest_ _|_ _Unset_) - **data** (_MappingRuleSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** RoleMappingRuleSearchResult - **Return type:** RoleMappingRuleSearchResult #### Examples **Search mapping rules for a role:** ```python def search_mapping_rules_for_role_example(role_id: RoleId) -> None: client = CamundaClient() result = client.search_mapping_rules_for_role( role_id=role_id, data=MappingRuleSearchQueryRequest(), ) if not isinstance(result.items, Unset): for rule in result.items: print(f"Mapping rule: {rule.mapping_rule_id}") ``` ### search_mapping_rules_for_tenant() ```python def search_mapping_rules_for_tenant(tenant_id, *, data=, consistency=None, **kwargs) ``` Search mapping rules for tenant > Retrieves a filtered and sorted list of MappingRules for a specified tenant. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **body** (_MappingRuleSearchQueryRequest_ _|_ _Unset_) - **data** (_MappingRuleSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** TenantMappingRuleSearchResult - **Return type:** TenantMappingRuleSearchResult #### Examples **Search mapping rules for a tenant:** ```python def search_mapping_rules_for_tenant_example(tenant_id: TenantId) -> None: client = CamundaClient() result = client.search_mapping_rules_for_tenant( tenant_id=tenant_id, data=MappingRuleSearchQueryRequest(), ) if not isinstance(result.items, Unset): for rule in result.items: print(f"Mapping rule: {rule.mapping_rule_id}") ``` ### search_message_subscriptions() ```python def search_message_subscriptions(*, data=, consistency=None, **kwargs) ``` Search message subscriptions > Search for message subscriptions based on given criteria. By default, both start and intermediate event subscriptions are returned. Use the messageSubscriptionType filter to restrict results to a single type. **Version notes:** - Start event subscriptions are only captured for deployments made with 8.10 or later. - The messageSubscriptionType field is only populated for data created > with Camunda 8.10 or later. For pre-8.10 data, intermediate event entries have no > messageSubscriptionType value stored. For convenience, the API returns PROCESS_EVENT > as a default for such search results, though. - Searching for intermediate event subscriptions **including legacy data** can be achieved by filtering for messageSubscriptionType not matching START_EVENT. * **Parameters:** - **body** (_MessageSubscriptionSearchQuery_ _|_ _Unset_) - **data** (_MessageSubscriptionSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) * **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. * **Returns:** MessageSubscriptionSearchQueryResult * **Return type:** MessageSubscriptionSearchQueryResult #### Examples **Search message subscriptions:** ```python def search_message_subscriptions_example() -> None: client = CamundaClient() result = client.search_message_subscriptions( data=MessageSubscriptionSearchQuery(), ) if not isinstance(result.items, Unset): for sub in result.items: print(f"Subscription: {sub.message_name}") ``` ### search_process_definitions() ```python def search_process_definitions(*, data=, consistency=None, **kwargs) ``` Search process definitions > Search for process definitions based on given criteria. - **Parameters:** - **body** (_ProcessDefinitionSearchQuery_ _|_ _Unset_) - **data** (_ProcessDefinitionSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ProcessDefinitionSearchQueryResult - **Return type:** ProcessDefinitionSearchQueryResult #### Examples **Search process definitions:** ```python def search_process_definitions_example() -> None: client = CamundaClient() result = client.search_process_definitions( data=ProcessDefinitionSearchQuery(), ) if not isinstance(result.items, Unset): for pd in result.items: print(f"Process definition: {pd.name}") ``` ### search_process_instance_incidents() ```python def search_process_instance_incidents(process_instance_key, *, data=, consistency=None, **kwargs) ``` Search related incidents > Search for incidents caused by the process instance or any of its called process or decision instances. Although the processInstanceKey is provided as a path parameter to indicate the root process instance, you may also include a processInstanceKey within the filter object to narrow results to specific child process instances. This is useful, for example, if you want to isolate incidents associated with subprocesses or called processes under the root instance while excluding incidents directly tied to the root. - **Parameters:** - **process_instance_key** (_str_) – System-generated key for a process instance. Example: 2251799813690746. - **body** (_IncidentSearchQuery_ _|_ _Unset_) - **data** (_IncidentSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The process instance with the given key was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** IncidentSearchQueryResult - **Return type:** IncidentSearchQueryResult #### Examples **Search process instance incidents:** ```python def search_process_instance_incidents_example(process_instance_key: ProcessInstanceKey) -> None: client = CamundaClient() result = client.search_process_instance_incidents( process_instance_key=process_instance_key, data=IncidentSearchQuery(), ) if not isinstance(result.items, Unset): for incident in result.items: print(f"Incident: {incident.incident_key}") ``` ### search_process_instances() ```python def search_process_instances(*, data=, consistency=None, **kwargs) ``` Search process instances > Search for process instances based on given criteria. - **Parameters:** - **body** (_ProcessInstanceSearchQuery_ _|_ _Unset_) – Process instance search request. - **data** (_ProcessInstanceSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ProcessInstanceSearchQueryResult - **Return type:** ProcessInstanceSearchQueryResult #### Examples **Search process instances:** ```python def search_process_instances_example() -> None: client = CamundaClient() result = client.search_process_instances( data=ProcessInstanceSearchQuery( filter_=ProcessInstanceSearchQueryFilter( process_definition_id="order-process", ), sort=[ ProcessInstanceSearchQuerySortRequest( field=ProcessInstanceSearchQuerySortRequestField.STARTDATE, order=SortOrderEnum.DESC, ) ], page=LimitBasedPagination(limit=10), ) ) for instance in result.items: print(f"{instance.process_instance_key}: {instance.state}") print(f"Total: {result.page.total_items}") ``` ### search_resources() ```python def search_resources(*, data=, consistency=None, **kwargs) ``` Search resources > Search for deployed resources based on given criteria. :::info This endpoint does not return BPMN process definitions, DMN decision definitions, or form resources. To query BPMN process definitions or DMN decision definitions, use their respective search APIs. ::: - **body**: :type body: ResourceSearchQuery | Unset ```` * **Raises:** * **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. * **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. * **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. * **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. * **errors.UnexpectedStatus** – If the response status code is not documented. * **httpx.TimeoutException** – If the request takes longer than Client.timeout. * **Returns:** ResourceSearchQueryResult * **Parameters:** * **data** (*ResourceSearchQuery* *|* *Unset*) * **consistency** (*ConsistencyOptions* *|* *None*) * **kwargs** (*Any*) * **Return type:** ResourceSearchQueryResult #### Examples **Search resources:** ```python def search_resources_example() -> None: client = CamundaClient() result = client.search_resources( data=ResourceSearchQuery(), ) if not isinstance(result.items, Unset): for resource in result.items: print(f"Resource: {resource.resource_name}") ```` ### search_roles() ```python def search_roles(*, data=, consistency=None, **kwargs) ``` Search roles > Search for roles based on given criteria. - **Parameters:** - **body** (_RoleSearchQueryRequest_ _|_ _Unset_) – Role search request. - **data** (_RoleSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** RoleSearchQueryResult - **Return type:** RoleSearchQueryResult #### Examples **Search roles:** ```python def search_roles_example() -> None: client = CamundaClient() result = client.search_roles( data=RoleSearchQueryRequest(), ) if not isinstance(result.items, Unset): for role in result.items: print(f"Role: {role.name}") ``` ### search_roles_for_group() ```python def search_roles_for_group(group_id, *, data=, consistency=None, **kwargs) ``` Search group roles > Search roles assigned to a group. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **body** (_RoleSearchQueryRequest_ _|_ _Unset_) – Role search request. - **data** (_RoleSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The group with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GroupRoleSearchResult - **Return type:** GroupRoleSearchResult #### Examples **Search roles for a group:** ```python def search_roles_for_group_example(group_id: GroupId) -> None: client = CamundaClient() result = client.search_roles_for_group( group_id=group_id, data=RoleSearchQueryRequest(), ) if not isinstance(result.items, Unset): for role in result.items: print(f"Role: {role.name}") ``` ### search_roles_for_tenant() ```python def search_roles_for_tenant(tenant_id, *, data=, consistency=None, **kwargs) ``` Search roles for tenant > Retrieves a filtered and sorted list of roles for a specified tenant. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **body** (_RoleSearchQueryRequest_ _|_ _Unset_) – Role search request. - **data** (_RoleSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** TenantRoleSearchResult - **Return type:** TenantRoleSearchResult #### Examples **Search roles for a tenant:** ```python def search_roles_for_tenant_example(tenant_id: TenantId) -> None: client = CamundaClient() result = client.search_roles_for_tenant( tenant_id=tenant_id, data=RoleSearchQueryRequest(), ) if not isinstance(result.items, Unset): for role in result.items: print(f"Role: {role.name}") ``` ### search_tenants() ```python def search_tenants(*, data=, consistency=None, **kwargs) ``` Search tenants > Retrieves a filtered and sorted list of tenants. - **Parameters:** - **body** (_TenantSearchQueryRequest_ _|_ _Unset_) – Tenant search request - **data** (_TenantSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** TenantSearchQueryResult - **Return type:** TenantSearchQueryResult #### Examples **Search tenants:** ```python def search_tenants_example() -> None: client = CamundaClient() result = client.search_tenants( data=TenantSearchQueryRequest(), ) if not isinstance(result.items, Unset): for tenant in result.items: print(f"Tenant: {tenant.name}") ``` ### search_user_task_audit_logs() ```python def search_user_task_audit_logs(user_task_key, *, data=, consistency=None, **kwargs) ``` Search user task audit logs > Search for user task audit logs based on given criteria. - **Parameters:** - **user_task_key** (_str_) – System-generated key for a user task. - **body** (_UserTaskAuditLogSearchQueryRequest_ _|_ _Unset_) – User task search query request. - **data** (_UserTaskAuditLogSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** AuditLogSearchQueryResult - **Return type:** AuditLogSearchQueryResult #### Examples **Search user task audit logs:** ```python def search_user_task_audit_logs_example(user_task_key: UserTaskKey) -> None: client = CamundaClient() result = client.search_user_task_audit_logs( user_task_key=user_task_key, data=UserTaskAuditLogSearchQueryRequest(), ) if not isinstance(result.items, Unset): for log in result.items: print(f"Audit log: {log.audit_log_key}") ``` ### search_user_task_effective_variables() ```python def search_user_task_effective_variables(user_task_key, *, data=, truncate_values=, consistency=None, **kwargs) ``` Search user task effective variables > Search for the effective variables of a user task. This endpoint returns deduplicated variables where each variable name appears at most once. When the same variable name exists at multiple scope levels in the scope hierarchy, the value from the innermost scope (closest to the user task) takes precedence. This is useful for retrieving the actual runtime state of variables as seen by the user task. By default, long variable values in the response are truncated. - **Parameters:** - **user_task_key** (_str_) – System-generated key for a user task. - **truncate_values** (_bool_ _|_ _Unset_) - **body** (_UserTaskEffectiveVariableSearchQueryRequest_ _|_ _Unset_) – User task effective variable search query request. Uses offset-based pagination only. - **data** (_UserTaskEffectiveVariableSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** VariableSearchQueryResult - **Return type:** VariableSearchQueryResult #### Examples **Search user task effective variables:** ```python def search_user_task_effective_variables_example(user_task_key: UserTaskKey) -> None: client = CamundaClient() result = client.search_user_task_effective_variables( user_task_key=user_task_key, ) if not isinstance(result.items, Unset): for var in result.items: print(f"Variable: {var.name}") ``` ### search_user_task_variables() ```python def search_user_task_variables(user_task_key, *, data=, truncate_values=, consistency=None, **kwargs) ``` Search user task variables > Search for user task variables based on given criteria. This endpoint returns all variable documents visible from the user task’s scope, including variables from parent scopes in the scope hierarchy. If the same variable name exists at multiple scope levels, each scope’s variable is returned as a separate result. Use the /user-tasks/{userTaskKey}/effective-variables/search endpoint to get deduplicated variables where the innermost scope takes precedence. By default, long variable values in the response are truncated. - **Parameters:** - **user_task_key** (_str_) – System-generated key for a user task. - **truncate_values** (_bool_ _|_ _Unset_) - **body** (_UserTaskVariableSearchQueryRequest_ _|_ _Unset_) – User task search query request. - **data** (_UserTaskVariableSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** VariableSearchQueryResult - **Return type:** VariableSearchQueryResult #### Examples **Search user task variables:** ```python def search_user_task_variables_example(user_task_key: UserTaskKey) -> None: client = CamundaClient() result = client.search_user_task_variables( user_task_key=user_task_key, ) if not isinstance(result.items, Unset): for var in result.items: print(f"Variable: {var.name}") ``` ### search_user_tasks() ```python def search_user_tasks(*, data=, consistency=None, **kwargs) ``` Search user tasks > Search for user tasks based on given criteria. - **Parameters:** - **body** (_UserTaskSearchQuery_ _|_ _Unset_) – User task search query request. - **data** (_UserTaskSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** UserTaskSearchQueryResult - **Return type:** UserTaskSearchQueryResult #### Examples **Search user tasks:** ```python def search_user_tasks_example() -> None: client = CamundaClient() result = client.search_user_tasks( data=UserTaskSearchQuery() ) if not isinstance(result.items, Unset): for task in result.items: print(f"Task: {task.user_task_key}") ``` ### search_users() ```python def search_users(*, data=, consistency=None, **kwargs) ``` Search users > Search for users based on given criteria. - **Parameters:** - **body** (_UserSearchQueryRequest_ _|_ _Unset_) - **data** (_UserSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** UserSearchResult - **Return type:** UserSearchResult #### Examples **Search users:** ```python def search_users_example() -> None: client = CamundaClient() result = client.search_users( data=UserSearchQueryRequest(), ) if not isinstance(result.items, Unset): for user in result.items: print(f"User: {user.username}") ``` ### search_users_for_group() ```python def search_users_for_group(group_id, *, data=, consistency=None, **kwargs) ``` Search group users > Search users assigned to a group. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **body** (_GroupUserSearchQueryRequest_ _|_ _Unset_) - **data** (_GroupUserSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The group with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GroupUserSearchResult - **Return type:** GroupUserSearchResult #### Examples **Search users in a group:** ```python def search_users_for_group_example(group_id: GroupId) -> None: client = CamundaClient() result = client.search_users_for_group( group_id=group_id, ) if not isinstance(result.items, Unset): for user in result.items: print(f"User: {user.username}") ``` ### search_users_for_role() ```python def search_users_for_role(role_id, *, data=, consistency=None, **kwargs) ``` Search role users > Search users with assigned role. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **body** (_RoleUserSearchQueryRequest_ _|_ _Unset_) - **data** (_RoleUserSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** RoleUserSearchResult - **Return type:** RoleUserSearchResult #### Examples **Search users for a role:** ```python def search_users_for_role_example(role_id: RoleId) -> None: client = CamundaClient() result = client.search_users_for_role( role_id=role_id, ) if not isinstance(result.items, Unset): for user in result.items: print(f"User: {user.username}") ``` ### search_users_for_tenant() ```python def search_users_for_tenant(tenant_id, *, data=, consistency=None, **kwargs) ``` Search users for tenant > Retrieves a filtered and sorted list of users for a specified tenant. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **body** (_TenantUserSearchQueryRequest_ _|_ _Unset_) - **data** (_TenantUserSearchQueryRequest_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** TenantUserSearchResult - **Return type:** TenantUserSearchResult #### Examples **Search users for a tenant:** ```python def search_users_for_tenant_example(tenant_id: TenantId) -> None: client = CamundaClient() result = client.search_users_for_tenant( tenant_id=tenant_id, ) if not isinstance(result.items, Unset): for user in result.items: print(f"User: {user.username}") ``` ### search_variables() ```python def search_variables(*, data=, truncate_values=, consistency=None, **kwargs) ``` Search variables > Search for variables based on given criteria. This endpoint returns variables that exist directly at the specified scopes - it does not include variables from parent scopes that would be visible through the scope hierarchy. Variables can be process-level (scoped to the process instance) or local (scoped to specific BPMN elements like tasks, subprocesses, etc.). By default, long variable values in the response are truncated. - **Parameters:** - **truncate_values** (_bool_ _|_ _Unset_) - **body** (_VariableSearchQuery_ _|_ _Unset_) – Variable search query request. - **data** (_VariableSearchQuery_ _|_ _Unset_) - **consistency** (_ConsistencyOptions_ _|_ _None_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** VariableSearchQueryResult - **Return type:** VariableSearchQueryResult #### Examples **Search variables:** ```python def search_variables_example() -> None: client = CamundaClient() result = client.search_variables() if not isinstance(result.items, Unset): for var in result.items: print(f"Variable: {var.name}") ``` ### suspend_batch_operation() ```python def suspend_batch_operation(batch_operation_key, *, data=, **kwargs) ``` Suspend Batch operation > Suspends a running batch operation. This is done asynchronously, the progress can be tracked using the batch operation status endpoint (/batch-operations/{batchOperationKey}). - **Parameters:** - **batch_operation_key** (_str_) – System-generated key for an batch operation. Example: 2251799813684321. - **body** (_Any_ _|_ _Unset_) - **data** (_Any_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The batch operation was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Suspend a batch operation:** ```python def suspend_batch_operation_example(batch_operation_key: BatchOperationKey) -> None: client = CamundaClient() client.suspend_batch_operation( batch_operation_key=batch_operation_key, ) ``` ### throw_job_error() ```python def throw_job_error(job_key, , data, **kwargs) ``` Throw error for job > Reports a business error (i.e. non-technical) that occurs while processing a job. - **Parameters:** - **job_key** (_str_) – System-generated key for a job. Example: 2251799813653498. - **body** (_JobErrorRequest_) - **data** (_JobErrorRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The job with the given key was not found or is not activated. - **errors.ConflictError** – If the response status code is 409. The job with the given key is in the wrong state currently. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Throw a job error:** ```python def throw_job_error_example(job_key: JobKey) -> None: client = CamundaClient() client.throw_job_error( job_key=job_key, data=JobErrorRequest( error_code="VALIDATION_ERROR", error_message="Input validation failed", ), ) ``` ### unassign_client_from_group() ```python def unassign_client_from_group(group_id, client_id, **kwargs) ``` Unassign a client from a group > Unassigns a client from a group. The client is removed as a group member, with associated authorizations, roles, and tenant assignments no longer applied. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **client_id** (_str_) – The unique identifier of an OAuth client. Minted outside the Camunda REST API: in SaaS by Console, in Self-Managed with OIDC by the external identity provider (e.g. EntraID, Keycloak, Okta). In Self-Managed with Basic authentication, machine-to-machine applications are modelled as users instead — see the user identifier. > Example: my-application. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The group with the given ID was not found, or the client is not assigned to this group. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a client from a group:** ```python def unassign_client_from_group_example(group_id: GroupId, client_id: ClientId) -> None: client = CamundaClient() client.unassign_client_from_group( group_id=group_id, client_id=client_id, ) ``` ### unassign_client_from_tenant() ```python def unassign_client_from_tenant(tenant_id, client_id, **kwargs) ``` Unassign a client from a tenant > Unassigns the client from the specified tenant. The client can no longer access tenant data. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **client_id** (_str_) – The unique identifier of an OAuth client. Minted outside the Camunda REST API: in SaaS by Console, in Self-Managed with OIDC by the external identity provider (e.g. EntraID, Keycloak, Okta). In Self-Managed with Basic authentication, machine-to-machine applications are modelled as users instead — see the user identifier. > Example: my-application. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The tenant does not exist or the client was not assigned to it. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a client from a tenant:** ```python def unassign_client_from_tenant_example(tenant_id: TenantId, client_id: ClientId) -> None: client = CamundaClient() client.unassign_client_from_tenant( tenant_id=tenant_id, client_id=client_id, ) ``` ### unassign_group_from_tenant() ```python def unassign_group_from_tenant(tenant_id, group_id, **kwargs) ``` Unassign a group from a tenant > Unassigns a group from a specified tenant. Members of the group (users, clients) will no longer have access to the tenant’s data - except they are assigned directly to the tenant. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The tenant or group was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a group from a tenant:** ```python def unassign_group_from_tenant_example(tenant_id: TenantId, group_id: GroupId) -> None: client = CamundaClient() client.unassign_group_from_tenant( tenant_id=tenant_id, group_id=group_id, ) ``` ### unassign_mapping_rule_from_group() ```python def unassign_mapping_rule_from_group(group_id, mapping_rule_id, **kwargs) ``` Unassign a mapping rule from a group > Unassigns a mapping rule from a group. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **mapping_rule_id** (_str_) – The unique identifier of a mapping rule. Example: my-mapping-rule. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The group or mapping rule with the given ID was not found, or the mapping rule is not assigned to this group. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a mapping rule from a group:** ```python def unassign_mapping_rule_from_group_example(group_id: GroupId, mapping_rule_id: MappingRuleId) -> None: client = CamundaClient() client.unassign_mapping_rule_from_group( group_id=group_id, mapping_rule_id=mapping_rule_id, ) ``` ### unassign_mapping_rule_from_tenant() ```python def unassign_mapping_rule_from_tenant(tenant_id, mapping_rule_id, **kwargs) ``` Unassign a mapping rule from a tenant > Unassigns a single mapping rule from a specified tenant without deleting the rule. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **mapping_rule_id** (_str_) – The unique identifier of a mapping rule. Example: my-mapping-rule. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The tenant or mapping rule was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a mapping rule from a tenant:** ```python def unassign_mapping_rule_from_tenant_example(tenant_id: TenantId, mapping_rule_id: MappingRuleId) -> None: client = CamundaClient() client.unassign_mapping_rule_from_tenant( tenant_id=tenant_id, mapping_rule_id=mapping_rule_id, ) ``` ### unassign_role_from_client() ```python def unassign_role_from_client(role_id, client_id, **kwargs) ``` Unassign a role from a client > Unassigns the specified role from the client. The client will no longer inherit the authorizations associated with this role. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **client_id** (_str_) – The unique identifier of an OAuth client. Minted outside the Camunda REST API: in SaaS by Console, in Self-Managed with OIDC by the external identity provider (e.g. EntraID, Keycloak, Okta). In Self-Managed with Basic authentication, machine-to-machine applications are modelled as users instead — see the user identifier. > Example: my-application. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role or client with the given ID or username was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a role from a client:** ```python def unassign_role_from_client_example(role_id: RoleId, client_id: ClientId) -> None: client = CamundaClient() client.unassign_role_from_client( role_id=role_id, client_id=client_id, ) ``` ### unassign_role_from_group() ```python def unassign_role_from_group(role_id, group_id, **kwargs) ``` Unassign a role from a group > Unassigns the specified role from the group. All group members (user or client) no longer inherit the authorizations associated with this role. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role or group with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a role from a group:** ```python def unassign_role_from_group_example(role_id: RoleId, group_id: GroupId) -> None: client = CamundaClient() client.unassign_role_from_group( role_id=role_id, group_id=group_id, ) ``` ### unassign_role_from_mapping_rule() ```python def unassign_role_from_mapping_rule(role_id, mapping_rule_id, **kwargs) ``` Unassign a role from a mapping rule > Unassigns a role from a mapping rule. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **mapping_rule_id** (_str_) – The unique identifier of a mapping rule. Example: my-mapping-rule. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role or mapping rule with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a role from a mapping rule:** ```python def unassign_role_from_mapping_rule_example(role_id: RoleId, mapping_rule_id: MappingRuleId) -> None: client = CamundaClient() client.unassign_role_from_mapping_rule( role_id=role_id, mapping_rule_id=mapping_rule_id, ) ``` ### unassign_role_from_tenant() ```python def unassign_role_from_tenant(tenant_id, role_id, **kwargs) ``` Unassign a role from a tenant > Unassigns a role from a specified tenant. Users, Clients or Groups, that have the role assigned, will no longer have access to the tenant’s data - unless they are assigned directly to the tenant. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The tenant or role was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a role from a tenant:** ```python def unassign_role_from_tenant_example(tenant_id: TenantId, role_id: RoleId) -> None: client = CamundaClient() client.unassign_role_from_tenant( tenant_id=tenant_id, role_id=role_id, ) ``` ### unassign_role_from_user() ```python def unassign_role_from_user(role_id, username, **kwargs) ``` Unassign a role from a user > Unassigns a role from a user. The user will no longer inherit the authorizations associated with this role. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **username** (_str_) – The unique name of a user. Example: swillis. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The role or user with the given ID or username was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a role from a user:** ```python def unassign_role_from_user_example(role_id: RoleId, username: Username) -> None: client = CamundaClient() client.unassign_role_from_user( role_id=role_id, username=username, ) ``` ### unassign_user_from_group() ```python def unassign_user_from_group(group_id, username, **kwargs) ``` Unassign a user from a group > Unassigns a user from a group. The user is removed as a group member, with associated authorizations, roles, and tenant assignments no longer applied. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **username** (_str_) – The unique name of a user. Example: swillis. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The group or user with the given ID was not found, or the user is not assigned to this group. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a user from a group:** ```python def unassign_user_from_group_example(group_id: GroupId, username: Username) -> None: client = CamundaClient() client.unassign_user_from_group( group_id=group_id, username=username, ) ``` ### unassign_user_from_tenant() ```python def unassign_user_from_tenant(tenant_id, username, **kwargs) ``` Unassign a user from a tenant > Unassigns the user from the specified tenant. The user can no longer access tenant data. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **username** (_str_) – The unique name of a user. Example: swillis. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The tenant or user was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a user from a tenant:** ```python def unassign_user_from_tenant_example(tenant_id: TenantId, username: Username) -> None: client = CamundaClient() client.unassign_user_from_tenant( tenant_id=tenant_id, username=username, ) ``` ### unassign_user_task() ```python def unassign_user_task(user_task_key, **kwargs) ``` Unassign user task > Removes the assignee of a task with the given key. Unassignment waits for blocking task listeners on this lifecycle transition. If listener processing is delayed beyond the request timeout, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. - **Parameters:** - **user_task_key** (_str_) – System-generated key for a user task. - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The user task with the given key was not found. - **errors.ConflictError** – If the response status code is 409. The user task with the given key is in the wrong state currently. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.GatewayTimeoutError** – If the response status code is 504. The request timed out between the gateway and the broker. For these endpoints, this often happens when user task listeners are configured and the corresponding listener job is not completed within the request timeout. Common causes include no available job workers for the listener type, busy or crashed job workers, or delayed job completion. As with any gateway timeout, general timeout causes (for example transient network issues) can also result in a 504 response. Troubleshooting: - verify that job workers for the listener type are running and healthy - check worker logs for crashes, retries, and completion failures - check network connectivity between workers, gateway, and broker - retry with backoff after transient failures - fail without retries if a problem persists - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Unassign a user task:** ```python def unassign_user_task_example(user_task_key: UserTaskKey) -> None: client = CamundaClient() client.unassign_user_task(user_task_key=user_task_key) ``` ### update_agent_instance() ```python def update_agent_instance(agent_instance_key, , data, **kwargs) ``` Update agent instance > Updates the mutable fields of an agent instance: status, metric counters, and tools. Metric values are treated as deltas and applied immediately to the aggregate counters. Tool updates replace the existing tool list. At least one of status, metrics, or tools must be provided. - **Parameters:** - **agent_instance_key** (_str_) – System-generated key for an agent instance. Example: 4503599627370496. - **body** (_AgentInstanceUpdateRequest_) – Request to update the mutable state of an agent instance. At least one of status, metrics, or tools must be provided. - **data** (_AgentInstanceUpdateRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The agent instance with the given key was not found. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Update an agent instance:** ```python def update_agent_instance_example(agent_instance_key: AgentInstanceKey) -> None: client = CamundaClient() client.update_agent_instance( agent_instance_key=agent_instance_key, data=AgentInstanceUpdateRequest( status=AgentInstanceUpdateRequestStatus.THINKING, ), ) ``` ### update_authorization() ```python def update_authorization(authorization_key, , data, **kwargs) ``` Update authorization > Update the authorization with the given key. - **Parameters:** - **authorization_key** (_str_) – System-generated key for an authorization. Example: 2251799813684332. - **body** (_AuthorizationIdBasedRequest_ _|_ _AuthorizationPropertyBasedRequest_) - **data** (_AuthorizationIdBasedRequest_ _|_ _AuthorizationPropertyBasedRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.NotFoundError** – If the response status code is 404. The authorization with the authorizationKey was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Update an authorization:** ```python def update_authorization_example(authorization_key: AuthorizationKey) -> None: client = CamundaClient() client.update_authorization( authorization_key=authorization_key, data=AuthorizationIdBasedRequest( resource_type=AuthorizationIdBasedRequestResourceType.PROCESS_DEFINITION, permission_types=[ AuthorizationIdBasedRequestPermissionTypesItem.READ, AuthorizationIdBasedRequestPermissionTypesItem.UPDATE, AuthorizationIdBasedRequestPermissionTypesItem.DELETE, ], resource_id="my-process", owner_type=OwnerTypeEnum.USER, owner_id="user@example.com", ), ) ``` ### update_global_cluster_variable() ```python def update_global_cluster_variable(name, , data, **kwargs) ``` Update a global-scoped cluster variable > Updates the value of an existing global cluster variable. The variable must exist, otherwise a 404 error is returned. - **Parameters:** - **name** (_str_) – The name of a cluster variable. Unique within its scope (global or tenant- specific). Example: feature-flag-checkout. - **body** (_UpdateClusterVariableRequest_) - **data** (_UpdateClusterVariableRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Cluster variable not found - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ClusterVariableResult - **Return type:** ClusterVariableResult #### Examples **Update a global cluster variable:** ```python def update_global_cluster_variable_example(name: ClusterVariableName) -> None: client = CamundaClient() result = client.update_global_cluster_variable( name=name, data=UpdateClusterVariableRequest( value=UpdateClusterVariableRequestValue.from_dict({"key": "updated-value"}), ), ) print(f"Updated variable: {result.name}") ``` ### update_global_task_listener() ```python def update_global_task_listener(id, , data, **kwargs) ``` Update global user task listener > Updates a global user task listener. - **Parameters:** - **id** (_str_) – The user-defined id for the global listener Example: GlobalListener_1. - **body** (_UpdateGlobalTaskListenerRequest_) - **data** (_UpdateGlobalTaskListenerRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The global user task listener was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GlobalTaskListenerResult - **Return type:** GlobalTaskListenerResult #### Examples **Update a global task listener:** ```python def update_global_task_listener_example(listener_id: GlobalListenerId) -> None: client = CamundaClient() result = client.update_global_task_listener( id=listener_id, data=UpdateGlobalTaskListenerRequest( event_types=[GlobalTaskListenerEventTypeEnum.COMPLETING], type_="updated-task-listener", ), ) print(f"Updated listener: {result.id}") ``` ### update_group() ```python def update_group(group_id, , data, **kwargs) ``` Update group > Update a group with the given ID. - **Parameters:** - **group_id** (_str_) – The unique identifier of a group. Example: engineering. - **body** (_GroupUpdateRequest_) - **data** (_GroupUpdateRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.NotFoundError** – If the response status code is 404. The group with the given ID was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** GroupUpdateResult - **Return type:** GroupUpdateResult #### Examples **Update a group:** ```python def update_group_example(group_id: GroupId) -> None: client = CamundaClient() client.update_group( group_id=group_id, data=GroupUpdateRequest(name="engineering-team"), ) ``` ### update_job() ```python def update_job(job_key, , data, **kwargs) ``` Update job > Update a job with the given key. - **Parameters:** - **job_key** (_str_) – System-generated key for a job. Example: 2251799813653498. - **body** (_JobUpdateRequest_) - **data** (_JobUpdateRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The job with the jobKey is not found. - **errors.ConflictError** – If the response status code is 409. The job with the given key is in the wrong state currently. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Update a job:** ```python def update_job_example(job_key: JobKey) -> None: client = CamundaClient() client.update_job( job_key=job_key, data=JobUpdateRequest( changeset=JobChangeset( retries=3, ), ), ) ``` ### update_mapping_rule() ```python def update_mapping_rule(mapping_rule_id, *, data=, **kwargs) ``` Update mapping rule > Update a mapping rule. - **Parameters:** - **mapping_rule_id** (_str_) – The unique identifier of a mapping rule. Example: my-mapping-rule. - **body** (_MappingRuleUpdateRequest_ _|_ _Unset_) - **data** (_MappingRuleUpdateRequest_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. The request to update a mapping rule was denied. More details are provided in the response body. - **errors.NotFoundError** – If the response status code is 404. The request to update a mapping rule was denied. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** MappingRuleUpdateResult - **Return type:** MappingRuleUpdateResult #### Examples **Update a mapping rule:** ```python def update_mapping_rule_example(mapping_rule_id: MappingRuleId) -> None: client = CamundaClient() client.update_mapping_rule( mapping_rule_id=mapping_rule_id, data=MappingRuleUpdateRequest( claim_name="groups", claim_value="senior-engineering", name="Senior Engineering Mapping", ), ) ``` ### update_role() ```python def update_role(role_id, , data, **kwargs) ``` Update role > Update a role with the given ID. - **Parameters:** - **role_id** (_str_) – The unique identifier of a role. Example: admin. - **body** (_RoleUpdateRequest_) - **data** (_RoleUpdateRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.NotFoundError** – If the response status code is 404. The role with the ID is not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** RoleUpdateResult - **Return type:** RoleUpdateResult #### Examples **Update a role:** ```python def update_role_example(role_id: RoleId) -> None: client = CamundaClient() client.update_role( role_id=role_id, data=RoleUpdateRequest(name="senior-developer"), ) ``` ### update_tenant() ```python def update_tenant(tenant_id, , data, **kwargs) ``` Update tenant > Updates an existing tenant. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **body** (_TenantUpdateRequest_) - **data** (_TenantUpdateRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Not found. The tenant was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** TenantUpdateResult - **Return type:** TenantUpdateResult #### Examples **Update a tenant:** ```python def update_tenant_example(tenant_id: TenantId) -> None: client = CamundaClient() client.update_tenant( tenant_id=tenant_id, data=TenantUpdateRequest(name="Acme Corp International"), ) ``` ### update_tenant_cluster_variable() ```python def update_tenant_cluster_variable(tenant_id, name, , data, **kwargs) ``` Update a tenant-scoped cluster variable > Updates the value of an existing tenant-scoped cluster variable. The variable must exist, otherwise a 404 error is returned. - **Parameters:** - **tenant_id** (_str_) – The unique identifier of the tenant. Example: customer-service. - **name** (_str_) – The name of a cluster variable. Unique within its scope (global or tenant- specific). Example: feature-flag-checkout. - **body** (_UpdateClusterVariableRequest_) - **data** (_UpdateClusterVariableRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.UnauthorizedError** – If the response status code is 401. The request lacks valid authentication credentials. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. Cluster variable not found - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** ClusterVariableResult - **Return type:** ClusterVariableResult #### Examples **Update a tenant cluster variable:** ```python def update_tenant_cluster_variable_example(tenant_id: TenantId, name: ClusterVariableName) -> None: client = CamundaClient() result = client.update_tenant_cluster_variable( tenant_id=tenant_id, name=name, data=UpdateClusterVariableRequest( value=UpdateClusterVariableRequestValue.from_dict({"key": "updated-tenant-value"}), ), ) print(f"Updated variable: {result.name}") ``` ### update_user() ```python def update_user(username, , data, **kwargs) ``` Update user > Updates a user. - **Parameters:** - **username** (_str_) – The unique name of a user. Example: swillis. - **body** (_UserUpdateRequest_) - **data** (_UserUpdateRequest_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.ForbiddenError** – If the response status code is 403. Forbidden. The request is not allowed. - **errors.NotFoundError** – If the response status code is 404. The user was not found. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** UserUpdateResult - **Return type:** UserUpdateResult #### Examples **Update a user:** ```python def update_user_example(username: Username) -> None: client = CamundaClient() client.update_user( username=username, data=UserUpdateRequest( name="Jane Smith", email="jsmith@example.com", ), ) ``` ### update_user_task() ```python def update_user_task(user_task_key, *, data=, **kwargs) ``` Update user task > Update a user task with the given key. Updates wait for blocking task listeners on this lifecycle transition. If listener processing is delayed beyond the request timeout, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. - **Parameters:** - **user_task_key** (_str_) – System-generated key for a user task. - **body** (_UserTaskUpdateRequest_ _|_ _Unset_) - **data** (_UserTaskUpdateRequest_ _|_ _Unset_) - **kwargs** (_Any_) - **Raises:** - **errors.BadRequestError** – If the response status code is 400. The provided data is not valid. - **errors.NotFoundError** – If the response status code is 404. The user task with the given key was not found. - **errors.ConflictError** – If the response status code is 409. The user task with the given key is in the wrong state currently. More details are provided in the response body. - **errors.InternalServerErrorError** – If the response status code is 500. An internal error occurred while processing the request. - **errors.ServiceUnavailableError** – If the response status code is 503. The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server’s compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains RESOURCE_EXHAUSTED. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: [internal processing](../../../components/zeebe/technical-concepts/internal-processing.md#handling-backpressure) . - **errors.GatewayTimeoutError** – If the response status code is 504. The request timed out between the gateway and the broker. For these endpoints, this often happens when user task listeners are configured and the corresponding listener job is not completed within the request timeout. Common causes include no available job workers for the listener type, busy or crashed job workers, or delayed job completion. As with any gateway timeout, general timeout causes (for example transient network issues) can also result in a 504 response. Troubleshooting: - verify that job workers for the listener type are running and healthy - check worker logs for crashes, retries, and completion failures - check network connectivity between workers, gateway, and broker - retry with backoff after transient failures - fail without retries if a problem persists - **errors.UnexpectedStatus** – If the response status code is not documented. - **httpx.TimeoutException** – If the request takes longer than Client.timeout. - **Returns:** None - **Return type:** None #### Examples **Update a user task:** ```python def update_user_task_example(user_task_key: UserTaskKey) -> None: client = CamundaClient() client.update_user_task( user_task_key=user_task_key, data=UserTaskUpdateRequest( changeset=Changeset( due_date=datetime.datetime(2025, 12, 31, 23, 59, 59), ), ), ) ``` --- ## Configuration(3) ## Client ```python class Client(base_url, , raise_on_unexpected_status=False, cookies=NOTHING, headers=NOTHING, timeout=None, verify_ssl=True, follow_redirects=False, httpx_args=NOTHING) ``` Bases: `object` A class for keeping track of data related to the API The following are accepted as keyword arguments and will be used to construct httpx Clients internally: > `base_url`: The base URL for the API, all requests are made to a relative path to this URL > `cookies`: A dictionary of cookies to be sent with every request > `headers`: A dictionary of headers to be sent with every request > `timeout`: The maximum amount of a time a request can take. API functions will raise > httpx.TimeoutException if this is exceeded. > `verify_ssl`: Whether or not to verify the SSL certificate of the API server. This should be True in production, > but can be set to False for testing purposes. > `follow_redirects`: Whether or not to follow redirects. Default value is False. > `httpx_args`: A dictionary of additional arguments to be passed to the `httpx.Client` and `httpx.AsyncClient` constructor. - **Parameters:** - **base_url** (_str_) - **raise_on_unexpected_status** (_bool_) - **cookies** (_dict_ _[\*\*str_ _,_ _str_ _]_) - **headers** (_dict_ _[\*\*str_ _,_ _str_ _]_) - **timeout** (_httpx.Timeout_ _|_ _None_) - **verify_ssl** (_str_ _|_ _bool_ _|_ _ssl.SSLContext_) - **follow_redirects** (_bool_) - **httpx_args** (_dict_ _[\*\*str_ _,_ _Any_ _]_) ### raise_on_unexpected_status Whether or not to raise an errors.UnexpectedStatus if the API returns a status code that was not documented in the source OpenAPI document. Can also be provided as a keyword argument to the constructor. - **Type:** bool ### get_async_httpx_client() ```python def get_async_httpx_client() ``` Get the underlying httpx.AsyncClient, constructing a new one if not previously set - **Return type:** _AsyncClient_ ### get_httpx_client() ```python def get_httpx_client() ``` Get the underlying httpx.Client, constructing a new one if not previously set - **Return type:** _Client_ ### raise_on_unexpected_status ```python raise_on_unexpected_status: bool ``` ### set_async_httpx_client() ```python def set_async_httpx_client(async_client) ``` Manually set the underlying httpx.AsyncClient **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. - **Parameters:** **async_client** (_AsyncClient_) - **Return type:** [_Client_](#client) ### set_httpx_client() ```python def set_httpx_client(client) ``` Manually set the underlying httpx.Client **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. - **Parameters:** **client** (_Client_) - **Return type:** [_Client_](#client) ### with_cookies() ```python def with_cookies(cookies) ``` Get a new client matching this one with additional cookies - **Parameters:** **cookies** (_dict_ _[\*\*str_ _,_ _str_ _]_) - **Return type:** [_Client_](#client) ### with_headers() ```python def with_headers(headers) ``` Get a new client matching this one with additional headers - **Parameters:** **headers** (_dict_ _[\*\*str_ _,_ _str_ _]_) - **Return type:** [_Client_](#client) ### with_timeout() ```python def with_timeout(timeout) ``` Get a new client matching this one with a new timeout configuration - **Parameters:** **timeout** (_Timeout_) - **Return type:** [_Client_](#client) ## AuthenticatedClient ```python class AuthenticatedClient(base_url, token, prefix='Bearer', auth_header_name='Authorization', , raise_on_unexpected_status=False, cookies=NOTHING, headers=NOTHING, timeout=None, verify_ssl=True, follow_redirects=False, httpx_args=NOTHING) ``` Bases: `object` A Client which has been authenticated for use on secured endpoints The following are accepted as keyword arguments and will be used to construct httpx Clients internally: > `base_url`: The base URL for the API, all requests are made to a relative path to this URL > `cookies`: A dictionary of cookies to be sent with every request > `headers`: A dictionary of headers to be sent with every request > `timeout`: The maximum amount of a time a request can take. API functions will raise > httpx.TimeoutException if this is exceeded. > `verify_ssl`: Whether or not to verify the SSL certificate of the API server. This should be True in production, > but can be set to False for testing purposes. > `follow_redirects`: Whether or not to follow redirects. Default value is False. > `httpx_args`: A dictionary of additional arguments to be passed to the `httpx.Client` and `httpx.AsyncClient` constructor. - **Parameters:** - **base_url** (_str_) - **token** (_str_) - **prefix** (_str_) - **auth_header_name** (_str_) - **raise_on_unexpected_status** (_bool_) - **cookies** (_dict_ _[\*\*str_ _,_ _str_ _]_) - **headers** (_dict_ _[\*\*str_ _,_ _str_ _]_) - **timeout** (_httpx.Timeout_ _|_ _None_) - **verify_ssl** (_str_ _|_ _bool_ _|_ _ssl.SSLContext_) - **follow_redirects** (_bool_) - **httpx_args** (_dict_ _[\*\*str_ _,_ _Any_ _]_) ### raise_on_unexpected_status Whether or not to raise an errors.UnexpectedStatus if the API returns a status code that was not documented in the source OpenAPI document. Can also be provided as a keyword argument to the constructor. - **Type:** bool ### token The token to use for authentication - **Type:** str ### prefix The prefix to use for the Authorization header - **Type:** str ### auth_header_name The name of the Authorization header - **Type:** str ### auth_header_name ```python auth_header_name: str ``` ### get_async_httpx_client() ```python def get_async_httpx_client() ``` Get the underlying httpx.AsyncClient, constructing a new one if not previously set - **Return type:** _AsyncClient_ ### get_httpx_client() ```python def get_httpx_client() ``` Get the underlying httpx.Client, constructing a new one if not previously set - **Return type:** _Client_ ### prefix ```python prefix: str ``` ### raise_on_unexpected_status ```python raise_on_unexpected_status: bool ``` ### set_async_httpx_client() ```python def set_async_httpx_client(async_client) ``` Manually set the underlying httpx.AsyncClient **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. - **Parameters:** **async_client** (_AsyncClient_) - **Return type:** [_AuthenticatedClient_](#authenticatedclient) ### set_httpx_client() ```python def set_httpx_client(client) ``` Manually set the underlying httpx.Client **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. - **Parameters:** **client** (_Client_) - **Return type:** [_AuthenticatedClient_](#authenticatedclient) ### token ```python token: str ``` ### with_cookies() ```python def with_cookies(cookies) ``` Get a new client matching this one with additional cookies - **Parameters:** **cookies** (_dict_ _[\*\*str_ _,_ _str_ _]_) - **Return type:** [_AuthenticatedClient_](#authenticatedclient) ### with_headers() ```python def with_headers(headers) ``` Get a new client matching this one with additional headers - **Parameters:** **headers** (_dict_ _[\*\*str_ _,_ _str_ _]_) - **Return type:** [_AuthenticatedClient_](#authenticatedclient) ### with_timeout() ```python def with_timeout(timeout) ``` Get a new client matching this one with a new timeout configuration - **Parameters:** **timeout** (_Timeout_) - **Return type:** [_AuthenticatedClient_](#authenticatedclient) --- ## Python SDK API Reference # API Reference - [CamundaClient](client.md) - [`CamundaClient`](client.md#camunda_orchestration_sdk.CamundaClient) - [CamundaAsyncClient](async-client.md) - [`CamundaAsyncClient`](async-client.md#camunda_orchestration_sdk.CamundaAsyncClient) - [Configuration](configuration.md) - [`Client`](configuration.md#camunda_orchestration_sdk.Client) - [`AuthenticatedClient`](configuration.md#camunda_orchestration_sdk.AuthenticatedClient) - [Runtime](runtime.md) - [Authentication](runtime.md#module-camunda_orchestration_sdk.runtime.auth) - [Logging](runtime.md#module-camunda_orchestration_sdk.runtime.logging) - [Job Worker](runtime.md#module-camunda_orchestration_sdk.runtime.job_worker) - [Configuration Resolver](runtime.md#module-camunda_orchestration_sdk.runtime.configuration_resolver) - [Semantic Types](types.md) - [`AgentInstanceKey`](types.md#camunda_orchestration_sdk.semantic_types.AgentInstanceKey) - [`AuditLogEntityKey`](types.md#camunda_orchestration_sdk.semantic_types.AuditLogEntityKey) - [`AuditLogKey`](types.md#camunda_orchestration_sdk.semantic_types.AuditLogKey) - [`AuthorizationKey`](types.md#camunda_orchestration_sdk.semantic_types.AuthorizationKey) - [`BatchOperationKey`](types.md#camunda_orchestration_sdk.semantic_types.BatchOperationKey) - [`BusinessId`](types.md#camunda_orchestration_sdk.semantic_types.BusinessId) - [`ClientId`](types.md#camunda_orchestration_sdk.semantic_types.ClientId) - [`ClusterVariableName`](types.md#camunda_orchestration_sdk.semantic_types.ClusterVariableName) - [`ConditionalEvaluationKey`](types.md#camunda_orchestration_sdk.semantic_types.ConditionalEvaluationKey) - [`DecisionDefinitionId`](types.md#camunda_orchestration_sdk.semantic_types.DecisionDefinitionId) - [`DecisionDefinitionKey`](types.md#camunda_orchestration_sdk.semantic_types.DecisionDefinitionKey) - [`DecisionEvaluationInstanceKey`](types.md#camunda_orchestration_sdk.semantic_types.DecisionEvaluationInstanceKey) - [`DecisionEvaluationKey`](types.md#camunda_orchestration_sdk.semantic_types.DecisionEvaluationKey) - [`DecisionInstanceKey`](types.md#camunda_orchestration_sdk.semantic_types.DecisionInstanceKey) - [`DecisionRequirementsKey`](types.md#camunda_orchestration_sdk.semantic_types.DecisionRequirementsKey) - [`DeploymentKey`](types.md#camunda_orchestration_sdk.semantic_types.DeploymentKey) - [`DocumentId`](types.md#camunda_orchestration_sdk.semantic_types.DocumentId) - [`ElementId`](types.md#camunda_orchestration_sdk.semantic_types.ElementId) - [`ElementInstanceKey`](types.md#camunda_orchestration_sdk.semantic_types.ElementInstanceKey) - [`EndCursor`](types.md#camunda_orchestration_sdk.semantic_types.EndCursor) - [`FormId`](types.md#camunda_orchestration_sdk.semantic_types.FormId) - [`FormKey`](types.md#camunda_orchestration_sdk.semantic_types.FormKey) - [`GlobalListenerId`](types.md#camunda_orchestration_sdk.semantic_types.GlobalListenerId) - [`GroupId`](types.md#camunda_orchestration_sdk.semantic_types.GroupId) - [`IncidentKey`](types.md#camunda_orchestration_sdk.semantic_types.IncidentKey) - [`JobKey`](types.md#camunda_orchestration_sdk.semantic_types.JobKey) - [`MappingRuleId`](types.md#camunda_orchestration_sdk.semantic_types.MappingRuleId) - [`MessageKey`](types.md#camunda_orchestration_sdk.semantic_types.MessageKey) - [`MessageSubscriptionKey`](types.md#camunda_orchestration_sdk.semantic_types.MessageSubscriptionKey) - [`ProcessDefinitionId`](types.md#camunda_orchestration_sdk.semantic_types.ProcessDefinitionId) - [`ProcessDefinitionKey`](types.md#camunda_orchestration_sdk.semantic_types.ProcessDefinitionKey) - [`ProcessInstanceKey`](types.md#camunda_orchestration_sdk.semantic_types.ProcessInstanceKey) - [`RoleId`](types.md#camunda_orchestration_sdk.semantic_types.RoleId) - [`SignalKey`](types.md#camunda_orchestration_sdk.semantic_types.SignalKey) - [`StartCursor`](types.md#camunda_orchestration_sdk.semantic_types.StartCursor) - [`Tag`](types.md#camunda_orchestration_sdk.semantic_types.Tag) - [`TenantId`](types.md#camunda_orchestration_sdk.semantic_types.TenantId) - [`UserTaskKey`](types.md#camunda_orchestration_sdk.semantic_types.UserTaskKey) - [`Username`](types.md#camunda_orchestration_sdk.semantic_types.Username) - [`VariableKey`](types.md#camunda_orchestration_sdk.semantic_types.VariableKey) - [`lift_resource_key()`](types.md#camunda_orchestration_sdk.semantic_types.lift_resource_key) - [`lift_scope_key()`](types.md#camunda_orchestration_sdk.semantic_types.lift_scope_key) - [`try_lift_resource_key()`](types.md#camunda_orchestration_sdk.semantic_types.try_lift_resource_key) - [`try_lift_scope_key()`](types.md#camunda_orchestration_sdk.semantic_types.try_lift_scope_key) --- ## Runtime(Api-reference) ## Authentication ## AsyncAuthProvider ```python class AsyncAuthProvider(*args, **kwargs) ``` Bases: `Protocol` Async auth provider variant. If an auth provider implements this protocol, async clients will prefer it. ### aget_headers() ```python async def aget_headers() ``` - **Return type:** _Mapping_[str, str] ## AsyncOAuthClientCredentialsAuthProvider ```python class AsyncOAuthClientCredentialsAuthProvider(, oauth_url, client_id, client_secret, audience, cache_dir=None, disk_cache_disable=False, saas_401_cooldown_s=30.0, transport=None, timeout=None, logger=None) ``` Bases: `object` OAuth 2.0 Client Credentials provider with in-memory caching. This is designed for async clients. - **Parameters:** - **oauth_url** (_str_) - **client_id** (_str_) - **client_secret** (_str_) - **audience** (_str_) - **cache_dir** (_str_ _|_ _None_) - **disk_cache_disable** (_bool_) - **saas_401_cooldown_s** (_float_) - **transport** (_httpx.AsyncBaseTransport_ _|_ _None_) - **timeout** (_float_ _|_ _None_) - **logger** ([_SdkLogger_](#sdklogger) _|_ _None_) ### aclose() ```python async def aclose() ``` Close the underlying async HTTP client used for token requests. - **Return type:** None ### aget_headers() ```python async def aget_headers() ``` - **Return type:** _Mapping_[str, str] ### get_headers() ```python def get_headers() ``` Sync fallback satisfying the `AuthProvider` protocol. Returns cached token headers if a valid token is already held, otherwise returns empty headers (the next async request hook will call `aget_headers` to fetch a fresh token). - **Return type:** _Mapping_[str, str] ## AuthProvider ```python class AuthProvider(*args, **kwargs) ``` Bases: `Protocol` Provides per-request authentication headers. Implementations are expected to be lightweight and safe to call for every request. ### get_headers() ```python def get_headers() ``` - **Return type:** _Mapping_[str, str] ## BasicAuthProvider ```python class BasicAuthProvider(, username, password) ``` Bases: `object` HTTP Basic auth provider. - **Parameters:** - **username** (_str_) - **password** (_str_) ### get_headers() ```python def get_headers() ``` - **Return type:** _Mapping_[str, str] ## NullAuthProvider ```python class NullAuthProvider ``` Bases: `object` Default auth provider that adds no headers. ### get_headers() ```python def get_headers() ``` - **Return type:** dict[str, str] ## OAuthClientCredentialsAuthProvider ```python class OAuthClientCredentialsAuthProvider(, oauth_url, client_id, client_secret, audience, cache_dir=None, disk_cache_disable=False, saas_401_cooldown_s=30.0, transport=None, timeout=None, logger=None) ``` Bases: `object` OAuth 2.0 Client Credentials provider with in-memory caching. This is designed for sync clients. - **Parameters:** - **oauth_url** (_str_) - **client_id** (_str_) - **client_secret** (_str_) - **audience** (_str_) - **cache_dir** (_str_ _|_ _None_) - **disk_cache_disable** (_bool_) - **saas_401_cooldown_s** (_float_) - **transport** (_httpx.BaseTransport_ _|_ _None_) - **timeout** (_float_ _|_ _None_) - **logger** ([_SdkLogger_](#sdklogger) _|_ _None_) ### close() ```python def close() ``` Close the underlying HTTP client used for token requests. Call this when the application is shutting down if you created a provider instance yourself (or if you want deterministic cleanup in tests). - **Return type:** None ### get_headers() ```python def get_headers() ``` - **Return type:** _Mapping_[str, str] ### inject_auth_event_hooks() ```python def inject_auth_event_hooks(httpx_args, auth_provider, , async_client=False, log_level=None, logger=None) ``` Return a copy of httpx_args with a request hook that applies auth headers. This uses httpx event hooks so we don’t have to inject headers in every generated API call. - **Parameters:** - **httpx_args** (_dict_ _[\*\*str_ _,_ _Any_ _]_ _|_ _None_) - **auth_provider** (_object_) - **async_client** (_bool_) - **log_level** (_str_ _|_ _None_) - **logger** ([_SdkLogger_](#sdklogger) _|_ _None_) - **Return type:** dict[str, *Any*] ## Logging Pluggable logger abstraction for the Camunda SDK. Users can inject any logger that implements [`CamundaLogger`](#camundalogger) (stdlib `logging.Logger`, `loguru.logger`, or a custom object with `debug`/`info`/`warning`/`error` methods). When no logger is provided, loguru is used if installed, otherwise logging is silently disabled. ## CamundaLogger ```python class CamundaLogger(*args, **kwargs) ``` Bases: `Protocol` Protocol for a logger injectable into the SDK. Compatible with Python’s `logging.Logger`, `loguru.logger`, or any object that exposes these four methods. ### debug() ```python def debug(msg, *args, **kwargs) ``` - **Parameters:** - **msg** (_str_) - **args** (_Any_) - **kwargs** (_Any_) - **Return type:** None ### error() ```python def error(msg, *args, **kwargs) ``` - **Parameters:** - **msg** (_str_) - **args** (_Any_) - **kwargs** (_Any_) - **Return type:** None ### info() ```python def info(msg, *args, **kwargs) ``` - **Parameters:** - **msg** (_str_) - **args** (_Any_) - **kwargs** (_Any_) - **Return type:** None ### warning() ```python def warning(msg, *args, **kwargs) ``` - **Parameters:** - **msg** (_str_) - **args** (_Any_) - **kwargs** (_Any_) - **Return type:** None ## NullLogger ```python class NullLogger ``` Bases: `object` Logger that silently discards all messages. ### debug() ```python def debug(msg, *args, **kwargs) ``` - **Parameters:** - **msg** (_str_) - **args** (_Any_) - **kwargs** (_Any_) - **Return type:** None ### error() ```python def error(msg, *args, **kwargs) ``` - **Parameters:** - **msg** (_str_) - **args** (_Any_) - **kwargs** (_Any_) - **Return type:** None ### info() ```python def info(msg, *args, **kwargs) ``` - **Parameters:** - **msg** (_str_) - **args** (_Any_) - **kwargs** (_Any_) - **Return type:** None ### trace() ```python def trace(msg, *args, **kwargs) ``` - **Parameters:** - **msg** (_str_) - **args** (_Any_) - **kwargs** (_Any_) - **Return type:** None ### warning() ```python def warning(msg, *args, **kwargs) ``` - **Parameters:** - **msg** (_str_) - **args** (_Any_) - **kwargs** (_Any_) - **Return type:** None ## SdkLogger ```python class SdkLogger(logger, prefix='') ``` Bases: `object` Internal wrapper that normalises logger implementations. Adds `trace()` support (falls back to `debug()` on loggers that lack it) and `bind()` support (uses loguru’s native `bind` when available, otherwise prepends a `[key=value ...]` prefix to messages). - **Parameters:** - **logger** ([_CamundaLogger_](#camundalogger)) - **prefix** (_str_) ### bind() ```python def bind(**kwargs) ``` Create a child logger with additional context. If the underlying logger supports `bind()` (e.g. loguru), the native method is used. Otherwise context is rendered as a `[k=v ...]` prefix on each message. - **Parameters:** **kwargs** (_str_) - **Return type:** [_SdkLogger_](#sdklogger) ### debug() ```python def debug(msg, *args, **kwargs) ``` - **Parameters:** - **msg** (_str_) - **args** (_Any_) - **kwargs** (_Any_) - **Return type:** None ### error() ```python def error(msg, *args, **kwargs) ``` - **Parameters:** - **msg** (_str_) - **args** (_Any_) - **kwargs** (_Any_) - **Return type:** None ### info() ```python def info(msg, *args, **kwargs) ``` - **Parameters:** - **msg** (_str_) - **args** (_Any_) - **kwargs** (_Any_) - **Return type:** None ### trace() ```python def trace(msg) ``` - **Parameters:** **msg** (_str_) - **Return type:** None ### warning() ```python def warning(msg, *args, **kwargs) ``` - **Parameters:** - **msg** (_str_) - **args** (_Any_) - **kwargs** (_Any_) - **Return type:** None ### create_logger() ```python def create_logger(logger=None) ``` Create an [`SdkLogger`](#sdklogger). - **Parameters:** **logger** ([_CamundaLogger_](#camundalogger) _|_ _None_) – A user-supplied logger. When `None`, loguru is used if installed, otherwise a [`NullLogger`](#nulllogger) is used. - **Return type:** [_SdkLogger_](#sdklogger) ## Job Worker ### AsyncJobContext alias of [`ConnectedJobContext`](#connectedjobcontext) ## ConnectedJobContext ```python class ConnectedJobContext(type_, process_definition_id, process_definition_version, element_id, custom_headers, worker, retries, deadline, variables, tenant_id, job_key, process_instance_key, process_definition_key, element_instance_key, kind, listener_event_type, user_task, tags, root_process_instance_key, log=NOTHING, , client) ``` Bases: [`JobContext`](#jobcontext) Context for **async** handlers — includes an async client reference. Extends [`JobContext`](#jobcontext) with a `client` attribute that provides access to the Camunda API from within an async job handler. Use `await job.client.method(...)` to call API methods. This context is provided when the execution strategy is `"async"`. For `"thread"` handlers, see [`SyncJobContext`](#syncjobcontext). For `"process"` handlers, see [`JobContext`](#jobcontext). - **Parameters:** - **type\_** (_str_) - **process_definition_id** ([_ProcessDefinitionId_](types.md#camunda_orchestration_sdk.semantic_types.ProcessDefinitionId)) - **process_definition_version** (_int_) - **element_id** ([_ElementId_](types.md#camunda_orchestration_sdk.semantic_types.ElementId)) - **custom_headers** (_ActivatedJobResultCustomHeaders_) - **worker** (_str_) - **retries** (_int_) - **deadline** (_int_) - **variables** (_ActivatedJobResultVariables_) - **tenant_id** ([_TenantId_](types.md#camunda_orchestration_sdk.semantic_types.TenantId)) - **job_key** ([_JobKey_](types.md#camunda_orchestration_sdk.semantic_types.JobKey)) - **process_instance_key** ([_ProcessInstanceKey_](types.md#camunda_orchestration_sdk.semantic_types.ProcessInstanceKey)) - **process_definition_key** ([_ProcessDefinitionKey_](types.md#camunda_orchestration_sdk.semantic_types.ProcessDefinitionKey)) - **element_instance_key** ([_ElementInstanceKey_](types.md#camunda_orchestration_sdk.semantic_types.ElementInstanceKey)) - **kind** (_JobKindEnum_) - **listener_event_type** (_JobListenerEventTypeEnum_) - **user_task** (_ActivatedJobResultUserTask_ _|_ _None_) - **tags** (_list_ _[\*\*str_ _]_) - **root_process_instance_key** (_None_ _|_ [_ProcessInstanceKey_](types.md#camunda_orchestration_sdk.semantic_types.ProcessInstanceKey)) - **log** ([_SdkLogger_](#sdklogger)) - **client** ([_CamundaAsyncClient_](async-client.md#camunda_orchestration_sdk.CamundaAsyncClient)) ### client ```python client: [CamundaAsyncClient](async-client.md#camunda_orchestration_sdk.CamundaAsyncClient) ``` ### _classmethod_ create(job, client, logger=None) - **Parameters:** - **job** (_ActivatedJobResult_) - **client** ([_CamundaAsyncClient_](async-client.md#camunda_orchestration_sdk.CamundaAsyncClient)) - **logger** ([_SdkLogger_](#sdklogger) _|_ _None_) - **Return type:** [ConnectedJobContext](#connectedjobcontext) ## JobContext ```python class JobContext(type_, process_definition_id, process_definition_version, element_id, custom_headers, worker, retries, deadline, variables, tenant_id, job_key, process_instance_key, process_definition_key, element_instance_key, kind, listener_event_type, user_task, tags, root_process_instance_key, log=NOTHING) ``` Bases: `ActivatedJobResult` Read-only context for a job execution. - **Parameters:** - **type\_** (_str_) - **process_definition_id** ([_ProcessDefinitionId_](types.md#camunda_orchestration_sdk.semantic_types.ProcessDefinitionId)) - **process_definition_version** (_int_) - **element_id** ([_ElementId_](types.md#camunda_orchestration_sdk.semantic_types.ElementId)) - **custom_headers** (_ActivatedJobResultCustomHeaders_) - **worker** (_str_) - **retries** (_int_) - **deadline** (_int_) - **variables** (_ActivatedJobResultVariables_) - **tenant_id** ([_TenantId_](types.md#camunda_orchestration_sdk.semantic_types.TenantId)) - **job_key** ([_JobKey_](types.md#camunda_orchestration_sdk.semantic_types.JobKey)) - **process_instance_key** ([_ProcessInstanceKey_](types.md#camunda_orchestration_sdk.semantic_types.ProcessInstanceKey)) - **process_definition_key** ([_ProcessDefinitionKey_](types.md#camunda_orchestration_sdk.semantic_types.ProcessDefinitionKey)) - **element_instance_key** ([_ElementInstanceKey_](types.md#camunda_orchestration_sdk.semantic_types.ElementInstanceKey)) - **kind** (_JobKindEnum_) - **listener_event_type** (_JobListenerEventTypeEnum_) - **user_task** (_ActivatedJobResultUserTask_ _|_ _None_) - **tags** (_list_ _[\*\*str_ _]_) - **root_process_instance_key** (_None_ _|_ [_ProcessInstanceKey_](types.md#camunda_orchestration_sdk.semantic_types.ProcessInstanceKey)) - **log** ([_SdkLogger_](#sdklogger)) ### log A scoped logger bound to this job’s context (job type, job key). Use `job.log.info(...)` etc. inside your handler to emit structured log messages. - **Type:** [SdkLogger](#sdklogger) ### _classmethod_ from_job(job, logger=None) - **Parameters:** - **job** (_ActivatedJobResult_) - **logger** ([_SdkLogger_](#sdklogger) _|_ _None_) - **Return type:** [_JobContext_](#jobcontext) ### log ```python log: [SdkLogger](#sdklogger) ``` ### _exception_ JobError(error_code, message='') Bases: `Exception` Raise this exception to throw a BPMN error. - **Parameters:** - **error_code** (_str_) - **message** (_str_) ### _exception_ JobFailure(message, retries=None, retry_back_off=0) Bases: `Exception` Raise this exception to explicitly fail a job with custom retries/backoff. - **Parameters:** - **message** (_str_) - **retries** (_int_ _|_ _None_) - **retry_back_off** (_int_) ## JobWorker ```python class JobWorker(client, callback, config, logger=None, execution_strategy='auto', startup_jitter_max_seconds=0) ``` Bases: `object` - **Parameters:** - **client** ([_CamundaAsyncClient_](async-client.md#camunda_orchestration_sdk.CamundaAsyncClient)) - **callback** (_JobHandler_) - **config** ([_WorkerConfig_](#workerconfig)) - **logger** ([_SdkLogger_](#sdklogger) _|_ _None_) - **execution_strategy** (_EXECUTION_STRATEGY_) - **startup_jitter_max_seconds** (_float_) ### poll_loop() ```python async def poll_loop() ``` Background polling loop - always async ### start() ```python def start() ``` ### stop() ```python def stop() ``` ## SyncJobContext ```python class SyncJobContext(type_, process_definition_id, process_definition_version, element_id, custom_headers, worker, retries, deadline, variables, tenant_id, job_key, process_instance_key, process_definition_key, element_instance_key, kind, listener_event_type, user_task, tags, root_process_instance_key, log=NOTHING, , client) ``` Bases: [`JobContext`](#jobcontext) Context for **thread** handlers — includes a sync client reference. Extends [`JobContext`](#jobcontext) with a `client` attribute that provides access to the Camunda API from within a synchronous (thread) handler. Call `job.client.method(...)` directly — no `await` needed. This context is provided when the execution strategy is `"thread"`. For `"async"` handlers, see [`ConnectedJobContext`](#connectedjobcontext). For `"process"` handlers, see [`JobContext`](#jobcontext). - **Parameters:** - **type\_** (_str_) - **process_definition_id** ([_ProcessDefinitionId_](types.md#camunda_orchestration_sdk.semantic_types.ProcessDefinitionId)) - **process_definition_version** (_int_) - **element_id** ([_ElementId_](types.md#camunda_orchestration_sdk.semantic_types.ElementId)) - **custom_headers** (_ActivatedJobResultCustomHeaders_) - **worker** (_str_) - **retries** (_int_) - **deadline** (_int_) - **variables** (_ActivatedJobResultVariables_) - **tenant_id** ([_TenantId_](types.md#camunda_orchestration_sdk.semantic_types.TenantId)) - **job_key** ([_JobKey_](types.md#camunda_orchestration_sdk.semantic_types.JobKey)) - **process_instance_key** ([_ProcessInstanceKey_](types.md#camunda_orchestration_sdk.semantic_types.ProcessInstanceKey)) - **process_definition_key** ([_ProcessDefinitionKey_](types.md#camunda_orchestration_sdk.semantic_types.ProcessDefinitionKey)) - **element_instance_key** ([_ElementInstanceKey_](types.md#camunda_orchestration_sdk.semantic_types.ElementInstanceKey)) - **kind** (_JobKindEnum_) - **listener_event_type** (_JobListenerEventTypeEnum_) - **user_task** (_ActivatedJobResultUserTask_ _|_ _None_) - **tags** (_list_ _[\*\*str_ _]_) - **root_process_instance_key** (_None_ _|_ [_ProcessInstanceKey_](types.md#camunda_orchestration_sdk.semantic_types.ProcessInstanceKey)) - **log** ([_SdkLogger_](#sdklogger)) - **client** ([_CamundaClient_](client.md#camunda_orchestration_sdk.CamundaClient)) ### client ```python client: [CamundaClient](client.md#camunda_orchestration_sdk.CamundaClient) ``` ### _classmethod_ create(job, client, logger=None) - **Parameters:** - **job** (_ActivatedJobResult_) - **client** ([_CamundaClient_](client.md#camunda_orchestration_sdk.CamundaClient)) - **logger** ([_SdkLogger_](#sdklogger) _|_ _None_) - **Return type:** [SyncJobContext](#syncjobcontext) ## WorkerConfig ```python class WorkerConfig(job_type, job_timeout_milliseconds=None, request_timeout_milliseconds=None, max_concurrent_jobs=None, fetch_variables=None, worker_name=None) ``` Bases: `object` User-facing configuration. Fields left as `None` inherit the global default from `CAMUNDA_WORKER_*` environment variables (or the client constructor), falling back to the hardcoded SDK default when neither is set. - **Parameters:** - **job_type** (_str_) - **job_timeout_milliseconds** (_int_ _|_ _None_) - **request_timeout_milliseconds** (_int_ _|_ _None_) - **max_concurrent_jobs** (_int_ _|_ _None_) - **fetch_variables** (_list_ _[\*\*str_ _]_ _|_ _None_) - **worker_name** (_str_ _|_ _None_) ### fetch_variables ```python fetch_variables: list[str] | None* *= None ``` ### job_timeout_milliseconds ```python job_timeout_milliseconds: int | None* *= None ``` How long the job is reserved for this worker only. Falls back to `CAMUNDA_WORKER_TIMEOUT` env var if not set. ### job_type ```python job_type: str ``` Job type to activate and process. ### max_concurrent_jobs ```python max_concurrent_jobs: int | None* *= None ``` Max jobs executing at once. Falls back to `CAMUNDA_WORKER_MAX_CONCURRENT_JOBS` env var, then `10`. ### request_timeout_milliseconds ```python request_timeout_milliseconds: int | None* *= None ``` Long-poll request timeout in milliseconds. Falls back to `CAMUNDA_WORKER_REQUEST_TIMEOUT` env var, then `0`. ### worker_name ```python worker_name: str | None* *= None ``` Worker identifier. Falls back to `CAMUNDA_WORKER_NAME` env var, then `"camunda-python-sdk-worker"`. ### resolve_worker_config() ```python def resolve_worker_config(config, configuration) ``` Return a new WorkerConfig with `None` fields filled from _configuration_. Precedence: explicit field value > `CAMUNDA_WORKER_*` config > hardcoded default. Raises `ValueError` if `job_timeout_milliseconds` is still unset after merging. - **Parameters:** - **config** ([_WorkerConfig_](#workerconfig)) - **configuration** (_Any_) - **Return type:** [_WorkerConfig_](#workerconfig) ## Configuration Resolver ## CamundaSdkConfigPartial ```python class CamundaSdkConfigPartial ``` Bases: `TypedDict` ### CAMUNDA_AUTH_STRATEGY ```python CAMUNDA_AUTH_STRATEGY: Literal['NONE', 'OAUTH', 'BASIC'] ``` ### CAMUNDA_BASIC_AUTH_PASSWORD ```python CAMUNDA_BASIC_AUTH_PASSWORD: str ``` ### CAMUNDA_BASIC_AUTH_USERNAME ```python CAMUNDA_BASIC_AUTH_USERNAME: str ``` ### CAMUNDA_CLIENT_AUTH_CLIENTID ```python CAMUNDA_CLIENT_AUTH_CLIENTID: str ``` ### CAMUNDA_CLIENT_AUTH_CLIENTSECRET ```python CAMUNDA_CLIENT_AUTH_CLIENTSECRET: str ``` ### CAMUNDA_CLIENT_ID ```python CAMUNDA_CLIENT_ID: str ``` ### CAMUNDA_CLIENT_SECRET ```python CAMUNDA_CLIENT_SECRET: str ``` ### CAMUNDA_LOAD_ENVFILE ```python CAMUNDA_LOAD_ENVFILE: str ``` ### CAMUNDA_MTLS_CA ```python CAMUNDA_MTLS_CA: str ``` ### CAMUNDA_MTLS_CA_PATH ```python CAMUNDA_MTLS_CA_PATH: str ``` ### CAMUNDA_MTLS_CERT ```python CAMUNDA_MTLS_CERT: str ``` ### CAMUNDA_MTLS_CERT_PATH ```python CAMUNDA_MTLS_CERT_PATH: str ``` ### CAMUNDA_MTLS_KEY ```python CAMUNDA_MTLS_KEY: str ``` ### CAMUNDA_MTLS_KEY_PASSPHRASE ```python CAMUNDA_MTLS_KEY_PASSPHRASE: str ``` ### CAMUNDA_MTLS_KEY_PATH ```python CAMUNDA_MTLS_KEY_PATH: str ``` ### CAMUNDA_OAUTH_URL ```python CAMUNDA_OAUTH_URL: str ``` ### CAMUNDA_REST_ADDRESS ```python CAMUNDA_REST_ADDRESS: str ``` ### CAMUNDA_SDK_BACKPRESSURE_PROFILE ```python CAMUNDA_SDK_BACKPRESSURE_PROFILE: str ``` ### CAMUNDA_SDK_LOG_LEVEL ```python CAMUNDA_SDK_LOG_LEVEL: Literal['silent', 'error', 'warn', 'info', 'debug', 'trace', 'silly'] ``` ### CAMUNDA_TENANT_ID ```python CAMUNDA_TENANT_ID: str ``` ### CAMUNDA_TOKEN_AUDIENCE ```python CAMUNDA_TOKEN_AUDIENCE: str ``` ### CAMUNDA_TOKEN_CACHE_DIR ```python CAMUNDA_TOKEN_CACHE_DIR: str ``` ### CAMUNDA_TOKEN_DISK_CACHE_DISABLE ```python CAMUNDA_TOKEN_DISK_CACHE_DISABLE: str ``` ### CAMUNDA_WORKER_MAX_CONCURRENT_JOBS ```python CAMUNDA_WORKER_MAX_CONCURRENT_JOBS: str ``` ### CAMUNDA_WORKER_NAME ```python CAMUNDA_WORKER_NAME: str ``` ### CAMUNDA_WORKER_REQUEST_TIMEOUT ```python CAMUNDA_WORKER_REQUEST_TIMEOUT: str ``` ### CAMUNDA_WORKER_STARTUP_JITTER_MAX_SECONDS ```python CAMUNDA_WORKER_STARTUP_JITTER_MAX_SECONDS: str ``` ### CAMUNDA_WORKER_TIMEOUT ```python CAMUNDA_WORKER_TIMEOUT: str ``` ### ZEEBE_REST_ADDRESS ```python ZEEBE_REST_ADDRESS: str ``` ## CamundaSdkConfiguration ```python class CamundaSdkConfiguration(, ZEEBE_REST_ADDRESS='http://localhost:8080/v2', CAMUNDA_REST_ADDRESS='http://localhost:8080/v2', CAMUNDA_TOKEN_AUDIENCE='zeebe.camunda.io', CAMUNDA_OAUTH_URL='https://login.cloud.camunda.io/oauth/token', CAMUNDA_CLIENT_ID=None, CAMUNDA_CLIENT_SECRET=None, CAMUNDA_CLIENT_AUTH_CLIENTID=None, CAMUNDA_CLIENT_AUTH_CLIENTSECRET=None, CAMUNDA_AUTH_STRATEGY='NONE', CAMUNDA_BASIC_AUTH_USERNAME=None, CAMUNDA_BASIC_AUTH_PASSWORD=None, CAMUNDA_SDK_LOG_LEVEL='error', CAMUNDA_TOKEN_CACHE_DIR=None, CAMUNDA_TOKEN_DISK_CACHE_DISABLE=False, CAMUNDA_SDK_BACKPRESSURE_PROFILE='BALANCED', CAMUNDA_TENANT_ID=None, CAMUNDA_WORKER_TIMEOUT=None, CAMUNDA_WORKER_MAX_CONCURRENT_JOBS=None, CAMUNDA_WORKER_REQUEST_TIMEOUT=None, CAMUNDA_WORKER_NAME=None, CAMUNDA_WORKER_STARTUP_JITTER_MAX_SECONDS=None, CAMUNDA_MTLS_CERT_PATH=None, CAMUNDA_MTLS_KEY_PATH=None, CAMUNDA_MTLS_CA_PATH=None, CAMUNDA_MTLS_CERT=None, CAMUNDA_MTLS_KEY=None, CAMUNDA_MTLS_CA=None, CAMUNDA_MTLS_KEY_PASSPHRASE=None) ``` Bases: `BaseModel` - **Parameters:** - **ZEEBE_REST_ADDRESS** (_str_) - **CAMUNDA_REST_ADDRESS** (_str_) - **CAMUNDA_TOKEN_AUDIENCE** (_str_) - **CAMUNDA_OAUTH_URL** (_str_) - **CAMUNDA_CLIENT_ID** (_str_ _|_ _None_) - **CAMUNDA_CLIENT_SECRET** (_str_ _|_ _None_) - **CAMUNDA_CLIENT_AUTH_CLIENTID** (_str_ _|_ _None_) - **CAMUNDA_CLIENT_AUTH_CLIENTSECRET** (_str_ _|_ _None_) - **CAMUNDA_AUTH_STRATEGY** (_Literal_ _[_ _'NONE'_ _,_ _'OAUTH'_ _,_ _'BASIC'_ _]_) - **CAMUNDA_BASIC_AUTH_USERNAME** (_str_ _|_ _None_) - **CAMUNDA_BASIC_AUTH_PASSWORD** (_str_ _|_ _None_) - **CAMUNDA_SDK_LOG_LEVEL** (_Literal_ _[_ _'silent'_ _,_ _'error'_ _,_ _'warn'_ _,_ _'info'_ _,_ _'debug'_ _,_ _'trace'_ _,_ _'silly'_ _]_) - **CAMUNDA_TOKEN_CACHE_DIR** (_str_ _|_ _None_) - **CAMUNDA_TOKEN_DISK_CACHE_DISABLE** (_bool_) - **CAMUNDA_SDK_BACKPRESSURE_PROFILE** (_Literal_ _[_ _'BALANCED'_ _,_ _'LEGACY'_ _]_) - **CAMUNDA_TENANT_ID** (_str_ _|_ _None_) - **CAMUNDA_WORKER_TIMEOUT** (_int_ _|_ _None_) - **CAMUNDA_WORKER_MAX_CONCURRENT_JOBS** (_int_ _|_ _None_) - **CAMUNDA_WORKER_REQUEST_TIMEOUT** (_int_ _|_ _None_) - **CAMUNDA_WORKER_NAME** (_str_ _|_ _None_) - **CAMUNDA_WORKER_STARTUP_JITTER_MAX_SECONDS** (_float_ _|_ _None_) - **CAMUNDA_MTLS_CERT_PATH** (_str_ _|_ _None_) - **CAMUNDA_MTLS_KEY_PATH** (_str_ _|_ _None_) - **CAMUNDA_MTLS_CA_PATH** (_str_ _|_ _None_) - **CAMUNDA_MTLS_CERT** (_str_ _|_ _None_) - **CAMUNDA_MTLS_KEY** (_str_ _|_ _None_) - **CAMUNDA_MTLS_CA** (_str_ _|_ _None_) - **CAMUNDA_MTLS_KEY_PASSPHRASE** (_str_ _|_ _None_) ### CAMUNDA_AUTH_STRATEGY ```python CAMUNDA_AUTH_STRATEGY: CamundaAuthStrategy ``` ### CAMUNDA_BASIC_AUTH_PASSWORD ```python CAMUNDA_BASIC_AUTH_PASSWORD: str | None ``` ### CAMUNDA_BASIC_AUTH_USERNAME ```python CAMUNDA_BASIC_AUTH_USERNAME: str | None ``` ### CAMUNDA_CLIENT_AUTH_CLIENTID ```python CAMUNDA_CLIENT_AUTH_CLIENTID: str | None ``` ### CAMUNDA_CLIENT_AUTH_CLIENTSECRET ```python CAMUNDA_CLIENT_AUTH_CLIENTSECRET: str | None ``` ### CAMUNDA_CLIENT_ID ```python CAMUNDA_CLIENT_ID: str | None ``` ### CAMUNDA_CLIENT_SECRET ```python CAMUNDA_CLIENT_SECRET: str | None ``` ### CAMUNDA_MTLS_CA ```python CAMUNDA_MTLS_CA: str | None ``` ### CAMUNDA_MTLS_CA_PATH ```python CAMUNDA_MTLS_CA_PATH: str | None ``` ### CAMUNDA_MTLS_CERT ```python CAMUNDA_MTLS_CERT: str | None ``` ### CAMUNDA_MTLS_CERT_PATH ```python CAMUNDA_MTLS_CERT_PATH: str | None ``` ### CAMUNDA_MTLS_KEY ```python CAMUNDA_MTLS_KEY: str | None ``` ### CAMUNDA_MTLS_KEY_PASSPHRASE ```python CAMUNDA_MTLS_KEY_PASSPHRASE: str | None ``` ### CAMUNDA_MTLS_KEY_PATH ```python CAMUNDA_MTLS_KEY_PATH: str | None ``` ### CAMUNDA_OAUTH_URL ```python CAMUNDA_OAUTH_URL: str ``` ### CAMUNDA_REST_ADDRESS ```python CAMUNDA_REST_ADDRESS: str ``` ### CAMUNDA_SDK_BACKPRESSURE_PROFILE ```python CAMUNDA_SDK_BACKPRESSURE_PROFILE: CamundaBackpressureProfile ``` ### CAMUNDA_SDK_LOG_LEVEL ```python CAMUNDA_SDK_LOG_LEVEL: CamundaSdkLogLevel ``` ### CAMUNDA_TENANT_ID ```python CAMUNDA_TENANT_ID: str | None ``` ### CAMUNDA_TOKEN_AUDIENCE ```python CAMUNDA_TOKEN_AUDIENCE: str ``` ### CAMUNDA_TOKEN_CACHE_DIR ```python CAMUNDA_TOKEN_CACHE_DIR: str | None ``` ### CAMUNDA_TOKEN_DISK_CACHE_DISABLE ```python CAMUNDA_TOKEN_DISK_CACHE_DISABLE: bool ``` ### CAMUNDA_WORKER_MAX_CONCURRENT_JOBS ```python CAMUNDA_WORKER_MAX_CONCURRENT_JOBS: int | None ``` ### CAMUNDA_WORKER_NAME ```python CAMUNDA_WORKER_NAME: str | None ``` ### CAMUNDA_WORKER_REQUEST_TIMEOUT ```python CAMUNDA_WORKER_REQUEST_TIMEOUT: int | None ``` ### CAMUNDA_WORKER_STARTUP_JITTER_MAX_SECONDS ```python CAMUNDA_WORKER_STARTUP_JITTER_MAX_SECONDS: float | None ``` ### CAMUNDA_WORKER_TIMEOUT ```python CAMUNDA_WORKER_TIMEOUT: int | None ``` ### ZEEBE_REST_ADDRESS ```python ZEEBE_REST_ADDRESS: str ``` ### model*config *= {'extra': 'forbid'}\_ Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict]. ## ConfigurationResolver ```python class ConfigurationResolver(environment, explicit_configuration=None) ``` Bases: `object` Resolves an effective configuration from environment + explicit overrides. - **Parameters:** - **environment** ([_CamundaSdkConfigPartial_](#camundasdkconfigpartial) _|_ _Mapping_ _[\*\*str_ _,_ _Any_ _]_) - **explicit_configuration** ([_CamundaSdkConfigPartial_](#camundasdkconfigpartial) _|_ _Mapping_ _[\*\*str_ _,_ _Any_ _]_ _|_ _None_) ### resolve() ```python def resolve() ``` - **Return type:** [_ResolvedCamundaSdkConfiguration_](#resolvedcamundasdkconfiguration) ## ResolvedCamundaSdkConfiguration ```python class ResolvedCamundaSdkConfiguration(effective: 'CamundaSdkConfiguration', environment: 'CamundaSdkConfigPartial', explicit: 'CamundaSdkConfigPartial | None') ``` Bases: `object` - **Parameters:** - **effective** ([_CamundaSdkConfiguration_](#camundasdkconfiguration)) - **environment** ([_CamundaSdkConfigPartial_](#camundasdkconfigpartial)) - **explicit** ([_CamundaSdkConfigPartial_](#camundasdkconfigpartial) _|_ _None_) ### effective ```python effective: [CamundaSdkConfiguration](#camundasdkconfiguration) ``` ### environment ```python environment: [CamundaSdkConfigPartial](#camundasdkconfigpartial) ``` ### explicit ```python explicit: [CamundaSdkConfigPartial](#camundasdkconfigpartial) | None ``` ### read_environment() ```python def read_environment(environ=None) ``` - **Parameters:** **environ** (_Mapping_ _[\*\*str_ _,_ _str_ _]_ _|_ _None_) - **Return type:** [_CamundaSdkConfigPartial_](#camundasdkconfigpartial) --- ## Semantic Types ## AgentInstanceKey ```python class AgentInstanceKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [AgentInstanceKey](#agentinstancekey) ## AuditLogEntityKey ```python class AuditLogEntityKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [AuditLogEntityKey](#auditlogentitykey) ## AuditLogKey ```python class AuditLogKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [AuditLogKey](#auditlogkey) ## AuthorizationKey ```python class AuthorizationKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [AuthorizationKey](#authorizationkey) ## BatchOperationKey ```python class BatchOperationKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [BatchOperationKey](#batchoperationkey) ## BusinessId ```python class BusinessId(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [BusinessId](#businessid) ## ClientId ```python class ClientId(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [ClientId](#clientid) ## ClusterVariableName ```python class ClusterVariableName(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [ClusterVariableName](#clustervariablename) ## ConditionalEvaluationKey ```python class ConditionalEvaluationKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [ConditionalEvaluationKey](#conditionalevaluationkey) ## DecisionDefinitionId ```python class DecisionDefinitionId(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [DecisionDefinitionId](#decisiondefinitionid) ## DecisionDefinitionKey ```python class DecisionDefinitionKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [DecisionDefinitionKey](#decisiondefinitionkey) ## DecisionEvaluationInstanceKey ```python class DecisionEvaluationInstanceKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [DecisionEvaluationInstanceKey](#decisionevaluationinstancekey) ## DecisionEvaluationKey ```python class DecisionEvaluationKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [DecisionEvaluationKey](#decisionevaluationkey) ## DecisionInstanceKey ```python class DecisionInstanceKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [DecisionInstanceKey](#decisioninstancekey) ## DecisionRequirementsKey ```python class DecisionRequirementsKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [DecisionRequirementsKey](#decisionrequirementskey) ## DeploymentKey ```python class DeploymentKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [DeploymentKey](#deploymentkey) ## DocumentId ```python class DocumentId(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [DocumentId](#documentid) ## ElementId ```python class ElementId(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [ElementId](#elementid) ## ElementInstanceKey ```python class ElementInstanceKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [ElementInstanceKey](#elementinstancekey) ## EndCursor ```python class EndCursor(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [EndCursor](#endcursor) ## FormId ```python class FormId(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [FormId](#formid) ## FormKey ```python class FormKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [FormKey](#formkey) ## GlobalListenerId ```python class GlobalListenerId(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [GlobalListenerId](#globallistenerid) ## GroupId ```python class GroupId(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [GroupId](#groupid) ## IncidentKey ```python class IncidentKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [IncidentKey](#incidentkey) ## JobKey ```python class JobKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [JobKey](#jobkey) ## MappingRuleId ```python class MappingRuleId(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [MappingRuleId](#mappingruleid) ## MessageKey ```python class MessageKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [MessageKey](#messagekey) ## MessageSubscriptionKey ```python class MessageSubscriptionKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [MessageSubscriptionKey](#messagesubscriptionkey) ## ProcessDefinitionId ```python class ProcessDefinitionId(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [ProcessDefinitionId](#processdefinitionid) ## ProcessDefinitionKey ```python class ProcessDefinitionKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [ProcessDefinitionKey](#processdefinitionkey) ## ProcessInstanceKey ```python class ProcessInstanceKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [ProcessInstanceKey](#processinstancekey) ## RoleId ```python class RoleId(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [RoleId](#roleid) ## SignalKey ```python class SignalKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [SignalKey](#signalkey) ## StartCursor ```python class StartCursor(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [StartCursor](#startcursor) ## Tag ```python class Tag(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [Tag](#tag) ## TenantId ```python class TenantId(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [TenantId](#tenantid) ## UserTaskKey ```python class UserTaskKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [UserTaskKey](#usertaskkey) ## Username ```python class Username(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [Username](#username) ## VariableKey ```python class VariableKey(value) ``` Bases: `str` - **Parameters:** **value** (_str_) - **Return type:** [VariableKey](#variablekey) ### lift_resource_key() ```python def lift_resource_key(value) ``` - **Parameters:** **value** (_Any_) - **Return type:** [_ProcessDefinitionKey_](#processdefinitionkey) | [_DecisionRequirementsKey_](#decisionrequirementskey) | [_FormKey_](#formkey) | [_DecisionDefinitionKey_](#decisiondefinitionkey) ### lift_scope_key() ```python def lift_scope_key(value) ``` - **Parameters:** **value** (_Any_) - **Return type:** [_ProcessInstanceKey_](#processinstancekey) | [_ElementInstanceKey_](#elementinstancekey) ### try_lift_resource_key() ```python def try_lift_resource_key(value) ``` - **Parameters:** **value** (_Any_) - **Return type:** _Tuple_[bool, [_ProcessDefinitionKey_](#processdefinitionkey) | [_DecisionRequirementsKey_](#decisionrequirementskey) | [_FormKey_](#formkey) | [_DecisionDefinitionKey_](#decisiondefinitionkey) | Exception] ### try_lift_scope_key() ```python def try_lift_scope_key(value) ``` - **Parameters:** **value** (_Any_) - **Return type:** _Tuple_[bool, [_ProcessInstanceKey_](#processinstancekey) | [_ElementInstanceKey_](#elementinstancekey) | Exception] --- ## Authentication(Python-sdk) The SDK supports three authentication strategies, controlled by `CAMUNDA_AUTH_STRATEGY`: | Strategy | When to use | | -------- | --------------------------------------------------------- | | `NONE` | Local development with unauthenticated Camunda (default) | | `OAUTH` | Camunda SaaS or any OAuth 2.0 Client Credentials endpoint | | `BASIC` | Self-Managed Camunda with Basic auth (username/password) | ## Auto-detection If you omit `CAMUNDA_AUTH_STRATEGY`, the SDK infers it from the credentials you provide: - Only `CAMUNDA_CLIENT_ID` + `CAMUNDA_CLIENT_SECRET` → **OAUTH** - Only `CAMUNDA_BASIC_AUTH_USERNAME` + `CAMUNDA_BASIC_AUTH_PASSWORD` → **BASIC** - No credentials → **NONE** - Both OAuth and Basic credentials present → **error** (set `CAMUNDA_AUTH_STRATEGY` explicitly) ## OAuth 2.0 ```bash CAMUNDA_REST_ADDRESS=https://cluster.example/v2 CAMUNDA_AUTH_STRATEGY=OAUTH CAMUNDA_CLIENT_ID=your-client-id CAMUNDA_CLIENT_SECRET=your-client-secret # Optional: # CAMUNDA_OAUTH_URL=https://login.cloud.camunda.io/oauth/token # CAMUNDA_TOKEN_AUDIENCE=zeebe.camunda.io ``` ## Basic authentication ```bash CAMUNDA_REST_ADDRESS=http://localhost:8080/v2 CAMUNDA_AUTH_STRATEGY=BASIC CAMUNDA_BASIC_AUTH_USERNAME=your-username CAMUNDA_BASIC_AUTH_PASSWORD=your-password ``` Or programmatically: ```python from camunda_orchestration_sdk import CamundaClient client = CamundaClient( configuration={ "CAMUNDA_REST_ADDRESS": "http://localhost:8080/v2", "CAMUNDA_AUTH_STRATEGY": "BASIC", "CAMUNDA_BASIC_AUTH_USERNAME": "your-username", "CAMUNDA_BASIC_AUTH_PASSWORD": "your-password", } ) ``` --- ## Backpressure The SDK includes built-in adaptive backpressure management that protects the Camunda cluster from overload. When the cluster returns backpressure signals (HTTP 429, 503, or `RESOURCE_EXHAUSTED`), the SDK automatically reduces outbound concurrency. When conditions improve, it gradually recovers — returning to full throughput with no manual intervention. This is enabled by default with the `BALANCED` profile and requires no configuration. Operations that drain work from the cluster (completing jobs, failing jobs) are never throttled. | Profile | Behavior | | -------------------- | ----------------------------------------------------------------------------------------------- | | `BALANCED` (default) | Adaptive concurrency gating with AIMD-style permit management and exponential backoff at floor. | | `LEGACY` | Observe-only — records severity but never gates or queues requests. | Set the profile via the `CAMUNDA_SDK_BACKPRESSURE_PROFILE` environment variable. --- ## Configuration reference(Python-sdk) All `CAMUNDA_*` environment variables recognised by the SDK. These can also be passed as keys in the `configuration={...}` dict. | Variable | Default | Description | | ------------------------------------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------- | | `ZEEBE_REST_ADDRESS` | `http://localhost:8080/v2` | REST API base URL (alias for CAMUNDA_REST_ADDRESS). | | `CAMUNDA_REST_ADDRESS` | `http://localhost:8080/v2` | REST API base URL. `/v2` is appended automatically if missing. | | `CAMUNDA_TOKEN_AUDIENCE` | `zeebe.camunda.io` | OAuth token audience. | | `CAMUNDA_OAUTH_URL` | `https://login.cloud.camunda.io/oauth/token` | OAuth token endpoint URL. | | `CAMUNDA_CLIENT_ID` | — | OAuth client ID. | | `CAMUNDA_CLIENT_SECRET` | — | OAuth client secret. | | `CAMUNDA_CLIENT_AUTH_CLIENTID` | — | Alias for CAMUNDA_CLIENT_ID. | | `CAMUNDA_CLIENT_AUTH_CLIENTSECRET` | — | Alias for CAMUNDA_CLIENT_SECRET. | | `CAMUNDA_AUTH_STRATEGY` | `NONE` | Authentication strategy: NONE, OAUTH, or BASIC. Auto-inferred from credentials if omitted. | | `CAMUNDA_BASIC_AUTH_USERNAME` | — | Basic auth username. Required when CAMUNDA_AUTH_STRATEGY=BASIC. | | `CAMUNDA_BASIC_AUTH_PASSWORD` | — | Basic auth password. Required when CAMUNDA_AUTH_STRATEGY=BASIC. | | `CAMUNDA_SDK_LOG_LEVEL` | `error` | SDK log level: silent, error, warn, info, debug, trace, or silly. | | `CAMUNDA_TOKEN_CACHE_DIR` | — | Directory for OAuth token disk cache. Disabled if unset. | | `CAMUNDA_TOKEN_DISK_CACHE_DISABLE` | `false` | Disable OAuth token disk caching. | | `CAMUNDA_SDK_BACKPRESSURE_PROFILE` | `BALANCED` | Backpressure profile: BALANCED (adaptive gating, default) or LEGACY (observe-only, no gating). | | `CAMUNDA_TENANT_ID` | — | Default tenant ID applied to all operations that accept a tenant_id parameter. | | `CAMUNDA_WORKER_TIMEOUT` | — | Default job timeout in milliseconds for all workers. | | `CAMUNDA_WORKER_MAX_CONCURRENT_JOBS` | — | Default maximum concurrent jobs per worker. | | `CAMUNDA_WORKER_REQUEST_TIMEOUT` | — | Default long-poll request timeout in milliseconds for all workers. | | `CAMUNDA_WORKER_NAME` | — | Default worker name for all workers. | | `CAMUNDA_WORKER_STARTUP_JITTER_MAX_SECONDS` | — | Default maximum startup jitter in seconds for all workers. | | `CAMUNDA_MTLS_CERT_PATH` | — | Path to client certificate (PEM) for mTLS. | | `CAMUNDA_MTLS_KEY_PATH` | — | Path to client private key (PEM) for mTLS. | | `CAMUNDA_MTLS_CA_PATH` | — | Path to CA certificate bundle (PEM) for mTLS. Optional. | | `CAMUNDA_MTLS_CERT` | — | Inline PEM client certificate. Overrides CAMUNDA_MTLS_CERT_PATH. | | `CAMUNDA_MTLS_KEY` | — | Inline PEM client private key. Overrides CAMUNDA_MTLS_KEY_PATH. | | `CAMUNDA_MTLS_CA` | — | Inline PEM CA bundle. Overrides CAMUNDA_MTLS_CA_PATH. | | `CAMUNDA_MTLS_KEY_PASSPHRASE` | — | Passphrase for encrypted private key. | | `CAMUNDA_LOAD_ENVFILE` | — | Load configuration from a `.env` file. Set to `true` (or a file path). | --- ## Creating a Process Instance(Python-sdk) The recommended pattern is to obtain keys from a prior API response (e.g. a deployment) and pass them directly — no manual lifting needed: ```python from camunda_orchestration_sdk import CamundaClient, ProcessCreationByKey with CamundaClient() as client: # Deploy and capture the typed key deployment = client.deploy_resources_from_files(["process.bpmn"]) process_key = deployment.processes[0].process_definition_key # Use it directly — the type flows through without conversion result = client.create_process_instance( data=ProcessCreationByKey(process_definition_key=process_key) ) print(f"Process instance key: {result.process_instance_key}") ``` If you need to restore a key from external storage (database, message queue, config file), use the semantic type constructor. Validation runs automatically: ```python from camunda_orchestration_sdk import CamundaClient, ProcessCreationByKey, ProcessDefinitionKey with CamundaClient() as client: stored_key = "2251799813685249" # from a DB row or config result = client.create_process_instance( data=ProcessCreationByKey(process_definition_key=ProcessDefinitionKey(stored_key)) ) print(f"Process instance key: {result.process_instance_key}") ``` **Migrating from pre-release versions:** Early pre-release builds exported `lift_*` helper functions (e.g., `lift_process_definition_key`). These have been removed — use the type constructor directly instead: `ProcessDefinitionKey(value)`. The constructor performs the same validation and is the single API surface for semantic types. --- ## Deploying Resources(Python-sdk) Deploy BPMN, DMN, or Form files from disk: ```python from camunda_orchestration_sdk import CamundaClient with CamundaClient() as client: result = client.deploy_resources_from_files(["process.bpmn", "decision.dmn"]) print(f"Deployment key: {result.deployment_key}") for process in result.processes: print(f" Process: {process.process_definition_id} (key: {process.process_definition_key})") ``` --- ## Error Handling The SDK raises typed exceptions for API errors. Each HTTP error status code has a corresponding exception class (e.g. `BadRequestError` for 400, `NotFoundError` for 404). Every exception carries the `operation_id` of the method that raised it: ```python from camunda_orchestration_sdk import CamundaClient, ProcessCreationByKey, ProcessDefinitionKey from camunda_orchestration_sdk.errors import BadRequestError process_definition_key = ProcessDefinitionKey("2251799813685249") with CamundaClient() as client: try: result = client.create_process_instance( data=ProcessCreationByKey(process_definition_key=process_definition_key) ) except BadRequestError as e: print(f"Bad request ({e.operation_id}): {e}") ``` --- ## Eventual Consistency Some Camunda endpoints — particularly search and "get by key" operations — are eventually consistent: data written via one API call may not be immediately visible to a follow-up read. Endpoints flagged as eventually consistent in the OpenAPI spec accept an optional `consistency` parameter that transparently polls until the data is visible (or a timeout is reached). The `consistency` parameter is fully optional and defaults to `None`, so existing call sites continue to work unchanged. ```python from camunda_orchestration_sdk import CamundaClient from camunda_orchestration_sdk.models import ( ProcessInstanceSearchQuery, ProcessInstanceSearchQueryFilter, ) from camunda_orchestration_sdk.runtime.eventual import ( ConsistencyOptions, EventualConsistencyTimeoutError, ) with CamundaClient() as client: try: result = client.search_process_instances( data=ProcessInstanceSearchQuery( filter_=ProcessInstanceSearchQueryFilter( process_definition_id="order-process", ), ), # Opt in to transparent polling. Default predicate accepts the # first response whose `items` list is non-empty. consistency=ConsistencyOptions( wait_up_to_ms=5000, poll_interval_ms=200, ), ) for instance in result.items: print(instance.process_instance_key) except EventualConsistencyTimeoutError as exc: print(f"Timed out after {exc.elapsed_ms}ms ({exc.attempts} attempts)") ``` For non-GET endpoints (search/list) the default predicate succeeds on the first response whose `items` list is non-empty. For GET endpoints it succeeds on any non-`None` result and transparently retries `404 Not Found` while waiting. Pass a custom `predicate` to wait for a more specific condition: ```python from camunda_orchestration_sdk import CamundaClient from camunda_orchestration_sdk.models import ( ProcessInstanceSearchQuery, ProcessInstanceSearchQueryFilter, ) from camunda_orchestration_sdk.runtime.eventual import ConsistencyOptions with CamundaClient() as client: # Wait until at least 3 instances are visible. result = client.search_process_instances( data=ProcessInstanceSearchQuery( filter_=ProcessInstanceSearchQueryFilter( process_definition_id="order-process", ), ), consistency=ConsistencyOptions( wait_up_to_ms=10_000, poll_interval_ms=250, predicate=lambda r: len(r.items) >= 3, ), ) print(f"Got {len(result.items)} instances") ``` `ConsistencyOptions` fields: | Field | Type | Default | Description | | ------------------ | ----------------------------- | ---------- | -------------------------------------------------- | | `wait_up_to_ms` | `int` | _required_ | Maximum time to wait. `0` skips polling entirely. | | `poll_interval_ms` | `int` | `500` | Delay between polling attempts (minimum 10ms). | | `predicate` | `Callable[[T], bool] \| None` | `None` | Custom success check. Defaults as described above. | Polling aborts immediately on `400`, `401`, `403`, `409`, `422`, and `5xx` responses. `429` responses are retried with backoff. On timeout the SDK raises `EventualConsistencyTimeoutError`, which exposes `attempts`, `elapsed_ms`, `last_status`, and `operation_id`. The same parameter is available on `CamundaAsyncClient` and behaves identically using `asyncio.sleep` for the polling delay. --- ## Installing the SDK to your project ## Requirements - Python 3.10 or later ## Stable release (recommended for production) The stable version tracks the latest supported Camunda server release. The first stable release will be **8.9.0**. ```bash pip install camunda-orchestration-sdk ``` ## Pre-release / dev channel Pre-release versions (e.g. `8.9.0.dev2`) are published from the `main` branch and contain the latest changes targeting the next server minor version. Use these to preview upcoming features or validate your integration ahead of a stable release. ```bash # pip pip install --pre camunda-orchestration-sdk # pin to a specific pre-release pip install camunda-orchestration-sdk==8.9.0.dev2 ``` In a `requirements.txt`: ```text camunda-orchestration-sdk>=8.9.0.dev1 ``` > **Note:** Pre-release versions may contain breaking changes between builds. Pin to a specific version if you need reproducible builds. ## Versioning This SDK has a different release cadence from the Camunda server. Features and fixes land in the SDK during a server release. The major version of the SDK signals a 1:1 type coherence with the server API for a Camunda minor release. SDK version `n.y.z` -> server version `8.n`, so the type surface of SDK version 9.y.z matches the API surface of Camunda 8.9. Using a later SDK version, for example: SDK version 10.y.z with Camunda 8.9, means that the SDK contains additive surfaces that are not guaranteed at runtime, and the compiler cannot warn of unsupported operations. Using an earlier SDK version, for example: SDK version 9.y.z with Camunda 8.10, results in slightly degraded compiler reasoning: exhaustiveness checks cannot be guaranteed by the compiler for any extended surfaces (principally, enums with added members). In the vast majority of use-cases, this will not be an issue; but you should be aware that using the matching SDK major version for the server minor version provides the strongest compiler guarantees about runtime reliability. **Recommended approach**: - Check the [CHANGELOG](https://github.com/camunda/orchestration-cluster-api-python/releases). - As a sanity check during server version upgrade, rebuild applications with the matching SDK major version to identify any affected runtime surfaces. --- ## Job Workers(Python-sdk) Job workers long-poll for available jobs, execute a callback, and automatically complete or fail the job based on the return value. Workers are available on `CamundaAsyncClient`. Handlers receive a context object that includes a `client` reference, so your handler can make API calls during job execution. The context type depends on the execution strategy: - **Async handlers** → `ConnectedJobContext` with `client: CamundaAsyncClient` (use `await`) - **Thread handlers** → `SyncJobContext` with `client: CamundaClient` (call directly) - **Process handlers** → plain `JobContext` (no client — cannot be pickled across process boundaries) ```python from camunda_orchestration_sdk import CamundaAsyncClient, ConnectedJobContext, WorkerConfig async def handle_job(job_context: ConnectedJobContext) -> dict[str, object]: variables = job_context.variables.to_dict() job_context.log.info(f"Processing job {job_context.job_key}: {variables}") return {"result": "processed"} async def main() -> None: async with CamundaAsyncClient() as client: config = WorkerConfig( job_type="my-service-task", job_timeout_milliseconds=30_000, ) client.create_job_worker(config=config, callback=handle_job) # Keep workers running until cancelled await client.run_workers() asyncio.run(main()) ``` ## Using the Client in a Job Handler Because `ConnectedJobContext` and `SyncJobContext` include a `client` reference, your handler can make API calls during job execution — for example, publishing a message to trigger another part of the process. **Async handlers** (`execution_strategy="async"`) — `await` the client method directly: ```python from camunda_orchestration_sdk import ConnectedJobContext, MessagePublicationRequest, MessagePublicationRequestVariables async def handle_order(job: ConnectedJobContext) -> dict[str, object]: variables = job.variables.to_dict() order_id = variables["orderId"] await job.client.publish_message( data=MessagePublicationRequest( name="order-processed", correlation_key=order_id, time_to_live=60000, variables=MessagePublicationRequestVariables.from_dict({"orderId": order_id, "status": "completed"}), ) ) job.log.info(f"Published order-processed message for order {order_id}") return {"status": "done"} ``` **Sync (thread) handlers** (`execution_strategy="thread"`) — `job.client` is a sync `CamundaClient`, so call methods directly: ```python from camunda_orchestration_sdk import MessagePublicationRequest, MessagePublicationRequestVariables, SyncJobContext def handle_order(job: SyncJobContext) -> dict[str, object]: variables = job.variables.to_dict() order_id = variables["orderId"] job.client.publish_message( data=MessagePublicationRequest( name="order-processed", correlation_key=order_id, time_to_live=60000, variables=MessagePublicationRequestVariables.from_dict({"orderId": order_id, "status": "completed"}), ) ) job.log.info(f"Published order-processed message for order {order_id}") return {"status": "done"} ``` > **Note:** The SDK automatically provides the right client type for each strategy — async handlers get `CamundaAsyncClient` (use `await`), thread handlers get `CamundaClient` (call directly). You don't need to create or manage these clients yourself. ## Job Logger Each `JobContext` exposes a `log` property — a scoped logger automatically bound with the job's context (job type, worker name, and job key). Use it inside your handler for structured, per-job log output: ```python async def handler(job: ConnectedJobContext) -> dict[str, object]: job.log.info(f"Starting work on {job.job_key}") # ... do work ... job.log.debug("Work completed successfully") return {"done": True} ``` The job logger inherits the SDK's logger configuration (loguru by default, or whatever you passed via `logger=`). If you injected a custom logger into the client, job handlers will use a child of that same logger. > **Note:** When using the `"process"` execution strategy, the job logger silently degrades to a no-op (`NullLogger`) because loggers cannot be pickled across process boundaries. The worker's main-process logger still records all job lifecycle events (activation, completion, failure, errors). If you need per-job logging from a process-isolated handler, configure a logger inside the handler itself. ## Execution Strategies Job workers support multiple execution strategies to match your workload type. Pass `execution_strategy` as a keyword argument to `create_job_worker`, or let the SDK auto-detect. | Strategy | How it runs your handler | Context type | Best for | | ------------------ | ------------------------------------------------------------------------------ | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | | `"auto"` (default) | Auto-detects: `"async"` for `async def` handlers, `"thread"` for sync handlers | `ConnectedJobContext` or `SyncJobContext` | Most use cases — sensible defaults without configuration | | `"async"` | Runs on the main `asyncio` event loop | `ConnectedJobContext` (async client) | I/O-bound async work (HTTP calls, database queries). Best throughput for handlers that call remote systems over HTTP | | `"thread"` | Runs in a `ThreadPoolExecutor` | `SyncJobContext` (sync client) | CPU-bound work, blocking I/O (file system, synchronous HTTP libraries) | | `"process"` | Runs in a `ProcessPoolExecutor` | `JobContext` (no client) | Heavy CPU-bound work that needs to escape the GIL (image processing, ML inference) | > **Choosing between `"async"` and `"thread"`:** If your job handler makes HTTP calls to remote systems (APIs, databases, microservices), `"async"` delivers the best performance — it can multiplex many concurrent jobs on a single thread without blocking. Use `"thread"` when your handler performs CPU-bound computation or calls synchronous libraries that would block the event loop. **Auto-detection logic:** If your handler is an `async def`, the strategy defaults to `"async"`. If it's a regular `def`, the strategy defaults to `"thread"`. You can override this explicitly: ```python from camunda_orchestration_sdk import SyncJobContext, JobContext # Force thread pool for a sync handler (receives SyncJobContext) def io_handler(job: SyncJobContext) -> dict[str, object]: return {"done": True} client.create_job_worker( config=WorkerConfig(job_type="io-bound-task", job_timeout_milliseconds=30_000), callback=io_handler, execution_strategy="thread", ) # Force process pool for CPU-heavy work (receives plain JobContext) def cpu_handler(job: JobContext) -> dict[str, object]: return {"computed": True} client.create_job_worker( config=WorkerConfig(job_type="image-processing", job_timeout_milliseconds=120_000), callback=cpu_handler, execution_strategy="process", ) ``` **Process strategy caveats:** The `"process"` strategy serialises (pickles) your handler and its context to send them to a worker process. Because the SDK client cannot be pickled, handlers running under this strategy receive a plain `JobContext` (without a `client` attribute) instead of `ConnectedJobContext`/`SyncJobContext`. This means: - Your handler function and its closure must be picklable (top-level functions work; lambdas and closures over unpicklable objects do not). - Your handler must accept `JobContext`, not `ConnectedJobContext` or `SyncJobContext` — the type checker enforces this via overloaded signatures on `create_job_worker`. - `job.log` degrades to a silent no-op logger in the child process (see [Job Logger](#job-logger)). - There is additional overhead per job from serialisation and inter-process communication. ## Worker Configuration `WorkerConfig` supports: | Parameter | Default | Description | | ------------------------------ | ----------------------------------- | ---------------------------------------------- | | `job_type` | _(required)_ | The BPMN service task type to poll for | | `job_timeout_milliseconds` | env / _(required)_ | How long the worker has to complete the job | | `request_timeout_milliseconds` | env / `0` | Long-poll request timeout (0 = server default) | | `max_concurrent_jobs` | env / `10` | Maximum jobs executing concurrently | | `fetch_variables` | `None` | List of variable names to fetch (None = all) | | `worker_name` | env / `"camunda-python-sdk-worker"` | Identifier for this worker in Camunda | The following are keyword-only arguments on `create_job_worker`, not part of `WorkerConfig`: | Parameter | Default | Description | | ---------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `execution_strategy` | `"auto"` | `"auto"`, `"async"`, `"thread"`, or `"process"`. Controls how the handler is invoked and which context type it receives. | | `startup_jitter_max_seconds` | env / `0` | Maximum random delay (in seconds) before the worker starts polling. When multiple application instances restart simultaneously, this spreads out initial activation requests to avoid saturating the server. A value of `0` (the default) means no delay. | ### Heritable Worker Defaults Worker configuration fields marked "env" in the table above can be set globally via environment variables or the client constructor. Individual `WorkerConfig` values take precedence. | Environment variable | Maps to | | ------------------------------------------- | ------------------------------ | | `CAMUNDA_WORKER_TIMEOUT` | `job_timeout_milliseconds` | | `CAMUNDA_WORKER_MAX_CONCURRENT_JOBS` | `max_concurrent_jobs` | | `CAMUNDA_WORKER_REQUEST_TIMEOUT` | `request_timeout_milliseconds` | | `CAMUNDA_WORKER_NAME` | `worker_name` | | `CAMUNDA_WORKER_STARTUP_JITTER_MAX_SECONDS` | `startup_jitter_max_seconds` | **Precedence:** explicit `WorkerConfig` value > environment variable / client constructor > hardcoded default. Example — set defaults via environment variables: ```bash export CAMUNDA_WORKER_TIMEOUT=30000 export CAMUNDA_WORKER_MAX_CONCURRENT_JOBS=32 ``` ```python # No need to set job_timeout_milliseconds on every worker — inherited from env client.create_job_worker( config=WorkerConfig(job_type="payment-service"), callback=handle_payment, ) client.create_job_worker( config=WorkerConfig(job_type="notification-service"), callback=handle_notification, ) ``` Example — set defaults via client constructor: ```python client = CamundaAsyncClient(configuration={ "CAMUNDA_WORKER_TIMEOUT": "30000", "CAMUNDA_WORKER_MAX_CONCURRENT_JOBS": "16", "CAMUNDA_WORKER_NAME": "my-app", }) # Both workers inherit timeout, concurrency, and name client.create_job_worker( config=WorkerConfig(job_type="payment-service"), callback=handle_payment, ) client.create_job_worker( config=WorkerConfig(job_type="shipping-service"), callback=handle_shipping, ) ``` ## Failing a Job To explicitly fail a job with a custom error message, retry count, and backoff, raise `JobFailure` in your handler: ```python from camunda_orchestration_sdk import ConnectedJobContext, JobFailure async def handle_job(job: ConnectedJobContext) -> dict[str, object]: if not job.variables.to_dict().get("required_field"): raise JobFailure( message="Missing required field", retries=2, retry_back_off=5000, # milliseconds ) return {"result": "ok"} ``` | Parameter | Default | Description | | ---------------- | ------------ | ----------------------------------------------------------------- | | `message` | _(required)_ | Error message attached to the failure | | `retries` | `None` | Remaining retries. `None` decrements the current retry count by 1 | | `retry_back_off` | `0` | Backoff before the next retry, in milliseconds | If an unhandled exception escapes your handler, the job is automatically failed with the exception message and the retry count decremented by 1. ## Throwing a BPMN Error To throw a [BPMN error](../../components/modeler/bpmn/error-events/error-events.md) from a job handler — for example, to trigger an error boundary event — raise `JobError`: ```python from camunda_orchestration_sdk import ConnectedJobContext, JobError async def handle_payment(job: ConnectedJobContext) -> dict[str, object]: variables = job.variables.to_dict() if variables.get("amount", 0) > 10_000: raise JobError(error_code="AMOUNT_TOO_HIGH", message="Payment exceeds limit") return {"status": "approved"} ``` | Parameter | Default | Description | | ------------ | ------------ | -------------------------------------------------------------- | | `error_code` | _(required)_ | The error code that is matched against BPMN error catch events | | `message` | `""` | An optional error message for logging/diagnostics | The `error_code` must match the error code defined on a BPMN error catch event in your process model. If no catch event matches, the job becomes an incident. ## Job Corrections (User Task Listeners) When a job worker handles a [user task listener](../../components/concepts/user-task-listeners.md), it can correct task properties (assignee, due date, candidate groups, etc.) as part of the completion. Return a `JobCompletionRequest` with a `result` containing `JobResultCorrections`: ```python from camunda_orchestration_sdk import ConnectedJobContext from camunda_orchestration_sdk.models import ( JobCompletionRequest, JobResultUserTask, JobResultCorrections, ) async def validate_task(job: ConnectedJobContext) -> JobCompletionRequest: return JobCompletionRequest( result=JobResultUserTask( type_="userTask", corrections=JobResultCorrections( assignee="corrected-user", priority=80, ), ), ) ``` To deny a task completion (reject the work), set `denied=True`: ```python async def review_task(job: ConnectedJobContext) -> JobCompletionRequest: return JobCompletionRequest( result=JobResultUserTask( type_="userTask", denied=True, denied_reason="Insufficient documentation", ), ) ``` | Correctable attribute | Type | Clear value | | --------------------- | ------------- | ----------------- | | `assignee` | `str` | Empty string `""` | | `due_date` | `datetime` | Empty string `""` | | `follow_up_date` | `datetime` | Empty string `""` | | `candidate_users` | `list[str]` | Empty list `[]` | | `candidate_groups` | `list[str]` | Empty list `[]` | | `priority` | `int` (0–100) | — | Omitting an attribute or passing `None` preserves the persisted value. This works with all handler types (async, thread, and process). --- ## Logging(Python-sdk) By default the SDK logs via [loguru](https://github.com/Delgan/loguru). You can inject any logger that exposes `debug`, `info`, `warning`, and `error` methods — including Python's built-in `logging.Logger`. ## Using the default logger (loguru) No configuration needed. Control verbosity with `CAMUNDA_SDK_LOG_LEVEL` or loguru's own `LOGURU_LEVEL` environment variable: ```bash CAMUNDA_SDK_LOG_LEVEL=debug python your_script.py ``` ## Injecting a custom logger Pass a `logger=` argument to `CamundaClient` or `CamundaAsyncClient`. The logger is forwarded to all internal components (auth providers, HTTP hooks, job workers). **stdlib `logging`:** ```python from camunda_orchestration_sdk import CamundaClient my_logger = logging.getLogger("my_app.camunda") my_logger.setLevel(logging.DEBUG) client = CamundaClient(logger=my_logger) ``` **Custom logger object:** ```python from camunda_orchestration_sdk import CamundaClient class MyLogger: def debug(self, msg: object, *args: object, **kwargs: object) -> None: print(f"[DEBUG] {msg}") def info(self, msg: object, *args: object, **kwargs: object) -> None: print(f"[INFO] {msg}") def warning(self, msg: object, *args: object, **kwargs: object) -> None: print(f"[WARN] {msg}") def error(self, msg: object, *args: object, **kwargs: object) -> None: print(f"[ERROR] {msg}") client = CamundaClient(logger=MyLogger()) ``` ## Disabling logging Pass an instance of `NullLogger` to silence all SDK output: ```python from camunda_orchestration_sdk import CamundaClient, NullLogger client = CamundaClient(logger=NullLogger()) ``` --- ## Migrating from v9 to v10 v10 tracks Camunda 8.10. The 8.10 OpenAPI spec promotes several identifier and name fields from plain strings to **semantic types**. The SDK enforces them at construction time, so any v9 code that passes a plain `str` to these methods will need to wrap the value with the corresponding brand. ## New branded types | Brand | Used for | | --------------------- | -------------------------- | | `RoleId` | Role identifiers | | `GroupId` | Group identifiers | | `ClientId` | OAuth client identifiers | | `MappingRuleId` | Mapping-rule identifiers | | `ClusterVariableName` | Cluster variable names | | `AgentInstanceKey` | Agent-instance system keys | ## Migration ```python from camunda_orchestration_sdk import CamundaClient, GroupId, RoleId with CamundaClient() as client: # v9 — plain strings were accepted: # client.assign_role_to_group(role_id="developer", group_id="engineering") # v10 — wrap with the branded type constructor at the boundary client.assign_role_to_group( role_id=RoleId("developer"), group_id=GroupId("engineering"), ) ``` The brand constructors are subclasses of `str`, so the wrapped values remain valid where a `str` is expected (f-strings, logging, JSON serialisation). The wrap exists to enforce the upstream pattern and length constraints once, at the boundary, so a malformed identifier fails fast with `ValueError` instead of producing an HTTP 400 from the cluster. ## Deprecated model class renames 26 model classes were renamed in v10 to match upstream conventions. The old names continue to work with a deprecation warning and will be removed in v11. No action is required to upgrade — but updating imports is recommended. | Old name (deprecated) | New name | | ---------------------------------------- | --------------------------------------------- | | `CreateMappingRuleResponse201` | `MappingRuleCreateResult` | | `GetUserResponse200` | `UserResult` | | `SearchClientsForGroupData` | `GroupClientSearchQueryRequest` | | `SearchClientsForGroupResponse200` | `GroupClientSearchResult` | | `SearchClientsForRoleData` | `RoleClientSearchQueryRequest` | | `SearchClientsForRoleResponse200` | `RoleClientSearchResult` | | `SearchClientsForTenantData` | `TenantClientSearchQueryRequest` | | `SearchClientsForTenantResponse200` | `TenantClientSearchResult` | | `SearchMappingRuleResponse200` | `MappingRuleSearchQueryResult` | | `SearchMappingRulesForGroupResponse200` | `GroupMappingRuleSearchResult` | | `SearchMappingRulesForRoleResponse200` | `RoleMappingRuleSearchResult` | | `SearchMappingRulesForTenantResponse200` | `TenantMappingRuleSearchResult` | | `SearchRolesForGroupResponse200` | `GroupRoleSearchResult` | | `SearchRolesForTenantResponse200` | `TenantRoleSearchResult` | | `SearchUserTaskEffectiveVariablesData` | `UserTaskEffectiveVariableSearchQueryRequest` | | `SearchUserTaskVariablesData` | `UserTaskVariableSearchQueryRequest` | | `SearchUsersForGroupData` | `GroupUserSearchQueryRequest` | | `SearchUsersForGroupResponse200` | `GroupUserSearchResult` | | `SearchUsersForRoleData` | `RoleUserSearchQueryRequest` | | `SearchUsersForRoleResponse200` | `RoleUserSearchResult` | | `SearchUsersForTenantData` | `TenantUserSearchQueryRequest` | | `SearchUsersForTenantResponse200` | `TenantUserSearchResult` | | `SearchUsersResponse200` | `UserSearchResult` | | `SearchVariablesData` | `VariableSearchQuery` | | `UpdateMappingRuleResponse200` | `MappingRuleUpdateResult` | | `UpdateUserResponse200` | `UserUpdateResult` | The following 3 request body classes were removed entirely (the upstream operations no longer take a request body). These cannot be aliased and are a hard break: - `CancelProcessInstanceData` - `DeleteDecisionInstanceData` - `DeleteProcessInstanceData` ## What does NOT change - The wire format is unchanged — all values are still strings on the wire. - No method signatures changed name or arity. - Branded values are assignable anywhere a `str` is expected, so existing string-handling code continues to work. - Existing valid v9 values continue to satisfy the new constraints (the patterns are permissive supersets of typical identifiers). See [`semantic_types.py`](https://github.com/camunda/orchestration-cluster-api-python/blob/main/generated/camunda_orchestration_sdk/semantic_types.py) for the canonical list of brands and their constraints. --- ## Programmatic configuration (use sparingly) Only use `configuration={...}` when you must supply or mutate configuration dynamically (e.g. tests, multi-tenant routing, or ephemeral preview environments). Keys mirror their `CAMUNDA_*` environment names. ```python from camunda_orchestration_sdk import CamundaClient client = CamundaClient( configuration={ "CAMUNDA_REST_ADDRESS": "http://localhost:8080/v2", "CAMUNDA_AUTH_STRATEGY": "NONE", } ) ``` --- ## Quick start (Zero-config – recommended) Keep configuration out of application code. Let the client read `CAMUNDA_*` variables from the environment (12-factor style). This makes secret rotation, environment promotion (dev → staging → prod), and operational tooling (vaults / secret managers) safer and simpler. If no configuration is present, the SDK defaults to a local Camunda 8 Run-style endpoint at `http://localhost:8080/v2`. ```python from camunda_orchestration_sdk import CamundaAsyncClient, CamundaClient # Zero-config construction: reads CAMUNDA_* from the environment client = CamundaClient() async_client = CamundaAsyncClient() ``` Typical `.env` (example): ```bash CAMUNDA_REST_ADDRESS=https://cluster.example/v2 CAMUNDA_AUTH_STRATEGY=OAUTH CAMUNDA_CLIENT_ID=*** CAMUNDA_CLIENT_SECRET=*** ``` ### Loading configuration from a `.env` file (`CAMUNDA_LOAD_ENVFILE`) The SDK can optionally load configuration values from a dotenv file. - Set `CAMUNDA_LOAD_ENVFILE=true` (or `1` / `yes`) to load `.env` from the current working directory. - Set `CAMUNDA_LOAD_ENVFILE=/path/to/file.env` to load from an explicit path. - If the file does not exist, it is silently ignored. - Precedence is: `.env` < environment variables < explicit `configuration={...}` passed to the client. - The resolver reads dotenv values without mutating `os.environ`. Example `.env`: ```bash CAMUNDA_REST_ADDRESS=http://localhost:8080/v2 CAMUNDA_CLIENT_ID=your-client-id CAMUNDA_CLIENT_SECRET=your-client-secret ``` Enable loading from the current directory: ```bash export CAMUNDA_LOAD_ENVFILE=true python your_script.py ``` Or enable loading from a specific file: ```bash export CAMUNDA_LOAD_ENVFILE=~/camunda/dev.env python your_script.py ``` You can also enable it via the explicit configuration dict: ```python from camunda_orchestration_sdk import CamundaClient client = CamundaClient(configuration={"CAMUNDA_LOAD_ENVFILE": "true"}) ``` --- ## Self-signed TLS / mTLS(Python-sdk) The SDK supports custom TLS certificates via environment variables. This is useful for: - **Self-signed server certificates** — trust a CA that signed your server's certificate, without presenting a client identity. - **Mutual TLS (mTLS)** — present a client certificate and key to prove the client's identity. - **Both** — trust a custom CA _and_ present client credentials. ## Trusting a self-signed server certificate Set only the CA certificate to trust the server's self-signed certificate: ```bash # Path to PEM file: CAMUNDA_MTLS_CA_PATH=/path/to/ca.pem # Or inline PEM: CAMUNDA_MTLS_CA="-----BEGIN CERTIFICATE-----\n..." ``` ## Mutual TLS (client certificate) To present a client certificate for mutual TLS, provide both the certificate and private key: ```bash CAMUNDA_MTLS_CERT_PATH=/path/to/client.crt CAMUNDA_MTLS_KEY_PATH=/path/to/client.key # Optional — passphrase if the key is encrypted: # CAMUNDA_MTLS_KEY_PASSPHRASE=secret ``` ## Full mTLS with custom CA Combine a custom CA with client credentials: ```bash CAMUNDA_MTLS_CA_PATH=/path/to/ca.pem CAMUNDA_MTLS_CERT_PATH=/path/to/client.crt CAMUNDA_MTLS_KEY_PATH=/path/to/client.key ``` Inline PEM values (`CAMUNDA_MTLS_CERT`, `CAMUNDA_MTLS_KEY`, `CAMUNDA_MTLS_CA`) take precedence over their `_PATH` counterparts. No code changes are needed — the SDK picks up TLS configuration from environment variables automatically: ```python from camunda_orchestration_sdk import CamundaClient client = CamundaClient() # TLS configured from env vars ``` --- ## Semantic Types(Python-sdk) The SDK uses distinct types for identifiers like `ProcessDefinitionKey`, `ProcessInstanceKey`, `JobKey`, `TenantId`, etc., defined in `camunda_orchestration_sdk.semantic_types` and re-exported from the top-level package. These types inherit from `str`, so they serialize transparently to/from JSON and are compatible with any code expecting a string. ## Why they exist Camunda's API has many operations that accept string keys — process definition keys, process instance keys, incident keys, job keys, and so on. Without semantic types, it is easy to accidentally pass a process instance key where a process definition key is expected, or mix up a job key with an incident key. The type checker cannot help you if everything is `str`. Semantic types make these identifiers **distinct at the type level**. Pyright (and other type checkers) will flag an error if you pass a `ProcessInstanceKey` where a `ProcessDefinitionKey` is expected, catching bugs before runtime. ## How to use them Treat semantic types as **opaque identifiers** — receive them from API responses and pass them to subsequent API calls without inspecting or transforming the underlying value: ```python from camunda_orchestration_sdk import CamundaClient, ProcessCreationByKey client = CamundaClient() # Deploy → the response already carries typed keys deployment = client.deploy_resources_from_files(["process.bpmn"]) process_key = deployment.processes[0].process_definition_key # ProcessDefinitionKey # Pass it directly to another call — no conversion needed result = client.create_process_instance( data=ProcessCreationByKey(process_definition_key=process_key) ) # The result also carries typed keys instance_key = result.process_instance_key # ProcessInstanceKey client.cancel_process_instance(process_instance_key=instance_key) ``` ## Serialising in and out of the type system Semantic types inherit from `str` and validate on construction, so they work transparently: ```python from camunda_orchestration_sdk import ProcessDefinitionKey, ProcessInstanceKey # --- Serialising out (to storage / JSON / message queue) --- # A semantic type IS a str, so it works directly with any str API: process_key: ProcessDefinitionKey = deployment.processes[0].process_definition_key db.save("process_key", process_key) # stores the raw string json.dumps({"key": process_key}) # "2251799813685249" # --- Deserialising in (from storage / external input) --- # Wrap the raw string with the type constructor (validates automatically): raw = db.load("process_key") # returns a plain str typed_key = ProcessDefinitionKey(raw) # validates and wraps the value result = client.create_process_instance( data=ProcessCreationByKey(process_definition_key=typed_key) ) ``` The available semantic types include: `ProcessDefinitionKey`, `ProcessDefinitionId`, `ProcessInstanceKey`, `JobKey`, `IncidentKey`, `DecisionDefinitionKey`, `DecisionDefinitionId`, `DeploymentKey`, `UserTaskKey`, `MessageKey`, `SignalKey`, `TenantId`, `ElementId`, `FormKey`, and others. All are importable from `camunda_orchestration_sdk` or `camunda_orchestration_sdk.semantic_types`. --- ## Using the SDK The SDK provides two clients with identical API surfaces: - **`CamundaClient`** — synchronous. Every method blocks until the response arrives. Use this in scripts, CLI tools, Django views, Flask handlers, or anywhere you don't have an async event loop. - **`CamundaAsyncClient`** — asynchronous (`async`/`await`). Every method is a coroutine. Use this in FastAPI, aiohttp, or any `asyncio`-based application. **Job workers require `CamundaAsyncClient`** because they use `asyncio` for long-polling and concurrent job execution. Both clients share the same method names and parameters — the only difference is calling convention: ```python # Sync from camunda_orchestration_sdk import CamundaClient with CamundaClient() as client: topology = client.get_topology() ``` ```python # Async from camunda_orchestration_sdk import CamundaAsyncClient async def main() -> None: async with CamundaAsyncClient() as client: topology = await client.get_topology() asyncio.run(main()) ``` > **Which one should I use?** If your application already uses `asyncio` (FastAPI, aiohttp, etc.) or you need job workers, use `CamundaAsyncClient`. Otherwise, `CamundaClient` is simpler and works everywhere. --- ## Python SDK # Camunda Orchestration Cluster API – Python SDK A fully typed Python client for the [Camunda 8 Orchestration Cluster REST API](../apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). Fully compliant with the Camunda OpenAPI spec with hand-written runtime infrastructure for authentication, configuration, and job workers. - **Sync and async** — `CamundaClient` (synchronous) and `CamundaAsyncClient` (async/await) - **Strict typing** — pyright-strict compatible with PEP 561 `py.typed` marker - **Zero-config** — reads `CAMUNDA_*` environment variables (12-factor style) - **Job workers** — long-poll workers with thread, process, or async execution strategies - **OAuth & Basic auth** — pluggable authentication with automatic token management - **Pluggable logging** — inject your own logger (stdlib `logging`, loguru, or custom) --- ## Migrate from the removed Tasklist API :::warning The Tasklist API was removed in Camunda 8.10 and is no longer part of the current documentation set. ::: For the release-level summary of this removal, see the [8.10 release announcement](/reference/announcements-release-notes/8100/8100-announcements.md#removal-of-legacy-apis-tasklist-v1-dependent-features-and-zeebe-process-test). Use the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) to build current task applications, and review [migrating to the Orchestration Cluster REST API](/apis-tools/migration-manuals/migrate-to-camunda-api.md) if you still have clients that call the removed Tasklist API. For current Tasklist behavior, see [Tasklist API changes](/components/tasklist/api-versions.md) and [user task authorization](/components/tasklist/user-task-authorization.md). --- ## Assertions The class `CamundaAssert` is the entry point for all assertions. It is based on [AssertJ](https://github.com/assertj/assertj) and [Awaitility](http://www.awaitility.org/). The assertions follow the style: `assertThat(object_to_test)` + expected property. Use the assertions by adding the following static import in your test class: ```java ``` :::info Assertions are blocking Camunda executes BPMN processes asynchronously. For testing, this means that there might be a delay between creating a process instance and reaching the expected state. The assertions handle the asynchronous behavior and wait until the expected property is fulfilled. Only if the property is not fulfilled within the given time, the assertion fails. ::: :::tip CPT provides the most common assertions. However, if you miss an assertion you can implement a [custom assertion](#custom-assertions) yourself. ::: ## Configuration You can configure the behavior of the assertions in the following ways. ### Assertion timeout By default, assertions wait 10 seconds for the expected property to be fulfilled and wait 100 milliseconds between two attempts. You can change these defaults globally in the configuration or per assertion. Configure the assertions globally in your `application.yml` (or `application.properties`): ```yaml camunda: process-test: assertion: # Set the assertion timeout to 1 minute timeout: PT1M # Set the assertion interval to 100 milliseconds interval: PT0.1S ``` Configure the assertions globally in your `/camunda-container-runtime.properties` file: ```properties # Set the assertion timeout to 1 minute assertion.timeout=PT1M # Set the assertion interval to 100 milliseconds assertion.interval=PT0.1S ``` Alternatively, you can configure the assertions within your test class using `CamundaAssert`. ```java @BeforeAll static void configureAssertions() { // Set the assertion timeout to 1 minute CamundaAssert.setAssertionTimeout(Duration.ofMinutes(1)); // Set the assertion interval to 100 milliseconds CamundaAssert.setAssertionInterval(Duration.ofMillis(100)); } ``` You can override the global timeout for an assertion using `withAssertionTimeout()`. The given timeout applies only to subsequent assertions in the calling chain. ```java assertThat(processInstance) .withAssertionTimeout(Duration.ofMinutes(1)) .isCompleted(); ``` ### Element selector By default, the element instance assertions identify the BPMN elements by their ID. You can change the [ElementSelector](utilities.md#element-selector) globally in your test class using `CamundaAssert`. ```java @BeforeAll static void configureAssertions() { // Identify the BPMN elements by their name CamundaAssert.setElementSelector(ElementSelectors::byName); } ``` ### Judge configuration Override the global [judge configuration](configuration.md#judge-configuration) for a single assertion chain using `withJudgeConfig`. ```java assertThat(processInstance) .withJudgeConfig(config -> config.withThreshold(0.9)) .hasVariableSatisfiesJudge("result", "Contains a valid JSON response with status OK."); ``` ### Semantic similarity configuration Override the global [semantic similarity configuration](configuration.md#semantic-similarity-configuration) for a single assertion chain using `withSemanticSimilarityConfig`. ```java assertThat(processInstance) .withSemanticSimilarityConfig(config -> config.withThreshold(0.9)) .hasVariableSimilarTo("greeting", "Hello, how can I help you today?"); ``` ## Process instance assertions You can verify the process instance state and other properties using `CamundaAssert.assertThat()` or `CamundaAssert.assertThatProcessInstance()`. Use the process instance creation event or a [ProcessInstanceSelector](utilities.md#process-instance-selector) to identify the process instance. ### With process instance event Use the creation event of the create instance command to identify the process instance: ```java // given/when ProcessInstanceEvent processInstance = client .newCreateInstanceCommand() .bpmnProcessId("my-process") .latestVersion() .send() .join(); // then assertThat(processInstance).isActive(); ``` ### With process instance result Use the result event of the create instance command to identify the process instance: ```java // given/when ProcessInstanceResult processInstance = client .newCreateInstanceCommand() .bpmnProcessId("my-process") .latestVersion() .withResult() .send() .join(); // then assertThat(processInstance).isActive(); ``` ### With process instance selector Use a [ProcessInstanceSelector](utilities.md#process-instance-selector) to identify the process instance. ```java // by process instance key assertThatProcessInstance(ProcessInstanceSelectors.byKey(processInstanceKey)).isActive(); // by process ID assertThatProcessInstance(ProcessInstanceSelectors.byProcessId("my-process")).isActive(); ``` ### isActive Assert that the process instance is active. The assertion fails if the process instance is completed, terminated, or not created. ```java assertThat(processInstance).isActive(); ``` ### isCompleted Assert that the process instance is completed. The assertion fails if the process instance is active, terminated, or not created. ```java assertThat(processInstance).isCompleted(); ``` ### isTerminated Assert that the process instance is terminated. The assertion fails if the process instance is active, completed, or not created. ```java assertThat(processInstance).isTerminated(); ``` ### isCreated Assert that the process instance is created and either active, completed, or terminated. The assertion fails if the process instance is not created. ```java assertThat(processInstance).isCreated(); ``` ### hasActiveIncidents Assert that the process instance has at least one active incident. The assertion fails if there is no active incident. ```java assertThat(processInstance).hasActiveIncidents(); ``` ### hasNoActiveIncidents Assert that the process instance has no active incidents. The assertion fails if there is any active incident. ```java assertThat(processInstance).hasNoActiveIncidents(); ``` ## Element instance assertions You can verify the element instance states and other properties using `CamundaAssert.assertThat(processInstance)`. Use the BPMN element ID or a [ElementSelector](utilities.md#element-selector) to identify the elements. ### With BPMN element ID Use the BPMN element ID to identify the elements: ```java assertThat(processInstance).hasActiveElements("task_A"); ``` You can customize how the elements are identified in the [configuration](#element-selector). ### With element selector Use a [ElementSelector](utilities.md#element-selector) to identify the elements: ```java // by BPMN element ID assertThat(processInstance).hasActiveElements(ElementSelectors.byId("task_A")); // by BPMN element name assertThat(processInstance).hasActiveElements(ElementSelectors.byName("A")); ``` ### hasActiveElements Assert that the given BPMN elements of the process instance are active. The assertion fails if at least one element is completed, terminated, or not entered. ```java assertThat(processInstance).hasActiveElements("task_A", "task_B"); ``` ### hasActiveElement Assert that the BPMN element of the process instance is active the given amount of times. The assertion fails if the element is not active or not exactly the given amount of times. ```java assertThat(processInstance).hasActiveElement("task_A", 2); ``` ### hasActiveElementsExactly Assert that only the given BPMN elements are active. The assertion fails if at least one element is not active, or other elements are active. ```java assertThat(processInstance).hasActiveElementsExactly("task_A", "task_B"); ``` ### hasNoActiveElements Assert that the given BPMN elements are not active. The assertion fails if at least one element is active. ```java assertThat(processInstance).hasNoActiveElements("task_A", "task_B"); ``` ### hasNotActivatedElements Assert that the given BPMN elements are not activated (i.e. not entered). The assertion fails if at least one element is active, completed, or terminated. This assertion does not wait for the given activities. ```java assertThat(processInstance).hasNotActivatedElements("task_A", "task_B"); ``` ### hasCompletedElements Assert that the given BPMN elements of the process instance are completed. The assertion fails if at least one element is active, terminated, or not entered. ```java assertThat(processInstance).hasCompletedElements("task_A", "task_B"); ``` ### hasCompletedElement Assert that the BPMN element of the process instance is completed the given amount of times. The assertion fails if the element is not completed or not exactly the given amount of times. ```java assertThat(processInstance).hasCompletedElement("task_A", 2); ``` ### hasCompletedElementsInOrder Assert that the given BPMN elements are completed in order. Elements that do not match any of the given element IDs are ignored. The assertion fails if at least one of the elements is not completed, or the order is not correct. ```java assertThat(processInstance).hasCompletedElementsInOrder("task_A", "task_B"); ``` ### hasTerminatedElements Assert that the given BPMN elements of the process instance are terminated. The assertion fails if at least one element is active, completed, or not entered. ```java assertThat(processInstance).hasTerminatedElements("task_A", "task_B"); ``` ### hasTerminatedElement Assert that the BPMN element of the process instance is terminated the given amount of times. The assertion fails if the element is not terminated or not exactly the given amount of times. ```java assertThat(processInstance).hasTerminatedElement("task_A", 2); ``` ## Variable assertions You can verify the process variables using `CamundaAssert.assertThat(processInstance)`. Use the variable name or a [VariableSelector](utilities.md#variable-selector) to identify the variable. ### With variable name Use the variable name to identify the variable: ```java assertThat(processInstance).hasVariable("approved", true); ``` ### With variable selector Use a [VariableSelector](utilities.md#variable-selector) to identify the variable: ```java // by variable name assertThat(processInstance).hasVariable(VariableSelectors.byName("approved"), true); // by partial variable value assertThat(processInstance).hasVariableSatisfies( VariableSelectors.byValueContains("order-123"), Order.class, order -> { }); ``` ### hasVariableNames Assert that the process instance has the given variables. The assertion fails if at least one variable doesn't exist. ```java assertThat(processInstance).hasVariableNames("var1", "var2"); ``` ### hasVariable Assert that the process instance has the variable with the given value. The assertion fails if the variable doesn't exist or has a different value. ```java assertThat(processInstance).hasVariable("var1", 100); ``` ### hasVariables Assert that the process instance has the given variables. The assertion fails if at least one variable doesn't exist or has a different value. ```java Map expectedVariables = // assertThat(processInstance).hasVariables(expectedVariables); ``` ### hasVariableSatisfies Assert that the process instance has a variable with a value that satisfies the given requirements. The assertion transforms the value into the given type. In the consumer, you can use [AssertJ](https://github.com/assertj/assertj) to verify the value. The assertion fails if the variable doesn't exist, the value is of a different type, or the value doesn't satisfy the requirements. ```java assertThat(processInstance).hasVariableSatisfies("order", Order.class, order -> { Assertions.assertThat(order.status()).isEqualTo("approved"); Assertions.assertThat(order.items()) .hasSize(3) .extracting("name", "quantity") .containsExactlyInAnyOrder( tuple("Helmet", 1), tuple("Flag", 1), tuple("Oxygen tank", 3) ); }); ``` ### hasVariableSatisfiesJudge Assert that a process variable satisfies a natural language expectation using a configured LLM judge. The expectation is evaluated only once. The assertion fails if the LLM score is below the configured threshold (default: 0.5). It requires [judge configuration](configuration.md#judge-configuration). ```java assertThat(processInstance) .hasVariableSatisfiesJudge("result", "Contains a valid JSON response with status OK."); ``` ### hasVariableSimilarTo Assert that a process variable is semantically similar to an expected string using a configured embedding model. The variable value and the expected value are converted to embeddings, and the cosine similarity is compared against the configured threshold. The assertion fails if the variable doesn't exist or the similarity score is below the configured threshold (default: 0.5). It requires [semantic similarity configuration](configuration.md#semantic-similarity-configuration). ```java assertThat(processInstance) .hasVariableSimilarTo("greeting", "Hello, how can I help you today?"); ``` ### hasLocalVariableNames Assert that the process instance has the local variables in the scope of the given element. Use the BPMN element ID or a [ElementSelector](utilities.md#element-selector) to identify the element. The assertion fails if at least one variable doesn't exist. ```java assertThat(processInstance).hasLocalVariableNames(ElementSelectors.byId("task_A"), "var1", "var2"); ``` ### hasLocalVariable Assert that the process instance has the local variable with the value in the scope of the given element. Use the BPMN element ID or a [ElementSelector](utilities.md#element-selector) to identify the element. The assertion fails if the variable doesn't exist or has a different value. ```java assertThat(processInstance).hasLocalVariable(ElementSelectors.byId("task_A"), "var1", 100); ``` ### hasLocalVariables Assert that the process instance has the local variables in the scope of the given element. Use the BPMN element ID or a [ElementSelector](utilities.md#element-selector) to identify the element. The assertion fails if at least one variable doesn't exist or has a different value. ```java Map expectedVariables = // assertThat(processInstance).hasLocalVariables(ElementSelectors.byId("task_A"), expectedVariables); ``` ### hasLocalVariableSatisfies Assert that the process instance has a local variable in the scope of the given element with a value that satisfies the given requirements. Use the BPMN element ID or a [ElementSelector](utilities.md#element-selector) to identify the element. The assertion transforms the value into the given type. In the consumer, you can use [AssertJ](https://github.com/assertj/assertj) to verify the value. The assertion fails if the variable doesn't exist, the value is of a different type, or the value doesn't satisfy the requirements. ```java assertThat(processInstance).hasLocalVariableSatisfies( ElementSelectors.byId("send-email"), "to", EmailTo.class, emailTo -> { Assertions.assertThat(emailTo.name()).isEqualTo("Zee"); Assertions.assertThat(emailTo.email()).isEqualTo("zee@camunda.com"); }); ``` ### hasLocalVariableSatisfiesJudge Assert that a local variable in the scope of a given element satisfies a natural language expectation using a configured LLM judge. Use the BPMN element ID or an [element selector](utilities.md#element-selector) to identify the element. The expectation is evaluated only once. The assertion fails if the LLM score is below the configured threshold (default: 0.5). It requires [judge configuration](configuration.md#judge-configuration). ```java assertThat(processInstance) .hasLocalVariableSatisfiesJudge( ElementSelectors.byName("Greet Customer"), "output", "Contains a polite greeting addressed to the customer."); ``` ### hasLocalVariableSimilarTo Assert that a local variable in the scope of a given element is semantically similar to an expected string using a configured embedding model. Use the BPMN element ID or an [element selector](utilities.md#element-selector) to identify the element. The assertion fails if the variable doesn't exist or the similarity score is below the configured threshold (default: 0.5). It requires [semantic similarity configuration](configuration.md#semantic-similarity-configuration). ```java assertThat(processInstance) .hasLocalVariableSimilarTo( ElementSelectors.byName("Greet Customer"), "output", "Hello, how can I help you today?"); ``` ## Process instance message assertions You can verify the message subscriptions of a process instance using `CamundaAssert.assertThat(processInstance)`. ### isWaitingForMessage Assert that the process instance is waiting for the given message. The assertion fails if the process instance has no active message subscription for the given message name and optional correlation key. ```java // 1) By message name assertThat(processInstance).isWaitingForMessage("message-name"); // 2) By message name and correlation key assertThat(processInstance).isWaitingForMessage("message-name", "correlation-key"); ``` ### isNotWaitingForMessage Assert that the process instance is not waiting for the given message. The assertion fails if the process instance has an active message subscription for the given message name and optional correlation key. ```java // 1) By message name assertThat(processInstance).isNotWaitingForMessage("message-name"); // 2) By message name and correlation key assertThat(processInstance).isNotWaitingForMessage("message-name", "correlation-key"); ``` ### hasCorrelatedMessage Assert that the given message was correlated to the process instance. The assertion fails if the process instance has no correlated message subscription for the given message name and optional correlation key. ```java // 1) By message name assertThat(processInstance).hasCorrelatedMessage("message-name"); // 2) By message name and correlation key assertThat(processInstance).hasCorrelatedMessage("message-name", "correlation-key"); ``` ## User task assertions You can verify the user task states and other properties using `CamundaAssert.assertThat()` or `CamundaAssert.assertThatUserTask()`. Use a [UserTaskSelector](utilities.md#user-task-selector) to identify the user task. ### With user task selector Use a [UserTaskSelector](utilities.md#user-task-selector) to identify the user task: ```java // by BPMN element ID assertThatUserTask(UserTaskSelectors.byElementId("user-task-id")).isCompleted(); // by user task name assertThatUserTask(UserTaskSelectors.byTaskName("User Task")).isCompleted(); // by process instance key assertThatUserTask(UserTaskSelectors.byProcessInstanceKey(processInstanceKey)).isCompleted(); ``` ### isCreated Asserts that the user task is created. The assertion fails if the task is in any other state. ```java assertThatUserTask(UserTaskSelectors.byTaskName("User Task")).isCreated(); ``` ### isCompleted Asserts that the user task is completed. The assertion fails if the task is in any other state. ```java assertThatUserTask(UserTaskSelectors.byTaskName("User Task")).isCompleted(); ``` ### isCanceled Asserts that the user task is canceled. The assertion fails if the task is in any other state. ```java assertThatUserTask(UserTaskSelectors.byTaskName("User Task")).isCanceled(); ``` ### isFailed Asserts that the user task is failed. The assertion fails if the task is in any other state. ```java assertThatUserTask(UserTaskSelectors.byTaskName("User Task")).isFailed(); ``` ### hasAssignee Asserts that the user task has the expected assignee. ```java assertThatUserTask(UserTaskSelectors.byTaskName("User Task")).hasAssignee("John Doe"); ``` ### hasPriority Asserts that the user task has the expected priority. ```java assertThatUserTask(UserTaskSelectors.byTaskName("User Task")).hasPriority(100); ``` ### hasElementId Asserts that the user task has the expected BPMN element ID. ```java assertThatUserTask(UserTaskSelectors.byTaskName("User Task")).hasElementId("user-task-id"); ``` ### hasName Asserts that the user task has the expected name. ```java assertThatUserTask(UserTaskSelectors.byElementId("user-task-id")).hasName("User Task"); ``` ### hasProcessInstanceKey Asserts that the user task has the expected process instance key. ```java assertThatUserTask(UserTaskSelectors.byTaskName("User Task")).hasProcessInstanceKey(processInstanceKey); ``` ### hasDueDate Asserts that the user task has the expected due date. ```java assertThatUserTask(UserTaskSelectors.byTaskName("User Task")).hasDueDate("2023-10-01T00:00:00Z"); ``` ### hasCompletionDate Asserts that the user task has the expected completion date. ```java assertThatUserTask(UserTaskSelectors.byTaskName("User Task")).hasCompletionDate("2023-10-01T00:00:00Z"); ``` ### hasFollowUpDate Asserts that the user task has the expected follow-up date. ```java assertThatUserTask(UserTaskSelectors.byTaskName("User Task")).hasFollowUpDate("2023-10-01T00:00:00Z"); ``` ### hasCreationDate Asserts that the user task has the expected creation date. ```java assertThatUserTask(UserTaskSelectors.byTaskName("User Task")).hasCreationDate("2023-10-01T00:00:00Z"); ``` ### hasCandidateGroup Asserts that the user task has the expected candidate group. ```java assertThatUserTask(UserTaskSelectors.byTaskName("User Task")).hasCandidateGroup("groupA"); ``` ### hasCandidateGroups Asserts that the user task has the expected candidate groups. ```java assertThatUserTask(UserTaskSelectors.byTaskName("User Task")).hasCandidateGroups("groupA", "groupB", "groupC"); ``` ## Decision assertions You can verify the decision evaluation state and other properties using `CamundaAssert.assertThat()` or `CamundaAssert.assertThatDecision()`. Use the evaluate decision response or a [DecisionSelector](utilities.md#decision-selector) to identify the decision instance. ### With evaluate decision response Use the response of the evaluate decision command to identify the decision instance: ```java // given/when EvaluateDecisionResponse response = client .newEvaluateDecisionCommand() .decisionId(decisionId) .variables(variables) .send() .join(); // then assertThat(response).isEvaluated(); ``` ### With decision selector Use a [DecisionSelector](utilities.md#decision-selector) to identify the decision instance: ```java // by decision ID assertThatDecision(DecisionSelectors.byId("decision-id")).isEvaluated(); // by decision name assertThatDecision(DecisionSelectors.byName("Decision Name")).isEvaluated(); // by process instance key assertThatDecision(DecisionSelectors.byProcessInstanceKey(processInstanceKey)).isEvaluated(); ``` ### isEvaluated Asserts that the decision is evaluated. The assertion fails if the evaluation failed and outputs the evaluation failure message. ```java assertThatDecision(DecisionSelectors.byId("decision-id")).isEvaluated(); ``` ### hasOutput Asserts that the decision is evaluated with the expected output. The verification fails if the decision evaluation failed or the output does not match. ```java // With primitive value assertThatDecision(DecisionSelectors.byId("decision-id")).hasOutput("output"); // With a map of values Map expectedOutput = // assertThatDecision(DecisionSelectors.byId("decision-id")).hasOutput(expectedOutput); // With a list of values List expectedOutput = // assertThatDecision(DecisionSelectors.byId("decision-id")).hasOutput(expectedOutput); ``` ### hasMatchedRules Asserts that the decision table has matched the given rule indices. The evaluation fails if the decision evaluation failed or at least one of the expected matched rules didn't match. The assertion will pass if the expected indexes are a subset of the total matches, e.g. `hasMatchedRules(1, 2)` will pass if rules [1, 2, 3] matched. ```java // Single rule assertThatDecision(DecisionSelectors.byId("decision-id")).hasMatchedRules(1); // Multiple rules assertThatDecision(DecisionSelectors.byId("decision-id")).hasMatchedRules(1, 3); ``` ### hasNotMatchedRules Asserts that the decision table has not matched the given rule indices. The assertion will fail if the decision evaluation has failed or at least one of the rules indexes has matched. ```java // Single rule assertThatDecision(DecisionSelectors.byId("decision-id")).hasNotMatchedRules(2); // Multiple rules assertThatDecision(DecisionSelectors.byId("decision-id")).hasNotMatchedRules(2, 4); ``` ### hasNoMatchedRules Asserts that the decision table matched no rules. The assertion will fail if the decision evaluation has failed or at least one rule matched. ```java assertThatDecision(DecisionSelectors.byId("decision-id")).hasNoMatchedRules(); ``` ## Value assertions You can verify arbitrary string values, independent of a process instance, using `CamundaAssert.assertThatValue()`. This is useful for evaluating values produced outside of a running process. For example, a single property of a variable object, with the same LLM judge and embedding-based similarity checks used for process variables. ### satisfiesJudge Assert that the given value satisfies a natural language expectation using a configured LLM judge. The expectation is evaluated only once. The assertion fails if the LLM score is below the configured threshold (default: 0.5). It requires [judge configuration](configuration.md#judge-configuration). ```java assertThatValue("The order has been shipped and will arrive tomorrow.") .satisfiesJudge("Confirms that the order is on its way to the customer."); ``` Override the global judge configuration for a single assertion chain using `withJudgeConfig`. ```java assertThatValue(response) .withJudgeConfig(config -> config.withThreshold(0.9)) .satisfiesJudge("Contains a valid JSON response with status OK."); ``` ### isSimilarTo Assert that the given value is semantically similar to an expected string using a configured embedding model. Both values are converted to embeddings, and the cosine similarity is compared against the configured threshold. The assertion fails if the similarity score is below the configured threshold (default: 0.5). It requires [semantic similarity configuration](configuration.md#semantic-similarity-configuration). ```java assertThatValue("Hi there, what can I do for you?") .isSimilarTo("Hello, how can I help you today?"); ``` Override the global semantic similarity configuration for a single assertion chain using `withSemanticSimilarityConfig`. ```java assertThatValue(response) .withSemanticSimilarityConfig(config -> config.withThreshold(0.9)) .isSimilarTo("Hello, how can I help you today?"); ``` ## Custom assertions You can build your own assertions similar to the assertions from CPT. - Use the preconfigured Camunda client to retrieve the process data. - Use [AssertJ](https://github.com/assertj/assertj)'s assertions to verify the expected properties. - Use [Awaitility](http://www.awaitility.org/) around verifications to compensate delays until the data is available. ```java @Test void shouldCreateUserTask() { // given: the process is deployed // when: create a process instance // then Awaitility.await() .ignoreException(ClientException.class) .untilAsserted( () -> { final List userTasks = getUserTasks(processInstanceKey); assertThat(userTasks).hasSize(1); final UserTask userTask = userTasks.getFirst(); assertThat(userTask) .returns("task", UserTask::getName) .returns("me", UserTask::getAssignee); }); } // helper method private List getUserTasks(final long processInstanceKey) { return client .newUserTaskSearchRequest() .filter(filter -> filter.processInstanceKey(processInstanceKey).state(UserTaskState.CREATED)) .send() .join() .items(); } ``` --- ## Configuration(Testing) By default, CPT uses a runtime based on [Testcontainers](#testcontainers-runtime). You can customize the runtime to your needs, or replace it with a [Remote runtime](#remote-runtime), for example, if you can't install a Docker runtime. ## Configuration files CPT properties can be set directly in a configuration file or resolved from environment variables. The file location and resolution mechanism depend on your setup: Configure CPT in your `application.yml` (or `application.properties`). Properties also support [Spring's external configuration](https://docs.spring.io/spring-boot/reference/features/external-config.html), so you can set them through environment variables, system properties, or additional profiles. Configure CPT in a `camunda-container-runtime.properties` file. Properties support automatic environment variable resolution. If a property is not explicitly set, it is resolved from an environment variable by prepending `CAMUNDA_PROCESSTEST_`, replacing dots with underscores, removing hyphens, and converting to uppercase. For example, `judge.chatModel.apiKey` resolves to `CAMUNDA_PROCESSTEST_JUDGE_CHATMODEL_APIKEY`. ## Testcontainers runtime The default runtime of CPT is based on [Testcontainers](https://java.testcontainers.org/). It uses the Camunda Docker image and includes the following components: - Camunda - Connectors :::note Why Testcontainers? CPT follows a common practice by using Testcontainers to provide an isolated, reproducible, and easily configurable environment using Docker containers. This ensures consistent test results, simplifies setup across different platforms, and allows integration with Camunda and other components without manual installation or complex dependencies. ::: :::tip Shared runtime If you use the same runtime configuration for all test classes, then you can use a [shared runtime](#shared-runtime) to speed up the test execution. ::: ### Prerequisites - A Docker-API compatible container runtime, such as Docker on Linux or Docker Desktop on Mac and Windows. If you're experiencing issues with your Docker runtime, have a look at the [Testcontainers documentation](https://java.testcontainers.org/supported_docker_environment/). ### Usage By default, the runtime uses the same version of the Camunda Docker images as the Maven module. You can change the Docker images and other runtime properties in the following way. In your `application.yml` (or `application.properties`): ```yaml camunda: process-test: # Change the version of the Camunda Docker image camunda-docker-image-version: 8.8.0 # Change the Camunda Docker image camunda-docker-image-name: camunda/camunda # Set additional Camunda environment variables camunda-env-vars: env_1: value_1 # Expose additional Camunda ports camunda-exposed-ports: - 9000 # Change the Camunda logger name camunda-logger-name: tc.camunda # Enable Connectors connectors-enabled: true # Change the Connectors Docker image connectors-docker-image-name: camunda/connectors # Change version of the Connectors Docker image connectors-docker-image-version: 8.8.0 # Set additional Connectors environment variables connectors-env-vars: env_1: value_1 # Set Connectors secrets connectors-secrets: secret_1: value_1 # Expose additional Connectors ports connectors-exposed-ports: - 9010 # Change the Connectors logger name connectors-logger-name: tc.connectors ``` In your `/camunda-container-runtime.properties` file: ```properties # Change the version of the Camunda Docker image camundaDockerImageVersion=8.8.0 # Change the Camunda Docker image camundaDockerImageName=camunda/camunda # Set additional Camunda environment variables camundaEnvVars.env_1=value_1 camundaEnvVars.env_2=value_2 # Expose additional Camunda ports camundaExposedPorts[0]=9000 camundaExposedPorts[1]=9001 # Change the Camunda logger name camundaLoggerName=tc.camunda # Enable Connectors connectorsEnabled=true # Change version of the Connectors Docker image connectorsDockerImageVersion=8.8.0 # Change the Connectors Docker image connectorsDockerImageName=camunda/connectors # Set additional Connectors environment variables connectorsEnvVars.env_1=value_1 connectorsEnvVars.env_2=value_2 # Set Connectors secrets connectorsSecrets.secret_1=value_1 connectorsSecrets.secret_2=value_2 # Expose additional Connectors ports connectorsExposedPorts[0]=9010 connectorsExposedPorts[1]=9011 # Change the Connectors logger name connectorsLoggerName=tc.connectors ``` Alternatively, you can register the JUnit extension manually and use the fluent builder: ```java package com.example; // No annotation: @CamundaProcessTest public class MyProcessTest { @RegisterExtension private static final CamundaProcessTestExtension EXTENSION = new CamundaProcessTestExtension() // Change the version of the Camunda Docker image .withCamundaDockerImageVersion("8.8.0") // Change the Camunda Docker image .withCamundaDockerImageName("camunda/camunda") // Set additional Camunda environment variables .withCamundaEnv("env_1", "value_1") // Expose additional Camunda ports .withCamundaExposedPort(4567) // Enable Connectors .withConnectorsEnabled(true) // Change the Connectors Docker image .withConnectorsDockerImageName("camunda/connectors") // Change version of the Connectors Docker image .withConnectorsDockerImageVersion("8.8.0") // Set additional Connectors environment variables .withConnectorsEnv("env_1", "value_1") // Set Connectors secrets .withConnectorsSecret("secret_1", "value_1"); } ``` ### Shared runtime By default, CPT creates a new runtime for each test class. You can change this behavior and use a shared Testcontainers runtime for all test classes to speed up the test execution. You can enable the shared runtime in the following way. In your `application.yml` (or `application.properties`): ```yaml camunda: process-test: # Switch from a managed to a shared runtime runtime-mode: shared ``` All test classes using the shared runtime will use the same runtime configuration. You can't change the runtime configuration for individual test classes, such as enabling connectors or setting connector secrets. However, you can switch to a managed runtime for individual test classes and override the runtime configuration. ```java @SpringBootTest( properties = { // Use a managed runtime for a different configuration "camunda.process-test.runtime-mode=managed", "camunda.process-test.connectors-enabled=true", } ) @CamundaSpringProcessTest public class MyProcessTest { // } ``` In your `/camunda-container-runtime.properties` file: ```properties # Switch from a managed to a shared runtime runtimeMode=shared ``` All test classes using the shared runtime will use the same runtime configuration. You can't change the runtime configuration for individual test classes, such as enabling connectors or setting connector secrets. However, you can switch to a managed runtime for individual test classes and override the runtime configuration. ```java package com.example; // No annotation: @CamundaProcessTest public class MyProcessTest { @RegisterExtension private static final CamundaProcessTestExtension EXTENSION = new CamundaProcessTestExtension() // Use a managed runtime for a different configuration .withRuntimeMode(CamundaProcessTestRuntimeMode.MANAGED) .withConnectorsEnabled(true); } ``` ### Multi-tenancy Multi-tenancy is disabled by default. You can enable multi-tenancy in the following way: In your `application.yml` (or `application.properties`): ```yaml camunda: process-test: # Enable multi-tenancy multi-tenancy-enabled: true ``` By enabling multi-tenancy, the runtime enables Basic Auth security and creates a default user with username/password `demo` with admin rights to interact with the runtime. A process test using multi-tenancy could look like the following example: ```java @SpringBootTest @CamundaSpringProcessTest public class MyProcessTest { private static final String DEFAULT_USERNAME = "demo"; private static final String TENANT_ID_1 = "tenant-1"; private static final String TENANT_ID_2 = "tenant-2"; @Autowired private CamundaClient client; @Autowired private CamundaProcessTestContext processTestContext; private CamundaClient clientForTenant1; @BeforeEach void setupTenants() { // create tenants client.newCreateTenantCommand().tenantId(TENANT_ID_1).name(TENANT_ID_1).send().join(); client.newCreateTenantCommand().tenantId(TENANT_ID_2).name(TENANT_ID_2).send().join(); // assign the default user to the tenants client .newAssignUserToTenantCommand() .username(DEFAULT_USERNAME) .tenantId(TENANT_ID_1) .send() .join(); client .newAssignUserToTenantCommand() .username(DEFAULT_USERNAME) .tenantId(TENANT_ID_2) .send() .join(); // create a client for tenant 1 clientForTenant1 = processTestContext.createClient( clientBuilder -> clientBuilder.defaultTenantId(TENANT_ID_1)); } @Test void createProcessInstance() { // given clientForTenant1 .newDeployResourceCommand() .addResourceFromClasspath("bpmn/order-process.bpmn") .send() .join(); // when final var processInstance = clientForTenant1 .newCreateInstanceCommand() .bpmnProcessId("order-process") .latestVersion() .variable("order_id", "order-1") .send() .join(); // then assertThatProcessInstance(processInstance).isCreated(); Assertions.assertThat(processInstance.getTenantId()).isEqualTo(TENANT_ID_1); } } ``` In your `/camunda-container-runtime.properties` file: ```properties # Enable multi-tenancy multiTenancyEnabled=true ``` Alternatively, you can register the JUnit extension manually and use the fluent builder: ```java package com.example; // No annotation: @CamundaProcessTest public class MyProcessTest { @RegisterExtension private static final CamundaProcessTestExtension EXTENSION = new CamundaProcessTestExtension() // Enable multi-tenancy .withMultiTenancyEnabled(true); } ``` By enabling multi-tenancy, the runtime enables Basic Auth security and creates a default user with username/password `demo` with admin rights to interact with the runtime. A process test using multi-tenancy could look like the following example: ```java @CamundaProcessTest public class MyProcessTest { private static final String DEFAULT_USERNAME = "demo"; private static final String TENANT_ID_1 = "tenant-1"; private static final String TENANT_ID_2 = "tenant-2"; private CamundaClient client; private CamundaProcessTestContext processTestContext; private CamundaClient clientForTenant1; @BeforeEach void setupTenants() { // create tenants client.newCreateTenantCommand().tenantId(TENANT_ID_1).name(TENANT_ID_1).send().join(); client.newCreateTenantCommand().tenantId(TENANT_ID_2).name(TENANT_ID_2).send().join(); // assign the default user to the tenants client .newAssignUserToTenantCommand() .username(DEFAULT_USERNAME) .tenantId(TENANT_ID_1) .send() .join(); client .newAssignUserToTenantCommand() .username(DEFAULT_USERNAME) .tenantId(TENANT_ID_2) .send() .join(); // create a client for tenant 1 clientForTenant1 = processTestContext.createClient( clientBuilder -> clientBuilder.defaultTenantId(TENANT_ID_1)); } @Test void createProcessInstance() { // given clientForTenant1 .newDeployResourceCommand() .addResourceFromClasspath("bpmn/order-process.bpmn") .send() .join(); // when final var processInstance = clientForTenant1 .newCreateInstanceCommand() .bpmnProcessId("order-process") .latestVersion() .variable("order_id", "order-1") .send() .join(); // then assertThatProcessInstance(processInstance).isCreated(); Assertions.assertThat(processInstance.getTenantId()).isEqualTo(TENANT_ID_1); } } ``` :::info You should assign the default user (`demo`) to all tenants to ensure that the assertions can access all data. ::: ### Custom containers You can add custom containers to the managed or shared Testcontainers runtime, for example, to add a database, an MCP server, or a mock service. The CPT runtime manages the lifecycle of the custom containers and ensures that they are started before the tests and stopped after the tests. The custom containers are added to the same network as the Camunda and Connectors containers to allow communication between the containers. You can add a custom container in the following way. Implement a `CamundaProcessTestContainerProvider` bean that creates the custom container. In this example, we create a WireMock container to mock external HTTP calls in the tests. ```java @Configuration public class TestConfig { @Bean public CamundaProcessTestContainerProvider wireMockProvider() { return containerContext -> new WireMockContainer(); } // A WireMock container to mock external HTTP calls in the tests private static final class WireMockContainer extends GenericContainer { public WireMockContainer() { // Configure the Docker image super("wiremock/wiremock:3.13.0"); // Configure the network alias for communication between the containers withNetworkAliases("wiremock"); // Configure the ports to expose withExposedPorts(8080); // Configure the logger withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger("tc.wiremock"), true)); // Configure the wait strategy to ensure that the container is ready before running the tests waitingFor( Wait.forHttp("/__admin/mappings").forPort(8080).withMethod("GET").forStatusCode(200)); // Custom container-specific configuration withCopyFileToContainer( // Copy the WireMock mapping file for the HTTP stubs to the container MountableFile.forClasspathResource("/wiremock/mapping.json"), "/home/wiremock/mappings/mapping.json"); } } } ``` In the `application.yml` configuration, we use a connector secret to bind the connector task to the WireMock container using its network alias `wiremock` and the exposed port `8080`. ```yaml camunda: process-test: connectors-enabled: true connectors-secrets: BASE_URL: http://wiremock:8080 ``` Implement the `CamundaProcessTestContainerProvider` interface that creates the custom container. In this example, we create a WireMock container to mock external HTTP calls in the tests. ```java public class WireMockContainerProvider implements CamundaProcessTestContainerProvider { @Override public GenericContainer createContainer(final CamundaProcessTestContainerContext containerContext) { return new WireMockContainer(); } // A WireMock container to mock external HTTP calls in the tests private static final class WireMockContainer extends GenericContainer { public WireMockContainer() { // Configure the Docker image super("wiremock/wiremock:3.13.0"); // Configure the network alias for communication between the containers withNetworkAliases("wiremock"); // Configure the ports to expose withExposedPorts(8080); // Configure the logger withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger("tc.wiremock"), true)); // Configure the wait strategy to ensure that the container is ready before running the tests waitingFor( Wait.forHttp("/__admin/mappings").forPort(8080).withMethod("GET").forStatusCode(200)); // Custom container-specific configuration withCopyFileToContainer( // Copy the WireMock mapping file for the HTTP stubs to the container MountableFile.forClasspathResource("/wiremock/mapping.json"), "/home/wiremock/mappings/mapping.json"); } } } ``` Register the container provider using the Java ServiceLoader mechanism by creating a file `io.camunda.process.test.api.runtime.CamundaProcessTestContainerProvider` in the `src/test/resources/META-INF/services` directory of your project and adding the fully qualified name of the container provider implementation: ``` com.example.WireMockContainerProvider ``` In the `/camunda-container-runtime.properties` configuration file, we use a connector secret to bind the connector task to the WireMock container using its network alias `wiremock` and the exposed port `8080`. ```properties connectorsEnabled=true connectorsSecrets.BASE_URL=http://wiremock:8080 ``` Alternatively, you can register the container provider on the JUnit extension using the fluent builder: ```java // No annotation: @CamundaProcessTest public class MyProcessTest { @RegisterExtension private static final CamundaProcessTestExtension EXTENSION = new CamundaProcessTestExtension() .withContainerProvider(new WireMockContainerProvider()) .withConnectorsEnabled(true) .withConnectorsSecret("BASE_URL", "http://wiremock:8080"); } ``` ## Remote runtime Instead of using the managed [Testcontainers runtime](#testcontainers-runtime), you can configure CPT to connect to a remote runtime, for example, to a local [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) running on your machine. When to use it: - You can't install a Docker-API compatible container runtime - Debugging of test case on your local machine :::info You are responsible for configuring and managing the remote runtime. Ensure the runtime is running before executing tests. Keep in mind that CPT automatically deletes all data between test runs to maintain a clean state. ::: ### Prerequisites - Install a Camunda 8 runtime, for example, [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) - Expose the management API port (`9600`) to delete the data between test runs (by default for a local Camunda 8 Run) - Enable the management clock endpoint to allow clock manipulations You can [configure Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run/configuration.md#configuration-options) by defining a `application.yaml` file with: ```yaml zeebe.clock.controlled: true ``` By default, Camunda 8 Run loads the `application.yaml` from the distribution's root directory. If you use a different path, then you need to set the path when starting the application with the command line argument `--config=application.yaml`: ``` ./start.sh --config=application.yaml ``` ### Usage You need to set the following property to switch to a remote runtime. In your `application.yml` (or `application.properties`): ```yaml camunda: process-test: # Switch from a managed to a remote runtime runtime-mode: remote ``` In your `/camunda-container-runtime.properties` file: ```properties # Switch from a managed to a remote runtime runtimeMode=remote ``` Alternatively, you can register the JUnit extension manually and use the fluent builder: ```java package com.example; // No annotation: @CamundaProcessTest public class MyProcessTest { @RegisterExtension private static final CamundaProcessTestExtension EXTENSION = new CamundaProcessTestExtension() // Switch from a managed to a remote runtime .withRuntimeMode(CamundaProcessTestRuntimeMode.REMOTE); } ``` ### Change the connection By default, CPT connects to a remote Camunda 8 Run running on your local machine. CPT checks if the remote runtime is available and ready, before running the tests. It waits up to 1 minute for the remote runtime to become ready. You can change the connection to the remote runtime and the connection time in the following way. In your `application.yml` (or `application.properties`): ```yaml camunda: process-test: runtime-mode: remote # Change the connection (default: Camunda 8 Run) remote: camunda-monitoring-api-address: http://0.0.0.0:9600 connectors-rest-api-address: http://0.0.0.0:8085 # The connection timeout in ISO-8601 duration format (default: PT1M) runtime-connection-timeout: PT1M client: rest-address: http://0.0.0.0:8080 grpc-address: http://0.0.0.0:26500 ``` :::note The properties `camunda.process-test.remote.client.rest-address` and `camunda.process-test.remote.client.grpc-address` are deprecated. Use `camunda.client.rest-address` and `camunda.client.grpc-address` instead. ::: In your `/camunda-container-runtime.properties` file: ```properties runtimeMode=remote # Change the connection (default: Camunda 8 Run) remote.camundaMonitoringApiAddress=http://0.0.0.0:9600 remote.connectorsRestApiAddress=http://0.0.0.0:8085 camunda.client.gateway.grpc.address=http://0.0.0.0:26500 camunda.client.gateway.rest.address=http://0.0.0.0:8080 # The connection timeout in ISO-8601 duration format (default: PT1M) remote.runtimeConnectionTimeout=PT1M ``` :::note The properties `remote.client.grpcAddress` and `remote.client.restAddress` are deprecated. Use `camunda.client.gateway.grpc.address` and `camunda.client.gateway.rest.address` instead. ::: Alternatively, register the JUnit extension manually and use the fluent builder: ```java package com.example; // No annotation: @CamundaProcessTest public class MyProcessTest { @RegisterExtension private static final CamundaProcessTestExtension EXTENSION = new CamundaProcessTestExtension() .withRuntimeMode(CamundaProcessTestRuntimeMode.REMOTE) // Change the connection (default: Camunda 8 Run) .withCamundaClientBuilderFactory(() -> CamundaClient.newClientBuilder() .restAddress(URI.create("http://0.0.0.0:8080")) .grpcAddress(URI.create("http://0.0.0.0:26500")) ) .withRemoteCamundaMonitoringApiAddress(URI.create("http://0.0.0.0:9600")) .withRemoteConnectorsRestApiAddress(URI.create("http://0.0.0.0:8085")) // Change the connection timeout (default: PT1M) .withRemoteRuntimeConnectionTimeout(Duration.ofMinutes(1)); } ``` ### Debugging of test cases You can use a remote runtime to debug your test cases on your local machine. Set breakpoints in your test case and run the test in debug mode from your IDE. When the test execution stops at a breakpoint, you can inspect the process instance state using Operate and the user task state using Tasklist. You can also use the Camunda client to interact with the runtime from the debugger console. ## Client configuration CPT configures the Camunda client automatically based on the runtime mode. You can customize the client configuration beyond the connection addresses, for example, to set up authentication. CPT applies all [Camunda client configurations](/apis-tools/camunda-spring-boot-starter/configuration.md) from your `application.yml`. For example, to configure Basic authentication for a remote runtime: ```yaml camunda: client: grpc-address: http://localhost:26500 rest-address: http://localhost:8080 auth: method: basic username: demo password: demo ``` For full flexibility, provide a `CamundaClientBuilderFactory` bean: ```java @Bean public CamundaClientBuilderFactory customClientBuilderFactory() { return () -> CamundaClient.newClientBuilder() .restAddress(URI.create("http://0.0.0.0:8080")) .grpcAddress(URI.create("http://0.0.0.0:26500")) .credentialsProvider( CredentialsProvider.newBasicAuthCredentialsProviderBuilder() .username("demo") .password("demo") .build()); } ``` In the `camunda-container-runtime.properties` file, you can set any [`ClientProperties`](https://javadoc.io/doc/io.camunda/camunda-client-java/latest/io/camunda/client/ClientProperties.html). For example, to configure the connection to a remote runtime: ```properties camunda.client.gateway.rest.address=http://0.0.0.0:8080 camunda.client.gateway.grpc.address=http://0.0.0.0:26500 ``` For more flexibility, use the fluent builder to set a client builder factory: ```java @RegisterExtension private static final CamundaProcessTestExtension EXTENSION = new CamundaProcessTestExtension() .withRuntimeMode(CamundaProcessTestRuntimeMode.REMOTE) .withCamundaClientBuilderFactory( () -> CamundaClient.newClientBuilder() .restAddress(URI.create("http://0.0.0.0:8080")) .grpcAddress(URI.create("http://0.0.0.0:26500"))); ``` To override specific client properties, for example to configure a credential provider, use `withCamundaClientBuilderOverrides`. This works together with the client builder factory and the configuration file: ```java @RegisterExtension private static final CamundaProcessTestExtension EXTENSION = new CamundaProcessTestExtension() .withCamundaClientBuilderOverrides( camundaClientBuilder -> camundaClientBuilder .credentialsProvider( CredentialsProvider.newBasicAuthCredentialsProviderBuilder() .username("demo") .password("demo") .build())); ``` ## Process Test Coverage CPT generates an HTML and JSON coverage report of your BPMN processes. You can configure the report generation in the following way. In your `application.yml` (or `application.properties`): ```yaml camunda: process-test: coverage: # Change the directory where the report is generated reportDirectory: target/coverage-report # Exclude processes from the report excludedProcesses: - process_1 - process_2 ``` In your `/camunda-container-runtime.properties` file: ```properties # Change the directory where the report is generated coverage.reportDirectory=target/coverage-report # Exclude processes from the report excludedProcesses[0]=process_1 excludedProcesses[1]=process_2 ``` ## Logging The test runtime uses [SLF4J](https://www.slf4j.org/) as the logging framework. If needed, you can enable the logging for the following packages: - `io.camunda.process.test` - The test runtime (recommended level `info`) - `tc.camunda` - The Camunda Docker container (recommended level `error`) - `tc.connectors` - The connectors Docker container (recommended level `error`) - `org.testcontainers` - The Testcontainers framework (recommended level `warn`) ## Judge configuration [Judge assertions](assertions.md#hasvariablesatisfiesjudge) use a configured LLM to score process variables (or plain values) against natural language expectations. This section covers how to set up the LLM provider and tune the judge behavior. ### Prerequisites CPT provides an optional [LangChain4j](https://docs.langchain4j.dev/) integration module that ships with preconfigured support for major LLM providers: OpenAI, Anthropic, Amazon Bedrock, Azure OpenAI, and OpenAI-compatible APIs. LangChain4j requires Java 17+. You can provide your own LLM integration through a custom `ChatModelAdapter` instead (see [custom ChatModelAdapter](#custom-chatmodeladapter)). :::tip For a guided walkthrough of setting up and testing AI agents, see [test your AI agents](/components/agentic-orchestration/evaluate-agents/test-ai-agents.md). ::: Camunda Process Test Spring includes the LangChain4j providers as a transitive dependency. No additional dependency is needed. Add the `camunda-process-test-langchain4j` dependency to your project: ```xml io.camunda camunda-process-test-langchain4j test ``` If you provide a custom `ChatModelAdapter` (see [custom ChatModelAdapter](#custom-chatmodeladapter)), this dependency is not required. ### Property reference All judge properties are nested under `camunda.process-test.judge` in Spring configuration. In Java properties files, use the `judge.` prefix with camelCase keys (for example, `judge.chat-model.api-key` becomes `judge.chatModel.apiKey`). For configuration examples, see [Step 2: configure the LLM provider and connectors](/components/agentic-orchestration/evaluate-agents/test-ai-agents.md#step-2-configure-the-llm-provider-and-connectors). Unless noted otherwise, properties in the provider tables are required. #### Judge settings | Property | Type | Default | Description | | --------------------- | -------- | ------- | -------------------------------------------------------- | | `judge.threshold` | `double` | `0.5` | Confidence threshold (0.0 to 1.0) for the judge to pass. | | `judge.custom-prompt` | `string` | | Custom evaluation prompt replacing the default criteria. | The default threshold of `0.5` treats a response as acceptable when it is at least partially satisfied according to the judge rubric. This is a practical default for AI-generated output, where wording and level of detail may vary between runs even when the response is still useful. Increase the threshold when your assertion needs stricter semantic agreement. #### Chat model settings | Property | Required | Type | Description | | ------------------------------ | -------- | ---------- | --------------------------------------------------------- | | `judge.chat-model.provider` | Yes | `string` | Set to `openai`. | | `judge.chat-model.model` | Yes | `string` | Model name (for example `gpt-4o`). | | `judge.chat-model.api-key` | Yes | `string` | API key. | | `judge.chat-model.timeout` | No | `duration` | Request timeout (ISO-8601 duration, for example `PT30S`). | | `judge.chat-model.temperature` | No | `double` | Temperature for response randomness (0.0 to 2.0). | **Example:** ```yaml camunda: process-test: judge: chat-model: provider: "openai" model: "gpt-4o" api-key: ${OPENAI_API_KEY} ``` | Property | Required | Type | Description | | ------------------------------ | -------- | ---------- | --------------------------------------------------------- | | `judge.chat-model.provider` | Yes | `string` | Set to `anthropic`. | | `judge.chat-model.model` | Yes | `string` | Model name (for example `claude-sonnet-4-20250514`). | | `judge.chat-model.api-key` | Yes | `string` | API key. | | `judge.chat-model.timeout` | No | `duration` | Request timeout (ISO-8601 duration, for example `PT30S`). | | `judge.chat-model.temperature` | No | `double` | Temperature for response randomness (0.0 to 2.0). | **Example:** ```yaml camunda: process-test: judge: chat-model: provider: "anthropic" model: "claude-sonnet-4-20250514" api-key: ${ANTHROPIC_API_KEY} ``` Supports Bedrock long-term API keys or AWS IAM credentials. Falls back to the [AWS default credentials provider chain](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/credentials-chain.html). | Property | Required | Type | Description | | ----------------------------------------- | ------------------------------ | ---------- | ---------------------------------------------------------------------------------------------- | | `judge.chat-model.provider` | Yes | `string` | Set to `amazon-bedrock`. | | `judge.chat-model.model` | Yes | `string` | Model name (for example `eu.anthropic.claude-haiku-4-5-20251001-v1:0`). | | `judge.chat-model.region` | No | `string` | AWS region (for example `eu-central-1`). | | `judge.chat-model.api-key` | No | `string` | Bedrock long-term API key. Optional if using IAM credentials or the default credentials chain. | | `judge.chat-model.credentials.access-key` | Conditionally, with secret key | `string` | AWS IAM access key. Optional if using an API key or the default credentials chain. | | `judge.chat-model.credentials.secret-key` | Conditionally, with access key | `string` | AWS IAM secret key. Optional if using an API key or the default credentials chain. | | `judge.chat-model.timeout` | No | `duration` | Request timeout (ISO-8601 duration, for example `PT30S`). | | `judge.chat-model.temperature` | No | `double` | Temperature for response randomness (0.0 to 2.0). | **Example:** ```yaml camunda: process-test: judge: chat-model: provider: "amazon-bedrock" model: "eu.anthropic.claude-haiku-4-5-20251001-v1:0" region: "eu-central-1" credentials: access-key: ${AWS_BEDROCK_ACCESS_KEY} secret-key: ${AWS_BEDROCK_SECRET_KEY} ``` Supports API key authentication. Falls back to [`DefaultAzureCredential`](https://learn.microsoft.com/en-us/java/api/com.azure.identity.defaultazurecredential). | Property | Required | Type | Description | | ------------------------------ | -------- | ---------- | -------------------------------------------------------------------------------------------------------------------------- | | `judge.chat-model.provider` | Yes | `string` | Set to `azure-openai`. | | `judge.chat-model.model` | Yes | `string` | Azure [deployment name](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/create-resource#deploy-a-model). | | `judge.chat-model.endpoint` | Yes | `string` | Azure OpenAI resource URL (for example `https://my-resource.openai.azure.com/`). | | `judge.chat-model.api-key` | No | `string` | API key. Optional; if omitted, falls back to `DefaultAzureCredential`. | | `judge.chat-model.timeout` | No | `duration` | Request timeout (ISO-8601 duration, for example `PT30S`). | | `judge.chat-model.temperature` | No | `double` | Temperature for response randomness (0.0 to 2.0). | **Example:** ```yaml camunda: process-test: judge: chat-model: provider: "azure-openai" model: "my-gpt4o-deployment" endpoint: "https://my-resource.openai.azure.com/" api-key: ${AZURE_OPENAI_API_KEY} ``` For local models (such as [Ollama](https://ollama.com/)) or any third-party API that implements the [OpenAI chat completions format](https://platform.openai.com/docs/api-reference/chat). | Property | Required | Type | Description | | ------------------------------ | -------- | ---------- | ------------------------------------------------------------------------ | | `judge.chat-model.provider` | Yes | `string` | Set to `openai-compatible`. | | `judge.chat-model.model` | Yes | `string` | Model name (for example `llama3`). | | `judge.chat-model.base-url` | Yes | `string` | Base URL for the API endpoint (for example `http://localhost:11434/v1`). | | `judge.chat-model.api-key` | No | `string` | API key. Optional for local providers. | | `judge.chat-model.headers.*` | No | `map` | Custom HTTP headers. | | `judge.chat-model.timeout` | No | `duration` | Request timeout (ISO-8601 duration, for example `PT30S`). | | `judge.chat-model.temperature` | No | `double` | Temperature for response randomness (0.0 to 2.0). | **Example (Ollama):** ```yaml camunda: process-test: judge: chat-model: provider: "openai-compatible" model: "llama3" base-url: "http://localhost:11434/v1" ``` For providers not listed above, use a custom provider name and pass arbitrary properties. See [Custom ChatModelAdapter](#custom-chatmodeladapter) for implementation details. | Property | Required | Type | Description | | -------------------------------------- | -------- | ---------- | --------------------------------------------------------------------------------------------- | | `judge.chat-model.provider` | Yes | `string` | Custom provider name matching your SPI implementation. | | `judge.chat-model.model` | Yes | `string` | Model name. | | `judge.chat-model.custom-properties.*` | No | `map` | Arbitrary key-value pairs passed to SPI providers via `ProviderConfig.getCustomProperties()`. | | `judge.chat-model.timeout` | No | `duration` | Request timeout (ISO-8601 duration, for example `PT30S`). | | `judge.chat-model.temperature` | No | `double` | Temperature for response randomness (0.0 to 2.0). | **Example:** ```yaml camunda: process-test: judge: chat-model: provider: "my-custom-provider" model: "my-model" custom-properties: endpoint: "https://my-llm.example.com/v1" ``` ### Custom prompt You can replace the default evaluation criteria with a custom prompt. The custom prompt replaces only the evaluation criteria (the "You are an impartial judge..." preamble). The system still controls the expectation and value injection, the scoring rubric, and the JSON output format. By default, CPT uses an internal prompt that instructs the model to act as an impartial judge, compare the provided value against the natural language expectation, apply the documented scoring rubric, and return the result in the expected JSON structure. ```yaml camunda: process-test: judge: custom-prompt: "You are a domain expert evaluating financial data accuracy." ``` ```properties judge.customPrompt=You are a domain expert evaluating financial data accuracy. ``` Or programmatically: ```java JudgeConfig.of(prompt -> myChatModelAdapter.generate(prompt)) .withCustomPrompt("You are a domain expert evaluating financial data accuracy."); ``` You can also override the custom prompt for a single assertion chain: ```java assertThat(processInstance) .withJudgeConfig(config -> config .withCustomPrompt("You are a domain expert evaluating financial data accuracy.")) .hasVariableSatisfiesJudge("result", "Contains valid totals."); ``` ### Custom ChatModelAdapter You can provide your own `ChatModelAdapter` implementation without depending on the `camunda-process-test-langchain4j` module. A `ChatModelAdapter` is a functional interface that takes a prompt string and returns a response string. If you have a single `ChatModelAdapter` bean and no `provider` property is set, CPT auto-detects and uses it: ```java @TestConfiguration class JudgeTestConfig { @Bean ChatModelAdapter chatModelAdapter() { return prompt -> myChatModelAdapter.generate(prompt); } } ``` When you have multiple beans, set `provider` to the bean name you want to use. In Spring, the bean name defaults to the method name: ```java @TestConfiguration class JudgeTestConfig { @Bean ChatModelAdapter openAiAdapter() { /* ... */ } @Bean ChatModelAdapter ollamaAdapter() { /* ... */ } } ``` ```yaml camunda: process-test: judge: chat-model: provider: "ollamaAdapter" # matches the bean method name ``` :::note Resolution order When using `@CamundaSpringProcessTest`, CPT resolves the judge adapter in the following order: 1. If a single `ChatModelAdapter` bean exists and no `provider` property is configured, that bean is used automatically. 2. If the `provider` property is configured and a bean with a matching name exists, that bean is selected. 3. If no matching bean is found, CPT falls back to the built-in LangChain4j implementations, provided that `camunda-process-test-langchain4j` is on the classpath. 4. If a `provider` is configured but no matching implementation can be resolved at all, CPT throws an exception. ::: Alternatively, you can configure the judge programmatically. Set the configuration globally using `CamundaAssert.setJudgeConfig()`: ```java CamundaAssert.setJudgeConfig( JudgeConfig.of(prompt -> myChatModelAdapter.generate(prompt)) .withThreshold(0.8)); ``` Implement `ChatModelAdapterProvider` and register it through `META-INF/services`: ```java public class MyCustomProvider implements ChatModelAdapterProvider { @Override public String getProviderName() { return "my-provider"; } @Override public ChatModelAdapter create(ProviderConfig config) { String endpoint = config.getCustomProperties().get("endpoint"); return prompt -> callEndpoint(endpoint, prompt); } } ``` Register the provider in `META-INF/services/io.camunda.process.test.api.judge.ChatModelAdapterProvider`: ``` com.example.MyCustomProvider ``` Alternatively, you can configure the judge programmatically. Set the configuration globally using `CamundaAssert.setJudgeConfig()`: ```java CamundaAssert.setJudgeConfig( JudgeConfig.of(prompt -> myChatModelAdapter.generate(prompt)) .withThreshold(0.8)); ``` Or register the JUnit extension manually with a judge configuration: ```java @RegisterExtension CamundaProcessTestExtension extension = new CamundaProcessTestExtension() .withJudgeConfig(JudgeConfig.of(prompt -> myChatModelAdapter.generate(prompt)) .withThreshold(0.8)); ``` ## Semantic similarity configuration [Semantic similarity assertions](assertions.md#hasvariablesimilarto) use a configured embedding model to compare process variables (or plain values) to an expected string using vector embeddings. The assertion converts both values to embeddings and compares them using cosine similarity. This section covers how to set up the embedding model provider and tune the similarity behavior. ### Prerequisites CPT provides an optional [LangChain4j](https://docs.langchain4j.dev/) integration module that ships with preconfigured support for major embedding model providers, such as OpenAI, Azure OpenAI, Amazon Bedrock, and OpenAI-compatible APIs. :::note LangChain4j requires Java 17+. ::: Camunda Process Test Spring includes the LangChain4j providers as a transitive dependency. No additional dependency or configuration is needed. Add the `camunda-process-test-langchain4j` dependency to your project: ```xml io.camunda camunda-process-test-langchain4j test ``` :::important You can provide your own embedding integration through a custom `EmbeddingModelAdapter`. In that case, this dependency is not required. See [custom EmbeddingModelAdapter](#custom-embeddingmodeladapter) for more details. ::: ### Property reference All semantic similarity properties are nested under `camunda.process-test.similarity` in Spring configuration. In Java properties files, use the `similarity.` prefix with camelCase keys. For example, `similarity.embedding-model.api-key` becomes `similarity.embeddingModel.apiKey`. :::note Unless noted otherwise, properties in the provider tables are required. ::: #### Similarity settings | Property | Type | Default | Description | | ------------------------------------------ | --------- | ------- | ---------------------------------------------------------------------------------------------------------------------------- | | `similarity.threshold` | `double` | `0.5` | Cosine similarity threshold (0.0 to 1.0) for the assertion to pass. | | `similarity.default-preprocessors-enabled` | `boolean` | `true` | When `true`, applies the default text preprocessors (lowercase, Unicode NFC, and whitespace normalization) before embedding. | The default threshold of 0.5 treats two strings as similar when their cosine similarity is at least 0.5. This is a practical default for AI-generated text, where wording and phrasing may vary between runs even when the meaning is the same. Increase the threshold when your assertion needs stricter semantic agreement. #### Embedding model settings | Property | Required | Type | Description | | --------------------------------------- | -------- | ---------- | ---------------------------------------------------------------------- | | `similarity.embedding-model.provider` | Yes | `string` | Set to `openai`. | | `similarity.embedding-model.model` | Yes | `string` | Model name (for example `text-embedding-3-small`). | | `similarity.embedding-model.api-key` | Yes | `string` | API key. | | `similarity.embedding-model.dimensions` | No | `integer` | Number of output dimensions for models that support custom dimensions. | | `similarity.embedding-model.timeout` | No | `duration` | Request timeout (ISO-8601 duration, for example `PT30S`). | **Example:** ```yaml camunda: process-test: similarity: embedding-model: provider: "openai" model: "text-embedding-3-small" api-key: ${OPENAI_API_KEY} ``` It supports Bedrock long-term API keys or AWS IAM credentials. It falls back to the [AWS default credentials provider chain](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/credentials-chain.html). | Property | Required | Type | Description | | --------------------------------------------------- | ------------------------------ | ---------- | ---------------------------------------------------------------------------------------------- | | `similarity.embedding-model.provider` | Yes | `string` | Set to `amazon-bedrock`. | | `similarity.embedding-model.model` | Yes | `string` | Model name (for example `amazon.titan-embed-text-v2:0`). | | `similarity.embedding-model.region` | No | `string` | AWS region (for example `eu-central-1`). | | `similarity.embedding-model.api-key` | No | `string` | Bedrock long-term API key. Optional if using IAM credentials or the default credentials chain. | | `similarity.embedding-model.credentials.access-key` | Conditionally, with secret key | `string` | AWS IAM access key. Optional if using an API key or the default credentials chain. | | `similarity.embedding-model.credentials.secret-key` | Conditionally, with access key | `string` | AWS IAM secret key. Optional if using an API key or the default credentials chain. | | `similarity.embedding-model.dimensions` | No | `integer` | Number of output dimensions for models that support custom dimensions. | | `similarity.embedding-model.normalize` | No | `boolean` | Whether to normalize the output embeddings. | | `similarity.embedding-model.timeout` | No | `duration` | Request timeout (ISO-8601 duration, for example `PT30S`). | **Example:** ```yaml camunda: process-test: similarity: embedding-model: provider: "amazon-bedrock" model: "amazon.titan-embed-text-v2:0" region: "eu-central-1" credentials: access-key: ${AWS_BEDROCK_ACCESS_KEY} secret-key: ${AWS_BEDROCK_SECRET_KEY} ``` It supports API key authentication. It falls back to [`DefaultAzureCredential`](https://learn.microsoft.com/en-us/java/api/com.azure.identity.defaultazurecredential). | Property | Required | Type | Description | | --------------------------------------- | -------- | ---------- | -------------------------------------------------------------------------------------------------------------------------- | | `similarity.embedding-model.provider` | Yes | `string` | Set to `azure-openai`. | | `similarity.embedding-model.model` | Yes | `string` | Azure [deployment name](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/create-resource#deploy-a-model). | | `similarity.embedding-model.endpoint` | Yes | `string` | Azure OpenAI resource URL (for example `https://my-resource.openai.azure.com/`). | | `similarity.embedding-model.api-key` | No | `string` | API key. Optional; if omitted, falls back to `DefaultAzureCredential`. | | `similarity.embedding-model.dimensions` | No | `integer` | Number of output dimensions for models that support custom dimensions. | | `similarity.embedding-model.timeout` | No | `duration` | Request timeout (ISO-8601 duration, for example `PT30S`). | **Example:** ```yaml camunda: process-test: similarity: embedding-model: provider: "azure-openai" model: "my-embedding-deployment" endpoint: "https://my-resource.openai.azure.com/" api-key: ${AZURE_OPENAI_API_KEY} ``` For local models, such as [Ollama](https://ollama.com/), or any third-party API that implements the [OpenAI embeddings format](https://platform.openai.com/docs/api-reference/embeddings). | Property | Required | Type | Description | | --------------------------------------- | -------- | ---------- | ------------------------------------------------------------------------ | | `similarity.embedding-model.provider` | Yes | `string` | Set to `openai-compatible`. | | `similarity.embedding-model.model` | Yes | `string` | Model name (for example `nomic-embed-text`). | | `similarity.embedding-model.base-url` | Yes | `string` | Base URL for the API endpoint (for example `http://localhost:11434/v1`). | | `similarity.embedding-model.api-key` | No | `string` | API key. Optional for local providers. | | `similarity.embedding-model.headers.*` | No | `map` | Custom HTTP headers. | | `similarity.embedding-model.dimensions` | No | `integer` | Number of output dimensions for models that support custom dimensions. | | `similarity.embedding-model.timeout` | No | `duration` | Request timeout (ISO-8601 duration, for example `PT30S`). | **Example (Ollama):** ```yaml camunda: process-test: similarity: embedding-model: provider: "openai-compatible" model: "nomic-embed-text" base-url: "http://localhost:11434/v1" ``` For providers not listed above, use a custom provider name and pass arbitrary properties. See [custom EmbeddingModelAdapter](#custom-embeddingmodeladapter) for implementation details. | Property | Required | Type | Description | | ------------------------------------------------ | -------- | ---------- | --------------------------------------------------------------------------------------------- | | `similarity.embedding-model.provider` | Yes | `string` | Custom provider name matching your SPI implementation. | | `similarity.embedding-model.model` | Yes | `string` | Model name. | | `similarity.embedding-model.custom-properties.*` | No | `map` | Arbitrary key-value pairs passed to SPI providers via `ProviderConfig.getCustomProperties()`. | | `similarity.embedding-model.timeout` | No | `duration` | Request timeout (ISO-8601 duration, for example `PT30S`). | **Example:** ```yaml camunda: process-test: similarity: embedding-model: provider: "my-custom-provider" model: "my-model" custom-properties: endpoint: "https://my-embeddings.example.com/v1" ``` ### Text preprocessors By default, CPT applies a set of text preprocessors to both the actual and expected values before computing embeddings. This improves stability of similarity scores by reducing noise from formatting differences. The default preprocessors are: - **Lowercase normalization**: converts text to lowercase. - **Unicode normalization**: applies Unicode NFC normalization. - **Whitespace normalization**: collapses repeated whitespace and trims leading/trailing whitespace. To disable the default preprocessors, set `similarity.default-preprocessors-enabled` to `false`: ```yaml camunda: process-test: similarity: default-preprocessors-enabled: false ``` ```properties similarity.defaultPreprocessorsEnabled=false ``` You can also configure preprocessors programmatically using `SemanticSimilarityConfig`: ```java SemanticSimilarityConfig.of(myEmbeddingAdapter, 0.7) .withoutPreprocessors(); ``` ### Custom EmbeddingModelAdapter You can provide your own `EmbeddingModelAdapter` implementation without depending on the `camunda-process-test-langchain4j` module. An `EmbeddingModelAdapter` is a functional interface that takes a string and returns a vector of floating-point numbers representing the text's semantic embedding. If you have a single `EmbeddingModelAdapter` bean and no `provider` property is set, CPT auto-detects and uses it: ```java @TestConfiguration class SimilarityTestConfig { @Bean EmbeddingModelAdapter embeddingModelAdapter() { return text -> myEmbeddingClient.embed(text); } } ``` When you have multiple beans, set `provider` to the bean name you want to use. In Spring, the bean name defaults to the method name: ```java @TestConfiguration class SimilarityTestConfig { @Bean EmbeddingModelAdapter openAiEmbeddingAdapter() { /* ... */ } @Bean EmbeddingModelAdapter ollamaEmbeddingAdapter() { /* ... */ } } ``` ```yaml camunda: process-test: similarity: embedding-model: provider: "ollamaEmbeddingAdapter" # matches the bean method name ``` :::note Resolution order When using `@CamundaSpringProcessTest`, CPT resolves the embedding adapter in the following order: 1. If a single `EmbeddingModelAdapter` bean exists and no `provider` property is configured, that bean is used automatically. 2. If the `provider` property is configured and a bean with a matching name exists, that bean is selected. 3. If no matching bean is found, CPT falls back to the built-in LangChain4j implementations, provided that `camunda-process-test-langchain4j` is on the classpath. 4. If a `provider` is configured but no matching implementation can be resolved at all, CPT throws an exception. ::: Alternatively, you can configure semantic similarity programmatically. Set the configuration globally using `CamundaAssert.setSemanticSimilarityConfig()`: ```java CamundaAssert.setSemanticSimilarityConfig( SemanticSimilarityConfig.of(text -> myEmbeddingClient.embed(text), 0.8)); ``` Implement `EmbeddingModelAdapterProvider` and register it through `META-INF/services`: ```java public class MyCustomEmbeddingProvider implements EmbeddingModelAdapterProvider { @Override public String getProviderName() { return "my-provider"; } @Override public EmbeddingModelAdapter create(ProviderConfig config) { String endpoint = config.getCustomProperties().get("endpoint"); return text -> callEndpoint(endpoint, text); } } ``` Register the provider in `META-INF/services/io.camunda.process.test.api.similarity.EmbeddingModelAdapterProvider`: ``` com.example.MyCustomEmbeddingProvider ``` Alternatively, you can configure semantic similarity programmatically. Set the configuration globally using `CamundaAssert.setSemanticSimilarityConfig()`: ```java CamundaAssert.setSemanticSimilarityConfig( SemanticSimilarityConfig.of(text -> myEmbeddingClient.embed(text), 0.8)); ``` Or register the JUnit extension manually with a semantic similarity configuration: ```java @RegisterExtension CamundaProcessTestExtension extension = new CamundaProcessTestExtension() .withSemanticSimilarityConfig( SemanticSimilarityConfig.of(text -> myEmbeddingClient.embed(text), 0.8)); ``` --- ## Connectors You can run your process test with [Connectors](/components/connectors/introduction.md) to verify the integration with external systems or the configuration of the connector tasks in your processes. For more unit-focused tests, mock the interaction; for example, by [completing connector jobs](utilities.md#mock-job-workers) with an expected result. :::note The instructions on this page are based on the default Testcontainer runtime. If you are using a remote runtime, consult the relevant distribution documentation, such as [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run/configuration.md#use-built-in-and-custom-connectors). ::: ## Enable connectors By default, the connectors are disabled. You need to change the configuration in the following way. In your `application.yml` (or `application.properties`): ```yaml camunda: process-test: # Enable connectors connectors-enabled: true ``` Or, directly on your test class: ```java @SpringBootTest(properties = {"camunda.process-test.connectors-enabled=true"}) @CamundaSpringProcessTest public class MyProcessTest { // } ``` In your `/camunda-container-runtime.properties` file: ```properties # Enable Connectors connectorsEnabled=true ``` Or, register the JUnit extension manually and use the fluent builder: ```java // No annotation: @CamundaProcessTest public class MyProcessTest { @RegisterExtension private static final CamundaProcessTestExtension EXTENSION = new CamundaProcessTestExtension() // Enable Connectors .withConnectorsEnabled(true); } ``` ## Connector secrets If you use [Connectors secrets](/components/connectors/use-connectors/index.md#using-secrets) in your processes, you can add the secrets to the test runtime in the following way. In your `application.yml` (or `application.properties`): ```yaml camunda: process-test: connectors-enabled: true connectors-secrets: GITHUB_TOKEN: ghp_secret SLACK_TOKEN: xoxb-secret ``` Or, on your test class: ```java @SpringBootTest( properties = { "camunda.process-test.connectors-enabled=true", "camunda.process-test.connectors-secrets.GITHUB_TOKEN=ghp_secret", "camunda.process-test.connectors-secrets.SLACK_TOKEN=xoxb-secret" } ) @CamundaSpringProcessTest public class MyProcessTest { // } ``` In your `/camunda-container-runtime.properties` file: ```properties connectorsEnabled=true connectorsSecrets.GITHUB_TOKEN=ghp_secret connectorsSecrets.SLACK_TOKEN=xoxb-secret ``` Or, via JUnit extension: ```java // No annotation: @CamundaProcessTest public class MyProcessTest { @RegisterExtension private static final CamundaProcessTestExtension EXTENSION = new CamundaProcessTestExtension() .withConnectorsEnabled(true) .withConnectorsSecret("GITHUB_TOKEN", "ghp_secret") .withConnectorsSecret("SLACK_TOKEN", "xoxb-secret"); } ``` ## Invoke an inbound connector You can retrieve the URL address to invoke an inbound connector in your test from the `CamundaProcessTestContext`. ```java @SpringBootTest @CamundaSpringProcessTest public class MyProcessTest { @Autowired private CamundaClient client; @Autowired private CamundaProcessTestContext processTestContext; @Test void shouldInvokeConnector() { // given: a process instance waiting at a connector event // when final String inboundConnectorAddress = processTestContext.getConnectorsAddress() + "/inbound/" + CONNECTOR_ID; // invoke the connector address, for example, via HTTP request // then: verify that the connector event is completed } } ``` ```java @CamundaProcessTest public class MyProcessTest { // to be injected private CamundaClient client; private CamundaProcessTestContext processTestContext; @Test void shouldInvokeConnector() { // given: a process instance waiting at a connector event // when final String inboundConnectorAddress = processTestContext.getConnectorsAddress() + "/inbound/" + CONNECTOR_ID; // invoke the connector address, for example, via HTTP request // then: verify that the connector event is completed } } ``` :::tip You might need to wrap the invocation of the connector in a retry loop, for example, by using [Awaitility](http://www.awaitility.org/). There can be a delay between verifying that the connectors event is active and opening the connectors inbound subscription. ::: ## Access host ports By default, the connectors run inside the Testcontainers environment in isolation and can't access your local machine. However, you can expose [host ports](https://java.testcontainers.org/features/networking/#exposing-host-ports-to-the-container) to the containers, for example, to invoke a mock HTTP server running on your local machine from an outbound REST connector. Expose the host ports using `TestContainers.exposeHostPorts(port)`. Inside the container, the local machine is available under the hostname `host.testcontainers.internal`. ```java @WireMockTest(httpPort = 9999) @SpringBootTest( properties = { "camunda.process-test.connectors-enabled=true", "camunda.process-test.connectors-secrets.BASE_URL=http://host.testcontainers.internal:9999" }) @CamundaSpringProcessTest public class MyProcessTest { @Autowired private CamundaClient client; @BeforeAll static void setup() { Testcontainers.exposeHostPorts(9999); } @Test void shouldInvokeUrlFromConnector() { // given: stub the HTTP server stubFor( get(urlPathMatching("/test")) .willReturn( aResponse() .withHeader("Content-Type", "application/json") .withStatus(200) .withBody("{\"status\":\"okay\"}"))); // when: a process instance invoked the outbound connector // then: verify the HTTP request CamundaAssert.assertThat(processInstance) .isCompleted() .hasVariable("status", "okay"); verify(getRequestedFor(urlEqualTo("/test"))); } } ``` ```java @WireMockTest(httpPort = 9999) public class MyProcessTest { @RegisterExtension private static final CamundaProcessTestExtension EXTENSION = new CamundaProcessTestExtension() .withConnectorsEnabled(true) .withConnectorsSecret("BASE_URL", "http://host.testcontainers.internal:9999"); private CamundaClient client; @BeforeAll static void setup() { Testcontainers.exposeHostPorts(9999); } @Test void shouldInvokeUrlFromConnector() { // given: stub the HTTP server stubFor( get(urlPathMatching("/test")) .willReturn( aResponse() .withHeader("Content-Type", "application/json") .withStatus(200) .withBody("{\"status\":\"okay\"}"))); // when: a process instance invoked the outbound connector // then: verify the HTTP request CamundaAssert.assertThat(processInstance) .isCompleted() .hasVariable("status", "okay"); verify(getRequestedFor(urlEqualTo("/test"))); } } ``` :::tip You can configure the URL of an outbound connector in your BPMN process using [Connectors secrets](/components/connectors/use-connectors/index.md#using-secrets) to replace it in the tests, for example, setting the URL expression to `"{{secrets.BASE_URL}}" + "/test"`. ::: ## Custom connectors By default, the runtime uses the built-in connectors bundle in the same version as the Maven module. You can change the version or use a custom connectors bundle in the following way. In your `application.yml` (or `application.properties`): ```yaml camunda: process-test: connectors-enabled: true connectors-docker-image-name: my-org/my-connectors connectors-docker-image-version: 1.0.0 ``` In your `/camunda-container-runtime.properties` file: ```properties connectorsEnabled=true connectorsDockerImageName=my-org/my-connectors connectorsDockerImageVersion=1.0.0 ``` Or, via JUnit extension: ```java // No annotation: @CamundaProcessTest public class MyProcessTest { @RegisterExtension private static final CamundaProcessTestExtension EXTENSION = new CamundaProcessTestExtension() .withConnectorsEnabled(true) .withConnectorsDockerImageName("my-org/my-connectors") .withConnectorsDockerImageVersion("1.0.0"); } ``` --- ## Camunda Process Test ## About [Camunda Process Test](https://github.com/camunda/camunda/tree/main/testing/camunda-process-test-java) (CPT) is a Java library to test your BPMN processes and your process application. CPT provides different runtimes to execute your process tests: - [Testcontainers runtime](configuration.md#testcontainers-runtime) (default) - A managed runtime based on [Testcontainers](https://java.testcontainers.org/) and Docker. - [Remote runtime](configuration.md#remote-runtime) - Your own runtime, such as, [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) :::info Public API CPT is part of the Camunda 8 [public API](/reference/public-api.md) and is covered by our SemVer stability guarantees (except for alpha features). Breaking changes will not be introduced in minor or patch releases. ::: :::note CPT is the successor to Zeebe Process Test (ZPT). Our previous testing library was removed in Camunda 8.10. See the [migration guide](/apis-tools/migration-manuals/migrate-to-camunda-process-test.md) on how to migrate your process tests. ::: ## Prerequisites - Java: - For the Camunda Java client: 8+ - For the Camunda Spring Boot Starter: 17+ - [JUnit 5](https://junit.org/junit5/) For the default [Testcontainers runtime](configuration.md#testcontainers-runtime): - A Docker-API compatible container runtime, such as Docker on Linux or Docker Desktop on Mac and Windows. ## Install CPT has two variants: - For the [Camunda Spring Boot Starter](/apis-tools/camunda-spring-boot-starter/getting-started.md) - For the [Camunda Java client](/apis-tools/java-client/getting-started.md) Choose the one depending on which library you use in your process application. Add the following dependency to your Maven project: ```xml io.camunda camunda-process-test-spring ${camunda.version} test ``` ### Spring Boot 3 support If you use the [dedicated Spring Boot 3 starter](/apis-tools/camunda-spring-boot-starter/getting-started.md#dedicated-spring-boot-3-and-4-modules) (`camunda-spring-boot-3-starter`), you must also use the dedicated Spring Boot 3 test artifact: ```xml io.camunda camunda-process-test-spring-boot-3 ${camunda.version} test ``` Add the following dependency to your Maven project: ```xml io.camunda camunda-process-test-java ${camunda.version} test ``` ## Write a test Create a new Java class with the following structure: ```java package com.example; @SpringBootTest @CamundaSpringProcessTest public class MyProcessTest { @Autowired private CamundaClient client; @Autowired private CamundaProcessTestContext processTestContext; @Test void shouldCreateProcessInstance() { // given process definition is deployed // when final ProcessInstanceEvent processInstance = client .newCreateInstanceCommand() .bpmnProcessId("my-process") .latestVersion() .send() .join(); // then CamundaAssert.assertThat(processInstance).isActive(); } } ``` - `@SpringBootTest` is the standard Spring annotation for tests. - `@CamundaSpringProcessTest` registers the Camunda test execution listener that starts and stops the test runtime. - `@Test` is the standard JUnit 5 annotation for a test case. - (_optional_) Inject a preconfigured `CamundaClient` to interact with the Camunda runtime. - (_optional_) Inject a `CamundaProcessTestContext` to interact with the test runtime. - (_optional_) Use `CamundaAssert` to verify the process instance state. The Spring test requires a Spring Boot process application in the same package. Usually, the process application [deploys the process resources](/apis-tools/camunda-spring-boot-starter/getting-started.md#deploy-process-models) using the annotation `@Deployment`. If you have no process application yet, you can add a minimal one inside the test class as follows: ```java @SpringBootApplication @Deployment(resources = "classpath*:/bpmn/**/*.bpmn") static class TestProcessApplication {} ``` ```java package com.example; @CamundaProcessTest public class MyProcessTest { // to be injected private CamundaClient client; private CamundaProcessTestContext processTestContext; @Test void shouldCreateProcessInstance() { // given client .newDeployResourceCommand() .addResourceFromClasspath("my-process.bpmn") .send() .join(); // when final ProcessInstanceEvent processInstance = client .newCreateInstanceCommand() .bpmnProcessId("my-process") .latestVersion() .send() .join(); // then CamundaAssert.assertThat(processInstance).isActive(); } } ``` - `@CamundaProcessTest` registers the Camunda JUnit extension that starts and stops the test runtime. - `@Test` is the standard JUnit 5 annotation for a test case. - (_optional_) Get a preconfigured `CamundaClient` injected to interact with the Camunda runtime. - (_optional_) Get a `CamundaProcessTestContext` injected to interact with the test runtime. - (_optional_) Use `CamundaAssert` to verify the process instance state. :::tip Shared runtime If you use the same runtime configuration for all test classes, you can use a [shared runtime](configuration.md#shared-runtime) to speed up the test execution. ::: ### Deploy resources You can deploy additional BPMN processes and other resources by adding the annotation `@TestDeployment` on the test class or the method. An annotation on the test method takes precedence over an annotation on the test class. The resources are loaded from the root classpath of the test. ```java @Test @TestDeployment(resources = "my-process.bpmn") void shouldCreateProcessInstance() { // the given resources are deployed before running the test } ``` ## Test lifecycle CPT performs the following actions during the JUnit 5 lifecycle when running a test class: - `beforeAll` (test methods) - Start the test runtime - `beforeEach` (test method) - Inject the `CamundaClient`, the `CamundaProcessTestContext`, and the `TestScenarioRunner` - Publish the client created event for the Spring Boot process application to trigger the deployment and start job workers - Deploy resources defined via `@TestDeployment` - `afterEach` (test method) - Collect the data for the coverage report - Print the created process instances if the test failed - Close the client connections - Publish the client closed event for the Spring Boot process application to stop job workers - Reset the Camunda runtime clock - Delete all data in the Camunda runtime - `afterAll` (test methods) - Generate the coverage report - Stop the test runtime ### Limitations CPT doesn't support Spring Boot process applications with `@PostConstruct` methods or a `CommandLineRunner` implementation. These methods are executed when the test class is initialized, but not before each test method. We recommend to use a minimal configuration for the test instead of the Spring Boot process application and invoke the `@PostConstruct` or `run()` methods manually before each test method. ```java @SpringBootTest(classes = {TestProcessApplication.class}) @CamundaSpringProcessTest public class ProcessTest { @Autowired private CamundaClient client; @BeforeEach void invokeProcessApplication() throws Exception { final Application springBootApplication = new Application(); springBootApplication.setCamundaClient(client); // call the @PostConstruct methods springBootApplication.afterStarted(); // call the CommandLineRunner method springBootApplication.run(); } } ``` Minimal test configuration: ```java // must be in a different package than the Spring Boot application package org.example.test; @SpringBootApplication( // list all required packages for the process test, such as job workers scanBasePackages = {"org.example.services", "org.example.workers"} ) @Deployment(resources = "classpath*:/bpmn/**/*.bpmn") public class TestProcessApplication {} ``` - `beforeAll` (test methods) - Start the test runtime - `beforeEach` (test method) - Inject the `CamundaClient`, the `CamundaProcessTestContext`, and the `TestScenarioRunner` - Deploy resources defined via `@TestDeployment` - `afterEach` (test method) - Collect the data for the coverage report - Print the created process instances if the test failed - Close the client connections - Reset the Camunda runtime clock - Delete all data in the Camunda runtime - `afterAll` (test methods) - Generate the coverage report - Stop the test runtime ## Next steps Learn more about the following topics: - `CamundaAssert` and [assertions](assertions.md) - `CamundaProcessTestContext` and [utilities](utilities.md) - How to [configure the runtime](configuration.md) - How to [test AI agent processes](/components/agentic-orchestration/evaluate-agents/test-ai-agents.md) - Best practices for [writing process tests](/components/best-practices/development/testing-process-definitions.md) Refer to the [API documentation](https://javadoc.io/doc/io.camunda/camunda-process-test-java/latest/io/camunda/process/test/api/package-summary.html) for details. ## Examples Take a look at the example project on [GitHub](https://github.com/camunda/camunda/tree/main/testing/camunda-process-test-example). This demonstrates the usage of the library for a demo Spring Boot process application. ## Process Test Coverage After a test run, CPT prints the coverage of your BPMN processes to the log and generates a detailed HTML and JSON report. You can use the report to identify untested paths in your processes and to increase your test coverage. A link to the HTML report is printed in the log: ``` Process coverage: io.camunda.InvoiceApprovalTest ======================== - Process_InvoiceApproval: 100% Coverage report: file:///my/home/projects/my-process-application/target/coverage-report/report.html ``` ![An example process test coverage HTML report](assets/process-coverage-report.png) --- ## JSON test cases You can write your process tests in JSON format instead of coding the test logic in Java. The JSON file describes test cases with instructions that align with CPT's assertions and utilities. CPT's JSON test cases use the same schema as [test scenario files in Play](/components/hub/workspace/modeler/validation/test-scenario-files.md), so you can edit the same files in Play and execute them with CPT. ## Write a JSON test case The JSON format is defined in the [JSON schema](https://camunda.com/json-schema/cpt-test-cases/8.9/schema.json). It defines the following structure: - `testCases`: An array of test cases to be executed. - `name`: The name of the test case. - `description`: A description of the test case. - `instructions`: An array of [instructions](#reference-instructions) to execute the test case. - Each instruction has a `type` that defines the action to be performed (for example, `CREATE_PROCESS_INSTANCE`). - Additional properties depend on the instruction type (for example, process definition ID and variables). How to start: 1. Create a new JSON file in your test resources folder (for example, `src/test/resources/test-cases/invoice-approval.json`) 2. Refer to the JSON schema `https://camunda.com/json-schema/cpt-test-cases/8.9/schema.json` in the `$schema` property. Use the same schema version as the CPT version you are using to ensure compatibility. 3. Add your test cases and use the [available instructions](#reference-instructions) to define the behavior of your process test. The basic structure of the JSON file looks like this: ```JSON { "$schema": "https://camunda.com/json-schema/cpt-test-cases/8.9/schema.json", "testCases": [ { "name": "My first test case", "description": "A human-readable description of the test case.", "instructions": [ ] } ] } ``` You can find a full example of a JSON test case file in the [Examples](#examples) section below. :::tip Use AI to support the generation of your JSON files. Refer to the documentation, provide a description of your test case, and your BPMN processes to get a first draft of your test cases. Or, use an IDE with JSON schema support to get auto-completion and validation while writing your test cases, for example [IntelliJ IDEA](https://www.jetbrains.com/help/idea/json.html#ws_json_schema_add_custom). ::: ## Run a JSON test case You can run your JSON test case files as parameterized JUnit tests. Add the `@TestCaseSource` annotation to your test method to read the files and provide test cases as arguments. Then, execute the test cases using the `TestCaseRunner` provided by CPT. The runner executes the test case instructions by leveraging CPT's assertions and utilities. If an assertion instruction fails, the runner throws an assertion error, causing the test to fail. If all instructions pass, the test case is considered successful. ```java @SpringBootTest @CamundaSpringProcessTest public class MyProcessTest { @Autowired private TestCaseRunner testCaseRunner; @ParameterizedTest @TestCaseSource void shouldPass(final TestCase testCase, final String fileName) { // given: the process definitions are deployed // when/then: run and verify the test case testCaseRunner.run(testCase); } } ``` ```java @CamundaProcessTest public class MyProcessTest { private TestCaseRunner testCaseRunner; @ParameterizedTest @TestCaseSource void shouldPass(final TestCase testCase, final String fileName) { // given: the process definitions are deployed // when/then: run and verify the test case testCaseRunner.run(testCase); } } ``` You can set the following fields in the `@TestCaseSource` annotation to configure which files to load: - `directory`: The classpath directory to scan for JSON test case files. Defaults to `/test-cases`. - `fileNames`: An array of specific file names to load from the directory. If not set, all files in the directory are loaded. - `fileExtension`: The file extension to filter files in the directory. Defaults to `json`. The filter is ignored if `fileNames` is set. ### Connect your process application The `TestCaseRunner` integrates seamlessly with CPT's [test lifecycle](getting-started.md#test-lifecycle). It connects to your process application and starts job workers, if enabled. You can add additional steps before and after running the test case, for example, to deploy additional resources or to mock external services of your process application. ```java @SpringBootTest @CamundaSpringProcessTest public class MyProcessTest { @Autowired private CamundaClient client; @Autowired private CamundaProcessTestContext processTestContext; @Autowired private TestCaseRunner testCaseRunner; @MockitoBean private AccountingService accountingService; @ParameterizedTest @TestCaseSource void shouldPass(final TestCase testCase, final String fileName) { // given: the process definitions are deployed via @Deployment on the process application // optionally: set up mocks, job workers, etc. // when/then: run and verify the test case testCaseRunner.run(testCase); // optionally: verify mock invocations, external resources, etc. Mockito.verify(accountingService).addInvoiceToAccount("0815", "INV-1001"); } } ``` ```java @CamundaProcessTest @ExtendWith(MockitoExtension.class) public class MyProcessTest { private CamundaClient client; private CamundaProcessTestContext processTestContext; private TestCaseRunner testCaseRunner; // Inject the mock in the process application @Mock private AccountingService accountingService; @ParameterizedTest @TestCaseSource @TestDeployment(resources = "invoice-approval.bpmn") void shouldPass(final TestCase testCase, final String fileName) { // given: the process definitions are deployed via @TestDeployment // optionally: set up mocks, job workers, etc. // when/then: run and verify the test case testCaseRunner.run(testCase); // optionally: verify mock invocations, external resources, etc. Mockito.verify(accountingService).addInvoiceToAccount("0815", "INV-1001"); } } ``` ## Examples You can find some example process tests using JSON test cases on [GitHub](https://github.com/camunda/camunda/tree/main/testing/camunda-process-test-example), like the following one: ```json reference referenceLinkText="Source" title="Invoice Approval JSON test case" https://github.com/camunda/camunda/blob/stable/8.9/testing/camunda-process-test-example/src/test/resources/test-cases/invoice-approval.json ``` ## Reference: Instructions Instructions define the actions and assertions to be performed in a test case. Each instruction has a `type` property that identifies the instruction, along with additional properties specific to that instruction type. ### ASSERT_DECISION An instruction to assert the evaluation of a decision. See the [assertions documentation](assertions.md#decision-assertions) for more details. Property Description Type Required Default type Instruction type, must be "ASSERT_DECISION" string Yes decisionSelector The selector to identify the decision. DecisionSelector Yes output Expected output of the decision (any JSON type) any No matchedRules Expected matched rule indexes array of integer No notMatchedRules Expected not matched rule indexes array of integer No noMatchedRules Assert that no rules were matched boolean No false Example: ```json { "type": "ASSERT_DECISION", "decisionSelector": { "decisionDefinitionId": "ChooseRocket" }, "output": { "rocket": "Ariane 6" }, "matchedRules": [3] } ``` ### ASSERT_ELEMENT_INSTANCE An instruction to assert the state of an element instance. See the [assertions documentation](assertions.md#element-instance-assertions) for more details. Property Description Type Required Default type Instruction type, must be "ASSERT_ELEMENT_INSTANCE" string Yes processInstanceSelector The selector to identify the process instance. ProcessInstanceSelector Yes elementSelector The selector to identify the element. ElementSelector Yes state The expected state of the element instance. enum: IS_ACTIVE, IS_COMPLETED, IS_TERMINATED Yes amount The expected amount of element instances in the given state. integer (minimum: 1) No 1 Example: ```json { "type": "ASSERT_ELEMENT_INSTANCE", "processInstanceSelector": { "processDefinitionId": "MoonExplorationProcess" }, "elementSelector": { "elementId": "LaunchRocket" }, "state": "IS_COMPLETED" } ``` ### ASSERT_ELEMENT_INSTANCES An instruction to assert the state of multiple element instances. See the [assertions documentation](assertions.md#element-instance-assertions) for more details. Property Description Type Required Default type Instruction type, must be "ASSERT_ELEMENT_INSTANCES" string Yes processInstanceSelector The selector to identify the process instance. ProcessInstanceSelector Yes elementSelectors The selectors to identify the elements. array of ElementSelector Yes state The expected state of the element instances. enum: IS_ACTIVE, IS_COMPLETED, IS_TERMINATED, IS_NOT_ACTIVE, IS_NOT_ACTIVATED, IS_ACTIVE_EXACTLY, IS_COMPLETED_IN_ORDER Yes Example: ```json { "type": "ASSERT_ELEMENT_INSTANCES", "processInstanceSelector": { "processDefinitionId": "MoonExplorationProcess" }, "elementSelectors": [ { "elementId": "PrepareMission" }, { "elementId": "LaunchRocket" }, { "elementId": "LandOnMoon" } ], "state": "IS_COMPLETED_IN_ORDER" } ``` ### ASSERT_PROCESS_INSTANCE An instruction to assert the state of a process instance. See the [assertions documentation](assertions.md#process-instance-assertions) for more details. Property Description Type Required Default type Instruction type, must be "ASSERT_PROCESS_INSTANCE" string Yes processInstanceSelector The selector to identify the process instance. ProcessInstanceSelector Yes state The expected state of the process instance. enum: IS_ACTIVE, IS_COMPLETED, IS_CREATED, IS_TERMINATED No hasActiveIncidents Whether the process instance has active incidents. boolean No Example: ```json { "type": "ASSERT_PROCESS_INSTANCE", "processInstanceSelector": { "processDefinitionId": "MoonExplorationProcess" }, "state": "IS_COMPLETED" } ``` ### ASSERT_PROCESS_INSTANCE_MESSAGE_SUBSCRIPTION An instruction to assert the state of a process instance message subscription. See the [assertions documentation](assertions.md#process-instance-message-assertions) for more details. Property Description Type Required Default type Instruction type, must be "ASSERT_PROCESS_INSTANCE_MESSAGE_SUBSCRIPTION" string Yes processInstanceSelector The selector to identify the process instance. ProcessInstanceSelector Yes messageSelector The selector to identify the message. MessageSelector Yes state The expected state of the message subscription. enum: IS_WAITING, IS_NOT_WAITING, IS_CORRELATED Yes Example: ```json { "type": "ASSERT_PROCESS_INSTANCE_MESSAGE_SUBSCRIPTION", "processInstanceSelector": { "processDefinitionId": "MoonExplorationProcess" }, "messageSelector": { "messageName": "AstronautReady" }, "state": "IS_CORRELATED" } ``` ### ASSERT_USER_TASK An instruction to assert the state of a user task. See the [assertions documentation](assertions.md#user-task-assertions) for more details. Property Description Type Required Default type Instruction type, must be "ASSERT_USER_TASK" string Yes userTaskSelector The selector to identify the user task. UserTaskSelector Yes state The expected state of the user task. enum: IS_CREATED, IS_COMPLETED, IS_CANCELED, IS_FAILED No assignee The expected assignee of the user task. string No candidateGroups The expected candidate groups of the user task. array of string No priority The expected priority of the user task. integer No elementId The expected element ID of the user task. string No name The expected name of the user task. string No dueDate The expected due date of the user task in ISO-8601 format. string No followUpDate The expected follow-up date of the user task in ISO-8601 format. string No Example: ```json { "type": "ASSERT_USER_TASK", "userTaskSelector": { "elementId": "ReviewMissionPlan" }, "state": "IS_CREATED", "assignee": "zee-astronaut", "priority": 100 } ``` ### ASSERT_VARIABLES An instruction to assert the variables of a process instance. See the [assertions documentation](assertions.md#variable-assertions) for more details. Property Description Type Required Default type Instruction type, must be "ASSERT_VARIABLES" string Yes processInstanceSelector The selector to identify the process instance. ProcessInstanceSelector Yes elementSelector The selector to identify the element for local variables. ElementSelector No variableNames The expected variable names. array of string No variables The expected variables with their values. object No Example: ```json { "type": "ASSERT_VARIABLES", "processInstanceSelector": { "processDefinitionId": "MoonExplorationProcess" }, "variables": { "missionStatus": "completed", "astronautName": "Zee" } } ``` ### BROADCAST_SIGNAL An instruction to broadcast a signal. Property Description Type Required Default type Instruction type, must be "BROADCAST_SIGNAL" string Yes signalName The name of the signal to broadcast. string Yes variables The variables to broadcast with the signal. object No Example: ```json { "type": "BROADCAST_SIGNAL", "signalName": "EmergencyEvacuation", "variables": { "reason": "meteor-shower", "destination": "space-station" } } ``` ### COMPLETE_JOB An instruction to complete a job. See the [utilities documentation](utilities.md#complete-jobs) for more details. Property Description Type Required Default type Instruction type, must be "COMPLETE_JOB" string Yes jobSelector The selector to identify the job to complete. JobSelector Yes variables The variables to complete the job with. object No useExampleData Whether to complete the job with example data from the BPMN element. This property has precedence over variables. boolean No false Example: ```json { "type": "COMPLETE_JOB", "jobSelector": { "jobType": "analyze-moon-samples" }, "variables": { "analysisResult": "high-mineral-content" } } ``` ### COMPLETE_JOB_AD_HOC_SUB_PROCESS An instruction to complete a job of an ad-hoc sub-process. See the [utilities documentation](utilities.md#ad-hoc-sub-process-jobs) for more details. Property Description Type Required Default type Instruction type, must be "COMPLETE_JOB_AD_HOC_SUB_PROCESS" string Yes jobSelector The selector to identify the job to complete. JobSelector Yes variables The variables to complete the job with. object No activateElements The elements to activate in the ad-hoc sub-process. array of ActivateElementInstruction No cancelRemainingInstances Whether to cancel remaining instances of the ad-hoc sub-process. boolean No false completionConditionFulfilled Whether the completion condition of the ad-hoc sub-process is fulfilled. boolean No false #### Activate Element Instruction An instruction to activate an element in an ad-hoc sub-process. Property Description Type Required elementId The ID of the element to activate. string Yes variables The variables to set when activating the element. object No Example: ```json { "type": "COMPLETE_JOB_AD_HOC_SUB_PROCESS", "jobSelector": { "elementId": "conduct-experiment" }, "variables": { "experimentResult": "success" }, "activateElements": [ { "elementId": "CollectMoonSamples", "variables": { "sampleType": "regolith" } } ] } ``` ### COMPLETE_JOB_USER_TASK_LISTENER An instruction to complete a job of a user task listener. See the [utilities documentation](utilities.md#user-task-listener-jobs) for more details. Property Description Type Required Default type Instruction type, must be "COMPLETE_JOB_USER_TASK_LISTENER" string Yes jobSelector The selector to identify the job to complete. JobSelector Yes denied Whether the worker denies the work. boolean No false deniedReason The reason for denying the job. string No corrections The corrections to apply to the user task. Only applicable if denied is false. UserTaskCorrections No #### User Task Corrections The corrections to apply to a user task. Property Description Type Required assignee The assignee of the task. string No dueDate The due date of the task. string No followUpDate The follow up date of the task. string No candidateUsers The candidate users of the task. array of string No candidateGroups The candidate groups of the task. array of string No priority The priority of the task. integer No Example: ```json { "type": "COMPLETE_JOB_USER_TASK_LISTENER", "jobSelector": { "jobType": "validate-astronaut-assignment" }, "corrections": { "assignee": "zee-senior-astronaut", "priority": 50 } } ``` ### COMPLETE_USER_TASK An instruction to complete a user task. See the [utilities documentation](utilities.md#complete-user-tasks) for more details. Property Description Type Required Default type Instruction type, must be "COMPLETE_USER_TASK" string Yes userTaskSelector The selector to identify the user task to complete. UserTaskSelector Yes variables The variables to set when completing the user task. Ignored if useExampleData is true. object No useExampleData Whether to complete the user task with example data from the BPMN element. If true, the variables property is ignored. boolean No false Example: ```json { "type": "COMPLETE_USER_TASK", "userTaskSelector": { "elementId": "ReviewMissionPlan" }, "variables": { "approved": true, "comments": "Mission plan looks good for moon exploration" } } ``` ### CORRELATE_MESSAGE An instruction to correlate a message. Property Description Type Required Default type Instruction type, must be "CORRELATE_MESSAGE" string Yes name The name of the message. string Yes correlationKey The correlation key of the message. string No variables The variables to correlate with the message. object No Example: ```json { "type": "CORRELATE_MESSAGE", "name": "AstronautReady", "correlationKey": "mission-001", "variables": { "astronautName": "Zee", "status": "ready-for-launch" } } ``` ### CREATE_PROCESS_INSTANCE An instruction to create a new process instance. Property Description Type Required Default type Instruction type, must be "CREATE_PROCESS_INSTANCE" string Yes processDefinitionSelector The selector to identify the process definition to create the process instance for. ProcessDefinitionSelector Yes variables The variables to create the process instance with. object No startInstructions The instructions to execute when starting the process instance. array of StartInstruction No runtimeInstructions The instructions to affect the runtime behavior of the process instance. array of RuntimeInstruction No #### Start Instruction An instruction to execute when starting a process instance. Property Description Type Required elementId The ID of the element to start the process instance at. string Yes #### Runtime Instruction An instruction to affect the runtime behavior of a process instance. Property Description Type Required type The type of the runtime instruction. Currently supports "TERMINATE_PROCESS_INSTANCE". string Yes afterElementId The ID of the element after which to terminate the process instance. Required when type is "TERMINATE_PROCESS_INSTANCE". string Yes Example: ```json { "type": "CREATE_PROCESS_INSTANCE", "processDefinitionSelector": { "processDefinitionId": "MoonExplorationProcess" }, "variables": { "missionName": "Artemis-Zee", "destination": "Moon", "astronautCount": 4 } } ``` ### EVALUATE_CONDITIONAL_START_EVENT An instruction to evaluate conditional start events. Property Description Type Required Default type Instruction type, must be "EVALUATE_CONDITIONAL_START_EVENT" string Yes variables The variables to evaluate the conditional start events with. object Yes Example: ```json { "type": "EVALUATE_CONDITIONAL_START_EVENT", "variables": { "weatherCondition": "clear", "fuelLevel": 100 } } ``` ### EVALUATE_DECISION An instruction to evaluate a DMN decision. Property Description Type Required Default type Instruction type, must be "EVALUATE_DECISION" string Yes decisionDefinitionSelector The selector to identify the decision definition to evaluate. DecisionDefinitionSelector Yes variables The variables to evaluate the decision with. object No Example: ```json { "type": "EVALUATE_DECISION", "decisionDefinitionSelector": { "decisionDefinitionId": "ChooseRocket" }, "variables": { "payload": 5000, "destination": "Moon" } } ``` ### INCREASE_TIME An instruction to increase the time. See the [utilities documentation](utilities.md#increase-time) for more details. Property Description Type Required Default type Instruction type, must be "INCREASE_TIME" string Yes duration The duration to increase the time by, in ISO 8601 duration format (for example, "PT1H", "P2D"). string Yes Example: ```json { "type": "INCREASE_TIME", "duration": "P3D" } ``` ### MOCK_CHILD_PROCESS An instruction to mock a child process. See the [utilities documentation](utilities.md#mock-child-processes) for more details. Property Description Type Required Default type Instruction type, must be "MOCK_CHILD_PROCESS" string Yes processDefinitionId The ID of the child process to mock. string Yes variables The variables to set for the mocked child process. object No Example: ```json { "type": "MOCK_CHILD_PROCESS", "processDefinitionId": "AstronautTrainingProcess", "variables": { "trainingCompleted": true, "grade": "excellent" } } ``` ### MOCK_DMN_DECISION An instruction to mock a DMN decision. See the [utilities documentation](utilities.md#mock-dmn-decisions) for more details. Property Description Type Required Default type Instruction type, must be "MOCK_DMN_DECISION" string Yes decisionDefinitionId The decision definition ID to mock. string Yes variables The variables to set as the decision output. object No Example: ```json { "type": "MOCK_DMN_DECISION", "decisionDefinitionId": "ChooseRocket", "variables": { "rocket": "Falcon Heavy" } } ``` ### MOCK_JOB_WORKER_COMPLETE_JOB An instruction to mock a job worker who completes jobs. See the [utilities documentation](utilities.md#complete-job) for more details. Property Description Type Required Default type Instruction type, must be "MOCK_JOB_WORKER_COMPLETE_JOB" string Yes jobType The job type to mock. This should match the zeebeJobType in the BPMN model. string Yes variables The variables to complete the job with. object No useExampleData Whether to use example data from the BPMN element. If true, the variables property is ignored. boolean No false Example: ```json { "type": "MOCK_JOB_WORKER_COMPLETE_JOB", "jobType": "calculate-trajectory", "variables": { "trajectory": "optimal", "fuelConsumption": 450 } } ``` ### MOCK_JOB_WORKER_THROW_BPMN_ERROR An instruction to mock a job worker who throws BPMN errors. See the [utilities documentation](utilities.md#throw-bpmn-error) for more details. Property Description Type Required Default type Instruction type, must be "MOCK_JOB_WORKER_THROW_BPMN_ERROR" string Yes jobType The job type to mock. This should match the zeebeJobType in the BPMN model. string Yes errorCode The error code to throw. This should match the error code in an error catch event. string Yes errorMessage The error message to include when throwing the error. string No variables The variables to include when throwing the error. object No Example: ```json { "type": "MOCK_JOB_WORKER_THROW_BPMN_ERROR", "jobType": "launch-rocket", "errorCode": "WEATHER_UNSUITABLE", "errorMessage": "High winds detected" } ``` ### PUBLISH_MESSAGE An instruction to publish a message. Property Description Type Required Default type Instruction type, must be "PUBLISH_MESSAGE" string Yes name The name of the message. string Yes correlationKey The correlation key of the message. string No variables The variables to publish with the message. object No timeToLive The time-to-live of the message in milliseconds. integer No messageId The message ID for uniqueness. string No Example: ```json { "type": "PUBLISH_MESSAGE", "name": "LaunchApproved", "correlationKey": "mission-001", "variables": { "approvedBy": "mission-control", "launchWindow": "2026-03-15T10:00:00Z" } } ``` ### RESOLVE_INCIDENT An instruction to resolve an incident. See the [utilities documentation](utilities.md#resolve-incidents) for more details. Property Description Type Required Default type Instruction type, must be "RESOLVE_INCIDENT" string Yes incidentSelector The selector to identify the incident to resolve. IncidentSelector Yes Example: ```json { "type": "RESOLVE_INCIDENT", "incidentSelector": { "elementId": "LaunchRocket" } } ``` ### SET_TIME An instruction to set the time. See the [utilities documentation](utilities.md#set-time) for more details. Property Description Type Required Default type Instruction type, must be "SET_TIME" string Yes time The time to set, in ISO 8601 instant format (for example, "2026-01-19T13:00:00Z"). string Yes Example: ```json { "type": "SET_TIME", "time": "2026-03-15T10:00:00Z" } ``` ### THROW_BPMN_ERROR_FROM_JOB An instruction to throw a BPMN error from a job. See the [utilities documentation](utilities.md#throw-bpmn-errors-from-jobs) for more details. Property Description Type Required Default type Instruction type, must be "THROW_BPMN_ERROR_FROM_JOB" string Yes jobSelector The selector to identify the job to throw the error from. JobSelector Yes errorCode The error code to throw. string Yes errorMessage The error message to throw. string No variables The variables to set when throwing the error. object No Example: ```json { "type": "THROW_BPMN_ERROR_FROM_JOB", "jobSelector": { "jobType": "deploy-satellite" }, "errorCode": "DEPLOYMENT_FAILED", "errorMessage": "Insufficient orbital velocity" } ``` ### UPDATE_VARIABLES An instruction to create or update process instance variables. See the [utilities documentation](utilities.md#update-variables) for more details. Property Description Type Required Default type Instruction type, must be "UPDATE_VARIABLES" string Yes processInstanceSelector The selector to identify the process instance. ProcessInstanceSelector Yes variables The variables to create or update. object Yes elementSelector The selector to identify the element for local variables. ElementSelector No Example: ```json { "type": "UPDATE_VARIABLES", "processInstanceSelector": { "processDefinitionId": "MoonExplorationProcess" }, "variables": { "currentPhase": "landing", "fuelRemaining": 75 } } ``` ## Reference: Selectors Selectors are used to identify specific resources in your process tests. Each selector must contain at least one of the specified properties. ### Decision Definition Selector A selector to identify a decision definition. Property Description Type Required decisionDefinitionId ID of the decision definition string Yes Example: ```json { "decisionDefinitionId": "ChooseRocket" } ``` ### Decision Selector A selector to identify a decision. The selector must contain at least one of the following properties: Property Description Type Required decisionDefinitionId ID of the decision definition string No decisionDefinitionName Name of the decision definition string No Example: ```json { "decisionDefinitionId": "ChooseRocket" } ``` ### Element Selector A selector to identify a BPMN element. The selector must contain at least one of the following properties: Property Description Type Required elementId ID of the BPMN element string No elementName Name of the BPMN element string No Example: ```json { "elementId": "LaunchRocket" } ``` ### Incident Selector A selector to identify an incident. The selector must contain at least one of the following properties: Property Description Type Required elementId ID of the BPMN element where the incident occurred string No processDefinitionId Process definition ID of the incident string No Example: ```json { "elementId": "LaunchRocket" } ``` ### Job Selector A selector to identify a job. The selector must contain at least one of the following properties: Property Description Type Required jobType Type of the job string No elementId ID of the BPMN element string No processDefinitionId Process definition ID of the job string No Example: ```json { "jobType": "analyze-moon-samples" } ``` ### Message Selector A selector to identify a message. Property Description Type Required messageName Name of the message string Yes correlationKey Correlation key of the message string No Example: ```json { "messageName": "AstronautReady" } ``` ### Process Definition Selector A selector to identify a process definition. Property Description Type Required processDefinitionId ID of the process definition string Yes Example: ```json { "processDefinitionId": "MoonExplorationProcess" } ``` ### Process Instance Selector A selector to identify a process instance. Property Description Type Required processDefinitionId Process definition ID of the process instance string Yes ```json { "processDefinitionId": "MoonExplorationProcess" } ``` ### User Task Selector A selector to identify a user task. The selector must contain at least one of the following properties: Property Description Type Required elementId ID of the BPMN element string No taskName Name of the user task string No processDefinitionId Process definition ID of the user task string No Example: ```json { "elementId": "ReviewMissionPlan" } ``` --- ## Utilities There are different utilities that can help you to write your process test. ## Manipulate the clock The Camunda runtime uses an internal clock to execute process instances and to calculate when a BPMN timer event is due. In a test, you can use `CamundaProcessTestContext` to manipulate the clock. When to use it: - Trigger an active BPMN timer event - Test scenarios that require a specific date or time, for example, a leap year :::tip If you trigger a BPMN timer event, you should assert that the BPMN timer event is active before manipulating the clock. Otherwise, you may manipulate the clock too early and the BPMN timer event is not triggered. ::: ### Increase time You can increase the time by a given duration. As a result, the clock is moved forward (i.e., in the future). ```java @Test void shouldTriggerTimerEvent() { // given: a process instance waiting at a BPMN timer event // when assertThat(processInstance).hasActiveElements("wait_2_days"); processTestContext.increaseTime(Duration.ofDays(2)); // then assertThat(processInstance).hasCompletedElements("wait_2_days"); } ``` ### Set time You can set the clock to a given date and time. ```java @Test void shouldCreateProcessInstanceInTheMorning() { // given processTestContext.setTime(Instant.parse("2025-10-01T08:00:00Z")); // when: create a process instance // then: verify the behavior at the given time } ``` ## Mock job workers You can mock a job worker to simulate its behavior without invoking the actual worker. The mock handles all jobs of the given job type. When to use it: - Test the process in isolation from the actual job workers - Simulate different outcomes of a job worker (success, BPMN error) - Mock disabled job workers or Connectors :::tip If you start the process application in your test case, you should [disable the job workers](../camunda-spring-boot-starter/configuration.md#disable-a-job-worker) to avoid interferences with the mocks, for example, by setting the following configuration: ```java @SpringBootTest(properties = {"camunda.client.worker.defaults.enabled=false"}) @CamundaSpringProcessTest class MyProcessTest { .. } ``` ::: ### Complete job The mock completes jobs with/without variables. ```java @Test void shouldCompleteJob() { // given: mock job worker for the job type "send-email" // 1) Complete jobs without variables processTestContext.mockJobWorker("send-email").thenComplete(); // 2) Complete jobs with variables final Map variables = Map.of( "emailSent", true, "timestamp", "2024-01-01T10:00:00Z" ); processTestContext.mockJobWorker("send-email").thenComplete(variables); // when: create a process instance // then: verify that the process instance completed all tasks } ``` ### Complete with example data The mock completes jobs with [example data](/components/modeler/data-handling.md#defining-example-data) that is defined at the related BPMN element. If the BPMN element has no example data, the mock completes the job without variables. ```java @Test void shouldCompleteJobWithExampleData() { // given: mock job worker for the job type "fetch-weather-data" processTestContext.mockJobWorker("fetch-weather-data").thenCompleteWithExampleData(); // when: create a process instance // then: verify that the process instance completed all tasks } ``` :::tip Add example data during modeling to provide context and make writing FEEL expressions easier. By using the same example data for mocks, you keep the data in the BPMN process itself and avoid repeating them in the process tests. This can simplify your tests and reducing the maintenance effort. ::: ### Throw BPMN error The mock throws BPMN errors for jobs with the given error code and optional error message and variables. ```java @Test void shouldThrowBpmnError() { // given: mock job worker for the job type "validate-order" // 1) Throw BPMN errors with error code "INVALID_ORDER" processTestContext.mockJobWorker("validate-order").thenThrowBpmnError("INVALID_ORDER"); // 2) Throw BPMN errors with error code "INVALID_ORDER" and variables final Map variables = Map.of( "reason", "The order exceeds the item limit." ); processTestContext .mockJobWorker("validate-order") .thenThrowBpmnError("INVALID_ORDER", variables); // 3) Throw BPMN errors with error code, error message, and variables processTestContext .mockJobWorker("validate-order") .thenThrowBpmnError("INVALID_ORDER", "Order validation failed", variables); // when: create a process instance // then: verify that the process instance handled the BPMN error } ``` ### Custom handler You can implement a custom handler to mock more complex behaviors. ```java @Test void shouldUseCustomHandler() { // given: mock job worker for the job type "calculate-discount" processTestContext .mockJobWorker("calculate-discount") .withHandler( (jobClient, job) -> { final Map variables = job.getVariablesAsMap(); final double orderAmount = (double) variables.get("orderAmount"); final double discount = orderAmount > 100 ? 0.1 : 0.0; jobClient.newCompleteCommand(job).variable("discount", discount).send().join(); }); // when: create a process instance // then: verify that the process instance has the expected variables } ``` ### Inspect mock invocations You can inspect the invocations of a mock job worker to verify how many jobs were handled and to get the details of each job. ```java @Test void shouldInspectMockInvocations() { // given: mock job worker for the job type "send-email" final JobWorkerMock mockJobWorker = processTestContext.mockJobWorker("send-email").thenComplete(); // when: create a process instance that triggers the job worker // then: verify the number of invocations assertThat(mockJobWorker.getInvocations()).isEqualTo(1); // and: inspect the details of each invocation assertThat(mockJobWorker.getActivatedJobs()) .hasSize(1) .flatExtracting(job -> job.getVariablesAsMap().entrySet()) .contains(entry("receiver", "Zee"), entry("subject", "Greetings")); } ``` ## Mock child processes You can mock a child process for a call activity to simulate its output without executing the actual child process. The mock deploys a dummy process with the given process ID that returns the given variables. When to use it: - Test the parent process in isolation from the actual child process - Simulate different outcomes of a child process - Mock a non-existing child process ```java @Test void shouldMockChildProcess() { // given: mock child process with the process ID "payment-process" // 1) Complete the child process without variables processTestContext.mockChildProcess("payment-process"); // 2) Complete the child process with variables final Map variables = Map.of( "paymentStatus", "completed", "transactionId", "TXN-12345" ); processTestContext.mockChildProcess("payment-process", variables); // when: create a process instance // then: verify that the process instance completed the call activity } ``` ### Child process with dynamic variables You can mock a child process with dynamic behavior whose output variables are derived from the parent process instance. The handler receives the parent variables and returns the child process variables. ```java @Test void shouldMockChildProcess() { // given: mock dynamic child process with the process ID "AstronautTrainingProcess" processTestContext.mockChildProcess( "AstronautTrainingProcess", parentVariables -> { final String astronautName = (String) parentVariables.get("astronautName"); final String grade = "Zee".equals(astronautName) ? "excellent" : "good"; return Map.of( "trainingCompleted", true, "grade", grade); }); // when: create a process instance // then: verify that the process instance completed the call activity } ``` ## Mock DMN decisions You can mock a DMN decision for a business rule task to simulate its output without evaluating the actual DMN decision. The mock deploys a dummy DMN decision with the given decision ID that returns the given variables. When to use it: - Test the process with the business rule task in isolation from the actual DMN decision - Simulate different outcomes of a DMN decision - Mock a non-existing DMN decision ```java @Test void shouldMockDmnDecision() { // given: mock DMN decision with the decision ID "credit-check-decision" final Map variables = Map.of( "approved", true, "riskLevel", "low", "creditLimit", 5000 ); processTestContext.mockDmnDecision("credit-check-decision", variables); // when: create a process instance // then: verify that the process instance completed the business rule task } ``` ## Conditional behavior The `when(condition).then(action)` API on `CamundaProcessTestContext` registers background behaviors that react to process state changes without blocking the test thread. This is useful for non-deterministic flows where the execution order is unknown. You can register multiple behaviors before starting the process, and they will react independently as the process progresses. Behaviors are cleared automatically after each test. ```java @Test void shouldCompleteTaskAutomatically() { // given: define a conditional behavior processTestContext .when(() -> assertThat(processInstance).hasActiveElements("approve_order")) .as("approve order") .then(() -> processTestContext.completeUserTask("approve_order", Map.of("approved", true))); // when: create a process instance // then: verify that the process instance is completed } ``` :::important The action should resolve the process state that the condition checks for. After an action executes, the engine waits for the condition to become false again before re-evaluating. For example, if the condition asserts that a user task is active, the action should complete that user task. This advances the process flow so that the condition no longer holds. Otherwise, the same condition may not be detected again reliably. ::: If the same conditional behavior applies to multiple tests, you can define it in a `@BeforeEach` method: ```java @BeforeEach void setupBehaviors() { processTestContext .when(() -> assertThat(processInstance).hasActiveElements("send_notification")) .as("complete send notification") .then(() -> processTestContext.completeJob("send-notification")); } @Test void shouldCompleteOrder() { // given: the conditional behavior is defined in @BeforeEach // when: create a process instance // then: verify that the process instance completed the task } ``` ### Chain multiple actions Actions are consumed in order on each condition match. The last action repeats indefinitely once all preceding actions are exhausted. ```java @Test void shouldHandleRepeatedTask() { // given: define a conditional behavior with chained actions processTestContext .when(() -> assertThat(processInstance).hasActiveElements("review_document")) .as("review document") .then(() -> processTestContext.completeUserTask("review_document", Map.of("approved", false, "comment", "Needs revision"))) .then(() -> processTestContext.completeUserTask("review_document", Map.of("approved", true, "comment", "Looks good"))); // when: create a process instance // then: verify that the process instance is completed } ``` ### Name a behavior You can assign a descriptive name to a conditional behavior using `.as()`. The name is used in log messages and diagnostics. ```java @Test void shouldNameBehavior() { // given: define a named conditional behavior processTestContext .when(() -> assertThat(processInstance).hasActiveElements("send_notification")) .as("send-notification is active") .then(() -> processTestContext.completeJob("send-notification")); // when: create a process instance // then: verify that the process instance completed the task } ``` ## Complete jobs You can complete an active job to simulate the behavior of a job worker without invoking the actual worker. The command waits for the first job with the given job type and completes it. If no job exists, the command fails. Identify the job by its job type or using a [JobSelector](#job-selector). You can pass variables or complete the job with the [example data](/components/modeler/data-handling.md#defining-example-data) from the related BPMN element. When to use it: - Test the process with full control over the job completion - Complete a repeated task with different outcomes ```java @Test void shouldCompleteJob() { // given: a process instance is waiting at a task // when: complete the job with type "send-notification" // 1) Without variables processTestContext.completeJob("send-notification"); // 2) With variables final Map variables = Map.of( "notification-sent", true, "recipients", List.of("user1@example.com", "user2@example.com") ); processTestContext.completeJob("send-notification", variables); // 3) With example data from the BPMN element processTestContext.completeJobWithExampleData("send-notification"); // 4) With job selector by element ID "send_notification_task" processTestContext.completeJob(JobSelectors.byElementId("send_notification_task")); // then: verify that the process instance completed the task } ``` ### Ad-hoc sub-process jobs You can simulate the behavior of an [ad-hoc sub-process job worker](/components/modeler/bpmn/ad-hoc-subprocesses/ad-hoc-subprocesses.md#job-worker-implementation), for example, an AI agent, and control the execution of the ad-hoc sub-process. The job completion allows you to activate an element in the ad-hoc sub-process or to fulfill the completion condition. ```java @Test void shouldCompleteJobOfAdHocSubProcess() { // given: the ad-hoc sub-process is active // when: complete the job of the ad-hoc sub-process // 1) With activating an element with variables processTestContext.completeJobOfAdHocSubProcess( JobSelectors.byElementId("ad-hoc-sub-process"), result -> result.activateElement("search-knowledge-base").variable("query", "launch rockets")); // 2) With job variables (for the ad-hoc sub-process) processTestContext.completeJobOfAdHocSubProcess( JobSelectors.byElementId("ad-hoc-sub-process"), Map.of("agent", agentContext), result -> result.activateElement("search-knowledge-base").variable("query", "launch rockets")); // 3) With fulfilling the completion condition processTestContext.completeJobOfAdHocSubProcess( JobSelectors.byElementId("ad-hoc-sub-process"), result -> result.completionConditionFulfilled(true)); // then: verify that the ad-hoc sub-process completed the task } ``` ### User task listener jobs You can simulate the behavior of a [user task listener job worker](/components/concepts/user-task-listeners.md#implement-a-user-task-listener). The job completion allows to correct the user task data or to deny the user task lifecycle transition. ```java @Test void shouldCompleteJobOfUserTaskListener() { // given: the process instance is waiting at a user task // when: complete the job of the user task listener // 1) With correcting user task data processTestContext.completeJobOfUserTaskListener( JobSelectors.byElementId("approve_request_task"), result -> result.correctAssignee("me").correctPriority(100)); // 2) With denying the user task lifecycle transition processTestContext.completeJobOfUserTaskListener( JobSelectors.byElementId("approve_request_task"), result -> result.deny(true).deniedReason("Policy violation")); // then: verify that the user task is completed } ``` ## Throw BPMN errors from jobs You can throw a BPMN error from an active job to simulate the behavior of a job worker without invoking the actual worker. The command waits for the first job with the given job type and throws the BPMN error. If no job exists, the command fails. Identify the job by its job type or using a [JobSelector](#job-selector). Optionally, you can pass variables and an error message with the BPMN error. When to use it: - Test the error paths in the process - Simulate different behaviors of a repeated task (success, BPMN error) ```java @Test void shouldThrowBpmnErrorFromJob() { // given: a process instance is waiting at a task // when: throw a BPMN error for the job with type "validate-data" // 1) With error code "VALIDATION_FAILED" and no variables processTestContext.throwBpmnErrorFromJob("validate-data", "VALIDATION_FAILED"); // 2) With error code "VALIDATION_FAILED" and variables final Map variables = Map.of( "error-message", "Invalid customer data", "error-code", "ERR_VALIDATION_001" ); processTestContext.throwBpmnErrorFromJob("validate-data", "VALIDATION_FAILED", variables); // 3) With error code, error message, and variables processTestContext.throwBpmnErrorFromJob( "validate-data", "VALIDATION_FAILED", "Data validation failed due to missing required fields", variables); // 4) With job selector by element ID "validate_data_task" processTestContext.throwBpmnErrorFromJob( JobSelectors.byElementId("validate_data_task"), "VALIDATION_FAILED"); // then: verify that the process instance handled the error } ``` ## Complete user tasks You can complete a user task to simulate the user behavior in Tasklist. The command waits for the first user task and completes it. If no user task exists, the command fails. Identify the user task by its BPMN element ID or using a [UserTaskSelector](#user-task-selector). You can pass variables or complete the user task with the [example data](/components/modeler/data-handling.md#defining-example-data) from the related BPMN element. When to use it: - Test a process with user tasks ```java @Test void shouldCompleteUserTask() { // given: a process instance is waiting at a user task // when: complete the user task // 1) With element ID "task_approveRequest" final Map variables = Map.of( "approved", true, "comment", "Request approved by manager", "approvedAmount", 5000.00 ); processTestContext.completeUserTask("task_approveRequest", variables); // 2) With selector by task name "Approve Request" processTestContext.completeUserTask( UserTaskSelectors.byTaskName("Approve Request"), variables); // 3) With example data from the BPMN element processTestContext.completeUserTaskWithExampleData( UserTaskSelectors.byElementId("task_approveRequest")); // then: verify that the process instance is completed } ``` ## Update variables You can update the variables of a process instance, or local variables of a BPMN element, for example, to trigger a BPMN conditional event. To update the local variables of a BPMN element, you need to identify the element using an [ElementSelector](#element-selector). ```java @Test void shouldTriggerConditionalEvent() { // given: a process instance is waiting at the conditional event // when: update the variables to trigger the conditional event // 1) Update process instance variables final Map variables = Map.of( "priority", 80, "riskLevel", "high" ); processTestContext.updateVariables( ProcessInstanceSelectors.byKey(processInstanceKey), variables); // 2) Update local variables of the element with ID "sub-process" processTestContext.updateLocalVariables( ProcessInstanceSelectors.byKey(processInstanceKey), ElementSelectors.byId("sub-process"), variables); // then: verify that the conditional event is completed } ``` ## Resolve incidents You can resolve an active incident. Use the [IncidentSelector](#incident-selector) to identify the incident based on different criteria, for example, by the BPMN element ID where the incident occurred. If the incident is caused by a job, the command increases the job retries by one before resolving the incident. If the incident is caused by a missing variable, you should [update the variables](#update-variables) before resolving the incident. ```java @Test void shouldResolveIncident() { // given: a process instance has an active incident // when: resolve the incident at the element with ID "validate-data" processTestContext.resolveIncident(IncidentSelectors.byElementId("validate-data")); // then: verify that the element is completed } ``` ## Selectors CPT provides selectors to identify entities such as jobs, user tasks, process instances, and more based on different criteria. You can use selectors in both utilities and assertions to target the entities you want to interact with or verify. The selector targets the first matching entity. CPT provides predefined selectors for common use cases. You can combine multiple selectors using `.and()` to create more specific selection criteria. ```java // Combine job selectors by job type and process instance key processTestContext.completeJob( JobSelectors.byJobType("send-notification") .and(JobSelectors.byProcessInstanceKey(processInstanceKey)) ); ``` Alternatively, you can implement your own custom selector when you need specialized selection logic. ```java // Implement a custom selector to select a user task by its assignee private static UserTaskSelector byAssignee(String assignee) { return new UserTaskSelectorByAssignee(assignee); } private static final class UserTaskSelectorByAssignee implements UserTaskSelector { private final String assignee; public UserTaskSelectorByAssignee(String assignee) { this.assignee = assignee; } @Override public boolean test(final UserTask userTask) { return assignee.equals(userTask.getAssignee()); } @Override public String describe() { return "assignee: " + assignee; } @Override public void applyFilter(final UserTaskFilter filter) { filter.assignee(assignee); } } ``` ### Decision selector You can use a decision selector to identify a DMN decision evaluation based on different criteria, such as decision ID or decision name. Predefined decision selectors are available in the `io.camunda.process.test.api.assertions.DecisionSelectors` class. ```java // Assert the evaluation of a DMN decision with the ID "credit-score" assertThatDecision(DecisionSelectors.byId("credit-score")).hasOutput(750); ``` ### Element selector You can use an element selector to identify a BPMN element based on different criteria, such as element ID or element name. Predefined element selectors are available in the `io.camunda.process.test.api.assertions.ElementSelectors` class. ```java // Assert the BPMN element with the ID "approve_request_task" assertThat(processInstance).hasActiveElements(ElementSelectors.byId("approve_request_task")); // Assert the BPMN element with the name "Approve Request" assertThat(processInstance).hasActiveElements(ElementSelectors.byName("Approve Request")); ``` ### Incident selector You can use an incident selector to identify an incident based on different criteria, such as BPMN element ID or process instance key. Predefined incident selectors are available in the `io.camunda.process.test.api.assertions.IncidentSelectors` class. ```java // Resolve an incident by the BPMN element ID "approve_request_task" processTestContext.resolveIncident(IncidentSelectors.byElementId("approve_request_task")); ``` ### Job selector You can use a job selector to identify a job based on different criteria, such as job type, BPMN element ID, or process instance key. Predefined job selectors are available in the `io.camunda.process.test.api.assertions.JobSelectors` class. ```java // Complete a job by its BPMN element ID processTestContext.completeJob(JobSelectors.byElementId("send_notification_task")); // Throw a BPMN error from a job by its job type processTestContext.throwBpmnErrorFromJob(JobSelectors.byJobType("validate-data"), "VALIDATION_FAILED"); ``` ### Process instance selector You can use a process instance selector to identify a process instance based on different criteria, such as process instance key or BPMN process ID. Predefined process instance selectors are available in the `io.camunda.process.test.api.assertions.ProcessInstanceSelectors` class. ```java // Assert a process instance by its process instance key assertThatProcessInstance(ProcessInstanceSelectors.byKey(processInstanceKey)).isCreated(); ``` ### User task selector You can use a user task selector to identify a user task based on different criteria, such as element ID, task name, or process instance key. Predefined user task selectors are available in the `io.camunda.process.test.api.assertions.UserTaskSelectors` class. ```java // Complete a user task by its task name processTestContext.completeUserTask(UserTaskSelectors.byTaskName("Approve Request"), variables); ``` ### Variable selector You can use a variable selector to identify a variable based on different criteria, such as variable name or variable value. Predefined variable selectors are available in the `io.camunda.process.test.api.assertions.VariableSelectors` class. ```java // Assert a variable by its name assertThatProcessInstance(processInstance).hasVariable(VariableSelectors.byName("approved"), true); ``` --- ## Migrate from Zeebe Process Test :::warning Zeebe Process Test was removed in Camunda 8.10. Use [Camunda Process Test](/apis-tools/testing/getting-started.md) instead. ::: For the release-level summary of this removal, see the [8.10 release announcement](/reference/announcements-release-notes/8100/8100-announcements.md#removal-of-legacy-apis-tasklist-v1-dependent-features-and-zeebe-process-test). Use [Camunda Process Test](/apis-tools/testing/getting-started.md) for current process testing, and review the [migration guide](/apis-tools/migration-manuals/migrate-to-camunda-process-test.md) to replace old ZPT dependencies, annotations, and assertions. ## Examples For example tests, refer to [GitHub](https://github.com/camunda-cloud/zeebe-process-test). --- ## Contributing Thanks for your interest in contributing to the Camunda 8 Orchestration Cluster TypeScript SDK. ## Development Setup Requirements: - Node >= 20 (>=18 works with global File polyfill; we target >=20 in CI) - npm 9+ recommended Install deps: ``` npm ci ``` Run full build + tests: ``` npm run build ``` Integration tests spin up containers (Zeebe, Operate, etc.). Use: ``` npm run test:integration ``` ## Deterministic Build & Timestamp Policy The repository enforces a drift guard: regenerated artifacts must be byte‑for‑byte identical across builds unless a real source/input change occurred. To make this reliable we **removed all embedded generation timestamps** (e.g. `generatedAt`, banner date strings) from committed artifacts. Current policy: - Do **not** reintroduce wall‑clock timestamps, date banners, or build times into any committed generated file (TypeScript, JSON, Markdown) unless they are logically required for runtime behavior. - If you need provenance, prefer stable content hashes (already present: `specHash`, branding key hashes) or add a new hash field rather than a timestamp. - The publish workflow sets `CAMUNDA_SDK_SKIP_FETCH_SPEC=1` to avoid pulling a moving upstream spec mid‑release. Rationale: 1. Eliminates false positive drift failures due solely to time. 2. Simplifies local verification: two consecutive `npm run build` runs must yield zero git diffs. 3. Improves review signal: any diff now reflects a substantive schema/template/script change. Contributor guidance: | Scenario | What to do | | ---------------------------------------- | -------------------------------------------------------------------------------- | | Need to record when something was built | Use runtime logging or external release notes, not a committed artifact field. | | Want to tag provenance in generated code | Add or extend a stable hash (e.g. combine spec hash + template hash). | | Adding a new generation script | Ensure output ordering is deterministic (sort keys, arrays) and omit timestamps. | If you inadvertently add a timestamp, the second local build will show a diff—remove the field instead of guarding it behind the deterministic flag. ## Commit Message Guidelines We use Conventional Commits enforced by commitlint. Format: ``` (optional scope): BREAKING CHANGE: ``` Allowed `type` values (common set): - feat - fix - chore - docs - style - refactor - test - ci - build - perf Rules: - Subject length: 5–100 characters (commitlint enforces `subject-min-length` & `subject-max-length`). - Use imperative mood ("add support", not "added support"). - Lowercase subject (except proper nouns). No PascalCase subjects (rule enforced). - Keep subject concise; body can include details, rationale, links. - Prefix breaking changes with `BREAKING CHANGE:` either in body or footer. Examples: ``` feat(worker): add job worker concurrency gating fix(retry): prevent double backoff application chore(ci): stabilize deterministic publish (skip spec fetch) docs: document deterministic build flag refactor(auth): simplify token refresh jitter logic ``` ### Breaking change commits Breaking change markers (`BREAKING CHANGE:` in the body/footer) trigger a major version bump and a CHANGELOG entry. **Never** use these markers for intra-branch corrections on a feature branch — they pollute the release history and cause unnecessary major version increments. PR CI includes a breaking change guard that fails when it detects these markers. If the breaking change is intentional, add the `breaking-change-approved` label to the PR. Otherwise, rewrite the commit history to remove the markers before merging. ## Branching & Releases Releases are performed by GitHub Actions using semantic-release: - `main` publishes alpha prereleases. - `stable/.` publishes stable patch releases for that minor line. Use feature branches and PRs; merge commits should follow conventional syntax to produce changelog entries. To understand what will be released, prefer inspecting the CI logs/artifacts for the release workflow (it runs semantic-release in dry-run mode as part of the pipeline). ## Testing Strategy - Unit tests: `npm test` (fast, deterministic). Avoid relying on wall-clock timers. - Integration tests: `npm run test:integration` (requires container stack up; CI spins it automatically in generate job). - Add test scaffolds for new REST operations via existing generation pipeline; run `npm run scaffold:methods` if needed. ## Validation & Schemas If adding new runtime validation paths or job worker actions, ensure they integrate cleanly with: - `CAMUNDA_SDK_VALIDATION` grammar (req/res strict/warn/none) - Job worker unique symbol receipts (`JobActionReceipt`). ## Performance Considerations Large generation outputs are committed; avoid unnecessary formatting churn. When modifying templates: - Keep imports stable. - Reuse deterministic timestamp injection. - Avoid introducing non-deterministic ordering (Object.keys without sort, randomization, etc.). ## Adding Dependencies Prefer lightweight, maintained libraries. Changes affecting bundle size must include justification in PR description. ## Security Do not log secrets. Redaction logic already masks sensitive env values in hydrated config logs. If adding new secret env vars, update redaction list. ## Code Style Prettier + ESLint run in build pipeline. Run: ``` npm run format && npm run lint ``` before pushing sizable changes. ## Questions Open a GitHub issue or start a PR draft with your questions in the description. Happy hacking! --- ## Function: classifyDomainError() :::caution Technical Preview The Functional Programming API is a **technical preview**. Its surface may change in future releases without following semver. ::: ```ts function classifyDomainError(err): DomainErrorTag; ``` ## Parameters ### err [`DomainError`](../type-aliases/DomainError.md) ## Returns [`DomainErrorTag`](../type-aliases/DomainErrorTag.md) --- ## Function: eventuallyTE() :::caution Technical Preview The Functional Programming API is a **technical preview**. Its surface may change in future releases without following semver. ::: ```ts function eventuallyTE(thunk, predicate, opts): TaskEither; ``` ## Type Parameters ### E `E` ### A `A` ## Parameters ### thunk () => `Promise`\<`A`\> ### predicate (`a`) => `boolean` \| `Promise`\<`boolean`\> ### opts #### intervalMs? `number` #### waitUpToMs `number` ## Returns [`TaskEither`](../type-aliases/TaskEither.md)\<`E`, `A`\> --- ## Function: foldDomainError() :::caution Technical Preview The Functional Programming API is a **technical preview**. Its surface may change in future releases without following semver. ::: ```ts function foldDomainError(handlers): (err) => A; ``` ## Type Parameters ### A `A` ## Parameters ### handlers #### generic (`e`) => `A` #### http (`e`) => `A` #### timeout (`e`) => `A` #### validation (`e`) => `A` ## Returns (`err`) => `A` --- ## Function: retryTE() :::caution Technical Preview The Functional Programming API is a **technical preview**. Its surface may change in future releases without following semver. ::: ```ts function retryTE(task, opts): TaskEither; ``` ## Type Parameters ### E `E` ### A `A` ## Parameters ### task [`TaskEither`](../type-aliases/TaskEither.md)\<`E`, `A`\> ### opts #### baseDelayMs? `number` #### max `number` #### shouldRetry? (`e`, `attempt`) => `boolean` \| `Promise`\<`boolean`\> ## Returns [`TaskEither`](../type-aliases/TaskEither.md)\<`E`, `A`\> --- ## Function: withTimeoutTE() :::caution Technical Preview The Functional Programming API is a **technical preview**. Its surface may change in future releases without following semver. ::: ```ts function withTimeoutTE(task, ms, onTimeout?): TaskEither; ``` ## Type Parameters ### E `E` ### A `A` ## Parameters ### task [`TaskEither`](../type-aliases/TaskEither.md)\<`E`, `A`\> ### ms `number` ### onTimeout? () => `E` ## Returns [`TaskEither`](../type-aliases/TaskEither.md)\<`E`, `A`\> --- ## fp :::caution Technical Preview The Functional Programming API is a **technical preview**. Its surface may change in future releases without following semver. ::: ## Type Aliases - [DomainError](type-aliases/DomainError.md) - [DomainErrorTag](type-aliases/DomainErrorTag.md) - [FnKeys](type-aliases/FnKeys.md) - [Fpify](type-aliases/Fpify.md) - [HttpError](type-aliases/HttpError.md) - [Left](type-aliases/Left.md) - [Right](type-aliases/Right.md) - [TaskEither](type-aliases/TaskEither.md) ## Functions - [classifyDomainError](functions/classifyDomainError.md) - [eventuallyTE](functions/eventuallyTE.md) - [foldDomainError](functions/foldDomainError.md) - [retryTE](functions/retryTE.md) - [withTimeoutTE](functions/withTimeoutTE.md) ## References ### CamundaFpClient Re-exports [CamundaFpClient](../index/type-aliases/CamundaFpClient.md) --- ### createCamundaFpClient Re-exports [createCamundaFpClient](../index/functions/createCamundaFpClient.md) --- ### Either Re-exports [Either](../index/type-aliases/Either.md) --- ### isLeft Re-exports [isLeft](../index/functions/isLeft.md) --- ### isRight Re-exports [isRight](../index/functions/isRight.md) --- ## Type Alias: DomainError :::caution Technical Preview The Functional Programming API is a **technical preview**. Its surface may change in future releases without following semver. ::: ```ts type DomainError = | CamundaValidationError | EventualConsistencyTimeoutError | HttpError | Error; ``` --- ## Type Alias: DomainErrorTag :::caution Technical Preview The Functional Programming API is a **technical preview**. Its surface may change in future releases without following semver. ::: ```ts type DomainErrorTag = "validation" | "timeout" | "http" | "generic"; ``` --- ## Type Alias: FnKeys # Type Alias: FnKeys\ :::caution Technical Preview The Functional Programming API is a **technical preview**. Its surface may change in future releases without following semver. ::: ```ts type FnKeys = { [K in keyof C]: C[K] extends (a: any) => any ? K : never; }[keyof C]; ``` ## Type Parameters ### C `C` --- ## Type Alias: Fpify # Type Alias: Fpify\ :::caution Technical Preview The Functional Programming API is a **technical preview**. Its surface may change in future releases without following semver. ::: ```ts type Fpify = { [K in FnKeys]: C[K] extends (a: infer A) => infer R ? (a: A) => TaskEither> : never; } & object & { [K in Exclude>]: C[K] }; ``` ## Type Declaration ### inner ```ts inner: C; ``` ## Type Parameters ### C `C` --- ## Type Alias: HttpError :::caution Technical Preview The Functional Programming API is a **technical preview**. Its surface may change in future releases without following semver. ::: ```ts type HttpError = object & Record; ``` ## Type Declaration ### body? ```ts optional body?: any; ``` ### message? ```ts optional message?: string; ``` ### name? ```ts optional name?: string; ``` ### status? ```ts optional status?: number; ``` --- ## Type Alias: Left # Type Alias: Left\ :::caution Technical Preview The Functional Programming API is a **technical preview**. Its surface may change in future releases without following semver. ::: ```ts type Left = object; ``` ## Type Parameters ### E `E` ## Properties ### \_tag ```ts _tag: "Left"; ``` --- ### left ```ts left: E; ``` --- ## Type Alias: Right # Type Alias: Right\ :::caution Technical Preview The Functional Programming API is a **technical preview**. Its surface may change in future releases without following semver. ::: ```ts type Right = object; ``` ## Type Parameters ### A `A` ## Properties ### \_tag ```ts _tag: "Right"; ``` --- ### right ```ts right: A; ``` --- ## Type Alias: TaskEither # Type Alias: TaskEither\ :::caution Technical Preview The Functional Programming API is a **technical preview**. Its surface may change in future releases without following semver. ::: ```ts type TaskEither = () => Promise>; ``` ## Type Parameters ### E `E` ### A `A` ## Returns `Promise`\<[`Either`](../../index/type-aliases/Either.md)\<`E`, `A`\>\> --- ## Class: CamundaClient ## Constructors ### Constructor ```ts new CamundaClient(opts?): CamundaClient; ``` #### Parameters ##### opts? [`CamundaOptions`](../interfaces/CamundaOptions.md) = `{}` #### Returns `CamundaClient` ## Accessors ### config #### Get Signature ```ts get config(): Readonly; ``` ##### Returns `Readonly`\<[`CamundaConfig`](../interfaces/CamundaConfig.md)\> ## Methods ### \_getSupportLogger() ```ts _getSupportLogger(): SupportLogger; ``` Internal accessor for support logger (no public API commitment yet). #### Returns [`SupportLogger`](../interfaces/SupportLogger.md) --- ### \_invokeWithRetry() ```ts _invokeWithRetry(op, opts): Promise; ``` Internal invocation helper to apply global backpressure gating + retry + normalization #### Type Parameters ##### T `T` #### Parameters ##### op () => `Promise`\<`T`\> ##### opts ###### classify? (`e`) => `object` ###### exempt? `boolean` ###### opId `string` ###### retryOverride? \| `false` \| `Partial`\<[`HttpRetryPolicy`](../interfaces/HttpRetryPolicy.md)\> #### Returns `Promise`\<`T`\> --- ### activateAdHocSubProcessActivities() ```ts activateAdHocSubProcessActivities(input, options?): CancelablePromise; ``` Activate activities within an ad-hoc sub-process Activates selected activities within an ad-hoc sub-process identified by element ID. The provided element IDs must exist within the ad-hoc sub-process instance identified by the provided adHocSubProcessInstanceKey. - #### Parameters ##### input [`activateAdHocSubProcessActivitiesInput`](../type-aliases/activateAdHocSubProcessActivitiesInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function activateAdHocSubProcessActivitiesExample( adHocSubProcessInstanceKey: ElementInstanceKey, elementId: ElementId ) { const camunda = createCamundaClient(); await camunda.activateAdHocSubProcessActivities({ adHocSubProcessInstanceKey, elements: [{ elementId }], }); } ``` #### Operation Id activateAdHocSubProcessActivities #### Tags Ad-hoc sub-process --- ### activateJobs() ```ts activateJobs(input, options?): CancelablePromise<{ jobs: EnrichedActivatedJob[]; }>; ``` Activate jobs Iterate through all known partitions and activate jobs up to the requested maximum. - #### Parameters ##### input [`JobActivationRequest`](../type-aliases/JobActivationRequest.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `jobs`: [`EnrichedActivatedJob`](../interfaces/EnrichedActivatedJob.md)[]; \}\> #### Example ```ts async function activateJobsExample() { const camunda = createCamundaClient(); const result = await camunda.activateJobs({ type: "payment-processing", timeout: 30000, maxJobsToActivate: 5, }); for (const job of result.jobs) { console.log(`Job ${job.jobKey}: ${job.type}`); // Each enriched job has helper methods await job.complete({ paymentId: "PAY-123" }); } } ``` #### Operation Id activateJobs #### Tags Job --- ### assignClientToGroup() ```ts assignClientToGroup(input, options?): CancelablePromise; ``` Assign a client to a group Assigns a client to a group, making it a member of the group. Members of the group inherit the group authorizations, roles, and tenant assignments. - #### Parameters ##### input [`assignClientToGroupInput`](../type-aliases/assignClientToGroupInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function assignClientToGroupExample( groupId: GroupId, clientId: ClientId ) { const camunda = createCamundaClient(); await camunda.assignClientToGroup({ groupId, clientId, }); } ``` #### Operation Id assignClientToGroup #### Tags Group --- ### assignClientToTenant() ```ts assignClientToTenant(input, options?): CancelablePromise; ``` Assign a client to a tenant Assign the client to the specified tenant. The client can then access tenant data and perform authorized actions. - #### Parameters ##### input [`assignClientToTenantInput`](../type-aliases/assignClientToTenantInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function assignClientToTenantExample( tenantId: TenantId, clientId: ClientId ) { const camunda = createCamundaClient(); await camunda.assignClientToTenant({ tenantId, clientId, }); } ``` #### Operation Id assignClientToTenant #### Tags Tenant --- ### assignGroupToTenant() ```ts assignGroupToTenant(input, options?): CancelablePromise; ``` Assign a group to a tenant Assigns a group to a specified tenant. Group members (users, clients) can then access tenant data and perform authorized actions. - #### Parameters ##### input [`assignGroupToTenantInput`](../type-aliases/assignGroupToTenantInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function assignGroupToTenantExample( tenantId: TenantId, groupId: GroupId ) { const camunda = createCamundaClient(); await camunda.assignGroupToTenant({ tenantId, groupId, }); } ``` #### Operation Id assignGroupToTenant #### Tags Tenant --- ### assignMappingRuleToGroup() ```ts assignMappingRuleToGroup(input, options?): CancelablePromise; ``` Assign a mapping rule to a group Assigns a mapping rule to a group. - #### Parameters ##### input [`assignMappingRuleToGroupInput`](../type-aliases/assignMappingRuleToGroupInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function assignMappingRuleToGroupExample( groupId: GroupId, mappingRuleId: MappingRuleId ) { const camunda = createCamundaClient(); await camunda.assignMappingRuleToGroup({ groupId, mappingRuleId, }); } ``` #### Operation Id assignMappingRuleToGroup #### Tags Group --- ### assignMappingRuleToTenant() ```ts assignMappingRuleToTenant(input, options?): CancelablePromise; ``` Assign a mapping rule to a tenant Assign a single mapping rule to a specified tenant. - #### Parameters ##### input [`assignMappingRuleToTenantInput`](../type-aliases/assignMappingRuleToTenantInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function assignMappingRuleToTenantExample( tenantId: TenantId, mappingRuleId: MappingRuleId ) { const camunda = createCamundaClient(); await camunda.assignMappingRuleToTenant({ tenantId, mappingRuleId, }); } ``` #### Operation Id assignMappingRuleToTenant #### Tags Tenant --- ### assignRoleToClient() ```ts assignRoleToClient(input, options?): CancelablePromise; ``` Assign a role to a client Assigns the specified role to the client. The client will inherit the authorizations associated with this role. - #### Parameters ##### input [`assignRoleToClientInput`](../type-aliases/assignRoleToClientInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function assignRoleToClientExample(roleId: RoleId, clientId: ClientId) { const camunda = createCamundaClient(); await camunda.assignRoleToClient({ roleId, clientId, }); } ``` #### Operation Id assignRoleToClient #### Tags Role --- ### assignRoleToGroup() ```ts assignRoleToGroup(input, options?): CancelablePromise; ``` Assign a role to a group Assigns the specified role to the group. Every member of the group (user or client) will inherit the authorizations associated with this role. - #### Parameters ##### input [`assignRoleToGroupInput`](../type-aliases/assignRoleToGroupInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function assignRoleToGroupExample(roleId: RoleId, groupId: GroupId) { const camunda = createCamundaClient(); await camunda.assignRoleToGroup({ roleId, groupId, }); } ``` #### Operation Id assignRoleToGroup #### Tags Role --- ### assignRoleToMappingRule() ```ts assignRoleToMappingRule(input, options?): CancelablePromise; ``` Assign a role to a mapping rule Assigns a role to a mapping rule. - #### Parameters ##### input [`assignRoleToMappingRuleInput`](../type-aliases/assignRoleToMappingRuleInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function assignRoleToMappingRuleExample( roleId: RoleId, mappingRuleId: MappingRuleId ) { const camunda = createCamundaClient(); await camunda.assignRoleToMappingRule({ roleId, mappingRuleId, }); } ``` #### Operation Id assignRoleToMappingRule #### Tags Role --- ### assignRoleToTenant() ```ts assignRoleToTenant(input, options?): CancelablePromise; ``` Assign a role to a tenant Assigns a role to a specified tenant. Users, Clients or Groups, that have the role assigned, will get access to the tenant's data and can perform actions according to their authorizations. - #### Parameters ##### input [`assignRoleToTenantInput`](../type-aliases/assignRoleToTenantInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function assignRoleToTenantExample(tenantId: TenantId, roleId: RoleId) { const camunda = createCamundaClient(); await camunda.assignRoleToTenant({ tenantId, roleId, }); } ``` #### Operation Id assignRoleToTenant #### Tags Tenant --- ### assignRoleToUser() ```ts assignRoleToUser(input, options?): CancelablePromise; ``` Assign a role to a user Assigns the specified role to the user. The user will inherit the authorizations associated with this role. - #### Parameters ##### input [`assignRoleToUserInput`](../type-aliases/assignRoleToUserInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function assignRoleToUserExample(roleId: RoleId, username: Username) { const camunda = createCamundaClient(); await camunda.assignRoleToUser({ roleId, username, }); } ``` #### Operation Id assignRoleToUser #### Tags Role --- ### assignUserTask() ```ts assignUserTask(input, options?): CancelablePromise; ``` Assign user task Assigns a user task with the given key to the given assignee. Assignment waits for blocking task listeners on this lifecycle transition. If listener processing is delayed beyond the request timeout, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. - #### Parameters ##### input [`assignUserTaskInput`](../type-aliases/assignUserTaskInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function assignUserTaskExample(userTaskKey: UserTaskKey) { const camunda = createCamundaClient(); await camunda.assignUserTask({ userTaskKey, assignee: "alice", allowOverride: true, }); } ``` #### Operation Id assignUserTask #### Tags User task --- ### assignUserToGroup() ```ts assignUserToGroup(input, options?): CancelablePromise; ``` Assign a user to a group Assigns a user to a group, making the user a member of the group. Group members inherit the group authorizations, roles, and tenant assignments. - #### Parameters ##### input [`assignUserToGroupInput`](../type-aliases/assignUserToGroupInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function assignUserToGroupExample(groupId: GroupId, username: Username) { const camunda = createCamundaClient(); await camunda.assignUserToGroup({ groupId, username, }); } ``` #### Operation Id assignUserToGroup #### Tags Group --- ### assignUserToTenant() ```ts assignUserToTenant(input, options?): CancelablePromise; ``` Assign a user to a tenant Assign a single user to a specified tenant. The user can then access tenant data and perform authorized actions. - #### Parameters ##### input [`assignUserToTenantInput`](../type-aliases/assignUserToTenantInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function assignUserToTenantExample( tenantId: TenantId, username: Username ) { const camunda = createCamundaClient(); await camunda.assignUserToTenant({ tenantId, username, }); } ``` #### Operation Id assignUserToTenant #### Tags Tenant --- ### broadcastSignal() ```ts broadcastSignal(input, options?): CancelablePromise; ``` Broadcast signal Broadcasts a signal. - #### Parameters ##### input [`SignalBroadcastRequest`](../type-aliases/SignalBroadcastRequest.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`SignalBroadcastResult`](../type-aliases/SignalBroadcastResult.md)\> #### Example ```ts async function broadcastSignalExample() { const camunda = createCamundaClient(); const result = await camunda.broadcastSignal({ signalName: "system-shutdown", variables: { reason: "Scheduled maintenance", }, }); console.log(`Signal broadcast key: ${result.signalKey}`); } ``` #### Operation Id broadcastSignal #### Tags Signal --- ### cancelBatchOperation() ```ts cancelBatchOperation(input, options?): CancelablePromise; ``` Cancel Batch operation Cancels a running batch operation. This is done asynchronously, the progress can be tracked using the batch operation status endpoint (/batch-operations/{batchOperationKey}). - #### Parameters ##### input ###### batchOperationKey [`BatchOperationKey`](../type-aliases/BatchOperationKey.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function cancelBatchOperationExample( batchOperationKey: BatchOperationKey ) { const camunda = createCamundaClient(); await camunda.cancelBatchOperation({ batchOperationKey }); } ``` #### Operation Id cancelBatchOperation #### Tags Batch operation --- ### cancelProcessInstance() ```ts cancelProcessInstance(input, options?): CancelablePromise; ``` Cancel process instance Cancels a running process instance. As a cancellation includes more than just the removal of the process instance resource, the cancellation resource must be posted. Cancellation can wait on listener-related processing; when that processing does not complete in time, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. - #### Parameters ##### input `object` & `object` ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function cancelProcessInstanceExample( processDefinitionId: ProcessDefinitionId ) { const camunda = createCamundaClient(); // Create a process instance and get its key from the response const created = await camunda.createProcessInstance({ processDefinitionId, }); // Cancel the process instance using the key from the creation response await camunda.cancelProcessInstance({ processInstanceKey: created.processInstanceKey, }); } ``` #### Operation Id cancelProcessInstance #### Tags Process instance --- ### cancelProcessInstancesBatchOperation() ```ts cancelProcessInstancesBatchOperation(input, options?): CancelablePromise; ``` Cancel process instances (batch) Cancels multiple running process instances. Since only ACTIVE root instances can be cancelled, any given filters for state and parentProcessInstanceKey are ignored and overridden during this batch operation. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). - #### Parameters ##### input [`ProcessInstanceCancellationBatchOperationRequest`](../type-aliases/ProcessInstanceCancellationBatchOperationRequest.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`BatchOperationCreatedResult`](../type-aliases/BatchOperationCreatedResult.md)\> #### Example ```ts async function cancelProcessInstancesBatchOperationExample( processDefinitionKey: ProcessDefinitionKey ) { const camunda = createCamundaClient(); const result = await camunda.cancelProcessInstancesBatchOperation({ filter: { processDefinitionKey, }, }); console.log(`Batch operation key: ${result.batchOperationKey}`); } ``` #### Operation Id cancelProcessInstancesBatchOperation #### Tags Process instance --- ### clearAuthCache() ```ts clearAuthCache(opts?): void; ``` #### Parameters ##### opts? ###### disk? `boolean` ###### memory? `boolean` #### Returns `void` --- ### completeJob() ```ts completeJob(input, options?): CancelablePromise; ``` Complete job Complete a job with the given payload, which allows completing the associated service task. - #### Parameters ##### input [`completeJobInput`](../type-aliases/completeJobInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function completeJobExample(jobKey: JobKey) { const camunda = createCamundaClient(); await camunda.completeJob({ jobKey, variables: { paymentId: "PAY-123", status: "completed", }, }); } ``` #### Operation Id completeJob #### Tags Job --- ### completeUserTask() ```ts completeUserTask(input, options?): CancelablePromise; ``` Complete user task Completes a user task with the given key. Completion waits for blocking task listeners on this lifecycle transition. If listener processing is delayed beyond the request timeout, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. - #### Parameters ##### input [`completeUserTaskInput`](../type-aliases/completeUserTaskInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function completeUserTaskExample(userTaskKey: UserTaskKey) { const camunda = createCamundaClient(); await camunda.completeUserTask({ userTaskKey, variables: { approved: true, comment: "Looks good", }, }); } ``` #### Operation Id completeUserTask #### Tags User task --- ### configure() ```ts configure(next): void; ``` #### Parameters ##### next [`CamundaOptions`](../interfaces/CamundaOptions.md) #### Returns `void` --- ### correlateMessage() ```ts correlateMessage(input, options?): CancelablePromise; ``` Correlate message Publishes a message and correlates it to a subscription. If correlation is successful it will return the first process instance key the message correlated with. The message is not buffered. Use the publish message endpoint to send messages that can be buffered. - #### Parameters ##### input [`MessageCorrelationRequest`](../type-aliases/MessageCorrelationRequest.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`MessageCorrelationResult`](../type-aliases/MessageCorrelationResult.md)\> #### Example ```ts async function correlateMessageExample() { const camunda = createCamundaClient(); const result = await camunda.correlateMessage({ name: "order-payment-received", correlationKey: "ORD-12345", variables: { paymentId: "PAY-123", amount: 99.95, }, }); console.log(`Message correlated to: ${result.processInstanceKey}`); } ``` #### Operation Id correlateMessage #### Tags Message --- ### createAdminUser() ```ts createAdminUser(input, options?): CancelablePromise; ``` Create admin user Creates a new user and assigns the admin role to it. This endpoint is only usable when users are managed in the Orchestration Cluster and while no user is assigned to the admin role. - #### Parameters ##### input [`UserRequest`](../type-aliases/UserRequest.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`UserCreateResult`](../type-aliases/UserCreateResult.md)\> #### Example ```ts async function createAdminUserExample(username: Username) { const camunda = createCamundaClient(); const result = await camunda.createAdminUser({ username, name: "Admin User", email: "admin@example.com", password: "admin-password-123", }); console.log(`Created admin user: ${result.username}`); } ``` #### Operation Id createAdminUser #### Tags Setup --- ### createAgentInstance() ```ts createAgentInstance(input, options?): CancelablePromise; ``` Create agent instance Creates a new agent instance. The returned key identifies the instance and must be used in subsequent update and query calls. - #### Parameters ##### input [`AgentInstanceCreationRequest`](../type-aliases/AgentInstanceCreationRequest.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`AgentInstanceCreationResult`](../type-aliases/AgentInstanceCreationResult.md)\> #### Example ```ts async function createAgentInstanceExample( elementInstanceKey: ElementInstanceKey ) { const camunda = createCamundaClient(); const result = await camunda.createAgentInstance({ elementInstanceKey, definition: { model: "gpt-4o", provider: "openai", systemPrompt: "You are a helpful assistant.", }, }); console.log(`Created agent instance: ${result.agentInstanceKey}`); } ``` #### Operation Id createAgentInstance #### Tags Agent instance --- ### createAuthorization() ```ts createAuthorization(input, options?): CancelablePromise; ``` Create authorization Create the authorization. - #### Parameters ##### input \| [`AuthorizationIdBasedRequest`](../type-aliases/AuthorizationIdBasedRequest.md) \| [`AuthorizationPropertyBasedRequest`](../type-aliases/AuthorizationPropertyBasedRequest.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`AuthorizationCreateResult`](../type-aliases/AuthorizationCreateResult.md)\> #### Example ```ts async function createAuthorizationExample() { const camunda = createCamundaClient(); const result = await camunda.createAuthorization({ ownerId: "user-123", ownerType: "USER", resourceId: "order-process", resourceType: "PROCESS_DEFINITION", permissionTypes: ["CREATE_PROCESS_INSTANCE", "READ_PROCESS_INSTANCE"], }); console.log(`Authorization key: ${result.authorizationKey}`); } ``` #### Operation Id createAuthorization #### Tags Authorization --- ### createDeployment() ```ts createDeployment(input, options?): CancelablePromise; ``` Deploy resources Deploys one or more resources (e.g. processes, decision models, or forms). This is an atomic call, i.e. either all resources are deployed or none of them are. - #### Parameters ##### input [`createDeploymentInput`](../type-aliases/createDeploymentInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ExtendedDeploymentResult`](../interfaces/ExtendedDeploymentResult.md)\> Enriched deployment result with typed arrays (processes, decisions, decisionRequirements, forms, resources). #### Example ```ts async function deployResourcesFromFilesExample() { const camunda = createCamundaClient(); // Node.js only: deploy directly from file paths const result = await camunda.deployResourcesFromFiles([ "./process.bpmn", "./decision.dmn", ]); console.log(`Deployment key: ${result.deploymentKey}`); } ``` #### Operation Id createDeployment #### Tags Resource --- ### createDocument() ```ts createDocument(input, options?): CancelablePromise; ``` Upload document Upload a document to the Camunda 8 cluster. Note that this is currently supported for document stores of type: AWS, Azure, GCP, in-memory (non-production), local (non-production) - #### Parameters ##### input [`createDocumentInput`](../type-aliases/createDocumentInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`DocumentReference`](../type-aliases/DocumentReference.md)\> #### Example ```ts async function createDocumentExample() { const camunda = createCamundaClient(); const file = new Blob(["Hello, world!"], { type: "text/plain" }); const result = await camunda.createDocument({ file, metadata: { fileName: "hello.txt" }, }); console.log(`Document ID: ${result.documentId}`); } ``` #### Operation Id createDocument #### Tags Document --- ### createDocumentLink() ```ts createDocumentLink(input, options?): CancelablePromise; ``` Create document link Create a link to a document in the Camunda 8 cluster. Note that this is currently supported for document stores of type: AWS, Azure, GCP - #### Parameters ##### input [`createDocumentLinkInput`](../type-aliases/createDocumentLinkInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`DocumentLink`](../type-aliases/DocumentLink.md)\> #### Example ```ts async function createDocumentLinkExample(documentId: DocumentId) { const camunda = createCamundaClient(); const link = await camunda.createDocumentLink({ documentId, timeToLive: 3600000, }); console.log(`Document link: ${link.url}`); } ``` #### Operation Id createDocumentLink #### Tags Document --- ### createDocuments() ```ts createDocuments(input, options?): CancelablePromise; ``` Upload multiple documents Upload multiple documents to the Camunda 8 cluster. The caller must provide a file name for each document, which will be used in case of a multi-status response to identify which documents failed to upload. The file name can be provided in the `Content-Disposition` header of the file part or in the `fileName` field of the metadata. You can add a parallel array of metadata objects. These are matched with the files based on index, and must have the same length as the files array. To pass homogenous metadata for all files, spread the metadata over the metadata array. A filename value provided explicitly via the metadata array in the request overrides the `Content-Disposition` header of the file part. In case of a multi-status response, the response body will contain a list of `DocumentBatchProblemDetail` objects, each of which contains the file name of the document that failed to upload and the reason for the failure. The client can choose to retry the whole batch or individual documents based on the response. Note that this is currently supported for document stores of type: AWS, Azure, GCP, in-memory (non-production), local (non-production) - #### Parameters ##### input [`createDocumentsInput`](../type-aliases/createDocumentsInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`DocumentCreationBatchResponse`](../type-aliases/DocumentCreationBatchResponse.md)\> #### Example ```ts async function createDocumentsExample() { const camunda = createCamundaClient(); const file1 = new Blob(["File one"], { type: "text/plain" }); const file2 = new Blob(["File two"], { type: "text/plain" }); const result = await camunda.createDocuments({ files: [file1, file2], metadataList: [{ fileName: "one.txt" }, { fileName: "two.txt" }], }); for (const doc of result.createdDocuments ?? []) { console.log(`Created: ${doc.documentId}`); } } ``` #### Operation Id createDocuments #### Tags Document --- ### createElementInstanceVariables() ```ts createElementInstanceVariables(input, options?): CancelablePromise; ``` Update element instance variables Updates all the variables of a particular scope (for example, process instance, element instance) with the given variable data. Specify the element instance in the `elementInstanceKey` parameter. Variable updates can be delayed by listener-related processing; if processing exceeds the request timeout, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. - #### Parameters ##### input [`createElementInstanceVariablesInput`](../type-aliases/createElementInstanceVariablesInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function createElementInstanceVariablesExample( elementInstanceKey: ElementInstanceKey ) { const camunda = createCamundaClient(); await camunda.createElementInstanceVariables({ elementInstanceKey, variables: { orderId: "ORD-12345", status: "processing" }, }); } ``` #### Operation Id createElementInstanceVariables #### Tags Element instance --- ### createGlobalClusterVariable() ```ts createGlobalClusterVariable(input, options?): CancelablePromise; ``` Create a global-scoped cluster variable Create a global-scoped cluster variable. - #### Parameters ##### input [`CreateClusterVariableRequest`](../type-aliases/CreateClusterVariableRequest.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ClusterVariableResult`](../type-aliases/ClusterVariableResult.md)\> #### Example ```ts async function createGlobalClusterVariableExample(name: ClusterVariableName) { const camunda = createCamundaClient(); const result = await camunda.createGlobalClusterVariable({ name, value: { darkMode: true }, }); console.log(`Created: ${result.name}`); } ``` #### Operation Id createGlobalClusterVariable #### Tags Cluster Variable --- ### createGlobalTaskListener() ```ts createGlobalTaskListener(input, options?): CancelablePromise; ``` Create global user task listener Create a new global user task listener. - #### Parameters ##### input [`CreateGlobalTaskListenerRequest`](../type-aliases/CreateGlobalTaskListenerRequest.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`GlobalTaskListenerResult`](../type-aliases/GlobalTaskListenerResult.md)\> #### Example ```ts async function createGlobalTaskListenerExample(id: GlobalListenerId) { const camunda = createCamundaClient(); const result = await camunda.createGlobalTaskListener({ id, eventTypes: ["completing"], type: "audit-log-listener", }); console.log(`Created listener: ${result.id}`); } ``` #### Operation Id createGlobalTaskListener #### Tags Global listener --- ### createGroup() ```ts createGroup(input, options?): CancelablePromise; ``` Create group Create a new group. The supplied `groupId` is validated against `^[a-zA-Z0-9_~@.+-]+$` (max 256 characters) by `IdentifierValidator.validateId` in the runtime. This strict validation applies wherever the Groups API is available: in OIDC deployments that set `camunda.security.authentication.oidc.groupsClaim` the Groups API (including this endpoint) is disabled entirely, so group CRUD never sees externally-minted IdP IDs. The BYOG relaxation only loosens validation when a group is referenced _as a member_ of a role or tenant (`assignRoleToGroup`, `assignGroupToTenant`); group CRUD itself always uses the strict default-id regex. The constraint is not advertised on the `GroupId` schema so that the same schema can be reused at member-reference sites without falsely rejecting externally-minted IdP group IDs there. - #### Parameters ##### input [`GroupCreateRequest`](../type-aliases/GroupCreateRequest.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`GroupCreateResult`](../type-aliases/GroupCreateResult.md)\> #### Example ```ts async function createGroupExample(groupId: GroupId) { const camunda = createCamundaClient(); const result = await camunda.createGroup({ groupId, name: "Engineering Team", }); console.log(`Created group: ${result.groupId}`); } ``` #### Operation Id createGroup #### Tags Group --- ### createJobWorker() ```ts createJobWorker(cfg): JobWorker; ``` Create a job worker that activates and processes jobs of the given type. Worker configuration fields inherit global defaults resolved via the unified configuration (environment variables or equivalent `CAMUNDA_WORKER_*` keys provided via `CamundaOptions.config`) when not explicitly set on the config object. #### Type Parameters ##### In `In` _extends_ `ZodType`\<`unknown`, `unknown`, `$ZodTypeInternals`\<`unknown`, `unknown`\>\> = `any` ##### Out `Out` _extends_ `ZodType`\<`unknown`, `unknown`, `$ZodTypeInternals`\<`unknown`, `unknown`\>\> = `any` ##### Headers `Headers` _extends_ `ZodType`\<`unknown`, `unknown`, `$ZodTypeInternals`\<`unknown`, `unknown`\>\> = `any` #### Parameters ##### cfg [`JobWorkerConfig`](../interfaces/JobWorkerConfig.md)\<`In`, `Out`, `Headers`\> Worker configuration #### Returns [`JobWorker`](../interfaces/JobWorker.md) #### Examples ```ts async function createJobWorkerExample() { const camunda = createCamundaClient(); const _worker = camunda.createJobWorker({ jobType: "payment-processing", jobTimeoutMs: 30000, maxParallelJobs: 5, jobHandler: async (job): Promise => { console.log(`Processing job ${job.jobKey}`); return job.complete({ processed: true }); }, }); // Workers run continuously until closed // worker.close(); } ``` ```ts async function jobWorkerWithErrorHandlingExample() { const camunda = createCamundaClient(); const worker = camunda.createJobWorker({ jobType: "email-sending", jobTimeoutMs: 60000, maxParallelJobs: 10, pollIntervalMs: 300, jobHandler: async (job): Promise => { try { console.log(`Sending email for job ${job.jobKey}`); return job.complete({ sent: true }); } catch (err) { return job.fail({ errorMessage: String(err), retries: (job.retries ?? 1) - 1, }); } }, }); void worker; } ``` --- ### createMappingRule() ```ts createMappingRule(input, options?): CancelablePromise; ``` Create mapping rule Create a new mapping rule - #### Parameters ##### input [`MappingRuleCreateRequest`](../type-aliases/MappingRuleCreateRequest.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`MappingRuleCreateUpdateResult`](../type-aliases/MappingRuleCreateUpdateResult.md)\> #### Example ```ts async function createMappingRuleExample(mappingRuleId: MappingRuleId) { const camunda = createCamundaClient(); const result = await camunda.createMappingRule({ mappingRuleId, name: "LDAP Group Mapping", claimName: "groups", claimValue: "engineering", }); console.log(`Created mapping rule: ${result.mappingRuleId}`); } ``` #### Operation Id createMappingRule #### Tags Mapping rule --- ### createProcessInstance() ```ts createProcessInstance(input, options?): CancelablePromise; ``` Create process instance Creates and starts an instance of the specified process. The process definition to use to create the instance can be specified either using its unique key (as returned by Deploy resources), or using the BPMN process id and a version. Waits for the completion of the process instance before returning a result when awaitCompletion is enabled. - #### Parameters ##### input \| [`ProcessInstanceCreationInstructionByKey`](../type-aliases/ProcessInstanceCreationInstructionByKey.md) \| [`ProcessInstanceCreationInstructionById`](../type-aliases/ProcessInstanceCreationInstructionById.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`CreateProcessInstanceResult`](../type-aliases/CreateProcessInstanceResult.md)\> #### Examples ```ts async function createProcessInstanceByIdExample( processDefinitionId: ProcessDefinitionId ) { const camunda = createCamundaClient(); const result = await camunda.createProcessInstance({ processDefinitionId, variables: { orderId: "ORD-12345", amount: 99.95, }, }); console.log(`Started process instance: ${result.processInstanceKey}`); } ``` ```ts async function createProcessInstanceByKeyExample( processDefinitionKey: ProcessDefinitionKey ) { const camunda = createCamundaClient(); // Key from a previous API response (e.g. deployment) const result = await camunda.createProcessInstance({ processDefinitionKey, variables: { orderId: "ORD-12345", amount: 99.95, }, }); console.log(`Started process instance: ${result.processInstanceKey}`); } ``` #### Operation Id createProcessInstance #### Tags Process instance --- ### createRole() ```ts createRole(input, options?): CancelablePromise; ``` Create role Create a new role. - #### Parameters ##### input [`RoleCreateRequest`](../type-aliases/RoleCreateRequest.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`RoleCreateResult`](../type-aliases/RoleCreateResult.md)\> #### Example ```ts async function createRoleExample(roleId: RoleId) { const camunda = createCamundaClient(); const result = await camunda.createRole({ roleId, name: "Process Admin", }); console.log(`Created role: ${result.roleId}`); } ``` #### Operation Id createRole #### Tags Role --- ### createTenant() ```ts createTenant(input, options?): CancelablePromise; ``` Create tenant Creates a new tenant. - #### Parameters ##### input [`TenantCreateRequest`](../type-aliases/TenantCreateRequest.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`TenantCreateResult`](../type-aliases/TenantCreateResult.md)\> #### Example ```ts async function createTenantExample(tenantId: TenantId) { const camunda = createCamundaClient(); const result = await camunda.createTenant({ tenantId, name: "Customer Service", }); console.log(`Created tenant: ${result.tenantId}`); } ``` #### Operation Id createTenant #### Tags Tenant --- ### createTenantClusterVariable() ```ts createTenantClusterVariable(input, options?): CancelablePromise; ``` Create a tenant-scoped cluster variable Create a new cluster variable for the given tenant. - #### Parameters ##### input [`createTenantClusterVariableInput`](../type-aliases/createTenantClusterVariableInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ClusterVariableResult`](../type-aliases/ClusterVariableResult.md)\> #### Example ```ts async function createTenantClusterVariableExample( tenantId: TenantId, name: ClusterVariableName ) { const camunda = createCamundaClient(); const result = await camunda.createTenantClusterVariable({ tenantId, name, value: { region: "us-east-1" }, }); console.log(`Created: ${result.name}`); } ``` #### Operation Id createTenantClusterVariable #### Tags Cluster Variable --- ### createThreadedJobWorker() ```ts createThreadedJobWorker(cfg): ThreadedJobWorker; ``` Create a threaded job worker that runs handler logic in a pool of worker threads. The handler must be a separate module file that exports a default function with signature `(job, client) => Promise`. This keeps the main event loop free for polling and I/O, dramatically improving throughput for CPU-bound job handlers. Worker configuration fields inherit global defaults resolved via the unified configuration (environment variables or equivalent `CAMUNDA_WORKER_*` keys provided via `CamundaOptions.config`) when not explicitly set on the config object. #### Type Parameters ##### In `In` _extends_ `ZodType`\<`unknown`, `unknown`, `$ZodTypeInternals`\<`unknown`, `unknown`\>\> = `any` ##### Out `Out` _extends_ `ZodType`\<`unknown`, `unknown`, `$ZodTypeInternals`\<`unknown`, `unknown`\>\> = `any` ##### Headers `Headers` _extends_ `ZodType`\<`unknown`, `unknown`, `$ZodTypeInternals`\<`unknown`, `unknown`\>\> = `any` #### Parameters ##### cfg [`ThreadedJobWorkerConfig`](../interfaces/ThreadedJobWorkerConfig.md)\<`In`, `Out`, `Headers`\> Threaded worker configuration #### Returns [`ThreadedJobWorker`](../interfaces/ThreadedJobWorker.md) #### Example ```ts const worker = client.createThreadedJobWorker({ jobType: "cpu-heavy-task", handlerModule: "./my-handler.js", maxParallelJobs: 32, jobTimeoutMs: 30000, }); ``` --- ### createUser() ```ts createUser(input, options?): CancelablePromise; ``` Create user Create a new user. - #### Parameters ##### input [`UserRequest`](../type-aliases/UserRequest.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`UserCreateResult`](../type-aliases/UserCreateResult.md)\> #### Example ```ts async function createUserExample(username: Username) { const camunda = createCamundaClient(); const result = await camunda.createUser({ username, name: "Alice Smith", email: "alice@example.com", password: "secure-password-123", }); console.log(`Created user: ${result.username}`); } ``` #### Operation Id createUser #### Tags User --- ### deleteAuthorization() ```ts deleteAuthorization(input, options?): CancelablePromise; ``` Delete authorization Deletes the authorization with the given key. - #### Parameters ##### input [`deleteAuthorizationInput`](../type-aliases/deleteAuthorizationInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function deleteAuthorizationExample(authorizationKey: AuthorizationKey) { const camunda = createCamundaClient(); await camunda.deleteAuthorization({ authorizationKey }); } ``` #### Operation Id deleteAuthorization #### Tags Authorization --- ### deleteDecisionInstance() ```ts deleteDecisionInstance(input, options?): CancelablePromise; ``` Delete decision instance Delete all associated decision evaluations based on provided key. - #### Parameters ##### input `object` & `object` ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function deleteDecisionInstanceExample( decisionEvaluationKey: DecisionEvaluationKey ) { const camunda = createCamundaClient(); await camunda.deleteDecisionInstance({ decisionEvaluationKey }); } ``` #### Operation Id deleteDecisionInstance #### Tags Decision instance --- ### deleteDecisionInstancesBatchOperation() ```ts deleteDecisionInstancesBatchOperation(input, options?): CancelablePromise; ``` Delete decision instances (batch) Delete multiple decision instances. This will delete the historic data from secondary storage. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). - #### Parameters ##### input [`DecisionInstanceDeletionBatchOperationRequest`](../type-aliases/DecisionInstanceDeletionBatchOperationRequest.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`BatchOperationCreatedResult`](../type-aliases/BatchOperationCreatedResult.md)\> #### Example ```ts async function deleteDecisionInstancesBatchOperationExample() { const camunda = createCamundaClient(); const result = await camunda.deleteDecisionInstancesBatchOperation({ filter: {}, }); console.log(`Batch operation key: ${result.batchOperationKey}`); } ``` #### Operation Id deleteDecisionInstancesBatchOperation #### Tags Decision instance --- ### deleteDocument() ```ts deleteDocument(input, options?): CancelablePromise; ``` Delete document Delete a document from the Camunda 8 cluster. Note that this is currently supported for document stores of type: AWS, Azure, GCP, in-memory (non-production), local (non-production) - #### Parameters ##### input [`deleteDocumentInput`](../type-aliases/deleteDocumentInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function deleteDocumentExample(documentId: DocumentId) { const camunda = createCamundaClient(); await camunda.deleteDocument({ documentId }); } ``` #### Operation Id deleteDocument #### Tags Document --- ### deleteGlobalClusterVariable() ```ts deleteGlobalClusterVariable(input, options?): CancelablePromise; ``` Delete a global-scoped cluster variable Delete a global-scoped cluster variable. - #### Parameters ##### input [`deleteGlobalClusterVariableInput`](../type-aliases/deleteGlobalClusterVariableInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function deleteGlobalClusterVariableExample(name: ClusterVariableName) { const camunda = createCamundaClient(); await camunda.deleteGlobalClusterVariable({ name }); } ``` #### Operation Id deleteGlobalClusterVariable #### Tags Cluster Variable --- ### deleteGlobalTaskListener() ```ts deleteGlobalTaskListener(input, options?): CancelablePromise; ``` Delete global user task listener Deletes a global user task listener. - #### Parameters ##### input [`deleteGlobalTaskListenerInput`](../type-aliases/deleteGlobalTaskListenerInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function deleteGlobalTaskListenerExample(id: GlobalListenerId) { const camunda = createCamundaClient(); await camunda.deleteGlobalTaskListener({ id, }); } ``` #### Operation Id deleteGlobalTaskListener #### Tags Global listener --- ### deleteGroup() ```ts deleteGroup(input, options?): CancelablePromise; ``` Delete group Deletes the group with the given ID. - #### Parameters ##### input [`deleteGroupInput`](../type-aliases/deleteGroupInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function deleteGroupExample(groupId: GroupId) { const camunda = createCamundaClient(); await camunda.deleteGroup({ groupId }); } ``` #### Operation Id deleteGroup #### Tags Group --- ### deleteMappingRule() ```ts deleteMappingRule(input, options?): CancelablePromise; ``` Delete a mapping rule Deletes the mapping rule with the given ID. - #### Parameters ##### input [`deleteMappingRuleInput`](../type-aliases/deleteMappingRuleInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function deleteMappingRuleExample(mappingRuleId: MappingRuleId) { const camunda = createCamundaClient(); await camunda.deleteMappingRule({ mappingRuleId }); } ``` #### Operation Id deleteMappingRule #### Tags Mapping rule --- ### deleteProcessInstance() ```ts deleteProcessInstance(input, options?): CancelablePromise; ``` Delete process instance Deletes a process instance. Only instances that are completed or terminated can be deleted. - #### Parameters ##### input `object` & `object` ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function deleteProcessInstanceExample( processInstanceKey: ProcessInstanceKey ) { const camunda = createCamundaClient(); await camunda.deleteProcessInstance({ processInstanceKey }); } ``` #### Operation Id deleteProcessInstance #### Tags Process instance --- ### deleteProcessInstancesBatchOperation() ```ts deleteProcessInstancesBatchOperation(input, options?): CancelablePromise; ``` Delete process instances (batch) Delete multiple process instances. This will delete the historic data from secondary storage. Only process instances in a final state (COMPLETED or TERMINATED) can be deleted. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). - #### Parameters ##### input [`ProcessInstanceDeletionBatchOperationRequest`](../type-aliases/ProcessInstanceDeletionBatchOperationRequest.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`BatchOperationCreatedResult`](../type-aliases/BatchOperationCreatedResult.md)\> #### Example ```ts async function deleteProcessInstancesBatchOperationExample( processDefinitionKey: ProcessDefinitionKey ) { const camunda = createCamundaClient(); const result = await camunda.deleteProcessInstancesBatchOperation({ filter: { processDefinitionKey, }, }); console.log(`Batch operation key: ${result.batchOperationKey}`); } ``` #### Operation Id deleteProcessInstancesBatchOperation #### Tags Process instance --- ### deleteResource() ```ts deleteResource(input, options?): CancelablePromise; ``` Delete resource Deletes a deployed resource. This can be a process definition, decision requirements definition, or form definition deployed using the deploy resources endpoint. Specify the resource you want to delete in the `resourceKey` parameter. Once a resource has been deleted it cannot be recovered. If the resource needs to be available again, a new deployment of the resource is required. By default, only the resource itself is deleted from the runtime state. To also delete the historic data associated with a resource, set the `deleteHistory` flag in the request body to `true`. The historic data is deleted asynchronously via a batch operation. The details of the created batch operation are included in the response. Note that history deletion is only supported for process resources; for other resource types this flag is ignored and no history will be deleted. - #### Parameters ##### input `object` & `object` ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`DeleteResourceResponse`](../type-aliases/DeleteResourceResponse.md)\> #### Example ```ts async function deleteResourceExample(resourceKey: ProcessDefinitionKey) { const camunda = createCamundaClient(); // Use a process definition key as a resource key for deletion await camunda.deleteResource({ resourceKey, }); } ``` #### Operation Id deleteResource #### Tags Resource --- ### deleteRole() ```ts deleteRole(input, options?): CancelablePromise; ``` Delete role Deletes the role with the given ID. - #### Parameters ##### input [`deleteRoleInput`](../type-aliases/deleteRoleInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function deleteRoleExample(roleId: RoleId) { const camunda = createCamundaClient(); await camunda.deleteRole({ roleId }); } ``` #### Operation Id deleteRole #### Tags Role --- ### deleteTenant() ```ts deleteTenant(input, options?): CancelablePromise; ``` Delete tenant Deletes an existing tenant. - #### Parameters ##### input [`deleteTenantInput`](../type-aliases/deleteTenantInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function deleteTenantExample(tenantId: TenantId) { const camunda = createCamundaClient(); await camunda.deleteTenant({ tenantId }); } ``` #### Operation Id deleteTenant #### Tags Tenant --- ### deleteTenantClusterVariable() ```ts deleteTenantClusterVariable(input, options?): CancelablePromise; ``` Delete a tenant-scoped cluster variable Delete a tenant-scoped cluster variable. - #### Parameters ##### input [`deleteTenantClusterVariableInput`](../type-aliases/deleteTenantClusterVariableInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function deleteTenantClusterVariableExample( tenantId: TenantId, name: ClusterVariableName ) { const camunda = createCamundaClient(); await camunda.deleteTenantClusterVariable({ tenantId, name, }); } ``` #### Operation Id deleteTenantClusterVariable #### Tags Cluster Variable --- ### deleteUser() ```ts deleteUser(input, options?): CancelablePromise; ``` Delete user Deletes a user. - #### Parameters ##### input [`deleteUserInput`](../type-aliases/deleteUserInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function deleteUserExample(username: Username) { const camunda = createCamundaClient(); await camunda.deleteUser({ username }); } ``` #### Operation Id deleteUser #### Tags User --- ### deployResourcesFromFiles() ```ts deployResourcesFromFiles(resourceFilenames, options?): CancelablePromise; ``` Node-only convenience: deploy resources from local filesystem paths. #### Parameters ##### resourceFilenames `string`[] Absolute or relative file paths to BPMN/DMN/form/resource files. ##### options? Optional: tenantId. ###### tenantId? `string` #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ExtendedDeploymentResult`](../interfaces/ExtendedDeploymentResult.md)\> ExtendedDeploymentResult --- ### emitSupportLogPreamble() ```ts emitSupportLogPreamble(): void; ``` Emit the standard support log preamble & redacted configuration to the current support logger. Safe to call multiple times; subsequent calls are ignored (idempotent). Useful when a custom supportLogger was injected and you still want the canonical header & config dump. #### Returns `void` --- ### evaluateConditionals() ```ts evaluateConditionals(input, options?): CancelablePromise; ``` Evaluate root level conditional start events Evaluates root-level conditional start events for process definitions. If the evaluation is successful, it will return the keys of all created process instances, along with their associated process definition key. Multiple root-level conditional start events of the same process definition can trigger if their conditions evaluate to true. - #### Parameters ##### input [`ConditionalEvaluationInstruction`](../type-aliases/ConditionalEvaluationInstruction.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`EvaluateConditionalResult`](../type-aliases/EvaluateConditionalResult.md)\> #### Example ```ts async function evaluateConditionalsExample(tenantId: TenantId) { const camunda = createCamundaClient(); const result = await camunda.evaluateConditionals({ variables: { orderReady: true }, tenantId, }); console.log(`Evaluated conditionals: ${JSON.stringify(result)}`); } ``` #### Operation Id evaluateConditionals #### Tags Conditional --- ### evaluateDecision() ```ts evaluateDecision(input, options?): CancelablePromise; ``` Evaluate decision Evaluates a decision. You specify the decision to evaluate either by using its unique key (as returned by DeployResource), or using the decision ID. When using the decision ID, the latest deployed version of the decision is used. - #### Parameters ##### input \| [`DecisionEvaluationById`](../type-aliases/DecisionEvaluationById.md) \| [`DecisionEvaluationByKey`](../type-aliases/DecisionEvaluationByKey.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`EvaluateDecisionResult`](../type-aliases/EvaluateDecisionResult.md)\> #### Examples ```ts async function evaluateDecisionByIdExample( decisionDefinitionId: DecisionDefinitionId ) { const camunda = createCamundaClient(); const result = await camunda.evaluateDecision({ decisionDefinitionId, variables: { amount: 1000, invoiceCategory: "Misc", }, }); console.log(`Decision: ${result.decisionDefinitionId}`); console.log(`Output: ${result.output}`); } ``` ```ts async function evaluateDecisionByKeyExample( decisionDefinitionKey: DecisionDefinitionKey ) { const camunda = createCamundaClient(); const result = await camunda.evaluateDecision({ decisionDefinitionKey, variables: { amount: 1000, invoiceCategory: "Misc", }, }); console.log(`Decision output: ${result.output}`); } ``` #### Operation Id evaluateDecision #### Tags Decision definition --- ### evaluateExpression() ```ts evaluateExpression(input, options?): CancelablePromise; ``` Evaluate an expression Evaluates a FEEL expression and returns the result. Supports references to tenant scoped cluster variables when a tenant ID is provided. - #### Parameters ##### input [`ExpressionEvaluationRequest`](../type-aliases/ExpressionEvaluationRequest.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ExpressionEvaluationResult`](../type-aliases/ExpressionEvaluationResult.md)\> #### Example ```ts async function evaluateExpressionExample() { const camunda = createCamundaClient(); const result = await camunda.evaluateExpression({ expression: "= x + y", variables: { x: 10, y: 20 }, }); console.log(`Result: ${result.result}`); } ``` #### Operation Id evaluateExpression #### Tags Expression --- ### failJob() ```ts failJob(input, options?): CancelablePromise; ``` Fail job Mark the job as failed. - #### Parameters ##### input [`failJobInput`](../type-aliases/failJobInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function failJobExample(jobKey: JobKey) { const camunda = createCamundaClient(); await camunda.failJob({ jobKey, retries: 2, errorMessage: "Payment gateway timeout", retryBackOff: 5000, }); } ``` #### Operation Id failJob #### Tags Job --- ### forceAuthRefresh() ```ts forceAuthRefresh(): Promise; ``` #### Returns `Promise`\<`string` \| `undefined`\> --- ### getAgentInstance() ```ts getAgentInstance( input, consistencyManagement, options?): CancelablePromise; ``` Get agent instance Returns agent instance as JSON. - #### Parameters ##### input [`getAgentInstanceInput`](../type-aliases/getAgentInstanceInput.md) ##### consistencyManagement [`getAgentInstanceConsistency`](../type-aliases/getAgentInstanceConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`AgentInstanceResult`](../type-aliases/AgentInstanceResult.md)\> #### Example ```ts async function getAgentInstanceExample(agentInstanceKey: AgentInstanceKey) { const camunda = createCamundaClient(); const instance = await camunda.getAgentInstance( { agentInstanceKey }, { consistency: { waitUpToMs: 5000 } } ); console.log(`Status: ${instance.status}`); console.log(`Element: ${instance.elementId}`); } ``` #### Operation Id getAgentInstance #### Tags Agent instance #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getAuditLog() ```ts getAuditLog( input, consistencyManagement, options?): CancelablePromise; ``` Get audit log Get an audit log entry by auditLogKey. - #### Parameters ##### input [`getAuditLogInput`](../type-aliases/getAuditLogInput.md) ##### consistencyManagement [`getAuditLogConsistency`](../type-aliases/getAuditLogConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`AuditLogResult`](../type-aliases/AuditLogResult.md)\> #### Example ```ts async function getAuditLogExample(auditLogKey: AuditLogKey) { const camunda = createCamundaClient(); const log = await camunda.getAuditLog( { auditLogKey }, { consistency: { waitUpToMs: 5000 } } ); console.log(`Audit log: ${log.operationType}`); } ``` #### Operation Id getAuditLog #### Tags Audit Log #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getAuthentication() ```ts getAuthentication(options?): CancelablePromise; ``` Get current user Retrieves the current authenticated user. - #### Parameters ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`CamundaUserResult`](../type-aliases/CamundaUserResult.md)\> #### Example ```ts async function getAuthenticationExample() { const camunda = createCamundaClient(); const user = await camunda.getAuthentication(); console.log(`Authenticated as: ${user.username}`); } ``` #### Operation Id getAuthentication #### Tags Authentication --- ### getAuthHeaders() ```ts getAuthHeaders(): Promise>; ``` #### Returns `Promise`\<`Record`\<`string`, `string`\>\> --- ### getAuthorization() ```ts getAuthorization( input, consistencyManagement, options?): CancelablePromise; ``` Get authorization Get authorization by the given key. - #### Parameters ##### input [`getAuthorizationInput`](../type-aliases/getAuthorizationInput.md) ##### consistencyManagement [`getAuthorizationConsistency`](../type-aliases/getAuthorizationConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`AuthorizationResult`](../type-aliases/AuthorizationResult.md)\> #### Example ```ts async function getAuthorizationExample(authorizationKey: AuthorizationKey) { const camunda = createCamundaClient(); const authorization = await camunda.getAuthorization( { authorizationKey }, { consistency: { waitUpToMs: 5000 } } ); console.log(`Owner: ${authorization.ownerId} (${authorization.ownerType})`); } ``` #### Operation Id getAuthorization #### Tags Authorization #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getBackpressureState() ```ts getBackpressureState(): | { backoffMs: number; consecutive: number; permitsCurrent: number; permitsMax: number | null; severity: BackpressureSeverity; waiters: number; } | { consecutive: number; permitsCurrent: number; permitsMax: null; severity: string; waiters: number; }; ``` Public accessor for current backpressure adaptive limiter state (stable) #### Returns \| \{ `backoffMs`: `number`; `consecutive`: `number`; `permitsCurrent`: `number`; `permitsMax`: `number` \| `null`; `severity`: [`BackpressureSeverity`](../type-aliases/BackpressureSeverity.md); `waiters`: `number`; \} \| \{ `consecutive`: `number`; `permitsCurrent`: `number`; `permitsMax`: `null`; `severity`: `string`; `waiters`: `number`; \} --- ### getBatchOperation() ```ts getBatchOperation( input, consistencyManagement, options?): CancelablePromise; ``` Get batch operation Get batch operation by key. - #### Parameters ##### input [`getBatchOperationInput`](../type-aliases/getBatchOperationInput.md) ##### consistencyManagement [`getBatchOperationConsistency`](../type-aliases/getBatchOperationConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`BatchOperationResponse`](../type-aliases/BatchOperationResponse.md)\> #### Example ```ts async function getBatchOperationExample(batchOperationKey: BatchOperationKey) { const camunda = createCamundaClient(); const batch = await camunda.getBatchOperation( { batchOperationKey }, { consistency: { waitUpToMs: 5000 } } ); console.log(`Batch: ${batch.batchOperationType} (${batch.state})`); } ``` #### Operation Id getBatchOperation #### Tags Batch operation #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getConfig() ```ts getConfig(): Readonly; ``` Read-only snapshot of current hydrated configuration (do not mutate directly). Use configure(...) to apply changes. #### Returns `Readonly`\<[`CamundaConfig`](../interfaces/CamundaConfig.md)\> --- ### getDecisionDefinition() ```ts getDecisionDefinition( input, consistencyManagement, options?): CancelablePromise; ``` Get decision definition Returns a decision definition by key. - #### Parameters ##### input [`getDecisionDefinitionInput`](../type-aliases/getDecisionDefinitionInput.md) ##### consistencyManagement [`getDecisionDefinitionConsistency`](../type-aliases/getDecisionDefinitionConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`DecisionDefinitionResult`](../type-aliases/DecisionDefinitionResult.md)\> #### Example ```ts async function getDecisionDefinitionExample( decisionDefinitionKey: DecisionDefinitionKey ) { const camunda = createCamundaClient(); const definition = await camunda.getDecisionDefinition( { decisionDefinitionKey }, { consistency: { waitUpToMs: 5000 } } ); console.log(`Decision: ${definition.decisionDefinitionId}`); console.log(`Version: ${definition.version}`); } ``` #### Operation Id getDecisionDefinition #### Tags Decision definition #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getDecisionDefinitionXml() ```ts getDecisionDefinitionXml( input, consistencyManagement, options?): CancelablePromise; ``` Get decision definition XML Returns decision definition as XML. - #### Parameters ##### input [`getDecisionDefinitionXmlInput`](../type-aliases/getDecisionDefinitionXmlInput.md) ##### consistencyManagement [`getDecisionDefinitionXmlConsistency`](../type-aliases/getDecisionDefinitionXmlConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`string`\> #### Example ```ts async function getDecisionDefinitionXmlExample( decisionDefinitionKey: DecisionDefinitionKey ) { const camunda = createCamundaClient(); const xml = await camunda.getDecisionDefinitionXml( { decisionDefinitionKey }, { consistency: { waitUpToMs: 5000 } } ); console.log(`XML length: ${JSON.stringify(xml).length}`); } ``` #### Operation Id getDecisionDefinitionXML #### Tags Decision definition #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getDecisionInstance() ```ts getDecisionInstance( input, consistencyManagement, options?): CancelablePromise; ``` Get decision instance Returns a decision instance. - #### Parameters ##### input [`getDecisionInstanceInput`](../type-aliases/getDecisionInstanceInput.md) ##### consistencyManagement [`getDecisionInstanceConsistency`](../type-aliases/getDecisionInstanceConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`DecisionInstanceGetQueryResult`](../type-aliases/DecisionInstanceGetQueryResult.md)\> #### Example ```ts async function getDecisionInstanceExample( decisionEvaluationInstanceKey: DecisionEvaluationInstanceKey ) { const camunda = createCamundaClient(); const instance = await camunda.getDecisionInstance( { decisionEvaluationInstanceKey }, { consistency: { waitUpToMs: 5000 } } ); console.log(`Decision: ${instance.decisionDefinitionId}`); } ``` #### Operation Id getDecisionInstance #### Tags Decision instance #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getDecisionRequirements() ```ts getDecisionRequirements( input, consistencyManagement, options?): CancelablePromise; ``` Get decision requirements Returns Decision Requirements as JSON. - #### Parameters ##### input [`getDecisionRequirementsInput`](../type-aliases/getDecisionRequirementsInput.md) ##### consistencyManagement [`getDecisionRequirementsConsistency`](../type-aliases/getDecisionRequirementsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`DecisionRequirementsResult`](../type-aliases/DecisionRequirementsResult.md)\> #### Example ```ts async function getDecisionRequirementsExample( decisionRequirementsKey: DecisionRequirementsKey ) { const camunda = createCamundaClient(); const requirements = await camunda.getDecisionRequirements( { decisionRequirementsKey }, { consistency: { waitUpToMs: 5000 } } ); console.log(`Requirements: ${requirements.decisionRequirementsId}`); } ``` #### Operation Id getDecisionRequirements #### Tags Decision requirements #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getDecisionRequirementsXml() ```ts getDecisionRequirementsXml( input, consistencyManagement, options?): CancelablePromise; ``` Get decision requirements XML Returns decision requirements as XML. - #### Parameters ##### input [`getDecisionRequirementsXmlInput`](../type-aliases/getDecisionRequirementsXmlInput.md) ##### consistencyManagement [`getDecisionRequirementsXmlConsistency`](../type-aliases/getDecisionRequirementsXmlConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`string`\> #### Example ```ts async function getDecisionRequirementsXmlExample( decisionRequirementsKey: DecisionRequirementsKey ) { const camunda = createCamundaClient(); const xml = await camunda.getDecisionRequirementsXml( { decisionRequirementsKey }, { consistency: { waitUpToMs: 5000 } } ); console.log(`XML length: ${JSON.stringify(xml).length}`); } ``` #### Operation Id getDecisionRequirementsXML #### Tags Decision requirements #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getDocument() ```ts getDocument(input, options?): CancelablePromise; ``` Download document Download a document from the Camunda 8 cluster. Note that this is currently supported for document stores of type: AWS, Azure, GCP, in-memory (non-production), local (non-production) - #### Parameters ##### input [`getDocumentInput`](../type-aliases/getDocumentInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`Blob`\> #### Example ```ts async function getDocumentExample(documentId: DocumentId) { const camunda = createCamundaClient(); await camunda.getDocument({ documentId }); console.log(`Downloaded document: ${documentId}`); } ``` #### Operation Id getDocument #### Tags Document --- ### getElementInstance() ```ts getElementInstance( input, consistencyManagement, options?): CancelablePromise; ``` Get element instance Returns element instance as JSON. - #### Parameters ##### input [`getElementInstanceInput`](../type-aliases/getElementInstanceInput.md) ##### consistencyManagement [`getElementInstanceConsistency`](../type-aliases/getElementInstanceConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ElementInstanceResult`](../type-aliases/ElementInstanceResult.md)\> #### Example ```ts async function getElementInstanceExample( elementInstanceKey: ElementInstanceKey ) { const camunda = createCamundaClient(); const element = await camunda.getElementInstance( { elementInstanceKey }, { consistency: { waitUpToMs: 5000 } } ); console.log(`Element: ${element.elementId} (${element.type})`); } ``` #### Operation Id getElementInstance #### Tags Element instance #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getErrorMode() ```ts getErrorMode(): "throw" | "result"; ``` Internal accessor (read-only) for eventual consistency error mode. #### Returns `"throw"` \| `"result"` --- ### getFormByKey() ```ts getFormByKey( input, consistencyManagement, options?): CancelablePromise; ``` Get form by key Get a form by its unique form key. - #### Parameters ##### input [`getFormByKeyInput`](../type-aliases/getFormByKeyInput.md) ##### consistencyManagement [`getFormByKeyConsistency`](../type-aliases/getFormByKeyConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`FormResult`](../type-aliases/FormResult.md)\> #### Example ```ts async function getFormByKeyExample(formKey: FormKey) { const camunda = createCamundaClient(); const form = await camunda.getFormByKey( { formKey, }, { consistency: { waitUpToMs: 5000 } } ); console.log(`Form: ${form.formId}, version: ${form.version}`); } ``` #### Operation Id getFormByKey #### Tags Form #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getGlobalClusterVariable() ```ts getGlobalClusterVariable( input, consistencyManagement, options?): CancelablePromise; ``` Get a global-scoped cluster variable Get a global-scoped cluster variable. - #### Parameters ##### input [`getGlobalClusterVariableInput`](../type-aliases/getGlobalClusterVariableInput.md) ##### consistencyManagement [`getGlobalClusterVariableConsistency`](../type-aliases/getGlobalClusterVariableConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ClusterVariableResult`](../type-aliases/ClusterVariableResult.md)\> #### Example ```ts async function getGlobalClusterVariableExample(name: ClusterVariableName) { const camunda = createCamundaClient(); const variable = await camunda.getGlobalClusterVariable( { name }, { consistency: { waitUpToMs: 5000 } } ); console.log(`${variable.name} = ${variable.value}`); } ``` #### Operation Id getGlobalClusterVariable #### Tags Cluster Variable #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getGlobalJobStatistics() ```ts getGlobalJobStatistics( input, consistencyManagement, options?): CancelablePromise; ``` Global job statistics Returns global aggregated counts for jobs. Filter by the creation time window (required) and optionally by jobType. - #### Parameters ##### input [`getGlobalJobStatisticsInput`](../type-aliases/getGlobalJobStatisticsInput.md) ##### consistencyManagement [`getGlobalJobStatisticsConsistency`](../type-aliases/getGlobalJobStatisticsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`GlobalJobStatisticsQueryResult`](../type-aliases/GlobalJobStatisticsQueryResult.md)\> #### Example ```ts async function getGlobalJobStatisticsExample() { const camunda = createCamundaClient(); const result = await camunda.getGlobalJobStatistics( { from: "2025-01-01T00:00:00Z", to: "2025-12-31T23:59:59Z", }, { consistency: { waitUpToMs: 5000 } } ); console.log(`Statistics retrieved: ${JSON.stringify(result)}`); } ``` #### Operation Id getGlobalJobStatistics #### Tags Job #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getGlobalTaskListener() ```ts getGlobalTaskListener( input, consistencyManagement, options?): CancelablePromise; ``` Get global user task listener Get a global user task listener by its id. - #### Parameters ##### input [`getGlobalTaskListenerInput`](../type-aliases/getGlobalTaskListenerInput.md) ##### consistencyManagement [`getGlobalTaskListenerConsistency`](../type-aliases/getGlobalTaskListenerConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`GlobalTaskListenerResult`](../type-aliases/GlobalTaskListenerResult.md)\> #### Example ```ts async function getGlobalTaskListenerExample(id: GlobalListenerId) { const camunda = createCamundaClient(); const listener = await camunda.getGlobalTaskListener( { id }, { consistency: { waitUpToMs: 5000 } } ); console.log(`Listener: ${listener.type} (${listener.eventTypes})`); } ``` #### Operation Id getGlobalTaskListener #### Tags Global listener #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getGroup() ```ts getGroup( input, consistencyManagement, options?): CancelablePromise; ``` Get group Get a group by its ID. - #### Parameters ##### input [`getGroupInput`](../type-aliases/getGroupInput.md) ##### consistencyManagement [`getGroupConsistency`](../type-aliases/getGroupConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`GroupResult`](../type-aliases/GroupResult.md)\> #### Example ```ts async function getGroupExample(groupId: GroupId) { const camunda = createCamundaClient(); const group = await camunda.getGroup( { groupId }, { consistency: { waitUpToMs: 5000 } } ); console.log(`Group: ${group.name}`); } ``` #### Operation Id getGroup #### Tags Group #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getIncident() ```ts getIncident( input, consistencyManagement, options?): CancelablePromise; ``` Get incident Returns incident as JSON. - #### Parameters ##### input [`getIncidentInput`](../type-aliases/getIncidentInput.md) ##### consistencyManagement [`getIncidentConsistency`](../type-aliases/getIncidentConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`IncidentResult`](../type-aliases/IncidentResult.md)\> #### Example ```ts async function getIncidentExample(incidentKey: IncidentKey) { const camunda = createCamundaClient(); const incident = await camunda.getIncident( { incidentKey }, { consistency: { waitUpToMs: 5000 } } ); console.log(`Type: ${incident.errorType}`); console.log(`State: ${incident.state}`); console.log(`Message: ${incident.errorMessage}`); } ``` #### Operation Id getIncident #### Tags Incident #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getJobErrorStatistics() ```ts getJobErrorStatistics( input, consistencyManagement, options?): CancelablePromise; ``` Get error metrics for a job type Returns aggregated metrics per error for the given jobType. - #### Parameters ##### input [`JobErrorStatisticsQuery`](../type-aliases/JobErrorStatisticsQuery.md) ##### consistencyManagement [`getJobErrorStatisticsConsistency`](../type-aliases/getJobErrorStatisticsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`JobErrorStatisticsQueryResult`](../type-aliases/JobErrorStatisticsQueryResult.md)\> #### Example ```ts async function getJobErrorStatisticsExample() { const camunda = createCamundaClient(); const result = await camunda.getJobErrorStatistics( { filter: { from: "2025-01-01T00:00:00Z", to: "2025-12-31T23:59:59Z", jobType: "payment-processing", }, }, { consistency: { waitUpToMs: 5000 } } ); for (const stat of result.items ?? []) { console.log(`Error: ${stat.errorMessage}, workers: ${stat.workers}`); } } ``` #### Operation Id getJobErrorStatistics #### Tags Job #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getJobTimeSeriesStatistics() ```ts getJobTimeSeriesStatistics( input, consistencyManagement, options?): CancelablePromise; ``` Get time-series metrics for a job type Returns a list of time-bucketed metrics ordered ascending by time. The `from` and `to` fields select the time window of interest. Each item in the response corresponds to one time bucket of the requested resolution. - #### Parameters ##### input [`JobTimeSeriesStatisticsQuery`](../type-aliases/JobTimeSeriesStatisticsQuery.md) ##### consistencyManagement [`getJobTimeSeriesStatisticsConsistency`](../type-aliases/getJobTimeSeriesStatisticsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`JobTimeSeriesStatisticsQueryResult`](../type-aliases/JobTimeSeriesStatisticsQueryResult.md)\> #### Example ```ts async function getJobTimeSeriesStatisticsExample() { const camunda = createCamundaClient(); const result = await camunda.getJobTimeSeriesStatistics( { filter: { from: "2025-01-01T00:00:00Z", to: "2025-12-31T23:59:59Z", jobType: "payment-processing", }, }, { consistency: { waitUpToMs: 5000 } } ); for (const point of result.items ?? []) { console.log(`Time: ${point.time}, created: ${point.created.count}`); } } ``` #### Operation Id getJobTimeSeriesStatistics #### Tags Job #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getJobTypeStatistics() ```ts getJobTypeStatistics( input, consistencyManagement, options?): CancelablePromise; ``` Get job statistics by type Get statistics about jobs, grouped by job type. - #### Parameters ##### input [`JobTypeStatisticsQuery`](../type-aliases/JobTypeStatisticsQuery.md) ##### consistencyManagement [`getJobTypeStatisticsConsistency`](../type-aliases/getJobTypeStatisticsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`JobTypeStatisticsQueryResult`](../type-aliases/JobTypeStatisticsQueryResult.md)\> #### Example ```ts async function getJobTypeStatisticsExample() { const camunda = createCamundaClient(); const result = await camunda.getJobTypeStatistics( {}, { consistency: { waitUpToMs: 5000 } } ); for (const stat of result.items ?? []) { console.log(`Type: ${stat.jobType}, workers: ${stat.workers}`); } } ``` #### Operation Id getJobTypeStatistics #### Tags Job #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getJobWorkerStatistics() ```ts getJobWorkerStatistics( input, consistencyManagement, options?): CancelablePromise; ``` Get job statistics by worker Get statistics about jobs, grouped by worker, for a given job type. - #### Parameters ##### input [`JobWorkerStatisticsQuery`](../type-aliases/JobWorkerStatisticsQuery.md) ##### consistencyManagement [`getJobWorkerStatisticsConsistency`](../type-aliases/getJobWorkerStatisticsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`JobWorkerStatisticsQueryResult`](../type-aliases/JobWorkerStatisticsQueryResult.md)\> #### Example ```ts async function getJobWorkerStatisticsExample() { const camunda = createCamundaClient(); const result = await camunda.getJobWorkerStatistics( { filter: { from: "2025-01-01T00:00:00Z", to: "2025-12-31T23:59:59Z", jobType: "payment-processing", }, }, { consistency: { waitUpToMs: 5000 } } ); for (const stat of result.items ?? []) { console.log(`Worker: ${stat.worker}, completed: ${stat.completed.count}`); } } ``` #### Operation Id getJobWorkerStatistics #### Tags Job #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getLicense() ```ts getLicense(options?): CancelablePromise; ``` Get license status Obtains the status of the current Camunda license. - #### Parameters ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`LicenseResponse`](../type-aliases/LicenseResponse.md)\> #### Example ```ts async function getLicenseExample() { const camunda = createCamundaClient(); const license = await camunda.getLicense(); console.log(`License type: ${license.validLicense}`); } ``` #### Operation Id getLicense #### Tags License --- ### getMappingRule() ```ts getMappingRule( input, consistencyManagement, options?): CancelablePromise; ``` Get a mapping rule Gets the mapping rule with the given ID. - #### Parameters ##### input [`getMappingRuleInput`](../type-aliases/getMappingRuleInput.md) ##### consistencyManagement [`getMappingRuleConsistency`](../type-aliases/getMappingRuleConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`MappingRuleResult`](../type-aliases/MappingRuleResult.md)\> #### Example ```ts async function getMappingRuleExample(mappingRuleId: MappingRuleId) { const camunda = createCamundaClient(); const rule = await camunda.getMappingRule( { mappingRuleId }, { consistency: { waitUpToMs: 5000 } } ); console.log(`Rule: ${rule.name} (${rule.claimName}=${rule.claimValue})`); } ``` #### Operation Id getMappingRule #### Tags Mapping rule #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getProcessDefinition() ```ts getProcessDefinition( input, consistencyManagement, options?): CancelablePromise; ``` Get process definition Returns process definition as JSON. - #### Parameters ##### input [`getProcessDefinitionInput`](../type-aliases/getProcessDefinitionInput.md) ##### consistencyManagement [`getProcessDefinitionConsistency`](../type-aliases/getProcessDefinitionConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ProcessDefinitionResult`](../type-aliases/ProcessDefinitionResult.md)\> #### Example ```ts async function getProcessDefinitionExample( processDefinitionKey: ProcessDefinitionKey ) { const camunda = createCamundaClient(); const definition = await camunda.getProcessDefinition( { processDefinitionKey }, { consistency: { waitUpToMs: 5000 } } ); console.log( `Process: ${definition.processDefinitionId} v${definition.version}` ); } ``` #### Operation Id getProcessDefinition #### Tags Process definition #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getProcessDefinitionInstanceStatistics() ```ts getProcessDefinitionInstanceStatistics( input, consistencyManagement, options?): CancelablePromise; ``` Get process instance statistics Get statistics about process instances, grouped by process definition and tenant. - #### Parameters ##### input [`ProcessDefinitionInstanceStatisticsQuery`](../type-aliases/ProcessDefinitionInstanceStatisticsQuery.md) ##### consistencyManagement [`getProcessDefinitionInstanceStatisticsConsistency`](../type-aliases/getProcessDefinitionInstanceStatisticsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ProcessDefinitionInstanceStatisticsQueryResult`](../type-aliases/ProcessDefinitionInstanceStatisticsQueryResult.md)\> #### Example ```ts async function getProcessDefinitionInstanceStatisticsExample() { const camunda = createCamundaClient(); const result = await camunda.getProcessDefinitionInstanceStatistics( {}, { consistency: { waitUpToMs: 5000 } } ); for (const stat of result.items ?? []) { console.log( `Definition ${stat.processDefinitionId}: ${stat.activeInstancesWithoutIncidentCount} active` ); } } ``` #### Operation Id getProcessDefinitionInstanceStatistics #### Tags Process definition #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getProcessDefinitionInstanceVersionStatistics() ```ts getProcessDefinitionInstanceVersionStatistics( input, consistencyManagement, options?): CancelablePromise; ``` Get process instance statistics by version Get statistics about process instances, grouped by version for a given process definition. The process definition ID must be provided as a required field in the request body filter. - #### Parameters ##### input [`ProcessDefinitionInstanceVersionStatisticsQuery`](../type-aliases/ProcessDefinitionInstanceVersionStatisticsQuery.md) ##### consistencyManagement [`getProcessDefinitionInstanceVersionStatisticsConsistency`](../type-aliases/getProcessDefinitionInstanceVersionStatisticsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ProcessDefinitionInstanceVersionStatisticsQueryResult`](../type-aliases/ProcessDefinitionInstanceVersionStatisticsQueryResult.md)\> #### Example ```ts async function getProcessDefinitionInstanceVersionStatisticsExample( processDefinitionId: ProcessDefinitionId ) { const camunda = createCamundaClient(); const result = await camunda.getProcessDefinitionInstanceVersionStatistics( { filter: { processDefinitionId, }, }, { consistency: { waitUpToMs: 5000 } } ); for (const stat of result.items ?? []) { console.log( `Version ${stat.processDefinitionVersion}: ${stat.activeInstancesWithoutIncidentCount} active` ); } } ``` #### Operation Id getProcessDefinitionInstanceVersionStatistics #### Tags Process definition #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getProcessDefinitionMessageSubscriptionStatistics() ```ts getProcessDefinitionMessageSubscriptionStatistics( input, consistencyManagement, options?): CancelablePromise; ``` Get message subscription statistics Get message subscription statistics, grouped by process definition. - #### Parameters ##### input [`ProcessDefinitionMessageSubscriptionStatisticsQuery`](../type-aliases/ProcessDefinitionMessageSubscriptionStatisticsQuery.md) ##### consistencyManagement [`getProcessDefinitionMessageSubscriptionStatisticsConsistency`](../type-aliases/getProcessDefinitionMessageSubscriptionStatisticsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ProcessDefinitionMessageSubscriptionStatisticsQueryResult`](../type-aliases/ProcessDefinitionMessageSubscriptionStatisticsQueryResult.md)\> #### Example ```ts async function getProcessDefinitionMessageSubscriptionStatisticsExample() { const camunda = createCamundaClient(); const result = await camunda.getProcessDefinitionMessageSubscriptionStatistics( {}, { consistency: { waitUpToMs: 5000 } } ); for (const stat of result.items ?? []) { console.log( `Definition ${stat.processDefinitionId}: ${stat.activeSubscriptions} subscriptions` ); } } ``` #### Operation Id getProcessDefinitionMessageSubscriptionStatistics #### Tags Process definition #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getProcessDefinitionStatistics() ```ts getProcessDefinitionStatistics( input, consistencyManagement, options?): CancelablePromise; ``` Get process definition statistics Get statistics about elements in currently running process instances by process definition key and search filter. - #### Parameters ##### input [`getProcessDefinitionStatisticsInput`](../type-aliases/getProcessDefinitionStatisticsInput.md) ##### consistencyManagement [`getProcessDefinitionStatisticsConsistency`](../type-aliases/getProcessDefinitionStatisticsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ProcessDefinitionElementStatisticsQueryResult`](../type-aliases/ProcessDefinitionElementStatisticsQueryResult.md)\> #### Example ```ts async function getProcessDefinitionStatisticsExample( processDefinitionKey: ProcessDefinitionKey ) { const camunda = createCamundaClient(); const result = await camunda.getProcessDefinitionStatistics( { processDefinitionKey }, { consistency: { waitUpToMs: 5000 } } ); for (const stat of result.items ?? []) { console.log(`Element ${stat.elementId}: active=${stat.active}`); } } ``` #### Operation Id getProcessDefinitionStatistics #### Tags Process definition #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getProcessDefinitionXml() ```ts getProcessDefinitionXml( input, consistencyManagement, options?): CancelablePromise; ``` Get process definition XML Returns process definition as XML. - #### Parameters ##### input [`getProcessDefinitionXmlInput`](../type-aliases/getProcessDefinitionXmlInput.md) ##### consistencyManagement [`getProcessDefinitionXmlConsistency`](../type-aliases/getProcessDefinitionXmlConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`string`\> #### Example ```ts async function getProcessDefinitionXmlExample( processDefinitionKey: ProcessDefinitionKey ) { const camunda = createCamundaClient(); const xml = await camunda.getProcessDefinitionXml( { processDefinitionKey }, { consistency: { waitUpToMs: 5000 } } ); console.log(`XML length: ${JSON.stringify(xml).length}`); } ``` #### Operation Id getProcessDefinitionXML #### Tags Process definition #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getProcessInstance() ```ts getProcessInstance( input, consistencyManagement, options?): CancelablePromise; ``` Get process instance Get the process instance by the process instance key. - #### Parameters ##### input [`getProcessInstanceInput`](../type-aliases/getProcessInstanceInput.md) ##### consistencyManagement [`getProcessInstanceConsistency`](../type-aliases/getProcessInstanceConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ProcessInstanceResult`](../type-aliases/ProcessInstanceResult.md)\> #### Example ```ts async function getProcessInstanceExample( processInstanceKey: ProcessInstanceKey ) { const camunda = createCamundaClient(); const instance = await camunda.getProcessInstance( { processInstanceKey }, { consistency: { waitUpToMs: 5000 } } ); console.log(`State: ${instance.state}`); console.log(`Process: ${instance.processDefinitionId}`); } ``` #### Operation Id getProcessInstance #### Tags Process instance #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getProcessInstanceCallHierarchy() ```ts getProcessInstanceCallHierarchy( input, consistencyManagement, options?): CancelablePromise; ``` Get call hierarchy Returns the call hierarchy for a given process instance, showing its ancestry up to the root instance. - #### Parameters ##### input [`getProcessInstanceCallHierarchyInput`](../type-aliases/getProcessInstanceCallHierarchyInput.md) ##### consistencyManagement [`getProcessInstanceCallHierarchyConsistency`](../type-aliases/getProcessInstanceCallHierarchyConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ProcessInstanceCallHierarchyEntry`](../type-aliases/ProcessInstanceCallHierarchyEntry.md)[]\> #### Example ```ts async function getProcessInstanceCallHierarchyExample( processInstanceKey: ProcessInstanceKey ) { const camunda = createCamundaClient(); const result = await camunda.getProcessInstanceCallHierarchy( { processInstanceKey }, { consistency: { waitUpToMs: 5000 } } ); console.log(`Call hierarchy entries: ${result.length}`); } ``` #### Operation Id getProcessInstanceCallHierarchy #### Tags Process instance #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getProcessInstanceSequenceFlows() ```ts getProcessInstanceSequenceFlows( input, consistencyManagement, options?): CancelablePromise; ``` Get sequence flows Get sequence flows taken by the process instance. - #### Parameters ##### input [`getProcessInstanceSequenceFlowsInput`](../type-aliases/getProcessInstanceSequenceFlowsInput.md) ##### consistencyManagement [`getProcessInstanceSequenceFlowsConsistency`](../type-aliases/getProcessInstanceSequenceFlowsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ProcessInstanceSequenceFlowsQueryResult`](../type-aliases/ProcessInstanceSequenceFlowsQueryResult.md)\> #### Example ```ts async function getProcessInstanceSequenceFlowsExample( processInstanceKey: ProcessInstanceKey ) { const camunda = createCamundaClient(); const result = await camunda.getProcessInstanceSequenceFlows( { processInstanceKey }, { consistency: { waitUpToMs: 5000 } } ); for (const flow of result.items ?? []) { console.log(`Sequence flow: ${flow.sequenceFlowId}`); } } ``` #### Operation Id getProcessInstanceSequenceFlows #### Tags Process instance #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getProcessInstanceStatistics() ```ts getProcessInstanceStatistics( input, consistencyManagement, options?): CancelablePromise; ``` Get element instance statistics Get statistics about elements by the process instance key. - #### Parameters ##### input [`getProcessInstanceStatisticsInput`](../type-aliases/getProcessInstanceStatisticsInput.md) ##### consistencyManagement [`getProcessInstanceStatisticsConsistency`](../type-aliases/getProcessInstanceStatisticsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ProcessInstanceElementStatisticsQueryResult`](../type-aliases/ProcessInstanceElementStatisticsQueryResult.md)\> #### Example ```ts async function getProcessInstanceStatisticsExample( processInstanceKey: ProcessInstanceKey ) { const camunda = createCamundaClient(); const result = await camunda.getProcessInstanceStatistics( { processInstanceKey }, { consistency: { waitUpToMs: 5000 } } ); for (const stat of result.items ?? []) { console.log(`Element ${stat.elementId}: active=${stat.active}`); } } ``` #### Operation Id getProcessInstanceStatistics #### Tags Process instance #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getProcessInstanceStatisticsByDefinition() ```ts getProcessInstanceStatisticsByDefinition( input, consistencyManagement, options?): CancelablePromise; ``` Get process instance statistics by definition Returns statistics for active process instances with incidents, grouped by process definition. The result set is scoped to a specific incident error hash code, which must be provided as a filter in the request body. - #### Parameters ##### input [`IncidentProcessInstanceStatisticsByDefinitionQuery`](../type-aliases/IncidentProcessInstanceStatisticsByDefinitionQuery.md) ##### consistencyManagement [`getProcessInstanceStatisticsByDefinitionConsistency`](../type-aliases/getProcessInstanceStatisticsByDefinitionConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`IncidentProcessInstanceStatisticsByDefinitionQueryResult`](../type-aliases/IncidentProcessInstanceStatisticsByDefinitionQueryResult.md)\> #### Example ```ts async function getProcessInstanceStatisticsByDefinitionExample() { const camunda = createCamundaClient(); const result = await camunda.getProcessInstanceStatisticsByDefinition( { filter: { errorHashCode: 12345, }, }, { consistency: { waitUpToMs: 5000 } } ); for (const stat of result.items ?? []) { console.log( `Definition ${stat.processDefinitionId}: ${stat.activeInstancesWithErrorCount} incidents` ); } } ``` #### Operation Id getProcessInstanceStatisticsByDefinition #### Tags Incident #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getProcessInstanceStatisticsByError() ```ts getProcessInstanceStatisticsByError( input, consistencyManagement, options?): CancelablePromise; ``` Get process instance statistics by error Returns statistics for active process instances that currently have active incidents, grouped by incident error hash code. - #### Parameters ##### input [`IncidentProcessInstanceStatisticsByErrorQuery`](../type-aliases/IncidentProcessInstanceStatisticsByErrorQuery.md) ##### consistencyManagement [`getProcessInstanceStatisticsByErrorConsistency`](../type-aliases/getProcessInstanceStatisticsByErrorConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`IncidentProcessInstanceStatisticsByErrorQueryResult`](../type-aliases/IncidentProcessInstanceStatisticsByErrorQueryResult.md)\> #### Example ```ts async function getProcessInstanceStatisticsByErrorExample() { const camunda = createCamundaClient(); const result = await camunda.getProcessInstanceStatisticsByError( {}, { consistency: { waitUpToMs: 5000 } } ); for (const stat of result.items ?? []) { console.log( `Error: ${stat.errorMessage}, count: ${stat.activeInstancesWithErrorCount}` ); } } ``` #### Operation Id getProcessInstanceStatisticsByError #### Tags Incident #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getResource() ```ts getResource( input, consistencyManagement, options?): CancelablePromise; ``` Get resource Returns a deployed resource. :::info This endpoint does not return BPMN process definitions, DMN decision definitions, or form resources. To query BPMN process definitions or DMN decision definitions, use their respective APIs. ::: - #### Parameters ##### input [`getResourceInput`](../type-aliases/getResourceInput.md) ##### consistencyManagement [`getResourceConsistency`](../type-aliases/getResourceConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ResourceResult`](../type-aliases/ResourceResult.md)\> #### Example ```ts async function getResourceExample(resourceKey: ProcessDefinitionKey) { const camunda = createCamundaClient(); const resource = await camunda.getResource( { resourceKey, }, { consistency: { waitUpToMs: 0 } } ); console.log(`Resource: ${resource.resourceName} (${resource.resourceId})`); } ``` #### Operation Id getResource #### Tags Resource #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### ~~getResourceContent()~~ ```ts getResourceContent( input, consistencyManagement, options?): CancelablePromise<{ [key: string]: unknown; }>; ``` Get RPA resource content (deprecated) **Deprecated** — use `/resources/{resourceKey}/content/binary` instead, which supports all resource types and returns content as binary (octet-stream). Returns the content of a deployed RPA resource as JSON. :::info This endpoint only supports RPA resources. For generic resource content in binary format, use the `/resources/{resourceKey}/content/binary` endpoint. ::: #### Parameters ##### input [`getResourceContentInput`](../type-aliases/getResourceContentInput.md) ##### consistencyManagement [`getResourceContentConsistency`](../type-aliases/getResourceContentConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ \[`key`: `string`\]: `unknown`; \}\> #### Deprecated - #### Example ```ts async function getResourceContentExample(resourceKey: ProcessDefinitionKey) { const camunda = createCamundaClient(); const content = await camunda.getResourceContent( { resourceKey, }, { consistency: { waitUpToMs: 0 } } ); console.log(`Content retrieved (type: ${typeof content})`); } ``` #### Operation Id getResourceContent #### Tags Resource #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getResourceContentBinary() ```ts getResourceContentBinary( input, consistencyManagement, options?): CancelablePromise; ``` Get resource content as binary Returns the content of a deployed resource in binary format (octet-stream). :::info This endpoint does not return BPMN process definitions, DMN decision definitions, or form resources. To query BPMN process definitions or DMN decision definitions, use their respective APIs. ::: - #### Parameters ##### input [`getResourceContentBinaryInput`](../type-aliases/getResourceContentBinaryInput.md) ##### consistencyManagement [`getResourceContentBinaryConsistency`](../type-aliases/getResourceContentBinaryConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`Blob`\> #### Example ```ts async function getResourceContentBinaryExample( resourceKey: ProcessDefinitionKey ) { const camunda = createCamundaClient(); const content = await camunda.getResourceContentBinary( { resourceKey, }, { consistency: { waitUpToMs: 0 } } ); console.log(`Binary content retrieved (type: ${typeof content})`); } ``` #### Operation Id getResourceContentBinary #### Tags Resource #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getRole() ```ts getRole( input, consistencyManagement, options?): CancelablePromise; ``` Get role Get a role by its ID. - #### Parameters ##### input [`getRoleInput`](../type-aliases/getRoleInput.md) ##### consistencyManagement [`getRoleConsistency`](../type-aliases/getRoleConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`RoleResult`](../type-aliases/RoleResult.md)\> #### Example ```ts async function getRoleExample(roleId: RoleId) { const camunda = createCamundaClient(); const role = await camunda.getRole( { roleId }, { consistency: { waitUpToMs: 5000 } } ); console.log(`Role: ${role.name}`); } ``` #### Operation Id getRole #### Tags Role #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getStartProcessForm() ```ts getStartProcessForm( input, consistencyManagement, options?): CancelablePromise; ``` Get process start form Get the start form of a process. Note that this endpoint will only return linked forms. This endpoint does not support embedded forms. - #### Parameters ##### input [`getStartProcessFormInput`](../type-aliases/getStartProcessFormInput.md) ##### consistencyManagement [`getStartProcessFormConsistency`](../type-aliases/getStartProcessFormConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void` \| [`FormResult`](../type-aliases/FormResult.md)\> #### Example ```ts async function getStartProcessFormExample( processDefinitionKey: ProcessDefinitionKey ) { const camunda = createCamundaClient(); const form = await camunda.getStartProcessForm( { processDefinitionKey }, { consistency: { waitUpToMs: 5000 } } ); if (form) { console.log(`Form key: ${form.formKey}`); } } ``` #### Operation Id getStartProcessForm #### Tags Process definition #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getStatus() ```ts getStatus(options?): CancelablePromise; ``` Get cluster status Checks the health status of the cluster by verifying if there's at least one partition with a healthy leader. - #### Parameters ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function getStatusExample() { const camunda = createCamundaClient(); await camunda.getStatus(); console.log("Cluster is healthy"); } ``` #### Operation Id getStatus #### Tags Cluster --- ### getSystemConfiguration() ```ts getSystemConfiguration(options?): CancelablePromise; ``` System configuration (alpha) Returns the current system configuration. The response is an envelope that groups settings by feature area. This endpoint is an alpha feature and may be subject to change in future releases. - #### Parameters ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`SystemConfigurationResponse`](../type-aliases/SystemConfigurationResponse.md)\> #### Example ```ts async function getSystemConfigurationExample() { const camunda = createCamundaClient(); const config = await camunda.getSystemConfiguration(); console.log(`Configuration loaded: ${JSON.stringify(config)}`); } ``` #### Operation Id getSystemConfiguration #### Tags System --- ### getTenant() ```ts getTenant( input, consistencyManagement, options?): CancelablePromise; ``` Get tenant Retrieves a single tenant by tenant ID. - #### Parameters ##### input [`getTenantInput`](../type-aliases/getTenantInput.md) ##### consistencyManagement [`getTenantConsistency`](../type-aliases/getTenantConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`TenantResult`](../type-aliases/TenantResult.md)\> #### Example ```ts async function getTenantExample(tenantId: TenantId) { const camunda = createCamundaClient(); const tenant = await camunda.getTenant( { tenantId }, { consistency: { waitUpToMs: 5000 } } ); console.log(`Tenant: ${tenant.name}`); } ``` #### Operation Id getTenant #### Tags Tenant #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getTenantClusterVariable() ```ts getTenantClusterVariable( input, consistencyManagement, options?): CancelablePromise; ``` Get a tenant-scoped cluster variable Get a tenant-scoped cluster variable. - #### Parameters ##### input [`getTenantClusterVariableInput`](../type-aliases/getTenantClusterVariableInput.md) ##### consistencyManagement [`getTenantClusterVariableConsistency`](../type-aliases/getTenantClusterVariableConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ClusterVariableResult`](../type-aliases/ClusterVariableResult.md)\> #### Example ```ts async function getTenantClusterVariableExample( tenantId: TenantId, name: ClusterVariableName ) { const camunda = createCamundaClient(); const variable = await camunda.getTenantClusterVariable( { tenantId, name, }, { consistency: { waitUpToMs: 5000 } } ); console.log(`${variable.name} = ${variable.value}`); } ``` #### Operation Id getTenantClusterVariable #### Tags Cluster Variable #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getTopology() ```ts getTopology(options?): CancelablePromise; ``` Get cluster topology Obtains the current topology of the cluster the gateway is part of. - #### Parameters ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`TopologyResponse`](../type-aliases/TopologyResponse.md)\> #### Example ```ts async function getTopologyExample() { const camunda = createCamundaClient(); const topology = await camunda.getTopology(); console.log(`Cluster size: ${topology.clusterSize}`); console.log(`Partitions: ${topology.partitionsCount}`); for (const broker of topology.brokers ?? []) { console.log(` Broker ${broker.nodeId}: ${broker.host}:${broker.port}`); } } ``` #### Operation Id getTopology #### Tags Cluster --- ### getUsageMetrics() ```ts getUsageMetrics( input, consistencyManagement, options?): CancelablePromise; ``` Get usage metrics Retrieve the usage metrics based on given criteria. - #### Parameters ##### input [`getUsageMetricsInput`](../type-aliases/getUsageMetricsInput.md) ##### consistencyManagement [`getUsageMetricsConsistency`](../type-aliases/getUsageMetricsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`UsageMetricsResponse`](../type-aliases/UsageMetricsResponse.md)\> #### Example ```ts async function getUsageMetricsExample() { const camunda = createCamundaClient(); const metrics = await camunda.getUsageMetrics( { startTime: "2025-01-01T00:00:00Z", endTime: "2025-12-31T23:59:59Z", }, { consistency: { waitUpToMs: 5000 } } ); console.log(`Usage metrics retrieved: ${JSON.stringify(metrics)}`); } ``` #### Operation Id getUsageMetrics #### Tags System #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getUser() ```ts getUser( input, consistencyManagement, options?): CancelablePromise; ``` Get user Get a user by its username. - #### Parameters ##### input [`getUserInput`](../type-aliases/getUserInput.md) ##### consistencyManagement [`getUserConsistency`](../type-aliases/getUserConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`UserResult`](../type-aliases/UserResult.md)\> #### Example ```ts async function getUserExample(username: Username) { const camunda = createCamundaClient(); const user = await camunda.getUser( { username }, { consistency: { waitUpToMs: 5000 } } ); console.log(`User: ${user.name} (${user.email})`); } ``` #### Operation Id getUser #### Tags User #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getUserTask() ```ts getUserTask( input, consistencyManagement, options?): CancelablePromise; ``` Get user task Get the user task by the user task key. - #### Parameters ##### input [`getUserTaskInput`](../type-aliases/getUserTaskInput.md) ##### consistencyManagement [`getUserTaskConsistency`](../type-aliases/getUserTaskConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`UserTaskResult`](../type-aliases/UserTaskResult.md)\> #### Example ```ts async function getUserTaskExample(userTaskKey: UserTaskKey) { const camunda = createCamundaClient(); const task = await camunda.getUserTask( { userTaskKey }, { consistency: { waitUpToMs: 5000 } } ); console.log(`Task: ${task.name} (${task.state})`); } ``` #### Operation Id getUserTask #### Tags User task #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getUserTaskForm() ```ts getUserTaskForm( input, consistencyManagement, options?): CancelablePromise; ``` Get user task form Get the form of a user task. Note that this endpoint will only return linked forms. This endpoint does not support embedded forms. - #### Parameters ##### input [`getUserTaskFormInput`](../type-aliases/getUserTaskFormInput.md) ##### consistencyManagement [`getUserTaskFormConsistency`](../type-aliases/getUserTaskFormConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void` \| [`FormResult`](../type-aliases/FormResult.md)\> #### Example ```ts async function getUserTaskFormExample(userTaskKey: UserTaskKey) { const camunda = createCamundaClient(); const form = await camunda.getUserTaskForm( { userTaskKey }, { consistency: { waitUpToMs: 5000 } } ); if (form) { console.log(`Form key: ${form.formKey}`); } } ``` #### Operation Id getUserTaskForm #### Tags User task #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getVariable() ```ts getVariable( input, consistencyManagement, options?): CancelablePromise; ``` Get variable Get a variable by its key. This endpoint returns both process-level and local (element-scoped) variables. The variable's scopeKey indicates whether it's a process-level variable or scoped to a specific element instance. - #### Parameters ##### input [`getVariableInput`](../type-aliases/getVariableInput.md) ##### consistencyManagement [`getVariableConsistency`](../type-aliases/getVariableConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`VariableResult`](../type-aliases/VariableResult.md)\> #### Example ```ts async function getVariableExample(variableKey: VariableKey) { const camunda = createCamundaClient(); const variable = await camunda.getVariable( { variableKey }, { consistency: { waitUpToMs: 5000 } } ); console.log(`${variable.name} = ${variable.value}`); } ``` #### Operation Id getVariable #### Tags Variable #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### getWorkers() ```ts getWorkers(): any[]; ``` Return a read-only snapshot of currently registered job workers. #### Returns `any`[] --- ### logger() ```ts logger(scope?): Logger; ``` Access a scoped logger (internal & future user emission). #### Parameters ##### scope? `string` #### Returns [`Logger`](../../logger/interfaces/Logger.md) --- ### migrateProcessInstance() ```ts migrateProcessInstance(input, options?): CancelablePromise; ``` Migrate process instance Migrates a process instance to a new process definition. This request can contain multiple mapping instructions to define mapping between the active process instance's elements and target process definition elements. Use this to upgrade a process instance to a new version of a process or to a different process definition, e.g. to keep your running instances up-to-date with the latest process improvements. - #### Parameters ##### input [`migrateProcessInstanceInput`](../type-aliases/migrateProcessInstanceInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function migrateProcessInstanceExample( processInstanceKey: ProcessInstanceKey, targetProcessDefinitionKey: ProcessDefinitionKey, sourceElementId: ElementId, targetElementId: ElementId ) { const camunda = createCamundaClient(); await camunda.migrateProcessInstance({ processInstanceKey, targetProcessDefinitionKey, mappingInstructions: [ { sourceElementId, targetElementId, }, ], }); } ``` #### Operation Id migrateProcessInstance #### Tags Process instance --- ### migrateProcessInstancesBatchOperation() ```ts migrateProcessInstancesBatchOperation(input, options?): CancelablePromise; ``` Migrate process instances (batch) Migrate multiple process instances. Since only process instances with ACTIVE state can be migrated, any given filters for state are ignored and overridden during this batch operation. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). - #### Parameters ##### input [`ProcessInstanceMigrationBatchOperationRequest`](../type-aliases/ProcessInstanceMigrationBatchOperationRequest.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`BatchOperationCreatedResult`](../type-aliases/BatchOperationCreatedResult.md)\> #### Example ```ts async function migrateProcessInstancesBatchOperationExample( processDefinitionKey: ProcessDefinitionKey, targetProcessDefinitionKey: ProcessDefinitionKey, sourceElementId: ElementId, targetElementId: ElementId ) { const camunda = createCamundaClient(); const result = await camunda.migrateProcessInstancesBatchOperation({ filter: { processDefinitionKey, }, migrationPlan: { targetProcessDefinitionKey, mappingInstructions: [ { sourceElementId, targetElementId, }, ], }, }); console.log(`Batch operation key: ${result.batchOperationKey}`); } ``` #### Operation Id migrateProcessInstancesBatchOperation #### Tags Process instance --- ### modifyProcessInstance() ```ts modifyProcessInstance(input, options?): CancelablePromise; ``` Modify process instance Modifies a running process instance. This request can contain multiple instructions to activate an element of the process or to terminate an active instance of an element. Use this to repair a process instance that is stuck on an element or took an unintended path. For example, because an external system is not available or doesn't respond as expected. - #### Parameters ##### input [`modifyProcessInstanceInput`](../type-aliases/modifyProcessInstanceInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function modifyProcessInstanceExample( processInstanceKey: ProcessInstanceKey, elementId: ElementId, elementInstanceKey: ElementInstanceKey ) { const camunda = createCamundaClient(); await camunda.modifyProcessInstance({ processInstanceKey, activateInstructions: [{ elementId }], terminateInstructions: [{ elementInstanceKey }], }); } ``` #### Operation Id modifyProcessInstance #### Tags Process instance --- ### modifyProcessInstancesBatchOperation() ```ts modifyProcessInstancesBatchOperation(input, options?): CancelablePromise; ``` Modify process instances (batch) Modify multiple process instances. Since only process instances with ACTIVE state can be modified, any given filters for state are ignored and overridden during this batch operation. In contrast to single modification operation, it is not possible to add variable instructions or modify by element key. It is only possible to use the element id of the source and target. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). - #### Parameters ##### input [`ProcessInstanceModificationBatchOperationRequest`](../type-aliases/ProcessInstanceModificationBatchOperationRequest.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`BatchOperationCreatedResult`](../type-aliases/BatchOperationCreatedResult.md)\> #### Example ```ts async function modifyProcessInstancesBatchOperationExample( processDefinitionKey: ProcessDefinitionKey, sourceElementId: ElementId, targetElementId: ElementId ) { const camunda = createCamundaClient(); const result = await camunda.modifyProcessInstancesBatchOperation({ filter: { processDefinitionKey, }, moveInstructions: [ { sourceElementId, targetElementId, }, ], }); console.log(`Batch operation key: ${result.batchOperationKey}`); } ``` #### Operation Id modifyProcessInstancesBatchOperation #### Tags Process instance --- ### onAuthHeaders() ```ts onAuthHeaders(h): void; ``` #### Parameters ##### h (`headers`) => \| `Record`\<`string`, `string`\> \| `Promise`\<`Record`\<`string`, `string`\>\> #### Returns `void` --- ### pinClock() ```ts pinClock(input, options?): CancelablePromise; ``` Pin internal clock (alpha) Set a precise, static time for the Zeebe engine's internal clock. When the clock is pinned, it remains at the specified time and does not advance. To change the time, the clock must be pinned again with a new timestamp. This endpoint is an alpha feature and may be subject to change in future releases. - #### Parameters ##### input [`ClockPinRequest`](../type-aliases/ClockPinRequest.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function pinClockExample() { const camunda = createCamundaClient(); await camunda.pinClock({ timestamp: 1735689599000, }); console.log("Clock pinned"); } ``` #### Operation Id pinClock #### Tags Clock --- ### publishMessage() ```ts publishMessage(input, options?): CancelablePromise; ``` Publish message Publishes a single message. Messages are published to specific partitions computed from their correlation keys. Messages can be buffered. The endpoint does not wait for a correlation result. Use the message correlation endpoint for such use cases. - #### Parameters ##### input [`MessagePublicationRequest`](../type-aliases/MessagePublicationRequest.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`MessagePublicationResult`](../type-aliases/MessagePublicationResult.md)\> #### Example ```ts async function publishMessageExample() { const camunda = createCamundaClient(); await camunda.publishMessage({ name: "order-payment-received", correlationKey: "ORD-12345", timeToLive: 60000, variables: { paymentId: "PAY-123", }, }); } ``` #### Operation Id publishMessage #### Tags Message --- ### resetClock() ```ts resetClock(options?): CancelablePromise; ``` Reset internal clock (alpha) Resets the Zeebe engine's internal clock to the current system time, enabling it to tick in real-time. This operation is useful for returning the clock to normal behavior after it has been pinned to a specific time. This endpoint is an alpha feature and may be subject to change in future releases. - #### Parameters ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function resetClockExample() { const camunda = createCamundaClient(); await camunda.resetClock(); console.log("Clock reset"); } ``` #### Operation Id resetClock #### Tags Clock --- ### resolveIncident() ```ts resolveIncident(input, options?): CancelablePromise; ``` Resolve incident Marks the incident as resolved; most likely a call to Update job will be necessary to reset the job's retries, followed by this call. - #### Parameters ##### input [`resolveIncidentInput`](../type-aliases/resolveIncidentInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function resolveIncidentExample(incidentKey: IncidentKey) { const camunda = createCamundaClient(); await camunda.resolveIncident({ incidentKey }); } ``` #### Operation Id resolveIncident #### Tags Incident --- ### resolveIncidentsBatchOperation() ```ts resolveIncidentsBatchOperation(input, options?): CancelablePromise; ``` Resolve related incidents (batch) Resolves multiple instances of process instances. Since only process instances with ACTIVE state can have unresolved incidents, any given filters for state are ignored and overridden during this batch operation. This is done asynchronously, the progress can be tracked using the batchOperationKey from the response and the batch operation status endpoint (/batch-operations/{batchOperationKey}). - #### Parameters ##### input [`ProcessInstanceIncidentResolutionBatchOperationRequest`](../type-aliases/ProcessInstanceIncidentResolutionBatchOperationRequest.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`BatchOperationCreatedResult`](../type-aliases/BatchOperationCreatedResult.md)\> #### Example ```ts async function resolveIncidentsBatchOperationExample( processDefinitionKey: ProcessDefinitionKey ) { const camunda = createCamundaClient(); const result = await camunda.resolveIncidentsBatchOperation({ filter: { processDefinitionKey, }, }); console.log(`Batch operation key: ${result.batchOperationKey}`); } ``` #### Operation Id resolveIncidentsBatchOperation #### Tags Process instance --- ### resolveProcessInstanceIncidents() ```ts resolveProcessInstanceIncidents(input, options?): CancelablePromise; ``` Resolve related incidents Creates a batch operation to resolve multiple incidents of a process instance. - #### Parameters ##### input [`resolveProcessInstanceIncidentsInput`](../type-aliases/resolveProcessInstanceIncidentsInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`BatchOperationCreatedResult`](../type-aliases/BatchOperationCreatedResult.md)\> #### Example ```ts async function resolveProcessInstanceIncidentsExample( processInstanceKey: ProcessInstanceKey ) { const camunda = createCamundaClient(); const result = await camunda.resolveProcessInstanceIncidents({ processInstanceKey, }); console.log(`Batch operation key: ${result.batchOperationKey}`); } ``` #### Operation Id resolveProcessInstanceIncidents #### Tags Process instance --- ### resumeBatchOperation() ```ts resumeBatchOperation(input, options?): CancelablePromise; ``` Resume Batch operation Resumes a suspended batch operation. This is done asynchronously, the progress can be tracked using the batch operation status endpoint (/batch-operations/{batchOperationKey}). - #### Parameters ##### input ###### batchOperationKey [`BatchOperationKey`](../type-aliases/BatchOperationKey.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function resumeBatchOperationExample( batchOperationKey: BatchOperationKey ) { const camunda = createCamundaClient(); await camunda.resumeBatchOperation({ batchOperationKey }); } ``` #### Operation Id resumeBatchOperation #### Tags Batch operation --- ### searchAgentInstances() ```ts searchAgentInstances( input, consistencyManagement, options?): CancelablePromise; ``` Search agent instances Search for agent instances based on given criteria. - #### Parameters ##### input [`AgentInstanceSearchQuery`](../type-aliases/AgentInstanceSearchQuery.md) ##### consistencyManagement [`searchAgentInstancesConsistency`](../type-aliases/searchAgentInstancesConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`AgentInstanceSearchQueryResult`](../type-aliases/AgentInstanceSearchQueryResult.md)\> #### Example ```ts async function searchAgentInstancesExample() { const camunda = createCamundaClient(); const result = await camunda.searchAgentInstances( { filter: { status: { $eq: "IDLE" } }, sort: [{ field: "creationDate", order: "DESC" }], page: { limit: 10 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const instance of result.items ?? []) { console.log(`${instance.agentInstanceKey}: ${instance.status}`); } console.log(`Total: ${result.page.totalItems}`); } ``` #### Operation Id searchAgentInstances #### Tags Agent instance #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchAuditLogs() ```ts searchAuditLogs( input, consistencyManagement, options?): CancelablePromise; ``` Search audit logs Search for audit logs based on given criteria. - #### Parameters ##### input [`AuditLogSearchQueryRequest`](../type-aliases/AuditLogSearchQueryRequest.md) ##### consistencyManagement [`searchAuditLogsConsistency`](../type-aliases/searchAuditLogsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`AuditLogSearchQueryResult`](../type-aliases/AuditLogSearchQueryResult.md)\> #### Example ```ts async function searchAuditLogsExample() { const camunda = createCamundaClient(); const result = await camunda.searchAuditLogs( { page: { limit: 10 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const log of result.items ?? []) { console.log(`${log.auditLogKey}: ${log.operationType}`); } } ``` #### Operation Id searchAuditLogs #### Tags Audit Log #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchAuthorizations() ```ts searchAuthorizations( input, consistencyManagement, options?): CancelablePromise; ``` Search authorizations Search for authorizations based on given criteria. - #### Parameters ##### input [`AuthorizationSearchQuery`](../type-aliases/AuthorizationSearchQuery.md) ##### consistencyManagement [`searchAuthorizationsConsistency`](../type-aliases/searchAuthorizationsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`AuthorizationSearchResult`](../type-aliases/AuthorizationSearchResult.md)\> #### Example ```ts async function searchAuthorizationsExample() { const camunda = createCamundaClient(); const result = await camunda.searchAuthorizations( { filter: { ownerType: "USER" }, page: { limit: 10 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const auth of result.items ?? []) { console.log( `${auth.authorizationKey}: ${auth.ownerId} - ${auth.resourceType}` ); } } ``` #### Operation Id searchAuthorizations #### Tags Authorization #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchBatchOperationItems() ```ts searchBatchOperationItems( input, consistencyManagement, options?): CancelablePromise; ``` Search batch operation items Search for batch operation items based on given criteria. - #### Parameters ##### input [`BatchOperationItemSearchQuery`](../type-aliases/BatchOperationItemSearchQuery.md) ##### consistencyManagement [`searchBatchOperationItemsConsistency`](../type-aliases/searchBatchOperationItemsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`BatchOperationItemSearchQueryResult`](../type-aliases/BatchOperationItemSearchQueryResult.md)\> #### Example ```ts async function searchBatchOperationItemsExample() { const camunda = createCamundaClient(); const result = await camunda.searchBatchOperationItems( { page: { limit: 10 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const item of result.items ?? []) { console.log(`Item: ${item.itemKey} (${item.state})`); } } ``` #### Operation Id searchBatchOperationItems #### Tags Batch operation #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchBatchOperations() ```ts searchBatchOperations( input, consistencyManagement, options?): CancelablePromise; ``` Search batch operations Search for batch operations based on given criteria. - #### Parameters ##### input [`BatchOperationSearchQuery`](../type-aliases/BatchOperationSearchQuery.md) ##### consistencyManagement [`searchBatchOperationsConsistency`](../type-aliases/searchBatchOperationsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`BatchOperationSearchQueryResult`](../type-aliases/BatchOperationSearchQueryResult.md)\> #### Example ```ts async function searchBatchOperationsExample() { const camunda = createCamundaClient(); const result = await camunda.searchBatchOperations( { page: { limit: 10 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const batch of result.items ?? []) { console.log( `${batch.batchOperationKey}: ${batch.batchOperationType} (${batch.state})` ); } } ``` #### Operation Id searchBatchOperations #### Tags Batch operation #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchClientsForGroup() ```ts searchClientsForGroup( input, consistencyManagement, options?): CancelablePromise; ``` Search group clients Search clients assigned to a group. - #### Parameters ##### input [`searchClientsForGroupInput`](../type-aliases/searchClientsForGroupInput.md) ##### consistencyManagement [`searchClientsForGroupConsistency`](../type-aliases/searchClientsForGroupConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`GroupClientSearchResult`](../type-aliases/GroupClientSearchResult.md)\> #### Example ```ts async function searchClientsForGroupExample(groupId: GroupId) { const camunda = createCamundaClient(); const result = await camunda.searchClientsForGroup( { groupId }, { consistency: { waitUpToMs: 5000 } } ); for (const client of result.items ?? []) { console.log(`Client: ${client.clientId}`); } } ``` #### Operation Id searchClientsForGroup #### Tags Group #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchClientsForRole() ```ts searchClientsForRole( input, consistencyManagement, options?): CancelablePromise; ``` Search role clients Search clients with assigned role. - #### Parameters ##### input [`searchClientsForRoleInput`](../type-aliases/searchClientsForRoleInput.md) ##### consistencyManagement [`searchClientsForRoleConsistency`](../type-aliases/searchClientsForRoleConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`RoleClientSearchResult`](../type-aliases/RoleClientSearchResult.md)\> #### Example ```ts async function searchClientsForRoleExample(roleId: RoleId) { const camunda = createCamundaClient(); const result = await camunda.searchClientsForRole( { roleId }, { consistency: { waitUpToMs: 5000 } } ); for (const client of result.items ?? []) { console.log(`Client: ${client.clientId}`); } } ``` #### Operation Id searchClientsForRole #### Tags Role #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchClientsForTenant() ```ts searchClientsForTenant( input, consistencyManagement, options?): CancelablePromise; ``` Search clients for tenant Retrieves a filtered and sorted list of clients for a specified tenant. - #### Parameters ##### input [`searchClientsForTenantInput`](../type-aliases/searchClientsForTenantInput.md) ##### consistencyManagement [`searchClientsForTenantConsistency`](../type-aliases/searchClientsForTenantConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`TenantClientSearchResult`](../type-aliases/TenantClientSearchResult.md)\> #### Example ```ts async function searchClientsForTenantExample(tenantId: TenantId) { const camunda = createCamundaClient(); const result = await camunda.searchClientsForTenant( { tenantId }, { consistency: { waitUpToMs: 5000 } } ); for (const client of result.items ?? []) { console.log(`Client: ${client.clientId}`); } } ``` #### Operation Id searchClientsForTenant #### Tags Tenant #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchClusterVariables() ```ts searchClusterVariables( input, consistencyManagement, options?): CancelablePromise; ``` Search for cluster variables based on given criteria. By default, long variable values in the response are truncated. - #### Parameters ##### input [`searchClusterVariablesInput`](../type-aliases/searchClusterVariablesInput.md) ##### consistencyManagement [`searchClusterVariablesConsistency`](../type-aliases/searchClusterVariablesConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ClusterVariableSearchQueryResult`](../type-aliases/ClusterVariableSearchQueryResult.md)\> #### Example ```ts async function searchClusterVariablesExample() { const camunda = createCamundaClient(); const result = await camunda.searchClusterVariables( { page: { limit: 10 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const variable of result.items ?? []) { console.log(`${variable.name} = ${variable.value}`); } } ``` #### Operation Id searchClusterVariables #### Tags Cluster Variable #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchCorrelatedMessageSubscriptions() ```ts searchCorrelatedMessageSubscriptions( input, consistencyManagement, options?): CancelablePromise; ``` Search correlated message subscriptions Search correlated message subscriptions based on given criteria. - #### Parameters ##### input [`CorrelatedMessageSubscriptionSearchQuery`](../type-aliases/CorrelatedMessageSubscriptionSearchQuery.md) ##### consistencyManagement [`searchCorrelatedMessageSubscriptionsConsistency`](../type-aliases/searchCorrelatedMessageSubscriptionsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`CorrelatedMessageSubscriptionSearchQueryResult`](../type-aliases/CorrelatedMessageSubscriptionSearchQueryResult.md)\> #### Example ```ts async function searchCorrelatedMessageSubscriptionsExample() { const camunda = createCamundaClient(); const result = await camunda.searchCorrelatedMessageSubscriptions( { page: { limit: 10 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const sub of result.items ?? []) { console.log(`Correlated subscription: ${sub.messageName}`); } } ``` #### Operation Id searchCorrelatedMessageSubscriptions #### Tags Message subscription #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchDecisionDefinitions() ```ts searchDecisionDefinitions( input, consistencyManagement, options?): CancelablePromise; ``` Search decision definitions Search for decision definitions based on given criteria. - #### Parameters ##### input [`DecisionDefinitionSearchQuery`](../type-aliases/DecisionDefinitionSearchQuery.md) ##### consistencyManagement [`searchDecisionDefinitionsConsistency`](../type-aliases/searchDecisionDefinitionsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`DecisionDefinitionSearchQueryResult`](../type-aliases/DecisionDefinitionSearchQueryResult.md)\> #### Example ```ts async function searchDecisionDefinitionsExample( decisionDefinitionId: DecisionDefinitionId ) { const camunda = createCamundaClient(); const result = await camunda.searchDecisionDefinitions( { filter: { decisionDefinitionId }, }, { consistency: { waitUpToMs: 5000 } } ); for (const definition of result.items ?? []) { console.log(`${definition.decisionDefinitionId} v${definition.version}`); } } ``` #### Operation Id searchDecisionDefinitions #### Tags Decision definition #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchDecisionInstances() ```ts searchDecisionInstances( input, consistencyManagement, options?): CancelablePromise; ``` Search decision instances Search for decision instances based on given criteria. - #### Parameters ##### input [`DecisionInstanceSearchQuery`](../type-aliases/DecisionInstanceSearchQuery.md) ##### consistencyManagement [`searchDecisionInstancesConsistency`](../type-aliases/searchDecisionInstancesConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`DecisionInstanceSearchQueryResult`](../type-aliases/DecisionInstanceSearchQueryResult.md)\> #### Example ```ts async function searchDecisionInstancesExample() { const camunda = createCamundaClient(); const result = await camunda.searchDecisionInstances( { page: { limit: 10 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const instance of result.items ?? []) { console.log( `${instance.decisionEvaluationKey}: ${instance.decisionDefinitionId}` ); } } ``` #### Operation Id searchDecisionInstances #### Tags Decision instance #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchDecisionRequirements() ```ts searchDecisionRequirements( input, consistencyManagement, options?): CancelablePromise; ``` Search decision requirements Search for decision requirements based on given criteria. - #### Parameters ##### input [`DecisionRequirementsSearchQuery`](../type-aliases/DecisionRequirementsSearchQuery.md) ##### consistencyManagement [`searchDecisionRequirementsConsistency`](../type-aliases/searchDecisionRequirementsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`DecisionRequirementsSearchQueryResult`](../type-aliases/DecisionRequirementsSearchQueryResult.md)\> #### Example ```ts async function searchDecisionRequirementsExample() { const camunda = createCamundaClient(); const result = await camunda.searchDecisionRequirements( { page: { limit: 10 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const req of result.items ?? []) { console.log( `${req.decisionRequirementsKey}: ${req.decisionRequirementsId}` ); } } ``` #### Operation Id searchDecisionRequirements #### Tags Decision requirements #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchElementInstanceIncidents() ```ts searchElementInstanceIncidents( input, consistencyManagement, options?): CancelablePromise; ``` Search for incidents of a specific element instance Search for incidents caused by the specified element instance, including incidents of any child instances created from this element instance. Although the `elementInstanceKey` is provided as a path parameter to indicate the root element instance, you may also include an `elementInstanceKey` within the filter object to narrow results to specific child element instances. This is useful, for example, if you want to isolate incidents associated with nested or subordinate elements within the given element instance while excluding incidents directly tied to the root element itself. - #### Parameters ##### input [`searchElementInstanceIncidentsInput`](../type-aliases/searchElementInstanceIncidentsInput.md) ##### consistencyManagement [`searchElementInstanceIncidentsConsistency`](../type-aliases/searchElementInstanceIncidentsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`IncidentSearchQueryResult`](../type-aliases/IncidentSearchQueryResult.md)\> #### Example ```ts async function searchElementInstanceIncidentsExample( elementInstanceKey: ElementInstanceKey ) { const camunda = createCamundaClient(); const result = await camunda.searchElementInstanceIncidents( { elementInstanceKey }, { consistency: { waitUpToMs: 5000 } } ); for (const incident of result.items ?? []) { console.log(`Incident: ${incident.errorType}`); } } ``` #### Operation Id searchElementInstanceIncidents #### Tags Element instance #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchElementInstances() ```ts searchElementInstances( input, consistencyManagement, options?): CancelablePromise; ``` Search element instances Search for element instances based on given criteria. - #### Parameters ##### input [`ElementInstanceSearchQuery`](../type-aliases/ElementInstanceSearchQuery.md) ##### consistencyManagement [`searchElementInstancesConsistency`](../type-aliases/searchElementInstancesConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ElementInstanceSearchQueryResult`](../type-aliases/ElementInstanceSearchQueryResult.md)\> #### Example ```ts async function searchElementInstancesExample( processInstanceKey: ProcessInstanceKey ) { const camunda = createCamundaClient(); const result = await camunda.searchElementInstances( { filter: { processInstanceKey, }, page: { limit: 10 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const element of result.items ?? []) { console.log(`${element.elementId}: ${element.type} (${element.state})`); } } ``` #### Operation Id searchElementInstances #### Tags Element instance #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchGlobalTaskListeners() ```ts searchGlobalTaskListeners( input, consistencyManagement, options?): CancelablePromise; ``` Search global user task listeners Search for global user task listeners based on given criteria. - #### Parameters ##### input [`GlobalTaskListenerSearchQueryRequest`](../type-aliases/GlobalTaskListenerSearchQueryRequest.md) ##### consistencyManagement [`searchGlobalTaskListenersConsistency`](../type-aliases/searchGlobalTaskListenersConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`GlobalTaskListenerSearchQueryResult`](../type-aliases/GlobalTaskListenerSearchQueryResult.md)\> #### Example ```ts async function searchGlobalTaskListenersExample() { const camunda = createCamundaClient(); const result = await camunda.searchGlobalTaskListeners( { page: { limit: 10 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const listener of result.items ?? []) { console.log(`${listener.id}: ${listener.type} (${listener.eventTypes})`); } } ``` #### Operation Id searchGlobalTaskListeners #### Tags Global listener #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchGroupIdsForTenant() ```ts searchGroupIdsForTenant( input, consistencyManagement, options?): CancelablePromise; ``` Search groups for tenant Retrieves a filtered and sorted list of groups for a specified tenant. - #### Parameters ##### input [`searchGroupIdsForTenantInput`](../type-aliases/searchGroupIdsForTenantInput.md) ##### consistencyManagement [`searchGroupIdsForTenantConsistency`](../type-aliases/searchGroupIdsForTenantConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`TenantGroupSearchResult`](../type-aliases/TenantGroupSearchResult.md)\> #### Example ```ts async function searchGroupIdsForTenantExample(tenantId: TenantId) { const camunda = createCamundaClient(); const result = await camunda.searchGroupIdsForTenant( { tenantId }, { consistency: { waitUpToMs: 5000 } } ); for (const group of result.items ?? []) { console.log(`Group: ${group.groupId}`); } } ``` #### Operation Id searchGroupIdsForTenant #### Tags Tenant #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchGroups() ```ts searchGroups( input, consistencyManagement, options?): CancelablePromise; ``` Search groups Search for groups based on given criteria. - #### Parameters ##### input [`GroupSearchQueryRequest`](../type-aliases/GroupSearchQueryRequest.md) ##### consistencyManagement [`searchGroupsConsistency`](../type-aliases/searchGroupsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`GroupSearchQueryResult`](../type-aliases/GroupSearchQueryResult.md)\> #### Example ```ts async function searchGroupsExample() { const camunda = createCamundaClient(); const result = await camunda.searchGroups( { page: { limit: 10 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const group of result.items ?? []) { console.log(`${group.groupId}: ${group.name}`); } } ``` #### Operation Id searchGroups #### Tags Group #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchGroupsForRole() ```ts searchGroupsForRole( input, consistencyManagement, options?): CancelablePromise; ``` Search role groups Search groups with assigned role. - #### Parameters ##### input [`searchGroupsForRoleInput`](../type-aliases/searchGroupsForRoleInput.md) ##### consistencyManagement [`searchGroupsForRoleConsistency`](../type-aliases/searchGroupsForRoleConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`RoleGroupSearchResult`](../type-aliases/RoleGroupSearchResult.md)\> #### Example ```ts async function searchGroupsForRoleExample(roleId: RoleId) { const camunda = createCamundaClient(); const result = await camunda.searchGroupsForRole( { roleId }, { consistency: { waitUpToMs: 5000 } } ); for (const group of result.items ?? []) { console.log(`Group: ${group.groupId}`); } } ``` #### Operation Id searchGroupsForRole #### Tags Role #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchIncidents() ```ts searchIncidents( input, consistencyManagement, options?): CancelablePromise; ``` Search incidents Search for incidents based on given criteria. - #### Parameters ##### input [`IncidentSearchQuery`](../type-aliases/IncidentSearchQuery.md) ##### consistencyManagement [`searchIncidentsConsistency`](../type-aliases/searchIncidentsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`IncidentSearchQueryResult`](../type-aliases/IncidentSearchQueryResult.md)\> #### Example ```ts async function searchIncidentsExample() { const camunda = createCamundaClient(); const result = await camunda.searchIncidents( { filter: { state: "ACTIVE" }, sort: [{ field: "creationTime", order: "DESC" }], page: { limit: 20 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const incident of result.items ?? []) { console.log( `${incident.incidentKey}: ${incident.errorType} — ${incident.errorMessage}` ); } console.log(`Total active incidents: ${result.page.totalItems}`); } ``` #### Operation Id searchIncidents #### Tags Incident #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchJobs() ```ts searchJobs( input, consistencyManagement, options?): CancelablePromise; ``` Search jobs Search for jobs based on given criteria. - #### Parameters ##### input [`JobSearchQuery`](../type-aliases/JobSearchQuery.md) ##### consistencyManagement [`searchJobsConsistency`](../type-aliases/searchJobsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`JobSearchQueryResult`](../type-aliases/JobSearchQueryResult.md)\> #### Example ```ts async function searchJobsExample() { const camunda = createCamundaClient(); const result = await camunda.searchJobs( { filter: { type: "payment-processing", state: "CREATED" }, page: { limit: 10 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const job of result.items ?? []) { console.log(`Job ${job.jobKey}: ${job.type} (${job.state})`); } } ``` #### Operation Id searchJobs #### Tags Job #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchMappingRule() ```ts searchMappingRule( input, consistencyManagement, options?): CancelablePromise; ``` Search mapping rules Search for mapping rules based on given criteria. - #### Parameters ##### input [`MappingRuleSearchQueryRequest`](../type-aliases/MappingRuleSearchQueryRequest.md) ##### consistencyManagement [`searchMappingRuleConsistency`](../type-aliases/searchMappingRuleConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`MappingRuleSearchQueryResult`](../type-aliases/MappingRuleSearchQueryResult.md)\> #### Example ```ts async function searchMappingRulesExample() { const camunda = createCamundaClient(); const result = await camunda.searchMappingRule( { page: { limit: 10 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const rule of result.items ?? []) { console.log(`${rule.mappingRuleId}: ${rule.name}`); } } ``` #### Operation Id searchMappingRule #### Tags Mapping rule #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchMappingRulesForGroup() ```ts searchMappingRulesForGroup( input, consistencyManagement, options?): CancelablePromise; ``` Search group mapping rules Search mapping rules assigned to a group. - #### Parameters ##### input [`searchMappingRulesForGroupInput`](../type-aliases/searchMappingRulesForGroupInput.md) ##### consistencyManagement [`searchMappingRulesForGroupConsistency`](../type-aliases/searchMappingRulesForGroupConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`GroupMappingRuleSearchResult`](../type-aliases/GroupMappingRuleSearchResult.md)\> #### Example ```ts async function searchMappingRulesForGroupExample(groupId: GroupId) { const camunda = createCamundaClient(); const result = await camunda.searchMappingRulesForGroup( { groupId }, { consistency: { waitUpToMs: 5000 } } ); for (const rule of result.items ?? []) { console.log(`Mapping rule: ${rule.name}`); } } ``` #### Operation Id searchMappingRulesForGroup #### Tags Group #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchMappingRulesForRole() ```ts searchMappingRulesForRole( input, consistencyManagement, options?): CancelablePromise; ``` Search role mapping rules Search mapping rules with assigned role. - #### Parameters ##### input [`searchMappingRulesForRoleInput`](../type-aliases/searchMappingRulesForRoleInput.md) ##### consistencyManagement [`searchMappingRulesForRoleConsistency`](../type-aliases/searchMappingRulesForRoleConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`RoleMappingRuleSearchResult`](../type-aliases/RoleMappingRuleSearchResult.md)\> #### Example ```ts async function searchMappingRulesForRoleExample(roleId: RoleId) { const camunda = createCamundaClient(); const result = await camunda.searchMappingRulesForRole( { roleId }, { consistency: { waitUpToMs: 5000 } } ); for (const rule of result.items ?? []) { console.log(`Mapping rule: ${rule.name}`); } } ``` #### Operation Id searchMappingRulesForRole #### Tags Role #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchMappingRulesForTenant() ```ts searchMappingRulesForTenant( input, consistencyManagement, options?): CancelablePromise; ``` Search mapping rules for tenant Retrieves a filtered and sorted list of MappingRules for a specified tenant. - #### Parameters ##### input [`searchMappingRulesForTenantInput`](../type-aliases/searchMappingRulesForTenantInput.md) ##### consistencyManagement [`searchMappingRulesForTenantConsistency`](../type-aliases/searchMappingRulesForTenantConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`TenantMappingRuleSearchResult`](../type-aliases/TenantMappingRuleSearchResult.md)\> #### Example ```ts async function searchMappingRulesForTenantExample(tenantId: TenantId) { const camunda = createCamundaClient(); const result = await camunda.searchMappingRulesForTenant( { tenantId }, { consistency: { waitUpToMs: 5000 } } ); for (const rule of result.items ?? []) { console.log(`Mapping rule: ${rule.name}`); } } ``` #### Operation Id searchMappingRulesForTenant #### Tags Tenant #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchMessageSubscriptions() ```ts searchMessageSubscriptions( input, consistencyManagement, options?): CancelablePromise; ``` Search message subscriptions Search for message subscriptions based on given criteria. By default, both start and intermediate event subscriptions are returned. Use the `messageSubscriptionType` filter to restrict results to a single type. **Version notes:** - Start event subscriptions are only captured for deployments made with 8.10 or later. - The `messageSubscriptionType` field is only populated for data created with Camunda 8.10 or later. For pre-8.10 data, intermediate event entries have no `messageSubscriptionType` value stored. For convenience, the API returns `PROCESS_EVENT` as a default for such search results, though. - Searching for intermediate event subscriptions **including legacy data** can be achieved by filtering for `messageSubscriptionType` not matching `START_EVENT`. * #### Parameters ##### input [`MessageSubscriptionSearchQuery`](../type-aliases/MessageSubscriptionSearchQuery.md) ##### consistencyManagement [`searchMessageSubscriptionsConsistency`](../type-aliases/searchMessageSubscriptionsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`MessageSubscriptionSearchQueryResult`](../type-aliases/MessageSubscriptionSearchQueryResult.md)\> #### Example ```ts async function searchMessageSubscriptionsExample() { const camunda = createCamundaClient(); const result = await camunda.searchMessageSubscriptions( { page: { limit: 10 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const sub of result.items ?? []) { console.log(`Subscription: ${sub.messageName}`); } } ``` #### Operation Id searchMessageSubscriptions #### Tags Message subscription #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchProcessDefinitions() ```ts searchProcessDefinitions( input, consistencyManagement, options?): CancelablePromise; ``` Search process definitions Search for process definitions based on given criteria. - #### Parameters ##### input [`ProcessDefinitionSearchQuery`](../type-aliases/ProcessDefinitionSearchQuery.md) ##### consistencyManagement [`searchProcessDefinitionsConsistency`](../type-aliases/searchProcessDefinitionsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ProcessDefinitionSearchQueryResult`](../type-aliases/ProcessDefinitionSearchQueryResult.md)\> #### Example ```ts async function searchProcessDefinitionsExample() { const camunda = createCamundaClient(); const result = await camunda.searchProcessDefinitions( { page: { limit: 10 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const def of result.items ?? []) { console.log( `${def.processDefinitionKey}: ${def.processDefinitionId} v${def.version}` ); } } ``` #### Operation Id searchProcessDefinitions #### Tags Process definition #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchProcessInstanceIncidents() ```ts searchProcessInstanceIncidents( input, consistencyManagement, options?): CancelablePromise; ``` Search related incidents Search for incidents caused by the process instance or any of its called process or decision instances. Although the `processInstanceKey` is provided as a path parameter to indicate the root process instance, you may also include a `processInstanceKey` within the filter object to narrow results to specific child process instances. This is useful, for example, if you want to isolate incidents associated with subprocesses or called processes under the root instance while excluding incidents directly tied to the root. - #### Parameters ##### input [`searchProcessInstanceIncidentsInput`](../type-aliases/searchProcessInstanceIncidentsInput.md) ##### consistencyManagement [`searchProcessInstanceIncidentsConsistency`](../type-aliases/searchProcessInstanceIncidentsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`IncidentSearchQueryResult`](../type-aliases/IncidentSearchQueryResult.md)\> #### Example ```ts async function searchProcessInstanceIncidentsExample( processInstanceKey: ProcessInstanceKey ) { const camunda = createCamundaClient(); const result = await camunda.searchProcessInstanceIncidents( { processInstanceKey, }, { consistency: { waitUpToMs: 5000 } } ); for (const incident of result.items ?? []) { console.log(`Incident: ${incident.errorType} - ${incident.errorMessage}`); } } ``` #### Operation Id searchProcessInstanceIncidents #### Tags Process instance #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchProcessInstances() ```ts searchProcessInstances( input, consistencyManagement, options?): CancelablePromise; ``` Search process instances Search for process instances based on given criteria. - #### Parameters ##### input [`ProcessInstanceSearchQuery`](../type-aliases/ProcessInstanceSearchQuery.md) ##### consistencyManagement [`searchProcessInstancesConsistency`](../type-aliases/searchProcessInstancesConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ProcessInstanceSearchQueryResult`](../type-aliases/ProcessInstanceSearchQueryResult.md)\> #### Example ```ts async function searchProcessInstancesExample( processDefinitionId: ProcessDefinitionId ) { const camunda = createCamundaClient(); const result = await camunda.searchProcessInstances( { filter: { processDefinitionId }, sort: [{ field: "startDate", order: "DESC" }], page: { limit: 10 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const instance of result.items ?? []) { console.log(`${instance.processInstanceKey}: ${instance.state}`); } console.log(`Total: ${result.page.totalItems}`); } ``` #### Operation Id searchProcessInstances #### Tags Process instance #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchResources() ```ts searchResources( input, consistencyManagement, options?): CancelablePromise; ``` Search resources Search for deployed resources based on given criteria. :::info This endpoint does not return BPMN process definitions, DMN decision definitions, or form resources. To query BPMN process definitions or DMN decision definitions, use their respective search APIs. ::: - #### Parameters ##### input [`ResourceSearchQuery`](../type-aliases/ResourceSearchQuery.md) ##### consistencyManagement [`searchResourcesConsistency`](../type-aliases/searchResourcesConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ResourceSearchQueryResult`](../type-aliases/ResourceSearchQueryResult.md)\> #### Example ```ts async function searchResourcesExample() { const camunda = createCamundaClient(); const result = await camunda.searchResources( { page: { limit: 10 } }, { consistency: { waitUpToMs: 5000 } } ); for (const resource of result.items ?? []) { console.log(`Resource: ${resource.resourceName}`); } } ``` #### Operation Id searchResources #### Tags Resource #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchRoles() ```ts searchRoles( input, consistencyManagement, options?): CancelablePromise; ``` Search roles Search for roles based on given criteria. - #### Parameters ##### input [`RoleSearchQueryRequest`](../type-aliases/RoleSearchQueryRequest.md) ##### consistencyManagement [`searchRolesConsistency`](../type-aliases/searchRolesConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`RoleSearchQueryResult`](../type-aliases/RoleSearchQueryResult.md)\> #### Example ```ts async function searchRolesExample() { const camunda = createCamundaClient(); const result = await camunda.searchRoles( { page: { limit: 10 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const role of result.items ?? []) { console.log(`${role.roleId}: ${role.name}`); } } ``` #### Operation Id searchRoles #### Tags Role #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchRolesForGroup() ```ts searchRolesForGroup( input, consistencyManagement, options?): CancelablePromise; ``` Search group roles Search roles assigned to a group. - #### Parameters ##### input [`searchRolesForGroupInput`](../type-aliases/searchRolesForGroupInput.md) ##### consistencyManagement [`searchRolesForGroupConsistency`](../type-aliases/searchRolesForGroupConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`GroupRoleSearchResult`](../type-aliases/GroupRoleSearchResult.md)\> #### Example ```ts async function searchRolesForGroupExample(groupId: GroupId) { const camunda = createCamundaClient(); const result = await camunda.searchRolesForGroup( { groupId }, { consistency: { waitUpToMs: 5000 } } ); for (const role of result.items ?? []) { console.log(`Role: ${role.name}`); } } ``` #### Operation Id searchRolesForGroup #### Tags Group #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchRolesForTenant() ```ts searchRolesForTenant( input, consistencyManagement, options?): CancelablePromise; ``` Search roles for tenant Retrieves a filtered and sorted list of roles for a specified tenant. - #### Parameters ##### input [`searchRolesForTenantInput`](../type-aliases/searchRolesForTenantInput.md) ##### consistencyManagement [`searchRolesForTenantConsistency`](../type-aliases/searchRolesForTenantConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`TenantRoleSearchResult`](../type-aliases/TenantRoleSearchResult.md)\> #### Example ```ts async function searchRolesForTenantExample(tenantId: TenantId) { const camunda = createCamundaClient(); const result = await camunda.searchRolesForTenant( { tenantId }, { consistency: { waitUpToMs: 5000 } } ); for (const role of result.items ?? []) { console.log(`Role: ${role.name}`); } } ``` #### Operation Id searchRolesForTenant #### Tags Tenant #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchTenants() ```ts searchTenants( input, consistencyManagement, options?): CancelablePromise; ``` Search tenants Retrieves a filtered and sorted list of tenants. - #### Parameters ##### input [`TenantSearchQueryRequest`](../type-aliases/TenantSearchQueryRequest.md) ##### consistencyManagement [`searchTenantsConsistency`](../type-aliases/searchTenantsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`TenantSearchQueryResult`](../type-aliases/TenantSearchQueryResult.md)\> #### Example ```ts async function searchTenantsExample() { const camunda = createCamundaClient(); const result = await camunda.searchTenants( { page: { limit: 10 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const tenant of result.items ?? []) { console.log(`${tenant.tenantId}: ${tenant.name}`); } } ``` #### Operation Id searchTenants #### Tags Tenant #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchUsers() ```ts searchUsers( input, consistencyManagement, options?): CancelablePromise; ``` Search users Search for users based on given criteria. - #### Parameters ##### input [`UserSearchQueryRequest`](../type-aliases/UserSearchQueryRequest.md) ##### consistencyManagement [`searchUsersConsistency`](../type-aliases/searchUsersConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`UserSearchResult`](../type-aliases/UserSearchResult.md)\> #### Example ```ts async function searchUsersExample() { const camunda = createCamundaClient(); const result = await camunda.searchUsers( { filter: {}, page: { limit: 10 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const user of result.items ?? []) { console.log(`${user.username}: ${user.name}`); } } ``` #### Operation Id searchUsers #### Tags User #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchUsersForGroup() ```ts searchUsersForGroup( input, consistencyManagement, options?): CancelablePromise; ``` Search group users Search users assigned to a group. - #### Parameters ##### input [`searchUsersForGroupInput`](../type-aliases/searchUsersForGroupInput.md) ##### consistencyManagement [`searchUsersForGroupConsistency`](../type-aliases/searchUsersForGroupConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`GroupUserSearchResult`](../type-aliases/GroupUserSearchResult.md)\> #### Example ```ts async function searchUsersForGroupExample(groupId: GroupId) { const camunda = createCamundaClient(); const result = await camunda.searchUsersForGroup( { groupId }, { consistency: { waitUpToMs: 5000 } } ); for (const user of result.items ?? []) { console.log(`Member: ${user.username}`); } } ``` #### Operation Id searchUsersForGroup #### Tags Group #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchUsersForRole() ```ts searchUsersForRole( input, consistencyManagement, options?): CancelablePromise; ``` Search role users Search users with assigned role. - #### Parameters ##### input [`searchUsersForRoleInput`](../type-aliases/searchUsersForRoleInput.md) ##### consistencyManagement [`searchUsersForRoleConsistency`](../type-aliases/searchUsersForRoleConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`RoleUserSearchResult`](../type-aliases/RoleUserSearchResult.md)\> #### Example ```ts async function searchUsersForRoleExample(roleId: RoleId) { const camunda = createCamundaClient(); const result = await camunda.searchUsersForRole( { roleId }, { consistency: { waitUpToMs: 5000 } } ); for (const user of result.items ?? []) { console.log(`User: ${user.username}`); } } ``` #### Operation Id searchUsersForRole #### Tags Role #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchUsersForTenant() ```ts searchUsersForTenant( input, consistencyManagement, options?): CancelablePromise; ``` Search users for tenant Retrieves a filtered and sorted list of users for a specified tenant. - #### Parameters ##### input [`searchUsersForTenantInput`](../type-aliases/searchUsersForTenantInput.md) ##### consistencyManagement [`searchUsersForTenantConsistency`](../type-aliases/searchUsersForTenantConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`TenantUserSearchResult`](../type-aliases/TenantUserSearchResult.md)\> #### Example ```ts async function searchUsersForTenantExample(tenantId: TenantId) { const camunda = createCamundaClient(); const result = await camunda.searchUsersForTenant( { tenantId }, { consistency: { waitUpToMs: 5000 } } ); for (const user of result.items ?? []) { console.log(`Tenant member: ${user.username}`); } } ``` #### Operation Id searchUsersForTenant #### Tags Tenant #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchUserTaskAuditLogs() ```ts searchUserTaskAuditLogs( input, consistencyManagement, options?): CancelablePromise; ``` Search user task audit logs Search for user task audit logs based on given criteria. - #### Parameters ##### input [`searchUserTaskAuditLogsInput`](../type-aliases/searchUserTaskAuditLogsInput.md) ##### consistencyManagement [`searchUserTaskAuditLogsConsistency`](../type-aliases/searchUserTaskAuditLogsConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`AuditLogSearchQueryResult`](../type-aliases/AuditLogSearchQueryResult.md)\> #### Example ```ts async function searchUserTaskAuditLogsExample(userTaskKey: UserTaskKey) { const camunda = createCamundaClient(); const result = await camunda.searchUserTaskAuditLogs( { userTaskKey }, { consistency: { waitUpToMs: 5000 } } ); for (const log of result.items ?? []) { console.log(`Audit: ${log.operationType} at ${log.timestamp}`); } } ``` #### Operation Id searchUserTaskAuditLogs #### Tags User task #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchUserTaskEffectiveVariables() ```ts searchUserTaskEffectiveVariables( input, consistencyManagement, options?): CancelablePromise; ``` Search user task effective variables Search for the effective variables of a user task. This endpoint returns deduplicated variables where each variable name appears at most once. When the same variable name exists at multiple scope levels in the scope hierarchy, the value from the innermost scope (closest to the user task) takes precedence. This is useful for retrieving the actual runtime state of variables as seen by the user task. By default, long variable values in the response are truncated. - #### Parameters ##### input [`searchUserTaskEffectiveVariablesInput`](../type-aliases/searchUserTaskEffectiveVariablesInput.md) ##### consistencyManagement [`searchUserTaskEffectiveVariablesConsistency`](../type-aliases/searchUserTaskEffectiveVariablesConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`VariableSearchQueryResult`](../type-aliases/VariableSearchQueryResult.md)\> #### Example ```ts async function searchUserTaskEffectiveVariablesExample( userTaskKey: UserTaskKey ) { const camunda = createCamundaClient(); const result = await camunda.searchUserTaskEffectiveVariables( { userTaskKey }, { consistency: { waitUpToMs: 5000 } } ); for (const variable of result.items ?? []) { console.log(`${variable.name} = ${variable.value}`); } } ``` #### Operation Id searchUserTaskEffectiveVariables #### Tags User task #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchUserTasks() ```ts searchUserTasks( input, consistencyManagement, options?): CancelablePromise; ``` Search user tasks Search for user tasks based on given criteria. - #### Parameters ##### input [`UserTaskSearchQuery`](../type-aliases/UserTaskSearchQuery.md) ##### consistencyManagement [`searchUserTasksConsistency`](../type-aliases/searchUserTasksConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`UserTaskSearchQueryResult`](../type-aliases/UserTaskSearchQueryResult.md)\> #### Example ```ts async function searchUserTasksExample() { const camunda = createCamundaClient(); const result = await camunda.searchUserTasks( { filter: { assignee: "alice", state: "CREATED" }, sort: [{ field: "creationDate", order: "DESC" }], page: { limit: 10 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const task of result.items ?? []) { console.log(`${task.userTaskKey}: ${task.name} (${task.state})`); } } ``` #### Operation Id searchUserTasks #### Tags User task #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchUserTaskVariables() ```ts searchUserTaskVariables( input, consistencyManagement, options?): CancelablePromise; ``` Search user task variables Search for user task variables based on given criteria. This endpoint returns all variable documents visible from the user task's scope, including variables from parent scopes in the scope hierarchy. If the same variable name exists at multiple scope levels, each scope's variable is returned as a separate result. Use the `/user-tasks/{userTaskKey}/effective-variables/search` endpoint to get deduplicated variables where the innermost scope takes precedence. By default, long variable values in the response are truncated. - #### Parameters ##### input [`searchUserTaskVariablesInput`](../type-aliases/searchUserTaskVariablesInput.md) ##### consistencyManagement [`searchUserTaskVariablesConsistency`](../type-aliases/searchUserTaskVariablesConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`VariableSearchQueryResult`](../type-aliases/VariableSearchQueryResult.md)\> #### Example ```ts async function searchUserTaskVariablesExample(userTaskKey: UserTaskKey) { const camunda = createCamundaClient(); const result = await camunda.searchUserTaskVariables( { userTaskKey }, { consistency: { waitUpToMs: 5000 } } ); for (const variable of result.items ?? []) { console.log(`${variable.name} = ${variable.value}`); } } ``` #### Operation Id searchUserTaskVariables #### Tags User task #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### searchVariables() ```ts searchVariables( input, consistencyManagement, options?): CancelablePromise; ``` Search variables Search for variables based on given criteria. This endpoint returns variables that exist directly at the specified scopes - it does not include variables from parent scopes that would be visible through the scope hierarchy. Variables can be process-level (scoped to the process instance) or local (scoped to specific BPMN elements like tasks, subprocesses, etc.). By default, long variable values in the response are truncated. - #### Parameters ##### input [`searchVariablesInput`](../type-aliases/searchVariablesInput.md) ##### consistencyManagement [`searchVariablesConsistency`](../type-aliases/searchVariablesConsistency.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`VariableSearchQueryResult`](../type-aliases/VariableSearchQueryResult.md)\> #### Example ```ts async function searchVariablesExample(processInstanceKey: ProcessInstanceKey) { const camunda = createCamundaClient(); const result = await camunda.searchVariables( { filter: { processInstanceKey, }, page: { limit: 10 }, }, { consistency: { waitUpToMs: 5000 } } ); for (const variable of result.items ?? []) { console.log(`${variable.name} = ${variable.value}`); } } ``` #### Operation Id searchVariables #### Tags Variable #### Consistency eventual - this endpoint is backed by data that is eventually consistent with the system state. --- ### stopAllWorkers() ```ts stopAllWorkers(): void; ``` Stop all registered job workers (best-effort) and terminate the shared thread pool. #### Returns `void` --- ### suspendBatchOperation() ```ts suspendBatchOperation(input, options?): CancelablePromise; ``` Suspend Batch operation Suspends a running batch operation. This is done asynchronously, the progress can be tracked using the batch operation status endpoint (/batch-operations/{batchOperationKey}). - #### Parameters ##### input ###### batchOperationKey [`BatchOperationKey`](../type-aliases/BatchOperationKey.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function suspendBatchOperationExample( batchOperationKey: BatchOperationKey ) { const camunda = createCamundaClient(); await camunda.suspendBatchOperation({ batchOperationKey }); } ``` #### Operation Id suspendBatchOperation #### Tags Batch operation --- ### throwJobError() ```ts throwJobError(input, options?): CancelablePromise; ``` Throw error for job Reports a business error (i.e. non-technical) that occurs while processing a job. - #### Parameters ##### input [`throwJobErrorInput`](../type-aliases/throwJobErrorInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function throwJobErrorExample(jobKey: JobKey) { const camunda = createCamundaClient(); await camunda.throwJobError({ jobKey, errorCode: "PAYMENT_FAILED", errorMessage: "Payment provider returned error", }); } ``` #### Operation Id throwJobError #### Tags Job --- ### unassignClientFromGroup() ```ts unassignClientFromGroup(input, options?): CancelablePromise; ``` Unassign a client from a group Unassigns a client from a group. The client is removed as a group member, with associated authorizations, roles, and tenant assignments no longer applied. - #### Parameters ##### input [`unassignClientFromGroupInput`](../type-aliases/unassignClientFromGroupInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function unassignClientFromGroupExample( groupId: GroupId, clientId: ClientId ) { const camunda = createCamundaClient(); await camunda.unassignClientFromGroup({ groupId, clientId, }); } ``` #### Operation Id unassignClientFromGroup #### Tags Group --- ### unassignClientFromTenant() ```ts unassignClientFromTenant(input, options?): CancelablePromise; ``` Unassign a client from a tenant Unassigns the client from the specified tenant. The client can no longer access tenant data. - #### Parameters ##### input [`unassignClientFromTenantInput`](../type-aliases/unassignClientFromTenantInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function unassignClientFromTenantExample( tenantId: TenantId, clientId: ClientId ) { const camunda = createCamundaClient(); await camunda.unassignClientFromTenant({ tenantId, clientId, }); } ``` #### Operation Id unassignClientFromTenant #### Tags Tenant --- ### unassignGroupFromTenant() ```ts unassignGroupFromTenant(input, options?): CancelablePromise; ``` Unassign a group from a tenant Unassigns a group from a specified tenant. Members of the group (users, clients) will no longer have access to the tenant's data - except they are assigned directly to the tenant. - #### Parameters ##### input [`unassignGroupFromTenantInput`](../type-aliases/unassignGroupFromTenantInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function unassignGroupFromTenantExample( tenantId: TenantId, groupId: GroupId ) { const camunda = createCamundaClient(); await camunda.unassignGroupFromTenant({ tenantId, groupId, }); } ``` #### Operation Id unassignGroupFromTenant #### Tags Tenant --- ### unassignMappingRuleFromGroup() ```ts unassignMappingRuleFromGroup(input, options?): CancelablePromise; ``` Unassign a mapping rule from a group Unassigns a mapping rule from a group. - #### Parameters ##### input [`unassignMappingRuleFromGroupInput`](../type-aliases/unassignMappingRuleFromGroupInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function unassignMappingRuleFromGroupExample( groupId: GroupId, mappingRuleId: MappingRuleId ) { const camunda = createCamundaClient(); await camunda.unassignMappingRuleFromGroup({ groupId, mappingRuleId, }); } ``` #### Operation Id unassignMappingRuleFromGroup #### Tags Group --- ### unassignMappingRuleFromTenant() ```ts unassignMappingRuleFromTenant(input, options?): CancelablePromise; ``` Unassign a mapping rule from a tenant Unassigns a single mapping rule from a specified tenant without deleting the rule. - #### Parameters ##### input [`unassignMappingRuleFromTenantInput`](../type-aliases/unassignMappingRuleFromTenantInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function unassignMappingRuleFromTenantExample( tenantId: TenantId, mappingRuleId: MappingRuleId ) { const camunda = createCamundaClient(); await camunda.unassignMappingRuleFromTenant({ tenantId, mappingRuleId, }); } ``` #### Operation Id unassignMappingRuleFromTenant #### Tags Tenant --- ### unassignRoleFromClient() ```ts unassignRoleFromClient(input, options?): CancelablePromise; ``` Unassign a role from a client Unassigns the specified role from the client. The client will no longer inherit the authorizations associated with this role. - #### Parameters ##### input [`unassignRoleFromClientInput`](../type-aliases/unassignRoleFromClientInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function unassignRoleFromClientExample( roleId: RoleId, clientId: ClientId ) { const camunda = createCamundaClient(); await camunda.unassignRoleFromClient({ roleId, clientId, }); } ``` #### Operation Id unassignRoleFromClient #### Tags Role --- ### unassignRoleFromGroup() ```ts unassignRoleFromGroup(input, options?): CancelablePromise; ``` Unassign a role from a group Unassigns the specified role from the group. All group members (user or client) no longer inherit the authorizations associated with this role. - #### Parameters ##### input [`unassignRoleFromGroupInput`](../type-aliases/unassignRoleFromGroupInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function unassignRoleFromGroupExample(roleId: RoleId, groupId: GroupId) { const camunda = createCamundaClient(); await camunda.unassignRoleFromGroup({ roleId, groupId, }); } ``` #### Operation Id unassignRoleFromGroup #### Tags Role --- ### unassignRoleFromMappingRule() ```ts unassignRoleFromMappingRule(input, options?): CancelablePromise; ``` Unassign a role from a mapping rule Unassigns a role from a mapping rule. - #### Parameters ##### input [`unassignRoleFromMappingRuleInput`](../type-aliases/unassignRoleFromMappingRuleInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function unassignRoleFromMappingRuleExample( roleId: RoleId, mappingRuleId: MappingRuleId ) { const camunda = createCamundaClient(); await camunda.unassignRoleFromMappingRule({ roleId, mappingRuleId, }); } ``` #### Operation Id unassignRoleFromMappingRule #### Tags Role --- ### unassignRoleFromTenant() ```ts unassignRoleFromTenant(input, options?): CancelablePromise; ``` Unassign a role from a tenant Unassigns a role from a specified tenant. Users, Clients or Groups, that have the role assigned, will no longer have access to the tenant's data - unless they are assigned directly to the tenant. - #### Parameters ##### input [`unassignRoleFromTenantInput`](../type-aliases/unassignRoleFromTenantInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function unassignRoleFromTenantExample( tenantId: TenantId, roleId: RoleId ) { const camunda = createCamundaClient(); await camunda.unassignRoleFromTenant({ tenantId, roleId, }); } ``` #### Operation Id unassignRoleFromTenant #### Tags Tenant --- ### unassignRoleFromUser() ```ts unassignRoleFromUser(input, options?): CancelablePromise; ``` Unassign a role from a user Unassigns a role from a user. The user will no longer inherit the authorizations associated with this role. - #### Parameters ##### input [`unassignRoleFromUserInput`](../type-aliases/unassignRoleFromUserInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function unassignRoleFromUserExample(roleId: RoleId, username: Username) { const camunda = createCamundaClient(); await camunda.unassignRoleFromUser({ roleId, username, }); } ``` #### Operation Id unassignRoleFromUser #### Tags Role --- ### unassignUserFromGroup() ```ts unassignUserFromGroup(input, options?): CancelablePromise; ``` Unassign a user from a group Unassigns a user from a group. The user is removed as a group member, with associated authorizations, roles, and tenant assignments no longer applied. - #### Parameters ##### input [`unassignUserFromGroupInput`](../type-aliases/unassignUserFromGroupInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function unassignUserFromGroupExample( groupId: GroupId, username: Username ) { const camunda = createCamundaClient(); await camunda.unassignUserFromGroup({ groupId, username, }); } ``` #### Operation Id unassignUserFromGroup #### Tags Group --- ### unassignUserFromTenant() ```ts unassignUserFromTenant(input, options?): CancelablePromise; ``` Unassign a user from a tenant Unassigns the user from the specified tenant. The user can no longer access tenant data. - #### Parameters ##### input [`unassignUserFromTenantInput`](../type-aliases/unassignUserFromTenantInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function unassignUserFromTenantExample( tenantId: TenantId, username: Username ) { const camunda = createCamundaClient(); await camunda.unassignUserFromTenant({ tenantId, username, }); } ``` #### Operation Id unassignUserFromTenant #### Tags Tenant --- ### unassignUserTask() ```ts unassignUserTask(input, options?): CancelablePromise; ``` Unassign user task Removes the assignee of a task with the given key. Unassignment waits for blocking task listeners on this lifecycle transition. If listener processing is delayed beyond the request timeout, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. - #### Parameters ##### input [`unassignUserTaskInput`](../type-aliases/unassignUserTaskInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function unassignUserTaskExample(userTaskKey: UserTaskKey) { const camunda = createCamundaClient(); await camunda.unassignUserTask({ userTaskKey }); } ``` #### Operation Id unassignUserTask #### Tags User task --- ### updateAgentInstance() ```ts updateAgentInstance(input, options?): CancelablePromise; ``` Update agent instance Updates the mutable fields of an agent instance: status, metric counters, and tools. Metric values are treated as deltas and applied immediately to the aggregate counters. Tool updates replace the existing tool list. At least one of status, metrics, or tools must be provided. - #### Parameters ##### input [`updateAgentInstanceInput`](../type-aliases/updateAgentInstanceInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function updateAgentInstanceExample(agentInstanceKey: AgentInstanceKey) { const camunda = createCamundaClient(); await camunda.updateAgentInstance({ agentInstanceKey, status: "THINKING", metrics: { inputTokens: 150, outputTokens: 50, modelCalls: 1, }, }); console.log(`Updated agent instance: ${agentInstanceKey}`); } ``` #### Operation Id updateAgentInstance #### Tags Agent instance --- ### updateAuthorization() ```ts updateAuthorization(input, options?): CancelablePromise; ``` Update authorization Update the authorization with the given key. - #### Parameters ##### input [`updateAuthorizationInput`](../type-aliases/updateAuthorizationInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function updateAuthorizationExample(authorizationKey: AuthorizationKey) { const camunda = createCamundaClient(); await camunda.updateAuthorization({ authorizationKey, ownerId: "user-123", ownerType: "USER", resourceId: "order-process", resourceType: "PROCESS_DEFINITION", permissionTypes: [ "CREATE_PROCESS_INSTANCE", "READ_PROCESS_INSTANCE", "DELETE_PROCESS_INSTANCE", ], }); } ``` #### Operation Id updateAuthorization #### Tags Authorization --- ### updateGlobalClusterVariable() ```ts updateGlobalClusterVariable(input, options?): CancelablePromise; ``` Update a global-scoped cluster variable Updates the value of an existing global cluster variable. The variable must exist, otherwise a 404 error is returned. - #### Parameters ##### input [`updateGlobalClusterVariableInput`](../type-aliases/updateGlobalClusterVariableInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ClusterVariableResult`](../type-aliases/ClusterVariableResult.md)\> #### Example ```ts async function updateGlobalClusterVariableExample(name: ClusterVariableName) { const camunda = createCamundaClient(); await camunda.updateGlobalClusterVariable({ name, value: { darkMode: false }, }); } ``` #### Operation Id updateGlobalClusterVariable #### Tags Cluster Variable --- ### updateGlobalTaskListener() ```ts updateGlobalTaskListener(input, options?): CancelablePromise; ``` Update global user task listener Updates a global user task listener. - #### Parameters ##### input [`updateGlobalTaskListenerInput`](../type-aliases/updateGlobalTaskListenerInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`GlobalTaskListenerResult`](../type-aliases/GlobalTaskListenerResult.md)\> #### Example ```ts async function updateGlobalTaskListenerExample(id: GlobalListenerId) { const camunda = createCamundaClient(); await camunda.updateGlobalTaskListener({ id, eventTypes: ["completing"], type: "updated-audit-listener", }); } ``` #### Operation Id updateGlobalTaskListener #### Tags Global listener --- ### updateGroup() ```ts updateGroup(input, options?): CancelablePromise; ``` Update group Update a group with the given ID. - #### Parameters ##### input [`updateGroupInput`](../type-aliases/updateGroupInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`GroupUpdateResult`](../type-aliases/GroupUpdateResult.md)\> #### Example ```ts async function updateGroupExample(groupId: GroupId) { const camunda = createCamundaClient(); await camunda.updateGroup({ groupId, name: "Engineering Team", }); } ``` #### Operation Id updateGroup #### Tags Group --- ### updateJob() ```ts updateJob(input, options?): CancelablePromise; ``` Update job Update a job with the given key. - #### Parameters ##### input [`updateJobInput`](../type-aliases/updateJobInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function updateJobExample(jobKey: JobKey) { const camunda = createCamundaClient(); await camunda.updateJob({ jobKey, changeset: { retries: 5, timeout: 60000 }, }); } ``` #### Operation Id updateJob #### Tags Job --- ### updateMappingRule() ```ts updateMappingRule(input, options?): CancelablePromise; ``` Update mapping rule Update a mapping rule. - #### Parameters ##### input [`updateMappingRuleInput`](../type-aliases/updateMappingRuleInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`MappingRuleCreateUpdateResult`](../type-aliases/MappingRuleCreateUpdateResult.md)\> #### Example ```ts async function updateMappingRuleExample(mappingRuleId: MappingRuleId) { const camunda = createCamundaClient(); await camunda.updateMappingRule({ mappingRuleId, name: "LDAP Group Mapping", claimName: "groups", claimValue: "engineering-team", }); } ``` #### Operation Id updateMappingRule #### Tags Mapping rule --- ### updateRole() ```ts updateRole(input, options?): CancelablePromise; ``` Update role Update a role with the given ID. - #### Parameters ##### input [`updateRoleInput`](../type-aliases/updateRoleInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`RoleUpdateResult`](../type-aliases/RoleUpdateResult.md)\> #### Example ```ts async function updateRoleExample(roleId: RoleId) { const camunda = createCamundaClient(); await camunda.updateRole({ roleId, name: "Process Administrator", }); } ``` #### Operation Id updateRole #### Tags Role --- ### updateTenant() ```ts updateTenant(input, options?): CancelablePromise; ``` Update tenant Updates an existing tenant. - #### Parameters ##### input [`updateTenantInput`](../type-aliases/updateTenantInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`TenantUpdateResult`](../type-aliases/TenantUpdateResult.md)\> #### Example ```ts async function updateTenantExample(tenantId: TenantId) { const camunda = createCamundaClient(); await camunda.updateTenant({ tenantId, name: "Customer Service Team", }); } ``` #### Operation Id updateTenant #### Tags Tenant --- ### updateTenantClusterVariable() ```ts updateTenantClusterVariable(input, options?): CancelablePromise; ``` Update a tenant-scoped cluster variable Updates the value of an existing tenant-scoped cluster variable. The variable must exist, otherwise a 404 error is returned. - #### Parameters ##### input [`updateTenantClusterVariableInput`](../type-aliases/updateTenantClusterVariableInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`ClusterVariableResult`](../type-aliases/ClusterVariableResult.md)\> #### Example ```ts async function updateTenantClusterVariableExample( tenantId: TenantId, name: ClusterVariableName ) { const camunda = createCamundaClient(); await camunda.updateTenantClusterVariable({ tenantId, name, value: { region: "eu-west-1" }, }); } ``` #### Operation Id updateTenantClusterVariable #### Tags Cluster Variable --- ### updateUser() ```ts updateUser(input, options?): CancelablePromise; ``` Update user Updates a user. - #### Parameters ##### input [`updateUserInput`](../type-aliases/updateUserInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<[`UserUpdateResult`](../type-aliases/UserUpdateResult.md)\> #### Example ```ts async function updateUserExample(username: Username) { const camunda = createCamundaClient(); await camunda.updateUser({ username, name: "Alice Jones", email: "alice.jones@example.com", }); } ``` #### Operation Id updateUser #### Tags User --- ### updateUserTask() ```ts updateUserTask(input, options?): CancelablePromise; ``` Update user task Update a user task with the given key. Updates wait for blocking task listeners on this lifecycle transition. If listener processing is delayed beyond the request timeout, this endpoint can return 504. Other gateway timeout causes are also possible. Retry with backoff and inspect listener worker availability and logs when this repeats. - #### Parameters ##### input [`updateUserTaskInput`](../type-aliases/updateUserTaskInput.md) ##### options? [`OperationOptions`](../interfaces/OperationOptions.md) #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> #### Example ```ts async function updateUserTaskExample(userTaskKey: UserTaskKey) { const camunda = createCamundaClient(); await camunda.updateUserTask({ userTaskKey, changeset: { candidateUsers: ["alice", "bob"], dueDate: "2025-12-31T23:59:59Z", priority: 80, }, }); } ``` #### Operation Id updateUserTask #### Tags User task --- ### withCorrelation() ```ts withCorrelation(id, fn): Promise; ``` #### Type Parameters ##### T `T` #### Parameters ##### id `string` ##### fn () => `T` \| `Promise`\<`T`\> #### Returns `Promise`\<`T`\> --- ## Class: CamundaValidationError ## Extends - `Error` ## Constructors ### Constructor ```ts new CamundaValidationError(params): CamundaValidationError; ``` #### Parameters ##### params ###### issues `string`[] ###### message `string` ###### operationId? `string` ###### side `"request"` \| `"response"` ###### summary `string` #### Returns `CamundaValidationError` #### Overrides ```ts Error.constructor; ``` ## Properties ### issues ```ts issues: string[]; ``` --- ### operationId? ```ts optional operationId?: string; ``` --- ### side ```ts side: "request" | "response"; ``` --- ### summary ```ts summary: string; ``` --- ## Class: CancelError ## Extends - `Error` ## Constructors ### Constructor ```ts new CancelError(): CancelError; ``` #### Returns `CancelError` #### Overrides ```ts Error.constructor; ``` --- ## Class: EventualConsistencyTimeoutError ## Extends - `Error` ## Constructors ### Constructor ```ts new EventualConsistencyTimeoutError(params): EventualConsistencyTimeoutError; ``` #### Parameters ##### params ###### attempts `number` ###### elapsedMs `number` ###### lastResponse? `any` ###### lastStatus? `number` ###### message? `string` ###### operationId? `string` #### Returns `EventualConsistencyTimeoutError` #### Overrides ```ts Error.constructor; ``` ## Properties ### attempts ```ts attempts: number; ``` --- ### code ```ts code: string = "CAMUNDA_SDK_EVENTUAL_TIMEOUT"; ``` --- ### elapsedMs ```ts elapsedMs: number; ``` --- ### lastResponseSnippet? ```ts optional lastResponseSnippet?: string; ``` --- ### lastStatus? ```ts optional lastStatus?: number; ``` --- ### operationId? ```ts optional operationId?: string; ``` --- ## Function: assertConstraint() ```ts function assertConstraint(value, label, c): void; ``` ## Parameters ### value `string` ### label `string` ### c #### maxLength? `number` #### minLength? `number` #### pattern? `string` ## Returns `void` --- ## Function: createCamundaClient() ```ts function createCamundaClient(options?): CamundaClient; ``` ## Parameters ### options? [`CamundaOptions`](../interfaces/CamundaOptions.md) ## Returns [`CamundaClient`](../classes/CamundaClient.md) --- ## Function: createCamundaClientLoose() ```ts function createCamundaClientLoose(...args): object; ``` Create a client where all branded key types are widened to string. Use when integrating with external systems or when dynamic string keys are common and brand friction is unwanted. For maximum type safety prefer the strict createCamundaClient. ## Parameters ### args ...\[[`CamundaOptions`](../interfaces/CamundaOptions.md)\] ## Returns `object` ### config ```ts config: object; ``` #### config.\_\_raw ```ts readonly __raw: object; ``` ##### Index Signature ```ts [key: string]: string | undefined ``` #### config.auth ```ts readonly auth: object; ``` #### config.auth.basic? ```ts optional basic?: object; ``` #### config.auth.basic.password? ```ts optional password?: string; ``` #### config.auth.basic.username? ```ts optional username?: string; ``` #### config.auth.strategy ```ts strategy: AuthStrategy; ``` #### config.backpressure ```ts readonly backpressure: object; ``` #### config.backpressure.decayQuietMs ```ts decayQuietMs: number; ``` #### config.backpressure.enabled ```ts enabled: boolean; ``` #### config.backpressure.floor ```ts floor: number; ``` #### config.backpressure.healthyRecoveryMultiplier ```ts healthyRecoveryMultiplier: number; ``` #### config.backpressure.initialMax ```ts initialMax: number; ``` #### config.backpressure.maxWaiters ```ts maxWaiters: number; ``` #### config.backpressure.observeOnly ```ts observeOnly: boolean; ``` #### config.backpressure.profile ```ts profile: string; ``` #### config.backpressure.recoveryIntervalMs ```ts recoveryIntervalMs: number; ``` #### config.backpressure.recoveryStep ```ts recoveryStep: number; ``` #### config.backpressure.severeFactor ```ts severeFactor: number; ``` #### config.backpressure.severeThreshold ```ts severeThreshold: number; ``` #### config.backpressure.softFactor ```ts softFactor: number; ``` #### config.backpressure.unlimitedAfterHealthyMs ```ts unlimitedAfterHealthyMs: number; ``` #### config.defaultTenantId ```ts readonly defaultTenantId: string; ``` #### config.eventual? ```ts readonly optional eventual?: object; ``` #### config.eventual.pollDefaultMs ```ts pollDefaultMs: number; ``` #### config.httpRetry ```ts readonly httpRetry: object; ``` #### config.httpRetry.baseDelayMs ```ts baseDelayMs: number; ``` #### config.httpRetry.maxAttempts ```ts maxAttempts: number; ``` #### config.httpRetry.maxDelayMs ```ts maxDelayMs: number; ``` #### config.logLevel ```ts readonly logLevel: "trace" | "error" | "silent" | "warn" | "info" | "debug"; ``` #### config.mtls? ```ts readonly optional mtls?: object; ``` #### config.mtls.ca? ```ts optional ca?: string; ``` #### config.mtls.caPath? ```ts optional caPath?: string; ``` #### config.mtls.cert? ```ts optional cert?: string; ``` #### config.mtls.certPath? ```ts optional certPath?: string; ``` #### config.mtls.key? ```ts optional key?: string; ``` #### config.mtls.keyPassphrase? ```ts optional keyPassphrase?: string; ``` #### config.mtls.keyPath? ```ts optional keyPath?: string; ``` #### config.oauth ```ts readonly oauth: object; ``` #### config.oauth.cacheDir? ```ts optional cacheDir?: string; ``` #### config.oauth.clientId? ```ts optional clientId?: string; ``` #### config.oauth.clientSecret? ```ts optional clientSecret?: string; ``` #### config.oauth.grantType ```ts grantType: string; ``` #### config.oauth.oauthUrl ```ts oauthUrl: string; ``` #### config.oauth.retry ```ts retry: object; ``` #### config.oauth.retry.baseDelayMs ```ts baseDelayMs: number; ``` #### config.oauth.retry.max ```ts max: number; ``` #### config.oauth.scope? ```ts optional scope?: string; ``` #### config.oauth.timeoutMs ```ts timeoutMs: number; ``` #### config.restAddress ```ts readonly restAddress: string; ``` #### config.supportLog? ```ts readonly optional supportLog?: object; ``` #### config.supportLog.enabled ```ts enabled: boolean; ``` #### config.supportLog.filePath ```ts filePath: string; ``` #### config.telemetry? ```ts readonly optional telemetry?: object; ``` #### config.telemetry.correlation ```ts correlation: boolean; ``` #### config.telemetry.log ```ts log: boolean; ``` #### config.tokenAudience ```ts readonly tokenAudience: string; ``` #### config.validation ```ts readonly validation: object; ``` #### config.validation.raw ```ts raw: string; ``` #### config.validation.req ```ts req: ValidationMode; ``` #### config.validation.res ```ts res: ValidationMode; ``` #### config.workerDefaults? ```ts readonly optional workerDefaults?: object; ``` #### config.workerDefaults.jobTimeoutMs? ```ts optional jobTimeoutMs?: number; ``` #### config.workerDefaults.maxParallelJobs? ```ts optional maxParallelJobs?: number; ``` #### config.workerDefaults.pollTimeoutMs? ```ts optional pollTimeoutMs?: number; ``` #### config.workerDefaults.startupJitterMaxSeconds? ```ts optional startupJitterMaxSeconds?: number; ``` #### config.workerDefaults.workerName? ```ts optional workerName?: string; ``` ### \_getSupportLogger() ```ts _getSupportLogger(...a): object; ``` #### Parameters ##### a ...\[\] #### Returns `object` ##### log() ```ts log(...a): void; ``` ###### Parameters ###### a ...\[`string` \| `number` \| `boolean` \| `object`, `boolean`\] ###### Returns `void` ### \_invokeWithRetry() ```ts _invokeWithRetry(...a): Promise; ``` #### Parameters ##### a ...\[(...`a`) => `Promise`\<`unknown`\>, `object`\] #### Returns `Promise`\<`unknown`\> ### activateAdHocSubProcessActivities() ```ts activateAdHocSubProcessActivities(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### activateJobs() ```ts activateJobs(...a): CancelablePromise<{ jobs: object[]; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `jobs`: `object`[]; \}\> ### assignClientToGroup() ```ts assignClientToGroup(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### assignClientToTenant() ```ts assignClientToTenant(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### assignGroupToTenant() ```ts assignGroupToTenant(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### assignMappingRuleToGroup() ```ts assignMappingRuleToGroup(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### assignMappingRuleToTenant() ```ts assignMappingRuleToTenant(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### assignRoleToClient() ```ts assignRoleToClient(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### assignRoleToGroup() ```ts assignRoleToGroup(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### assignRoleToMappingRule() ```ts assignRoleToMappingRule(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### assignRoleToTenant() ```ts assignRoleToTenant(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### assignRoleToUser() ```ts assignRoleToUser(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### assignUserTask() ```ts assignUserTask(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### assignUserToGroup() ```ts assignUserToGroup(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### assignUserToTenant() ```ts assignUserToTenant(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### broadcastSignal() ```ts broadcastSignal(...a): CancelablePromise<{ signalKey: string; tenantId: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `signalKey`: `string`; `tenantId`: `string`; \}\> ### cancelBatchOperation() ```ts cancelBatchOperation(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### cancelProcessInstance() ```ts cancelProcessInstance(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### cancelProcessInstancesBatchOperation() ```ts cancelProcessInstancesBatchOperation(...a): CancelablePromise<{ batchOperationKey: string; batchOperationType: BatchOperationTypeEnum; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `batchOperationKey`: `string`; `batchOperationType`: [`BatchOperationTypeEnum`](../type-aliases/BatchOperationTypeEnum.md); \}\> ### clearAuthCache() ```ts clearAuthCache(...a): void; ``` #### Parameters ##### a ...\[`object`\] #### Returns `void` ### completeJob() ```ts completeJob(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### completeUserTask() ```ts completeUserTask(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### configure() ```ts configure(...a): void; ``` #### Parameters ##### a ...\[`object`\] #### Returns `void` ### correlateMessage() ```ts correlateMessage(...a): CancelablePromise<{ messageKey: string; processInstanceKey: string; tenantId: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `messageKey`: `string`; `processInstanceKey`: `string`; `tenantId`: `string`; \}\> ### createAdminUser() ```ts createAdminUser(...a): CancelablePromise<{ email: string | null; name: string | null; username: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `email`: `string` \| `null`; `name`: `string` \| `null`; `username`: `string`; \}\> ### createAgentInstance() ```ts createAgentInstance(...a): CancelablePromise<{ agentInstanceKey: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `agentInstanceKey`: `string`; \}\> ### createAuthorization() ```ts createAuthorization(...a): CancelablePromise<{ authorizationKey: string; }>; ``` #### Parameters ##### a ...\[ \| \{ `ownerId`: `string`; `ownerType`: [`OwnerTypeEnum`](../type-aliases/OwnerTypeEnum.md); `permissionTypes`: [`PermissionTypeEnum`](../type-aliases/PermissionTypeEnum.md)[]; `resourceId`: `string`; `resourceType`: [`ResourceTypeEnum`](../type-aliases/ResourceTypeEnum.md); \} \| \{ `ownerId`: `string`; `ownerType`: [`OwnerTypeEnum`](../type-aliases/OwnerTypeEnum.md); `permissionTypes`: [`PermissionTypeEnum`](../type-aliases/PermissionTypeEnum.md)[]; `resourcePropertyName`: `string`; `resourceType`: [`ResourceTypeEnum`](../type-aliases/ResourceTypeEnum.md); \}, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `authorizationKey`: `string`; \}\> ### createDeployment() ```ts createDeployment(...a): CancelablePromise<{ decisionRequirements: object[]; decisions: object[]; deploymentKey: string; deployments: object[]; forms: object[]; processes: object[]; resources: object[]; tenantId: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `decisionRequirements`: `object`[]; `decisions`: `object`[]; `deploymentKey`: `string`; `deployments`: `object`[]; `forms`: `object`[]; `processes`: `object`[]; `resources`: `object`[]; `tenantId`: `string`; \}\> ### createDocument() ```ts createDocument(...a): CancelablePromise<{ camunda.document.type: "camunda"; contentHash: string | null; documentId: string; metadata: { contentType: string; customProperties: { [key: string]: unknown; }; expiresAt: string | null; fileName: string; processDefinitionId: | { [key: number]: string; __brand: "ProcessDefinitionId"; } | null; processInstanceKey: | { [key: number]: string; __brand: "ProcessInstanceKey"; } | null; size: number; }; storeId: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `camunda.document.type`: `"camunda"`; `contentHash`: `string` \| `null`; `documentId`: `string`; `metadata`: \{ `contentType`: `string`; `customProperties`: \{ \[`key`: `string`\]: `unknown`; \}; `expiresAt`: `string` \| `null`; `fileName`: `string`; `processDefinitionId`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"ProcessDefinitionId"`; \} \| `null`; `processInstanceKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"ProcessInstanceKey"`; \} \| `null`; `size`: `number`; \}; `storeId`: `string`; \}\> ### createDocumentLink() ```ts createDocumentLink(...a): CancelablePromise<{ expiresAt: string; url: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `expiresAt`: `string`; `url`: `string`; \}\> ### createDocuments() ```ts createDocuments(...a): CancelablePromise<{ createdDocuments: object[]; failedDocuments: object[]; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `createdDocuments`: `object`[]; `failedDocuments`: `object`[]; \}\> ### createElementInstanceVariables() ```ts createElementInstanceVariables(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### createGlobalClusterVariable() ```ts createGlobalClusterVariable(...a): CancelablePromise<{ name: string; scope: ClusterVariableScopeEnum; tenantId: string | null; value: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `name`: `string`; `scope`: [`ClusterVariableScopeEnum`](../type-aliases/ClusterVariableScopeEnum.md); `tenantId`: `string` \| `null`; `value`: `string`; \}\> ### createGlobalTaskListener() ```ts createGlobalTaskListener(...a): CancelablePromise<{ afterNonGlobal?: boolean; eventTypes: GlobalTaskListenerEventTypeEnum[]; id: string; priority?: number; retries?: number; source: GlobalListenerSourceEnum; type?: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `afterNonGlobal?`: `boolean`; `eventTypes`: [`GlobalTaskListenerEventTypeEnum`](../type-aliases/GlobalTaskListenerEventTypeEnum.md)[]; `id`: `string`; `priority?`: `number`; `retries?`: `number`; `source`: [`GlobalListenerSourceEnum`](../type-aliases/GlobalListenerSourceEnum.md); `type?`: `string`; \}\> ### createGroup() ```ts createGroup(...a): CancelablePromise<{ description: string | null; groupId: string; name: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `description`: `string` \| `null`; `groupId`: `string`; `name`: `string`; \}\> ### createJobWorker() ```ts createJobWorker(...a): object; ``` #### Parameters ##### a ...\[`object`\] #### Returns `object` ##### activeJobs ```ts activeJobs: number; ``` ##### name ```ts name: string; ``` ##### stopped ```ts stopped: boolean; ``` ##### start() ```ts start(...a): void; ``` ###### Parameters ###### a ...\[\] ###### Returns `void` ##### stop() ```ts stop(...a): void; ``` ###### Parameters ###### a ...\[\] ###### Returns `void` ##### stopGracefully() ```ts stopGracefully(...a): Promise<{ remainingJobs: number; timedOut: boolean; }>; ``` ###### Parameters ###### a ...\[`object`\] ###### Returns `Promise`\<\{ `remainingJobs`: `number`; `timedOut`: `boolean`; \}\> ### createMappingRule() ```ts createMappingRule(...a): CancelablePromise<{ claimName: string; claimValue: string; mappingRuleId: string; name: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `claimName`: `string`; `claimValue`: `string`; `mappingRuleId`: `string`; `name`: `string`; \}\> ### createProcessInstance() ```ts createProcessInstance(...a): CancelablePromise<{ businessId: | { [key: number]: string; __brand: "BusinessId"; } | null; processDefinitionId: string; processDefinitionKey: string; processDefinitionVersion: number; processInstanceKey: string; tags: string[]; tenantId: string; variables: { [key: string]: unknown; }; }>; ``` #### Parameters ##### a ...\[ \| \{ `awaitCompletion?`: `boolean`; `businessId?`: \{ \[`key`: `number`\]: `string`; `__brand`: `"BusinessId"`; \}; `fetchVariables?`: `string`[]; `operationReference?`: `number`; `processDefinitionKey`: `string`; `processDefinitionVersion?`: `number`; `requestTimeout?`: `number`; `runtimeInstructions?`: `object`[]; `startInstructions?`: `object`[]; `tags?`: `string`[]; `tenantId?`: \{ \[`key`: `number`\]: `string`; `__brand`: `"TenantId"`; \}; `variables?`: \{ \[`key`: `string`\]: `unknown`; \}; \} \| \{ `awaitCompletion?`: `boolean`; `businessId?`: \{ \[`key`: `number`\]: `string`; `__brand`: `"BusinessId"`; \}; `fetchVariables?`: `string`[]; `operationReference?`: `number`; `processDefinitionId`: `string`; `processDefinitionVersion?`: `number`; `requestTimeout?`: `number`; `runtimeInstructions?`: `object`[]; `startInstructions?`: `object`[]; `tags?`: `string`[]; `tenantId?`: \{ \[`key`: `number`\]: `string`; `__brand`: `"TenantId"`; \}; `variables?`: \{ \[`key`: `string`\]: `unknown`; \}; \}, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `businessId`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"BusinessId"`; \} \| `null`; `processDefinitionId`: `string`; `processDefinitionKey`: `string`; `processDefinitionVersion`: `number`; `processInstanceKey`: `string`; `tags`: `string`[]; `tenantId`: `string`; `variables`: \{ \[`key`: `string`\]: `unknown`; \}; \}\> ### createRole() ```ts createRole(...a): CancelablePromise<{ description: string | null; name: string; roleId: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `description`: `string` \| `null`; `name`: `string`; `roleId`: `string`; \}\> ### createTenant() ```ts createTenant(...a): CancelablePromise<{ description: string | null; name: string; tenantId: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `description`: `string` \| `null`; `name`: `string`; `tenantId`: `string`; \}\> ### createTenantClusterVariable() ```ts createTenantClusterVariable(...a): CancelablePromise<{ name: string; scope: ClusterVariableScopeEnum; tenantId: string | null; value: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `name`: `string`; `scope`: [`ClusterVariableScopeEnum`](../type-aliases/ClusterVariableScopeEnum.md); `tenantId`: `string` \| `null`; `value`: `string`; \}\> ### createThreadedJobWorker() ```ts createThreadedJobWorker(...a): object; ``` #### Parameters ##### a ...\[`object`\] #### Returns `object` ##### activeJobs ```ts activeJobs: number; ``` ##### busyThreads ```ts busyThreads: number; ``` ##### name ```ts name: string; ``` ##### poolSize ```ts poolSize: number; ``` ##### ready ```ts ready: Promise; ``` ##### stopped ```ts stopped: boolean; ``` ##### start() ```ts start(...a): void; ``` ###### Parameters ###### a ...\[\] ###### Returns `void` ##### stop() ```ts stop(...a): void; ``` ###### Parameters ###### a ...\[\] ###### Returns `void` ##### stopGracefully() ```ts stopGracefully(...a): Promise<{ remainingJobs: number; timedOut: boolean; }>; ``` ###### Parameters ###### a ...\[`object`\] ###### Returns `Promise`\<\{ `remainingJobs`: `number`; `timedOut`: `boolean`; \}\> ### createUser() ```ts createUser(...a): CancelablePromise<{ email: string | null; name: string | null; username: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `email`: `string` \| `null`; `name`: `string` \| `null`; `username`: `string`; \}\> ### deleteAuthorization() ```ts deleteAuthorization(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### deleteDecisionInstance() ```ts deleteDecisionInstance(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### deleteDecisionInstancesBatchOperation() ```ts deleteDecisionInstancesBatchOperation(...a): CancelablePromise<{ batchOperationKey: string; batchOperationType: BatchOperationTypeEnum; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `batchOperationKey`: `string`; `batchOperationType`: [`BatchOperationTypeEnum`](../type-aliases/BatchOperationTypeEnum.md); \}\> ### deleteDocument() ```ts deleteDocument(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### deleteGlobalClusterVariable() ```ts deleteGlobalClusterVariable(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### deleteGlobalTaskListener() ```ts deleteGlobalTaskListener(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### deleteGroup() ```ts deleteGroup(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### deleteMappingRule() ```ts deleteMappingRule(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### deleteProcessInstance() ```ts deleteProcessInstance(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### deleteProcessInstancesBatchOperation() ```ts deleteProcessInstancesBatchOperation(...a): CancelablePromise<{ batchOperationKey: string; batchOperationType: BatchOperationTypeEnum; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `batchOperationKey`: `string`; `batchOperationType`: [`BatchOperationTypeEnum`](../type-aliases/BatchOperationTypeEnum.md); \}\> ### deleteResource() ```ts deleteResource(...a): CancelablePromise<{ batchOperation: | { batchOperationKey: string; batchOperationType: BatchOperationTypeEnum; } | null; resourceKey: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `batchOperation`: \| \{ `batchOperationKey`: `string`; `batchOperationType`: [`BatchOperationTypeEnum`](../type-aliases/BatchOperationTypeEnum.md); \} \| `null`; `resourceKey`: `string`; \}\> ### deleteRole() ```ts deleteRole(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### deleteTenant() ```ts deleteTenant(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### deleteTenantClusterVariable() ```ts deleteTenantClusterVariable(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### deleteUser() ```ts deleteUser(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### deployResourcesFromFiles() ```ts deployResourcesFromFiles(...a): CancelablePromise<{ decisionRequirements: object[]; decisions: object[]; deploymentKey: string; deployments: object[]; forms: object[]; processes: object[]; resources: object[]; tenantId: string; }>; ``` #### Parameters ##### a ...\[`string`[], `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `decisionRequirements`: `object`[]; `decisions`: `object`[]; `deploymentKey`: `string`; `deployments`: `object`[]; `forms`: `object`[]; `processes`: `object`[]; `resources`: `object`[]; `tenantId`: `string`; \}\> ### emitSupportLogPreamble() ```ts emitSupportLogPreamble(...a): void; ``` #### Parameters ##### a ...\[\] #### Returns `void` ### evaluateConditionals() ```ts evaluateConditionals(...a): CancelablePromise<{ conditionalEvaluationKey: string; processInstances: object[]; tenantId: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `conditionalEvaluationKey`: `string`; `processInstances`: `object`[]; `tenantId`: `string`; \}\> ### evaluateDecision() ```ts evaluateDecision(...a): CancelablePromise<{ decisionDefinitionId: string; decisionDefinitionKey: string; decisionDefinitionName: string; decisionDefinitionVersion: number; decisionEvaluationKey: string; decisionInstanceKey: string; decisionRequirementsId: string; decisionRequirementsKey: string; evaluatedDecisions: object[]; failedDecisionDefinitionId: | { [key: number]: string; __brand: "DecisionDefinitionId"; } | null; failureMessage: string | null; output: string; tenantId: string; }>; ``` #### Parameters ##### a ...\[ \| \{ `decisionDefinitionId`: `string`; `tenantId?`: \{ \[`key`: `number`\]: `string`; `__brand`: `"TenantId"`; \}; `variables?`: \{ \[`key`: `string`\]: `unknown`; \}; \} \| \{ `decisionDefinitionKey`: `string`; `tenantId?`: \{ \[`key`: `number`\]: `string`; `__brand`: `"TenantId"`; \}; `variables?`: \{ \[`key`: `string`\]: `unknown`; \}; \}, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `decisionDefinitionId`: `string`; `decisionDefinitionKey`: `string`; `decisionDefinitionName`: `string`; `decisionDefinitionVersion`: `number`; `decisionEvaluationKey`: `string`; `decisionInstanceKey`: `string`; `decisionRequirementsId`: `string`; `decisionRequirementsKey`: `string`; `evaluatedDecisions`: `object`[]; `failedDecisionDefinitionId`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"DecisionDefinitionId"`; \} \| `null`; `failureMessage`: `string` \| `null`; `output`: `string`; `tenantId`: `string`; \}\> ### evaluateExpression() ```ts evaluateExpression(...a): CancelablePromise<{ expression: string; result: unknown; warnings: object[]; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `expression`: `string`; `result`: `unknown`; `warnings`: `object`[]; \}\> ### failJob() ```ts failJob(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### forceAuthRefresh() ```ts forceAuthRefresh(...a): Promise; ``` #### Parameters ##### a ...\[\] #### Returns `Promise`\<`string` \| `undefined`\> ### getAgentInstance() ```ts getAgentInstance(...a): CancelablePromise<{ agentInstanceKey: string; completionDate: string | null; creationDate: string; definition: { model: string; provider: string; systemPrompt: string; }; elementId: string; elementInstanceKeys: string[]; lastUpdatedDate: string; limits: { maxModelCalls: number; maxTokens: number; maxToolCalls: number; }; metrics: { inputTokens: number; modelCalls: number; outputTokens: number; toolCalls: number; }; processDefinitionKey: string; processInstanceKey: string; status: AgentInstanceStatusEnum; tenantId: string; tools: object[]; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `agentInstanceKey`: `string`; `completionDate`: `string` \| `null`; `creationDate`: `string`; `definition`: \{ `model`: `string`; `provider`: `string`; `systemPrompt`: `string`; \}; `elementId`: `string`; `elementInstanceKeys`: `string`[]; `lastUpdatedDate`: `string`; `limits`: \{ `maxModelCalls`: `number`; `maxTokens`: `number`; `maxToolCalls`: `number`; \}; `metrics`: \{ `inputTokens`: `number`; `modelCalls`: `number`; `outputTokens`: `number`; `toolCalls`: `number`; \}; `processDefinitionKey`: `string`; `processInstanceKey`: `string`; `status`: [`AgentInstanceStatusEnum`](../type-aliases/AgentInstanceStatusEnum.md); `tenantId`: `string`; `tools`: `object`[]; \}\> ### getAuditLog() ```ts getAuditLog(...a): CancelablePromise<{ actorId: string | null; actorType: | AuditLogActorTypeEnum | null; agentElementId: string | null; auditLogKey: string; batchOperationKey: | { [key: number]: string; __brand: "BatchOperationKey"; } | null; batchOperationType: | BatchOperationTypeEnum | null; category: AuditLogCategoryEnum; decisionDefinitionId: | { [key: number]: string; __brand: "DecisionDefinitionId"; } | null; decisionDefinitionKey: | { [key: number]: string; __brand: "DecisionDefinitionKey"; } | null; decisionEvaluationKey: | { [key: number]: string; __brand: "DecisionEvaluationKey"; } | null; decisionRequirementsId: string | null; decisionRequirementsKey: | { [key: number]: string; __brand: "DecisionRequirementsKey"; } | null; deploymentKey: | { [key: number]: string; __brand: "DeploymentKey"; } | null; elementInstanceKey: | { [key: number]: string; __brand: "ElementInstanceKey"; } | null; entityDescription: string | null; entityKey: string; entityType: AuditLogEntityTypeEnum; formKey: | { [key: number]: string; __brand: "FormKey"; } | null; jobKey: | { [key: number]: string; __brand: "JobKey"; } | null; operationType: AuditLogOperationTypeEnum; processDefinitionId: | { [key: number]: string; __brand: "ProcessDefinitionId"; } | null; processDefinitionKey: | { [key: number]: string; __brand: "ProcessDefinitionKey"; } | null; processInstanceKey: | { [key: number]: string; __brand: "ProcessInstanceKey"; } | null; relatedEntityKey: | { [key: number]: string; __brand: "AuditLogEntityKey"; } | null; relatedEntityType: | AuditLogEntityTypeEnum | null; resourceKey: | { [key: number]: string; __brand: "FormKey"; } | { [key: number]: string; __brand: "ProcessDefinitionKey"; } | { [key: number]: string; __brand: "DecisionRequirementsKey"; } | { [key: number]: string; __brand: "DecisionDefinitionKey"; } | null; result: AuditLogResultEnum; rootProcessInstanceKey: | { [key: number]: string; __brand: "ProcessInstanceKey"; } | null; tenantId: | { [key: number]: string; __brand: "TenantId"; } | null; timestamp: string; userTaskKey: | { [key: number]: string; __brand: "UserTaskKey"; } | null; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `actorId`: `string` \| `null`; `actorType`: \| [`AuditLogActorTypeEnum`](../type-aliases/AuditLogActorTypeEnum.md) \| `null`; `agentElementId`: `string` \| `null`; `auditLogKey`: `string`; `batchOperationKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"BatchOperationKey"`; \} \| `null`; `batchOperationType`: \| [`BatchOperationTypeEnum`](../type-aliases/BatchOperationTypeEnum.md) \| `null`; `category`: [`AuditLogCategoryEnum`](../type-aliases/AuditLogCategoryEnum.md); `decisionDefinitionId`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"DecisionDefinitionId"`; \} \| `null`; `decisionDefinitionKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"DecisionDefinitionKey"`; \} \| `null`; `decisionEvaluationKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"DecisionEvaluationKey"`; \} \| `null`; `decisionRequirementsId`: `string` \| `null`; `decisionRequirementsKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"DecisionRequirementsKey"`; \} \| `null`; `deploymentKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"DeploymentKey"`; \} \| `null`; `elementInstanceKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"ElementInstanceKey"`; \} \| `null`; `entityDescription`: `string` \| `null`; `entityKey`: `string`; `entityType`: [`AuditLogEntityTypeEnum`](../type-aliases/AuditLogEntityTypeEnum.md); `formKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"FormKey"`; \} \| `null`; `jobKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"JobKey"`; \} \| `null`; `operationType`: [`AuditLogOperationTypeEnum`](../type-aliases/AuditLogOperationTypeEnum.md); `processDefinitionId`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"ProcessDefinitionId"`; \} \| `null`; `processDefinitionKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"ProcessDefinitionKey"`; \} \| `null`; `processInstanceKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"ProcessInstanceKey"`; \} \| `null`; `relatedEntityKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"AuditLogEntityKey"`; \} \| `null`; `relatedEntityType`: \| [`AuditLogEntityTypeEnum`](../type-aliases/AuditLogEntityTypeEnum.md) \| `null`; `resourceKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"FormKey"`; \} \| \{ \[`key`: `number`\]: `string`; `__brand`: `"ProcessDefinitionKey"`; \} \| \{ \[`key`: `number`\]: `string`; `__brand`: `"DecisionRequirementsKey"`; \} \| \{ \[`key`: `number`\]: `string`; `__brand`: `"DecisionDefinitionKey"`; \} \| `null`; `result`: [`AuditLogResultEnum`](../type-aliases/AuditLogResultEnum.md); `rootProcessInstanceKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"ProcessInstanceKey"`; \} \| `null`; `tenantId`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"TenantId"`; \} \| `null`; `timestamp`: `string`; `userTaskKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"UserTaskKey"`; \} \| `null`; \}\> ### getAuthentication() ```ts getAuthentication(...a): CancelablePromise<{ authorizedComponents: string[]; c8Links: { [key: string]: string; }; canLogout: boolean; displayName: string | null; email: string | null; groups: string[]; roles: string[]; salesPlanType: string | null; tenants: object[]; username: string; }>; ``` #### Parameters ##### a ...\[`object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `authorizedComponents`: `string`[]; `c8Links`: \{ \[`key`: `string`\]: `string`; \}; `canLogout`: `boolean`; `displayName`: `string` \| `null`; `email`: `string` \| `null`; `groups`: `string`[]; `roles`: `string`[]; `salesPlanType`: `string` \| `null`; `tenants`: `object`[]; `username`: `string`; \}\> ### getAuthHeaders() ```ts getAuthHeaders(...a): Promise<{ [key: string]: string; }>; ``` #### Parameters ##### a ...\[\] #### Returns `Promise`\<\{ \[`key`: `string`\]: `string`; \}\> ### getAuthorization() ```ts getAuthorization(...a): CancelablePromise<{ authorizationKey: string; ownerId: string; ownerType: OwnerTypeEnum; permissionTypes: PermissionTypeEnum[]; resourceId: string | null; resourcePropertyName: string | null; resourceType: ResourceTypeEnum; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `authorizationKey`: `string`; `ownerId`: `string`; `ownerType`: [`OwnerTypeEnum`](../type-aliases/OwnerTypeEnum.md); `permissionTypes`: [`PermissionTypeEnum`](../type-aliases/PermissionTypeEnum.md)[]; `resourceId`: `string` \| `null`; `resourcePropertyName`: `string` \| `null`; `resourceType`: [`ResourceTypeEnum`](../type-aliases/ResourceTypeEnum.md); \}\> ### getBackpressureState() ```ts getBackpressureState(...a): | { backoffMs: number; consecutive: number; permitsCurrent: number; permitsMax: number | null; severity: BackpressureSeverity; waiters: number; } | { consecutive: number; permitsCurrent: number; permitsMax: null; severity: string; waiters: number; }; ``` #### Parameters ##### a ...\[\] #### Returns \| \{ `backoffMs`: `number`; `consecutive`: `number`; `permitsCurrent`: `number`; `permitsMax`: `number` \| `null`; `severity`: [`BackpressureSeverity`](../type-aliases/BackpressureSeverity.md); `waiters`: `number`; \} \| \{ `consecutive`: `number`; `permitsCurrent`: `number`; `permitsMax`: `null`; `severity`: `string`; `waiters`: `number`; \} ### getBatchOperation() ```ts getBatchOperation(...a): CancelablePromise<{ actorId: string | null; actorType: | AuditLogActorTypeEnum | null; batchOperationKey: string; batchOperationType: BatchOperationTypeEnum; endDate: string | null; errors: object[]; operationsCompletedCount: number; operationsFailedCount: number; operationsTotalCount: number; startDate: string | null; state: BatchOperationStateEnum; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `actorId`: `string` \| `null`; `actorType`: \| [`AuditLogActorTypeEnum`](../type-aliases/AuditLogActorTypeEnum.md) \| `null`; `batchOperationKey`: `string`; `batchOperationType`: [`BatchOperationTypeEnum`](../type-aliases/BatchOperationTypeEnum.md); `endDate`: `string` \| `null`; `errors`: `object`[]; `operationsCompletedCount`: `number`; `operationsFailedCount`: `number`; `operationsTotalCount`: `number`; `startDate`: `string` \| `null`; `state`: [`BatchOperationStateEnum`](../type-aliases/BatchOperationStateEnum.md); \}\> ### getConfig() ```ts getConfig(...a): object; ``` #### Parameters ##### a ...\[\] #### Returns `object` ##### \_\_raw ```ts readonly __raw: object; ``` ###### Index Signature ```ts [key: string]: string | undefined ``` ##### auth ```ts readonly auth: object; ``` ###### auth.basic? ```ts optional basic?: object; ``` ###### auth.basic.password? ```ts optional password?: string; ``` ###### auth.basic.username? ```ts optional username?: string; ``` ###### auth.strategy ```ts strategy: AuthStrategy; ``` ##### backpressure ```ts readonly backpressure: object; ``` ###### backpressure.decayQuietMs ```ts decayQuietMs: number; ``` ###### backpressure.enabled ```ts enabled: boolean; ``` ###### backpressure.floor ```ts floor: number; ``` ###### backpressure.healthyRecoveryMultiplier ```ts healthyRecoveryMultiplier: number; ``` ###### backpressure.initialMax ```ts initialMax: number; ``` ###### backpressure.maxWaiters ```ts maxWaiters: number; ``` ###### backpressure.observeOnly ```ts observeOnly: boolean; ``` ###### backpressure.profile ```ts profile: string; ``` ###### backpressure.recoveryIntervalMs ```ts recoveryIntervalMs: number; ``` ###### backpressure.recoveryStep ```ts recoveryStep: number; ``` ###### backpressure.severeFactor ```ts severeFactor: number; ``` ###### backpressure.severeThreshold ```ts severeThreshold: number; ``` ###### backpressure.softFactor ```ts softFactor: number; ``` ###### backpressure.unlimitedAfterHealthyMs ```ts unlimitedAfterHealthyMs: number; ``` ##### defaultTenantId ```ts readonly defaultTenantId: string; ``` ##### eventual? ```ts readonly optional eventual?: object; ``` ###### eventual.pollDefaultMs ```ts pollDefaultMs: number; ``` ##### httpRetry ```ts readonly httpRetry: object; ``` ###### httpRetry.baseDelayMs ```ts baseDelayMs: number; ``` ###### httpRetry.maxAttempts ```ts maxAttempts: number; ``` ###### httpRetry.maxDelayMs ```ts maxDelayMs: number; ``` ##### logLevel ```ts readonly logLevel: "trace" | "error" | "silent" | "warn" | "info" | "debug"; ``` ##### mtls? ```ts readonly optional mtls?: object; ``` ###### mtls.ca? ```ts optional ca?: string; ``` ###### mtls.caPath? ```ts optional caPath?: string; ``` ###### mtls.cert? ```ts optional cert?: string; ``` ###### mtls.certPath? ```ts optional certPath?: string; ``` ###### mtls.key? ```ts optional key?: string; ``` ###### mtls.keyPassphrase? ```ts optional keyPassphrase?: string; ``` ###### mtls.keyPath? ```ts optional keyPath?: string; ``` ##### oauth ```ts readonly oauth: object; ``` ###### oauth.cacheDir? ```ts optional cacheDir?: string; ``` ###### oauth.clientId? ```ts optional clientId?: string; ``` ###### oauth.clientSecret? ```ts optional clientSecret?: string; ``` ###### oauth.grantType ```ts grantType: string; ``` ###### oauth.oauthUrl ```ts oauthUrl: string; ``` ###### oauth.retry ```ts retry: object; ``` ###### oauth.retry.baseDelayMs ```ts baseDelayMs: number; ``` ###### oauth.retry.max ```ts max: number; ``` ###### oauth.scope? ```ts optional scope?: string; ``` ###### oauth.timeoutMs ```ts timeoutMs: number; ``` ##### restAddress ```ts readonly restAddress: string; ``` ##### supportLog? ```ts readonly optional supportLog?: object; ``` ###### supportLog.enabled ```ts enabled: boolean; ``` ###### supportLog.filePath ```ts filePath: string; ``` ##### telemetry? ```ts readonly optional telemetry?: object; ``` ###### telemetry.correlation ```ts correlation: boolean; ``` ###### telemetry.log ```ts log: boolean; ``` ##### tokenAudience ```ts readonly tokenAudience: string; ``` ##### validation ```ts readonly validation: object; ``` ###### validation.raw ```ts raw: string; ``` ###### validation.req ```ts req: ValidationMode; ``` ###### validation.res ```ts res: ValidationMode; ``` ##### workerDefaults? ```ts readonly optional workerDefaults?: object; ``` ###### workerDefaults.jobTimeoutMs? ```ts optional jobTimeoutMs?: number; ``` ###### workerDefaults.maxParallelJobs? ```ts optional maxParallelJobs?: number; ``` ###### workerDefaults.pollTimeoutMs? ```ts optional pollTimeoutMs?: number; ``` ###### workerDefaults.startupJitterMaxSeconds? ```ts optional startupJitterMaxSeconds?: number; ``` ###### workerDefaults.workerName? ```ts optional workerName?: string; ``` ### getDecisionDefinition() ```ts getDecisionDefinition(...a): CancelablePromise<{ decisionDefinitionId: string; decisionDefinitionKey: string; decisionRequirementsId: string; decisionRequirementsKey: string; decisionRequirementsName: string; decisionRequirementsVersion: number; name: string; tenantId: string; version: number; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `decisionDefinitionId`: `string`; `decisionDefinitionKey`: `string`; `decisionRequirementsId`: `string`; `decisionRequirementsKey`: `string`; `decisionRequirementsName`: `string`; `decisionRequirementsVersion`: `number`; `name`: `string`; `tenantId`: `string`; `version`: `number`; \}\> ### getDecisionDefinitionXml() ```ts getDecisionDefinitionXml(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`string`\> ### getDecisionInstance() ```ts getDecisionInstance(...a): CancelablePromise<{ decisionDefinitionId: string; decisionDefinitionKey: string; decisionDefinitionName: string; decisionDefinitionType: DecisionDefinitionTypeEnum; decisionDefinitionVersion: number; decisionEvaluationInstanceKey: string; decisionEvaluationKey: string; elementInstanceKey: | { [key: number]: string; __brand: "ElementInstanceKey"; } | null; evaluatedInputs: object[]; evaluationDate: string; evaluationFailure: string | null; matchedRules: object[]; processDefinitionKey: | { [key: number]: string; __brand: "ProcessDefinitionKey"; } | null; processInstanceKey: | { [key: number]: string; __brand: "ProcessInstanceKey"; } | null; result: string; rootDecisionDefinitionKey: string; rootProcessInstanceKey: | { [key: number]: string; __brand: "ProcessInstanceKey"; } | null; state: DecisionInstanceStateEnum; tenantId: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `decisionDefinitionId`: `string`; `decisionDefinitionKey`: `string`; `decisionDefinitionName`: `string`; `decisionDefinitionType`: [`DecisionDefinitionTypeEnum`](../type-aliases/DecisionDefinitionTypeEnum.md); `decisionDefinitionVersion`: `number`; `decisionEvaluationInstanceKey`: `string`; `decisionEvaluationKey`: `string`; `elementInstanceKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"ElementInstanceKey"`; \} \| `null`; `evaluatedInputs`: `object`[]; `evaluationDate`: `string`; `evaluationFailure`: `string` \| `null`; `matchedRules`: `object`[]; `processDefinitionKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"ProcessDefinitionKey"`; \} \| `null`; `processInstanceKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"ProcessInstanceKey"`; \} \| `null`; `result`: `string`; `rootDecisionDefinitionKey`: `string`; `rootProcessInstanceKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"ProcessInstanceKey"`; \} \| `null`; `state`: [`DecisionInstanceStateEnum`](../type-aliases/DecisionInstanceStateEnum.md); `tenantId`: `string`; \}\> ### getDecisionRequirements() ```ts getDecisionRequirements(...a): CancelablePromise<{ decisionRequirementsId: string; decisionRequirementsKey: string; decisionRequirementsName: string; resourceName: string; tenantId: string; version: number; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `decisionRequirementsId`: `string`; `decisionRequirementsKey`: `string`; `decisionRequirementsName`: `string`; `resourceName`: `string`; `tenantId`: `string`; `version`: `number`; \}\> ### getDecisionRequirementsXml() ```ts getDecisionRequirementsXml(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`string`\> ### getDocument() ```ts getDocument(...a): CancelablePromise<{ }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ \}\> ### getElementInstance() ```ts getElementInstance(...a): CancelablePromise<{ elementId: string; elementInstanceKey: string; elementName: string; endDate: string | null; hasIncident: boolean; incidentKey: | { [key: number]: string; __brand: "IncidentKey"; } | null; processDefinitionId: string; processDefinitionKey: string; processInstanceKey: string; rootProcessInstanceKey: | { [key: number]: string; __brand: "ProcessInstanceKey"; } | null; startDate: string; state: ElementInstanceStateEnum; tenantId: string; type: | "USER_TASK" | "UNKNOWN" | "UNSPECIFIED" | "PROCESS" | "SUB_PROCESS" | "EVENT_SUB_PROCESS" | "AD_HOC_SUB_PROCESS" | "AD_HOC_SUB_PROCESS_INNER_INSTANCE" | "START_EVENT" | "INTERMEDIATE_CATCH_EVENT" | "INTERMEDIATE_THROW_EVENT" | "BOUNDARY_EVENT" | "END_EVENT" | "SERVICE_TASK" | "RECEIVE_TASK" | "MANUAL_TASK" | "TASK" | "EXCLUSIVE_GATEWAY" | "INCLUSIVE_GATEWAY" | "PARALLEL_GATEWAY" | "EVENT_BASED_GATEWAY" | "SEQUENCE_FLOW" | "MULTI_INSTANCE_BODY" | "CALL_ACTIVITY" | "BUSINESS_RULE_TASK" | "SCRIPT_TASK" | "SEND_TASK"; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `elementId`: `string`; `elementInstanceKey`: `string`; `elementName`: `string`; `endDate`: `string` \| `null`; `hasIncident`: `boolean`; `incidentKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"IncidentKey"`; \} \| `null`; `processDefinitionId`: `string`; `processDefinitionKey`: `string`; `processInstanceKey`: `string`; `rootProcessInstanceKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"ProcessInstanceKey"`; \} \| `null`; `startDate`: `string`; `state`: [`ElementInstanceStateEnum`](../type-aliases/ElementInstanceStateEnum.md); `tenantId`: `string`; `type`: \| `"USER_TASK"` \| `"UNKNOWN"` \| `"UNSPECIFIED"` \| `"PROCESS"` \| `"SUB_PROCESS"` \| `"EVENT_SUB_PROCESS"` \| `"AD_HOC_SUB_PROCESS"` \| `"AD_HOC_SUB_PROCESS_INNER_INSTANCE"` \| `"START_EVENT"` \| `"INTERMEDIATE_CATCH_EVENT"` \| `"INTERMEDIATE_THROW_EVENT"` \| `"BOUNDARY_EVENT"` \| `"END_EVENT"` \| `"SERVICE_TASK"` \| `"RECEIVE_TASK"` \| `"MANUAL_TASK"` \| `"TASK"` \| `"EXCLUSIVE_GATEWAY"` \| `"INCLUSIVE_GATEWAY"` \| `"PARALLEL_GATEWAY"` \| `"EVENT_BASED_GATEWAY"` \| `"SEQUENCE_FLOW"` \| `"MULTI_INSTANCE_BODY"` \| `"CALL_ACTIVITY"` \| `"BUSINESS_RULE_TASK"` \| `"SCRIPT_TASK"` \| `"SEND_TASK"`; \}\> ### getErrorMode() ```ts getErrorMode(...a): "throw" | "result"; ``` #### Parameters ##### a ...\[\] #### Returns `"throw"` \| `"result"` ### getFormByKey() ```ts getFormByKey(...a): CancelablePromise<{ formId: string; formKey: string; schema: string; tenantId: string; version: number; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `formId`: `string`; `formKey`: `string`; `schema`: `string`; `tenantId`: `string`; `version`: `number`; \}\> ### getGlobalClusterVariable() ```ts getGlobalClusterVariable(...a): CancelablePromise<{ name: string; scope: ClusterVariableScopeEnum; tenantId: string | null; value: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `name`: `string`; `scope`: [`ClusterVariableScopeEnum`](../type-aliases/ClusterVariableScopeEnum.md); `tenantId`: `string` \| `null`; `value`: `string`; \}\> ### getGlobalJobStatistics() ```ts getGlobalJobStatistics(...a): CancelablePromise<{ completed: { count: number; lastUpdatedAt: string | null; }; created: { count: number; lastUpdatedAt: string | null; }; failed: { count: number; lastUpdatedAt: string | null; }; isIncomplete: boolean; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `completed`: \{ `count`: `number`; `lastUpdatedAt`: `string` \| `null`; \}; `created`: \{ `count`: `number`; `lastUpdatedAt`: `string` \| `null`; \}; `failed`: \{ `count`: `number`; `lastUpdatedAt`: `string` \| `null`; \}; `isIncomplete`: `boolean`; \}\> ### getGlobalTaskListener() ```ts getGlobalTaskListener(...a): CancelablePromise<{ afterNonGlobal?: boolean; eventTypes: GlobalTaskListenerEventTypeEnum[]; id: string; priority?: number; retries?: number; source: GlobalListenerSourceEnum; type?: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `afterNonGlobal?`: `boolean`; `eventTypes`: [`GlobalTaskListenerEventTypeEnum`](../type-aliases/GlobalTaskListenerEventTypeEnum.md)[]; `id`: `string`; `priority?`: `number`; `retries?`: `number`; `source`: [`GlobalListenerSourceEnum`](../type-aliases/GlobalListenerSourceEnum.md); `type?`: `string`; \}\> ### getGroup() ```ts getGroup(...a): CancelablePromise<{ description: string | null; groupId: string; name: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `description`: `string` \| `null`; `groupId`: `string`; `name`: `string`; \}\> ### getIncident() ```ts getIncident(...a): CancelablePromise<{ creationTime: string; elementId: string; elementInstanceKey: string; errorMessage: string; errorType: IncidentErrorTypeEnum; incidentKey: string; jobKey: | { [key: number]: string; __brand: "JobKey"; } | null; processDefinitionId: string; processDefinitionKey: string; processInstanceKey: string; rootProcessInstanceKey: | { [key: number]: string; __brand: "ProcessInstanceKey"; } | null; state: IncidentStateEnum; tenantId: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `creationTime`: `string`; `elementId`: `string`; `elementInstanceKey`: `string`; `errorMessage`: `string`; `errorType`: [`IncidentErrorTypeEnum`](../type-aliases/IncidentErrorTypeEnum.md); `incidentKey`: `string`; `jobKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"JobKey"`; \} \| `null`; `processDefinitionId`: `string`; `processDefinitionKey`: `string`; `processInstanceKey`: `string`; `rootProcessInstanceKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"ProcessInstanceKey"`; \} \| `null`; `state`: [`IncidentStateEnum`](../type-aliases/IncidentStateEnum.md); `tenantId`: `string`; \}\> ### getJobErrorStatistics() ```ts getJobErrorStatistics(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### getJobTimeSeriesStatistics() ```ts getJobTimeSeriesStatistics(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### getJobTypeStatistics() ```ts getJobTypeStatistics(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### getJobWorkerStatistics() ```ts getJobWorkerStatistics(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### getLicense() ```ts getLicense(...a): CancelablePromise<{ expiresAt: string | null; isCommercial: boolean; licenseType: string; validLicense: boolean; }>; ``` #### Parameters ##### a ...\[`object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `expiresAt`: `string` \| `null`; `isCommercial`: `boolean`; `licenseType`: `string`; `validLicense`: `boolean`; \}\> ### getMappingRule() ```ts getMappingRule(...a): CancelablePromise<{ claimName: string; claimValue: string; mappingRuleId: string; name: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `claimName`: `string`; `claimValue`: `string`; `mappingRuleId`: `string`; `name`: `string`; \}\> ### getProcessDefinition() ```ts getProcessDefinition(...a): CancelablePromise<{ hasStartForm: boolean; name: string | null; processDefinitionId: string; processDefinitionKey: string; resourceName: string; tenantId: string; version: number; versionTag: string | null; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `hasStartForm`: `boolean`; `name`: `string` \| `null`; `processDefinitionId`: `string`; `processDefinitionKey`: `string`; `resourceName`: `string`; `tenantId`: `string`; `version`: `number`; `versionTag`: `string` \| `null`; \}\> ### getProcessDefinitionInstanceStatistics() ```ts getProcessDefinitionInstanceStatistics(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### getProcessDefinitionInstanceVersionStatistics() ```ts getProcessDefinitionInstanceVersionStatistics(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### getProcessDefinitionMessageSubscriptionStatistics() ```ts getProcessDefinitionMessageSubscriptionStatistics(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### getProcessDefinitionStatistics() ```ts getProcessDefinitionStatistics(...a): CancelablePromise<{ items: object[]; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; \}\> ### getProcessDefinitionXml() ```ts getProcessDefinitionXml(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`string`\> ### getProcessInstance() ```ts getProcessInstance(...a): CancelablePromise<{ businessId: | { [key: number]: string; __brand: "BusinessId"; } | null; endDate: string | null; hasIncident: boolean; parentElementInstanceKey: | { [key: number]: string; __brand: "ElementInstanceKey"; } | null; parentProcessInstanceKey: | { [key: number]: string; __brand: "ProcessInstanceKey"; } | null; processDefinitionId: string; processDefinitionKey: string; processDefinitionName: string | null; processDefinitionVersion: number; processDefinitionVersionTag: string | null; processInstanceKey: string; rootProcessInstanceKey: | { [key: number]: string; __brand: "ProcessInstanceKey"; } | null; startDate: string; state: ProcessInstanceStateEnum; tags: string[]; tenantId: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `businessId`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"BusinessId"`; \} \| `null`; `endDate`: `string` \| `null`; `hasIncident`: `boolean`; `parentElementInstanceKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"ElementInstanceKey"`; \} \| `null`; `parentProcessInstanceKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"ProcessInstanceKey"`; \} \| `null`; `processDefinitionId`: `string`; `processDefinitionKey`: `string`; `processDefinitionName`: `string` \| `null`; `processDefinitionVersion`: `number`; `processDefinitionVersionTag`: `string` \| `null`; `processInstanceKey`: `string`; `rootProcessInstanceKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"ProcessInstanceKey"`; \} \| `null`; `startDate`: `string`; `state`: [`ProcessInstanceStateEnum`](../type-aliases/ProcessInstanceStateEnum.md); `tags`: `string`[]; `tenantId`: `string`; \}\> ### getProcessInstanceCallHierarchy() ```ts getProcessInstanceCallHierarchy(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`object`[]\> ### getProcessInstanceSequenceFlows() ```ts getProcessInstanceSequenceFlows(...a): CancelablePromise<{ items: object[]; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; \}\> ### getProcessInstanceStatistics() ```ts getProcessInstanceStatistics(...a): CancelablePromise<{ items: object[]; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; \}\> ### getProcessInstanceStatisticsByDefinition() ```ts getProcessInstanceStatisticsByDefinition(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### getProcessInstanceStatisticsByError() ```ts getProcessInstanceStatisticsByError(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### getResource() ```ts getResource(...a): CancelablePromise<{ resourceId: string; resourceKey: string; resourceName: string; tenantId: string; version: number; versionTag: string | null; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `resourceId`: `string`; `resourceKey`: `string`; `resourceName`: `string`; `tenantId`: `string`; `version`: `number`; `versionTag`: `string` \| `null`; \}\> ### getResourceContent() ```ts getResourceContent(...a): CancelablePromise<{ [key: string]: unknown; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ \[`key`: `string`\]: `unknown`; \}\> ### getResourceContentBinary() ```ts getResourceContentBinary(...a): CancelablePromise<{ }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ \}\> ### getRole() ```ts getRole(...a): CancelablePromise<{ description: string | null; name: string; roleId: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `description`: `string` \| `null`; `name`: `string`; `roleId`: `string`; \}\> ### getStartProcessForm() ```ts getStartProcessForm(...a): CancelablePromise< | void | { formId: string; formKey: string; schema: string; tenantId: string; version: number; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\< \| `void` \| \{ `formId`: `string`; `formKey`: `string`; `schema`: `string`; `tenantId`: `string`; `version`: `number`; \}\> ### getStatus() ```ts getStatus(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### getSystemConfiguration() ```ts getSystemConfiguration(...a): CancelablePromise<{ authentication: { canLogout: boolean; isLoginDelegated: boolean; }; cloud: { clusterId: string | null; mixpanelAPIHost: string | null; mixpanelToken: string | null; organizationId: string | null; stage: CloudStage | null; }; components: { active: WebappComponent[]; }; deployment: { contextPath: string; isEnterprise: boolean; isMultiTenancyEnabled: boolean; maxRequestSize: number; }; jobMetrics: { enabled: boolean; exportInterval: string; maxJobTypeLength: number; maxTenantIdLength: number; maxUniqueKeys: number; maxWorkerNameLength: number; }; }>; ``` #### Parameters ##### a ...\[`object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `authentication`: \{ `canLogout`: `boolean`; `isLoginDelegated`: `boolean`; \}; `cloud`: \{ `clusterId`: `string` \| `null`; `mixpanelAPIHost`: `string` \| `null`; `mixpanelToken`: `string` \| `null`; `organizationId`: `string` \| `null`; `stage`: [`CloudStage`](../type-aliases/CloudStage.md) \| `null`; \}; `components`: \{ `active`: [`WebappComponent`](../type-aliases/WebappComponent.md)[]; \}; `deployment`: \{ `contextPath`: `string`; `isEnterprise`: `boolean`; `isMultiTenancyEnabled`: `boolean`; `maxRequestSize`: `number`; \}; `jobMetrics`: \{ `enabled`: `boolean`; `exportInterval`: `string`; `maxJobTypeLength`: `number`; `maxTenantIdLength`: `number`; `maxUniqueKeys`: `number`; `maxWorkerNameLength`: `number`; \}; \}\> ### getTenant() ```ts getTenant(...a): CancelablePromise<{ description: string | null; name: string; tenantId: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `description`: `string` \| `null`; `name`: `string`; `tenantId`: `string`; \}\> ### getTenantClusterVariable() ```ts getTenantClusterVariable(...a): CancelablePromise<{ name: string; scope: ClusterVariableScopeEnum; tenantId: string | null; value: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `name`: `string`; `scope`: [`ClusterVariableScopeEnum`](../type-aliases/ClusterVariableScopeEnum.md); `tenantId`: `string` \| `null`; `value`: `string`; \}\> ### getTopology() ```ts getTopology(...a): CancelablePromise<{ brokers: object[]; clusterId: string | null; clusterSize: number; gatewayVersion: string; lastCompletedChangeId: string; partitionsCount: number; replicationFactor: number; }>; ``` #### Parameters ##### a ...\[`object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `brokers`: `object`[]; `clusterId`: `string` \| `null`; `clusterSize`: `number`; `gatewayVersion`: `string`; `lastCompletedChangeId`: `string`; `partitionsCount`: `number`; `replicationFactor`: `number`; \}\> ### getUsageMetrics() ```ts getUsageMetrics(...a): CancelablePromise<{ activeTenants: number; assignees: number; decisionInstances: number; processInstances: number; tenants: { [key: string]: object; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `activeTenants`: `number`; `assignees`: `number`; `decisionInstances`: `number`; `processInstances`: `number`; `tenants`: \{ \[`key`: `string`\]: `object`; \}; \}\> ### getUser() ```ts getUser(...a): CancelablePromise<{ email: string | null; name: string | null; username: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `email`: `string` \| `null`; `name`: `string` \| `null`; `username`: `string`; \}\> ### getUserTask() ```ts getUserTask(...a): CancelablePromise<{ assignee: string | null; candidateGroups: string[]; candidateUsers: string[]; completionDate: string | null; creationDate: string; customHeaders: { [key: string]: string; }; dueDate: string | null; elementId: string; elementInstanceKey: string; externalFormReference: string | null; followUpDate: string | null; formKey: | { [key: number]: string; __brand: "FormKey"; } | null; name: string | null; priority: number; processDefinitionId: string; processDefinitionKey: string; processDefinitionVersion: number; processInstanceKey: string; processName: string | null; rootProcessInstanceKey: | { [key: number]: string; __brand: "ProcessInstanceKey"; } | null; state: UserTaskStateEnum; tags: string[]; tenantId: string; userTaskKey: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `assignee`: `string` \| `null`; `candidateGroups`: `string`[]; `candidateUsers`: `string`[]; `completionDate`: `string` \| `null`; `creationDate`: `string`; `customHeaders`: \{ \[`key`: `string`\]: `string`; \}; `dueDate`: `string` \| `null`; `elementId`: `string`; `elementInstanceKey`: `string`; `externalFormReference`: `string` \| `null`; `followUpDate`: `string` \| `null`; `formKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"FormKey"`; \} \| `null`; `name`: `string` \| `null`; `priority`: `number`; `processDefinitionId`: `string`; `processDefinitionKey`: `string`; `processDefinitionVersion`: `number`; `processInstanceKey`: `string`; `processName`: `string` \| `null`; `rootProcessInstanceKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"ProcessInstanceKey"`; \} \| `null`; `state`: [`UserTaskStateEnum`](../type-aliases/UserTaskStateEnum.md); `tags`: `string`[]; `tenantId`: `string`; `userTaskKey`: `string`; \}\> ### getUserTaskForm() ```ts getUserTaskForm(...a): CancelablePromise< | void | { formId: string; formKey: string; schema: string; tenantId: string; version: number; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\< \| `void` \| \{ `formId`: `string`; `formKey`: `string`; `schema`: `string`; `tenantId`: `string`; `version`: `number`; \}\> ### getVariable() ```ts getVariable(...a): CancelablePromise<{ name: string; processInstanceKey: string; rootProcessInstanceKey: | { [key: number]: string; __brand: "ProcessInstanceKey"; } | null; scopeKey: string; tenantId: string; value: string; variableKey: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `name`: `string`; `processInstanceKey`: `string`; `rootProcessInstanceKey`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"ProcessInstanceKey"`; \} \| `null`; `scopeKey`: `string`; `tenantId`: `string`; `value`: `string`; `variableKey`: `string`; \}\> ### getWorkers() ```ts getWorkers(...a): any[]; ``` #### Parameters ##### a ...\[\] #### Returns `any`[] ### logger() ```ts logger(...a): object; ``` #### Parameters ##### a ...\[`string`\] #### Returns `object` ##### code() ```ts code(...a): void; ``` ###### Parameters ###### a ...\[[`LogLevel`](../../logger/type-aliases/LogLevel.md), `string`, `string`, `any`\] ###### Returns `void` ##### debug() ```ts debug(...a): void; ``` ###### Parameters ###### a ...`any`[] ###### Returns `void` ##### error() ```ts error(...a): void; ``` ###### Parameters ###### a ...`any`[] ###### Returns `void` ##### info() ```ts info(...a): void; ``` ###### Parameters ###### a ...`any`[] ###### Returns `void` ##### level() ```ts level(...a): LogLevel; ``` ###### Parameters ###### a ...\[\] ###### Returns [`LogLevel`](../../logger/type-aliases/LogLevel.md) ##### scope() ```ts scope(...a): { level: () => LogLevel; setLevel: (level: LogLevel) => void; setTransport: (t?: ((e: { level: LogLevel; scope: string; ts: number; args: any[]; code?: string | undefined; data?: any; }) => void) | undefined) => void; ... 7 more ...; code: (level: LogLevel, code: string, msg: string, data?: any) => void; }; ``` ###### Parameters ###### a ...\[`string`\] ###### Returns \{ level: () =\> LogLevel; setLevel: (level: LogLevel) =\> void; setTransport: (t?: ((e: \{ level: LogLevel; scope: string; ts: number; args: any\[\]; code?: string \| undefined; data?: any; \}) =\> void) \| undefined) =\> void; ... 7 more ...; code: (level: LogLevel, code: string, msg: string, data?: any) =\> void; \} ##### setLevel() ```ts setLevel(...a): void; ``` ###### Parameters ###### a ...\[[`LogLevel`](../../logger/type-aliases/LogLevel.md)\] ###### Returns `void` ##### setTransport() ```ts setTransport(...a): void; ``` ###### Parameters ###### a ...\[(...`a`) => `void`\] ###### Returns `void` ##### silly() ```ts silly(...a): void; ``` ###### Parameters ###### a ...`any`[] ###### Returns `void` ##### trace() ```ts trace(...a): void; ``` ###### Parameters ###### a ...`any`[] ###### Returns `void` ##### warn() ```ts warn(...a): void; ``` ###### Parameters ###### a ...`any`[] ###### Returns `void` ### migrateProcessInstance() ```ts migrateProcessInstance(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### migrateProcessInstancesBatchOperation() ```ts migrateProcessInstancesBatchOperation(...a): CancelablePromise<{ batchOperationKey: string; batchOperationType: BatchOperationTypeEnum; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `batchOperationKey`: `string`; `batchOperationType`: [`BatchOperationTypeEnum`](../type-aliases/BatchOperationTypeEnum.md); \}\> ### modifyProcessInstance() ```ts modifyProcessInstance(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### modifyProcessInstancesBatchOperation() ```ts modifyProcessInstancesBatchOperation(...a): CancelablePromise<{ batchOperationKey: string; batchOperationType: BatchOperationTypeEnum; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `batchOperationKey`: `string`; `batchOperationType`: [`BatchOperationTypeEnum`](../type-aliases/BatchOperationTypeEnum.md); \}\> ### onAuthHeaders() ```ts onAuthHeaders(...a): void; ``` #### Parameters ##### a ...\[(...`a`) => \| `Promise`\<\{ \[`key`: `string`\]: `string`; \}\> \| \{ \[`key`: `string`\]: `string`; \}\] #### Returns `void` ### pinClock() ```ts pinClock(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### publishMessage() ```ts publishMessage(...a): CancelablePromise<{ messageKey: string; tenantId: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `messageKey`: `string`; `tenantId`: `string`; \}\> ### resetClock() ```ts resetClock(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### resolveIncident() ```ts resolveIncident(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### resolveIncidentsBatchOperation() ```ts resolveIncidentsBatchOperation(...a): CancelablePromise<{ batchOperationKey: string; batchOperationType: BatchOperationTypeEnum; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `batchOperationKey`: `string`; `batchOperationType`: [`BatchOperationTypeEnum`](../type-aliases/BatchOperationTypeEnum.md); \}\> ### resolveProcessInstanceIncidents() ```ts resolveProcessInstanceIncidents(...a): CancelablePromise<{ batchOperationKey: string; batchOperationType: BatchOperationTypeEnum; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `batchOperationKey`: `string`; `batchOperationType`: [`BatchOperationTypeEnum`](../type-aliases/BatchOperationTypeEnum.md); \}\> ### resumeBatchOperation() ```ts resumeBatchOperation(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### searchAgentInstances() ```ts searchAgentInstances(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchAuditLogs() ```ts searchAuditLogs(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchAuthorizations() ```ts searchAuthorizations(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchBatchOperationItems() ```ts searchBatchOperationItems(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchBatchOperations() ```ts searchBatchOperations(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchClientsForGroup() ```ts searchClientsForGroup(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchClientsForRole() ```ts searchClientsForRole(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchClientsForTenant() ```ts searchClientsForTenant(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchClusterVariables() ```ts searchClusterVariables(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchCorrelatedMessageSubscriptions() ```ts searchCorrelatedMessageSubscriptions(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchDecisionDefinitions() ```ts searchDecisionDefinitions(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchDecisionInstances() ```ts searchDecisionInstances(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchDecisionRequirements() ```ts searchDecisionRequirements(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchElementInstanceIncidents() ```ts searchElementInstanceIncidents(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchElementInstances() ```ts searchElementInstances(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchGlobalTaskListeners() ```ts searchGlobalTaskListeners(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchGroupIdsForTenant() ```ts searchGroupIdsForTenant(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchGroups() ```ts searchGroups(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchGroupsForRole() ```ts searchGroupsForRole(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchIncidents() ```ts searchIncidents(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchJobs() ```ts searchJobs(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchMappingRule() ```ts searchMappingRule(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchMappingRulesForGroup() ```ts searchMappingRulesForGroup(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchMappingRulesForRole() ```ts searchMappingRulesForRole(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchMappingRulesForTenant() ```ts searchMappingRulesForTenant(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchMessageSubscriptions() ```ts searchMessageSubscriptions(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchProcessDefinitions() ```ts searchProcessDefinitions(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchProcessInstanceIncidents() ```ts searchProcessInstanceIncidents(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchProcessInstances() ```ts searchProcessInstances(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchResources() ```ts searchResources(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchRoles() ```ts searchRoles(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchRolesForGroup() ```ts searchRolesForGroup(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchRolesForTenant() ```ts searchRolesForTenant(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchTenants() ```ts searchTenants(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchUsers() ```ts searchUsers(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchUsersForGroup() ```ts searchUsersForGroup(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchUsersForRole() ```ts searchUsersForRole(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchUsersForTenant() ```ts searchUsersForTenant(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchUserTaskAuditLogs() ```ts searchUserTaskAuditLogs(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchUserTaskEffectiveVariables() ```ts searchUserTaskEffectiveVariables(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchUserTasks() ```ts searchUserTasks(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchUserTaskVariables() ```ts searchUserTaskVariables(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### searchVariables() ```ts searchVariables(...a): CancelablePromise<{ items: object[]; page: { endCursor: | { [key: number]: string; __brand: "EndCursor"; } | null; hasMoreTotalItems: boolean; startCursor: | { [key: number]: string; __brand: "StartCursor"; } | null; totalItems: number; }; }>; ``` #### Parameters ##### a ...\[`object`, `object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `items`: `object`[]; `page`: \{ `endCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"EndCursor"`; \} \| `null`; `hasMoreTotalItems`: `boolean`; `startCursor`: \| \{ \[`key`: `number`\]: `string`; `__brand`: `"StartCursor"`; \} \| `null`; `totalItems`: `number`; \}; \}\> ### stopAllWorkers() ```ts stopAllWorkers(...a): void; ``` #### Parameters ##### a ...\[\] #### Returns `void` ### suspendBatchOperation() ```ts suspendBatchOperation(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### throwJobError() ```ts throwJobError(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### unassignClientFromGroup() ```ts unassignClientFromGroup(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### unassignClientFromTenant() ```ts unassignClientFromTenant(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### unassignGroupFromTenant() ```ts unassignGroupFromTenant(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### unassignMappingRuleFromGroup() ```ts unassignMappingRuleFromGroup(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### unassignMappingRuleFromTenant() ```ts unassignMappingRuleFromTenant(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### unassignRoleFromClient() ```ts unassignRoleFromClient(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### unassignRoleFromGroup() ```ts unassignRoleFromGroup(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### unassignRoleFromMappingRule() ```ts unassignRoleFromMappingRule(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### unassignRoleFromTenant() ```ts unassignRoleFromTenant(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### unassignRoleFromUser() ```ts unassignRoleFromUser(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### unassignUserFromGroup() ```ts unassignUserFromGroup(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### unassignUserFromTenant() ```ts unassignUserFromTenant(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### unassignUserTask() ```ts unassignUserTask(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### updateAgentInstance() ```ts updateAgentInstance(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### updateAuthorization() ```ts updateAuthorization(...a): CancelablePromise; ``` #### Parameters ##### a ...\[ \| \{ `authorizationKey`: `string`; `ownerId`: `string`; `ownerType`: [`OwnerTypeEnum`](../type-aliases/OwnerTypeEnum.md); `permissionTypes`: [`PermissionTypeEnum`](../type-aliases/PermissionTypeEnum.md)[]; `resourceId`: `string`; `resourceType`: [`ResourceTypeEnum`](../type-aliases/ResourceTypeEnum.md); \} \| \{ `authorizationKey`: `string`; `ownerId`: `string`; `ownerType`: [`OwnerTypeEnum`](../type-aliases/OwnerTypeEnum.md); `permissionTypes`: [`PermissionTypeEnum`](../type-aliases/PermissionTypeEnum.md)[]; `resourcePropertyName`: `string`; `resourceType`: [`ResourceTypeEnum`](../type-aliases/ResourceTypeEnum.md); \}, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### updateGlobalClusterVariable() ```ts updateGlobalClusterVariable(...a): CancelablePromise<{ name: string; scope: ClusterVariableScopeEnum; tenantId: string | null; value: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `name`: `string`; `scope`: [`ClusterVariableScopeEnum`](../type-aliases/ClusterVariableScopeEnum.md); `tenantId`: `string` \| `null`; `value`: `string`; \}\> ### updateGlobalTaskListener() ```ts updateGlobalTaskListener(...a): CancelablePromise<{ afterNonGlobal?: boolean; eventTypes: GlobalTaskListenerEventTypeEnum[]; id: string; priority?: number; retries?: number; source: GlobalListenerSourceEnum; type?: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `afterNonGlobal?`: `boolean`; `eventTypes`: [`GlobalTaskListenerEventTypeEnum`](../type-aliases/GlobalTaskListenerEventTypeEnum.md)[]; `id`: `string`; `priority?`: `number`; `retries?`: `number`; `source`: [`GlobalListenerSourceEnum`](../type-aliases/GlobalListenerSourceEnum.md); `type?`: `string`; \}\> ### updateGroup() ```ts updateGroup(...a): CancelablePromise<{ description: string | null; groupId: string; name: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `description`: `string` \| `null`; `groupId`: `string`; `name`: `string`; \}\> ### updateJob() ```ts updateJob(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### updateMappingRule() ```ts updateMappingRule(...a): CancelablePromise<{ claimName: string; claimValue: string; mappingRuleId: string; name: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `claimName`: `string`; `claimValue`: `string`; `mappingRuleId`: `string`; `name`: `string`; \}\> ### updateRole() ```ts updateRole(...a): CancelablePromise<{ description: string | null; name: string; roleId: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `description`: `string` \| `null`; `name`: `string`; `roleId`: `string`; \}\> ### updateTenant() ```ts updateTenant(...a): CancelablePromise<{ description: string | null; name: string; tenantId: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `description`: `string` \| `null`; `name`: `string`; `tenantId`: `string`; \}\> ### updateTenantClusterVariable() ```ts updateTenantClusterVariable(...a): CancelablePromise<{ name: string; scope: ClusterVariableScopeEnum; tenantId: string | null; value: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `name`: `string`; `scope`: [`ClusterVariableScopeEnum`](../type-aliases/ClusterVariableScopeEnum.md); `tenantId`: `string` \| `null`; `value`: `string`; \}\> ### updateUser() ```ts updateUser(...a): CancelablePromise<{ email: string | null; name: string | null; username: string; }>; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<\{ `email`: `string` \| `null`; `name`: `string` \| `null`; `username`: `string`; \}\> ### updateUserTask() ```ts updateUserTask(...a): CancelablePromise; ``` #### Parameters ##### a ...\[`object`, `object`\] #### Returns [`CancelablePromise`](../interfaces/CancelablePromise.md)\<`void`\> ### withCorrelation() ```ts withCorrelation(...a): Promise; ``` #### Parameters ##### a ...\[`string`, (...`a`) => `unknown`\] #### Returns `Promise`\<`unknown`\> --- ## Function: createCamundaFpClient() ```ts function createCamundaFpClient(options?): CamundaFpClient; ``` **`Experimental`** This feature under development and is not guaranteed to be fully tested or stable. ## Parameters ### options? [`CamundaOptions`](../interfaces/CamundaOptions.md) ## Returns [`CamundaFpClient`](../type-aliases/CamundaFpClient.md) ## Description Camunda FP Client - a Task-Either compatible client. See the README and [this test](https://github.com/camunda/orchestration-cluster-api-js/blob/main/tests-integration/fp.test.ts) for example usage. --- ## Function: createCamundaResultClient() ```ts function createCamundaResultClient(options?): CamundaResultClient; ``` **`Experimental`** This feature is under development and is not guaranteed to be fully tested or stable. ## Parameters ### options? [`CamundaOptions`](../interfaces/CamundaOptions.md) ## Returns [`CamundaResultClient`](../type-aliases/CamundaResultClient.md) ## Description Factory returning a proxy that mirrors the CamundaClient surface but never throws. All async returning methods (Promise or CancelablePromise) are wrapped into Promise>. Synchronous utility methods (e.g. logger(), getConfig()) are passed through unchanged. --- ## Function: isErr() ```ts function isErr(r): r is { error: E; ok: false }; ``` ## Type Parameters ### T `T` ### E `E` ## Parameters ### r [`Result`](../type-aliases/Result.md)\<`T`, `E`\> ## Returns `r is { error: E; ok: false }` --- ## Function: isLeft() ```ts function isLeft(e): e is Left; ``` ## Type Parameters ### E `E` ### A `A` ## Parameters ### e [`Either`](../type-aliases/Either.md)\<`E`, `A`\> ## Returns `e is Left` --- ## Function: isOk() ```ts function isOk(r): r is { ok: true; value: T }; ``` ## Type Parameters ### T `T` ### E `E` ## Parameters ### r [`Result`](../type-aliases/Result.md)\<`T`, `E`\> ## Returns `r is { ok: true; value: T }` --- ## Function: isRight() ```ts function isRight(e): e is Right; ``` ## Type Parameters ### E `E` ### A `A` ## Parameters ### e [`Either`](../type-aliases/Either.md)\<`E`, `A`\> ## Returns `e is Right` --- ## Function: isSdkError() ```ts function isSdkError(e): e is SdkError; ``` ## Parameters ### e `unknown` ## Returns `e is SdkError` --- ## index ## Namespaces - [AgentInstanceKey](namespaces/AgentInstanceKey/index.md) - [AuditLogEntityKey](namespaces/AuditLogEntityKey/index.md) - [AuditLogKey](namespaces/AuditLogKey/index.md) - [AuthorizationKey](namespaces/AuthorizationKey/index.md) - [BatchOperationKey](namespaces/BatchOperationKey/index.md) - [BusinessId](namespaces/BusinessId/index.md) - [ClientId](namespaces/ClientId/index.md) - [ClusterVariableName](namespaces/ClusterVariableName/index.md) - [ConditionalEvaluationKey](namespaces/ConditionalEvaluationKey/index.md) - [DecisionDefinitionId](namespaces/DecisionDefinitionId/index.md) - [DecisionDefinitionKey](namespaces/DecisionDefinitionKey/index.md) - [DecisionEvaluationInstanceKey](namespaces/DecisionEvaluationInstanceKey/index.md) - [DecisionEvaluationKey](namespaces/DecisionEvaluationKey/index.md) - [DecisionInstanceKey](namespaces/DecisionInstanceKey/index.md) - [DecisionRequirementsKey](namespaces/DecisionRequirementsKey/index.md) - [DeploymentKey](namespaces/DeploymentKey/index.md) - [DocumentId](namespaces/DocumentId/index.md) - [ElementId](namespaces/ElementId/index.md) - [ElementInstanceKey](namespaces/ElementInstanceKey/index.md) - [EndCursor](namespaces/EndCursor/index.md) - [FormId](namespaces/FormId/index.md) - [FormKey](namespaces/FormKey/index.md) - [GlobalListenerId](namespaces/GlobalListenerId/index.md) - [GroupId](namespaces/GroupId/index.md) - [IncidentKey](namespaces/IncidentKey/index.md) - [JobKey](namespaces/JobKey/index.md) - [MappingRuleId](namespaces/MappingRuleId/index.md) - [MessageKey](namespaces/MessageKey/index.md) - [MessageSubscriptionKey](namespaces/MessageSubscriptionKey/index.md) - [ProcessDefinitionId](namespaces/ProcessDefinitionId/index.md) - [ProcessDefinitionKey](namespaces/ProcessDefinitionKey/index.md) - [ProcessInstanceKey](namespaces/ProcessInstanceKey/index.md) - [RoleId](namespaces/RoleId/index.md) - [SignalKey](namespaces/SignalKey/index.md) - [StartCursor](namespaces/StartCursor/index.md) - [Tag](namespaces/Tag/index.md) - [TenantId](namespaces/TenantId/index.md) - [Username](namespaces/Username/index.md) - [UserTaskKey](namespaces/UserTaskKey/index.md) - [VariableKey](namespaces/VariableKey/index.md) ## Classes - [CamundaClient](classes/CamundaClient.md) - [CamundaValidationError](classes/CamundaValidationError.md) - [CancelError](classes/CancelError.md) - [EventualConsistencyTimeoutError](classes/EventualConsistencyTimeoutError.md) ## Interfaces - [CamundaConfig](interfaces/CamundaConfig.md) - [CamundaOptions](interfaces/CamundaOptions.md) - [CancelablePromise](interfaces/CancelablePromise.md) - [CreateLoggerOptions](interfaces/CreateLoggerOptions.md) - [EnrichedActivatedJob](interfaces/EnrichedActivatedJob.md) - [ExtendedDeploymentResult](interfaces/ExtendedDeploymentResult.md) - [HttpRetryPolicy](interfaces/HttpRetryPolicy.md) - [JobWorker](interfaces/JobWorker.md) - [JobWorkerConfig](interfaces/JobWorkerConfig.md) - [OperationOptions](interfaces/OperationOptions.md) - [SupportLogger](interfaces/SupportLogger.md) - [TelemetryHooks](interfaces/TelemetryHooks.md) - [ThreadedJobWorker](interfaces/ThreadedJobWorker.md) - [ThreadedJobWorkerConfig](interfaces/ThreadedJobWorkerConfig.md) - [ThreadPool](interfaces/ThreadPool.md) ## Type Aliases - [ActivateAdHocSubProcessActivitiesData](type-aliases/ActivateAdHocSubProcessActivitiesData.md) - [ActivateAdHocSubProcessActivitiesError](type-aliases/ActivateAdHocSubProcessActivitiesError.md) - [ActivateAdHocSubProcessActivitiesErrors](type-aliases/ActivateAdHocSubProcessActivitiesErrors.md) - [activateAdHocSubProcessActivitiesInput](type-aliases/activateAdHocSubProcessActivitiesInput.md) - [ActivateAdHocSubProcessActivitiesResponse](type-aliases/ActivateAdHocSubProcessActivitiesResponse.md) - [ActivateAdHocSubProcessActivitiesResponses](type-aliases/ActivateAdHocSubProcessActivitiesResponses.md) - [ActivatedJobResult](type-aliases/ActivatedJobResult.md) - [ActivateJobsData](type-aliases/ActivateJobsData.md) - [ActivateJobsError](type-aliases/ActivateJobsError.md) - [ActivateJobsErrors](type-aliases/ActivateJobsErrors.md) - [activateJobsInput](type-aliases/activateJobsInput.md) - [ActivateJobsResponse](type-aliases/ActivateJobsResponse.md) - [ActivateJobsResponses](type-aliases/ActivateJobsResponses.md) - [AdHocSubProcessActivateActivitiesInstruction](type-aliases/AdHocSubProcessActivateActivitiesInstruction.md) - [AdHocSubProcessActivateActivityReference](type-aliases/AdHocSubProcessActivateActivityReference.md) - [AdvancedActorTypeFilter](type-aliases/AdvancedActorTypeFilter.md) - [AdvancedAgentInstanceKeyFilter](type-aliases/AdvancedAgentInstanceKeyFilter.md) - [AdvancedAgentInstanceStatusFilter](type-aliases/AdvancedAgentInstanceStatusFilter.md) - [AdvancedAuditLogEntityKeyFilter](type-aliases/AdvancedAuditLogEntityKeyFilter.md) - [AdvancedAuditLogKeyFilter](type-aliases/AdvancedAuditLogKeyFilter.md) - [AdvancedBatchOperationItemStateFilter](type-aliases/AdvancedBatchOperationItemStateFilter.md) - [AdvancedBatchOperationStateFilter](type-aliases/AdvancedBatchOperationStateFilter.md) - [AdvancedBatchOperationTypeFilter](type-aliases/AdvancedBatchOperationTypeFilter.md) - [AdvancedCategoryFilter](type-aliases/AdvancedCategoryFilter.md) - [AdvancedClusterVariableScopeFilter](type-aliases/AdvancedClusterVariableScopeFilter.md) - [AdvancedDateTimeFilter](type-aliases/AdvancedDateTimeFilter.md) - [AdvancedDecisionDefinitionKeyFilter](type-aliases/AdvancedDecisionDefinitionKeyFilter.md) - [AdvancedDecisionEvaluationInstanceKeyFilter](type-aliases/AdvancedDecisionEvaluationInstanceKeyFilter.md) - [AdvancedDecisionEvaluationKeyFilter](type-aliases/AdvancedDecisionEvaluationKeyFilter.md) - [AdvancedDecisionInstanceStateFilter](type-aliases/AdvancedDecisionInstanceStateFilter.md) - [AdvancedDecisionRequirementsKeyFilter](type-aliases/AdvancedDecisionRequirementsKeyFilter.md) - [AdvancedDeploymentKeyFilter](type-aliases/AdvancedDeploymentKeyFilter.md) - [AdvancedElementIdFilter](type-aliases/AdvancedElementIdFilter.md) - [AdvancedElementInstanceKeyFilter](type-aliases/AdvancedElementInstanceKeyFilter.md) - [AdvancedElementInstanceStateFilter](type-aliases/AdvancedElementInstanceStateFilter.md) - [AdvancedEntityTypeFilter](type-aliases/AdvancedEntityTypeFilter.md) - [AdvancedFormKeyFilter](type-aliases/AdvancedFormKeyFilter.md) - [AdvancedGlobalListenerSourceFilter](type-aliases/AdvancedGlobalListenerSourceFilter.md) - [AdvancedGlobalTaskListenerEventTypeFilter](type-aliases/AdvancedGlobalTaskListenerEventTypeFilter.md) - [AdvancedIncidentErrorTypeFilter](type-aliases/AdvancedIncidentErrorTypeFilter.md) - [AdvancedIncidentStateFilter](type-aliases/AdvancedIncidentStateFilter.md) - [AdvancedIntegerFilter](type-aliases/AdvancedIntegerFilter.md) - [AdvancedJobKeyFilter](type-aliases/AdvancedJobKeyFilter.md) - [AdvancedJobKindFilter](type-aliases/AdvancedJobKindFilter.md) - [AdvancedJobListenerEventTypeFilter](type-aliases/AdvancedJobListenerEventTypeFilter.md) - [AdvancedJobStateFilter](type-aliases/AdvancedJobStateFilter.md) - [AdvancedMessageSubscriptionKeyFilter](type-aliases/AdvancedMessageSubscriptionKeyFilter.md) - [AdvancedMessageSubscriptionStateFilter](type-aliases/AdvancedMessageSubscriptionStateFilter.md) - [AdvancedMessageSubscriptionTypeFilter](type-aliases/AdvancedMessageSubscriptionTypeFilter.md) - [AdvancedOperationTypeFilter](type-aliases/AdvancedOperationTypeFilter.md) - [AdvancedProcessDefinitionIdFilter](type-aliases/AdvancedProcessDefinitionIdFilter.md) - [AdvancedProcessDefinitionKeyFilter](type-aliases/AdvancedProcessDefinitionKeyFilter.md) - [AdvancedProcessInstanceKeyFilter](type-aliases/AdvancedProcessInstanceKeyFilter.md) - [AdvancedProcessInstanceStateFilter](type-aliases/AdvancedProcessInstanceStateFilter.md) - [AdvancedResourceKeyFilter](type-aliases/AdvancedResourceKeyFilter.md) - [AdvancedResultFilter](type-aliases/AdvancedResultFilter.md) - [AdvancedScopeKeyFilter](type-aliases/AdvancedScopeKeyFilter.md) - [AdvancedStringFilter](type-aliases/AdvancedStringFilter.md) - [AdvancedUserTaskStateFilter](type-aliases/AdvancedUserTaskStateFilter.md) - [AdvancedVariableKeyFilter](type-aliases/AdvancedVariableKeyFilter.md) - [AgentInstanceCreationRequest](type-aliases/AgentInstanceCreationRequest.md) - [AgentInstanceCreationResult](type-aliases/AgentInstanceCreationResult.md) - [AgentInstanceDefinition](type-aliases/AgentInstanceDefinition.md) - [AgentInstanceFilter](type-aliases/AgentInstanceFilter.md) - [AgentInstanceKey](type-aliases/AgentInstanceKey.md) - [AgentInstanceKeyExactMatch](type-aliases/AgentInstanceKeyExactMatch.md) - [AgentInstanceKeyFilterProperty](type-aliases/AgentInstanceKeyFilterProperty.md) - [AgentInstanceLimits](type-aliases/AgentInstanceLimits.md) - [AgentInstanceMetrics](type-aliases/AgentInstanceMetrics.md) - [AgentInstanceMetricsDelta](type-aliases/AgentInstanceMetricsDelta.md) - [AgentInstanceResult](type-aliases/AgentInstanceResult.md) - [AgentInstanceSearchQuery](type-aliases/AgentInstanceSearchQuery.md) - [AgentInstanceSearchQueryResult](type-aliases/AgentInstanceSearchQueryResult.md) - [AgentInstanceSearchQuerySortRequest](type-aliases/AgentInstanceSearchQuerySortRequest.md) - [AgentInstanceStatusEnum](type-aliases/AgentInstanceStatusEnum.md) - [AgentInstanceStatusExactMatch](type-aliases/AgentInstanceStatusExactMatch.md) - [AgentInstanceStatusFilterProperty](type-aliases/AgentInstanceStatusFilterProperty.md) - [AgentInstanceUpdateRequest](type-aliases/AgentInstanceUpdateRequest.md) - [AgentTool](type-aliases/AgentTool.md) - [AncestorScopeInstruction](type-aliases/AncestorScopeInstruction.md) - [AssignClientToGroupData](type-aliases/AssignClientToGroupData.md) - [AssignClientToGroupError](type-aliases/AssignClientToGroupError.md) - [AssignClientToGroupErrors](type-aliases/AssignClientToGroupErrors.md) - [assignClientToGroupInput](type-aliases/assignClientToGroupInput.md) - [AssignClientToGroupResponse](type-aliases/AssignClientToGroupResponse.md) - [AssignClientToGroupResponses](type-aliases/AssignClientToGroupResponses.md) - [AssignClientToTenantData](type-aliases/AssignClientToTenantData.md) - [AssignClientToTenantError](type-aliases/AssignClientToTenantError.md) - [AssignClientToTenantErrors](type-aliases/AssignClientToTenantErrors.md) - [assignClientToTenantInput](type-aliases/assignClientToTenantInput.md) - [AssignClientToTenantResponse](type-aliases/AssignClientToTenantResponse.md) - [AssignClientToTenantResponses](type-aliases/AssignClientToTenantResponses.md) - [AssignGroupToTenantData](type-aliases/AssignGroupToTenantData.md) - [AssignGroupToTenantError](type-aliases/AssignGroupToTenantError.md) - [AssignGroupToTenantErrors](type-aliases/AssignGroupToTenantErrors.md) - [assignGroupToTenantInput](type-aliases/assignGroupToTenantInput.md) - [AssignGroupToTenantResponse](type-aliases/AssignGroupToTenantResponse.md) - [AssignGroupToTenantResponses](type-aliases/AssignGroupToTenantResponses.md) - [AssignMappingRuleToGroupData](type-aliases/AssignMappingRuleToGroupData.md) - [AssignMappingRuleToGroupError](type-aliases/AssignMappingRuleToGroupError.md) - [AssignMappingRuleToGroupErrors](type-aliases/AssignMappingRuleToGroupErrors.md) - [assignMappingRuleToGroupInput](type-aliases/assignMappingRuleToGroupInput.md) - [AssignMappingRuleToGroupResponse](type-aliases/AssignMappingRuleToGroupResponse.md) - [AssignMappingRuleToGroupResponses](type-aliases/AssignMappingRuleToGroupResponses.md) - [AssignMappingRuleToTenantData](type-aliases/AssignMappingRuleToTenantData.md) - [AssignMappingRuleToTenantError](type-aliases/AssignMappingRuleToTenantError.md) - [AssignMappingRuleToTenantErrors](type-aliases/AssignMappingRuleToTenantErrors.md) - [assignMappingRuleToTenantInput](type-aliases/assignMappingRuleToTenantInput.md) - [AssignMappingRuleToTenantResponse](type-aliases/AssignMappingRuleToTenantResponse.md) - [AssignMappingRuleToTenantResponses](type-aliases/AssignMappingRuleToTenantResponses.md) - [AssignRoleToClientData](type-aliases/AssignRoleToClientData.md) - [AssignRoleToClientError](type-aliases/AssignRoleToClientError.md) - [AssignRoleToClientErrors](type-aliases/AssignRoleToClientErrors.md) - [assignRoleToClientInput](type-aliases/assignRoleToClientInput.md) - [AssignRoleToClientResponse](type-aliases/AssignRoleToClientResponse.md) - [AssignRoleToClientResponses](type-aliases/AssignRoleToClientResponses.md) - [AssignRoleToGroupData](type-aliases/AssignRoleToGroupData.md) - [AssignRoleToGroupError](type-aliases/AssignRoleToGroupError.md) - [AssignRoleToGroupErrors](type-aliases/AssignRoleToGroupErrors.md) - [assignRoleToGroupInput](type-aliases/assignRoleToGroupInput.md) - [AssignRoleToGroupResponse](type-aliases/AssignRoleToGroupResponse.md) - [AssignRoleToGroupResponses](type-aliases/AssignRoleToGroupResponses.md) - [AssignRoleToMappingRuleData](type-aliases/AssignRoleToMappingRuleData.md) - [AssignRoleToMappingRuleError](type-aliases/AssignRoleToMappingRuleError.md) - [AssignRoleToMappingRuleErrors](type-aliases/AssignRoleToMappingRuleErrors.md) - [assignRoleToMappingRuleInput](type-aliases/assignRoleToMappingRuleInput.md) - [AssignRoleToMappingRuleResponse](type-aliases/AssignRoleToMappingRuleResponse.md) - [AssignRoleToMappingRuleResponses](type-aliases/AssignRoleToMappingRuleResponses.md) - [AssignRoleToTenantData](type-aliases/AssignRoleToTenantData.md) - [AssignRoleToTenantError](type-aliases/AssignRoleToTenantError.md) - [AssignRoleToTenantErrors](type-aliases/AssignRoleToTenantErrors.md) - [assignRoleToTenantInput](type-aliases/assignRoleToTenantInput.md) - [AssignRoleToTenantResponse](type-aliases/AssignRoleToTenantResponse.md) - [AssignRoleToTenantResponses](type-aliases/AssignRoleToTenantResponses.md) - [AssignRoleToUserData](type-aliases/AssignRoleToUserData.md) - [AssignRoleToUserError](type-aliases/AssignRoleToUserError.md) - [AssignRoleToUserErrors](type-aliases/AssignRoleToUserErrors.md) - [assignRoleToUserInput](type-aliases/assignRoleToUserInput.md) - [AssignRoleToUserResponse](type-aliases/AssignRoleToUserResponse.md) - [AssignRoleToUserResponses](type-aliases/AssignRoleToUserResponses.md) - [AssignUserTaskData](type-aliases/AssignUserTaskData.md) - [AssignUserTaskError](type-aliases/AssignUserTaskError.md) - [AssignUserTaskErrors](type-aliases/AssignUserTaskErrors.md) - [assignUserTaskInput](type-aliases/assignUserTaskInput.md) - [AssignUserTaskResponse](type-aliases/AssignUserTaskResponse.md) - [AssignUserTaskResponses](type-aliases/AssignUserTaskResponses.md) - [AssignUserToGroupData](type-aliases/AssignUserToGroupData.md) - [AssignUserToGroupError](type-aliases/AssignUserToGroupError.md) - [AssignUserToGroupErrors](type-aliases/AssignUserToGroupErrors.md) - [assignUserToGroupInput](type-aliases/assignUserToGroupInput.md) - [AssignUserToGroupResponse](type-aliases/AssignUserToGroupResponse.md) - [AssignUserToGroupResponses](type-aliases/AssignUserToGroupResponses.md) - [AssignUserToTenantData](type-aliases/AssignUserToTenantData.md) - [AssignUserToTenantError](type-aliases/AssignUserToTenantError.md) - [AssignUserToTenantErrors](type-aliases/AssignUserToTenantErrors.md) - [assignUserToTenantInput](type-aliases/assignUserToTenantInput.md) - [AssignUserToTenantResponse](type-aliases/AssignUserToTenantResponse.md) - [AssignUserToTenantResponses](type-aliases/AssignUserToTenantResponses.md) - [AuditLogActorTypeEnum](type-aliases/AuditLogActorTypeEnum.md) - [AuditLogActorTypeExactMatch](type-aliases/AuditLogActorTypeExactMatch.md) - [AuditLogActorTypeFilterProperty](type-aliases/AuditLogActorTypeFilterProperty.md) - [AuditLogCategoryEnum](type-aliases/AuditLogCategoryEnum.md) - [AuditLogEntityKey](type-aliases/AuditLogEntityKey.md) - [AuditLogEntityKeyExactMatch](type-aliases/AuditLogEntityKeyExactMatch.md) - [AuditLogEntityKeyFilterProperty](type-aliases/AuditLogEntityKeyFilterProperty.md) - [AuditLogEntityTypeEnum](type-aliases/AuditLogEntityTypeEnum.md) - [AuditLogFilter](type-aliases/AuditLogFilter.md) - [AuditLogKey](type-aliases/AuditLogKey.md) - [AuditLogKeyExactMatch](type-aliases/AuditLogKeyExactMatch.md) - [AuditLogKeyFilterProperty](type-aliases/AuditLogKeyFilterProperty.md) - [AuditLogOperationTypeEnum](type-aliases/AuditLogOperationTypeEnum.md) - [AuditLogResult](type-aliases/AuditLogResult.md) - [AuditLogResultEnum](type-aliases/AuditLogResultEnum.md) - [AuditLogResultExactMatch](type-aliases/AuditLogResultExactMatch.md) - [AuditLogResultFilterProperty](type-aliases/AuditLogResultFilterProperty.md) - [AuditLogSearchQueryRequest](type-aliases/AuditLogSearchQueryRequest.md) - [AuditLogSearchQueryResult](type-aliases/AuditLogSearchQueryResult.md) - [AuditLogSearchQuerySortRequest](type-aliases/AuditLogSearchQuerySortRequest.md) - [AuthenticationConfigurationResponse](type-aliases/AuthenticationConfigurationResponse.md) - [AuthorizationCreateResult](type-aliases/AuthorizationCreateResult.md) - [AuthorizationFilter](type-aliases/AuthorizationFilter.md) - [AuthorizationIdBasedRequest](type-aliases/AuthorizationIdBasedRequest.md) - [AuthorizationKey](type-aliases/AuthorizationKey.md) - [AuthorizationPropertyBasedRequest](type-aliases/AuthorizationPropertyBasedRequest.md) - [AuthorizationRequest](type-aliases/AuthorizationRequest.md) - [AuthorizationResult](type-aliases/AuthorizationResult.md) - [AuthorizationSearchQuery](type-aliases/AuthorizationSearchQuery.md) - [AuthorizationSearchQuerySortRequest](type-aliases/AuthorizationSearchQuerySortRequest.md) - [AuthorizationSearchResult](type-aliases/AuthorizationSearchResult.md) - [AuthStrategy](type-aliases/AuthStrategy.md) - [BackpressureSeverity](type-aliases/BackpressureSeverity.md) - [BaseProcessInstanceFilterFields](type-aliases/BaseProcessInstanceFilterFields.md) - [BasicStringFilter](type-aliases/BasicStringFilter.md) - [BasicStringFilterProperty](type-aliases/BasicStringFilterProperty.md) - [BatchOperationCreatedResult](type-aliases/BatchOperationCreatedResult.md) - [BatchOperationError](type-aliases/BatchOperationError.md) - [BatchOperationFilter](type-aliases/BatchOperationFilter.md) - [BatchOperationItemFilter](type-aliases/BatchOperationItemFilter.md) - [BatchOperationItemResponse](type-aliases/BatchOperationItemResponse.md) - [BatchOperationItemSearchQuery](type-aliases/BatchOperationItemSearchQuery.md) - [BatchOperationItemSearchQueryResult](type-aliases/BatchOperationItemSearchQueryResult.md) - [BatchOperationItemSearchQuerySortRequest](type-aliases/BatchOperationItemSearchQuerySortRequest.md) - [BatchOperationItemStateEnum](type-aliases/BatchOperationItemStateEnum.md) - [BatchOperationItemStateExactMatch](type-aliases/BatchOperationItemStateExactMatch.md) - [BatchOperationItemStateFilterProperty](type-aliases/BatchOperationItemStateFilterProperty.md) - [BatchOperationKey](type-aliases/BatchOperationKey.md) - [BatchOperationResponse](type-aliases/BatchOperationResponse.md) - [BatchOperationSearchQuery](type-aliases/BatchOperationSearchQuery.md) - [BatchOperationSearchQueryResult](type-aliases/BatchOperationSearchQueryResult.md) - [BatchOperationSearchQuerySortRequest](type-aliases/BatchOperationSearchQuerySortRequest.md) - [BatchOperationStateEnum](type-aliases/BatchOperationStateEnum.md) - [BatchOperationStateExactMatch](type-aliases/BatchOperationStateExactMatch.md) - [BatchOperationStateFilterProperty](type-aliases/BatchOperationStateFilterProperty.md) - [BatchOperationTypeEnum](type-aliases/BatchOperationTypeEnum.md) - [BatchOperationTypeExactMatch](type-aliases/BatchOperationTypeExactMatch.md) - [BatchOperationTypeFilterProperty](type-aliases/BatchOperationTypeFilterProperty.md) - [BroadcastSignalData](type-aliases/BroadcastSignalData.md) - [BroadcastSignalError](type-aliases/BroadcastSignalError.md) - [BroadcastSignalErrors](type-aliases/BroadcastSignalErrors.md) - [broadcastSignalInput](type-aliases/broadcastSignalInput.md) - [BroadcastSignalResponse](type-aliases/BroadcastSignalResponse.md) - [BroadcastSignalResponses](type-aliases/BroadcastSignalResponses.md) - [BrokerInfo](type-aliases/BrokerInfo.md) - [BusinessId](type-aliases/BusinessId.md) - [CamundaClientLoose](type-aliases/CamundaClientLoose.md) - [CamundaFpClient](type-aliases/CamundaFpClient.md) - [CamundaKey](type-aliases/CamundaKey.md) - [CamundaResultClient](type-aliases/CamundaResultClient.md) - [CamundaUserResult](type-aliases/CamundaUserResult.md) - [CancelBatchOperationData](type-aliases/CancelBatchOperationData.md) - [CancelBatchOperationError](type-aliases/CancelBatchOperationError.md) - [CancelBatchOperationErrors](type-aliases/CancelBatchOperationErrors.md) - [cancelBatchOperationInput](type-aliases/cancelBatchOperationInput.md) - [CancelBatchOperationResponse](type-aliases/CancelBatchOperationResponse.md) - [CancelBatchOperationResponses](type-aliases/CancelBatchOperationResponses.md) - [CancelProcessInstanceData](type-aliases/CancelProcessInstanceData.md) - [CancelProcessInstanceError](type-aliases/CancelProcessInstanceError.md) - [CancelProcessInstanceErrors](type-aliases/CancelProcessInstanceErrors.md) - [cancelProcessInstanceInput](type-aliases/cancelProcessInstanceInput.md) - [CancelProcessInstanceRequest](type-aliases/CancelProcessInstanceRequest.md) - [CancelProcessInstanceResponse](type-aliases/CancelProcessInstanceResponse.md) - [CancelProcessInstanceResponses](type-aliases/CancelProcessInstanceResponses.md) - [CancelProcessInstancesBatchOperationData](type-aliases/CancelProcessInstancesBatchOperationData.md) - [CancelProcessInstancesBatchOperationError](type-aliases/CancelProcessInstancesBatchOperationError.md) - [CancelProcessInstancesBatchOperationErrors](type-aliases/CancelProcessInstancesBatchOperationErrors.md) - [cancelProcessInstancesBatchOperationInput](type-aliases/cancelProcessInstancesBatchOperationInput.md) - [CancelProcessInstancesBatchOperationResponse](type-aliases/CancelProcessInstancesBatchOperationResponse.md) - [CancelProcessInstancesBatchOperationResponses](type-aliases/CancelProcessInstancesBatchOperationResponses.md) - [CategoryExactMatch](type-aliases/CategoryExactMatch.md) - [CategoryFilterProperty](type-aliases/CategoryFilterProperty.md) - [Changeset](type-aliases/Changeset.md) - [ClientId](type-aliases/ClientId.md) - [ClientOptions](type-aliases/ClientOptions.md) - [ClockPinRequest](type-aliases/ClockPinRequest.md) - [CloudConfigurationResponse](type-aliases/CloudConfigurationResponse.md) - [CloudStage](type-aliases/CloudStage.md) - [ClusterVariableName](type-aliases/ClusterVariableName.md) - [ClusterVariableResult](type-aliases/ClusterVariableResult.md) - [ClusterVariableResultBase](type-aliases/ClusterVariableResultBase.md) - [ClusterVariableScopeEnum](type-aliases/ClusterVariableScopeEnum.md) - [ClusterVariableScopeExactMatch](type-aliases/ClusterVariableScopeExactMatch.md) - [ClusterVariableScopeFilterProperty](type-aliases/ClusterVariableScopeFilterProperty.md) - [ClusterVariableSearchQueryFilterRequest](type-aliases/ClusterVariableSearchQueryFilterRequest.md) - [ClusterVariableSearchQueryRequest](type-aliases/ClusterVariableSearchQueryRequest.md) - [ClusterVariableSearchQueryResult](type-aliases/ClusterVariableSearchQueryResult.md) - [ClusterVariableSearchQuerySortRequest](type-aliases/ClusterVariableSearchQuerySortRequest.md) - [ClusterVariableSearchResult](type-aliases/ClusterVariableSearchResult.md) - [CompleteJobData](type-aliases/CompleteJobData.md) - [CompleteJobError](type-aliases/CompleteJobError.md) - [CompleteJobErrors](type-aliases/CompleteJobErrors.md) - [completeJobInput](type-aliases/completeJobInput.md) - [CompleteJobResponse](type-aliases/CompleteJobResponse.md) - [CompleteJobResponses](type-aliases/CompleteJobResponses.md) - [CompleteUserTaskData](type-aliases/CompleteUserTaskData.md) - [CompleteUserTaskError](type-aliases/CompleteUserTaskError.md) - [CompleteUserTaskErrors](type-aliases/CompleteUserTaskErrors.md) - [completeUserTaskInput](type-aliases/completeUserTaskInput.md) - [CompleteUserTaskResponse](type-aliases/CompleteUserTaskResponse.md) - [CompleteUserTaskResponses](type-aliases/CompleteUserTaskResponses.md) - [ComponentsConfigurationResponse](type-aliases/ComponentsConfigurationResponse.md) - [ConditionalEvaluationInstruction](type-aliases/ConditionalEvaluationInstruction.md) - [ConditionalEvaluationKey](type-aliases/ConditionalEvaluationKey.md) - [CorrelatedMessageSubscriptionFilter](type-aliases/CorrelatedMessageSubscriptionFilter.md) - [CorrelatedMessageSubscriptionResult](type-aliases/CorrelatedMessageSubscriptionResult.md) - [CorrelatedMessageSubscriptionSearchQuery](type-aliases/CorrelatedMessageSubscriptionSearchQuery.md) - [CorrelatedMessageSubscriptionSearchQueryResult](type-aliases/CorrelatedMessageSubscriptionSearchQueryResult.md) - [CorrelatedMessageSubscriptionSearchQuerySortRequest](type-aliases/CorrelatedMessageSubscriptionSearchQuerySortRequest.md) - [CorrelateMessageData](type-aliases/CorrelateMessageData.md) - [CorrelateMessageError](type-aliases/CorrelateMessageError.md) - [CorrelateMessageErrors](type-aliases/CorrelateMessageErrors.md) - [correlateMessageInput](type-aliases/correlateMessageInput.md) - [CorrelateMessageResponse](type-aliases/CorrelateMessageResponse.md) - [CorrelateMessageResponses](type-aliases/CorrelateMessageResponses.md) - [CreateAdminUserData](type-aliases/CreateAdminUserData.md) - [CreateAdminUserError](type-aliases/CreateAdminUserError.md) - [CreateAdminUserErrors](type-aliases/CreateAdminUserErrors.md) - [createAdminUserInput](type-aliases/createAdminUserInput.md) - [CreateAdminUserResponse](type-aliases/CreateAdminUserResponse.md) - [CreateAdminUserResponses](type-aliases/CreateAdminUserResponses.md) - [CreateAgentInstanceData](type-aliases/CreateAgentInstanceData.md) - [CreateAgentInstanceError](type-aliases/CreateAgentInstanceError.md) - [CreateAgentInstanceErrors](type-aliases/CreateAgentInstanceErrors.md) - [createAgentInstanceInput](type-aliases/createAgentInstanceInput.md) - [CreateAgentInstanceResponse](type-aliases/CreateAgentInstanceResponse.md) - [CreateAgentInstanceResponses](type-aliases/CreateAgentInstanceResponses.md) - [CreateAuthorizationData](type-aliases/CreateAuthorizationData.md) - [CreateAuthorizationError](type-aliases/CreateAuthorizationError.md) - [CreateAuthorizationErrors](type-aliases/CreateAuthorizationErrors.md) - [createAuthorizationInput](type-aliases/createAuthorizationInput.md) - [CreateAuthorizationResponse](type-aliases/CreateAuthorizationResponse.md) - [CreateAuthorizationResponses](type-aliases/CreateAuthorizationResponses.md) - [CreateClusterVariableRequest](type-aliases/CreateClusterVariableRequest.md) - [CreateDeploymentData](type-aliases/CreateDeploymentData.md) - [CreateDeploymentError](type-aliases/CreateDeploymentError.md) - [CreateDeploymentErrors](type-aliases/CreateDeploymentErrors.md) - [createDeploymentInput](type-aliases/createDeploymentInput.md) - [CreateDeploymentResponse](type-aliases/CreateDeploymentResponse.md) - [CreateDeploymentResponses](type-aliases/CreateDeploymentResponses.md) - [CreateDocumentData](type-aliases/CreateDocumentData.md) - [CreateDocumentError](type-aliases/CreateDocumentError.md) - [CreateDocumentErrors](type-aliases/CreateDocumentErrors.md) - [createDocumentInput](type-aliases/createDocumentInput.md) - [CreateDocumentLinkData](type-aliases/CreateDocumentLinkData.md) - [CreateDocumentLinkError](type-aliases/CreateDocumentLinkError.md) - [CreateDocumentLinkErrors](type-aliases/CreateDocumentLinkErrors.md) - [createDocumentLinkInput](type-aliases/createDocumentLinkInput.md) - [CreateDocumentLinkResponse](type-aliases/CreateDocumentLinkResponse.md) - [CreateDocumentLinkResponses](type-aliases/CreateDocumentLinkResponses.md) - [CreateDocumentResponse](type-aliases/CreateDocumentResponse.md) - [CreateDocumentResponses](type-aliases/CreateDocumentResponses.md) - [CreateDocumentsData](type-aliases/CreateDocumentsData.md) - [CreateDocumentsError](type-aliases/CreateDocumentsError.md) - [CreateDocumentsErrors](type-aliases/CreateDocumentsErrors.md) - [createDocumentsInput](type-aliases/createDocumentsInput.md) - [CreateDocumentsResponse](type-aliases/CreateDocumentsResponse.md) - [CreateDocumentsResponses](type-aliases/CreateDocumentsResponses.md) - [CreateElementInstanceVariablesData](type-aliases/CreateElementInstanceVariablesData.md) - [CreateElementInstanceVariablesError](type-aliases/CreateElementInstanceVariablesError.md) - [CreateElementInstanceVariablesErrors](type-aliases/CreateElementInstanceVariablesErrors.md) - [createElementInstanceVariablesInput](type-aliases/createElementInstanceVariablesInput.md) - [CreateElementInstanceVariablesResponse](type-aliases/CreateElementInstanceVariablesResponse.md) - [CreateElementInstanceVariablesResponses](type-aliases/CreateElementInstanceVariablesResponses.md) - [CreateGlobalClusterVariableData](type-aliases/CreateGlobalClusterVariableData.md) - [CreateGlobalClusterVariableError](type-aliases/CreateGlobalClusterVariableError.md) - [CreateGlobalClusterVariableErrors](type-aliases/CreateGlobalClusterVariableErrors.md) - [createGlobalClusterVariableInput](type-aliases/createGlobalClusterVariableInput.md) - [CreateGlobalClusterVariableResponse](type-aliases/CreateGlobalClusterVariableResponse.md) - [CreateGlobalClusterVariableResponses](type-aliases/CreateGlobalClusterVariableResponses.md) - [CreateGlobalTaskListenerData](type-aliases/CreateGlobalTaskListenerData.md) - [CreateGlobalTaskListenerError](type-aliases/CreateGlobalTaskListenerError.md) - [CreateGlobalTaskListenerErrors](type-aliases/CreateGlobalTaskListenerErrors.md) - [createGlobalTaskListenerInput](type-aliases/createGlobalTaskListenerInput.md) - [CreateGlobalTaskListenerRequest](type-aliases/CreateGlobalTaskListenerRequest.md) - [CreateGlobalTaskListenerResponse](type-aliases/CreateGlobalTaskListenerResponse.md) - [CreateGlobalTaskListenerResponses](type-aliases/CreateGlobalTaskListenerResponses.md) - [CreateGroupData](type-aliases/CreateGroupData.md) - [CreateGroupError](type-aliases/CreateGroupError.md) - [CreateGroupErrors](type-aliases/CreateGroupErrors.md) - [createGroupInput](type-aliases/createGroupInput.md) - [CreateGroupResponse](type-aliases/CreateGroupResponse.md) - [CreateGroupResponses](type-aliases/CreateGroupResponses.md) - [CreateMappingRuleData](type-aliases/CreateMappingRuleData.md) - [CreateMappingRuleError](type-aliases/CreateMappingRuleError.md) - [CreateMappingRuleErrors](type-aliases/CreateMappingRuleErrors.md) - [createMappingRuleInput](type-aliases/createMappingRuleInput.md) - [CreateMappingRuleResponse](type-aliases/CreateMappingRuleResponse.md) - [CreateMappingRuleResponses](type-aliases/CreateMappingRuleResponses.md) - [CreateProcessInstanceData](type-aliases/CreateProcessInstanceData.md) - [CreateProcessInstanceError](type-aliases/CreateProcessInstanceError.md) - [CreateProcessInstanceErrors](type-aliases/CreateProcessInstanceErrors.md) - [createProcessInstanceInput](type-aliases/createProcessInstanceInput.md) - [CreateProcessInstanceResponse](type-aliases/CreateProcessInstanceResponse.md) - [CreateProcessInstanceResponses](type-aliases/CreateProcessInstanceResponses.md) - [CreateProcessInstanceResult](type-aliases/CreateProcessInstanceResult.md) - [CreateRoleData](type-aliases/CreateRoleData.md) - [CreateRoleError](type-aliases/CreateRoleError.md) - [CreateRoleErrors](type-aliases/CreateRoleErrors.md) - [createRoleInput](type-aliases/createRoleInput.md) - [CreateRoleResponse](type-aliases/CreateRoleResponse.md) - [CreateRoleResponses](type-aliases/CreateRoleResponses.md) - [CreateTenantClusterVariableData](type-aliases/CreateTenantClusterVariableData.md) - [CreateTenantClusterVariableError](type-aliases/CreateTenantClusterVariableError.md) - [CreateTenantClusterVariableErrors](type-aliases/CreateTenantClusterVariableErrors.md) - [createTenantClusterVariableInput](type-aliases/createTenantClusterVariableInput.md) - [CreateTenantClusterVariableResponse](type-aliases/CreateTenantClusterVariableResponse.md) - [CreateTenantClusterVariableResponses](type-aliases/CreateTenantClusterVariableResponses.md) - [CreateTenantData](type-aliases/CreateTenantData.md) - [CreateTenantError](type-aliases/CreateTenantError.md) - [CreateTenantErrors](type-aliases/CreateTenantErrors.md) - [createTenantInput](type-aliases/createTenantInput.md) - [CreateTenantResponse](type-aliases/CreateTenantResponse.md) - [CreateTenantResponses](type-aliases/CreateTenantResponses.md) - [CreateUserData](type-aliases/CreateUserData.md) - [CreateUserError](type-aliases/CreateUserError.md) - [CreateUserErrors](type-aliases/CreateUserErrors.md) - [createUserInput](type-aliases/createUserInput.md) - [CreateUserResponse](type-aliases/CreateUserResponse.md) - [CreateUserResponses](type-aliases/CreateUserResponses.md) - [CursorBackwardPagination](type-aliases/CursorBackwardPagination.md) - [CursorForwardPagination](type-aliases/CursorForwardPagination.md) - [DateTimeFilterProperty](type-aliases/DateTimeFilterProperty.md) - [DecisionDefinitionFilter](type-aliases/DecisionDefinitionFilter.md) - [DecisionDefinitionId](type-aliases/DecisionDefinitionId.md) - [DecisionDefinitionKey](type-aliases/DecisionDefinitionKey.md) - [DecisionDefinitionKeyExactMatch](type-aliases/DecisionDefinitionKeyExactMatch.md) - [DecisionDefinitionKeyFilterProperty](type-aliases/DecisionDefinitionKeyFilterProperty.md) - [DecisionDefinitionResult](type-aliases/DecisionDefinitionResult.md) - [DecisionDefinitionSearchQuery](type-aliases/DecisionDefinitionSearchQuery.md) - [DecisionDefinitionSearchQueryResult](type-aliases/DecisionDefinitionSearchQueryResult.md) - [DecisionDefinitionSearchQuerySortRequest](type-aliases/DecisionDefinitionSearchQuerySortRequest.md) - [DecisionDefinitionTypeEnum](type-aliases/DecisionDefinitionTypeEnum.md) - [DecisionEvaluationById](type-aliases/DecisionEvaluationById.md) - [DecisionEvaluationByKey](type-aliases/DecisionEvaluationByKey.md) - [DecisionEvaluationInstanceKey](type-aliases/DecisionEvaluationInstanceKey.md) - [DecisionEvaluationInstanceKeyExactMatch](type-aliases/DecisionEvaluationInstanceKeyExactMatch.md) - [DecisionEvaluationInstanceKeyFilterProperty](type-aliases/DecisionEvaluationInstanceKeyFilterProperty.md) - [DecisionEvaluationInstruction](type-aliases/DecisionEvaluationInstruction.md) - [DecisionEvaluationKey](type-aliases/DecisionEvaluationKey.md) - [DecisionEvaluationKeyExactMatch](type-aliases/DecisionEvaluationKeyExactMatch.md) - [DecisionEvaluationKeyFilterProperty](type-aliases/DecisionEvaluationKeyFilterProperty.md) - [DecisionInstanceDeletionBatchOperationRequest](type-aliases/DecisionInstanceDeletionBatchOperationRequest.md) - [DecisionInstanceFilter](type-aliases/DecisionInstanceFilter.md) - [DecisionInstanceGetQueryResult](type-aliases/DecisionInstanceGetQueryResult.md) - [DecisionInstanceKey](type-aliases/DecisionInstanceKey.md) - [DecisionInstanceResult](type-aliases/DecisionInstanceResult.md) - [DecisionInstanceSearchQuery](type-aliases/DecisionInstanceSearchQuery.md) - [DecisionInstanceSearchQueryResult](type-aliases/DecisionInstanceSearchQueryResult.md) - [DecisionInstanceSearchQuerySortRequest](type-aliases/DecisionInstanceSearchQuerySortRequest.md) - [DecisionInstanceStateEnum](type-aliases/DecisionInstanceStateEnum.md) - [DecisionInstanceStateExactMatch](type-aliases/DecisionInstanceStateExactMatch.md) - [DecisionInstanceStateFilterProperty](type-aliases/DecisionInstanceStateFilterProperty.md) - [DecisionRequirementsFilter](type-aliases/DecisionRequirementsFilter.md) - [DecisionRequirementsKey](type-aliases/DecisionRequirementsKey.md) - [DecisionRequirementsKeyExactMatch](type-aliases/DecisionRequirementsKeyExactMatch.md) - [DecisionRequirementsKeyFilterProperty](type-aliases/DecisionRequirementsKeyFilterProperty.md) - [DecisionRequirementsResult](type-aliases/DecisionRequirementsResult.md) - [DecisionRequirementsSearchQuery](type-aliases/DecisionRequirementsSearchQuery.md) - [DecisionRequirementsSearchQueryResult](type-aliases/DecisionRequirementsSearchQueryResult.md) - [DecisionRequirementsSearchQuerySortRequest](type-aliases/DecisionRequirementsSearchQuerySortRequest.md) - [DeleteAuthorizationData](type-aliases/DeleteAuthorizationData.md) - [DeleteAuthorizationError](type-aliases/DeleteAuthorizationError.md) - [DeleteAuthorizationErrors](type-aliases/DeleteAuthorizationErrors.md) - [deleteAuthorizationInput](type-aliases/deleteAuthorizationInput.md) - [DeleteAuthorizationResponse](type-aliases/DeleteAuthorizationResponse.md) - [DeleteAuthorizationResponses](type-aliases/DeleteAuthorizationResponses.md) - [DeleteDecisionInstanceData](type-aliases/DeleteDecisionInstanceData.md) - [DeleteDecisionInstanceError](type-aliases/DeleteDecisionInstanceError.md) - [DeleteDecisionInstanceErrors](type-aliases/DeleteDecisionInstanceErrors.md) - [deleteDecisionInstanceInput](type-aliases/deleteDecisionInstanceInput.md) - [DeleteDecisionInstanceRequest](type-aliases/DeleteDecisionInstanceRequest.md) - [DeleteDecisionInstanceResponse](type-aliases/DeleteDecisionInstanceResponse.md) - [DeleteDecisionInstanceResponses](type-aliases/DeleteDecisionInstanceResponses.md) - [DeleteDecisionInstancesBatchOperationData](type-aliases/DeleteDecisionInstancesBatchOperationData.md) - [DeleteDecisionInstancesBatchOperationError](type-aliases/DeleteDecisionInstancesBatchOperationError.md) - [DeleteDecisionInstancesBatchOperationErrors](type-aliases/DeleteDecisionInstancesBatchOperationErrors.md) - [deleteDecisionInstancesBatchOperationInput](type-aliases/deleteDecisionInstancesBatchOperationInput.md) - [DeleteDecisionInstancesBatchOperationResponse](type-aliases/DeleteDecisionInstancesBatchOperationResponse.md) - [DeleteDecisionInstancesBatchOperationResponses](type-aliases/DeleteDecisionInstancesBatchOperationResponses.md) - [DeleteDocumentData](type-aliases/DeleteDocumentData.md) - [DeleteDocumentError](type-aliases/DeleteDocumentError.md) - [DeleteDocumentErrors](type-aliases/DeleteDocumentErrors.md) - [deleteDocumentInput](type-aliases/deleteDocumentInput.md) - [DeleteDocumentResponse](type-aliases/DeleteDocumentResponse.md) - [DeleteDocumentResponses](type-aliases/DeleteDocumentResponses.md) - [DeleteGlobalClusterVariableData](type-aliases/DeleteGlobalClusterVariableData.md) - [DeleteGlobalClusterVariableError](type-aliases/DeleteGlobalClusterVariableError.md) - [DeleteGlobalClusterVariableErrors](type-aliases/DeleteGlobalClusterVariableErrors.md) - [deleteGlobalClusterVariableInput](type-aliases/deleteGlobalClusterVariableInput.md) - [DeleteGlobalClusterVariableResponse](type-aliases/DeleteGlobalClusterVariableResponse.md) - [DeleteGlobalClusterVariableResponses](type-aliases/DeleteGlobalClusterVariableResponses.md) - [DeleteGlobalTaskListenerData](type-aliases/DeleteGlobalTaskListenerData.md) - [DeleteGlobalTaskListenerError](type-aliases/DeleteGlobalTaskListenerError.md) - [DeleteGlobalTaskListenerErrors](type-aliases/DeleteGlobalTaskListenerErrors.md) - [deleteGlobalTaskListenerInput](type-aliases/deleteGlobalTaskListenerInput.md) - [DeleteGlobalTaskListenerResponse](type-aliases/DeleteGlobalTaskListenerResponse.md) - [DeleteGlobalTaskListenerResponses](type-aliases/DeleteGlobalTaskListenerResponses.md) - [DeleteGroupData](type-aliases/DeleteGroupData.md) - [DeleteGroupError](type-aliases/DeleteGroupError.md) - [DeleteGroupErrors](type-aliases/DeleteGroupErrors.md) - [deleteGroupInput](type-aliases/deleteGroupInput.md) - [DeleteGroupResponse](type-aliases/DeleteGroupResponse.md) - [DeleteGroupResponses](type-aliases/DeleteGroupResponses.md) - [DeleteMappingRuleData](type-aliases/DeleteMappingRuleData.md) - [DeleteMappingRuleError](type-aliases/DeleteMappingRuleError.md) - [DeleteMappingRuleErrors](type-aliases/DeleteMappingRuleErrors.md) - [deleteMappingRuleInput](type-aliases/deleteMappingRuleInput.md) - [DeleteMappingRuleResponse](type-aliases/DeleteMappingRuleResponse.md) - [DeleteMappingRuleResponses](type-aliases/DeleteMappingRuleResponses.md) - [DeleteProcessInstanceData](type-aliases/DeleteProcessInstanceData.md) - [DeleteProcessInstanceError](type-aliases/DeleteProcessInstanceError.md) - [DeleteProcessInstanceErrors](type-aliases/DeleteProcessInstanceErrors.md) - [deleteProcessInstanceInput](type-aliases/deleteProcessInstanceInput.md) - [DeleteProcessInstanceRequest](type-aliases/DeleteProcessInstanceRequest.md) - [DeleteProcessInstanceResponse](type-aliases/DeleteProcessInstanceResponse.md) - [DeleteProcessInstanceResponses](type-aliases/DeleteProcessInstanceResponses.md) - [DeleteProcessInstancesBatchOperationData](type-aliases/DeleteProcessInstancesBatchOperationData.md) - [DeleteProcessInstancesBatchOperationError](type-aliases/DeleteProcessInstancesBatchOperationError.md) - [DeleteProcessInstancesBatchOperationErrors](type-aliases/DeleteProcessInstancesBatchOperationErrors.md) - [deleteProcessInstancesBatchOperationInput](type-aliases/deleteProcessInstancesBatchOperationInput.md) - [DeleteProcessInstancesBatchOperationResponse](type-aliases/DeleteProcessInstancesBatchOperationResponse.md) - [DeleteProcessInstancesBatchOperationResponses](type-aliases/DeleteProcessInstancesBatchOperationResponses.md) - [DeleteResourceData](type-aliases/DeleteResourceData.md) - [DeleteResourceError](type-aliases/DeleteResourceError.md) - [DeleteResourceErrors](type-aliases/DeleteResourceErrors.md) - [deleteResourceInput](type-aliases/deleteResourceInput.md) - [DeleteResourceRequest](type-aliases/DeleteResourceRequest.md) - [DeleteResourceResponse](type-aliases/DeleteResourceResponse.md) - [DeleteResourceResponse2](type-aliases/DeleteResourceResponse2.md) - [DeleteResourceResponses](type-aliases/DeleteResourceResponses.md) - [DeleteRoleData](type-aliases/DeleteRoleData.md) - [DeleteRoleError](type-aliases/DeleteRoleError.md) - [DeleteRoleErrors](type-aliases/DeleteRoleErrors.md) - [deleteRoleInput](type-aliases/deleteRoleInput.md) - [DeleteRoleResponse](type-aliases/DeleteRoleResponse.md) - [DeleteRoleResponses](type-aliases/DeleteRoleResponses.md) - [DeleteTenantClusterVariableData](type-aliases/DeleteTenantClusterVariableData.md) - [DeleteTenantClusterVariableError](type-aliases/DeleteTenantClusterVariableError.md) - [DeleteTenantClusterVariableErrors](type-aliases/DeleteTenantClusterVariableErrors.md) - [deleteTenantClusterVariableInput](type-aliases/deleteTenantClusterVariableInput.md) - [DeleteTenantClusterVariableResponse](type-aliases/DeleteTenantClusterVariableResponse.md) - [DeleteTenantClusterVariableResponses](type-aliases/DeleteTenantClusterVariableResponses.md) - [DeleteTenantData](type-aliases/DeleteTenantData.md) - [DeleteTenantError](type-aliases/DeleteTenantError.md) - [DeleteTenantErrors](type-aliases/DeleteTenantErrors.md) - [deleteTenantInput](type-aliases/deleteTenantInput.md) - [DeleteTenantResponse](type-aliases/DeleteTenantResponse.md) - [DeleteTenantResponses](type-aliases/DeleteTenantResponses.md) - [DeleteUserData](type-aliases/DeleteUserData.md) - [DeleteUserError](type-aliases/DeleteUserError.md) - [DeleteUserErrors](type-aliases/DeleteUserErrors.md) - [deleteUserInput](type-aliases/deleteUserInput.md) - [DeleteUserResponse](type-aliases/DeleteUserResponse.md) - [DeleteUserResponses](type-aliases/DeleteUserResponses.md) - [DeploymentConfigurationResponse](type-aliases/DeploymentConfigurationResponse.md) - [DeploymentDecisionRequirementsResult](type-aliases/DeploymentDecisionRequirementsResult.md) - [DeploymentDecisionResult](type-aliases/DeploymentDecisionResult.md) - [DeploymentFormResult](type-aliases/DeploymentFormResult.md) - [DeploymentKey](type-aliases/DeploymentKey.md) - [DeploymentKeyExactMatch](type-aliases/DeploymentKeyExactMatch.md) - [DeploymentKeyFilterProperty](type-aliases/DeploymentKeyFilterProperty.md) - [DeploymentMetadataResult](type-aliases/DeploymentMetadataResult.md) - [DeploymentProcessResult](type-aliases/DeploymentProcessResult.md) - [DeploymentResourceResult](type-aliases/DeploymentResourceResult.md) - [DeploymentResult](type-aliases/DeploymentResult.md) - [DirectAncestorKeyInstruction](type-aliases/DirectAncestorKeyInstruction.md) - [DocumentCreationBatchResponse](type-aliases/DocumentCreationBatchResponse.md) - [DocumentCreationFailureDetail](type-aliases/DocumentCreationFailureDetail.md) - [DocumentId](type-aliases/DocumentId.md) - [DocumentLink](type-aliases/DocumentLink.md) - [DocumentLinkRequest](type-aliases/DocumentLinkRequest.md) - [DocumentMetadata](type-aliases/DocumentMetadata.md) - [DocumentMetadataResponse](type-aliases/DocumentMetadataResponse.md) - [DocumentReference](type-aliases/DocumentReference.md) - [Either](type-aliases/Either.md) - [ElementId](type-aliases/ElementId.md) - [ElementIdExactMatch](type-aliases/ElementIdExactMatch.md) - [ElementIdFilterProperty](type-aliases/ElementIdFilterProperty.md) - [ElementInstanceFilter](type-aliases/ElementInstanceFilter.md) - [ElementInstanceKey](type-aliases/ElementInstanceKey.md) - [ElementInstanceKeyExactMatch](type-aliases/ElementInstanceKeyExactMatch.md) - [ElementInstanceKeyFilterProperty](type-aliases/ElementInstanceKeyFilterProperty.md) - [ElementInstanceResult](type-aliases/ElementInstanceResult.md) - [ElementInstanceSearchQuery](type-aliases/ElementInstanceSearchQuery.md) - [ElementInstanceSearchQueryResult](type-aliases/ElementInstanceSearchQueryResult.md) - [ElementInstanceSearchQuerySortRequest](type-aliases/ElementInstanceSearchQuerySortRequest.md) - [ElementInstanceStateEnum](type-aliases/ElementInstanceStateEnum.md) - [ElementInstanceStateExactMatch](type-aliases/ElementInstanceStateExactMatch.md) - [ElementInstanceStateFilterProperty](type-aliases/ElementInstanceStateFilterProperty.md) - [EndCursor](type-aliases/EndCursor.md) - [EntityTypeExactMatch](type-aliases/EntityTypeExactMatch.md) - [EntityTypeFilterProperty](type-aliases/EntityTypeFilterProperty.md) - [EvaluateConditionalResult](type-aliases/EvaluateConditionalResult.md) - [EvaluateConditionalsData](type-aliases/EvaluateConditionalsData.md) - [EvaluateConditionalsError](type-aliases/EvaluateConditionalsError.md) - [EvaluateConditionalsErrors](type-aliases/EvaluateConditionalsErrors.md) - [evaluateConditionalsInput](type-aliases/evaluateConditionalsInput.md) - [EvaluateConditionalsResponse](type-aliases/EvaluateConditionalsResponse.md) - [EvaluateConditionalsResponses](type-aliases/EvaluateConditionalsResponses.md) - [EvaluatedDecisionInputItem](type-aliases/EvaluatedDecisionInputItem.md) - [EvaluatedDecisionOutputItem](type-aliases/EvaluatedDecisionOutputItem.md) - [EvaluatedDecisionResult](type-aliases/EvaluatedDecisionResult.md) - [EvaluateDecisionData](type-aliases/EvaluateDecisionData.md) - [EvaluateDecisionError](type-aliases/EvaluateDecisionError.md) - [EvaluateDecisionErrors](type-aliases/EvaluateDecisionErrors.md) - [evaluateDecisionInput](type-aliases/evaluateDecisionInput.md) - [EvaluateDecisionResponse](type-aliases/EvaluateDecisionResponse.md) - [EvaluateDecisionResponses](type-aliases/EvaluateDecisionResponses.md) - [EvaluateDecisionResult](type-aliases/EvaluateDecisionResult.md) - [EvaluateExpressionData](type-aliases/EvaluateExpressionData.md) - [EvaluateExpressionError](type-aliases/EvaluateExpressionError.md) - [EvaluateExpressionErrors](type-aliases/EvaluateExpressionErrors.md) - [evaluateExpressionInput](type-aliases/evaluateExpressionInput.md) - [EvaluateExpressionResponse](type-aliases/EvaluateExpressionResponse.md) - [EvaluateExpressionResponses](type-aliases/EvaluateExpressionResponses.md) - [ExpressionEvaluationRequest](type-aliases/ExpressionEvaluationRequest.md) - [ExpressionEvaluationResult](type-aliases/ExpressionEvaluationResult.md) - [ExpressionEvaluationWarningItem](type-aliases/ExpressionEvaluationWarningItem.md) - [FailJobData](type-aliases/FailJobData.md) - [FailJobError](type-aliases/FailJobError.md) - [FailJobErrors](type-aliases/FailJobErrors.md) - [failJobInput](type-aliases/failJobInput.md) - [FailJobResponse](type-aliases/FailJobResponse.md) - [FailJobResponses](type-aliases/FailJobResponses.md) - [FormId](type-aliases/FormId.md) - [FormKey](type-aliases/FormKey.md) - [FormKeyExactMatch](type-aliases/FormKeyExactMatch.md) - [FormKeyFilterProperty](type-aliases/FormKeyFilterProperty.md) - [FormResult](type-aliases/FormResult.md) - [getAgentInstanceConsistency](type-aliases/getAgentInstanceConsistency.md) - [GetAgentInstanceData](type-aliases/GetAgentInstanceData.md) - [GetAgentInstanceError](type-aliases/GetAgentInstanceError.md) - [GetAgentInstanceErrors](type-aliases/GetAgentInstanceErrors.md) - [getAgentInstanceInput](type-aliases/getAgentInstanceInput.md) - [GetAgentInstanceResponse](type-aliases/GetAgentInstanceResponse.md) - [GetAgentInstanceResponses](type-aliases/GetAgentInstanceResponses.md) - [getAuditLogConsistency](type-aliases/getAuditLogConsistency.md) - [GetAuditLogData](type-aliases/GetAuditLogData.md) - [GetAuditLogError](type-aliases/GetAuditLogError.md) - [GetAuditLogErrors](type-aliases/GetAuditLogErrors.md) - [getAuditLogInput](type-aliases/getAuditLogInput.md) - [GetAuditLogResponse](type-aliases/GetAuditLogResponse.md) - [GetAuditLogResponses](type-aliases/GetAuditLogResponses.md) - [GetAuthenticationData](type-aliases/GetAuthenticationData.md) - [GetAuthenticationError](type-aliases/GetAuthenticationError.md) - [GetAuthenticationErrors](type-aliases/GetAuthenticationErrors.md) - [getAuthenticationInput](type-aliases/getAuthenticationInput.md) - [GetAuthenticationResponse](type-aliases/GetAuthenticationResponse.md) - [GetAuthenticationResponses](type-aliases/GetAuthenticationResponses.md) - [getAuthorizationConsistency](type-aliases/getAuthorizationConsistency.md) - [GetAuthorizationData](type-aliases/GetAuthorizationData.md) - [GetAuthorizationError](type-aliases/GetAuthorizationError.md) - [GetAuthorizationErrors](type-aliases/GetAuthorizationErrors.md) - [getAuthorizationInput](type-aliases/getAuthorizationInput.md) - [GetAuthorizationResponse](type-aliases/GetAuthorizationResponse.md) - [GetAuthorizationResponses](type-aliases/GetAuthorizationResponses.md) - [getBatchOperationConsistency](type-aliases/getBatchOperationConsistency.md) - [GetBatchOperationData](type-aliases/GetBatchOperationData.md) - [GetBatchOperationError](type-aliases/GetBatchOperationError.md) - [GetBatchOperationErrors](type-aliases/GetBatchOperationErrors.md) - [getBatchOperationInput](type-aliases/getBatchOperationInput.md) - [GetBatchOperationResponse](type-aliases/GetBatchOperationResponse.md) - [GetBatchOperationResponses](type-aliases/GetBatchOperationResponses.md) - [getDecisionDefinitionConsistency](type-aliases/getDecisionDefinitionConsistency.md) - [GetDecisionDefinitionData](type-aliases/GetDecisionDefinitionData.md) - [GetDecisionDefinitionError](type-aliases/GetDecisionDefinitionError.md) - [GetDecisionDefinitionErrors](type-aliases/GetDecisionDefinitionErrors.md) - [getDecisionDefinitionInput](type-aliases/getDecisionDefinitionInput.md) - [GetDecisionDefinitionResponse](type-aliases/GetDecisionDefinitionResponse.md) - [GetDecisionDefinitionResponses](type-aliases/GetDecisionDefinitionResponses.md) - [getDecisionDefinitionXmlConsistency](type-aliases/getDecisionDefinitionXmlConsistency.md) - [GetDecisionDefinitionXmlData](type-aliases/GetDecisionDefinitionXmlData.md) - [GetDecisionDefinitionXmlError](type-aliases/GetDecisionDefinitionXmlError.md) - [GetDecisionDefinitionXmlErrors](type-aliases/GetDecisionDefinitionXmlErrors.md) - [getDecisionDefinitionXmlInput](type-aliases/getDecisionDefinitionXmlInput.md) - [GetDecisionDefinitionXmlResponse](type-aliases/GetDecisionDefinitionXmlResponse.md) - [GetDecisionDefinitionXmlResponses](type-aliases/GetDecisionDefinitionXmlResponses.md) - [getDecisionInstanceConsistency](type-aliases/getDecisionInstanceConsistency.md) - [GetDecisionInstanceData](type-aliases/GetDecisionInstanceData.md) - [GetDecisionInstanceError](type-aliases/GetDecisionInstanceError.md) - [GetDecisionInstanceErrors](type-aliases/GetDecisionInstanceErrors.md) - [getDecisionInstanceInput](type-aliases/getDecisionInstanceInput.md) - [GetDecisionInstanceResponse](type-aliases/GetDecisionInstanceResponse.md) - [GetDecisionInstanceResponses](type-aliases/GetDecisionInstanceResponses.md) - [getDecisionRequirementsConsistency](type-aliases/getDecisionRequirementsConsistency.md) - [GetDecisionRequirementsData](type-aliases/GetDecisionRequirementsData.md) - [GetDecisionRequirementsError](type-aliases/GetDecisionRequirementsError.md) - [GetDecisionRequirementsErrors](type-aliases/GetDecisionRequirementsErrors.md) - [getDecisionRequirementsInput](type-aliases/getDecisionRequirementsInput.md) - [GetDecisionRequirementsResponse](type-aliases/GetDecisionRequirementsResponse.md) - [GetDecisionRequirementsResponses](type-aliases/GetDecisionRequirementsResponses.md) - [getDecisionRequirementsXmlConsistency](type-aliases/getDecisionRequirementsXmlConsistency.md) - [GetDecisionRequirementsXmlData](type-aliases/GetDecisionRequirementsXmlData.md) - [GetDecisionRequirementsXmlError](type-aliases/GetDecisionRequirementsXmlError.md) - [GetDecisionRequirementsXmlErrors](type-aliases/GetDecisionRequirementsXmlErrors.md) - [getDecisionRequirementsXmlInput](type-aliases/getDecisionRequirementsXmlInput.md) - [GetDecisionRequirementsXmlResponse](type-aliases/GetDecisionRequirementsXmlResponse.md) - [GetDecisionRequirementsXmlResponses](type-aliases/GetDecisionRequirementsXmlResponses.md) - [GetDocumentData](type-aliases/GetDocumentData.md) - [GetDocumentError](type-aliases/GetDocumentError.md) - [GetDocumentErrors](type-aliases/GetDocumentErrors.md) - [getDocumentInput](type-aliases/getDocumentInput.md) - [GetDocumentResponse](type-aliases/GetDocumentResponse.md) - [GetDocumentResponses](type-aliases/GetDocumentResponses.md) - [getElementInstanceConsistency](type-aliases/getElementInstanceConsistency.md) - [GetElementInstanceData](type-aliases/GetElementInstanceData.md) - [GetElementInstanceError](type-aliases/GetElementInstanceError.md) - [GetElementInstanceErrors](type-aliases/GetElementInstanceErrors.md) - [getElementInstanceInput](type-aliases/getElementInstanceInput.md) - [GetElementInstanceResponse](type-aliases/GetElementInstanceResponse.md) - [GetElementInstanceResponses](type-aliases/GetElementInstanceResponses.md) - [getFormByKeyConsistency](type-aliases/getFormByKeyConsistency.md) - [GetFormByKeyData](type-aliases/GetFormByKeyData.md) - [GetFormByKeyError](type-aliases/GetFormByKeyError.md) - [GetFormByKeyErrors](type-aliases/GetFormByKeyErrors.md) - [getFormByKeyInput](type-aliases/getFormByKeyInput.md) - [GetFormByKeyResponse](type-aliases/GetFormByKeyResponse.md) - [GetFormByKeyResponses](type-aliases/GetFormByKeyResponses.md) - [getGlobalClusterVariableConsistency](type-aliases/getGlobalClusterVariableConsistency.md) - [GetGlobalClusterVariableData](type-aliases/GetGlobalClusterVariableData.md) - [GetGlobalClusterVariableError](type-aliases/GetGlobalClusterVariableError.md) - [GetGlobalClusterVariableErrors](type-aliases/GetGlobalClusterVariableErrors.md) - [getGlobalClusterVariableInput](type-aliases/getGlobalClusterVariableInput.md) - [GetGlobalClusterVariableResponse](type-aliases/GetGlobalClusterVariableResponse.md) - [GetGlobalClusterVariableResponses](type-aliases/GetGlobalClusterVariableResponses.md) - [getGlobalJobStatisticsConsistency](type-aliases/getGlobalJobStatisticsConsistency.md) - [GetGlobalJobStatisticsData](type-aliases/GetGlobalJobStatisticsData.md) - [GetGlobalJobStatisticsError](type-aliases/GetGlobalJobStatisticsError.md) - [GetGlobalJobStatisticsErrors](type-aliases/GetGlobalJobStatisticsErrors.md) - [getGlobalJobStatisticsInput](type-aliases/getGlobalJobStatisticsInput.md) - [GetGlobalJobStatisticsResponse](type-aliases/GetGlobalJobStatisticsResponse.md) - [GetGlobalJobStatisticsResponses](type-aliases/GetGlobalJobStatisticsResponses.md) - [getGlobalTaskListenerConsistency](type-aliases/getGlobalTaskListenerConsistency.md) - [GetGlobalTaskListenerData](type-aliases/GetGlobalTaskListenerData.md) - [GetGlobalTaskListenerError](type-aliases/GetGlobalTaskListenerError.md) - [GetGlobalTaskListenerErrors](type-aliases/GetGlobalTaskListenerErrors.md) - [getGlobalTaskListenerInput](type-aliases/getGlobalTaskListenerInput.md) - [GetGlobalTaskListenerResponse](type-aliases/GetGlobalTaskListenerResponse.md) - [GetGlobalTaskListenerResponses](type-aliases/GetGlobalTaskListenerResponses.md) - [getGroupConsistency](type-aliases/getGroupConsistency.md) - [GetGroupData](type-aliases/GetGroupData.md) - [GetGroupError](type-aliases/GetGroupError.md) - [GetGroupErrors](type-aliases/GetGroupErrors.md) - [getGroupInput](type-aliases/getGroupInput.md) - [GetGroupResponse](type-aliases/GetGroupResponse.md) - [GetGroupResponses](type-aliases/GetGroupResponses.md) - [getIncidentConsistency](type-aliases/getIncidentConsistency.md) - [GetIncidentData](type-aliases/GetIncidentData.md) - [GetIncidentError](type-aliases/GetIncidentError.md) - [GetIncidentErrors](type-aliases/GetIncidentErrors.md) - [getIncidentInput](type-aliases/getIncidentInput.md) - [GetIncidentResponse](type-aliases/GetIncidentResponse.md) - [GetIncidentResponses](type-aliases/GetIncidentResponses.md) - [getJobErrorStatisticsConsistency](type-aliases/getJobErrorStatisticsConsistency.md) - [GetJobErrorStatisticsData](type-aliases/GetJobErrorStatisticsData.md) - [GetJobErrorStatisticsError](type-aliases/GetJobErrorStatisticsError.md) - [GetJobErrorStatisticsErrors](type-aliases/GetJobErrorStatisticsErrors.md) - [getJobErrorStatisticsInput](type-aliases/getJobErrorStatisticsInput.md) - [GetJobErrorStatisticsResponse](type-aliases/GetJobErrorStatisticsResponse.md) - [GetJobErrorStatisticsResponses](type-aliases/GetJobErrorStatisticsResponses.md) - [getJobTimeSeriesStatisticsConsistency](type-aliases/getJobTimeSeriesStatisticsConsistency.md) - [GetJobTimeSeriesStatisticsData](type-aliases/GetJobTimeSeriesStatisticsData.md) - [GetJobTimeSeriesStatisticsError](type-aliases/GetJobTimeSeriesStatisticsError.md) - [GetJobTimeSeriesStatisticsErrors](type-aliases/GetJobTimeSeriesStatisticsErrors.md) - [getJobTimeSeriesStatisticsInput](type-aliases/getJobTimeSeriesStatisticsInput.md) - [GetJobTimeSeriesStatisticsResponse](type-aliases/GetJobTimeSeriesStatisticsResponse.md) - [GetJobTimeSeriesStatisticsResponses](type-aliases/GetJobTimeSeriesStatisticsResponses.md) - [getJobTypeStatisticsConsistency](type-aliases/getJobTypeStatisticsConsistency.md) - [GetJobTypeStatisticsData](type-aliases/GetJobTypeStatisticsData.md) - [GetJobTypeStatisticsError](type-aliases/GetJobTypeStatisticsError.md) - [GetJobTypeStatisticsErrors](type-aliases/GetJobTypeStatisticsErrors.md) - [getJobTypeStatisticsInput](type-aliases/getJobTypeStatisticsInput.md) - [GetJobTypeStatisticsResponse](type-aliases/GetJobTypeStatisticsResponse.md) - [GetJobTypeStatisticsResponses](type-aliases/GetJobTypeStatisticsResponses.md) - [getJobWorkerStatisticsConsistency](type-aliases/getJobWorkerStatisticsConsistency.md) - [GetJobWorkerStatisticsData](type-aliases/GetJobWorkerStatisticsData.md) - [GetJobWorkerStatisticsError](type-aliases/GetJobWorkerStatisticsError.md) - [GetJobWorkerStatisticsErrors](type-aliases/GetJobWorkerStatisticsErrors.md) - [getJobWorkerStatisticsInput](type-aliases/getJobWorkerStatisticsInput.md) - [GetJobWorkerStatisticsResponse](type-aliases/GetJobWorkerStatisticsResponse.md) - [GetJobWorkerStatisticsResponses](type-aliases/GetJobWorkerStatisticsResponses.md) - [GetLicenseData](type-aliases/GetLicenseData.md) - [GetLicenseError](type-aliases/GetLicenseError.md) - [GetLicenseErrors](type-aliases/GetLicenseErrors.md) - [getLicenseInput](type-aliases/getLicenseInput.md) - [GetLicenseResponse](type-aliases/GetLicenseResponse.md) - [GetLicenseResponses](type-aliases/GetLicenseResponses.md) - [getMappingRuleConsistency](type-aliases/getMappingRuleConsistency.md) - [GetMappingRuleData](type-aliases/GetMappingRuleData.md) - [GetMappingRuleError](type-aliases/GetMappingRuleError.md) - [GetMappingRuleErrors](type-aliases/GetMappingRuleErrors.md) - [getMappingRuleInput](type-aliases/getMappingRuleInput.md) - [GetMappingRuleResponse](type-aliases/GetMappingRuleResponse.md) - [GetMappingRuleResponses](type-aliases/GetMappingRuleResponses.md) - [getProcessDefinitionConsistency](type-aliases/getProcessDefinitionConsistency.md) - [GetProcessDefinitionData](type-aliases/GetProcessDefinitionData.md) - [GetProcessDefinitionError](type-aliases/GetProcessDefinitionError.md) - [GetProcessDefinitionErrors](type-aliases/GetProcessDefinitionErrors.md) - [getProcessDefinitionInput](type-aliases/getProcessDefinitionInput.md) - [getProcessDefinitionInstanceStatisticsConsistency](type-aliases/getProcessDefinitionInstanceStatisticsConsistency.md) - [GetProcessDefinitionInstanceStatisticsData](type-aliases/GetProcessDefinitionInstanceStatisticsData.md) - [GetProcessDefinitionInstanceStatisticsError](type-aliases/GetProcessDefinitionInstanceStatisticsError.md) - [GetProcessDefinitionInstanceStatisticsErrors](type-aliases/GetProcessDefinitionInstanceStatisticsErrors.md) - [getProcessDefinitionInstanceStatisticsInput](type-aliases/getProcessDefinitionInstanceStatisticsInput.md) - [GetProcessDefinitionInstanceStatisticsResponse](type-aliases/GetProcessDefinitionInstanceStatisticsResponse.md) - [GetProcessDefinitionInstanceStatisticsResponses](type-aliases/GetProcessDefinitionInstanceStatisticsResponses.md) - [getProcessDefinitionInstanceVersionStatisticsConsistency](type-aliases/getProcessDefinitionInstanceVersionStatisticsConsistency.md) - [GetProcessDefinitionInstanceVersionStatisticsData](type-aliases/GetProcessDefinitionInstanceVersionStatisticsData.md) - [GetProcessDefinitionInstanceVersionStatisticsError](type-aliases/GetProcessDefinitionInstanceVersionStatisticsError.md) - [GetProcessDefinitionInstanceVersionStatisticsErrors](type-aliases/GetProcessDefinitionInstanceVersionStatisticsErrors.md) - [getProcessDefinitionInstanceVersionStatisticsInput](type-aliases/getProcessDefinitionInstanceVersionStatisticsInput.md) - [GetProcessDefinitionInstanceVersionStatisticsResponse](type-aliases/GetProcessDefinitionInstanceVersionStatisticsResponse.md) - [GetProcessDefinitionInstanceVersionStatisticsResponses](type-aliases/GetProcessDefinitionInstanceVersionStatisticsResponses.md) - [getProcessDefinitionMessageSubscriptionStatisticsConsistency](type-aliases/getProcessDefinitionMessageSubscriptionStatisticsConsistency.md) - [GetProcessDefinitionMessageSubscriptionStatisticsData](type-aliases/GetProcessDefinitionMessageSubscriptionStatisticsData.md) - [GetProcessDefinitionMessageSubscriptionStatisticsError](type-aliases/GetProcessDefinitionMessageSubscriptionStatisticsError.md) - [GetProcessDefinitionMessageSubscriptionStatisticsErrors](type-aliases/GetProcessDefinitionMessageSubscriptionStatisticsErrors.md) - [getProcessDefinitionMessageSubscriptionStatisticsInput](type-aliases/getProcessDefinitionMessageSubscriptionStatisticsInput.md) - [GetProcessDefinitionMessageSubscriptionStatisticsResponse](type-aliases/GetProcessDefinitionMessageSubscriptionStatisticsResponse.md) - [GetProcessDefinitionMessageSubscriptionStatisticsResponses](type-aliases/GetProcessDefinitionMessageSubscriptionStatisticsResponses.md) - [GetProcessDefinitionResponse](type-aliases/GetProcessDefinitionResponse.md) - [GetProcessDefinitionResponses](type-aliases/GetProcessDefinitionResponses.md) - [getProcessDefinitionStatisticsConsistency](type-aliases/getProcessDefinitionStatisticsConsistency.md) - [GetProcessDefinitionStatisticsData](type-aliases/GetProcessDefinitionStatisticsData.md) - [GetProcessDefinitionStatisticsError](type-aliases/GetProcessDefinitionStatisticsError.md) - [GetProcessDefinitionStatisticsErrors](type-aliases/GetProcessDefinitionStatisticsErrors.md) - [getProcessDefinitionStatisticsInput](type-aliases/getProcessDefinitionStatisticsInput.md) - [GetProcessDefinitionStatisticsResponse](type-aliases/GetProcessDefinitionStatisticsResponse.md) - [GetProcessDefinitionStatisticsResponses](type-aliases/GetProcessDefinitionStatisticsResponses.md) - [getProcessDefinitionXmlConsistency](type-aliases/getProcessDefinitionXmlConsistency.md) - [GetProcessDefinitionXmlData](type-aliases/GetProcessDefinitionXmlData.md) - [GetProcessDefinitionXmlError](type-aliases/GetProcessDefinitionXmlError.md) - [GetProcessDefinitionXmlErrors](type-aliases/GetProcessDefinitionXmlErrors.md) - [getProcessDefinitionXmlInput](type-aliases/getProcessDefinitionXmlInput.md) - [GetProcessDefinitionXmlResponse](type-aliases/GetProcessDefinitionXmlResponse.md) - [GetProcessDefinitionXmlResponses](type-aliases/GetProcessDefinitionXmlResponses.md) - [getProcessInstanceCallHierarchyConsistency](type-aliases/getProcessInstanceCallHierarchyConsistency.md) - [GetProcessInstanceCallHierarchyData](type-aliases/GetProcessInstanceCallHierarchyData.md) - [GetProcessInstanceCallHierarchyError](type-aliases/GetProcessInstanceCallHierarchyError.md) - [GetProcessInstanceCallHierarchyErrors](type-aliases/GetProcessInstanceCallHierarchyErrors.md) - [getProcessInstanceCallHierarchyInput](type-aliases/getProcessInstanceCallHierarchyInput.md) - [GetProcessInstanceCallHierarchyResponse](type-aliases/GetProcessInstanceCallHierarchyResponse.md) - [GetProcessInstanceCallHierarchyResponses](type-aliases/GetProcessInstanceCallHierarchyResponses.md) - [getProcessInstanceConsistency](type-aliases/getProcessInstanceConsistency.md) - [GetProcessInstanceData](type-aliases/GetProcessInstanceData.md) - [GetProcessInstanceError](type-aliases/GetProcessInstanceError.md) - [GetProcessInstanceErrors](type-aliases/GetProcessInstanceErrors.md) - [getProcessInstanceInput](type-aliases/getProcessInstanceInput.md) - [GetProcessInstanceResponse](type-aliases/GetProcessInstanceResponse.md) - [GetProcessInstanceResponses](type-aliases/GetProcessInstanceResponses.md) - [getProcessInstanceSequenceFlowsConsistency](type-aliases/getProcessInstanceSequenceFlowsConsistency.md) - [GetProcessInstanceSequenceFlowsData](type-aliases/GetProcessInstanceSequenceFlowsData.md) - [GetProcessInstanceSequenceFlowsError](type-aliases/GetProcessInstanceSequenceFlowsError.md) - [GetProcessInstanceSequenceFlowsErrors](type-aliases/GetProcessInstanceSequenceFlowsErrors.md) - [getProcessInstanceSequenceFlowsInput](type-aliases/getProcessInstanceSequenceFlowsInput.md) - [GetProcessInstanceSequenceFlowsResponse](type-aliases/GetProcessInstanceSequenceFlowsResponse.md) - [GetProcessInstanceSequenceFlowsResponses](type-aliases/GetProcessInstanceSequenceFlowsResponses.md) - [getProcessInstanceStatisticsByDefinitionConsistency](type-aliases/getProcessInstanceStatisticsByDefinitionConsistency.md) - [GetProcessInstanceStatisticsByDefinitionData](type-aliases/GetProcessInstanceStatisticsByDefinitionData.md) - [GetProcessInstanceStatisticsByDefinitionError](type-aliases/GetProcessInstanceStatisticsByDefinitionError.md) - [GetProcessInstanceStatisticsByDefinitionErrors](type-aliases/GetProcessInstanceStatisticsByDefinitionErrors.md) - [getProcessInstanceStatisticsByDefinitionInput](type-aliases/getProcessInstanceStatisticsByDefinitionInput.md) - [GetProcessInstanceStatisticsByDefinitionResponse](type-aliases/GetProcessInstanceStatisticsByDefinitionResponse.md) - [GetProcessInstanceStatisticsByDefinitionResponses](type-aliases/GetProcessInstanceStatisticsByDefinitionResponses.md) - [getProcessInstanceStatisticsByErrorConsistency](type-aliases/getProcessInstanceStatisticsByErrorConsistency.md) - [GetProcessInstanceStatisticsByErrorData](type-aliases/GetProcessInstanceStatisticsByErrorData.md) - [GetProcessInstanceStatisticsByErrorError](type-aliases/GetProcessInstanceStatisticsByErrorError.md) - [GetProcessInstanceStatisticsByErrorErrors](type-aliases/GetProcessInstanceStatisticsByErrorErrors.md) - [getProcessInstanceStatisticsByErrorInput](type-aliases/getProcessInstanceStatisticsByErrorInput.md) - [GetProcessInstanceStatisticsByErrorResponse](type-aliases/GetProcessInstanceStatisticsByErrorResponse.md) - [GetProcessInstanceStatisticsByErrorResponses](type-aliases/GetProcessInstanceStatisticsByErrorResponses.md) - [getProcessInstanceStatisticsConsistency](type-aliases/getProcessInstanceStatisticsConsistency.md) - [GetProcessInstanceStatisticsData](type-aliases/GetProcessInstanceStatisticsData.md) - [GetProcessInstanceStatisticsError](type-aliases/GetProcessInstanceStatisticsError.md) - [GetProcessInstanceStatisticsErrors](type-aliases/GetProcessInstanceStatisticsErrors.md) - [getProcessInstanceStatisticsInput](type-aliases/getProcessInstanceStatisticsInput.md) - [GetProcessInstanceStatisticsResponse](type-aliases/GetProcessInstanceStatisticsResponse.md) - [GetProcessInstanceStatisticsResponses](type-aliases/GetProcessInstanceStatisticsResponses.md) - [getResourceConsistency](type-aliases/getResourceConsistency.md) - [getResourceContentBinaryConsistency](type-aliases/getResourceContentBinaryConsistency.md) - [GetResourceContentBinaryData](type-aliases/GetResourceContentBinaryData.md) - [GetResourceContentBinaryError](type-aliases/GetResourceContentBinaryError.md) - [GetResourceContentBinaryErrors](type-aliases/GetResourceContentBinaryErrors.md) - [getResourceContentBinaryInput](type-aliases/getResourceContentBinaryInput.md) - [GetResourceContentBinaryResponse](type-aliases/GetResourceContentBinaryResponse.md) - [GetResourceContentBinaryResponses](type-aliases/GetResourceContentBinaryResponses.md) - [getResourceContentConsistency](type-aliases/getResourceContentConsistency.md) - [GetResourceContentData](type-aliases/GetResourceContentData.md) - [GetResourceContentError](type-aliases/GetResourceContentError.md) - [GetResourceContentErrors](type-aliases/GetResourceContentErrors.md) - [getResourceContentInput](type-aliases/getResourceContentInput.md) - [GetResourceContentResponse](type-aliases/GetResourceContentResponse.md) - [GetResourceContentResponses](type-aliases/GetResourceContentResponses.md) - [GetResourceData](type-aliases/GetResourceData.md) - [GetResourceError](type-aliases/GetResourceError.md) - [GetResourceErrors](type-aliases/GetResourceErrors.md) - [getResourceInput](type-aliases/getResourceInput.md) - [GetResourceResponse](type-aliases/GetResourceResponse.md) - [GetResourceResponses](type-aliases/GetResourceResponses.md) - [getRoleConsistency](type-aliases/getRoleConsistency.md) - [GetRoleData](type-aliases/GetRoleData.md) - [GetRoleError](type-aliases/GetRoleError.md) - [GetRoleErrors](type-aliases/GetRoleErrors.md) - [getRoleInput](type-aliases/getRoleInput.md) - [GetRoleResponse](type-aliases/GetRoleResponse.md) - [GetRoleResponses](type-aliases/GetRoleResponses.md) - [getStartProcessFormConsistency](type-aliases/getStartProcessFormConsistency.md) - [GetStartProcessFormData](type-aliases/GetStartProcessFormData.md) - [GetStartProcessFormError](type-aliases/GetStartProcessFormError.md) - [GetStartProcessFormErrors](type-aliases/GetStartProcessFormErrors.md) - [getStartProcessFormInput](type-aliases/getStartProcessFormInput.md) - [GetStartProcessFormResponse](type-aliases/GetStartProcessFormResponse.md) - [GetStartProcessFormResponses](type-aliases/GetStartProcessFormResponses.md) - [GetStatusData](type-aliases/GetStatusData.md) - [GetStatusErrors](type-aliases/GetStatusErrors.md) - [getStatusInput](type-aliases/getStatusInput.md) - [GetStatusResponse](type-aliases/GetStatusResponse.md) - [GetStatusResponses](type-aliases/GetStatusResponses.md) - [GetSystemConfigurationData](type-aliases/GetSystemConfigurationData.md) - [GetSystemConfigurationError](type-aliases/GetSystemConfigurationError.md) - [GetSystemConfigurationErrors](type-aliases/GetSystemConfigurationErrors.md) - [getSystemConfigurationInput](type-aliases/getSystemConfigurationInput.md) - [GetSystemConfigurationResponse](type-aliases/GetSystemConfigurationResponse.md) - [GetSystemConfigurationResponses](type-aliases/GetSystemConfigurationResponses.md) - [getTenantClusterVariableConsistency](type-aliases/getTenantClusterVariableConsistency.md) - [GetTenantClusterVariableData](type-aliases/GetTenantClusterVariableData.md) - [GetTenantClusterVariableError](type-aliases/GetTenantClusterVariableError.md) - [GetTenantClusterVariableErrors](type-aliases/GetTenantClusterVariableErrors.md) - [getTenantClusterVariableInput](type-aliases/getTenantClusterVariableInput.md) - [GetTenantClusterVariableResponse](type-aliases/GetTenantClusterVariableResponse.md) - [GetTenantClusterVariableResponses](type-aliases/GetTenantClusterVariableResponses.md) - [getTenantConsistency](type-aliases/getTenantConsistency.md) - [GetTenantData](type-aliases/GetTenantData.md) - [GetTenantError](type-aliases/GetTenantError.md) - [GetTenantErrors](type-aliases/GetTenantErrors.md) - [getTenantInput](type-aliases/getTenantInput.md) - [GetTenantResponse](type-aliases/GetTenantResponse.md) - [GetTenantResponses](type-aliases/GetTenantResponses.md) - [GetTopologyData](type-aliases/GetTopologyData.md) - [GetTopologyError](type-aliases/GetTopologyError.md) - [GetTopologyErrors](type-aliases/GetTopologyErrors.md) - [getTopologyInput](type-aliases/getTopologyInput.md) - [GetTopologyResponse](type-aliases/GetTopologyResponse.md) - [GetTopologyResponses](type-aliases/GetTopologyResponses.md) - [getUsageMetricsConsistency](type-aliases/getUsageMetricsConsistency.md) - [GetUsageMetricsData](type-aliases/GetUsageMetricsData.md) - [GetUsageMetricsError](type-aliases/GetUsageMetricsError.md) - [GetUsageMetricsErrors](type-aliases/GetUsageMetricsErrors.md) - [getUsageMetricsInput](type-aliases/getUsageMetricsInput.md) - [GetUsageMetricsResponse](type-aliases/GetUsageMetricsResponse.md) - [GetUsageMetricsResponses](type-aliases/GetUsageMetricsResponses.md) - [getUserConsistency](type-aliases/getUserConsistency.md) - [GetUserData](type-aliases/GetUserData.md) - [GetUserError](type-aliases/GetUserError.md) - [GetUserErrors](type-aliases/GetUserErrors.md) - [getUserInput](type-aliases/getUserInput.md) - [GetUserResponse](type-aliases/GetUserResponse.md) - [GetUserResponses](type-aliases/GetUserResponses.md) - [getUserTaskConsistency](type-aliases/getUserTaskConsistency.md) - [GetUserTaskData](type-aliases/GetUserTaskData.md) - [GetUserTaskError](type-aliases/GetUserTaskError.md) - [GetUserTaskErrors](type-aliases/GetUserTaskErrors.md) - [getUserTaskFormConsistency](type-aliases/getUserTaskFormConsistency.md) - [GetUserTaskFormData](type-aliases/GetUserTaskFormData.md) - [GetUserTaskFormError](type-aliases/GetUserTaskFormError.md) - [GetUserTaskFormErrors](type-aliases/GetUserTaskFormErrors.md) - [getUserTaskFormInput](type-aliases/getUserTaskFormInput.md) - [GetUserTaskFormResponse](type-aliases/GetUserTaskFormResponse.md) - [GetUserTaskFormResponses](type-aliases/GetUserTaskFormResponses.md) - [getUserTaskInput](type-aliases/getUserTaskInput.md) - [GetUserTaskResponse](type-aliases/GetUserTaskResponse.md) - [GetUserTaskResponses](type-aliases/GetUserTaskResponses.md) - [getVariableConsistency](type-aliases/getVariableConsistency.md) - [GetVariableData](type-aliases/GetVariableData.md) - [GetVariableError](type-aliases/GetVariableError.md) - [GetVariableErrors](type-aliases/GetVariableErrors.md) - [getVariableInput](type-aliases/getVariableInput.md) - [GetVariableResponse](type-aliases/GetVariableResponse.md) - [GetVariableResponses](type-aliases/GetVariableResponses.md) - [GlobalJobStatisticsQueryResult](type-aliases/GlobalJobStatisticsQueryResult.md) - [GlobalListenerBase](type-aliases/GlobalListenerBase.md) - [GlobalListenerId](type-aliases/GlobalListenerId.md) - [GlobalListenerSourceEnum](type-aliases/GlobalListenerSourceEnum.md) - [GlobalListenerSourceExactMatch](type-aliases/GlobalListenerSourceExactMatch.md) - [GlobalListenerSourceFilterProperty](type-aliases/GlobalListenerSourceFilterProperty.md) - [GlobalTaskListenerBase](type-aliases/GlobalTaskListenerBase.md) - [GlobalTaskListenerEventTypeEnum](type-aliases/GlobalTaskListenerEventTypeEnum.md) - [GlobalTaskListenerEventTypeExactMatch](type-aliases/GlobalTaskListenerEventTypeExactMatch.md) - [GlobalTaskListenerEventTypeFilterProperty](type-aliases/GlobalTaskListenerEventTypeFilterProperty.md) - [GlobalTaskListenerEventTypes](type-aliases/GlobalTaskListenerEventTypes.md) - [GlobalTaskListenerResult](type-aliases/GlobalTaskListenerResult.md) - [GlobalTaskListenerSearchQueryFilterRequest](type-aliases/GlobalTaskListenerSearchQueryFilterRequest.md) - [GlobalTaskListenerSearchQueryRequest](type-aliases/GlobalTaskListenerSearchQueryRequest.md) - [GlobalTaskListenerSearchQueryResult](type-aliases/GlobalTaskListenerSearchQueryResult.md) - [GlobalTaskListenerSearchQuerySortRequest](type-aliases/GlobalTaskListenerSearchQuerySortRequest.md) - [GroupClientResult](type-aliases/GroupClientResult.md) - [GroupClientSearchQueryRequest](type-aliases/GroupClientSearchQueryRequest.md) - [GroupClientSearchQuerySortRequest](type-aliases/GroupClientSearchQuerySortRequest.md) - [GroupClientSearchResult](type-aliases/GroupClientSearchResult.md) - [GroupCreateRequest](type-aliases/GroupCreateRequest.md) - [GroupCreateResult](type-aliases/GroupCreateResult.md) - [GroupFilter](type-aliases/GroupFilter.md) - [GroupId](type-aliases/GroupId.md) - [GroupMappingRuleSearchResult](type-aliases/GroupMappingRuleSearchResult.md) - [GroupResult](type-aliases/GroupResult.md) - [GroupRoleSearchResult](type-aliases/GroupRoleSearchResult.md) - [GroupSearchQueryRequest](type-aliases/GroupSearchQueryRequest.md) - [GroupSearchQueryResult](type-aliases/GroupSearchQueryResult.md) - [GroupSearchQuerySortRequest](type-aliases/GroupSearchQuerySortRequest.md) - [GroupUpdateRequest](type-aliases/GroupUpdateRequest.md) - [GroupUpdateResult](type-aliases/GroupUpdateResult.md) - [GroupUserResult](type-aliases/GroupUserResult.md) - [GroupUserSearchQueryRequest](type-aliases/GroupUserSearchQueryRequest.md) - [GroupUserSearchQuerySortRequest](type-aliases/GroupUserSearchQuerySortRequest.md) - [GroupUserSearchResult](type-aliases/GroupUserSearchResult.md) - [IncidentErrorTypeEnum](type-aliases/IncidentErrorTypeEnum.md) - [IncidentErrorTypeExactMatch](type-aliases/IncidentErrorTypeExactMatch.md) - [IncidentErrorTypeFilterProperty](type-aliases/IncidentErrorTypeFilterProperty.md) - [IncidentFilter](type-aliases/IncidentFilter.md) - [IncidentKey](type-aliases/IncidentKey.md) - [IncidentProcessInstanceStatisticsByDefinitionFilter](type-aliases/IncidentProcessInstanceStatisticsByDefinitionFilter.md) - [IncidentProcessInstanceStatisticsByDefinitionQuery](type-aliases/IncidentProcessInstanceStatisticsByDefinitionQuery.md) - [IncidentProcessInstanceStatisticsByDefinitionQueryResult](type-aliases/IncidentProcessInstanceStatisticsByDefinitionQueryResult.md) - [IncidentProcessInstanceStatisticsByDefinitionQuerySortRequest](type-aliases/IncidentProcessInstanceStatisticsByDefinitionQuerySortRequest.md) - [IncidentProcessInstanceStatisticsByDefinitionResult](type-aliases/IncidentProcessInstanceStatisticsByDefinitionResult.md) - [IncidentProcessInstanceStatisticsByErrorQuery](type-aliases/IncidentProcessInstanceStatisticsByErrorQuery.md) - [IncidentProcessInstanceStatisticsByErrorQueryResult](type-aliases/IncidentProcessInstanceStatisticsByErrorQueryResult.md) - [IncidentProcessInstanceStatisticsByErrorQuerySortRequest](type-aliases/IncidentProcessInstanceStatisticsByErrorQuerySortRequest.md) - [IncidentProcessInstanceStatisticsByErrorResult](type-aliases/IncidentProcessInstanceStatisticsByErrorResult.md) - [IncidentResolutionRequest](type-aliases/IncidentResolutionRequest.md) - [IncidentResult](type-aliases/IncidentResult.md) - [IncidentSearchQuery](type-aliases/IncidentSearchQuery.md) - [IncidentSearchQueryResult](type-aliases/IncidentSearchQueryResult.md) - [IncidentSearchQuerySortRequest](type-aliases/IncidentSearchQuerySortRequest.md) - [IncidentStateEnum](type-aliases/IncidentStateEnum.md) - [IncidentStateExactMatch](type-aliases/IncidentStateExactMatch.md) - [IncidentStateFilterProperty](type-aliases/IncidentStateFilterProperty.md) - [InferredAncestorKeyInstruction](type-aliases/InferredAncestorKeyInstruction.md) - [IntegerFilterProperty](type-aliases/IntegerFilterProperty.md) - [Job](type-aliases/Job.md) - [JobActionReceipt](type-aliases/JobActionReceipt.md) - [JobActionReceipt](type-aliases/JobActionReceipt-1.md) - [JobActivationRequest](type-aliases/JobActivationRequest.md) - [JobActivationResult](type-aliases/JobActivationResult.md) - [JobChangeset](type-aliases/JobChangeset.md) - [JobCompletionRequest](type-aliases/JobCompletionRequest.md) - [JobErrorRequest](type-aliases/JobErrorRequest.md) - [JobErrorStatisticsFilter](type-aliases/JobErrorStatisticsFilter.md) - [JobErrorStatisticsItem](type-aliases/JobErrorStatisticsItem.md) - [JobErrorStatisticsQuery](type-aliases/JobErrorStatisticsQuery.md) - [JobErrorStatisticsQueryResult](type-aliases/JobErrorStatisticsQueryResult.md) - [JobFailRequest](type-aliases/JobFailRequest.md) - [JobFilter](type-aliases/JobFilter.md) - [JobKey](type-aliases/JobKey.md) - [JobKeyExactMatch](type-aliases/JobKeyExactMatch.md) - [JobKeyFilterProperty](type-aliases/JobKeyFilterProperty.md) - [JobKindEnum](type-aliases/JobKindEnum.md) - [JobKindExactMatch](type-aliases/JobKindExactMatch.md) - [JobKindFilterProperty](type-aliases/JobKindFilterProperty.md) - [JobListenerEventTypeEnum](type-aliases/JobListenerEventTypeEnum.md) - [JobListenerEventTypeExactMatch](type-aliases/JobListenerEventTypeExactMatch.md) - [JobListenerEventTypeFilterProperty](type-aliases/JobListenerEventTypeFilterProperty.md) - [JobMetricsConfigurationResponse](type-aliases/JobMetricsConfigurationResponse.md) - [JobResult](type-aliases/JobResult.md) - [JobResultActivateElement](type-aliases/JobResultActivateElement.md) - [JobResultAdHocSubProcess](type-aliases/JobResultAdHocSubProcess.md) - [JobResultCorrections](type-aliases/JobResultCorrections.md) - [JobResultUserTask](type-aliases/JobResultUserTask.md) - [JobSearchQuery](type-aliases/JobSearchQuery.md) - [JobSearchQueryResult](type-aliases/JobSearchQueryResult.md) - [JobSearchQuerySortRequest](type-aliases/JobSearchQuerySortRequest.md) - [JobSearchResult](type-aliases/JobSearchResult.md) - [JobStateEnum](type-aliases/JobStateEnum.md) - [JobStateExactMatch](type-aliases/JobStateExactMatch.md) - [JobStateFilterProperty](type-aliases/JobStateFilterProperty.md) - [JobTimeSeriesStatisticsFilter](type-aliases/JobTimeSeriesStatisticsFilter.md) - [JobTimeSeriesStatisticsItem](type-aliases/JobTimeSeriesStatisticsItem.md) - [JobTimeSeriesStatisticsQuery](type-aliases/JobTimeSeriesStatisticsQuery.md) - [JobTimeSeriesStatisticsQueryResult](type-aliases/JobTimeSeriesStatisticsQueryResult.md) - [JobTypeStatisticsFilter](type-aliases/JobTypeStatisticsFilter.md) - [JobTypeStatisticsItem](type-aliases/JobTypeStatisticsItem.md) - [JobTypeStatisticsQuery](type-aliases/JobTypeStatisticsQuery.md) - [JobTypeStatisticsQueryResult](type-aliases/JobTypeStatisticsQueryResult.md) - [JobUpdateRequest](type-aliases/JobUpdateRequest.md) - [JobWorkerStatisticsFilter](type-aliases/JobWorkerStatisticsFilter.md) - [JobWorkerStatisticsItem](type-aliases/JobWorkerStatisticsItem.md) - [JobWorkerStatisticsQuery](type-aliases/JobWorkerStatisticsQuery.md) - [JobWorkerStatisticsQueryResult](type-aliases/JobWorkerStatisticsQueryResult.md) - [LicenseResponse](type-aliases/LicenseResponse.md) - [LikeFilter](type-aliases/LikeFilter.md) - [LimitPagination](type-aliases/LimitPagination.md) - [LongKey](type-aliases/LongKey.md) - [Loose](type-aliases/Loose.md) - [MappingRuleCreateRequest](type-aliases/MappingRuleCreateRequest.md) - [MappingRuleCreateResult](type-aliases/MappingRuleCreateResult.md) - [MappingRuleCreateUpdateRequest](type-aliases/MappingRuleCreateUpdateRequest.md) - [MappingRuleCreateUpdateResult](type-aliases/MappingRuleCreateUpdateResult.md) - [MappingRuleFilter](type-aliases/MappingRuleFilter.md) - [MappingRuleId](type-aliases/MappingRuleId.md) - [MappingRuleResult](type-aliases/MappingRuleResult.md) - [MappingRuleSearchQueryRequest](type-aliases/MappingRuleSearchQueryRequest.md) - [MappingRuleSearchQueryResult](type-aliases/MappingRuleSearchQueryResult.md) - [MappingRuleSearchQuerySortRequest](type-aliases/MappingRuleSearchQuerySortRequest.md) - [MappingRuleUpdateRequest](type-aliases/MappingRuleUpdateRequest.md) - [MappingRuleUpdateResult](type-aliases/MappingRuleUpdateResult.md) - [MatchedDecisionRuleItem](type-aliases/MatchedDecisionRuleItem.md) - [MessageCorrelationRequest](type-aliases/MessageCorrelationRequest.md) - [MessageCorrelationResult](type-aliases/MessageCorrelationResult.md) - [MessageKey](type-aliases/MessageKey.md) - [MessagePublicationRequest](type-aliases/MessagePublicationRequest.md) - [MessagePublicationResult](type-aliases/MessagePublicationResult.md) - [MessageSubscriptionFilter](type-aliases/MessageSubscriptionFilter.md) - [MessageSubscriptionKey](type-aliases/MessageSubscriptionKey.md) - [MessageSubscriptionKeyExactMatch](type-aliases/MessageSubscriptionKeyExactMatch.md) - [MessageSubscriptionKeyFilterProperty](type-aliases/MessageSubscriptionKeyFilterProperty.md) - [MessageSubscriptionResult](type-aliases/MessageSubscriptionResult.md) - [MessageSubscriptionSearchQuery](type-aliases/MessageSubscriptionSearchQuery.md) - [MessageSubscriptionSearchQueryResult](type-aliases/MessageSubscriptionSearchQueryResult.md) - [MessageSubscriptionSearchQuerySortRequest](type-aliases/MessageSubscriptionSearchQuerySortRequest.md) - [MessageSubscriptionStateEnum](type-aliases/MessageSubscriptionStateEnum.md) - [MessageSubscriptionStateExactMatch](type-aliases/MessageSubscriptionStateExactMatch.md) - [MessageSubscriptionStateFilterProperty](type-aliases/MessageSubscriptionStateFilterProperty.md) - [MessageSubscriptionTypeEnum](type-aliases/MessageSubscriptionTypeEnum.md) - [MessageSubscriptionTypeExactMatch](type-aliases/MessageSubscriptionTypeExactMatch.md) - [MessageSubscriptionTypeFilterProperty](type-aliases/MessageSubscriptionTypeFilterProperty.md) - [MigrateProcessInstanceData](type-aliases/MigrateProcessInstanceData.md) - [MigrateProcessInstanceError](type-aliases/MigrateProcessInstanceError.md) - [MigrateProcessInstanceErrors](type-aliases/MigrateProcessInstanceErrors.md) - [migrateProcessInstanceInput](type-aliases/migrateProcessInstanceInput.md) - [MigrateProcessInstanceMappingInstruction](type-aliases/MigrateProcessInstanceMappingInstruction.md) - [MigrateProcessInstanceResponse](type-aliases/MigrateProcessInstanceResponse.md) - [MigrateProcessInstanceResponses](type-aliases/MigrateProcessInstanceResponses.md) - [MigrateProcessInstancesBatchOperationData](type-aliases/MigrateProcessInstancesBatchOperationData.md) - [MigrateProcessInstancesBatchOperationError](type-aliases/MigrateProcessInstancesBatchOperationError.md) - [MigrateProcessInstancesBatchOperationErrors](type-aliases/MigrateProcessInstancesBatchOperationErrors.md) - [migrateProcessInstancesBatchOperationInput](type-aliases/migrateProcessInstancesBatchOperationInput.md) - [MigrateProcessInstancesBatchOperationResponse](type-aliases/MigrateProcessInstancesBatchOperationResponse.md) - [MigrateProcessInstancesBatchOperationResponses](type-aliases/MigrateProcessInstancesBatchOperationResponses.md) - [ModifyProcessInstanceData](type-aliases/ModifyProcessInstanceData.md) - [ModifyProcessInstanceError](type-aliases/ModifyProcessInstanceError.md) - [ModifyProcessInstanceErrors](type-aliases/ModifyProcessInstanceErrors.md) - [modifyProcessInstanceInput](type-aliases/modifyProcessInstanceInput.md) - [ModifyProcessInstanceResponse](type-aliases/ModifyProcessInstanceResponse.md) - [ModifyProcessInstanceResponses](type-aliases/ModifyProcessInstanceResponses.md) - [ModifyProcessInstancesBatchOperationData](type-aliases/ModifyProcessInstancesBatchOperationData.md) - [ModifyProcessInstancesBatchOperationError](type-aliases/ModifyProcessInstancesBatchOperationError.md) - [ModifyProcessInstancesBatchOperationErrors](type-aliases/ModifyProcessInstancesBatchOperationErrors.md) - [modifyProcessInstancesBatchOperationInput](type-aliases/modifyProcessInstancesBatchOperationInput.md) - [ModifyProcessInstancesBatchOperationResponse](type-aliases/ModifyProcessInstancesBatchOperationResponse.md) - [ModifyProcessInstancesBatchOperationResponses](type-aliases/ModifyProcessInstancesBatchOperationResponses.md) - [ModifyProcessInstanceVariableInstruction](type-aliases/ModifyProcessInstanceVariableInstruction.md) - [OffsetPagination](type-aliases/OffsetPagination.md) - [OperationReference](type-aliases/OperationReference.md) - [OperationTypeExactMatch](type-aliases/OperationTypeExactMatch.md) - [OperationTypeFilterProperty](type-aliases/OperationTypeFilterProperty.md) - [OwnerTypeEnum](type-aliases/OwnerTypeEnum.md) - [Partition](type-aliases/Partition.md) - [PermissionTypeEnum](type-aliases/PermissionTypeEnum.md) - [PinClockData](type-aliases/PinClockData.md) - [PinClockError](type-aliases/PinClockError.md) - [PinClockErrors](type-aliases/PinClockErrors.md) - [pinClockInput](type-aliases/pinClockInput.md) - [PinClockResponse](type-aliases/PinClockResponse.md) - [PinClockResponses](type-aliases/PinClockResponses.md) - [ProblemDetail](type-aliases/ProblemDetail.md) - [ProcessDefinitionElementStatisticsQuery](type-aliases/ProcessDefinitionElementStatisticsQuery.md) - [ProcessDefinitionElementStatisticsQueryResult](type-aliases/ProcessDefinitionElementStatisticsQueryResult.md) - [ProcessDefinitionFilter](type-aliases/ProcessDefinitionFilter.md) - [ProcessDefinitionId](type-aliases/ProcessDefinitionId.md) - [ProcessDefinitionIdExactMatch](type-aliases/ProcessDefinitionIdExactMatch.md) - [ProcessDefinitionIdFilterProperty](type-aliases/ProcessDefinitionIdFilterProperty.md) - [ProcessDefinitionInstanceStatisticsQuery](type-aliases/ProcessDefinitionInstanceStatisticsQuery.md) - [ProcessDefinitionInstanceStatisticsQueryResult](type-aliases/ProcessDefinitionInstanceStatisticsQueryResult.md) - [ProcessDefinitionInstanceStatisticsQuerySortRequest](type-aliases/ProcessDefinitionInstanceStatisticsQuerySortRequest.md) - [ProcessDefinitionInstanceStatisticsResult](type-aliases/ProcessDefinitionInstanceStatisticsResult.md) - [ProcessDefinitionInstanceVersionStatisticsFilter](type-aliases/ProcessDefinitionInstanceVersionStatisticsFilter.md) - [ProcessDefinitionInstanceVersionStatisticsQuery](type-aliases/ProcessDefinitionInstanceVersionStatisticsQuery.md) - [ProcessDefinitionInstanceVersionStatisticsQueryResult](type-aliases/ProcessDefinitionInstanceVersionStatisticsQueryResult.md) - [ProcessDefinitionInstanceVersionStatisticsQuerySortRequest](type-aliases/ProcessDefinitionInstanceVersionStatisticsQuerySortRequest.md) - [ProcessDefinitionInstanceVersionStatisticsResult](type-aliases/ProcessDefinitionInstanceVersionStatisticsResult.md) - [ProcessDefinitionKey](type-aliases/ProcessDefinitionKey.md) - [ProcessDefinitionKeyExactMatch](type-aliases/ProcessDefinitionKeyExactMatch.md) - [ProcessDefinitionKeyFilterProperty](type-aliases/ProcessDefinitionKeyFilterProperty.md) - [ProcessDefinitionMessageSubscriptionStatisticsQuery](type-aliases/ProcessDefinitionMessageSubscriptionStatisticsQuery.md) - [ProcessDefinitionMessageSubscriptionStatisticsQueryResult](type-aliases/ProcessDefinitionMessageSubscriptionStatisticsQueryResult.md) - [ProcessDefinitionMessageSubscriptionStatisticsResult](type-aliases/ProcessDefinitionMessageSubscriptionStatisticsResult.md) - [ProcessDefinitionResult](type-aliases/ProcessDefinitionResult.md) - [ProcessDefinitionSearchQuery](type-aliases/ProcessDefinitionSearchQuery.md) - [ProcessDefinitionSearchQueryResult](type-aliases/ProcessDefinitionSearchQueryResult.md) - [ProcessDefinitionSearchQuerySortRequest](type-aliases/ProcessDefinitionSearchQuerySortRequest.md) - [ProcessDefinitionStatisticsFilter](type-aliases/ProcessDefinitionStatisticsFilter.md) - [ProcessElementStatisticsResult](type-aliases/ProcessElementStatisticsResult.md) - [ProcessInstanceCallHierarchyEntry](type-aliases/ProcessInstanceCallHierarchyEntry.md) - [ProcessInstanceCancellationBatchOperationRequest](type-aliases/ProcessInstanceCancellationBatchOperationRequest.md) - [ProcessInstanceCreationInstruction](type-aliases/ProcessInstanceCreationInstruction.md) - [ProcessInstanceCreationInstructionById](type-aliases/ProcessInstanceCreationInstructionById.md) - [ProcessInstanceCreationInstructionByKey](type-aliases/ProcessInstanceCreationInstructionByKey.md) - [ProcessInstanceCreationRuntimeInstruction](type-aliases/ProcessInstanceCreationRuntimeInstruction.md) - [ProcessInstanceCreationStartInstruction](type-aliases/ProcessInstanceCreationStartInstruction.md) - [ProcessInstanceCreationTerminateInstruction](type-aliases/ProcessInstanceCreationTerminateInstruction.md) - [ProcessInstanceDeletionBatchOperationRequest](type-aliases/ProcessInstanceDeletionBatchOperationRequest.md) - [ProcessInstanceElementStatisticsQueryResult](type-aliases/ProcessInstanceElementStatisticsQueryResult.md) - [ProcessInstanceFilter](type-aliases/ProcessInstanceFilter.md) - [ProcessInstanceFilterFields](type-aliases/ProcessInstanceFilterFields.md) - [ProcessInstanceIncidentResolutionBatchOperationRequest](type-aliases/ProcessInstanceIncidentResolutionBatchOperationRequest.md) - [ProcessInstanceKey](type-aliases/ProcessInstanceKey.md) - [ProcessInstanceKeyExactMatch](type-aliases/ProcessInstanceKeyExactMatch.md) - [ProcessInstanceKeyFilterProperty](type-aliases/ProcessInstanceKeyFilterProperty.md) - [ProcessInstanceMigrationBatchOperationPlan](type-aliases/ProcessInstanceMigrationBatchOperationPlan.md) - [ProcessInstanceMigrationBatchOperationRequest](type-aliases/ProcessInstanceMigrationBatchOperationRequest.md) - [ProcessInstanceMigrationInstruction](type-aliases/ProcessInstanceMigrationInstruction.md) - [ProcessInstanceModificationActivateInstruction](type-aliases/ProcessInstanceModificationActivateInstruction.md) - [ProcessInstanceModificationBatchOperationRequest](type-aliases/ProcessInstanceModificationBatchOperationRequest.md) - [ProcessInstanceModificationInstruction](type-aliases/ProcessInstanceModificationInstruction.md) - [ProcessInstanceModificationMoveBatchOperationInstruction](type-aliases/ProcessInstanceModificationMoveBatchOperationInstruction.md) - [ProcessInstanceModificationMoveInstruction](type-aliases/ProcessInstanceModificationMoveInstruction.md) - [ProcessInstanceModificationTerminateByIdInstruction](type-aliases/ProcessInstanceModificationTerminateByIdInstruction.md) - [ProcessInstanceModificationTerminateByKeyInstruction](type-aliases/ProcessInstanceModificationTerminateByKeyInstruction.md) - [ProcessInstanceModificationTerminateInstruction](type-aliases/ProcessInstanceModificationTerminateInstruction.md) - [ProcessInstanceReference](type-aliases/ProcessInstanceReference.md) - [ProcessInstanceResult](type-aliases/ProcessInstanceResult.md) - [ProcessInstanceSearchQuery](type-aliases/ProcessInstanceSearchQuery.md) - [ProcessInstanceSearchQueryResult](type-aliases/ProcessInstanceSearchQueryResult.md) - [ProcessInstanceSearchQuerySortRequest](type-aliases/ProcessInstanceSearchQuerySortRequest.md) - [ProcessInstanceSequenceFlowResult](type-aliases/ProcessInstanceSequenceFlowResult.md) - [ProcessInstanceSequenceFlowsQueryResult](type-aliases/ProcessInstanceSequenceFlowsQueryResult.md) - [ProcessInstanceStateEnum](type-aliases/ProcessInstanceStateEnum.md) - [ProcessInstanceStateExactMatch](type-aliases/ProcessInstanceStateExactMatch.md) - [ProcessInstanceStateFilterProperty](type-aliases/ProcessInstanceStateFilterProperty.md) - [PublishMessageData](type-aliases/PublishMessageData.md) - [PublishMessageError](type-aliases/PublishMessageError.md) - [PublishMessageErrors](type-aliases/PublishMessageErrors.md) - [publishMessageInput](type-aliases/publishMessageInput.md) - [PublishMessageResponse](type-aliases/PublishMessageResponse.md) - [PublishMessageResponses](type-aliases/PublishMessageResponses.md) - [ResetClockData](type-aliases/ResetClockData.md) - [ResetClockError](type-aliases/ResetClockError.md) - [ResetClockErrors](type-aliases/ResetClockErrors.md) - [resetClockInput](type-aliases/resetClockInput.md) - [ResetClockResponse](type-aliases/ResetClockResponse.md) - [ResetClockResponses](type-aliases/ResetClockResponses.md) - [ResolveIncidentData](type-aliases/ResolveIncidentData.md) - [ResolveIncidentError](type-aliases/ResolveIncidentError.md) - [ResolveIncidentErrors](type-aliases/ResolveIncidentErrors.md) - [resolveIncidentInput](type-aliases/resolveIncidentInput.md) - [ResolveIncidentResponse](type-aliases/ResolveIncidentResponse.md) - [ResolveIncidentResponses](type-aliases/ResolveIncidentResponses.md) - [ResolveIncidentsBatchOperationData](type-aliases/ResolveIncidentsBatchOperationData.md) - [ResolveIncidentsBatchOperationError](type-aliases/ResolveIncidentsBatchOperationError.md) - [ResolveIncidentsBatchOperationErrors](type-aliases/ResolveIncidentsBatchOperationErrors.md) - [resolveIncidentsBatchOperationInput](type-aliases/resolveIncidentsBatchOperationInput.md) - [ResolveIncidentsBatchOperationResponse](type-aliases/ResolveIncidentsBatchOperationResponse.md) - [ResolveIncidentsBatchOperationResponses](type-aliases/ResolveIncidentsBatchOperationResponses.md) - [ResolveProcessInstanceIncidentsData](type-aliases/ResolveProcessInstanceIncidentsData.md) - [ResolveProcessInstanceIncidentsError](type-aliases/ResolveProcessInstanceIncidentsError.md) - [ResolveProcessInstanceIncidentsErrors](type-aliases/ResolveProcessInstanceIncidentsErrors.md) - [resolveProcessInstanceIncidentsInput](type-aliases/resolveProcessInstanceIncidentsInput.md) - [ResolveProcessInstanceIncidentsResponse](type-aliases/ResolveProcessInstanceIncidentsResponse.md) - [ResolveProcessInstanceIncidentsResponses](type-aliases/ResolveProcessInstanceIncidentsResponses.md) - [ResourceFilter](type-aliases/ResourceFilter.md) - [ResourceKey](type-aliases/ResourceKey.md) - [ResourceKeyExactMatch](type-aliases/ResourceKeyExactMatch.md) - [ResourceKeyFilterProperty](type-aliases/ResourceKeyFilterProperty.md) - [ResourceResult](type-aliases/ResourceResult.md) - [ResourceSearchQuery](type-aliases/ResourceSearchQuery.md) - [ResourceSearchQueryResult](type-aliases/ResourceSearchQueryResult.md) - [ResourceSearchQuerySortRequest](type-aliases/ResourceSearchQuerySortRequest.md) - [ResourceTypeEnum](type-aliases/ResourceTypeEnum.md) - [Result](type-aliases/Result.md) - [ResumeBatchOperationData](type-aliases/ResumeBatchOperationData.md) - [ResumeBatchOperationError](type-aliases/ResumeBatchOperationError.md) - [ResumeBatchOperationErrors](type-aliases/ResumeBatchOperationErrors.md) - [resumeBatchOperationInput](type-aliases/resumeBatchOperationInput.md) - [ResumeBatchOperationResponse](type-aliases/ResumeBatchOperationResponse.md) - [ResumeBatchOperationResponses](type-aliases/ResumeBatchOperationResponses.md) - [RoleClientResult](type-aliases/RoleClientResult.md) - [RoleClientSearchQueryRequest](type-aliases/RoleClientSearchQueryRequest.md) - [RoleClientSearchQuerySortRequest](type-aliases/RoleClientSearchQuerySortRequest.md) - [RoleClientSearchResult](type-aliases/RoleClientSearchResult.md) - [RoleCreateRequest](type-aliases/RoleCreateRequest.md) - [RoleCreateResult](type-aliases/RoleCreateResult.md) - [RoleFilter](type-aliases/RoleFilter.md) - [RoleGroupResult](type-aliases/RoleGroupResult.md) - [RoleGroupSearchQueryRequest](type-aliases/RoleGroupSearchQueryRequest.md) - [RoleGroupSearchQuerySortRequest](type-aliases/RoleGroupSearchQuerySortRequest.md) - [RoleGroupSearchResult](type-aliases/RoleGroupSearchResult.md) - [RoleId](type-aliases/RoleId.md) - [RoleMappingRuleSearchResult](type-aliases/RoleMappingRuleSearchResult.md) - [RoleResult](type-aliases/RoleResult.md) - [RoleSearchQueryRequest](type-aliases/RoleSearchQueryRequest.md) - [RoleSearchQueryResult](type-aliases/RoleSearchQueryResult.md) - [RoleSearchQuerySortRequest](type-aliases/RoleSearchQuerySortRequest.md) - [RoleUpdateRequest](type-aliases/RoleUpdateRequest.md) - [RoleUpdateResult](type-aliases/RoleUpdateResult.md) - [RoleUserResult](type-aliases/RoleUserResult.md) - [RoleUserSearchQueryRequest](type-aliases/RoleUserSearchQueryRequest.md) - [RoleUserSearchQuerySortRequest](type-aliases/RoleUserSearchQuerySortRequest.md) - [RoleUserSearchResult](type-aliases/RoleUserSearchResult.md) - [ScopeKey](type-aliases/ScopeKey.md) - [ScopeKeyExactMatch](type-aliases/ScopeKeyExactMatch.md) - [ScopeKeyFilterProperty](type-aliases/ScopeKeyFilterProperty.md) - [SdkError](type-aliases/SdkError.md) - [searchAgentInstancesConsistency](type-aliases/searchAgentInstancesConsistency.md) - [SearchAgentInstancesData](type-aliases/SearchAgentInstancesData.md) - [SearchAgentInstancesError](type-aliases/SearchAgentInstancesError.md) - [SearchAgentInstancesErrors](type-aliases/SearchAgentInstancesErrors.md) - [searchAgentInstancesInput](type-aliases/searchAgentInstancesInput.md) - [SearchAgentInstancesResponse](type-aliases/SearchAgentInstancesResponse.md) - [SearchAgentInstancesResponses](type-aliases/SearchAgentInstancesResponses.md) - [searchAuditLogsConsistency](type-aliases/searchAuditLogsConsistency.md) - [SearchAuditLogsData](type-aliases/SearchAuditLogsData.md) - [SearchAuditLogsError](type-aliases/SearchAuditLogsError.md) - [SearchAuditLogsErrors](type-aliases/SearchAuditLogsErrors.md) - [searchAuditLogsInput](type-aliases/searchAuditLogsInput.md) - [SearchAuditLogsResponse](type-aliases/SearchAuditLogsResponse.md) - [SearchAuditLogsResponses](type-aliases/SearchAuditLogsResponses.md) - [searchAuthorizationsConsistency](type-aliases/searchAuthorizationsConsistency.md) - [SearchAuthorizationsData](type-aliases/SearchAuthorizationsData.md) - [SearchAuthorizationsError](type-aliases/SearchAuthorizationsError.md) - [SearchAuthorizationsErrors](type-aliases/SearchAuthorizationsErrors.md) - [searchAuthorizationsInput](type-aliases/searchAuthorizationsInput.md) - [SearchAuthorizationsResponse](type-aliases/SearchAuthorizationsResponse.md) - [SearchAuthorizationsResponses](type-aliases/SearchAuthorizationsResponses.md) - [searchBatchOperationItemsConsistency](type-aliases/searchBatchOperationItemsConsistency.md) - [SearchBatchOperationItemsData](type-aliases/SearchBatchOperationItemsData.md) - [SearchBatchOperationItemsError](type-aliases/SearchBatchOperationItemsError.md) - [SearchBatchOperationItemsErrors](type-aliases/SearchBatchOperationItemsErrors.md) - [searchBatchOperationItemsInput](type-aliases/searchBatchOperationItemsInput.md) - [SearchBatchOperationItemsResponse](type-aliases/SearchBatchOperationItemsResponse.md) - [SearchBatchOperationItemsResponses](type-aliases/SearchBatchOperationItemsResponses.md) - [searchBatchOperationsConsistency](type-aliases/searchBatchOperationsConsistency.md) - [SearchBatchOperationsData](type-aliases/SearchBatchOperationsData.md) - [SearchBatchOperationsError](type-aliases/SearchBatchOperationsError.md) - [SearchBatchOperationsErrors](type-aliases/SearchBatchOperationsErrors.md) - [searchBatchOperationsInput](type-aliases/searchBatchOperationsInput.md) - [SearchBatchOperationsResponse](type-aliases/SearchBatchOperationsResponse.md) - [SearchBatchOperationsResponses](type-aliases/SearchBatchOperationsResponses.md) - [searchClientsForGroupConsistency](type-aliases/searchClientsForGroupConsistency.md) - [SearchClientsForGroupData](type-aliases/SearchClientsForGroupData.md) - [SearchClientsForGroupError](type-aliases/SearchClientsForGroupError.md) - [SearchClientsForGroupErrors](type-aliases/SearchClientsForGroupErrors.md) - [searchClientsForGroupInput](type-aliases/searchClientsForGroupInput.md) - [SearchClientsForGroupResponse](type-aliases/SearchClientsForGroupResponse.md) - [SearchClientsForGroupResponses](type-aliases/SearchClientsForGroupResponses.md) - [searchClientsForRoleConsistency](type-aliases/searchClientsForRoleConsistency.md) - [SearchClientsForRoleData](type-aliases/SearchClientsForRoleData.md) - [SearchClientsForRoleError](type-aliases/SearchClientsForRoleError.md) - [SearchClientsForRoleErrors](type-aliases/SearchClientsForRoleErrors.md) - [searchClientsForRoleInput](type-aliases/searchClientsForRoleInput.md) - [SearchClientsForRoleResponse](type-aliases/SearchClientsForRoleResponse.md) - [SearchClientsForRoleResponses](type-aliases/SearchClientsForRoleResponses.md) - [searchClientsForTenantConsistency](type-aliases/searchClientsForTenantConsistency.md) - [SearchClientsForTenantData](type-aliases/SearchClientsForTenantData.md) - [searchClientsForTenantInput](type-aliases/searchClientsForTenantInput.md) - [SearchClientsForTenantResponse](type-aliases/SearchClientsForTenantResponse.md) - [SearchClientsForTenantResponses](type-aliases/SearchClientsForTenantResponses.md) - [searchClusterVariablesConsistency](type-aliases/searchClusterVariablesConsistency.md) - [SearchClusterVariablesData](type-aliases/SearchClusterVariablesData.md) - [SearchClusterVariablesError](type-aliases/SearchClusterVariablesError.md) - [SearchClusterVariablesErrors](type-aliases/SearchClusterVariablesErrors.md) - [searchClusterVariablesInput](type-aliases/searchClusterVariablesInput.md) - [SearchClusterVariablesResponse](type-aliases/SearchClusterVariablesResponse.md) - [SearchClusterVariablesResponses](type-aliases/SearchClusterVariablesResponses.md) - [searchCorrelatedMessageSubscriptionsConsistency](type-aliases/searchCorrelatedMessageSubscriptionsConsistency.md) - [SearchCorrelatedMessageSubscriptionsData](type-aliases/SearchCorrelatedMessageSubscriptionsData.md) - [SearchCorrelatedMessageSubscriptionsError](type-aliases/SearchCorrelatedMessageSubscriptionsError.md) - [SearchCorrelatedMessageSubscriptionsErrors](type-aliases/SearchCorrelatedMessageSubscriptionsErrors.md) - [searchCorrelatedMessageSubscriptionsInput](type-aliases/searchCorrelatedMessageSubscriptionsInput.md) - [SearchCorrelatedMessageSubscriptionsResponse](type-aliases/SearchCorrelatedMessageSubscriptionsResponse.md) - [SearchCorrelatedMessageSubscriptionsResponses](type-aliases/SearchCorrelatedMessageSubscriptionsResponses.md) - [searchDecisionDefinitionsConsistency](type-aliases/searchDecisionDefinitionsConsistency.md) - [SearchDecisionDefinitionsData](type-aliases/SearchDecisionDefinitionsData.md) - [SearchDecisionDefinitionsError](type-aliases/SearchDecisionDefinitionsError.md) - [SearchDecisionDefinitionsErrors](type-aliases/SearchDecisionDefinitionsErrors.md) - [searchDecisionDefinitionsInput](type-aliases/searchDecisionDefinitionsInput.md) - [SearchDecisionDefinitionsResponse](type-aliases/SearchDecisionDefinitionsResponse.md) - [SearchDecisionDefinitionsResponses](type-aliases/SearchDecisionDefinitionsResponses.md) - [searchDecisionInstancesConsistency](type-aliases/searchDecisionInstancesConsistency.md) - [SearchDecisionInstancesData](type-aliases/SearchDecisionInstancesData.md) - [SearchDecisionInstancesError](type-aliases/SearchDecisionInstancesError.md) - [SearchDecisionInstancesErrors](type-aliases/SearchDecisionInstancesErrors.md) - [searchDecisionInstancesInput](type-aliases/searchDecisionInstancesInput.md) - [SearchDecisionInstancesResponse](type-aliases/SearchDecisionInstancesResponse.md) - [SearchDecisionInstancesResponses](type-aliases/SearchDecisionInstancesResponses.md) - [searchDecisionRequirementsConsistency](type-aliases/searchDecisionRequirementsConsistency.md) - [SearchDecisionRequirementsData](type-aliases/SearchDecisionRequirementsData.md) - [SearchDecisionRequirementsError](type-aliases/SearchDecisionRequirementsError.md) - [SearchDecisionRequirementsErrors](type-aliases/SearchDecisionRequirementsErrors.md) - [searchDecisionRequirementsInput](type-aliases/searchDecisionRequirementsInput.md) - [SearchDecisionRequirementsResponse](type-aliases/SearchDecisionRequirementsResponse.md) - [SearchDecisionRequirementsResponses](type-aliases/SearchDecisionRequirementsResponses.md) - [searchElementInstanceIncidentsConsistency](type-aliases/searchElementInstanceIncidentsConsistency.md) - [SearchElementInstanceIncidentsData](type-aliases/SearchElementInstanceIncidentsData.md) - [SearchElementInstanceIncidentsError](type-aliases/SearchElementInstanceIncidentsError.md) - [SearchElementInstanceIncidentsErrors](type-aliases/SearchElementInstanceIncidentsErrors.md) - [searchElementInstanceIncidentsInput](type-aliases/searchElementInstanceIncidentsInput.md) - [SearchElementInstanceIncidentsResponse](type-aliases/SearchElementInstanceIncidentsResponse.md) - [SearchElementInstanceIncidentsResponses](type-aliases/SearchElementInstanceIncidentsResponses.md) - [searchElementInstancesConsistency](type-aliases/searchElementInstancesConsistency.md) - [SearchElementInstancesData](type-aliases/SearchElementInstancesData.md) - [SearchElementInstancesError](type-aliases/SearchElementInstancesError.md) - [SearchElementInstancesErrors](type-aliases/SearchElementInstancesErrors.md) - [searchElementInstancesInput](type-aliases/searchElementInstancesInput.md) - [SearchElementInstancesResponse](type-aliases/SearchElementInstancesResponse.md) - [SearchElementInstancesResponses](type-aliases/SearchElementInstancesResponses.md) - [searchGlobalTaskListenersConsistency](type-aliases/searchGlobalTaskListenersConsistency.md) - [SearchGlobalTaskListenersData](type-aliases/SearchGlobalTaskListenersData.md) - [SearchGlobalTaskListenersError](type-aliases/SearchGlobalTaskListenersError.md) - [SearchGlobalTaskListenersErrors](type-aliases/SearchGlobalTaskListenersErrors.md) - [searchGlobalTaskListenersInput](type-aliases/searchGlobalTaskListenersInput.md) - [SearchGlobalTaskListenersResponse](type-aliases/SearchGlobalTaskListenersResponse.md) - [SearchGlobalTaskListenersResponses](type-aliases/SearchGlobalTaskListenersResponses.md) - [searchGroupIdsForTenantConsistency](type-aliases/searchGroupIdsForTenantConsistency.md) - [SearchGroupIdsForTenantData](type-aliases/SearchGroupIdsForTenantData.md) - [searchGroupIdsForTenantInput](type-aliases/searchGroupIdsForTenantInput.md) - [SearchGroupIdsForTenantResponse](type-aliases/SearchGroupIdsForTenantResponse.md) - [SearchGroupIdsForTenantResponses](type-aliases/SearchGroupIdsForTenantResponses.md) - [searchGroupsConsistency](type-aliases/searchGroupsConsistency.md) - [SearchGroupsData](type-aliases/SearchGroupsData.md) - [SearchGroupsError](type-aliases/SearchGroupsError.md) - [SearchGroupsErrors](type-aliases/SearchGroupsErrors.md) - [searchGroupsForRoleConsistency](type-aliases/searchGroupsForRoleConsistency.md) - [SearchGroupsForRoleData](type-aliases/SearchGroupsForRoleData.md) - [SearchGroupsForRoleError](type-aliases/SearchGroupsForRoleError.md) - [SearchGroupsForRoleErrors](type-aliases/SearchGroupsForRoleErrors.md) - [searchGroupsForRoleInput](type-aliases/searchGroupsForRoleInput.md) - [SearchGroupsForRoleResponse](type-aliases/SearchGroupsForRoleResponse.md) - [SearchGroupsForRoleResponses](type-aliases/SearchGroupsForRoleResponses.md) - [searchGroupsInput](type-aliases/searchGroupsInput.md) - [SearchGroupsResponse](type-aliases/SearchGroupsResponse.md) - [SearchGroupsResponses](type-aliases/SearchGroupsResponses.md) - [searchIncidentsConsistency](type-aliases/searchIncidentsConsistency.md) - [SearchIncidentsData](type-aliases/SearchIncidentsData.md) - [SearchIncidentsError](type-aliases/SearchIncidentsError.md) - [SearchIncidentsErrors](type-aliases/SearchIncidentsErrors.md) - [searchIncidentsInput](type-aliases/searchIncidentsInput.md) - [SearchIncidentsResponse](type-aliases/SearchIncidentsResponse.md) - [SearchIncidentsResponses](type-aliases/SearchIncidentsResponses.md) - [searchJobsConsistency](type-aliases/searchJobsConsistency.md) - [SearchJobsData](type-aliases/SearchJobsData.md) - [SearchJobsError](type-aliases/SearchJobsError.md) - [SearchJobsErrors](type-aliases/SearchJobsErrors.md) - [searchJobsInput](type-aliases/searchJobsInput.md) - [SearchJobsResponse](type-aliases/SearchJobsResponse.md) - [SearchJobsResponses](type-aliases/SearchJobsResponses.md) - [searchMappingRuleConsistency](type-aliases/searchMappingRuleConsistency.md) - [SearchMappingRuleData](type-aliases/SearchMappingRuleData.md) - [SearchMappingRuleError](type-aliases/SearchMappingRuleError.md) - [SearchMappingRuleErrors](type-aliases/SearchMappingRuleErrors.md) - [searchMappingRuleInput](type-aliases/searchMappingRuleInput.md) - [SearchMappingRuleResponse](type-aliases/SearchMappingRuleResponse.md) - [SearchMappingRuleResponses](type-aliases/SearchMappingRuleResponses.md) - [searchMappingRulesForGroupConsistency](type-aliases/searchMappingRulesForGroupConsistency.md) - [SearchMappingRulesForGroupData](type-aliases/SearchMappingRulesForGroupData.md) - [SearchMappingRulesForGroupError](type-aliases/SearchMappingRulesForGroupError.md) - [SearchMappingRulesForGroupErrors](type-aliases/SearchMappingRulesForGroupErrors.md) - [searchMappingRulesForGroupInput](type-aliases/searchMappingRulesForGroupInput.md) - [SearchMappingRulesForGroupResponse](type-aliases/SearchMappingRulesForGroupResponse.md) - [SearchMappingRulesForGroupResponses](type-aliases/SearchMappingRulesForGroupResponses.md) - [searchMappingRulesForRoleConsistency](type-aliases/searchMappingRulesForRoleConsistency.md) - [SearchMappingRulesForRoleData](type-aliases/SearchMappingRulesForRoleData.md) - [SearchMappingRulesForRoleError](type-aliases/SearchMappingRulesForRoleError.md) - [SearchMappingRulesForRoleErrors](type-aliases/SearchMappingRulesForRoleErrors.md) - [searchMappingRulesForRoleInput](type-aliases/searchMappingRulesForRoleInput.md) - [SearchMappingRulesForRoleResponse](type-aliases/SearchMappingRulesForRoleResponse.md) - [SearchMappingRulesForRoleResponses](type-aliases/SearchMappingRulesForRoleResponses.md) - [searchMappingRulesForTenantConsistency](type-aliases/searchMappingRulesForTenantConsistency.md) - [SearchMappingRulesForTenantData](type-aliases/SearchMappingRulesForTenantData.md) - [searchMappingRulesForTenantInput](type-aliases/searchMappingRulesForTenantInput.md) - [SearchMappingRulesForTenantResponse](type-aliases/SearchMappingRulesForTenantResponse.md) - [SearchMappingRulesForTenantResponses](type-aliases/SearchMappingRulesForTenantResponses.md) - [searchMessageSubscriptionsConsistency](type-aliases/searchMessageSubscriptionsConsistency.md) - [SearchMessageSubscriptionsData](type-aliases/SearchMessageSubscriptionsData.md) - [SearchMessageSubscriptionsError](type-aliases/SearchMessageSubscriptionsError.md) - [SearchMessageSubscriptionsErrors](type-aliases/SearchMessageSubscriptionsErrors.md) - [searchMessageSubscriptionsInput](type-aliases/searchMessageSubscriptionsInput.md) - [SearchMessageSubscriptionsResponse](type-aliases/SearchMessageSubscriptionsResponse.md) - [SearchMessageSubscriptionsResponses](type-aliases/SearchMessageSubscriptionsResponses.md) - [searchProcessDefinitionsConsistency](type-aliases/searchProcessDefinitionsConsistency.md) - [SearchProcessDefinitionsData](type-aliases/SearchProcessDefinitionsData.md) - [SearchProcessDefinitionsError](type-aliases/SearchProcessDefinitionsError.md) - [SearchProcessDefinitionsErrors](type-aliases/SearchProcessDefinitionsErrors.md) - [searchProcessDefinitionsInput](type-aliases/searchProcessDefinitionsInput.md) - [SearchProcessDefinitionsResponse](type-aliases/SearchProcessDefinitionsResponse.md) - [SearchProcessDefinitionsResponses](type-aliases/SearchProcessDefinitionsResponses.md) - [searchProcessInstanceIncidentsConsistency](type-aliases/searchProcessInstanceIncidentsConsistency.md) - [SearchProcessInstanceIncidentsData](type-aliases/SearchProcessInstanceIncidentsData.md) - [SearchProcessInstanceIncidentsError](type-aliases/SearchProcessInstanceIncidentsError.md) - [SearchProcessInstanceIncidentsErrors](type-aliases/SearchProcessInstanceIncidentsErrors.md) - [searchProcessInstanceIncidentsInput](type-aliases/searchProcessInstanceIncidentsInput.md) - [SearchProcessInstanceIncidentsResponse](type-aliases/SearchProcessInstanceIncidentsResponse.md) - [SearchProcessInstanceIncidentsResponses](type-aliases/SearchProcessInstanceIncidentsResponses.md) - [searchProcessInstancesConsistency](type-aliases/searchProcessInstancesConsistency.md) - [SearchProcessInstancesData](type-aliases/SearchProcessInstancesData.md) - [SearchProcessInstancesError](type-aliases/SearchProcessInstancesError.md) - [SearchProcessInstancesErrors](type-aliases/SearchProcessInstancesErrors.md) - [searchProcessInstancesInput](type-aliases/searchProcessInstancesInput.md) - [SearchProcessInstancesResponse](type-aliases/SearchProcessInstancesResponse.md) - [SearchProcessInstancesResponses](type-aliases/SearchProcessInstancesResponses.md) - [SearchQueryPageRequest](type-aliases/SearchQueryPageRequest.md) - [SearchQueryPageResponse](type-aliases/SearchQueryPageResponse.md) - [SearchQueryRequest](type-aliases/SearchQueryRequest.md) - [SearchQueryResponse](type-aliases/SearchQueryResponse.md) - [searchResourcesConsistency](type-aliases/searchResourcesConsistency.md) - [SearchResourcesData](type-aliases/SearchResourcesData.md) - [SearchResourcesError](type-aliases/SearchResourcesError.md) - [SearchResourcesErrors](type-aliases/SearchResourcesErrors.md) - [searchResourcesInput](type-aliases/searchResourcesInput.md) - [SearchResourcesResponse](type-aliases/SearchResourcesResponse.md) - [SearchResourcesResponses](type-aliases/SearchResourcesResponses.md) - [searchRolesConsistency](type-aliases/searchRolesConsistency.md) - [SearchRolesData](type-aliases/SearchRolesData.md) - [SearchRolesError](type-aliases/SearchRolesError.md) - [SearchRolesErrors](type-aliases/SearchRolesErrors.md) - [searchRolesForGroupConsistency](type-aliases/searchRolesForGroupConsistency.md) - [SearchRolesForGroupData](type-aliases/SearchRolesForGroupData.md) - [SearchRolesForGroupError](type-aliases/SearchRolesForGroupError.md) - [SearchRolesForGroupErrors](type-aliases/SearchRolesForGroupErrors.md) - [searchRolesForGroupInput](type-aliases/searchRolesForGroupInput.md) - [SearchRolesForGroupResponse](type-aliases/SearchRolesForGroupResponse.md) - [SearchRolesForGroupResponses](type-aliases/SearchRolesForGroupResponses.md) - [searchRolesForTenantConsistency](type-aliases/searchRolesForTenantConsistency.md) - [SearchRolesForTenantData](type-aliases/SearchRolesForTenantData.md) - [searchRolesForTenantInput](type-aliases/searchRolesForTenantInput.md) - [SearchRolesForTenantResponse](type-aliases/SearchRolesForTenantResponse.md) - [SearchRolesForTenantResponses](type-aliases/SearchRolesForTenantResponses.md) - [searchRolesInput](type-aliases/searchRolesInput.md) - [SearchRolesResponse](type-aliases/SearchRolesResponse.md) - [SearchRolesResponses](type-aliases/SearchRolesResponses.md) - [searchTenantsConsistency](type-aliases/searchTenantsConsistency.md) - [SearchTenantsData](type-aliases/SearchTenantsData.md) - [SearchTenantsError](type-aliases/SearchTenantsError.md) - [SearchTenantsErrors](type-aliases/SearchTenantsErrors.md) - [searchTenantsInput](type-aliases/searchTenantsInput.md) - [SearchTenantsResponse](type-aliases/SearchTenantsResponse.md) - [SearchTenantsResponses](type-aliases/SearchTenantsResponses.md) - [searchUsersConsistency](type-aliases/searchUsersConsistency.md) - [SearchUsersData](type-aliases/SearchUsersData.md) - [SearchUsersError](type-aliases/SearchUsersError.md) - [SearchUsersErrors](type-aliases/SearchUsersErrors.md) - [searchUsersForGroupConsistency](type-aliases/searchUsersForGroupConsistency.md) - [SearchUsersForGroupData](type-aliases/SearchUsersForGroupData.md) - [SearchUsersForGroupError](type-aliases/SearchUsersForGroupError.md) - [SearchUsersForGroupErrors](type-aliases/SearchUsersForGroupErrors.md) - [searchUsersForGroupInput](type-aliases/searchUsersForGroupInput.md) - [SearchUsersForGroupResponse](type-aliases/SearchUsersForGroupResponse.md) - [SearchUsersForGroupResponses](type-aliases/SearchUsersForGroupResponses.md) - [searchUsersForRoleConsistency](type-aliases/searchUsersForRoleConsistency.md) - [SearchUsersForRoleData](type-aliases/SearchUsersForRoleData.md) - [SearchUsersForRoleError](type-aliases/SearchUsersForRoleError.md) - [SearchUsersForRoleErrors](type-aliases/SearchUsersForRoleErrors.md) - [searchUsersForRoleInput](type-aliases/searchUsersForRoleInput.md) - [SearchUsersForRoleResponse](type-aliases/SearchUsersForRoleResponse.md) - [SearchUsersForRoleResponses](type-aliases/SearchUsersForRoleResponses.md) - [searchUsersForTenantConsistency](type-aliases/searchUsersForTenantConsistency.md) - [SearchUsersForTenantData](type-aliases/SearchUsersForTenantData.md) - [searchUsersForTenantInput](type-aliases/searchUsersForTenantInput.md) - [SearchUsersForTenantResponse](type-aliases/SearchUsersForTenantResponse.md) - [SearchUsersForTenantResponses](type-aliases/SearchUsersForTenantResponses.md) - [searchUsersInput](type-aliases/searchUsersInput.md) - [SearchUsersResponse](type-aliases/SearchUsersResponse.md) - [SearchUsersResponses](type-aliases/SearchUsersResponses.md) - [searchUserTaskAuditLogsConsistency](type-aliases/searchUserTaskAuditLogsConsistency.md) - [SearchUserTaskAuditLogsData](type-aliases/SearchUserTaskAuditLogsData.md) - [SearchUserTaskAuditLogsError](type-aliases/SearchUserTaskAuditLogsError.md) - [SearchUserTaskAuditLogsErrors](type-aliases/SearchUserTaskAuditLogsErrors.md) - [searchUserTaskAuditLogsInput](type-aliases/searchUserTaskAuditLogsInput.md) - [SearchUserTaskAuditLogsResponse](type-aliases/SearchUserTaskAuditLogsResponse.md) - [SearchUserTaskAuditLogsResponses](type-aliases/SearchUserTaskAuditLogsResponses.md) - [searchUserTaskEffectiveVariablesConsistency](type-aliases/searchUserTaskEffectiveVariablesConsistency.md) - [SearchUserTaskEffectiveVariablesData](type-aliases/SearchUserTaskEffectiveVariablesData.md) - [SearchUserTaskEffectiveVariablesError](type-aliases/SearchUserTaskEffectiveVariablesError.md) - [SearchUserTaskEffectiveVariablesErrors](type-aliases/SearchUserTaskEffectiveVariablesErrors.md) - [searchUserTaskEffectiveVariablesInput](type-aliases/searchUserTaskEffectiveVariablesInput.md) - [SearchUserTaskEffectiveVariablesResponse](type-aliases/SearchUserTaskEffectiveVariablesResponse.md) - [SearchUserTaskEffectiveVariablesResponses](type-aliases/SearchUserTaskEffectiveVariablesResponses.md) - [searchUserTasksConsistency](type-aliases/searchUserTasksConsistency.md) - [SearchUserTasksData](type-aliases/SearchUserTasksData.md) - [SearchUserTasksError](type-aliases/SearchUserTasksError.md) - [SearchUserTasksErrors](type-aliases/SearchUserTasksErrors.md) - [searchUserTasksInput](type-aliases/searchUserTasksInput.md) - [SearchUserTasksResponse](type-aliases/SearchUserTasksResponse.md) - [SearchUserTasksResponses](type-aliases/SearchUserTasksResponses.md) - [searchUserTaskVariablesConsistency](type-aliases/searchUserTaskVariablesConsistency.md) - [SearchUserTaskVariablesData](type-aliases/SearchUserTaskVariablesData.md) - [SearchUserTaskVariablesError](type-aliases/SearchUserTaskVariablesError.md) - [SearchUserTaskVariablesErrors](type-aliases/SearchUserTaskVariablesErrors.md) - [searchUserTaskVariablesInput](type-aliases/searchUserTaskVariablesInput.md) - [SearchUserTaskVariablesResponse](type-aliases/SearchUserTaskVariablesResponse.md) - [SearchUserTaskVariablesResponses](type-aliases/SearchUserTaskVariablesResponses.md) - [searchVariablesConsistency](type-aliases/searchVariablesConsistency.md) - [SearchVariablesData](type-aliases/SearchVariablesData.md) - [SearchVariablesError](type-aliases/SearchVariablesError.md) - [SearchVariablesErrors](type-aliases/SearchVariablesErrors.md) - [searchVariablesInput](type-aliases/searchVariablesInput.md) - [SearchVariablesResponse](type-aliases/SearchVariablesResponse.md) - [SearchVariablesResponses](type-aliases/SearchVariablesResponses.md) - [SetVariableRequest](type-aliases/SetVariableRequest.md) - [SignalBroadcastRequest](type-aliases/SignalBroadcastRequest.md) - [SignalBroadcastResult](type-aliases/SignalBroadcastResult.md) - [SignalKey](type-aliases/SignalKey.md) - [SortOrderEnum](type-aliases/SortOrderEnum.md) - [SourceElementIdInstruction](type-aliases/SourceElementIdInstruction.md) - [SourceElementInstanceKeyInstruction](type-aliases/SourceElementInstanceKeyInstruction.md) - [SourceElementInstruction](type-aliases/SourceElementInstruction.md) - [StartCursor](type-aliases/StartCursor.md) - [StatusMetric](type-aliases/StatusMetric.md) - [StringFilterProperty](type-aliases/StringFilterProperty.md) - [SuspendBatchOperationData](type-aliases/SuspendBatchOperationData.md) - [SuspendBatchOperationError](type-aliases/SuspendBatchOperationError.md) - [SuspendBatchOperationErrors](type-aliases/SuspendBatchOperationErrors.md) - [suspendBatchOperationInput](type-aliases/suspendBatchOperationInput.md) - [SuspendBatchOperationResponse](type-aliases/SuspendBatchOperationResponse.md) - [SuspendBatchOperationResponses](type-aliases/SuspendBatchOperationResponses.md) - [SystemConfigurationResponse](type-aliases/SystemConfigurationResponse.md) - [Tag](type-aliases/Tag.md) - [TagSet](type-aliases/TagSet.md) - [TenantClientResult](type-aliases/TenantClientResult.md) - [TenantClientSearchQueryRequest](type-aliases/TenantClientSearchQueryRequest.md) - [TenantClientSearchQuerySortRequest](type-aliases/TenantClientSearchQuerySortRequest.md) - [TenantClientSearchResult](type-aliases/TenantClientSearchResult.md) - [TenantCreateRequest](type-aliases/TenantCreateRequest.md) - [TenantCreateResult](type-aliases/TenantCreateResult.md) - [TenantFilter](type-aliases/TenantFilter.md) - [TenantFilterEnum](type-aliases/TenantFilterEnum.md) - [TenantGroupResult](type-aliases/TenantGroupResult.md) - [TenantGroupSearchQueryRequest](type-aliases/TenantGroupSearchQueryRequest.md) - [TenantGroupSearchQuerySortRequest](type-aliases/TenantGroupSearchQuerySortRequest.md) - [TenantGroupSearchResult](type-aliases/TenantGroupSearchResult.md) - [TenantId](type-aliases/TenantId.md) - [TenantMappingRuleSearchResult](type-aliases/TenantMappingRuleSearchResult.md) - [TenantResult](type-aliases/TenantResult.md) - [TenantRoleSearchResult](type-aliases/TenantRoleSearchResult.md) - [TenantSearchQueryRequest](type-aliases/TenantSearchQueryRequest.md) - [TenantSearchQueryResult](type-aliases/TenantSearchQueryResult.md) - [TenantSearchQuerySortRequest](type-aliases/TenantSearchQuerySortRequest.md) - [TenantUpdateRequest](type-aliases/TenantUpdateRequest.md) - [TenantUpdateResult](type-aliases/TenantUpdateResult.md) - [TenantUserResult](type-aliases/TenantUserResult.md) - [TenantUserSearchQueryRequest](type-aliases/TenantUserSearchQueryRequest.md) - [TenantUserSearchQuerySortRequest](type-aliases/TenantUserSearchQuerySortRequest.md) - [TenantUserSearchResult](type-aliases/TenantUserSearchResult.md) - [ThreadedJob](type-aliases/ThreadedJob.md) - [ThreadedJobHandler](type-aliases/ThreadedJobHandler.md) - [ThrowJobErrorData](type-aliases/ThrowJobErrorData.md) - [ThrowJobErrorError](type-aliases/ThrowJobErrorError.md) - [ThrowJobErrorErrors](type-aliases/ThrowJobErrorErrors.md) - [throwJobErrorInput](type-aliases/throwJobErrorInput.md) - [ThrowJobErrorResponse](type-aliases/ThrowJobErrorResponse.md) - [ThrowJobErrorResponses](type-aliases/ThrowJobErrorResponses.md) - [TopologyResponse](type-aliases/TopologyResponse.md) - [UnassignClientFromGroupData](type-aliases/UnassignClientFromGroupData.md) - [UnassignClientFromGroupError](type-aliases/UnassignClientFromGroupError.md) - [UnassignClientFromGroupErrors](type-aliases/UnassignClientFromGroupErrors.md) - [unassignClientFromGroupInput](type-aliases/unassignClientFromGroupInput.md) - [UnassignClientFromGroupResponse](type-aliases/UnassignClientFromGroupResponse.md) - [UnassignClientFromGroupResponses](type-aliases/UnassignClientFromGroupResponses.md) - [UnassignClientFromTenantData](type-aliases/UnassignClientFromTenantData.md) - [UnassignClientFromTenantError](type-aliases/UnassignClientFromTenantError.md) - [UnassignClientFromTenantErrors](type-aliases/UnassignClientFromTenantErrors.md) - [unassignClientFromTenantInput](type-aliases/unassignClientFromTenantInput.md) - [UnassignClientFromTenantResponse](type-aliases/UnassignClientFromTenantResponse.md) - [UnassignClientFromTenantResponses](type-aliases/UnassignClientFromTenantResponses.md) - [UnassignGroupFromTenantData](type-aliases/UnassignGroupFromTenantData.md) - [UnassignGroupFromTenantError](type-aliases/UnassignGroupFromTenantError.md) - [UnassignGroupFromTenantErrors](type-aliases/UnassignGroupFromTenantErrors.md) - [unassignGroupFromTenantInput](type-aliases/unassignGroupFromTenantInput.md) - [UnassignGroupFromTenantResponse](type-aliases/UnassignGroupFromTenantResponse.md) - [UnassignGroupFromTenantResponses](type-aliases/UnassignGroupFromTenantResponses.md) - [UnassignMappingRuleFromGroupData](type-aliases/UnassignMappingRuleFromGroupData.md) - [UnassignMappingRuleFromGroupError](type-aliases/UnassignMappingRuleFromGroupError.md) - [UnassignMappingRuleFromGroupErrors](type-aliases/UnassignMappingRuleFromGroupErrors.md) - [unassignMappingRuleFromGroupInput](type-aliases/unassignMappingRuleFromGroupInput.md) - [UnassignMappingRuleFromGroupResponse](type-aliases/UnassignMappingRuleFromGroupResponse.md) - [UnassignMappingRuleFromGroupResponses](type-aliases/UnassignMappingRuleFromGroupResponses.md) - [UnassignMappingRuleFromTenantData](type-aliases/UnassignMappingRuleFromTenantData.md) - [UnassignMappingRuleFromTenantError](type-aliases/UnassignMappingRuleFromTenantError.md) - [UnassignMappingRuleFromTenantErrors](type-aliases/UnassignMappingRuleFromTenantErrors.md) - [unassignMappingRuleFromTenantInput](type-aliases/unassignMappingRuleFromTenantInput.md) - [UnassignMappingRuleFromTenantResponse](type-aliases/UnassignMappingRuleFromTenantResponse.md) - [UnassignMappingRuleFromTenantResponses](type-aliases/UnassignMappingRuleFromTenantResponses.md) - [UnassignRoleFromClientData](type-aliases/UnassignRoleFromClientData.md) - [UnassignRoleFromClientError](type-aliases/UnassignRoleFromClientError.md) - [UnassignRoleFromClientErrors](type-aliases/UnassignRoleFromClientErrors.md) - [unassignRoleFromClientInput](type-aliases/unassignRoleFromClientInput.md) - [UnassignRoleFromClientResponse](type-aliases/UnassignRoleFromClientResponse.md) - [UnassignRoleFromClientResponses](type-aliases/UnassignRoleFromClientResponses.md) - [UnassignRoleFromGroupData](type-aliases/UnassignRoleFromGroupData.md) - [UnassignRoleFromGroupError](type-aliases/UnassignRoleFromGroupError.md) - [UnassignRoleFromGroupErrors](type-aliases/UnassignRoleFromGroupErrors.md) - [unassignRoleFromGroupInput](type-aliases/unassignRoleFromGroupInput.md) - [UnassignRoleFromGroupResponse](type-aliases/UnassignRoleFromGroupResponse.md) - [UnassignRoleFromGroupResponses](type-aliases/UnassignRoleFromGroupResponses.md) - [UnassignRoleFromMappingRuleData](type-aliases/UnassignRoleFromMappingRuleData.md) - [UnassignRoleFromMappingRuleError](type-aliases/UnassignRoleFromMappingRuleError.md) - [UnassignRoleFromMappingRuleErrors](type-aliases/UnassignRoleFromMappingRuleErrors.md) - [unassignRoleFromMappingRuleInput](type-aliases/unassignRoleFromMappingRuleInput.md) - [UnassignRoleFromMappingRuleResponse](type-aliases/UnassignRoleFromMappingRuleResponse.md) - [UnassignRoleFromMappingRuleResponses](type-aliases/UnassignRoleFromMappingRuleResponses.md) - [UnassignRoleFromTenantData](type-aliases/UnassignRoleFromTenantData.md) - [UnassignRoleFromTenantError](type-aliases/UnassignRoleFromTenantError.md) - [UnassignRoleFromTenantErrors](type-aliases/UnassignRoleFromTenantErrors.md) - [unassignRoleFromTenantInput](type-aliases/unassignRoleFromTenantInput.md) - [UnassignRoleFromTenantResponse](type-aliases/UnassignRoleFromTenantResponse.md) - [UnassignRoleFromTenantResponses](type-aliases/UnassignRoleFromTenantResponses.md) - [UnassignRoleFromUserData](type-aliases/UnassignRoleFromUserData.md) - [UnassignRoleFromUserError](type-aliases/UnassignRoleFromUserError.md) - [UnassignRoleFromUserErrors](type-aliases/UnassignRoleFromUserErrors.md) - [unassignRoleFromUserInput](type-aliases/unassignRoleFromUserInput.md) - [UnassignRoleFromUserResponse](type-aliases/UnassignRoleFromUserResponse.md) - [UnassignRoleFromUserResponses](type-aliases/UnassignRoleFromUserResponses.md) - [UnassignUserFromGroupData](type-aliases/UnassignUserFromGroupData.md) - [UnassignUserFromGroupError](type-aliases/UnassignUserFromGroupError.md) - [UnassignUserFromGroupErrors](type-aliases/UnassignUserFromGroupErrors.md) - [unassignUserFromGroupInput](type-aliases/unassignUserFromGroupInput.md) - [UnassignUserFromGroupResponse](type-aliases/UnassignUserFromGroupResponse.md) - [UnassignUserFromGroupResponses](type-aliases/UnassignUserFromGroupResponses.md) - [UnassignUserFromTenantData](type-aliases/UnassignUserFromTenantData.md) - [UnassignUserFromTenantError](type-aliases/UnassignUserFromTenantError.md) - [UnassignUserFromTenantErrors](type-aliases/UnassignUserFromTenantErrors.md) - [unassignUserFromTenantInput](type-aliases/unassignUserFromTenantInput.md) - [UnassignUserFromTenantResponse](type-aliases/UnassignUserFromTenantResponse.md) - [UnassignUserFromTenantResponses](type-aliases/UnassignUserFromTenantResponses.md) - [UnassignUserTaskData](type-aliases/UnassignUserTaskData.md) - [UnassignUserTaskError](type-aliases/UnassignUserTaskError.md) - [UnassignUserTaskErrors](type-aliases/UnassignUserTaskErrors.md) - [unassignUserTaskInput](type-aliases/unassignUserTaskInput.md) - [UnassignUserTaskResponse](type-aliases/UnassignUserTaskResponse.md) - [UnassignUserTaskResponses](type-aliases/UnassignUserTaskResponses.md) - [UpdateAgentInstanceData](type-aliases/UpdateAgentInstanceData.md) - [UpdateAgentInstanceError](type-aliases/UpdateAgentInstanceError.md) - [UpdateAgentInstanceErrors](type-aliases/UpdateAgentInstanceErrors.md) - [updateAgentInstanceInput](type-aliases/updateAgentInstanceInput.md) - [UpdateAgentInstanceResponse](type-aliases/UpdateAgentInstanceResponse.md) - [UpdateAgentInstanceResponses](type-aliases/UpdateAgentInstanceResponses.md) - [UpdateAuthorizationData](type-aliases/UpdateAuthorizationData.md) - [UpdateAuthorizationError](type-aliases/UpdateAuthorizationError.md) - [UpdateAuthorizationErrors](type-aliases/UpdateAuthorizationErrors.md) - [updateAuthorizationInput](type-aliases/updateAuthorizationInput.md) - [UpdateAuthorizationResponse](type-aliases/UpdateAuthorizationResponse.md) - [UpdateAuthorizationResponses](type-aliases/UpdateAuthorizationResponses.md) - [UpdateClusterVariableRequest](type-aliases/UpdateClusterVariableRequest.md) - [UpdateGlobalClusterVariableData](type-aliases/UpdateGlobalClusterVariableData.md) - [UpdateGlobalClusterVariableError](type-aliases/UpdateGlobalClusterVariableError.md) - [UpdateGlobalClusterVariableErrors](type-aliases/UpdateGlobalClusterVariableErrors.md) - [updateGlobalClusterVariableInput](type-aliases/updateGlobalClusterVariableInput.md) - [UpdateGlobalClusterVariableResponse](type-aliases/UpdateGlobalClusterVariableResponse.md) - [UpdateGlobalClusterVariableResponses](type-aliases/UpdateGlobalClusterVariableResponses.md) - [UpdateGlobalTaskListenerData](type-aliases/UpdateGlobalTaskListenerData.md) - [UpdateGlobalTaskListenerError](type-aliases/UpdateGlobalTaskListenerError.md) - [UpdateGlobalTaskListenerErrors](type-aliases/UpdateGlobalTaskListenerErrors.md) - [updateGlobalTaskListenerInput](type-aliases/updateGlobalTaskListenerInput.md) - [UpdateGlobalTaskListenerRequest](type-aliases/UpdateGlobalTaskListenerRequest.md) - [UpdateGlobalTaskListenerResponse](type-aliases/UpdateGlobalTaskListenerResponse.md) - [UpdateGlobalTaskListenerResponses](type-aliases/UpdateGlobalTaskListenerResponses.md) - [UpdateGroupData](type-aliases/UpdateGroupData.md) - [UpdateGroupError](type-aliases/UpdateGroupError.md) - [UpdateGroupErrors](type-aliases/UpdateGroupErrors.md) - [updateGroupInput](type-aliases/updateGroupInput.md) - [UpdateGroupResponse](type-aliases/UpdateGroupResponse.md) - [UpdateGroupResponses](type-aliases/UpdateGroupResponses.md) - [UpdateJobData](type-aliases/UpdateJobData.md) - [UpdateJobError](type-aliases/UpdateJobError.md) - [UpdateJobErrors](type-aliases/UpdateJobErrors.md) - [updateJobInput](type-aliases/updateJobInput.md) - [UpdateJobResponse](type-aliases/UpdateJobResponse.md) - [UpdateJobResponses](type-aliases/UpdateJobResponses.md) - [UpdateMappingRuleData](type-aliases/UpdateMappingRuleData.md) - [UpdateMappingRuleError](type-aliases/UpdateMappingRuleError.md) - [UpdateMappingRuleErrors](type-aliases/UpdateMappingRuleErrors.md) - [updateMappingRuleInput](type-aliases/updateMappingRuleInput.md) - [UpdateMappingRuleResponse](type-aliases/UpdateMappingRuleResponse.md) - [UpdateMappingRuleResponses](type-aliases/UpdateMappingRuleResponses.md) - [UpdateRoleData](type-aliases/UpdateRoleData.md) - [UpdateRoleError](type-aliases/UpdateRoleError.md) - [UpdateRoleErrors](type-aliases/UpdateRoleErrors.md) - [updateRoleInput](type-aliases/updateRoleInput.md) - [UpdateRoleResponse](type-aliases/UpdateRoleResponse.md) - [UpdateRoleResponses](type-aliases/UpdateRoleResponses.md) - [UpdateTenantClusterVariableData](type-aliases/UpdateTenantClusterVariableData.md) - [UpdateTenantClusterVariableError](type-aliases/UpdateTenantClusterVariableError.md) - [UpdateTenantClusterVariableErrors](type-aliases/UpdateTenantClusterVariableErrors.md) - [updateTenantClusterVariableInput](type-aliases/updateTenantClusterVariableInput.md) - [UpdateTenantClusterVariableResponse](type-aliases/UpdateTenantClusterVariableResponse.md) - [UpdateTenantClusterVariableResponses](type-aliases/UpdateTenantClusterVariableResponses.md) - [UpdateTenantData](type-aliases/UpdateTenantData.md) - [UpdateTenantError](type-aliases/UpdateTenantError.md) - [UpdateTenantErrors](type-aliases/UpdateTenantErrors.md) - [updateTenantInput](type-aliases/updateTenantInput.md) - [UpdateTenantResponse](type-aliases/UpdateTenantResponse.md) - [UpdateTenantResponses](type-aliases/UpdateTenantResponses.md) - [UpdateUserData](type-aliases/UpdateUserData.md) - [UpdateUserError](type-aliases/UpdateUserError.md) - [UpdateUserErrors](type-aliases/UpdateUserErrors.md) - [updateUserInput](type-aliases/updateUserInput.md) - [UpdateUserResponse](type-aliases/UpdateUserResponse.md) - [UpdateUserResponses](type-aliases/UpdateUserResponses.md) - [UpdateUserTaskData](type-aliases/UpdateUserTaskData.md) - [UpdateUserTaskError](type-aliases/UpdateUserTaskError.md) - [UpdateUserTaskErrors](type-aliases/UpdateUserTaskErrors.md) - [updateUserTaskInput](type-aliases/updateUserTaskInput.md) - [UpdateUserTaskResponse](type-aliases/UpdateUserTaskResponse.md) - [UpdateUserTaskResponses](type-aliases/UpdateUserTaskResponses.md) - [UsageMetricsResponse](type-aliases/UsageMetricsResponse.md) - [UsageMetricsResponseItem](type-aliases/UsageMetricsResponseItem.md) - [UserCreateResult](type-aliases/UserCreateResult.md) - [UserFilter](type-aliases/UserFilter.md) - [Username](type-aliases/Username.md) - [UserRequest](type-aliases/UserRequest.md) - [UserResult](type-aliases/UserResult.md) - [UserSearchQueryRequest](type-aliases/UserSearchQueryRequest.md) - [UserSearchQuerySortRequest](type-aliases/UserSearchQuerySortRequest.md) - [UserSearchResult](type-aliases/UserSearchResult.md) - [UserTaskAssignmentRequest](type-aliases/UserTaskAssignmentRequest.md) - [UserTaskAuditLogFilter](type-aliases/UserTaskAuditLogFilter.md) - [UserTaskAuditLogSearchQueryRequest](type-aliases/UserTaskAuditLogSearchQueryRequest.md) - [UserTaskCompletionRequest](type-aliases/UserTaskCompletionRequest.md) - [UserTaskEffectiveVariableSearchQueryRequest](type-aliases/UserTaskEffectiveVariableSearchQueryRequest.md) - [UserTaskFilter](type-aliases/UserTaskFilter.md) - [UserTaskKey](type-aliases/UserTaskKey.md) - [UserTaskProperties](type-aliases/UserTaskProperties.md) - [UserTaskResult](type-aliases/UserTaskResult.md) - [UserTaskSearchQuery](type-aliases/UserTaskSearchQuery.md) - [UserTaskSearchQueryResult](type-aliases/UserTaskSearchQueryResult.md) - [UserTaskSearchQuerySortRequest](type-aliases/UserTaskSearchQuerySortRequest.md) - [UserTaskStateEnum](type-aliases/UserTaskStateEnum.md) - [UserTaskStateExactMatch](type-aliases/UserTaskStateExactMatch.md) - [UserTaskStateFilterProperty](type-aliases/UserTaskStateFilterProperty.md) - [UserTaskUpdateRequest](type-aliases/UserTaskUpdateRequest.md) - [UserTaskVariableFilter](type-aliases/UserTaskVariableFilter.md) - [UserTaskVariableSearchQueryRequest](type-aliases/UserTaskVariableSearchQueryRequest.md) - [UserTaskVariableSearchQuerySortRequest](type-aliases/UserTaskVariableSearchQuerySortRequest.md) - [UserUpdateRequest](type-aliases/UserUpdateRequest.md) - [UserUpdateResult](type-aliases/UserUpdateResult.md) - [UseSourceParentKeyInstruction](type-aliases/UseSourceParentKeyInstruction.md) - [ValidationMode](type-aliases/ValidationMode.md) - [VariableFilter](type-aliases/VariableFilter.md) - [VariableKey](type-aliases/VariableKey.md) - [VariableKeyExactMatch](type-aliases/VariableKeyExactMatch.md) - [VariableKeyFilterProperty](type-aliases/VariableKeyFilterProperty.md) - [VariableResult](type-aliases/VariableResult.md) - [VariableResultBase](type-aliases/VariableResultBase.md) - [VariableSearchQuery](type-aliases/VariableSearchQuery.md) - [VariableSearchQueryResult](type-aliases/VariableSearchQueryResult.md) - [VariableSearchQuerySortRequest](type-aliases/VariableSearchQuerySortRequest.md) - [VariableSearchResult](type-aliases/VariableSearchResult.md) - [VariableValueFilterProperty](type-aliases/VariableValueFilterProperty.md) - [WebappComponent](type-aliases/WebappComponent.md) ## Variables - [AgentInstanceStatusEnum](variables/AgentInstanceStatusEnum.md) - [AuditLogActorTypeEnum](variables/AuditLogActorTypeEnum.md) - [AuditLogCategoryEnum](variables/AuditLogCategoryEnum.md) - [AuditLogEntityTypeEnum](variables/AuditLogEntityTypeEnum.md) - [AuditLogOperationTypeEnum](variables/AuditLogOperationTypeEnum.md) - [AuditLogResultEnum](variables/AuditLogResultEnum.md) - [BatchOperationItemStateEnum](variables/BatchOperationItemStateEnum.md) - [BatchOperationStateEnum](variables/BatchOperationStateEnum.md) - [BatchOperationTypeEnum](variables/BatchOperationTypeEnum.md) - [ClusterVariableScopeEnum](variables/ClusterVariableScopeEnum.md) - [DecisionDefinitionTypeEnum](variables/DecisionDefinitionTypeEnum.md) - [DecisionInstanceStateEnum](variables/DecisionInstanceStateEnum.md) - [ElementInstanceStateEnum](variables/ElementInstanceStateEnum.md) - [GlobalListenerSourceEnum](variables/GlobalListenerSourceEnum.md) - [GlobalTaskListenerEventTypeEnum](variables/GlobalTaskListenerEventTypeEnum.md) - [IncidentErrorTypeEnum](variables/IncidentErrorTypeEnum.md) - [IncidentStateEnum](variables/IncidentStateEnum.md) - [JobKindEnum](variables/JobKindEnum.md) - [JobListenerEventTypeEnum](variables/JobListenerEventTypeEnum.md) - [JobStateEnum](variables/JobStateEnum.md) - [MessageSubscriptionStateEnum](variables/MessageSubscriptionStateEnum.md) - [MessageSubscriptionTypeEnum](variables/MessageSubscriptionTypeEnum.md) - [OwnerTypeEnum](variables/OwnerTypeEnum.md) - [PermissionTypeEnum](variables/PermissionTypeEnum.md) - [ProcessInstanceStateEnum](variables/ProcessInstanceStateEnum.md) - [ResourceTypeEnum](variables/ResourceTypeEnum.md) - [SortOrderEnum](variables/SortOrderEnum.md) - [SPEC_HASH](variables/SPEC_HASH.md) - [TenantFilterEnum](variables/TenantFilterEnum.md) - [UserTaskStateEnum](variables/UserTaskStateEnum.md) ## Functions - [assertConstraint](functions/assertConstraint.md) - [createCamundaClient](functions/createCamundaClient.md) - [createCamundaClientLoose](functions/createCamundaClientLoose.md) - [createCamundaFpClient](functions/createCamundaFpClient.md) - [createCamundaResultClient](functions/createCamundaResultClient.md) - [isErr](functions/isErr.md) - [isLeft](functions/isLeft.md) - [isOk](functions/isOk.md) - [isRight](functions/isRight.md) - [isSdkError](functions/isSdkError.md) ## References ### default Renames and re-exports [createCamundaClient](functions/createCamundaClient.md) --- ### JobActionReceiptSymbol Renames and re-exports [JobActionReceipt](type-aliases/JobActionReceipt.md) --- ## Interface: CamundaConfig ## Properties ### \_\_raw ```ts __raw: Record; ``` --- ### auth ```ts auth: object; ``` #### basic? ```ts optional basic?: object; ``` ##### basic.password? ```ts optional password?: string; ``` ##### basic.username? ```ts optional username?: string; ``` #### strategy ```ts strategy: AuthStrategy; ``` --- ### backpressure ```ts backpressure: object; ``` #### decayQuietMs ```ts decayQuietMs: number; ``` #### enabled ```ts enabled: boolean; ``` #### floor ```ts floor: number; ``` #### healthyRecoveryMultiplier ```ts healthyRecoveryMultiplier: number; ``` #### initialMax ```ts initialMax: number; ``` #### maxWaiters ```ts maxWaiters: number; ``` #### observeOnly ```ts observeOnly: boolean; ``` #### profile ```ts profile: string; ``` #### recoveryIntervalMs ```ts recoveryIntervalMs: number; ``` #### recoveryStep ```ts recoveryStep: number; ``` #### severeFactor ```ts severeFactor: number; ``` #### severeThreshold ```ts severeThreshold: number; ``` #### softFactor ```ts softFactor: number; ``` #### unlimitedAfterHealthyMs ```ts unlimitedAfterHealthyMs: number; ``` --- ### defaultTenantId ```ts defaultTenantId: string; ``` --- ### eventual? ```ts optional eventual?: object; ``` #### pollDefaultMs ```ts pollDefaultMs: number; ``` --- ### httpRetry ```ts httpRetry: object; ``` #### baseDelayMs ```ts baseDelayMs: number; ``` #### maxAttempts ```ts maxAttempts: number; ``` #### maxDelayMs ```ts maxDelayMs: number; ``` --- ### logLevel ```ts logLevel: "trace" | "error" | "silent" | "warn" | "info" | "debug"; ``` --- ### mtls? ```ts optional mtls?: object; ``` #### ca? ```ts optional ca?: string; ``` #### caPath? ```ts optional caPath?: string; ``` #### cert? ```ts optional cert?: string; ``` #### certPath? ```ts optional certPath?: string; ``` #### key? ```ts optional key?: string; ``` #### keyPassphrase? ```ts optional keyPassphrase?: string; ``` #### keyPath? ```ts optional keyPath?: string; ``` --- ### oauth ```ts oauth: object; ``` #### cacheDir? ```ts optional cacheDir?: string; ``` #### clientId? ```ts optional clientId?: string; ``` #### clientSecret? ```ts optional clientSecret?: string; ``` #### grantType ```ts grantType: string; ``` #### oauthUrl ```ts oauthUrl: string; ``` #### retry ```ts retry: object; ``` ##### retry.baseDelayMs ```ts baseDelayMs: number; ``` ##### retry.max ```ts max: number; ``` #### scope? ```ts optional scope?: string; ``` #### timeoutMs ```ts timeoutMs: number; ``` --- ### restAddress ```ts restAddress: string; ``` --- ### supportLog? ```ts optional supportLog?: object; ``` #### enabled ```ts enabled: boolean; ``` #### filePath ```ts filePath: string; ``` --- ### telemetry? ```ts optional telemetry?: object; ``` #### correlation ```ts correlation: boolean; ``` #### log ```ts log: boolean; ``` --- ### tokenAudience ```ts tokenAudience: string; ``` --- ### validation ```ts validation: object; ``` #### raw ```ts raw: string; ``` #### req ```ts req: ValidationMode; ``` #### res ```ts res: ValidationMode; ``` --- ### workerDefaults? ```ts optional workerDefaults?: object; ``` #### jobTimeoutMs? ```ts optional jobTimeoutMs?: number; ``` #### maxParallelJobs? ```ts optional maxParallelJobs?: number; ``` #### pollTimeoutMs? ```ts optional pollTimeoutMs?: number; ``` #### startupJitterMaxSeconds? ```ts optional startupJitterMaxSeconds?: number; ``` #### workerName? ```ts optional workerName?: string; ``` --- ## Interface: CamundaOptions ## Properties ### config? ```ts optional config?: Partial<{ CAMUNDA_AUTH_STRATEGY: "OAUTH" | "NONE" | "BASIC"; CAMUNDA_BASIC_AUTH_PASSWORD: string; CAMUNDA_BASIC_AUTH_USERNAME: string; CAMUNDA_CLIENT_ID: string; CAMUNDA_CLIENT_SECRET: string; CAMUNDA_DEFAULT_TENANT_ID: string; CAMUNDA_MTLS_CA: string; CAMUNDA_MTLS_CA_PATH: string; CAMUNDA_MTLS_CERT: string; CAMUNDA_MTLS_CERT_PATH: string; CAMUNDA_MTLS_KEY: string; CAMUNDA_MTLS_KEY_PASSPHRASE: string; CAMUNDA_MTLS_KEY_PATH: string; CAMUNDA_OAUTH_CACHE_DIR: string; CAMUNDA_OAUTH_GRANT_TYPE: string; CAMUNDA_OAUTH_RETRY_BASE_DELAY_MS: number; CAMUNDA_OAUTH_RETRY_MAX: number; CAMUNDA_OAUTH_SCOPE: string; CAMUNDA_OAUTH_TIMEOUT_MS: number; CAMUNDA_OAUTH_URL: string; CAMUNDA_REST_ADDRESS: string; CAMUNDA_SDK_BACKPRESSURE_DECAY_QUIET_MS: number; CAMUNDA_SDK_BACKPRESSURE_FLOOR: number; CAMUNDA_SDK_BACKPRESSURE_HEALTHY_RECOVERY_MULTIPLIER: number; CAMUNDA_SDK_BACKPRESSURE_INITIAL_MAX: number; CAMUNDA_SDK_BACKPRESSURE_MAX_WAITERS: number; CAMUNDA_SDK_BACKPRESSURE_PROFILE: "BALANCED" | "CONSERVATIVE" | "AGGRESSIVE" | "LEGACY"; CAMUNDA_SDK_BACKPRESSURE_RECOVERY_INTERVAL_MS: number; CAMUNDA_SDK_BACKPRESSURE_RECOVERY_STEP: number; CAMUNDA_SDK_BACKPRESSURE_SEVERE_FACTOR: number; CAMUNDA_SDK_BACKPRESSURE_SEVERE_THRESHOLD: number; CAMUNDA_SDK_BACKPRESSURE_SOFT_FACTOR: number; CAMUNDA_SDK_BACKPRESSURE_UNLIMITED_AFTER_HEALTHY_MS: number; CAMUNDA_SDK_EVENTUAL_POLL_DEFAULT_MS: number; CAMUNDA_SDK_HTTP_RETRY_BASE_DELAY_MS: number; CAMUNDA_SDK_HTTP_RETRY_MAX_ATTEMPTS: number; CAMUNDA_SDK_HTTP_RETRY_MAX_DELAY_MS: number; CAMUNDA_SDK_LOG_LEVEL: "trace" | "error" | "silent" | "warn" | "info" | "debug" | "silly"; CAMUNDA_SDK_TELEMETRY_CORRELATION: boolean; CAMUNDA_SDK_TELEMETRY_LOG: boolean; CAMUNDA_SDK_VALIDATION: string; CAMUNDA_SUPPORT_LOG_ENABLED: boolean; CAMUNDA_SUPPORT_LOG_FILE_PATH: string; CAMUNDA_SUPPORT_LOGGER: boolean; CAMUNDA_TOKEN_AUDIENCE: string; CAMUNDA_WORKER_MAX_CONCURRENT_JOBS: number; CAMUNDA_WORKER_NAME: string; CAMUNDA_WORKER_REQUEST_TIMEOUT: number; CAMUNDA_WORKER_STARTUP_JITTER_MAX_SECONDS: number; CAMUNDA_WORKER_TIMEOUT: number; }>; ``` --- ### env? ```ts optional env?: Record; ``` --- ### fetch? ```ts optional fetch?: (input, init?) => Promise; ``` #### Parameters ##### input `RequestInfo` \| `URL` ##### init? `RequestInit` #### Returns `Promise`\<`Response`\> --- ### log? ```ts optional log?: object; ``` #### level? ```ts optional level?: LogLevel; ``` #### transport? ```ts optional transport?: LogTransport; ``` --- ### supportLogger? ```ts optional supportLogger?: SupportLogger; ``` --- ### telemetry? ```ts optional telemetry?: object; ``` #### correlation? ```ts optional correlation?: boolean; ``` #### hooks? ```ts optional hooks?: TelemetryHooks; ``` #### mirrorToLog? ```ts optional mirrorToLog?: boolean; ``` --- ### throwOnError? ```ts optional throwOnError?: boolean; ``` --- ## Interface: CancelablePromise # Interface: CancelablePromise\ ## Extends - `Promise`\<`T`\> ## Type Parameters ### T `T` ## Methods ### cancel() ```ts cancel(): void; ``` #### Returns `void` --- ## Interface: CreateLoggerOptions ## Properties ### level? ```ts optional level?: LogLevel; ``` --- ### scope? ```ts optional scope?: string; ``` --- ### transport? ```ts optional transport?: LogTransport; ``` --- ## Interface: EnrichedActivatedJob Enriched job type with convenience methods. ## Extends - `ActivatedJobResult` ## Properties ### acknowledged? ```ts optional acknowledged?: boolean; ``` Set true once any acknowledgement method is invoked. --- ### customHeaders ```ts customHeaders: object; ``` A set of custom headers defined during modelling; returned as a serialized JSON document. #### Index Signature ```ts [key: string]: unknown ``` #### Inherited from ```ts ActivatedJobResult.customHeaders; ``` --- ### deadline ```ts deadline: number; ``` When the job can be activated again, sent as a UNIX epoch timestamp. #### Inherited from ```ts ActivatedJobResult.deadline; ``` --- ### elementId ```ts elementId: ElementId; ``` The associated task element ID. #### Inherited from ```ts ActivatedJobResult.elementId; ``` --- ### elementInstanceKey ```ts elementInstanceKey: ElementInstanceKey; ``` The element instance key of the task. #### Inherited from ```ts ActivatedJobResult.elementInstanceKey; ``` --- ### jobKey ```ts jobKey: JobKey; ``` The key, a unique identifier for the job. #### Inherited from ```ts ActivatedJobResult.jobKey; ``` --- ### kind ```ts kind: JobKindEnum; ``` #### Inherited from ```ts ActivatedJobResult.kind; ``` --- ### listenerEventType ```ts listenerEventType: JobListenerEventTypeEnum; ``` #### Inherited from ```ts ActivatedJobResult.listenerEventType; ``` --- ### log ```ts log: Logger; ``` --- ### modifyJobTimeout ```ts modifyJobTimeout: (__namedParameters) => Promise; ``` Extend the timeout for the job by setting a new timeout #### Parameters ##### \_\_namedParameters ###### newTimeoutMs `number` #### Returns `Promise`\<`void`\> --- ### modifyRetries ```ts modifyRetries: (__namedParameters) => Promise; ``` #### Parameters ##### \_\_namedParameters ###### retries `number` #### Returns `Promise`\<`void`\> --- ### processDefinitionId ```ts processDefinitionId: ProcessDefinitionId; ``` The bpmn process ID of the job's process definition. #### Inherited from ```ts ActivatedJobResult.processDefinitionId; ``` --- ### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` The key of the job's process definition. #### Inherited from ```ts ActivatedJobResult.processDefinitionKey; ``` --- ### processDefinitionVersion ```ts processDefinitionVersion: number; ``` The version of the job's process definition. #### Inherited from ```ts ActivatedJobResult.processDefinitionVersion; ``` --- ### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The job's process instance key. #### Inherited from ```ts ActivatedJobResult.processInstanceKey; ``` --- ### retries ```ts retries: number; ``` The amount of retries left to this job (should always be positive). #### Inherited from ```ts ActivatedJobResult.retries; ``` --- ### rootProcessInstanceKey ```ts rootProcessInstanceKey: ProcessInstanceKey | null; ``` The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. #### Inherited from ```ts ActivatedJobResult.rootProcessInstanceKey; ``` --- ### tags ```ts tags: TagSet; ``` #### Inherited from ```ts ActivatedJobResult.tags; ``` --- ### tenantId ```ts tenantId: TenantId; ``` The ID of the tenant that owns the job. #### Inherited from ```ts ActivatedJobResult.tenantId; ``` --- ### type ```ts type: string; ``` The type of the job (should match what was requested). #### Inherited from ```ts ActivatedJobResult.type; ``` --- ### userTask ```ts userTask: UserTaskProperties | null; ``` User task properties, if the job is a user task. This is `null` if the job is not a user task. #### Inherited from ```ts ActivatedJobResult.userTask; ``` --- ### variables ```ts variables: object; ``` All variables visible to the task scope, computed at activation time. #### Index Signature ```ts [key: string]: unknown ``` #### Inherited from ```ts ActivatedJobResult.variables; ``` --- ### worker ```ts worker: string; ``` The name of the worker which activated this job. #### Inherited from ```ts ActivatedJobResult.worker; ``` ## Methods ### cancelWorkflow() ```ts cancelWorkflow(): Promise<"JOB_ACTION_RECEIPT">; ``` #### Returns `Promise`\<`"JOB_ACTION_RECEIPT"`\> --- ### complete() ```ts complete(variables?, result?): Promise<"JOB_ACTION_RECEIPT">; ``` #### Parameters ##### variables? ##### result? [`JobResult`](../type-aliases/JobResult.md) #### Returns `Promise`\<`"JOB_ACTION_RECEIPT"`\> --- ### error() ```ts error(error): Promise<"JOB_ACTION_RECEIPT">; ``` #### Parameters ##### error [`JobErrorRequest`](../type-aliases/JobErrorRequest.md) #### Returns `Promise`\<`"JOB_ACTION_RECEIPT"`\> --- ### fail() ```ts fail(body): Promise<"JOB_ACTION_RECEIPT">; ``` #### Parameters ##### body `any` #### Returns `Promise`\<`"JOB_ACTION_RECEIPT"`\> --- ### ignore() ```ts ignore(): Promise<"JOB_ACTION_RECEIPT">; ``` #### Returns `Promise`\<`"JOB_ACTION_RECEIPT"`\> --- ## Interface: ExtendedDeploymentResult Extended deployment result with typed buckets for direct access to deployed artifacts. ## Extends - `_DataOf`\<_typeof_ `Sdk.createDeployment`\> ## Properties ### decisionRequirements ```ts decisionRequirements: DeploymentDecisionRequirementsResult[]; ``` --- ### decisions ```ts decisions: DeploymentDecisionResult[]; ``` --- ### deploymentKey ```ts deploymentKey: DeploymentKey; ``` The unique key identifying the deployment. #### Inherited from ```ts _DataOf.deploymentKey; ``` --- ### deployments ```ts deployments: DeploymentMetadataResult[]; ``` Items deployed by the request. #### Inherited from ```ts _DataOf.deployments; ``` --- ### forms ```ts forms: DeploymentFormResult[]; ``` --- ### processes ```ts processes: DeploymentProcessResult[]; ``` --- ### resources ```ts resources: DeploymentResourceResult[]; ``` --- ### tenantId ```ts tenantId: TenantId; ``` The tenant ID associated with the deployment. #### Inherited from ```ts _DataOf.tenantId; ``` --- ## Interface: HttpRetryPolicy ## Properties ### baseDelayMs ```ts baseDelayMs: number; ``` --- ### maxAttempts ```ts maxAttempts: number; ``` --- ### maxDelayMs ```ts maxDelayMs: number; ``` --- ## Interface: JobWorker ## Accessors ### activeJobs #### Get Signature ```ts get activeJobs(): number; ``` ##### Returns `number` --- ### name #### Get Signature ```ts get name(): string; ``` ##### Returns `string` --- ### stopped #### Get Signature ```ts get stopped(): boolean; ``` ##### Returns `boolean` ## Methods ### start() ```ts start(): void; ``` #### Returns `void` --- ### stop() ```ts stop(): void; ``` #### Returns `void` --- ### stopGracefully() ```ts stopGracefully(opts?): Promise<{ remainingJobs: number; timedOut: boolean; }>; ``` Gracefully stop the worker: prevent new polls, allow any in-flight activation to finish without cancellation, and wait for currently active jobs to drain (be acknowledged) up to waitUpToMs. If timeout is reached, falls back to hard stop logic (cancels activation if still pending). #### Parameters ##### opts? ###### checkIntervalMs? `number` ###### waitUpToMs? `number` #### Returns `Promise`\<\{ `remainingJobs`: `number`; `timedOut`: `boolean`; \}\> --- ## Interface: JobWorkerConfig # Interface: JobWorkerConfig\ ## Type Parameters ### In `In` _extends_ `z.ZodTypeAny` = `any` ### Out `Out` _extends_ `z.ZodTypeAny` = `any` ### Headers `Headers` _extends_ `z.ZodTypeAny` = `any` ## Properties ### autoStart? ```ts optional autoStart?: boolean; ``` Immediately start polling for work - default `true` --- ### customHeadersSchema? ```ts optional customHeadersSchema?: Headers; ``` Zod schema for custom headers in the activated job --- ### fetchVariables? ```ts optional fetchVariables?: In extends ZodType> ? Extract, string>[] : string[]; ``` Optional list of variable names to fetch during activation --- ### inputSchema? ```ts optional inputSchema?: In; ``` Zod schema for variables in the activated job --- ### jobHandler ```ts jobHandler: (job) => "JOB_ACTION_RECEIPT" | Promise<"JOB_ACTION_RECEIPT">; ``` #### Parameters ##### job [`Job`](../type-aliases/Job.md)\<`In`, `Headers`\> #### Returns `"JOB_ACTION_RECEIPT"` \| `Promise`\<`"JOB_ACTION_RECEIPT"`\> --- ### jobTimeoutMs? ```ts optional jobTimeoutMs?: number; ``` Job activation timeout in ms — default `60000`. Overridden by CAMUNDA_WORKER_TIMEOUT env var. --- ### jobType ```ts jobType: string; ``` Zeebe job type --- ### ~~maxBackoffTimeMs?~~ ```ts optional maxBackoffTimeMs?: number; ``` #### Deprecated Not used; pacing handled by long polling + client backpressure. Present only for migration compatibility. --- ### maxParallelJobs? ```ts optional maxParallelJobs?: number; ``` Concurrency limit — default `10`. Overridden by CAMUNDA_WORKER_MAX_CONCURRENT_JOBS env var. --- ### outputSchema? ```ts optional outputSchema?: Out; ``` Zod schema for variables in the complete command --- ### pollIntervalMs? ```ts optional pollIntervalMs?: number; ``` Backoff between polls - default 1ms --- ### pollTimeoutMs? ```ts optional pollTimeoutMs?: number; ``` The request will be completed when at least one job is activated or after the requestTimeout. If the requestTimeout = 0, the request will be completed after a default configured timeout in the broker. To immediately complete the request when no job is activated set the requestTimeout to a negative value --- ### startupJitterMaxSeconds? ```ts optional startupJitterMaxSeconds?: number; ``` Maximum random delay (in seconds) before the worker starts polling. When multiple application instances restart simultaneously, this spreads out initial activation requests to avoid saturating the server. `0` (the default) means no delay. --- ### validateSchemas? ```ts optional validateSchemas?: boolean; ``` Validate any provided input, output, customheader schema default: false --- ### workerName? ```ts optional workerName?: string; ``` Optional explicit name --- ## Interface: OperationOptions Per-call options for individual SDK method invocations. ## Properties ### retry? ```ts optional retry?: false | Partial; ``` Override retry behaviour for this call. - Pass `false` to disable retry entirely (single attempt). - Pass a partial policy to override specific fields (merged with global config). --- ## Interface: SupportLogger ## Methods ### log() ```ts log(message, addTimestamp?): void; ``` #### Parameters ##### message `string` \| `number` \| `boolean` \| `object` ##### addTimestamp? `boolean` #### Returns `void` --- ## Interface: TelemetryHooks ## Methods ### afterResponse()? ```ts optional afterResponse(e): void; ``` #### Parameters ##### e `TelemetryHttpEndEvent` #### Returns `void` --- ### authError()? ```ts optional authError(e): void; ``` #### Parameters ##### e `TelemetryAuthErrorEvent` #### Returns `void` --- ### authStart()? ```ts optional authStart(e): void; ``` #### Parameters ##### e `TelemetryAuthStartEvent` #### Returns `void` --- ### authSuccess()? ```ts optional authSuccess(e): void; ``` #### Parameters ##### e `TelemetryAuthSuccessEvent` #### Returns `void` --- ### beforeRequest()? ```ts optional beforeRequest(e): void; ``` #### Parameters ##### e `TelemetryHttpStartEvent` #### Returns `void` --- ### requestError()? ```ts optional requestError(e): void; ``` #### Parameters ##### e `TelemetryHttpErrorEvent` #### Returns `void` --- ### retry()? ```ts optional retry(e): void; ``` #### Parameters ##### e `TelemetryRetryEvent` #### Returns `void` --- ## Interface: ThreadPool ## Accessors ### busyCount #### Get Signature ```ts get busyCount(): number; ``` Number of threads currently processing a job. ##### Returns `number` --- ### idleCount #### Get Signature ```ts get idleCount(): number; ``` Number of threads that are ready and idle. ##### Returns `number` --- ### onThreadReady #### Set Signature ```ts set onThreadReady(cb): void; ``` Register a callback invoked whenever a thread becomes ready or idle. ##### Parameters ###### cb (() => `void`) \| `undefined` ##### Returns `void` --- ### ready #### Get Signature ```ts get ready(): Promise; ``` Resolves when all threads have been spawned and signalled ready. ##### Returns `Promise`\<`void`\> --- ### size #### Get Signature ```ts get size(): number; ``` Total number of threads in the pool. ##### Returns `number` ## Methods ### dispatch() ```ts dispatch( pw, jobData, handlerModule, callbacks): Promise; ``` Dispatch a serialized job to a specific idle worker. The caller is responsible for checking idleness first. #### Parameters ##### pw `PoolWorker` ##### jobData `Record`\<`string`, `unknown`\> ##### handlerModule `string` ##### callbacks ###### onComplete (`completionAction?`) => `void` ###### onError (`err`) => `void` #### Returns `Promise`\<`void`\> --- ### getIdleWorker() ```ts getIdleWorker(): PoolWorker | undefined; ``` Find the first ready & idle thread. #### Returns `PoolWorker` \| `undefined` --- ### terminate() ```ts terminate(): void; ``` Terminate all threads and reject any in-flight tasks. #### Returns `void` --- ## Interface: ThreadedJobWorker A job worker that runs handler logic in a shared pool of worker_threads, keeping the main Node.js event loop free for polling and I/O. The thread pool is owned by CamundaClient and shared across all threaded workers. Each thread is generic — the handler module path is sent with each job, and threads cache loaded handlers by module path. ## Accessors ### activeJobs #### Get Signature ```ts get activeJobs(): number; ``` ##### Returns `number` --- ### busyThreads #### Get Signature ```ts get busyThreads(): number; ``` Number of threads currently processing a job (across all workers). ##### Returns `number` --- ### name #### Get Signature ```ts get name(): string; ``` ##### Returns `string` --- ### poolSize #### Get Signature ```ts get poolSize(): number; ``` Number of threads in the shared pool. ##### Returns `number` --- ### ready #### Get Signature ```ts get ready(): Promise; ``` Resolves when the shared thread pool has finished initialising. ##### Returns `Promise`\<`void`\> --- ### stopped #### Get Signature ```ts get stopped(): boolean; ``` ##### Returns `boolean` ## Methods ### start() ```ts start(): void; ``` #### Returns `void` --- ### stop() ```ts stop(): void; ``` #### Returns `void` --- ### stopGracefully() ```ts stopGracefully(opts?): Promise<{ remainingJobs: number; timedOut: boolean; }>; ``` #### Parameters ##### opts? ###### checkIntervalMs? `number` ###### waitUpToMs? `number` #### Returns `Promise`\<\{ `remainingJobs`: `number`; `timedOut`: `boolean`; \}\> --- ## Interface: ThreadedJobWorkerConfig # Interface: ThreadedJobWorkerConfig\ Configuration for a threaded job worker. Same as JobWorkerConfig but replaces `jobHandler` with `handlerModule`. ## Type Parameters ### In `In` _extends_ `z.ZodTypeAny` = `any` ### Out `Out` _extends_ `z.ZodTypeAny` = `any` ### Headers `Headers` _extends_ `z.ZodTypeAny` = `any` ## Properties ### autoStart? ```ts optional autoStart?: boolean; ``` Immediately start polling for work - default `true` --- ### customHeadersSchema? ```ts optional customHeadersSchema?: Headers; ``` Zod schema for custom headers in the activated job --- ### fetchVariables? ```ts optional fetchVariables?: In extends ZodType> ? Extract, string>[] : string[]; ``` Optional list of variable names to fetch during activation --- ### handlerModule ```ts handlerModule: string; ``` Absolute or relative path to a JS/TS module that exports a default handler function. The function signature must be: `(job, client) => Promise` --- ### inputSchema? ```ts optional inputSchema?: In; ``` Zod schema for variables in the activated job --- ### jobTimeoutMs? ```ts optional jobTimeoutMs?: number; ``` Job activation timeout in ms — default `60000`. Overridden by CAMUNDA_WORKER_TIMEOUT env var. --- ### jobType ```ts jobType: string; ``` Zeebe job type --- ### maxParallelJobs? ```ts optional maxParallelJobs?: number; ``` Concurrency limit — default `10`. Overridden by CAMUNDA_WORKER_MAX_CONCURRENT_JOBS env var. --- ### outputSchema? ```ts optional outputSchema?: Out; ``` Zod schema for variables in the complete command --- ### pollIntervalMs? ```ts optional pollIntervalMs?: number; ``` Backoff between polls - default 1ms --- ### pollTimeoutMs? ```ts optional pollTimeoutMs?: number; ``` The request will be completed when at least one job is activated or after the requestTimeout. If the requestTimeout = 0, the request will be completed after a default configured timeout in the broker. To immediately complete the request when no job is activated set the requestTimeout to a negative value --- ### startupJitterMaxSeconds? ```ts optional startupJitterMaxSeconds?: number; ``` Maximum random delay (in seconds) before the worker starts polling. When multiple application instances restart simultaneously, this spreads out initial activation requests to avoid saturating the server. `0` (the default) means no delay. --- ### threadPoolSize? ```ts optional threadPoolSize?: number; ``` Number of threads in the shared pool (used only when the pool is first created; subsequent workers share the existing pool). Default: number of CPU cores available to the process. --- ### validateSchemas? ```ts optional validateSchemas?: boolean; ``` Validate any provided input, output, customheader schema default: false --- ### workerName? ```ts optional workerName?: string; ``` Optional explicit name --- ## Function: assumeExists() ```ts function assumeExists(value): AgentInstanceKey; ``` ## Parameters ### value `string` ## Returns [`AgentInstanceKey`](../../../type-aliases/AgentInstanceKey.md) --- ## Function: getValue() ```ts function getValue(key): string; ``` ## Parameters ### key [`AgentInstanceKey`](../../../type-aliases/AgentInstanceKey.md) ## Returns `string` --- ## Function: isValid() ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## AgentInstanceKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(Functions) ```ts function assumeExists(value): AuditLogEntityKey; ``` ## Parameters ### value `string` ## Returns [`AuditLogEntityKey`](../../../type-aliases/AuditLogEntityKey.md) --- ## Function: getValue()(Functions) ```ts function getValue(key): string; ``` ## Parameters ### key [`AuditLogEntityKey`](../../../type-aliases/AuditLogEntityKey.md) ## Returns `string` --- ## Function: isValid()(Functions) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## AuditLogEntityKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(3) ```ts function assumeExists(value): AuditLogKey; ``` ## Parameters ### value `string` ## Returns [`AuditLogKey`](../../../type-aliases/AuditLogKey.md) --- ## Function: getValue()(3) ```ts function getValue(key): string; ``` ## Parameters ### key [`AuditLogKey`](../../../type-aliases/AuditLogKey.md) ## Returns `string` --- ## Function: isValid()(3) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## AuditLogKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(4) ```ts function assumeExists(value): AuthorizationKey; ``` ## Parameters ### value `string` ## Returns [`AuthorizationKey`](../../../type-aliases/AuthorizationKey.md) --- ## Function: getValue()(4) ```ts function getValue(key): string; ``` ## Parameters ### key [`AuthorizationKey`](../../../type-aliases/AuthorizationKey.md) ## Returns `string` --- ## Function: isValid()(4) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## AuthorizationKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(5) ```ts function assumeExists(value): BatchOperationKey; ``` ## Parameters ### value `string` ## Returns [`BatchOperationKey`](../../../type-aliases/BatchOperationKey.md) --- ## Function: getValue()(5) ```ts function getValue(key): string; ``` ## Parameters ### key [`BatchOperationKey`](../../../type-aliases/BatchOperationKey.md) ## Returns `string` --- ## Function: isValid()(5) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## BatchOperationKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(6) ```ts function assumeExists(value): BusinessId; ``` ## Parameters ### value `string` ## Returns [`BusinessId`](../../../type-aliases/BusinessId.md) --- ## Function: getValue()(6) ```ts function getValue(key): string; ``` ## Parameters ### key [`BusinessId`](../../../type-aliases/BusinessId.md) ## Returns `string` --- ## Function: isValid()(6) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## BusinessId ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(7) ```ts function assumeExists(value): ClientId; ``` ## Parameters ### value `string` ## Returns [`ClientId`](../../../type-aliases/ClientId.md) --- ## Function: getValue()(7) ```ts function getValue(key): string; ``` ## Parameters ### key [`ClientId`](../../../type-aliases/ClientId.md) ## Returns `string` --- ## Function: isValid()(7) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## ClientId ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(8) ```ts function assumeExists(value): ClusterVariableName; ``` ## Parameters ### value `string` ## Returns [`ClusterVariableName`](../../../type-aliases/ClusterVariableName.md) --- ## Function: getValue()(8) ```ts function getValue(key): string; ``` ## Parameters ### key [`ClusterVariableName`](../../../type-aliases/ClusterVariableName.md) ## Returns `string` --- ## Function: isValid()(8) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## ClusterVariableName ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(9) ```ts function assumeExists(value): ConditionalEvaluationKey; ``` ## Parameters ### value `string` ## Returns [`ConditionalEvaluationKey`](../../../type-aliases/ConditionalEvaluationKey.md) --- ## Function: getValue()(9) ```ts function getValue(key): string; ``` ## Parameters ### key [`ConditionalEvaluationKey`](../../../type-aliases/ConditionalEvaluationKey.md) ## Returns `string` --- ## Function: isValid()(9) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## ConditionalEvaluationKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(10) ```ts function assumeExists(value): DecisionDefinitionId; ``` ## Parameters ### value `string` ## Returns [`DecisionDefinitionId`](../../../type-aliases/DecisionDefinitionId.md) --- ## Function: getValue()(10) ```ts function getValue(key): string; ``` ## Parameters ### key [`DecisionDefinitionId`](../../../type-aliases/DecisionDefinitionId.md) ## Returns `string` --- ## Function: isValid()(10) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## DecisionDefinitionId ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(11) ```ts function assumeExists(value): DecisionDefinitionKey; ``` ## Parameters ### value `string` ## Returns [`DecisionDefinitionKey`](../../../type-aliases/DecisionDefinitionKey.md) --- ## Function: getValue()(11) ```ts function getValue(key): string; ``` ## Parameters ### key [`DecisionDefinitionKey`](../../../type-aliases/DecisionDefinitionKey.md) ## Returns `string` --- ## Function: isValid()(11) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## DecisionDefinitionKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(12) ```ts function assumeExists(value): DecisionEvaluationInstanceKey; ``` ## Parameters ### value `string` ## Returns [`DecisionEvaluationInstanceKey`](../../../type-aliases/DecisionEvaluationInstanceKey.md) --- ## Function: getValue()(12) ```ts function getValue(key): string; ``` ## Parameters ### key [`DecisionEvaluationInstanceKey`](../../../type-aliases/DecisionEvaluationInstanceKey.md) ## Returns `string` --- ## Function: isValid()(12) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## DecisionEvaluationInstanceKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(13) ```ts function assumeExists(value): DecisionEvaluationKey; ``` ## Parameters ### value `string` ## Returns [`DecisionEvaluationKey`](../../../type-aliases/DecisionEvaluationKey.md) --- ## Function: getValue()(13) ```ts function getValue(key): string; ``` ## Parameters ### key [`DecisionEvaluationKey`](../../../type-aliases/DecisionEvaluationKey.md) ## Returns `string` --- ## Function: isValid()(13) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## DecisionEvaluationKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(14) ```ts function assumeExists(value): DecisionInstanceKey; ``` ## Parameters ### value `string` ## Returns [`DecisionInstanceKey`](../../../type-aliases/DecisionInstanceKey.md) --- ## Function: getValue()(14) ```ts function getValue(key): string; ``` ## Parameters ### key [`DecisionInstanceKey`](../../../type-aliases/DecisionInstanceKey.md) ## Returns `string` --- ## Function: isValid()(14) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## DecisionInstanceKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(15) ```ts function assumeExists(value): DecisionRequirementsKey; ``` ## Parameters ### value `string` ## Returns [`DecisionRequirementsKey`](../../../type-aliases/DecisionRequirementsKey.md) --- ## Function: getValue()(15) ```ts function getValue(key): string; ``` ## Parameters ### key [`DecisionRequirementsKey`](../../../type-aliases/DecisionRequirementsKey.md) ## Returns `string` --- ## Function: isValid()(15) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## DecisionRequirementsKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(16) ```ts function assumeExists(value): DeploymentKey; ``` ## Parameters ### value `string` ## Returns [`DeploymentKey`](../../../type-aliases/DeploymentKey.md) --- ## Function: getValue()(16) ```ts function getValue(key): string; ``` ## Parameters ### key [`DeploymentKey`](../../../type-aliases/DeploymentKey.md) ## Returns `string` --- ## Function: isValid()(16) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## DeploymentKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(17) ```ts function assumeExists(value): DocumentId; ``` ## Parameters ### value `string` ## Returns [`DocumentId`](../../../type-aliases/DocumentId.md) --- ## Function: getValue()(17) ```ts function getValue(key): string; ``` ## Parameters ### key [`DocumentId`](../../../type-aliases/DocumentId.md) ## Returns `string` --- ## Function: isValid()(17) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## DocumentId ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(18) ```ts function assumeExists(value): ElementId; ``` ## Parameters ### value `string` ## Returns [`ElementId`](../../../type-aliases/ElementId.md) --- ## Function: getValue()(18) ```ts function getValue(key): string; ``` ## Parameters ### key [`ElementId`](../../../type-aliases/ElementId.md) ## Returns `string` --- ## Function: isValid()(18) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## ElementId ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(19) ```ts function assumeExists(value): ElementInstanceKey; ``` ## Parameters ### value `string` ## Returns [`ElementInstanceKey`](../../../type-aliases/ElementInstanceKey.md) --- ## Function: getValue()(19) ```ts function getValue(key): string; ``` ## Parameters ### key [`ElementInstanceKey`](../../../type-aliases/ElementInstanceKey.md) ## Returns `string` --- ## Function: isValid()(19) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## ElementInstanceKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(20) ```ts function assumeExists(value): EndCursor; ``` ## Parameters ### value `string` ## Returns [`EndCursor`](../../../type-aliases/EndCursor.md) --- ## Function: getValue()(20) ```ts function getValue(key): string; ``` ## Parameters ### key [`EndCursor`](../../../type-aliases/EndCursor.md) ## Returns `string` --- ## Function: isValid()(20) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## EndCursor ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(21) ```ts function assumeExists(value): FormId; ``` ## Parameters ### value `string` ## Returns [`FormId`](../../../type-aliases/FormId.md) --- ## Function: getValue()(21) ```ts function getValue(key): string; ``` ## Parameters ### key [`FormId`](../../../type-aliases/FormId.md) ## Returns `string` --- ## Function: isValid()(21) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## FormId ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(22) ```ts function assumeExists(value): FormKey; ``` ## Parameters ### value `string` ## Returns [`FormKey`](../../../type-aliases/FormKey.md) --- ## Function: getValue()(22) ```ts function getValue(key): string; ``` ## Parameters ### key [`FormKey`](../../../type-aliases/FormKey.md) ## Returns `string` --- ## Function: isValid()(22) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## FormKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(23) ```ts function assumeExists(value): GlobalListenerId; ``` ## Parameters ### value `string` ## Returns [`GlobalListenerId`](../../../type-aliases/GlobalListenerId.md) --- ## Function: getValue()(23) ```ts function getValue(key): string; ``` ## Parameters ### key [`GlobalListenerId`](../../../type-aliases/GlobalListenerId.md) ## Returns `string` --- ## Function: isValid()(23) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## GlobalListenerId ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(24) ```ts function assumeExists(value): GroupId; ``` ## Parameters ### value `string` ## Returns [`GroupId`](../../../type-aliases/GroupId.md) --- ## Function: getValue()(24) ```ts function getValue(key): string; ``` ## Parameters ### key [`GroupId`](../../../type-aliases/GroupId.md) ## Returns `string` --- ## Function: isValid()(24) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## GroupId ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(25) ```ts function assumeExists(value): IncidentKey; ``` ## Parameters ### value `string` ## Returns [`IncidentKey`](../../../type-aliases/IncidentKey.md) --- ## Function: getValue()(25) ```ts function getValue(key): string; ``` ## Parameters ### key [`IncidentKey`](../../../type-aliases/IncidentKey.md) ## Returns `string` --- ## Function: isValid()(25) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## IncidentKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(26) ```ts function assumeExists(value): JobKey; ``` ## Parameters ### value `string` ## Returns [`JobKey`](../../../type-aliases/JobKey.md) --- ## Function: getValue()(26) ```ts function getValue(key): string; ``` ## Parameters ### key [`JobKey`](../../../type-aliases/JobKey.md) ## Returns `string` --- ## Function: isValid()(26) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## JobKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(27) ```ts function assumeExists(value): MappingRuleId; ``` ## Parameters ### value `string` ## Returns [`MappingRuleId`](../../../type-aliases/MappingRuleId.md) --- ## Function: getValue()(27) ```ts function getValue(key): string; ``` ## Parameters ### key [`MappingRuleId`](../../../type-aliases/MappingRuleId.md) ## Returns `string` --- ## Function: isValid()(27) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## MappingRuleId ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(28) ```ts function assumeExists(value): MessageKey; ``` ## Parameters ### value `string` ## Returns [`MessageKey`](../../../type-aliases/MessageKey.md) --- ## Function: getValue()(28) ```ts function getValue(key): string; ``` ## Parameters ### key [`MessageKey`](../../../type-aliases/MessageKey.md) ## Returns `string` --- ## Function: isValid()(28) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## MessageKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(29) ```ts function assumeExists(value): MessageSubscriptionKey; ``` ## Parameters ### value `string` ## Returns [`MessageSubscriptionKey`](../../../type-aliases/MessageSubscriptionKey.md) --- ## Function: getValue()(29) ```ts function getValue(key): string; ``` ## Parameters ### key [`MessageSubscriptionKey`](../../../type-aliases/MessageSubscriptionKey.md) ## Returns `string` --- ## Function: isValid()(29) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## MessageSubscriptionKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(30) ```ts function assumeExists(value): ProcessDefinitionId; ``` ## Parameters ### value `string` ## Returns [`ProcessDefinitionId`](../../../type-aliases/ProcessDefinitionId.md) --- ## Function: getValue()(30) ```ts function getValue(key): string; ``` ## Parameters ### key [`ProcessDefinitionId`](../../../type-aliases/ProcessDefinitionId.md) ## Returns `string` --- ## Function: isValid()(30) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## ProcessDefinitionId ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(31) ```ts function assumeExists(value): ProcessDefinitionKey; ``` ## Parameters ### value `string` ## Returns [`ProcessDefinitionKey`](../../../type-aliases/ProcessDefinitionKey.md) --- ## Function: getValue()(31) ```ts function getValue(key): string; ``` ## Parameters ### key [`ProcessDefinitionKey`](../../../type-aliases/ProcessDefinitionKey.md) ## Returns `string` --- ## Function: isValid()(31) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## ProcessDefinitionKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(32) ```ts function assumeExists(value): ProcessInstanceKey; ``` ## Parameters ### value `string` ## Returns [`ProcessInstanceKey`](../../../type-aliases/ProcessInstanceKey.md) --- ## Function: getValue()(32) ```ts function getValue(key): string; ``` ## Parameters ### key [`ProcessInstanceKey`](../../../type-aliases/ProcessInstanceKey.md) ## Returns `string` --- ## Function: isValid()(32) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## ProcessInstanceKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(33) ```ts function assumeExists(value): RoleId; ``` ## Parameters ### value `string` ## Returns [`RoleId`](../../../type-aliases/RoleId.md) --- ## Function: getValue()(33) ```ts function getValue(key): string; ``` ## Parameters ### key [`RoleId`](../../../type-aliases/RoleId.md) ## Returns `string` --- ## Function: isValid()(33) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## RoleId ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(34) ```ts function assumeExists(value): SignalKey; ``` ## Parameters ### value `string` ## Returns [`SignalKey`](../../../type-aliases/SignalKey.md) --- ## Function: getValue()(34) ```ts function getValue(key): string; ``` ## Parameters ### key [`SignalKey`](../../../type-aliases/SignalKey.md) ## Returns `string` --- ## Function: isValid()(34) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## SignalKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(35) ```ts function assumeExists(value): StartCursor; ``` ## Parameters ### value `string` ## Returns [`StartCursor`](../../../type-aliases/StartCursor.md) --- ## Function: getValue()(35) ```ts function getValue(key): string; ``` ## Parameters ### key [`StartCursor`](../../../type-aliases/StartCursor.md) ## Returns `string` --- ## Function: isValid()(35) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## StartCursor ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: fromString() ```ts function fromString(value): Tag; ``` ## Parameters ### value `string` ## Returns [`Tag`](../../../type-aliases/Tag.md) --- ## Function: getValue()(36) ```ts function getValue(key): string; ``` ## Parameters ### key [`Tag`](../../../type-aliases/Tag.md) ## Returns `string` --- ## Function: isValid()(36) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## Tag ## Functions - [fromString](functions/fromString.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(36) ```ts function assumeExists(value): TenantId; ``` ## Parameters ### value `string` ## Returns [`TenantId`](../../../type-aliases/TenantId.md) --- ## Function: getValue()(37) ```ts function getValue(key): string; ``` ## Parameters ### key [`TenantId`](../../../type-aliases/TenantId.md) ## Returns `string` --- ## Function: isValid()(37) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## TenantId ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(37) ```ts function assumeExists(value): UserTaskKey; ``` ## Parameters ### value `string` ## Returns [`UserTaskKey`](../../../type-aliases/UserTaskKey.md) --- ## Function: getValue()(38) ```ts function getValue(key): string; ``` ## Parameters ### key [`UserTaskKey`](../../../type-aliases/UserTaskKey.md) ## Returns `string` --- ## Function: isValid()(38) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## UserTaskKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(38) ```ts function assumeExists(value): Username; ``` ## Parameters ### value `string` ## Returns [`Username`](../../../type-aliases/Username.md) --- ## Function: getValue()(39) ```ts function getValue(key): string; ``` ## Parameters ### key [`Username`](../../../type-aliases/Username.md) ## Returns `string` --- ## Function: isValid()(39) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## Username ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Function: assumeExists()(39) ```ts function assumeExists(value): VariableKey; ``` ## Parameters ### value `string` ## Returns [`VariableKey`](../../../type-aliases/VariableKey.md) --- ## Function: getValue()(40) ```ts function getValue(key): string; ``` ## Parameters ### key [`VariableKey`](../../../type-aliases/VariableKey.md) ## Returns `string` --- ## Function: isValid()(40) ```ts function isValid(value): boolean; ``` ## Parameters ### value `string` ## Returns `boolean` --- ## VariableKey ## Functions - [assumeExists](functions/assumeExists.md) - [getValue](functions/getValue.md) - [isValid](functions/isValid.md) --- ## Type Alias: ActivateAdHocSubProcessActivitiesData ```ts type ActivateAdHocSubProcessActivitiesData = object; ``` ## Properties ### body ```ts body: AdHocSubProcessActivateActivitiesInstruction; ``` --- ### path ```ts path: object; ``` #### adHocSubProcessInstanceKey ```ts adHocSubProcessInstanceKey: ElementInstanceKey; ``` The key of the ad-hoc sub-process instance that contains the activities. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/element-instances/ad-hoc-activities/{adHocSubProcessInstanceKey}/activation"; ``` --- ## Type Alias: ActivateAdHocSubProcessActivitiesError ```ts type ActivateAdHocSubProcessActivitiesError = ActivateAdHocSubProcessActivitiesErrors[keyof ActivateAdHocSubProcessActivitiesErrors]; ``` --- ## Type Alias: ActivateAdHocSubProcessActivitiesErrors ```ts type ActivateAdHocSubProcessActivitiesErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The ad-hoc sub-process instance is not found or the provided key does not identify an ad-hoc sub-process. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: ActivateAdHocSubProcessActivitiesResponse ```ts type ActivateAdHocSubProcessActivitiesResponse = ActivateAdHocSubProcessActivitiesResponses[keyof ActivateAdHocSubProcessActivitiesResponses]; ``` --- ## Type Alias: ActivateAdHocSubProcessActivitiesResponses ```ts type ActivateAdHocSubProcessActivitiesResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The ad-hoc sub-process instance is modified. --- ## Type Alias: ActivateJobsData ```ts type ActivateJobsData = object; ``` ## Properties ### body ```ts body: JobActivationRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/jobs/activation"; ``` --- ## Type Alias: ActivateJobsError ```ts type ActivateJobsError = ActivateJobsErrors[keyof ActivateJobsErrors]; ``` --- ## Type Alias: ActivateJobsErrors ```ts type ActivateJobsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: ActivateJobsResponse ```ts type ActivateJobsResponse = ActivateJobsResponses[keyof ActivateJobsResponses]; ``` --- ## Type Alias: ActivateJobsResponses ```ts type ActivateJobsResponses = object; ``` ## Properties ### 200 ```ts 200: JobActivationResult; ``` The list of activated jobs. --- ## Type Alias: ActivatedJobResult ```ts type ActivatedJobResult = object; ``` ## Properties ### customHeaders ```ts customHeaders: object; ``` A set of custom headers defined during modelling; returned as a serialized JSON document. #### Index Signature ```ts [key: string]: unknown ``` --- ### deadline ```ts deadline: number; ``` When the job can be activated again, sent as a UNIX epoch timestamp. --- ### elementId ```ts elementId: ElementId; ``` The associated task element ID. --- ### elementInstanceKey ```ts elementInstanceKey: ElementInstanceKey; ``` The element instance key of the task. --- ### jobKey ```ts jobKey: JobKey; ``` The key, a unique identifier for the job. --- ### kind ```ts kind: JobKindEnum; ``` --- ### listenerEventType ```ts listenerEventType: JobListenerEventTypeEnum; ``` --- ### processDefinitionId ```ts processDefinitionId: ProcessDefinitionId; ``` The bpmn process ID of the job's process definition. --- ### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` The key of the job's process definition. --- ### processDefinitionVersion ```ts processDefinitionVersion: number; ``` The version of the job's process definition. --- ### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The job's process instance key. --- ### retries ```ts retries: number; ``` The amount of retries left to this job (should always be positive). --- ### rootProcessInstanceKey ```ts rootProcessInstanceKey: ProcessInstanceKey | null; ``` The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. --- ### tags ```ts tags: TagSet; ``` --- ### tenantId ```ts tenantId: TenantId; ``` The ID of the tenant that owns the job. --- ### type ```ts type: string; ``` The type of the job (should match what was requested). --- ### userTask ```ts userTask: UserTaskProperties | null; ``` User task properties, if the job is a user task. This is `null` if the job is not a user task. --- ### variables ```ts variables: object; ``` All variables visible to the task scope, computed at activation time. #### Index Signature ```ts [key: string]: unknown ``` --- ### worker ```ts worker: string; ``` The name of the worker which activated this job. --- ## Type Alias: AdHocSubProcessActivateActivitiesInstruction ```ts type AdHocSubProcessActivateActivitiesInstruction = object; ``` ## Properties ### cancelRemainingInstances? ```ts optional cancelRemainingInstances?: boolean; ``` Whether to cancel remaining instances of the ad-hoc sub-process. --- ### elements ```ts elements: AdHocSubProcessActivateActivityReference[]; ``` Activities to activate. --- ## Type Alias: AdHocSubProcessActivateActivityReference ```ts type AdHocSubProcessActivateActivityReference = object; ``` ## Properties ### elementId ```ts elementId: ElementId; ``` The ID of the element that should be activated. --- ### variables? ```ts optional variables?: object; ``` Variables to be set when activating the element. #### Index Signature ```ts [key: string]: unknown ``` --- ## Type Alias: AdvancedActorTypeFilter ```ts type AdvancedActorTypeFilter = object; ``` Advanced filter Advanced AuditLogActorTypeEnum filter. ## Properties ### $eq? ```ts optional $eq?: AuditLogActorTypeEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: AuditLogActorTypeEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: AuditLogActorTypeEnum; ``` Checks for inequality with the provided value. --- ## Type Alias: AdvancedAgentInstanceKeyFilter ```ts type AdvancedAgentInstanceKeyFilter = object; ``` Advanced filter Advanced AgentInstanceKey filter. ## Properties ### $eq? ```ts optional $eq?: AgentInstanceKey; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: AgentInstanceKey[]; ``` Checks if the property matches any of the provided values. --- ### $neq? ```ts optional $neq?: AgentInstanceKey; ``` Checks for inequality with the provided value. --- ### $notIn? ```ts optional $notIn?: AgentInstanceKey[]; ``` Checks if the property matches none of the provided values. --- ## Type Alias: AdvancedAgentInstanceStatusFilter ```ts type AdvancedAgentInstanceStatusFilter = object; ``` Advanced filter Advanced AgentInstanceStatusEnum filter. ## Properties ### $eq? ```ts optional $eq?: AgentInstanceStatusEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: AgentInstanceStatusEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: AgentInstanceStatusEnum; ``` Checks for inequality with the provided value. --- ## Type Alias: AdvancedAuditLogEntityKeyFilter ```ts type AdvancedAuditLogEntityKeyFilter = object; ``` Advanced filter Advanced entityKey filter. ## Properties ### $eq? ```ts optional $eq?: AuditLogEntityKey; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: AuditLogEntityKey[]; ``` Checks if the property matches any of the provided values. --- ### $neq? ```ts optional $neq?: AuditLogEntityKey; ``` Checks for inequality with the provided value. --- ### $notIn? ```ts optional $notIn?: AuditLogEntityKey[]; ``` Checks if the property matches none of the provided values. --- ## Type Alias: AdvancedAuditLogKeyFilter ```ts type AdvancedAuditLogKeyFilter = object; ``` Advanced filter Advanced AuditLogKey filter. ## Properties ### $eq? ```ts optional $eq?: AuditLogKey; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: AuditLogKey[]; ``` Checks if the property matches any of the provided values. --- ### $neq? ```ts optional $neq?: AuditLogKey; ``` Checks for inequality with the provided value. --- ### $notIn? ```ts optional $notIn?: AuditLogKey[]; ``` Checks if the property matches none of the provided values. --- ## Type Alias: AdvancedBatchOperationItemStateFilter ```ts type AdvancedBatchOperationItemStateFilter = object; ``` Advanced filter Advanced BatchOperationItemStateEnum filter. ## Properties ### $eq? ```ts optional $eq?: BatchOperationItemStateEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: BatchOperationItemStateEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: BatchOperationItemStateEnum; ``` Checks for inequality with the provided value. --- ## Type Alias: AdvancedBatchOperationStateFilter ```ts type AdvancedBatchOperationStateFilter = object; ``` Advanced filter Advanced BatchOperationStateEnum filter. ## Properties ### $eq? ```ts optional $eq?: BatchOperationStateEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: BatchOperationStateEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: BatchOperationStateEnum; ``` Checks for inequality with the provided value. --- ## Type Alias: AdvancedBatchOperationTypeFilter ```ts type AdvancedBatchOperationTypeFilter = object; ``` Advanced filter Advanced BatchOperationTypeEnum filter. ## Properties ### $eq? ```ts optional $eq?: BatchOperationTypeEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: BatchOperationTypeEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: BatchOperationTypeEnum; ``` Checks for inequality with the provided value. --- ## Type Alias: AdvancedCategoryFilter ```ts type AdvancedCategoryFilter = object; ``` Advanced filter Advanced AuditLogCategoryEnum filter. ## Properties ### $eq? ```ts optional $eq?: AuditLogCategoryEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: AuditLogCategoryEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: AuditLogCategoryEnum; ``` Checks for inequality with the provided value. --- ## Type Alias: AdvancedClusterVariableScopeFilter ```ts type AdvancedClusterVariableScopeFilter = object; ``` Advanced filter Advanced ClusterVariableScopeEnum filter. ## Properties ### $eq? ```ts optional $eq?: ClusterVariableScopeEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: ClusterVariableScopeEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: ClusterVariableScopeEnum; ``` Checks for inequality with the provided value. --- ## Type Alias: AdvancedDateTimeFilter ```ts type AdvancedDateTimeFilter = object; ``` Advanced filter Advanced date-time filter. ## Properties ### $eq? ```ts optional $eq?: string; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $gt? ```ts optional $gt?: string; ``` Greater than comparison with the provided value. --- ### $gte? ```ts optional $gte?: string; ``` Greater than or equal comparison with the provided value. --- ### $in? ```ts optional $in?: string[]; ``` Checks if the property matches any of the provided values. --- ### $lt? ```ts optional $lt?: string; ``` Lower than comparison with the provided value. --- ### $lte? ```ts optional $lte?: string; ``` Lower than or equal comparison with the provided value. --- ### $neq? ```ts optional $neq?: string; ``` Checks for inequality with the provided value. --- ## Type Alias: AdvancedDecisionDefinitionKeyFilter ```ts type AdvancedDecisionDefinitionKeyFilter = object; ``` Advanced filter Advanced DecisionDefinitionKey filter. ## Properties ### $eq? ```ts optional $eq?: DecisionDefinitionKey; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: DecisionDefinitionKey[]; ``` Checks if the property matches any of the provided values. --- ### $neq? ```ts optional $neq?: DecisionDefinitionKey; ``` Checks for inequality with the provided value. --- ### $notIn? ```ts optional $notIn?: DecisionDefinitionKey[]; ``` Checks if the property matches none of the provided values. --- ## Type Alias: AdvancedDecisionEvaluationInstanceKeyFilter ```ts type AdvancedDecisionEvaluationInstanceKeyFilter = object; ``` Advanced filter Advanced DecisionEvaluationInstanceKey filter. ## Properties ### $eq? ```ts optional $eq?: DecisionEvaluationInstanceKey; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: DecisionEvaluationInstanceKey[]; ``` Checks if the property matches any of the provided values. --- ### $neq? ```ts optional $neq?: DecisionEvaluationInstanceKey; ``` Checks for inequality with the provided value. --- ### $notIn? ```ts optional $notIn?: DecisionEvaluationInstanceKey[]; ``` Checks if the property matches none of the provided values. --- ## Type Alias: AdvancedDecisionEvaluationKeyFilter ```ts type AdvancedDecisionEvaluationKeyFilter = object; ``` Advanced filter Advanced DecisionEvaluationKey filter. ## Properties ### $eq? ```ts optional $eq?: DecisionEvaluationKey; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: DecisionEvaluationKey[]; ``` Checks if the property matches any of the provided values. --- ### $neq? ```ts optional $neq?: DecisionEvaluationKey; ``` Checks for inequality with the provided value. --- ### $notIn? ```ts optional $notIn?: DecisionEvaluationKey[]; ``` Checks if the property matches none of the provided values. --- ## Type Alias: AdvancedDecisionInstanceStateFilter ```ts type AdvancedDecisionInstanceStateFilter = object; ``` Advanced filter Advanced DecisionInstanceStateEnum filter. ## Properties ### $eq? ```ts optional $eq?: DecisionInstanceStateEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: DecisionInstanceStateEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: DecisionInstanceStateEnum; ``` Checks for inequality with the provided value. --- ### $notIn? ```ts optional $notIn?: DecisionInstanceStateEnum[]; ``` Checks if the property matches none of the provided values. --- ## Type Alias: AdvancedDecisionRequirementsKeyFilter ```ts type AdvancedDecisionRequirementsKeyFilter = object; ``` Advanced filter Advanced DecisionRequirementsKey filter. ## Properties ### $eq? ```ts optional $eq?: DecisionRequirementsKey; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: DecisionRequirementsKey[]; ``` Checks if the property matches any of the provided values. --- ### $neq? ```ts optional $neq?: DecisionRequirementsKey; ``` Checks for inequality with the provided value. --- ### $notIn? ```ts optional $notIn?: DecisionRequirementsKey[]; ``` Checks if the property matches none of the provided values. --- ## Type Alias: AdvancedDeploymentKeyFilter ```ts type AdvancedDeploymentKeyFilter = object; ``` Advanced filter Advanced DeploymentKey filter. ## Properties ### $eq? ```ts optional $eq?: DeploymentKey; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: DeploymentKey[]; ``` Checks if the property matches any of the provided values. --- ### $neq? ```ts optional $neq?: DeploymentKey; ``` Checks for inequality with the provided value. --- ### $notIn? ```ts optional $notIn?: DeploymentKey[]; ``` Checks if the property matches none of the provided values. --- ## Type Alias: AdvancedElementIdFilter ```ts type AdvancedElementIdFilter = object; ``` Advanced filter Advanced ElementId filter. ## Properties ### $eq? ```ts optional $eq?: ElementId; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: ElementId[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: ElementId; ``` Checks for inequality with the provided value. --- ### $notIn? ```ts optional $notIn?: ElementId[]; ``` Checks if the property matches none of the provided values. --- ## Type Alias: AdvancedElementInstanceKeyFilter ```ts type AdvancedElementInstanceKeyFilter = object; ``` Advanced filter Advanced ElementInstanceKey filter. ## Properties ### $eq? ```ts optional $eq?: ElementInstanceKey; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: ElementInstanceKey[]; ``` Checks if the property matches any of the provided values. --- ### $neq? ```ts optional $neq?: ElementInstanceKey; ``` Checks for inequality with the provided value. --- ### $notIn? ```ts optional $notIn?: ElementInstanceKey[]; ``` Checks if the property matches none of the provided values. --- ## Type Alias: AdvancedElementInstanceStateFilter ```ts type AdvancedElementInstanceStateFilter = object; ``` Advanced filter Advanced ElementInstanceStateEnum filter. ## Properties ### $eq? ```ts optional $eq?: ElementInstanceStateEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: ElementInstanceStateEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: ElementInstanceStateEnum; ``` Checks for inequality with the provided value. --- ## Type Alias: AdvancedEntityTypeFilter ```ts type AdvancedEntityTypeFilter = object; ``` Advanced filter Advanced AuditLogEntityTypeEnum filter. ## Properties ### $eq? ```ts optional $eq?: AuditLogEntityTypeEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: AuditLogEntityTypeEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: AuditLogEntityTypeEnum; ``` Checks for inequality with the provided value. --- ## Type Alias: AdvancedFormKeyFilter ```ts type AdvancedFormKeyFilter = object; ``` Advanced filter Advanced FormKey filter. ## Properties ### $eq? ```ts optional $eq?: FormKey; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: FormKey[]; ``` Checks if the property matches any of the provided values. --- ### $neq? ```ts optional $neq?: FormKey; ``` Checks for inequality with the provided value. --- ### $notIn? ```ts optional $notIn?: FormKey[]; ``` Checks if the property matches none of the provided values. --- ## Type Alias: AdvancedGlobalListenerSourceFilter ```ts type AdvancedGlobalListenerSourceFilter = object; ``` Advanced filter Advanced global listener source filter. ## Properties ### $eq? ```ts optional $eq?: GlobalListenerSourceEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: GlobalListenerSourceEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: GlobalListenerSourceEnum; ``` Checks for inequality with the provided value. --- ## Type Alias: AdvancedGlobalTaskListenerEventTypeFilter ```ts type AdvancedGlobalTaskListenerEventTypeFilter = object; ``` Advanced filter Advanced global listener event type filter. ## Properties ### $eq? ```ts optional $eq?: GlobalTaskListenerEventTypeEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: GlobalTaskListenerEventTypeEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: GlobalTaskListenerEventTypeEnum; ``` Checks for inequality with the provided value. --- ## Type Alias: AdvancedIncidentErrorTypeFilter ```ts type AdvancedIncidentErrorTypeFilter = object; ``` Advanced filter Advanced IncidentErrorTypeEnum filter ## Properties ### $eq? ```ts optional $eq?: IncidentErrorTypeEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: IncidentErrorTypeEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: IncidentErrorTypeEnum; ``` Checks for inequality with the provided value. --- ### $notIn? ```ts optional $notIn?: IncidentErrorTypeEnum[]; ``` Checks if the property does not match any of the provided values. --- ## Type Alias: AdvancedIncidentStateFilter ```ts type AdvancedIncidentStateFilter = object; ``` Advanced filter Advanced IncidentStateEnum filter ## Properties ### $eq? ```ts optional $eq?: IncidentStateEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: IncidentStateEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: IncidentStateEnum; ``` Checks for inequality with the provided value. --- ### $notIn? ```ts optional $notIn?: IncidentStateEnum[]; ``` Checks if the property does not match any of the provided values. --- ## Type Alias: AdvancedIntegerFilter ```ts type AdvancedIntegerFilter = object; ``` Advanced filter Advanced integer (int32) filter. ## Properties ### $eq? ```ts optional $eq?: number; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $gt? ```ts optional $gt?: number; ``` Greater than comparison with the provided value. --- ### $gte? ```ts optional $gte?: number; ``` Greater than or equal comparison with the provided value. --- ### $in? ```ts optional $in?: number[]; ``` Checks if the property matches any of the provided values. --- ### $lt? ```ts optional $lt?: number; ``` Lower than comparison with the provided value. --- ### $lte? ```ts optional $lte?: number; ``` Lower than or equal comparison with the provided value. --- ### $neq? ```ts optional $neq?: number; ``` Checks for inequality with the provided value. --- ## Type Alias: AdvancedJobKeyFilter ```ts type AdvancedJobKeyFilter = object; ``` Advanced filter Advanced JobKey filter. ## Properties ### $eq? ```ts optional $eq?: JobKey; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: JobKey[]; ``` Checks if the property matches any of the provided values. --- ### $neq? ```ts optional $neq?: JobKey; ``` Checks for inequality with the provided value. --- ### $notIn? ```ts optional $notIn?: JobKey[]; ``` Checks if the property matches none of the provided values. --- ## Type Alias: AdvancedJobKindFilter ```ts type AdvancedJobKindFilter = object; ``` Advanced filter Advanced JobKindEnum filter. ## Properties ### $eq? ```ts optional $eq?: JobKindEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: JobKindEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: JobKindEnum; ``` Checks for inequality with the provided value. --- ## Type Alias: AdvancedJobListenerEventTypeFilter ```ts type AdvancedJobListenerEventTypeFilter = object; ``` Advanced filter Advanced JobListenerEventTypeEnum filter. ## Properties ### $eq? ```ts optional $eq?: JobListenerEventTypeEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: JobListenerEventTypeEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: JobListenerEventTypeEnum; ``` Checks for inequality with the provided value. --- ## Type Alias: AdvancedJobStateFilter ```ts type AdvancedJobStateFilter = object; ``` Advanced filter Advanced JobStateEnum filter. ## Properties ### $eq? ```ts optional $eq?: JobStateEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: JobStateEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: JobStateEnum; ``` Checks for inequality with the provided value. --- ## Type Alias: AdvancedMessageSubscriptionKeyFilter ```ts type AdvancedMessageSubscriptionKeyFilter = object; ``` Advanced filter Advanced MessageSubscriptionKey filter. ## Properties ### $eq? ```ts optional $eq?: MessageSubscriptionKey; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: MessageSubscriptionKey[]; ``` Checks if the property matches any of the provided values. --- ### $neq? ```ts optional $neq?: MessageSubscriptionKey; ``` Checks for equality with the provided value. --- ### $notIn? ```ts optional $notIn?: MessageSubscriptionKey[]; ``` Checks if the property matches none of the provided values. --- ## Type Alias: AdvancedMessageSubscriptionStateFilter ```ts type AdvancedMessageSubscriptionStateFilter = object; ``` Advanced filter Advanced MessageSubscriptionStateEnum filter ## Properties ### $eq? ```ts optional $eq?: MessageSubscriptionStateEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: MessageSubscriptionStateEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: MessageSubscriptionStateEnum; ``` Checks for inequality with the provided value. --- ## Type Alias: AdvancedMessageSubscriptionTypeFilter ```ts type AdvancedMessageSubscriptionTypeFilter = object; ``` Advanced filter Advanced MessageSubscriptionTypeEnum filter ## Properties ### $eq? ```ts optional $eq?: MessageSubscriptionTypeEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: MessageSubscriptionTypeEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: MessageSubscriptionTypeEnum; ``` Checks for inequality with the provided value. --- ## Type Alias: AdvancedOperationTypeFilter ```ts type AdvancedOperationTypeFilter = object; ``` Advanced filter Advanced AuditLogOperationTypeEnum filter. ## Properties ### $eq? ```ts optional $eq?: AuditLogOperationTypeEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: AuditLogOperationTypeEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: AuditLogOperationTypeEnum; ``` Checks for inequality with the provided value. --- ## Type Alias: AdvancedProcessDefinitionIdFilter ```ts type AdvancedProcessDefinitionIdFilter = object; ``` Advanced filter Advanced ProcessDefinitionId filter. ## Properties ### $eq? ```ts optional $eq?: ProcessDefinitionId; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: ProcessDefinitionId[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: ProcessDefinitionId; ``` Checks for inequality with the provided value. --- ### $notIn? ```ts optional $notIn?: ProcessDefinitionId[]; ``` Checks if the property matches none of the provided values. --- ## Type Alias: AdvancedProcessDefinitionKeyFilter ```ts type AdvancedProcessDefinitionKeyFilter = object; ``` Advanced filter Advanced ProcessDefinitionKey filter. ## Properties ### $eq? ```ts optional $eq?: ProcessDefinitionKey; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: ProcessDefinitionKey[]; ``` Checks if the property matches any of the provided values. --- ### $neq? ```ts optional $neq?: ProcessDefinitionKey; ``` Checks for inequality with the provided value. --- ### $notIn? ```ts optional $notIn?: ProcessDefinitionKey[]; ``` Checks if the property matches none of the provided values. --- ## Type Alias: AdvancedProcessInstanceKeyFilter ```ts type AdvancedProcessInstanceKeyFilter = object; ``` Advanced filter Advanced ProcessInstanceKey filter. ## Properties ### $eq? ```ts optional $eq?: ProcessInstanceKey; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: ProcessInstanceKey[]; ``` Checks if the property matches any of the provided values. --- ### $neq? ```ts optional $neq?: ProcessInstanceKey; ``` Checks for inequality with the provided value. --- ### $notIn? ```ts optional $notIn?: ProcessInstanceKey[]; ``` Checks if the property matches none of the provided values. --- ## Type Alias: AdvancedProcessInstanceStateFilter ```ts type AdvancedProcessInstanceStateFilter = object; ``` Advanced filter Advanced ProcessInstanceStateEnum filter. ## Properties ### $eq? ```ts optional $eq?: ProcessInstanceStateEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: ProcessInstanceStateEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: ProcessInstanceStateEnum; ``` Checks for inequality with the provided value. --- ## Type Alias: AdvancedResourceKeyFilter ```ts type AdvancedResourceKeyFilter = object; ``` Advanced filter Advanced ResourceKey filter. ## Properties ### $eq? ```ts optional $eq?: ResourceKey; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: ResourceKey[]; ``` Checks if the property matches any of the provided values. --- ### $neq? ```ts optional $neq?: ResourceKey; ``` Checks for inequality with the provided value. --- ### $notIn? ```ts optional $notIn?: ResourceKey[]; ``` Checks if the property matches none of the provided values. --- ## Type Alias: AdvancedResultFilter ```ts type AdvancedResultFilter = object; ``` Advanced filter Advanced AuditLogResultEnum filter. ## Properties ### $eq? ```ts optional $eq?: AuditLogResultEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: AuditLogResultEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: AuditLogResultEnum; ``` Checks for inequality with the provided value. --- ## Type Alias: AdvancedScopeKeyFilter ```ts type AdvancedScopeKeyFilter = object; ``` Advanced filter Advanced ScopeKey filter. ## Properties ### $eq? ```ts optional $eq?: ScopeKey; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: ScopeKey[]; ``` Checks if the property matches any of the provided values. --- ### $neq? ```ts optional $neq?: ScopeKey; ``` Checks for inequality with the provided value. --- ### $notIn? ```ts optional $notIn?: ScopeKey[]; ``` Checks if the property matches none of the provided values. --- ## Type Alias: AdvancedStringFilter ```ts type AdvancedStringFilter = BasicStringFilter & object; ``` Advanced filter Advanced string filter. ## Type Declaration ### $like? ```ts optional $like?: LikeFilter; ``` --- ## Type Alias: AdvancedUserTaskStateFilter ```ts type AdvancedUserTaskStateFilter = object; ``` Advanced filter Advanced UserTaskStateEnum filter. ## Properties ### $eq? ```ts optional $eq?: UserTaskStateEnum; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: UserTaskStateEnum[]; ``` Checks if the property matches any of the provided values. --- ### $like? ```ts optional $like?: LikeFilter; ``` --- ### $neq? ```ts optional $neq?: UserTaskStateEnum; ``` Checks for inequality with the provided value. --- ## Type Alias: AdvancedVariableKeyFilter ```ts type AdvancedVariableKeyFilter = object; ``` Advanced filter Advanced VariableKey filter. ## Properties ### $eq? ```ts optional $eq?: VariableKey; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: VariableKey[]; ``` Checks if the property matches any of the provided values. --- ### $neq? ```ts optional $neq?: VariableKey; ``` Checks for inequality with the provided value. --- ### $notIn? ```ts optional $notIn?: VariableKey[]; ``` Checks if the property matches none of the provided values. --- ## Type Alias: AgentInstanceCreationRequest ```ts type AgentInstanceCreationRequest = object; ``` Request to create a new agent instance. ## Properties ### definition ```ts definition: AgentInstanceDefinition; ``` Static definition set once at creation. --- ### elementInstanceKey ```ts elementInstanceKey: ElementInstanceKey; ``` The key of the AHSP or AI Agent Task element instance. The engine uses this key to infer processInstanceKey, elementId, processDefinitionKey, and tenantId. --- ### limits? ```ts optional limits?: AgentInstanceLimits; ``` Limits for the agent execution. When omitted, all limits default to -1 (no limit). --- ## Type Alias: AgentInstanceCreationResult ```ts type AgentInstanceCreationResult = object; ``` Response returned after successfully creating an agent instance. ## Properties ### agentInstanceKey ```ts agentInstanceKey: AgentInstanceKey; ``` The system-generated key for the created agent instance. --- ## Type Alias: AgentInstanceDefinition ```ts type AgentInstanceDefinition = object; ``` The static definition of an agent instance, set once at creation. ## Properties ### model ```ts model: string; ``` The LLM model identifier (for example, gpt-4o). --- ### provider ```ts provider: string; ``` The LLM provider (for example, openai or anthropic). --- ### systemPrompt ```ts systemPrompt: string; ``` The system prompt configured for this agent instance. --- ## Type Alias: AgentInstanceFilter ```ts type AgentInstanceFilter = object; ``` Agent instance search filter. ## Properties ### agentInstanceKey? ```ts optional agentInstanceKey?: AgentInstanceKeyFilterProperty; ``` The unique key of the agent instance. --- ### completionDate? ```ts optional completionDate?: DateTimeFilterProperty; ``` The completion date of the agent instance. --- ### creationDate? ```ts optional creationDate?: DateTimeFilterProperty; ``` The creation date of the agent instance. --- ### elementId? ```ts optional elementId?: ElementIdFilterProperty; ``` The BPMN element ID of the agent task. --- ### elementInstanceKeys? ```ts optional elementInstanceKeys?: ElementInstanceKeyFilterProperty[]; ``` The keys of element instances associated with this agent instance. If multiple keys are provided, the filter matches agent instances associated with all of the provided keys at the same time. --- ### lastUpdatedDate? ```ts optional lastUpdatedDate?: DateTimeFilterProperty; ``` The date the agent instance was last updated. --- ### processDefinitionKey? ```ts optional processDefinitionKey?: ProcessDefinitionKeyFilterProperty; ``` The key of the process definition associated with this agent instance. --- ### processInstanceKey? ```ts optional processInstanceKey?: ProcessInstanceKeyFilterProperty; ``` The key of the process instance that owns this agent instance. --- ### status? ```ts optional status?: AgentInstanceStatusFilterProperty; ``` The current status of the agent instance. --- ### tenantId? ```ts optional tenantId?: StringFilterProperty; ``` The tenant ID of the agent instance. --- ## Type Alias: AgentInstanceKey ```ts type AgentInstanceKey = CamundaKey<"AgentInstanceKey">; ``` System-generated key for an agent instance. --- ## Type Alias: AgentInstanceKeyExactMatch ```ts type AgentInstanceKeyExactMatch = AgentInstanceKey; ``` Exact match Matches the value exactly. --- ## Type Alias: AgentInstanceKeyFilterProperty ```ts type AgentInstanceKeyFilterProperty = | AgentInstanceKeyExactMatch | AdvancedAgentInstanceKeyFilter; ``` AgentInstanceKey property with full advanced search capabilities. --- ## Type Alias: AgentInstanceLimits ```ts type AgentInstanceLimits = object; ``` The configured limits for an agent instance, set once at creation. ## Properties ### maxModelCalls ```ts maxModelCalls: number; ``` Maximum LLM calls allowed. -1 if no limit is configured. --- ### maxTokens ```ts maxTokens: number; ``` Maximum total tokens allowed. -1 if no limit is configured. --- ### maxToolCalls ```ts maxToolCalls: number; ``` Maximum tool calls allowed. -1 if no limit is configured. --- ## Type Alias: AgentInstanceMetrics ```ts type AgentInstanceMetrics = object; ``` Aggregated metrics for an agent instance across all model calls. ## Properties ### inputTokens ```ts inputTokens: number; ``` Total input tokens consumed across all model calls. --- ### modelCalls ```ts modelCalls: number; ``` Total number of LLM calls made. --- ### outputTokens ```ts outputTokens: number; ``` Total output tokens produced across all model calls. --- ### toolCalls ```ts toolCalls: number; ``` Total number of tool calls made. --- ## Type Alias: AgentInstanceMetricsDelta ```ts type AgentInstanceMetricsDelta = object; ``` Metric increments to apply to the agent instance aggregate counters. The engine accumulates these deltas into running totals on each UPDATED event. All fields are optional; omit a field to leave the corresponding counter unchanged. ## Properties ### inputTokens? ```ts optional inputTokens?: number; ``` Increment to apply to the total input token counter. --- ### modelCalls? ```ts optional modelCalls?: number; ``` Increment to apply to the total model call counter. --- ### outputTokens? ```ts optional outputTokens?: number; ``` Increment to apply to the total output token counter. --- ### toolCalls? ```ts optional toolCalls?: number; ``` Increment to apply to the total tool call counter. --- ## Type Alias: AgentInstanceResult ```ts type AgentInstanceResult = object; ``` ## Properties ### agentInstanceKey ```ts agentInstanceKey: AgentInstanceKey; ``` The unique key for this agent instance. --- ### completionDate ```ts completionDate: string | null; ``` The date when this agent instance completed. Null while the agent is still running. --- ### creationDate ```ts creationDate: string; ``` The date when this agent instance was created. --- ### definition ```ts definition: AgentInstanceDefinition; ``` The static definition of the agent, including model, provider, and system prompt. --- ### elementId ```ts elementId: ElementId; ``` The BPMN element ID of the ad-hoc sub-process or AI agent task that owns this agent instance. --- ### elementInstanceKeys ```ts elementInstanceKeys: ElementInstanceKey[]; ``` The keys of all element instances associated with this agent instance. --- ### lastUpdatedDate ```ts lastUpdatedDate: string; ``` The date when this agent instance was last updated. --- ### limits ```ts limits: AgentInstanceLimits; ``` The configured limits for this agent instance, set once at creation. --- ### metrics ```ts metrics: AgentInstanceMetrics; ``` Aggregated metrics across all iterations of this agent instance. --- ### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` The key of the process definition associated with this agent instance. --- ### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The key of the process instance that owns this agent instance. --- ### status ```ts status: AgentInstanceStatusEnum; ``` --- ### tenantId ```ts tenantId: TenantId; ``` The tenant ID of this agent instance. --- ### tools ```ts tools: AgentTool[]; ``` The tools available to the agent. --- ## Type Alias: AgentInstanceSearchQuery ```ts type AgentInstanceSearchQuery = SearchQueryRequest & object; ``` Agent instance search request. ## Type Declaration ### filter? ```ts optional filter?: AgentInstanceFilter; ``` The agent instance search filters. ### sort? ```ts optional sort?: AgentInstanceSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: AgentInstanceSearchQueryResult ```ts type AgentInstanceSearchQueryResult = SearchQueryResponse & object; ``` Agent instance search response. ## Type Declaration ### items ```ts items: AgentInstanceResult[]; ``` The matching agent instances. --- ## Type Alias: AgentInstanceSearchQuerySortRequest ```ts type AgentInstanceSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: "creationDate" | "lastUpdatedDate" | "completionDate" | "status"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: AgentInstanceStatusEnum ```ts type AgentInstanceStatusEnum = (typeof AgentInstanceStatusEnum)[keyof typeof AgentInstanceStatusEnum]; ``` The current status of an agent instance. --- ## Type Alias: AgentInstanceStatusExactMatch ```ts type AgentInstanceStatusExactMatch = AgentInstanceStatusEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: AgentInstanceStatusFilterProperty ```ts type AgentInstanceStatusFilterProperty = | AgentInstanceStatusExactMatch | AdvancedAgentInstanceStatusFilter; ``` AgentInstanceStatusEnum property with full advanced search capabilities. --- ## Type Alias: AgentInstanceUpdateRequest ```ts type AgentInstanceUpdateRequest = object; ``` Request to update the mutable state of an agent instance. At least one of status, metrics, or tools must be provided. ## Properties ### metrics? ```ts optional metrics?: AgentInstanceMetricsDelta; ``` Metric increments to apply to the aggregate counters. --- ### status? ```ts optional status?: AgentInstanceStatusEnum; ``` The new status of the agent instance. --- ### tools? ```ts optional tools?: AgentTool[]; ``` The complete list of tools available to the agent, replacing any previously stored tools. When provided, the engine replaces the existing tool list with this value. --- ## Type Alias: AgentTool ```ts type AgentTool = object; ``` A tool available to the agent. ## Properties ### description ```ts description: string | null; ``` A human-readable description of the tool. --- ### elementId ```ts elementId: string | null; ``` The BPMN element ID of the tool element within the ad-hoc sub-process. --- ### name ```ts name: string; ``` The tool name as visible to the LLM. --- ## Type Alias: AncestorScopeInstruction ```ts type AncestorScopeInstruction = | (object & DirectAncestorKeyInstruction) | (object & InferredAncestorKeyInstruction) | (object & UseSourceParentKeyInstruction); ``` Defines the ancestor scope for the created element instances. The default behavior resembles a "direct" scope instruction with an `ancestorElementInstanceKey` of `"-1"`. --- ## Type Alias: AssignClientToGroupData ```ts type AssignClientToGroupData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### clientId ```ts clientId: ClientId; ``` The client ID. #### groupId ```ts groupId: GroupId; ``` The group ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/groups/{groupId}/clients/{clientId}"; ``` --- ## Type Alias: AssignClientToGroupError ```ts type AssignClientToGroupError = AssignClientToGroupErrors[keyof AssignClientToGroupErrors]; ``` --- ## Type Alias: AssignClientToGroupErrors ```ts type AssignClientToGroupErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The group with the given ID was not found. --- ### 409 ```ts 409: ProblemDetail; ``` The client with the given ID is already assigned to the group. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: AssignClientToGroupResponse ```ts type AssignClientToGroupResponse = AssignClientToGroupResponses[keyof AssignClientToGroupResponses]; ``` --- ## Type Alias: AssignClientToGroupResponses ```ts type AssignClientToGroupResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The client was assigned successfully to the group. --- ## Type Alias: AssignClientToTenantData ```ts type AssignClientToTenantData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### clientId ```ts clientId: ClientId; ``` The unique identifier of the application. #### tenantId ```ts tenantId: TenantId; ``` The unique identifier of the tenant. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/tenants/{tenantId}/clients/{clientId}"; ``` --- ## Type Alias: AssignClientToTenantError ```ts type AssignClientToTenantError = AssignClientToTenantErrors[keyof AssignClientToTenantErrors]; ``` --- ## Type Alias: AssignClientToTenantErrors ```ts type AssignClientToTenantErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The tenant was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: AssignClientToTenantResponse ```ts type AssignClientToTenantResponse = AssignClientToTenantResponses[keyof AssignClientToTenantResponses]; ``` --- ## Type Alias: AssignClientToTenantResponses ```ts type AssignClientToTenantResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The client was successfully assigned to the tenant. --- ## Type Alias: AssignGroupToTenantData ```ts type AssignGroupToTenantData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### groupId ```ts groupId: GroupId; ``` The unique identifier of the group. #### tenantId ```ts tenantId: TenantId; ``` The unique identifier of the tenant. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/tenants/{tenantId}/groups/{groupId}"; ``` --- ## Type Alias: AssignGroupToTenantError ```ts type AssignGroupToTenantError = AssignGroupToTenantErrors[keyof AssignGroupToTenantErrors]; ``` --- ## Type Alias: AssignGroupToTenantErrors ```ts type AssignGroupToTenantErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Not found. The tenant or group was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: AssignGroupToTenantResponse ```ts type AssignGroupToTenantResponse = AssignGroupToTenantResponses[keyof AssignGroupToTenantResponses]; ``` --- ## Type Alias: AssignGroupToTenantResponses ```ts type AssignGroupToTenantResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The group was successfully assigned to the tenant. --- ## Type Alias: AssignMappingRuleToGroupData ```ts type AssignMappingRuleToGroupData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### groupId ```ts groupId: GroupId; ``` The group ID. #### mappingRuleId ```ts mappingRuleId: MappingRuleId; ``` The mapping rule ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/groups/{groupId}/mapping-rules/{mappingRuleId}"; ``` --- ## Type Alias: AssignMappingRuleToGroupError ```ts type AssignMappingRuleToGroupError = AssignMappingRuleToGroupErrors[keyof AssignMappingRuleToGroupErrors]; ``` --- ## Type Alias: AssignMappingRuleToGroupErrors ```ts type AssignMappingRuleToGroupErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The group or mapping rule with the given ID was not found. --- ### 409 ```ts 409: ProblemDetail; ``` The mapping rule with the given ID is already assigned to the group. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: AssignMappingRuleToGroupResponse ```ts type AssignMappingRuleToGroupResponse = AssignMappingRuleToGroupResponses[keyof AssignMappingRuleToGroupResponses]; ``` --- ## Type Alias: AssignMappingRuleToGroupResponses ```ts type AssignMappingRuleToGroupResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The mapping rule was assigned successfully to the group. --- ## Type Alias: AssignMappingRuleToTenantData ```ts type AssignMappingRuleToTenantData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### mappingRuleId ```ts mappingRuleId: MappingRuleId; ``` The unique identifier of the mapping rule. #### tenantId ```ts tenantId: TenantId; ``` The unique identifier of the tenant. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/tenants/{tenantId}/mapping-rules/{mappingRuleId}"; ``` --- ## Type Alias: AssignMappingRuleToTenantError ```ts type AssignMappingRuleToTenantError = AssignMappingRuleToTenantErrors[keyof AssignMappingRuleToTenantErrors]; ``` --- ## Type Alias: AssignMappingRuleToTenantErrors ```ts type AssignMappingRuleToTenantErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Not found. The tenant or mapping rule was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: AssignMappingRuleToTenantResponse ```ts type AssignMappingRuleToTenantResponse = AssignMappingRuleToTenantResponses[keyof AssignMappingRuleToTenantResponses]; ``` --- ## Type Alias: AssignMappingRuleToTenantResponses ```ts type AssignMappingRuleToTenantResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The mapping rule was successfully assigned to the tenant. --- ## Type Alias: AssignRoleToClientData ```ts type AssignRoleToClientData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### clientId ```ts clientId: ClientId; ``` The client ID. #### roleId ```ts roleId: RoleId; ``` The role ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/roles/{roleId}/clients/{clientId}"; ``` --- ## Type Alias: AssignRoleToClientError ```ts type AssignRoleToClientError = AssignRoleToClientErrors[keyof AssignRoleToClientErrors]; ``` --- ## Type Alias: AssignRoleToClientErrors ```ts type AssignRoleToClientErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The role with the given ID was not found. --- ### 409 ```ts 409: ProblemDetail; ``` The role was already assigned to the client with the given ID. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: AssignRoleToClientResponse ```ts type AssignRoleToClientResponse = AssignRoleToClientResponses[keyof AssignRoleToClientResponses]; ``` --- ## Type Alias: AssignRoleToClientResponses ```ts type AssignRoleToClientResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The role was assigned successfully to the client. --- ## Type Alias: AssignRoleToGroupData ```ts type AssignRoleToGroupData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### groupId ```ts groupId: GroupId; ``` The group ID. #### roleId ```ts roleId: RoleId; ``` The role ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/roles/{roleId}/groups/{groupId}"; ``` --- ## Type Alias: AssignRoleToGroupError ```ts type AssignRoleToGroupError = AssignRoleToGroupErrors[keyof AssignRoleToGroupErrors]; ``` --- ## Type Alias: AssignRoleToGroupErrors ```ts type AssignRoleToGroupErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The role or group with the given ID was not found. --- ### 409 ```ts 409: ProblemDetail; ``` The role is already assigned to the group with the given ID. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: AssignRoleToGroupResponse ```ts type AssignRoleToGroupResponse = AssignRoleToGroupResponses[keyof AssignRoleToGroupResponses]; ``` --- ## Type Alias: AssignRoleToGroupResponses ```ts type AssignRoleToGroupResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The role was assigned successfully to the group. --- ## Type Alias: AssignRoleToMappingRuleData ```ts type AssignRoleToMappingRuleData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### mappingRuleId ```ts mappingRuleId: MappingRuleId; ``` The mapping rule ID. #### roleId ```ts roleId: RoleId; ``` The role ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/roles/{roleId}/mapping-rules/{mappingRuleId}"; ``` --- ## Type Alias: AssignRoleToMappingRuleError ```ts type AssignRoleToMappingRuleError = AssignRoleToMappingRuleErrors[keyof AssignRoleToMappingRuleErrors]; ``` --- ## Type Alias: AssignRoleToMappingRuleErrors ```ts type AssignRoleToMappingRuleErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The role or mapping rule with the given ID was not found. --- ### 409 ```ts 409: ProblemDetail; ``` The role is already assigned to the mapping rule with the given ID. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: AssignRoleToMappingRuleResponse ```ts type AssignRoleToMappingRuleResponse = AssignRoleToMappingRuleResponses[keyof AssignRoleToMappingRuleResponses]; ``` --- ## Type Alias: AssignRoleToMappingRuleResponses ```ts type AssignRoleToMappingRuleResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The role was assigned successfully to the mapping rule. --- ## Type Alias: AssignRoleToTenantData ```ts type AssignRoleToTenantData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### roleId ```ts roleId: RoleId; ``` The unique identifier of the role. #### tenantId ```ts tenantId: TenantId; ``` The unique identifier of the tenant. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/tenants/{tenantId}/roles/{roleId}"; ``` --- ## Type Alias: AssignRoleToTenantError ```ts type AssignRoleToTenantError = AssignRoleToTenantErrors[keyof AssignRoleToTenantErrors]; ``` --- ## Type Alias: AssignRoleToTenantErrors ```ts type AssignRoleToTenantErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Not found. The tenant or role was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: AssignRoleToTenantResponse ```ts type AssignRoleToTenantResponse = AssignRoleToTenantResponses[keyof AssignRoleToTenantResponses]; ``` --- ## Type Alias: AssignRoleToTenantResponses ```ts type AssignRoleToTenantResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The role was successfully assigned to the tenant. --- ## Type Alias: AssignRoleToUserData ```ts type AssignRoleToUserData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### roleId ```ts roleId: RoleId; ``` The role ID. #### username ```ts username: Username; ``` The user username. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/roles/{roleId}/users/{username}"; ``` --- ## Type Alias: AssignRoleToUserError ```ts type AssignRoleToUserError = AssignRoleToUserErrors[keyof AssignRoleToUserErrors]; ``` --- ## Type Alias: AssignRoleToUserErrors ```ts type AssignRoleToUserErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The role or user with the given ID or username was not found. --- ### 409 ```ts 409: ProblemDetail; ``` The role is already assigned to the user with the given ID. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: AssignRoleToUserResponse ```ts type AssignRoleToUserResponse = AssignRoleToUserResponses[keyof AssignRoleToUserResponses]; ``` --- ## Type Alias: AssignRoleToUserResponses ```ts type AssignRoleToUserResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The role was assigned successfully to the user. --- ## Type Alias: AssignUserTaskData ```ts type AssignUserTaskData = object; ``` ## Properties ### body ```ts body: UserTaskAssignmentRequest; ``` --- ### path ```ts path: object; ``` #### userTaskKey ```ts userTaskKey: UserTaskKey; ``` The key of the user task to assign. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/user-tasks/{userTaskKey}/assignment"; ``` --- ## Type Alias: AssignUserTaskError ```ts type AssignUserTaskError = AssignUserTaskErrors[keyof AssignUserTaskErrors]; ``` --- ## Type Alias: AssignUserTaskErrors ```ts type AssignUserTaskErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 404 ```ts 404: ProblemDetail; ``` The user task with the given key was not found. --- ### 409 ```ts 409: ProblemDetail; ``` The user task with the given key is in the wrong state currently. More details are provided in the response body. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ### 504 ```ts 504: ProblemDetail; ``` The request timed out between the gateway and the broker. For these endpoints, this often happens when user task listeners are configured and the corresponding listener job is not completed within the request timeout. Common causes include no available job workers for the listener type, busy or crashed job workers, or delayed job completion. As with any gateway timeout, general timeout causes (for example transient network issues) can also result in a 504 response. Troubleshooting: - verify that job workers for the listener type are running and healthy - check worker logs for crashes, retries, and completion failures - check network connectivity between workers, gateway, and broker - retry with backoff after transient failures - fail without retries if a problem persists --- ## Type Alias: AssignUserTaskResponse ```ts type AssignUserTaskResponse = AssignUserTaskResponses[keyof AssignUserTaskResponses]; ``` --- ## Type Alias: AssignUserTaskResponses ```ts type AssignUserTaskResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The user task's assignment was adjusted. --- ## Type Alias: AssignUserToGroupData ```ts type AssignUserToGroupData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### groupId ```ts groupId: GroupId; ``` The group ID. #### username ```ts username: Username; ``` The user username. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/groups/{groupId}/users/{username}"; ``` --- ## Type Alias: AssignUserToGroupError ```ts type AssignUserToGroupError = AssignUserToGroupErrors[keyof AssignUserToGroupErrors]; ``` --- ## Type Alias: AssignUserToGroupErrors ```ts type AssignUserToGroupErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The group or user with the given ID or username was not found. --- ### 409 ```ts 409: ProblemDetail; ``` The user with the given ID is already assigned to the group. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: AssignUserToGroupResponse ```ts type AssignUserToGroupResponse = AssignUserToGroupResponses[keyof AssignUserToGroupResponses]; ``` --- ## Type Alias: AssignUserToGroupResponses ```ts type AssignUserToGroupResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The user was assigned successfully to the group. --- ## Type Alias: AssignUserToTenantData ```ts type AssignUserToTenantData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### tenantId ```ts tenantId: TenantId; ``` The unique identifier of the tenant. #### username ```ts username: Username; ``` The unique identifier of the user. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/tenants/{tenantId}/users/{username}"; ``` --- ## Type Alias: AssignUserToTenantError ```ts type AssignUserToTenantError = AssignUserToTenantErrors[keyof AssignUserToTenantErrors]; ``` --- ## Type Alias: AssignUserToTenantErrors ```ts type AssignUserToTenantErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Not found. The tenant or user was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: AssignUserToTenantResponse ```ts type AssignUserToTenantResponse = AssignUserToTenantResponses[keyof AssignUserToTenantResponses]; ``` --- ## Type Alias: AssignUserToTenantResponses ```ts type AssignUserToTenantResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The user was successfully assigned to the tenant. --- ## Type Alias: AuditLogActorTypeEnum ```ts type AuditLogActorTypeEnum = (typeof AuditLogActorTypeEnum)[keyof typeof AuditLogActorTypeEnum]; ``` The type of actor who performed the operation. --- ## Type Alias: AuditLogActorTypeExactMatch ```ts type AuditLogActorTypeExactMatch = AuditLogActorTypeEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: AuditLogActorTypeFilterProperty ```ts type AuditLogActorTypeFilterProperty = | AuditLogActorTypeExactMatch | AdvancedActorTypeFilter; ``` AuditLogActorTypeEnum property with full advanced search capabilities. --- ## Type Alias: AuditLogCategoryEnum ```ts type AuditLogCategoryEnum = (typeof AuditLogCategoryEnum)[keyof typeof AuditLogCategoryEnum]; ``` The category of the audit log operation. --- ## Type Alias: AuditLogEntityKey ```ts type AuditLogEntityKey = CamundaKey<"AuditLogEntityKey">; ``` System-generated entity key for an audit log entry. --- ## Type Alias: AuditLogEntityKeyExactMatch ```ts type AuditLogEntityKeyExactMatch = AuditLogEntityKey; ``` Exact match Matches the value exactly. --- ## Type Alias: AuditLogEntityKeyFilterProperty ```ts type AuditLogEntityKeyFilterProperty = | AuditLogEntityKeyExactMatch | AdvancedAuditLogEntityKeyFilter; ``` EntityKey property with full advanced search capabilities. --- ## Type Alias: AuditLogEntityTypeEnum ```ts type AuditLogEntityTypeEnum = (typeof AuditLogEntityTypeEnum)[keyof typeof AuditLogEntityTypeEnum]; ``` The type of entity affected by the operation. --- ## Type Alias: AuditLogFilter ```ts type AuditLogFilter = object; ``` Audit log filter request ## Properties ### actorId? ```ts optional actorId?: StringFilterProperty; ``` The actor ID search filter. --- ### actorType? ```ts optional actorType?: AuditLogActorTypeFilterProperty; ``` The actor type search filter. --- ### agentElementId? ```ts optional agentElementId?: StringFilterProperty; ``` The agent element ID search filter. --- ### auditLogKey? ```ts optional auditLogKey?: AuditLogKeyFilterProperty; ``` The audit log key search filter. --- ### batchOperationType? ```ts optional batchOperationType?: BatchOperationTypeFilterProperty; ``` The batch operation type search filter. --- ### category? ```ts optional category?: CategoryFilterProperty; ``` The category search filter. --- ### decisionDefinitionId? ```ts optional decisionDefinitionId?: StringFilterProperty; ``` The decision definition ID search filter. --- ### decisionDefinitionKey? ```ts optional decisionDefinitionKey?: DecisionDefinitionKeyFilterProperty; ``` The decision definition key search filter. --- ### decisionEvaluationKey? ```ts optional decisionEvaluationKey?: DecisionEvaluationKeyFilterProperty; ``` The decision evaluation key search filter. --- ### decisionRequirementsId? ```ts optional decisionRequirementsId?: StringFilterProperty; ``` The decision requirements ID search filter. --- ### decisionRequirementsKey? ```ts optional decisionRequirementsKey?: DecisionRequirementsKeyFilterProperty; ``` The decision requirements key search filter. --- ### deploymentKey? ```ts optional deploymentKey?: DeploymentKeyFilterProperty; ``` The deployment key search filter. --- ### elementInstanceKey? ```ts optional elementInstanceKey?: ElementInstanceKeyFilterProperty; ``` The element instance key search filter. --- ### entityDescription? ```ts optional entityDescription?: StringFilterProperty; ``` The entity description filter. --- ### entityKey? ```ts optional entityKey?: AuditLogEntityKeyFilterProperty; ``` The entity key search filter. --- ### entityType? ```ts optional entityType?: EntityTypeFilterProperty; ``` The entity type search filter. --- ### formKey? ```ts optional formKey?: FormKeyFilterProperty; ``` The form key search filter. --- ### jobKey? ```ts optional jobKey?: JobKeyFilterProperty; ``` The job key search filter. --- ### operationType? ```ts optional operationType?: OperationTypeFilterProperty; ``` The operation type search filter. --- ### processDefinitionId? ```ts optional processDefinitionId?: StringFilterProperty; ``` The process definition ID search filter. --- ### processDefinitionKey? ```ts optional processDefinitionKey?: ProcessDefinitionKeyFilterProperty; ``` The process definition key search filter. --- ### processInstanceKey? ```ts optional processInstanceKey?: ProcessInstanceKeyFilterProperty; ``` The process instance key search filter. --- ### relatedEntityKey? ```ts optional relatedEntityKey?: AuditLogEntityKeyFilterProperty; ``` The related entity key search filter. --- ### relatedEntityType? ```ts optional relatedEntityType?: EntityTypeFilterProperty; ``` The related entity type search filter. --- ### resourceKey? ```ts optional resourceKey?: ResourceKeyFilterProperty; ``` The resource key search filter. --- ### result? ```ts optional result?: AuditLogResultFilterProperty; ``` The result search filter. --- ### tenantId? ```ts optional tenantId?: StringFilterProperty; ``` The tenant ID search filter. --- ### timestamp? ```ts optional timestamp?: DateTimeFilterProperty; ``` The timestamp search filter. --- ### userTaskKey? ```ts optional userTaskKey?: BasicStringFilterProperty; ``` The user task key search filter. --- ## Type Alias: AuditLogKey ```ts type AuditLogKey = CamundaKey<"AuditLogKey">; ``` System-generated key for an audit log entry. --- ## Type Alias: AuditLogKeyExactMatch ```ts type AuditLogKeyExactMatch = AuditLogKey; ``` Exact match Matches the value exactly. --- ## Type Alias: AuditLogKeyFilterProperty ```ts type AuditLogKeyFilterProperty = | AuditLogKeyExactMatch | AdvancedAuditLogKeyFilter; ``` AuditLogKey property with full advanced search capabilities. --- ## Type Alias: AuditLogOperationTypeEnum ```ts type AuditLogOperationTypeEnum = (typeof AuditLogOperationTypeEnum)[keyof typeof AuditLogOperationTypeEnum]; ``` The type of operation performed. --- ## Type Alias: AuditLogResult ```ts type AuditLogResult = object; ``` Audit log item. ## Properties ### actorId ```ts actorId: string | null; ``` The ID of the actor who performed the operation. --- ### actorType ```ts actorType: AuditLogActorTypeEnum | null; ``` The type of the actor who performed the operation. --- ### agentElementId ```ts agentElementId: string | null; ``` The element ID of the agent that performed the operation (e.g. ad-hoc subprocess element ID). --- ### auditLogKey ```ts auditLogKey: AuditLogKey; ``` The unique key of the audit log entry. --- ### batchOperationKey ```ts batchOperationKey: BatchOperationKey | null; ``` Key of the batch operation. --- ### batchOperationType ```ts batchOperationType: BatchOperationTypeEnum | null; ``` The type of batch operation performed, if this is part of a batch. --- ### category ```ts category: AuditLogCategoryEnum; ``` --- ### decisionDefinitionId ```ts decisionDefinitionId: DecisionDefinitionId | null; ``` The decision definition ID. --- ### decisionDefinitionKey ```ts decisionDefinitionKey: DecisionDefinitionKey | null; ``` The key of the decision definition. --- ### decisionEvaluationKey ```ts decisionEvaluationKey: DecisionEvaluationKey | null; ``` The key of the decision evaluation. --- ### decisionRequirementsId ```ts decisionRequirementsId: string | null; ``` The decision requirements ID. --- ### decisionRequirementsKey ```ts decisionRequirementsKey: DecisionRequirementsKey | null; ``` The assigned key of the decision requirements. --- ### deploymentKey ```ts deploymentKey: DeploymentKey | null; ``` The key of the deployment. --- ### elementInstanceKey ```ts elementInstanceKey: ElementInstanceKey | null; ``` The key of the element instance. --- ### entityDescription ```ts entityDescription: string | null; ``` Additional description of the entity affected by the operation. For example, for variable operations, this will contain the variable name. --- ### entityKey ```ts entityKey: AuditLogEntityKey; ``` --- ### entityType ```ts entityType: AuditLogEntityTypeEnum; ``` --- ### formKey ```ts formKey: FormKey | null; ``` The key of the form. --- ### jobKey ```ts jobKey: JobKey | null; ``` The key of the job. --- ### operationType ```ts operationType: AuditLogOperationTypeEnum; ``` --- ### processDefinitionId ```ts processDefinitionId: ProcessDefinitionId | null; ``` The process definition ID. --- ### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey | null; ``` The key of the process definition. --- ### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey | null; ``` The key of the process instance. --- ### relatedEntityKey ```ts relatedEntityKey: AuditLogEntityKey | null; ``` The key of the related entity. The content depends on the operation type and entity type. For example, for authorization operations, this will contain the ID of the owner (e.g., user or group) the authorization belongs to. --- ### relatedEntityType ```ts relatedEntityType: AuditLogEntityTypeEnum | null; ``` The type of the related entity. The content depends on the operation type and entity type. For example, for authorization operations, this will contain the type of the owner (e.g., USER or GROUP) the authorization belongs to. --- ### resourceKey ```ts resourceKey: ResourceKey | null; ``` The system-assigned key for this resource. --- ### result ```ts result: AuditLogResultEnum; ``` --- ### rootProcessInstanceKey ```ts rootProcessInstanceKey: ProcessInstanceKey | null; ``` The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. --- ### tenantId ```ts tenantId: TenantId | null; ``` The tenant ID of the audit log. --- ### timestamp ```ts timestamp: string; ``` The timestamp when the operation occurred. --- ### userTaskKey ```ts userTaskKey: UserTaskKey | null; ``` The key of the user task. --- ## Type Alias: AuditLogResultEnum ```ts type AuditLogResultEnum = (typeof AuditLogResultEnum)[keyof typeof AuditLogResultEnum]; ``` The result status of the operation. --- ## Type Alias: AuditLogResultExactMatch ```ts type AuditLogResultExactMatch = AuditLogResultEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: AuditLogResultFilterProperty ```ts type AuditLogResultFilterProperty = | AuditLogResultExactMatch | AdvancedResultFilter; ``` AuditLogResultEnum property with full advanced search capabilities. --- ## Type Alias: AuditLogSearchQueryRequest ```ts type AuditLogSearchQueryRequest = SearchQueryRequest & object; ``` Audit log search request. ## Type Declaration ### filter? ```ts optional filter?: AuditLogFilter; ``` The audit log search filters. ### sort? ```ts optional sort?: AuditLogSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: AuditLogSearchQueryResult ```ts type AuditLogSearchQueryResult = SearchQueryResponse & object; ``` Audit log search response. ## Type Declaration ### items ```ts items: AuditLogResult[]; ``` The matching audit logs. --- ## Type Alias: AuditLogSearchQuerySortRequest ```ts type AuditLogSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: | "actorId" | "actorType" | "auditLogKey" | "batchOperationKey" | "batchOperationType" | "category" | "decisionDefinitionId" | "decisionDefinitionKey" | "decisionEvaluationKey" | "decisionRequirementsId" | "decisionRequirementsKey" | "elementInstanceKey" | "entityKey" | "entityType" | "jobKey" | "operationType" | "processDefinitionId" | "processDefinitionKey" | "processInstanceKey" | "result" | "tenantId" | "timestamp" | "userTaskKey"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: AuthStrategy ```ts type AuthStrategy = "NONE" | "OAUTH" | "BASIC"; ``` --- ## Type Alias: AuthenticationConfigurationResponse ```ts type AuthenticationConfigurationResponse = object; ``` Configuration for authentication and session management. ## Properties ### canLogout ```ts canLogout: boolean; ``` Whether users can log out (false for SaaS deployments). --- ### isLoginDelegated ```ts isLoginDelegated: boolean; ``` Whether login is delegated to an external identity provider. --- ## Type Alias: AuthorizationCreateResult ```ts type AuthorizationCreateResult = object; ``` ## Properties ### authorizationKey ```ts authorizationKey: AuthorizationKey; ``` The key of the created authorization. --- ## Type Alias: AuthorizationFilter ```ts type AuthorizationFilter = object; ``` Authorization search filter. ## Properties ### ownerId? ```ts optional ownerId?: string; ``` The ID of the owner of permissions. --- ### ownerType? ```ts optional ownerType?: OwnerTypeEnum; ``` --- ### resourceIds? ```ts optional resourceIds?: string[]; ``` The IDs of the resource to search permissions for. --- ### resourcePropertyNames? ```ts optional resourcePropertyNames?: string[]; ``` The names of the resource properties to search permissions for. --- ### resourceType? ```ts optional resourceType?: ResourceTypeEnum; ``` The type of resource to search permissions for. --- ## Type Alias: AuthorizationIdBasedRequest ```ts type AuthorizationIdBasedRequest = object; ``` ## Properties ### ownerId ```ts ownerId: string; ``` The ID of the owner of the permissions. --- ### ownerType ```ts ownerType: OwnerTypeEnum; ``` --- ### permissionTypes ```ts permissionTypes: PermissionTypeEnum[]; ``` The permission types to add. --- ### resourceId ```ts resourceId: string; ``` The ID of the resource to add permissions to. --- ### resourceType ```ts resourceType: ResourceTypeEnum; ``` The type of resource to add permissions to. --- ## Type Alias: AuthorizationKey ```ts type AuthorizationKey = CamundaKey<"AuthorizationKey">; ``` System-generated key for an authorization. --- ## Type Alias: AuthorizationPropertyBasedRequest ```ts type AuthorizationPropertyBasedRequest = object; ``` ## Properties ### ownerId ```ts ownerId: string; ``` The ID of the owner of the permissions. --- ### ownerType ```ts ownerType: OwnerTypeEnum; ``` --- ### permissionTypes ```ts permissionTypes: PermissionTypeEnum[]; ``` The permission types to add. --- ### resourcePropertyName ```ts resourcePropertyName: string; ``` The name of the resource property on which this authorization is based. --- ### resourceType ```ts resourceType: ResourceTypeEnum; ``` The type of resource to add permissions to. --- ## Type Alias: AuthorizationRequest ```ts type AuthorizationRequest = | AuthorizationIdBasedRequest | AuthorizationPropertyBasedRequest; ``` Defines an authorization request. Either an id-based or a property-based authorization can be provided. --- ## Type Alias: AuthorizationResult ```ts type AuthorizationResult = object; ``` ## Properties ### authorizationKey ```ts authorizationKey: AuthorizationKey; ``` The key of the authorization. --- ### ownerId ```ts ownerId: string; ``` The ID of the owner of permissions. --- ### ownerType ```ts ownerType: OwnerTypeEnum; ``` --- ### permissionTypes ```ts permissionTypes: PermissionTypeEnum[]; ``` Specifies the types of the permissions. --- ### resourceId ```ts resourceId: string | null; ``` ID of the resource the permission relates to (mutually exclusive with `resourcePropertyName`). --- ### resourcePropertyName ```ts resourcePropertyName: string | null; ``` The name of the resource property the permission relates to (mutually exclusive with `resourceId`). --- ### resourceType ```ts resourceType: ResourceTypeEnum; ``` The type of resource that the permissions relate to. --- ## Type Alias: AuthorizationSearchQuery ```ts type AuthorizationSearchQuery = SearchQueryRequest & object; ``` ## Type Declaration ### filter? ```ts optional filter?: AuthorizationFilter; ``` The authorization search filters. ### sort? ```ts optional sort?: AuthorizationSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: AuthorizationSearchQuerySortRequest ```ts type AuthorizationSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: | "ownerId" | "ownerType" | "resourceId" | "resourcePropertyName" | "resourceType"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: AuthorizationSearchResult ```ts type AuthorizationSearchResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: AuthorizationResult[]; ``` The matching authorizations. --- ## Type Alias: BackpressureSeverity ```ts type BackpressureSeverity = "healthy" | "soft" | "severe"; ``` --- ## Type Alias: BaseProcessInstanceFilterFields ```ts type BaseProcessInstanceFilterFields = object; ``` Base process instance search filter. ## Properties ### ~~batchOperationId?~~ ```ts optional batchOperationId?: StringFilterProperty; ``` The batch operation id. **Deprecated**: Use `batchOperationKey` instead. This field will be removed in a future release. If both `batchOperationId` and `batchOperationKey` are provided, the request will be rejected with a 400 error. #### Deprecated --- ### batchOperationKey? ```ts optional batchOperationKey?: StringFilterProperty; ``` The batch operation key. --- ### businessId? ```ts optional businessId?: StringFilterProperty; ``` The business id associated with the process instance. --- ### elementId? ```ts optional elementId?: StringFilterProperty; ``` The element id associated with the process instance. --- ### elementInstanceState? ```ts optional elementInstanceState?: ElementInstanceStateFilterProperty; ``` The state of the element instances associated with the process instance. --- ### endDate? ```ts optional endDate?: DateTimeFilterProperty; ``` The end date. --- ### errorMessage? ```ts optional errorMessage?: StringFilterProperty; ``` The error message related to the process. --- ### hasElementInstanceIncident? ```ts optional hasElementInstanceIncident?: boolean; ``` Whether the element instance has an incident or not. --- ### hasIncident? ```ts optional hasIncident?: boolean; ``` Whether this process instance has a related incident or not. --- ### hasRetriesLeft? ```ts optional hasRetriesLeft?: boolean; ``` Whether the process has failed jobs with retries left. --- ### incidentErrorHashCode? ```ts optional incidentErrorHashCode?: IntegerFilterProperty; ``` The incident error hash code, associated with this process. --- ### parentElementInstanceKey? ```ts optional parentElementInstanceKey?: ElementInstanceKeyFilterProperty; ``` The parent element instance key. --- ### parentProcessInstanceKey? ```ts optional parentProcessInstanceKey?: ProcessInstanceKeyFilterProperty; ``` The parent process instance key. --- ### processInstanceKey? ```ts optional processInstanceKey?: ProcessInstanceKeyFilterProperty; ``` The key of this process instance. --- ### startDate? ```ts optional startDate?: DateTimeFilterProperty; ``` The start date. --- ### state? ```ts optional state?: ProcessInstanceStateFilterProperty; ``` The process instance state. --- ### tags? ```ts optional tags?: TagSet; ``` --- ### tenantId? ```ts optional tenantId?: StringFilterProperty; ``` The tenant id. --- ### variables? ```ts optional variables?: VariableValueFilterProperty[]; ``` The process instance variables. --- ## Type Alias: BasicStringFilter ```ts type BasicStringFilter = object; ``` Advanced filter Basic advanced string filter. ## Properties ### $eq? ```ts optional $eq?: string; ``` Checks for equality with the provided value. --- ### $exists? ```ts optional $exists?: boolean; ``` Checks if the current property exists. --- ### $in? ```ts optional $in?: string[]; ``` Checks if the property matches any of the provided values. --- ### $neq? ```ts optional $neq?: string; ``` Checks for inequality with the provided value. --- ### $notIn? ```ts optional $notIn?: string[]; ``` Checks if the property matches none of the provided values. --- ## Type Alias: BasicStringFilterProperty ```ts type BasicStringFilterProperty = string | BasicStringFilter; ``` String property with basic advanced search capabilities. --- ## Type Alias: BatchOperationCreatedResult ```ts type BatchOperationCreatedResult = object; ``` The created batch operation. ## Properties ### batchOperationKey ```ts batchOperationKey: BatchOperationKey; ``` Key of the batch operation. --- ### batchOperationType ```ts batchOperationType: BatchOperationTypeEnum; ``` --- ## Type Alias: BatchOperationError ```ts type BatchOperationError = object; ``` ## Properties ### message ```ts message: string; ``` The error message that occurred during the batch operation. --- ### partitionId ```ts partitionId: number; ``` The partition ID where the error occurred. --- ### type ```ts type: "QUERY_FAILED" | "RESULT_BUFFER_SIZE_EXCEEDED"; ``` The type of the error that occurred during the batch operation. --- ## Type Alias: BatchOperationFilter ```ts type BatchOperationFilter = object; ``` Batch operation filter request. ## Properties ### actorId? ```ts optional actorId?: StringFilterProperty; ``` The ID of the actor who performed the operation. --- ### actorType? ```ts optional actorType?: AuditLogActorTypeEnum; ``` The type of the actor who performed the operation. --- ### batchOperationKey? ```ts optional batchOperationKey?: BasicStringFilterProperty; ``` The key (or operate legacy ID) of the batch operation. --- ### operationType? ```ts optional operationType?: BatchOperationTypeFilterProperty; ``` The type of the batch operation. --- ### state? ```ts optional state?: BatchOperationStateFilterProperty; ``` The state of the batch operation. --- ## Type Alias: BatchOperationItemFilter ```ts type BatchOperationItemFilter = object; ``` Batch operation item filter request. ## Properties ### batchOperationKey? ```ts optional batchOperationKey?: BasicStringFilterProperty; ``` The key (or operate legacy ID) of the batch operation. --- ### itemKey? ```ts optional itemKey?: BasicStringFilterProperty; ``` The key of the item, e.g. a process instance key. --- ### operationType? ```ts optional operationType?: BatchOperationTypeFilterProperty; ``` The type of the batch operation. --- ### processInstanceKey? ```ts optional processInstanceKey?: ProcessInstanceKeyFilterProperty; ``` The process instance key of the processed item. --- ### state? ```ts optional state?: BatchOperationItemStateFilterProperty; ``` The state of the batch operation. --- ## Type Alias: BatchOperationItemResponse ```ts type BatchOperationItemResponse = object; ``` ## Properties ### batchOperationKey ```ts batchOperationKey: BatchOperationKey; ``` The key (or operate legacy ID) of the batch operation. --- ### errorMessage ```ts errorMessage: string | null; ``` The error message from the engine in case of a failed operation. --- ### itemKey ```ts itemKey: string; ``` Key of the item, e.g. a process instance key. --- ### operationType ```ts operationType: BatchOperationTypeEnum; ``` --- ### processedDate ```ts processedDate: string | null; ``` The date this item was processed. This is `null` if the item has not yet been processed. --- ### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey | null; ``` The process instance key of the processed item. Null for batch-op types whose targets are not process instances (e.g. DELETE_DECISION_INSTANCE, DELETE_DECISION_DEFINITION, DELETE_PROCESS_DEFINITION). --- ### rootProcessInstanceKey ```ts rootProcessInstanceKey: ProcessInstanceKey | null; ``` The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. --- ### state ```ts state: "ACTIVE" | "COMPLETED" | "SKIPPED" | "CANCELED" | "FAILED"; ``` State of the item. --- ## Type Alias: BatchOperationItemSearchQuery ```ts type BatchOperationItemSearchQuery = SearchQueryRequest & object; ``` Batch operation item search request. ## Type Declaration ### filter? ```ts optional filter?: BatchOperationItemFilter; ``` The batch operation item search filters. ### sort? ```ts optional sort?: BatchOperationItemSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: BatchOperationItemSearchQueryResult ```ts type BatchOperationItemSearchQueryResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: BatchOperationItemResponse[]; ``` The matching batch operation items. --- ## Type Alias: BatchOperationItemSearchQuerySortRequest ```ts type BatchOperationItemSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: | "batchOperationKey" | "itemKey" | "processInstanceKey" | "processedDate" | "state"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: BatchOperationItemStateEnum ```ts type BatchOperationItemStateEnum = (typeof BatchOperationItemStateEnum)[keyof typeof BatchOperationItemStateEnum]; ``` The batch operation item state. --- ## Type Alias: BatchOperationItemStateExactMatch ```ts type BatchOperationItemStateExactMatch = BatchOperationItemStateEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: BatchOperationItemStateFilterProperty ```ts type BatchOperationItemStateFilterProperty = | BatchOperationItemStateExactMatch | AdvancedBatchOperationItemStateFilter; ``` BatchOperationItemStateEnum property with full advanced search capabilities. --- ## Type Alias: BatchOperationKey ```ts type BatchOperationKey = CamundaKey<"BatchOperationKey">; ``` System-generated key for an batch operation. --- ## Type Alias: BatchOperationResponse ```ts type BatchOperationResponse = object; ``` ## Properties ### actorId ```ts actorId: string | null; ``` The ID of the actor who performed the operation. Available for batch operations created since 8.9. --- ### actorType ```ts actorType: AuditLogActorTypeEnum | null; ``` The type of the actor who performed the operation. This is `null` if the batch operation was created before 8.9, or if the actor information is not available. --- ### batchOperationKey ```ts batchOperationKey: BatchOperationKey; ``` Key or (Operate Legacy ID = UUID) of the batch operation. --- ### batchOperationType ```ts batchOperationType: BatchOperationTypeEnum; ``` --- ### endDate ```ts endDate: string | null; ``` The end date of the batch operation. This is `null` if the batch operation is still running. --- ### errors ```ts errors: BatchOperationError[]; ``` The errors that occurred per partition during the batch operation. --- ### operationsCompletedCount ```ts operationsCompletedCount: number; ``` The number of successfully completed tasks. --- ### operationsFailedCount ```ts operationsFailedCount: number; ``` The number of items which failed during execution of the batch operation. (e.g. because they are rejected by the Zeebe engine). --- ### operationsTotalCount ```ts operationsTotalCount: number; ``` The total number of items contained in this batch operation. --- ### startDate ```ts startDate: string | null; ``` The start date of the batch operation. This is `null` if the batch operation has not yet started. --- ### state ```ts state: BatchOperationStateEnum; ``` --- ## Type Alias: BatchOperationSearchQuery ```ts type BatchOperationSearchQuery = SearchQueryRequest & object; ``` Batch operation search request. ## Type Declaration ### filter? ```ts optional filter?: BatchOperationFilter; ``` The batch operation search filters. ### sort? ```ts optional sort?: BatchOperationSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: BatchOperationSearchQueryResult ```ts type BatchOperationSearchQueryResult = SearchQueryResponse & object; ``` The batch operation search query result. ## Type Declaration ### items ```ts items: BatchOperationResponse[]; ``` The matching batch operations. --- ## Type Alias: BatchOperationSearchQuerySortRequest ```ts type BatchOperationSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: | "batchOperationKey" | "operationType" | "state" | "startDate" | "endDate" | "actorType" | "actorId"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: BatchOperationStateEnum ```ts type BatchOperationStateEnum = (typeof BatchOperationStateEnum)[keyof typeof BatchOperationStateEnum]; ``` The batch operation state. --- ## Type Alias: BatchOperationStateExactMatch ```ts type BatchOperationStateExactMatch = BatchOperationStateEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: BatchOperationStateFilterProperty ```ts type BatchOperationStateFilterProperty = | BatchOperationStateExactMatch | AdvancedBatchOperationStateFilter; ``` BatchOperationStateEnum property with full advanced search capabilities. --- ## Type Alias: BatchOperationTypeEnum ```ts type BatchOperationTypeEnum = (typeof BatchOperationTypeEnum)[keyof typeof BatchOperationTypeEnum]; ``` The type of the batch operation. --- ## Type Alias: BatchOperationTypeExactMatch ```ts type BatchOperationTypeExactMatch = BatchOperationTypeEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: BatchOperationTypeFilterProperty ```ts type BatchOperationTypeFilterProperty = | BatchOperationTypeExactMatch | AdvancedBatchOperationTypeFilter; ``` BatchOperationTypeEnum property with full advanced search capabilities. --- ## Type Alias: BroadcastSignalData ```ts type BroadcastSignalData = object; ``` ## Properties ### body ```ts body: SignalBroadcastRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/signals/broadcast"; ``` --- ## Type Alias: BroadcastSignalError ```ts type BroadcastSignalError = BroadcastSignalErrors[keyof BroadcastSignalErrors]; ``` --- ## Type Alias: BroadcastSignalErrors ```ts type BroadcastSignalErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 404 ```ts 404: ProblemDetail; ``` The signal is not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: BroadcastSignalResponse ```ts type BroadcastSignalResponse = BroadcastSignalResponses[keyof BroadcastSignalResponses]; ``` --- ## Type Alias: BroadcastSignalResponses ```ts type BroadcastSignalResponses = object; ``` ## Properties ### 200 ```ts 200: SignalBroadcastResult; ``` The signal was broadcast. --- ## Type Alias: BrokerInfo ```ts type BrokerInfo = object; ``` Provides information on a broker node. ## Properties ### host ```ts host: string; ``` The hostname for reaching the broker. --- ### nodeId ```ts nodeId: number; ``` The unique (within a cluster) node ID for the broker. --- ### partitions ```ts partitions: Partition[]; ``` A list of partitions managed or replicated on this broker. --- ### port ```ts port: number; ``` The port for reaching the broker. --- ### version ```ts version: string; ``` The broker version. --- ## Type Alias: BusinessId ```ts type BusinessId = CamundaKey<"BusinessId">; ``` An optional, user-defined string identifier that identifies the process instance within the scope of a process definition (scoped by tenant). If provided and uniqueness enforcement is enabled, the engine will reject creation if another root process instance with the same business id is already active for the same process definition. Note that any active child process instances with the same business id are not taken into account. --- ## Type Alias: CamundaClientLoose ```ts type CamundaClientLoose = ReturnType; ``` --- ## Type Alias: CamundaFpClient ```ts type CamundaFpClient = Fpify; ``` --- ## Type Alias: CamundaKey # Type Alias: CamundaKey\ ```ts type CamundaKey = string & object; ``` ## Type Declaration ### \_\_brand ```ts readonly __brand: T; ``` ## Type Parameters ### T `T` _extends_ `string` = `string` --- ## Type Alias: CamundaResultClient ```ts type CamundaResultClient = object & { [K in keyof CamundaClient]: CamundaClient[K] extends ( a: infer A ) => Promise ? (a: A) => Promise> : CamundaClient[K] extends (a: infer A) => any ? ( a: A ) => | Promise>> | ReturnType : CamundaClient[K]; }; ``` ## Type Declaration ### inner ```ts inner: CamundaClient; ``` --- ## Type Alias: CamundaUserResult ```ts type CamundaUserResult = object; ``` ## Properties ### authorizedComponents ```ts authorizedComponents: string[]; ``` The web components the user is authorized to use. --- ### c8Links ```ts c8Links: object; ``` The links to the components in the C8 stack. #### Index Signature ```ts [key: string]: string ``` --- ### canLogout ```ts canLogout: boolean; ``` Flag for understanding if the user is able to perform logout. --- ### displayName ```ts displayName: string | null; ``` The display name of the user. --- ### email ```ts email: string | null; ``` The email of the user. --- ### groups ```ts groups: string[]; ``` The groups assigned to the user. --- ### roles ```ts roles: string[]; ``` The roles assigned to the user. --- ### salesPlanType ```ts salesPlanType: string | null; ``` The plan of the user. --- ### tenants ```ts tenants: TenantResult[]; ``` The tenants the user is a member of. --- ### username ```ts username: Username; ``` The username of the user. --- ## Type Alias: CancelBatchOperationData ```ts type CancelBatchOperationData = object; ``` ## Properties ### body? ```ts optional body?: unknown; ``` --- ### path ```ts path: object; ``` #### batchOperationKey ```ts batchOperationKey: BatchOperationKey; ``` The key (or operate legacy ID) of the batch operation. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/batch-operations/{batchOperationKey}/cancellation"; ``` --- ## Type Alias: CancelBatchOperationError ```ts type CancelBatchOperationError = CancelBatchOperationErrors[keyof CancelBatchOperationErrors]; ``` --- ## Type Alias: CancelBatchOperationErrors ```ts type CancelBatchOperationErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Not found. The batch operation was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: CancelBatchOperationResponse ```ts type CancelBatchOperationResponse = CancelBatchOperationResponses[keyof CancelBatchOperationResponses]; ``` --- ## Type Alias: CancelBatchOperationResponses ```ts type CancelBatchOperationResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The batch operation cancel request was created. --- ## Type Alias: CancelProcessInstanceData ```ts type CancelProcessInstanceData = object; ``` ## Properties ### body? ```ts optional body?: CancelProcessInstanceRequest; ``` --- ### path ```ts path: object; ``` #### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The key of the process instance to cancel. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-instances/{processInstanceKey}/cancellation"; ``` --- ## Type Alias: CancelProcessInstanceError ```ts type CancelProcessInstanceError = CancelProcessInstanceErrors[keyof CancelProcessInstanceErrors]; ``` --- ## Type Alias: CancelProcessInstanceErrors ```ts type CancelProcessInstanceErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 404 ```ts 404: ProblemDetail; ``` The process instance is not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ### 504 ```ts 504: ProblemDetail; ``` The request timed out between the gateway and the broker. For these endpoints, this often happens when user task listeners are configured and the corresponding listener job is not completed within the request timeout. Common causes include no available job workers for the listener type, busy or crashed job workers, or delayed job completion. As with any gateway timeout, general timeout causes (for example transient network issues) can also result in a 504 response. Troubleshooting: - verify that job workers for the listener type are running and healthy - check worker logs for crashes, retries, and completion failures - check network connectivity between workers, gateway, and broker - retry with backoff after transient failures - fail without retries if a problem persists --- ## Type Alias: CancelProcessInstanceRequest ```ts type CancelProcessInstanceRequest = { operationReference?: OperationReference; } | null; ``` --- ## Type Alias: CancelProcessInstanceResponse ```ts type CancelProcessInstanceResponse = CancelProcessInstanceResponses[keyof CancelProcessInstanceResponses]; ``` --- ## Type Alias: CancelProcessInstanceResponses ```ts type CancelProcessInstanceResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The process instance is canceled. --- ## Type Alias: CancelProcessInstancesBatchOperationData ```ts type CancelProcessInstancesBatchOperationData = object; ``` ## Properties ### body ```ts body: ProcessInstanceCancellationBatchOperationRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-instances/cancellation"; ``` --- ## Type Alias: CancelProcessInstancesBatchOperationError ```ts type CancelProcessInstancesBatchOperationError = CancelProcessInstancesBatchOperationErrors[keyof CancelProcessInstancesBatchOperationErrors]; ``` --- ## Type Alias: CancelProcessInstancesBatchOperationErrors ```ts type CancelProcessInstancesBatchOperationErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The process instance batch operation failed. More details are provided in the response body. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: CancelProcessInstancesBatchOperationResponse ```ts type CancelProcessInstancesBatchOperationResponse = CancelProcessInstancesBatchOperationResponses[keyof CancelProcessInstancesBatchOperationResponses]; ``` --- ## Type Alias: CancelProcessInstancesBatchOperationResponses ```ts type CancelProcessInstancesBatchOperationResponses = object; ``` ## Properties ### 200 ```ts 200: BatchOperationCreatedResult; ``` The batch operation request was created. --- ## Type Alias: CategoryExactMatch ```ts type CategoryExactMatch = AuditLogCategoryEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: CategoryFilterProperty ```ts type CategoryFilterProperty = CategoryExactMatch | AdvancedCategoryFilter; ``` AuditLogCategoryEnum property with full advanced search capabilities. --- ## Type Alias: Changeset ```ts type Changeset = { [key: string]: unknown; candidateGroups?: string[] | null; candidateUsers?: string[] | null; dueDate?: string | null; followUpDate?: string | null; priority?: number | null; } | null; ``` JSON object with changed task attribute values. The following attributes can be adjusted with this endpoint, additional attributes will be ignored: - `candidateGroups` - reset by providing an empty list - `candidateUsers` - reset by providing an empty list - `dueDate` - reset by providing an empty String - `followUpDate` - reset by providing an empty String - `priority` - minimum 0, maximum 100, default 50 Providing any of those attributes with a `null` value or omitting it preserves the persisted attribute's value. The assignee cannot be adjusted with this endpoint, use the Assign task endpoint. This ensures correct event emission for assignee changes. ## Union Members ### Type Literal ```ts { [key: string]: unknown; candidateGroups?: string[] | null; candidateUsers?: string[] | null; dueDate?: string | null; followUpDate?: string | null; priority?: number | null; } ``` ### Index Signature ```ts [key: string]: unknown ``` #### candidateGroups? ```ts optional candidateGroups?: string[] | null; ``` The list of candidate groups of the task. Reset by providing an empty list. #### candidateUsers? ```ts optional candidateUsers?: string[] | null; ``` The list of candidate users of the task. Reset by providing an empty list. #### dueDate? ```ts optional dueDate?: string | null; ``` The due date of the task. Reset by providing an empty String. #### followUpDate? ```ts optional followUpDate?: string | null; ``` The follow-up date of the task. Reset by providing an empty String. #### priority? ```ts optional priority?: number | null; ``` The priority of the task. --- `null` --- ## Type Alias: ClientId ```ts type ClientId = CamundaKey<"ClientId">; ``` The unique identifier of an OAuth client. Minted outside the Camunda REST API: in SaaS by Console, in Self-Managed with OIDC by the external identity provider (e.g. EntraID, Keycloak, Okta). In Self-Managed with Basic authentication, machine-to-machine applications are modelled as users instead — see the user identifier. --- ## Type Alias: ClientOptions ```ts type ClientOptions = object; ``` ## Properties ### baseUrl ```ts baseUrl: "{schema}://{host}:{port}/v2" | (string & object); ``` --- ## Type Alias: ClockPinRequest ```ts type ClockPinRequest = object; ``` ## Properties ### timestamp ```ts timestamp: number; ``` The exact time in epoch milliseconds to which the clock should be pinned. --- ## Type Alias: CloudConfigurationResponse ```ts type CloudConfigurationResponse = object; ``` Configuration for SaaS/cloud-specific settings. ## Properties ### clusterId ```ts clusterId: string | null; ``` The SaaS cluster ID, if applicable. --- ### mixpanelAPIHost ```ts mixpanelAPIHost: string | null; ``` The Mixpanel API host URL. --- ### mixpanelToken ```ts mixpanelToken: string | null; ``` The Mixpanel analytics token for the cloud UI. --- ### organizationId ```ts organizationId: string | null; ``` The SaaS organization ID, if applicable. --- ### stage ```ts stage: CloudStage | null; ``` The cloud deployment stage. --- ## Type Alias: CloudStage ```ts type CloudStage = "dev" | "int" | "prod"; ``` The cloud deployment stage. --- ## Type Alias: ClusterVariableName ```ts type ClusterVariableName = CamundaKey<"ClusterVariableName">; ``` The name of a cluster variable. Unique within its scope (global or tenant-specific). --- ## Type Alias: ClusterVariableResult ```ts type ClusterVariableResult = ClusterVariableResultBase & object; ``` ## Type Declaration ### value ```ts value: string; ``` Full value of this cluster variable. --- ## Type Alias: ClusterVariableResultBase ```ts type ClusterVariableResultBase = object; ``` Cluster variable response item. ## Properties ### name ```ts name: ClusterVariableName; ``` The name of the cluster variable. Unique within its scope (global or tenant-specific). --- ### scope ```ts scope: ClusterVariableScopeEnum; ``` --- ### tenantId ```ts tenantId: string | null; ``` Only provided if the cluster variable scope is TENANT. Null for global scope variables. --- ## Type Alias: ClusterVariableScopeEnum ```ts type ClusterVariableScopeEnum = (typeof ClusterVariableScopeEnum)[keyof typeof ClusterVariableScopeEnum]; ``` The scope of a cluster variable. --- ## Type Alias: ClusterVariableScopeExactMatch ```ts type ClusterVariableScopeExactMatch = ClusterVariableScopeEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: ClusterVariableScopeFilterProperty ```ts type ClusterVariableScopeFilterProperty = | ClusterVariableScopeExactMatch | AdvancedClusterVariableScopeFilter; ``` ClusterVariableScopeEnum property with full advanced search capabilities. --- ## Type Alias: ClusterVariableSearchQueryFilterRequest ```ts type ClusterVariableSearchQueryFilterRequest = object; ``` Cluster variable filter request. ## Properties ### isTruncated? ```ts optional isTruncated?: boolean; ``` Filter cluster variables by truncation status of their stored values. When true, returns only variables whose stored values are truncated (i.e., the value exceeds the storage size limit and is truncated in storage). When false, returns only variables with non-truncated stored values. This filter is based on the underlying storage characteristic, not the response format. --- ### name? ```ts optional name?: StringFilterProperty; ``` Name of the cluster variable. --- ### scope? ```ts optional scope?: ClusterVariableScopeFilterProperty; ``` The scope filter for cluster variables. --- ### tenantId? ```ts optional tenantId?: StringFilterProperty; ``` Tenant ID of this variable. --- ### value? ```ts optional value?: StringFilterProperty; ``` The value of the cluster variable. --- ## Type Alias: ClusterVariableSearchQueryRequest ```ts type ClusterVariableSearchQueryRequest = SearchQueryRequest & object; ``` Cluster variable search query request. ## Type Declaration ### filter? ```ts optional filter?: ClusterVariableSearchQueryFilterRequest; ``` The cluster variable search filters. ### sort? ```ts optional sort?: ClusterVariableSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: ClusterVariableSearchQueryResult ```ts type ClusterVariableSearchQueryResult = SearchQueryResponse & object; ``` Cluster variable search query response. ## Type Declaration ### items ```ts items: ClusterVariableSearchResult[]; ``` The matching cluster variables. --- ## Type Alias: ClusterVariableSearchQuerySortRequest ```ts type ClusterVariableSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: "name" | "value" | "tenantId" | "scope"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: ClusterVariableSearchResult ```ts type ClusterVariableSearchResult = ClusterVariableResultBase & object; ``` Cluster variable search response item. ## Type Declaration ### isTruncated ```ts isTruncated: boolean; ``` Whether the value is truncated or not. ### value ```ts value: string; ``` Value of this cluster variable. Can be truncated. --- ## Type Alias: CompleteJobData ```ts type CompleteJobData = object; ``` ## Properties ### body? ```ts optional body?: JobCompletionRequest; ``` --- ### path ```ts path: object; ``` #### jobKey ```ts jobKey: JobKey; ``` The key of the job to complete. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/jobs/{jobKey}/completion"; ``` --- ## Type Alias: CompleteJobError ```ts type CompleteJobError = CompleteJobErrors[keyof CompleteJobErrors]; ``` --- ## Type Alias: CompleteJobErrors ```ts type CompleteJobErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 404 ```ts 404: ProblemDetail; ``` The job with the given key was not found. --- ### 409 ```ts 409: ProblemDetail; ``` The job with the given key is in the wrong state currently. More details are provided in the response body. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: CompleteJobResponse ```ts type CompleteJobResponse = CompleteJobResponses[keyof CompleteJobResponses]; ``` --- ## Type Alias: CompleteJobResponses ```ts type CompleteJobResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The job was completed successfully. --- ## Type Alias: CompleteUserTaskData ```ts type CompleteUserTaskData = object; ``` ## Properties ### body? ```ts optional body?: UserTaskCompletionRequest; ``` --- ### path ```ts path: object; ``` #### userTaskKey ```ts userTaskKey: UserTaskKey; ``` The key of the user task to complete. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/user-tasks/{userTaskKey}/completion"; ``` --- ## Type Alias: CompleteUserTaskError ```ts type CompleteUserTaskError = CompleteUserTaskErrors[keyof CompleteUserTaskErrors]; ``` --- ## Type Alias: CompleteUserTaskErrors ```ts type CompleteUserTaskErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 404 ```ts 404: ProblemDetail; ``` The user task with the given key was not found. --- ### 409 ```ts 409: ProblemDetail; ``` The user task with the given key is in the wrong state currently. More details are provided in the response body. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ### 504 ```ts 504: ProblemDetail; ``` The request timed out between the gateway and the broker. For these endpoints, this often happens when user task listeners are configured and the corresponding listener job is not completed within the request timeout. Common causes include no available job workers for the listener type, busy or crashed job workers, or delayed job completion. As with any gateway timeout, general timeout causes (for example transient network issues) can also result in a 504 response. Troubleshooting: - verify that job workers for the listener type are running and healthy - check worker logs for crashes, retries, and completion failures - check network connectivity between workers, gateway, and broker - retry with backoff after transient failures - fail without retries if a problem persists --- ## Type Alias: CompleteUserTaskResponse ```ts type CompleteUserTaskResponse = CompleteUserTaskResponses[keyof CompleteUserTaskResponses]; ``` --- ## Type Alias: CompleteUserTaskResponses ```ts type CompleteUserTaskResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The user task was completed successfully. --- ## Type Alias: ComponentsConfigurationResponse ```ts type ComponentsConfigurationResponse = object; ``` Configuration for active Camunda components in the deployment. ## Properties ### active ```ts active: WebappComponent[]; ``` List of webapp components whose UI is enabled in this deployment. --- ## Type Alias: ConditionalEvaluationInstruction ```ts type ConditionalEvaluationInstruction = object; ``` ## Properties ### processDefinitionKey? ```ts optional processDefinitionKey?: ProcessDefinitionKey; ``` Used to evaluate root-level conditional start events of the process definition with the given key. --- ### tenantId? ```ts optional tenantId?: TenantId; ``` Used to evaluate root-level conditional start events for a tenant with the given ID. This will only evaluate root-level conditional start events of process definitions which belong to the tenant. --- ### variables ```ts variables: object; ``` JSON object representing the variables to use for evaluation of the conditions and to pass to the process instances that have been triggered. #### Index Signature ```ts [key: string]: unknown ``` --- ## Type Alias: ConditionalEvaluationKey ```ts type ConditionalEvaluationKey = CamundaKey<"ConditionalEvaluationKey">; ``` System-generated key for a conditional evaluation. --- ## Type Alias: CorrelateMessageData ```ts type CorrelateMessageData = object; ``` ## Properties ### body ```ts body: MessageCorrelationRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/messages/correlation"; ``` --- ## Type Alias: CorrelateMessageError ```ts type CorrelateMessageError = CorrelateMessageErrors[keyof CorrelateMessageErrors]; ``` --- ## Type Alias: CorrelateMessageErrors ```ts type CorrelateMessageErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Not found --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: CorrelateMessageResponse ```ts type CorrelateMessageResponse = CorrelateMessageResponses[keyof CorrelateMessageResponses]; ``` --- ## Type Alias: CorrelateMessageResponses ```ts type CorrelateMessageResponses = object; ``` ## Properties ### 200 ```ts 200: MessageCorrelationResult; ``` The message is correlated to one or more process instances --- ## Type Alias: CorrelatedMessageSubscriptionFilter ```ts type CorrelatedMessageSubscriptionFilter = object; ``` Correlated message subscriptions search filter. ## Properties ### correlationKey? ```ts optional correlationKey?: StringFilterProperty; ``` The correlation key of the message. --- ### correlationTime? ```ts optional correlationTime?: DateTimeFilterProperty; ``` The time when the message was correlated. --- ### elementId? ```ts optional elementId?: StringFilterProperty; ``` The element ID that received the message. --- ### elementInstanceKey? ```ts optional elementInstanceKey?: ElementInstanceKeyFilterProperty; ``` The element instance key that received the message. --- ### messageKey? ```ts optional messageKey?: BasicStringFilterProperty; ``` The message key. --- ### messageName? ```ts optional messageName?: StringFilterProperty; ``` The name of the message. --- ### partitionId? ```ts optional partitionId?: IntegerFilterProperty; ``` The partition ID that correlated the message. --- ### processDefinitionId? ```ts optional processDefinitionId?: StringFilterProperty; ``` The process definition ID associated with this correlated message subscription. --- ### processDefinitionKey? ```ts optional processDefinitionKey?: ProcessDefinitionKeyFilterProperty; ``` The process definition key associated with this correlated message subscription. For intermediate message events, this only works for data created with 8.9 and later. --- ### processInstanceKey? ```ts optional processInstanceKey?: ProcessInstanceKeyFilterProperty; ``` The process instance key associated with this correlated message subscription. --- ### subscriptionKey? ```ts optional subscriptionKey?: MessageSubscriptionKeyFilterProperty; ``` The subscription key that received the message. --- ### tenantId? ```ts optional tenantId?: StringFilterProperty; ``` The tenant ID associated with this correlated message subscription. --- ## Type Alias: CorrelatedMessageSubscriptionResult ```ts type CorrelatedMessageSubscriptionResult = object; ``` ## Properties ### correlationKey ```ts correlationKey: string | null; ``` The correlation key of the message. --- ### correlationTime ```ts correlationTime: string; ``` The time when the message was correlated. --- ### elementId ```ts elementId: string; ``` The element ID that received the message. --- ### elementInstanceKey ```ts elementInstanceKey: ElementInstanceKey | null; ``` The element instance key that received the message. It is `null` for start event subscriptions. --- ### messageKey ```ts messageKey: MessageKey; ``` The message key. --- ### messageName ```ts messageName: string; ``` The name of the message. --- ### partitionId ```ts partitionId: number; ``` The partition ID that correlated the message. --- ### processDefinitionId ```ts processDefinitionId: ProcessDefinitionId; ``` The process definition ID associated with this correlated message subscription. --- ### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` The process definition key associated with this correlated message subscription. --- ### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The process instance key associated with this correlated message subscription. --- ### rootProcessInstanceKey ```ts rootProcessInstanceKey: ProcessInstanceKey | null; ``` The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. --- ### subscriptionKey ```ts subscriptionKey: MessageSubscriptionKey; ``` The subscription key that received the message. --- ### tenantId ```ts tenantId: TenantId; ``` The tenant ID associated with this correlated message subscription. --- ## Type Alias: CorrelatedMessageSubscriptionSearchQuery ```ts type CorrelatedMessageSubscriptionSearchQuery = SearchQueryRequest & object; ``` ## Type Declaration ### filter? ```ts optional filter?: CorrelatedMessageSubscriptionFilter; ``` The correlated message subscriptions search filters. ### sort? ```ts optional sort?: CorrelatedMessageSubscriptionSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: CorrelatedMessageSubscriptionSearchQueryResult ```ts type CorrelatedMessageSubscriptionSearchQueryResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: CorrelatedMessageSubscriptionResult[]; ``` The matching correlated message subscriptions. --- ## Type Alias: CorrelatedMessageSubscriptionSearchQuerySortRequest ```ts type CorrelatedMessageSubscriptionSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: | "correlationKey" | "correlationTime" | "elementId" | "elementInstanceKey" | "messageKey" | "messageName" | "partitionId" | "processDefinitionId" | "processDefinitionKey" | "processInstanceKey" | "subscriptionKey" | "tenantId"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: CreateAdminUserData ```ts type CreateAdminUserData = object; ``` ## Properties ### body ```ts body: UserRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/setup/user"; ``` --- ## Type Alias: CreateAdminUserError ```ts type CreateAdminUserError = CreateAdminUserErrors[keyof CreateAdminUserErrors]; ``` --- ## Type Alias: CreateAdminUserErrors ```ts type CreateAdminUserErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: CreateAdminUserResponse ```ts type CreateAdminUserResponse = CreateAdminUserResponses[keyof CreateAdminUserResponses]; ``` --- ## Type Alias: CreateAdminUserResponses ```ts type CreateAdminUserResponses = object; ``` ## Properties ### 201 ```ts 201: UserCreateResult; ``` The admin user was created successfully. --- ## Type Alias: CreateAgentInstanceData ```ts type CreateAgentInstanceData = object; ``` ## Properties ### body ```ts body: AgentInstanceCreationRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/agent-instances"; ``` --- ## Type Alias: CreateAgentInstanceError ```ts type CreateAgentInstanceError = CreateAgentInstanceErrors[keyof CreateAgentInstanceErrors]; ``` --- ## Type Alias: CreateAgentInstanceErrors ```ts type CreateAgentInstanceErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The elementInstanceKey does not correspond to an active element instance. More details are provided in the response body. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: CreateAgentInstanceResponse ```ts type CreateAgentInstanceResponse = CreateAgentInstanceResponses[keyof CreateAgentInstanceResponses]; ``` --- ## Type Alias: CreateAgentInstanceResponses ```ts type CreateAgentInstanceResponses = object; ``` ## Properties ### 200 ```ts 200: AgentInstanceCreationResult; ``` The agent instance was created. --- ## Type Alias: CreateAuthorizationData ```ts type CreateAuthorizationData = object; ``` ## Properties ### body ```ts body: AuthorizationRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/authorizations"; ``` --- ## Type Alias: CreateAuthorizationError ```ts type CreateAuthorizationError = CreateAuthorizationErrors[keyof CreateAuthorizationErrors]; ``` --- ## Type Alias: CreateAuthorizationErrors ```ts type CreateAuthorizationErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The owner was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: CreateAuthorizationResponse ```ts type CreateAuthorizationResponse = CreateAuthorizationResponses[keyof CreateAuthorizationResponses]; ``` --- ## Type Alias: CreateAuthorizationResponses ```ts type CreateAuthorizationResponses = object; ``` ## Properties ### 201 ```ts 201: AuthorizationCreateResult; ``` The authorization was created successfully. --- ## Type Alias: CreateClusterVariableRequest ```ts type CreateClusterVariableRequest = object; ``` ## Properties ### name ```ts name: ClusterVariableName; ``` The name of the cluster variable. Must be unique within its scope (global or tenant-specific). --- ### value ```ts value: object; ``` The value of the cluster variable. Can be any JSON object or primitive value. Will be serialized as a JSON string in responses. #### Index Signature ```ts [key: string]: unknown ``` --- ## Type Alias: CreateDeploymentData ```ts type CreateDeploymentData = object; ``` ## Properties ### body ```ts body: object; ``` #### resources ```ts resources: (Blob | File)[]; ``` The binary data to create the deployment resources. It is possible to have more than one form part with different form part names for the binary data to create a deployment. #### tenantId? ```ts optional tenantId?: TenantId; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/deployments"; ``` --- ## Type Alias: CreateDeploymentError ```ts type CreateDeploymentError = CreateDeploymentErrors[keyof CreateDeploymentErrors]; ``` --- ## Type Alias: CreateDeploymentErrors ```ts type CreateDeploymentErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: CreateDeploymentResponse ```ts type CreateDeploymentResponse = CreateDeploymentResponses[keyof CreateDeploymentResponses]; ``` --- ## Type Alias: CreateDeploymentResponses ```ts type CreateDeploymentResponses = object; ``` ## Properties ### 200 ```ts 200: DeploymentResult; ``` The resources are deployed. --- ## Type Alias: CreateDocumentData ```ts type CreateDocumentData = object; ``` ## Properties ### body ```ts body: object; ``` #### file ```ts file: Blob | File; ``` #### metadata? ```ts optional metadata?: DocumentMetadata; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: object; ``` #### documentId? ```ts optional documentId?: DocumentId; ``` The ID of the document to upload. If not provided, a new ID will be generated. Specifying an existing ID will result in an error if the document already exists. #### storeId? ```ts optional storeId?: string; ``` The ID of the document store to upload the documents to. Currently, only a single document store is supported per cluster. However, this attribute is included to allow for potential future support of multiple document stores. --- ### url ```ts url: "/documents"; ``` --- ## Type Alias: CreateDocumentError ```ts type CreateDocumentError = CreateDocumentErrors[keyof CreateDocumentErrors]; ``` --- ## Type Alias: CreateDocumentErrors ```ts type CreateDocumentErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 415 ```ts 415: ProblemDetail; ``` The server cannot process the request because the media type (Content-Type) of the request payload is not supported by the server for the requested resource and method. --- ## Type Alias: CreateDocumentLinkData ```ts type CreateDocumentLinkData = object; ``` ## Properties ### body? ```ts optional body?: DocumentLinkRequest; ``` --- ### path ```ts path: object; ``` #### documentId ```ts documentId: DocumentId; ``` The ID of the document to link. --- ### query? ```ts optional query?: object; ``` #### contentHash? ```ts optional contentHash?: string; ``` The hash of the document content that was computed by the document store during upload. The hash is part of the document reference that is returned when uploading a document. If the client fails to provide the correct hash, the request will be rejected. #### storeId? ```ts optional storeId?: string; ``` The ID of the document store where the document is located. --- ### url ```ts url: "/documents/{documentId}/links"; ``` --- ## Type Alias: CreateDocumentLinkError ```ts type CreateDocumentLinkError = CreateDocumentLinkErrors[keyof CreateDocumentLinkErrors]; ``` --- ## Type Alias: CreateDocumentLinkErrors ```ts type CreateDocumentLinkErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ## Type Alias: CreateDocumentLinkResponse ```ts type CreateDocumentLinkResponse = CreateDocumentLinkResponses[keyof CreateDocumentLinkResponses]; ``` --- ## Type Alias: CreateDocumentLinkResponses ```ts type CreateDocumentLinkResponses = object; ``` ## Properties ### 201 ```ts 201: DocumentLink; ``` The document link was created successfully. --- ## Type Alias: CreateDocumentResponse ```ts type CreateDocumentResponse = CreateDocumentResponses[keyof CreateDocumentResponses]; ``` --- ## Type Alias: CreateDocumentResponses ```ts type CreateDocumentResponses = object; ``` ## Properties ### 201 ```ts 201: DocumentReference; ``` The document was uploaded successfully. --- ## Type Alias: CreateDocumentsData ```ts type CreateDocumentsData = object; ``` ## Properties ### body ```ts body: object; ``` #### files ```ts files: (Blob | File)[]; ``` The documents to upload. #### metadataList? ```ts optional metadataList?: DocumentMetadata[]; ``` Optional JSON array of metadata object whose index aligns with each file entry. The metadata array must have the same length as the files array. --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: object; ``` #### storeId? ```ts optional storeId?: string; ``` The ID of the document store to upload the documents to. Currently, only a single document store is supported per cluster. However, this attribute is included to allow for potential future support of multiple document stores. --- ### url ```ts url: "/documents/batch"; ``` --- ## Type Alias: CreateDocumentsError ```ts type CreateDocumentsError = CreateDocumentsErrors[keyof CreateDocumentsErrors]; ``` --- ## Type Alias: CreateDocumentsErrors ```ts type CreateDocumentsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 415 ```ts 415: ProblemDetail; ``` The server cannot process the request because the media type (Content-Type) of the request payload is not supported by the server for the requested resource and method. --- ## Type Alias: CreateDocumentsResponse ```ts type CreateDocumentsResponse = CreateDocumentsResponses[keyof CreateDocumentsResponses]; ``` --- ## Type Alias: CreateDocumentsResponses ```ts type CreateDocumentsResponses = object; ``` ## Properties ### 201 ```ts 201: DocumentCreationBatchResponse; ``` All documents were uploaded successfully. --- ### 207 ```ts 207: DocumentCreationBatchResponse; ``` Some documents were uploaded successfully, others failed. --- ## Type Alias: CreateElementInstanceVariablesData ```ts type CreateElementInstanceVariablesData = object; ``` ## Properties ### body ```ts body: SetVariableRequest; ``` --- ### path ```ts path: object; ``` #### elementInstanceKey ```ts elementInstanceKey: ElementInstanceKey; ``` The key of the element instance to update the variables for. This can be the process instance key (as obtained during instance creation), or a given element, such as a service task (see the `elementInstanceKey` on the job message). --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/element-instances/{elementInstanceKey}/variables"; ``` --- ## Type Alias: CreateElementInstanceVariablesError ```ts type CreateElementInstanceVariablesError = CreateElementInstanceVariablesErrors[keyof CreateElementInstanceVariablesErrors]; ``` --- ## Type Alias: CreateElementInstanceVariablesErrors ```ts type CreateElementInstanceVariablesErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ### 504 ```ts 504: ProblemDetail; ``` The request timed out between the gateway and the broker. For these endpoints, this often happens when user task listeners are configured and the corresponding listener job is not completed within the request timeout. Common causes include no available job workers for the listener type, busy or crashed job workers, or delayed job completion. As with any gateway timeout, general timeout causes (for example transient network issues) can also result in a 504 response. Troubleshooting: - verify that job workers for the listener type are running and healthy - check worker logs for crashes, retries, and completion failures - check network connectivity between workers, gateway, and broker - retry with backoff after transient failures - fail without retries if a problem persists --- ## Type Alias: CreateElementInstanceVariablesResponse ```ts type CreateElementInstanceVariablesResponse = CreateElementInstanceVariablesResponses[keyof CreateElementInstanceVariablesResponses]; ``` --- ## Type Alias: CreateElementInstanceVariablesResponses ```ts type CreateElementInstanceVariablesResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The variables were updated. --- ## Type Alias: CreateGlobalClusterVariableData ```ts type CreateGlobalClusterVariableData = object; ``` ## Properties ### body ```ts body: CreateClusterVariableRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/cluster-variables/global"; ``` --- ## Type Alias: CreateGlobalClusterVariableError ```ts type CreateGlobalClusterVariableError = CreateGlobalClusterVariableErrors[keyof CreateGlobalClusterVariableErrors]; ``` --- ## Type Alias: CreateGlobalClusterVariableErrors ```ts type CreateGlobalClusterVariableErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: CreateGlobalClusterVariableResponse ```ts type CreateGlobalClusterVariableResponse = CreateGlobalClusterVariableResponses[keyof CreateGlobalClusterVariableResponses]; ``` --- ## Type Alias: CreateGlobalClusterVariableResponses ```ts type CreateGlobalClusterVariableResponses = object; ``` ## Properties ### 200 ```ts 200: ClusterVariableResult; ``` Cluster variable created --- ## Type Alias: CreateGlobalTaskListenerData ```ts type CreateGlobalTaskListenerData = object; ``` ## Properties ### body ```ts body: CreateGlobalTaskListenerRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/global-task-listeners"; ``` --- ## Type Alias: CreateGlobalTaskListenerError ```ts type CreateGlobalTaskListenerError = CreateGlobalTaskListenerErrors[keyof CreateGlobalTaskListenerErrors]; ``` --- ## Type Alias: CreateGlobalTaskListenerErrors ```ts type CreateGlobalTaskListenerErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 409 ```ts 409: ProblemDetail; ``` A global listener with this id already exists. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: CreateGlobalTaskListenerRequest ```ts type CreateGlobalTaskListenerRequest = GlobalTaskListenerBase & object; ``` ## Type Declaration ### eventTypes ```ts eventTypes: GlobalTaskListenerEventTypes; ``` ### id ```ts id: GlobalListenerId; ``` --- ## Type Alias: CreateGlobalTaskListenerResponse ```ts type CreateGlobalTaskListenerResponse = CreateGlobalTaskListenerResponses[keyof CreateGlobalTaskListenerResponses]; ``` --- ## Type Alias: CreateGlobalTaskListenerResponses ```ts type CreateGlobalTaskListenerResponses = object; ``` ## Properties ### 201 ```ts 201: GlobalTaskListenerResult; ``` The global user task listener was created successfully. --- ## Type Alias: CreateGroupData ```ts type CreateGroupData = object; ``` ## Properties ### body? ```ts optional body?: GroupCreateRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/groups"; ``` --- ## Type Alias: CreateGroupError ```ts type CreateGroupError = CreateGroupErrors[keyof CreateGroupErrors]; ``` --- ## Type Alias: CreateGroupErrors ```ts type CreateGroupErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: CreateGroupResponse ```ts type CreateGroupResponse = CreateGroupResponses[keyof CreateGroupResponses]; ``` --- ## Type Alias: CreateGroupResponses ```ts type CreateGroupResponses = object; ``` ## Properties ### 201 ```ts 201: GroupCreateResult; ``` The group was created successfully. --- ## Type Alias: CreateMappingRuleData ```ts type CreateMappingRuleData = object; ``` ## Properties ### body? ```ts optional body?: MappingRuleCreateRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/mapping-rules"; ``` --- ## Type Alias: CreateMappingRuleError ```ts type CreateMappingRuleError = CreateMappingRuleErrors[keyof CreateMappingRuleErrors]; ``` --- ## Type Alias: CreateMappingRuleErrors ```ts type CreateMappingRuleErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` The request to create a mapping rule was denied. More details are provided in the response body. --- ### 404 ```ts 404: ProblemDetail; ``` The request to create a mapping rule was denied. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: CreateMappingRuleResponse ```ts type CreateMappingRuleResponse = CreateMappingRuleResponses[keyof CreateMappingRuleResponses]; ``` --- ## Type Alias: CreateMappingRuleResponses ```ts type CreateMappingRuleResponses = object; ``` ## Properties ### 201 ```ts 201: MappingRuleCreateResult; ``` The mapping rule was created successfully. --- ## Type Alias: CreateProcessInstanceData ```ts type CreateProcessInstanceData = object; ``` ## Properties ### body ```ts body: ProcessInstanceCreationInstruction; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-instances"; ``` --- ## Type Alias: CreateProcessInstanceError ```ts type CreateProcessInstanceError = CreateProcessInstanceErrors[keyof CreateProcessInstanceErrors]; ``` --- ## Type Alias: CreateProcessInstanceErrors ```ts type CreateProcessInstanceErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 409 ```ts 409: ProblemDetail; ``` The process instance creation was rejected due to a business ID uniqueness conflict. This can happen only when Business ID Uniqueness Control is enabled and an active root process instance with the provided business ID already exists for the same process definition and tenant. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ### 504 ```ts 504: ProblemDetail; ``` The process instance creation request timed out in the gateway. This can happen if the `awaitCompletion` request parameter is set to `true` and the created process instance did not complete within the defined request timeout. This often happens when the created instance is not fully automated or contains wait states. --- ## Type Alias: CreateProcessInstanceResponse ```ts type CreateProcessInstanceResponse = CreateProcessInstanceResponses[keyof CreateProcessInstanceResponses]; ``` --- ## Type Alias: CreateProcessInstanceResponses ```ts type CreateProcessInstanceResponses = object; ``` ## Properties ### 200 ```ts 200: CreateProcessInstanceResult; ``` The process instance was created. --- ## Type Alias: CreateProcessInstanceResult ```ts type CreateProcessInstanceResult = object; ``` ## Properties ### businessId ```ts businessId: BusinessId | null; ``` Business id as provided on creation. --- ### processDefinitionId ```ts processDefinitionId: ProcessDefinitionId; ``` The BPMN process id of the process definition which was used to create the process. instance --- ### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` The key of the process definition which was used to create the process instance. --- ### processDefinitionVersion ```ts processDefinitionVersion: number; ``` The version of the process definition which was used to create the process instance. --- ### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The unique identifier of the created process instance; to be used wherever a request needs a process instance key (e.g. CancelProcessInstanceRequest). --- ### tags ```ts tags: TagSet; ``` --- ### tenantId ```ts tenantId: TenantId; ``` The tenant id of the created process instance. --- ### variables ```ts variables: object; ``` All the variables visible in the root scope. #### Index Signature ```ts [key: string]: unknown ``` --- ## Type Alias: CreateRoleData ```ts type CreateRoleData = object; ``` ## Properties ### body? ```ts optional body?: RoleCreateRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/roles"; ``` --- ## Type Alias: CreateRoleError ```ts type CreateRoleError = CreateRoleErrors[keyof CreateRoleErrors]; ``` --- ## Type Alias: CreateRoleErrors ```ts type CreateRoleErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: CreateRoleResponse ```ts type CreateRoleResponse = CreateRoleResponses[keyof CreateRoleResponses]; ``` --- ## Type Alias: CreateRoleResponses ```ts type CreateRoleResponses = object; ``` ## Properties ### 201 ```ts 201: RoleCreateResult; ``` The role was created successfully. --- ## Type Alias: CreateTenantClusterVariableData ```ts type CreateTenantClusterVariableData = object; ``` ## Properties ### body ```ts body: CreateClusterVariableRequest; ``` --- ### path ```ts path: object; ``` #### tenantId ```ts tenantId: TenantId; ``` The tenant ID --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/cluster-variables/tenants/{tenantId}"; ``` --- ## Type Alias: CreateTenantClusterVariableError ```ts type CreateTenantClusterVariableError = CreateTenantClusterVariableErrors[keyof CreateTenantClusterVariableErrors]; ``` --- ## Type Alias: CreateTenantClusterVariableErrors ```ts type CreateTenantClusterVariableErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The tenant with the given ID was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: CreateTenantClusterVariableResponse ```ts type CreateTenantClusterVariableResponse = CreateTenantClusterVariableResponses[keyof CreateTenantClusterVariableResponses]; ``` --- ## Type Alias: CreateTenantClusterVariableResponses ```ts type CreateTenantClusterVariableResponses = object; ``` ## Properties ### 200 ```ts 200: ClusterVariableResult; ``` Cluster variable created --- ## Type Alias: CreateTenantData ```ts type CreateTenantData = object; ``` ## Properties ### body ```ts body: TenantCreateRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/tenants"; ``` --- ## Type Alias: CreateTenantError ```ts type CreateTenantError = CreateTenantErrors[keyof CreateTenantErrors]; ``` --- ## Type Alias: CreateTenantErrors ```ts type CreateTenantErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Not found. The resource was not found. --- ### 409 ```ts 409: unknown; ``` Tenant with this id already exists. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: CreateTenantResponse ```ts type CreateTenantResponse = CreateTenantResponses[keyof CreateTenantResponses]; ``` --- ## Type Alias: CreateTenantResponses ```ts type CreateTenantResponses = object; ``` ## Properties ### 201 ```ts 201: TenantCreateResult; ``` The tenant was created successfully. --- ## Type Alias: CreateUserData ```ts type CreateUserData = object; ``` ## Properties ### body ```ts body: UserRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/users"; ``` --- ## Type Alias: CreateUserError ```ts type CreateUserError = CreateUserErrors[keyof CreateUserErrors]; ``` --- ## Type Alias: CreateUserErrors ```ts type CreateUserErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 409 ```ts 409: ProblemDetail; ``` A user with this username already exists. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: CreateUserResponse ```ts type CreateUserResponse = CreateUserResponses[keyof CreateUserResponses]; ``` --- ## Type Alias: CreateUserResponses ```ts type CreateUserResponses = object; ``` ## Properties ### 201 ```ts 201: UserCreateResult; ``` The user was created successfully. --- ## Type Alias: CursorBackwardPagination ```ts type CursorBackwardPagination = object; ``` Cursor-based backward pagination ## Properties ### before ```ts before: StartCursor; ``` Use the `startCursor` value from the previous response to fetch the previous page of results. --- ### limit? ```ts optional limit?: number; ``` The maximum number of items to return in one request. --- ## Type Alias: CursorForwardPagination ```ts type CursorForwardPagination = object; ``` Cursor-based forward pagination ## Properties ### after ```ts after: EndCursor; ``` Use the `endCursor` value from the previous response to fetch the next page of results. --- ### limit? ```ts optional limit?: number; ``` The maximum number of items to return in one request. --- ## Type Alias: DateTimeFilterProperty ```ts type DateTimeFilterProperty = string | AdvancedDateTimeFilter; ``` Date-time property with full advanced search capabilities. --- ## Type Alias: DecisionDefinitionFilter ```ts type DecisionDefinitionFilter = object; ``` Decision definition search filter. ## Properties ### decisionDefinitionId? ```ts optional decisionDefinitionId?: DecisionDefinitionId; ``` The DMN ID of the decision definition. --- ### decisionDefinitionKey? ```ts optional decisionDefinitionKey?: DecisionDefinitionKey; ``` The assigned key, which acts as a unique identifier for this decision definition. --- ### decisionRequirementsId? ```ts optional decisionRequirementsId?: string; ``` the DMN ID of the decision requirements graph that the decision definition is part of. --- ### decisionRequirementsKey? ```ts optional decisionRequirementsKey?: DecisionRequirementsKey; ``` The assigned key of the decision requirements graph that the decision definition is part of. --- ### decisionRequirementsName? ```ts optional decisionRequirementsName?: string; ``` The DMN name of the decision requirements that the decision definition is part of. --- ### decisionRequirementsVersion? ```ts optional decisionRequirementsVersion?: number; ``` The assigned version of the decision requirements that the decision definition is part of. --- ### isLatestVersion? ```ts optional isLatestVersion?: boolean; ``` Whether to only return the latest version of each decision definition. When using this filter, pagination functionality is limited, you can only paginate forward using `after` and `limit`. The response contains no `startCursor` in the `page`, and requests ignore the `from` and `before` in the `page`. --- ### name? ```ts optional name?: string; ``` The DMN name of the decision definition. --- ### tenantId? ```ts optional tenantId?: TenantId; ``` The tenant ID of the decision definition. --- ### version? ```ts optional version?: number; ``` The assigned version of the decision definition. --- ## Type Alias: DecisionDefinitionId ```ts type DecisionDefinitionId = CamundaKey<"DecisionDefinitionId">; ``` Id of a decision definition, from the model. Only ids of decision definitions that are deployed are useful. --- ## Type Alias: DecisionDefinitionKey ```ts type DecisionDefinitionKey = CamundaKey<"DecisionDefinitionKey">; ``` System-generated key for a decision definition. --- ## Type Alias: DecisionDefinitionKeyExactMatch ```ts type DecisionDefinitionKeyExactMatch = DecisionDefinitionKey; ``` Exact match Matches the value exactly. --- ## Type Alias: DecisionDefinitionKeyFilterProperty ```ts type DecisionDefinitionKeyFilterProperty = | DecisionDefinitionKeyExactMatch | AdvancedDecisionDefinitionKeyFilter; ``` DecisionDefinitionKey property with full advanced search capabilities. --- ## Type Alias: DecisionDefinitionResult ```ts type DecisionDefinitionResult = object; ``` ## Properties ### decisionDefinitionId ```ts decisionDefinitionId: DecisionDefinitionId; ``` The DMN ID of the decision definition. --- ### decisionDefinitionKey ```ts decisionDefinitionKey: DecisionDefinitionKey; ``` The assigned key, which acts as a unique identifier for this decision definition. --- ### decisionRequirementsId ```ts decisionRequirementsId: string; ``` the DMN ID of the decision requirements graph that the decision definition is part of. --- ### decisionRequirementsKey ```ts decisionRequirementsKey: DecisionRequirementsKey; ``` The assigned key of the decision requirements graph that the decision definition is part of. --- ### decisionRequirementsName ```ts decisionRequirementsName: string; ``` The DMN name of the decision requirements that the decision definition is part of. --- ### decisionRequirementsVersion ```ts decisionRequirementsVersion: number; ``` The assigned version of the decision requirements that the decision definition is part of. --- ### name ```ts name: string; ``` The DMN name of the decision definition. --- ### tenantId ```ts tenantId: TenantId; ``` The tenant ID of the decision definition. --- ### version ```ts version: number; ``` The assigned version of the decision definition. --- ## Type Alias: DecisionDefinitionSearchQuery ```ts type DecisionDefinitionSearchQuery = SearchQueryRequest & object; ``` ## Type Declaration ### filter? ```ts optional filter?: DecisionDefinitionFilter; ``` The decision definition search filters. ### sort? ```ts optional sort?: DecisionDefinitionSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: DecisionDefinitionSearchQueryResult ```ts type DecisionDefinitionSearchQueryResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: DecisionDefinitionResult[]; ``` The matching decision definitions. --- ## Type Alias: DecisionDefinitionSearchQuerySortRequest ```ts type DecisionDefinitionSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: | "decisionDefinitionKey" | "decisionDefinitionId" | "name" | "version" | "decisionRequirementsId" | "decisionRequirementsKey" | "decisionRequirementsName" | "decisionRequirementsVersion" | "tenantId"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: DecisionDefinitionTypeEnum ```ts type DecisionDefinitionTypeEnum = (typeof DecisionDefinitionTypeEnum)[keyof typeof DecisionDefinitionTypeEnum]; ``` The type of the decision. UNSPECIFIED is deprecated and should not be used anymore, for removal in 8.10 --- ## Type Alias: DecisionEvaluationById ```ts type DecisionEvaluationById = object; ``` Decision evaluation by ID ## Properties ### decisionDefinitionId ```ts decisionDefinitionId: DecisionDefinitionId; ``` The ID of the decision to be evaluated. When using the decision ID, the latest deployed version of the decision is used. --- ### tenantId? ```ts optional tenantId?: TenantId; ``` The tenant ID of the decision. --- ### variables? ```ts optional variables?: object; ``` The decision evaluation variables as JSON document. #### Index Signature ```ts [key: string]: unknown ``` --- ## Type Alias: DecisionEvaluationByKey ```ts type DecisionEvaluationByKey = object; ``` Decision evaluation by key ## Properties ### decisionDefinitionKey ```ts decisionDefinitionKey: DecisionDefinitionKey; ``` --- ### tenantId? ```ts optional tenantId?: TenantId; ``` The tenant ID of the decision. --- ### variables? ```ts optional variables?: object; ``` The decision evaluation variables as JSON document. #### Index Signature ```ts [key: string]: unknown ``` --- ## Type Alias: DecisionEvaluationInstanceKey ```ts type DecisionEvaluationInstanceKey = CamundaKey<"DecisionEvaluationInstanceKey">; ``` System-generated identifier for a decision evaluation instance. It is composed of the parent decision evaluation key and the 1-based index of the evaluated decision within that evaluation, joined by a hyphen (format: `-`). --- ## Type Alias: DecisionEvaluationInstanceKeyExactMatch ```ts type DecisionEvaluationInstanceKeyExactMatch = DecisionEvaluationInstanceKey; ``` Exact match Matches the value exactly. --- ## Type Alias: DecisionEvaluationInstanceKeyFilterProperty ```ts type DecisionEvaluationInstanceKeyFilterProperty = | DecisionEvaluationInstanceKeyExactMatch | AdvancedDecisionEvaluationInstanceKeyFilter; ``` DecisionEvaluationInstanceKey property with full advanced search capabilities. --- ## Type Alias: DecisionEvaluationInstruction ```ts type DecisionEvaluationInstruction = | DecisionEvaluationById | DecisionEvaluationByKey; ``` --- ## Type Alias: DecisionEvaluationKey ```ts type DecisionEvaluationKey = CamundaKey<"DecisionEvaluationKey">; ``` System-generated key for a decision evaluation. --- ## Type Alias: DecisionEvaluationKeyExactMatch ```ts type DecisionEvaluationKeyExactMatch = DecisionEvaluationKey; ``` Exact match Matches the value exactly. --- ## Type Alias: DecisionEvaluationKeyFilterProperty ```ts type DecisionEvaluationKeyFilterProperty = | DecisionEvaluationKeyExactMatch | AdvancedDecisionEvaluationKeyFilter; ``` DecisionEvaluationKey property with full advanced search capabilities. --- ## Type Alias: DecisionInstanceDeletionBatchOperationRequest ```ts type DecisionInstanceDeletionBatchOperationRequest = object; ``` The decision instance filter that defines which decision instances should be deleted. ## Properties ### filter ```ts filter: DecisionInstanceFilter; ``` The decision instance filter. --- ### operationReference? ```ts optional operationReference?: OperationReference; ``` --- ## Type Alias: DecisionInstanceFilter ```ts type DecisionInstanceFilter = object; ``` Decision instance search filter. ## Properties ### decisionDefinitionId? ```ts optional decisionDefinitionId?: DecisionDefinitionId; ``` The ID of the DMN decision. --- ### decisionDefinitionKey? ```ts optional decisionDefinitionKey?: DecisionDefinitionKeyFilterProperty; ``` The key of the decision. --- ### decisionDefinitionName? ```ts optional decisionDefinitionName?: string; ``` The name of the DMN decision. --- ### decisionDefinitionType? ```ts optional decisionDefinitionType?: DecisionDefinitionTypeEnum; ``` --- ### decisionDefinitionVersion? ```ts optional decisionDefinitionVersion?: number; ``` The version of the decision. --- ### decisionEvaluationInstanceKey? ```ts optional decisionEvaluationInstanceKey?: DecisionEvaluationInstanceKeyFilterProperty; ``` The key of the decision evaluation instance. --- ### decisionEvaluationKey? ```ts optional decisionEvaluationKey?: DecisionEvaluationKey; ``` The key of the parent decision evaluation. Note that this is not the identifier of an individual decision instance; the `decisionEvaluationInstanceKey` is the identifier for a decision instance. --- ### decisionRequirementsKey? ```ts optional decisionRequirementsKey?: DecisionRequirementsKeyFilterProperty; ``` The key of the decision requirements definition. --- ### elementInstanceKey? ```ts optional elementInstanceKey?: ElementInstanceKeyFilterProperty; ``` The key of the element instance this decision instance is linked to. --- ### evaluationDate? ```ts optional evaluationDate?: DateTimeFilterProperty; ``` The evaluation date of the decision instance. --- ### evaluationFailure? ```ts optional evaluationFailure?: string; ``` The evaluation failure of the decision instance. --- ### processDefinitionKey? ```ts optional processDefinitionKey?: ProcessDefinitionKey; ``` The key of the process definition. --- ### processInstanceKey? ```ts optional processInstanceKey?: ProcessInstanceKey; ``` The key of the process instance. --- ### rootDecisionDefinitionKey? ```ts optional rootDecisionDefinitionKey?: DecisionDefinitionKeyFilterProperty; ``` The key of the root decision definition. --- ### state? ```ts optional state?: DecisionInstanceStateFilterProperty; ``` The state of the decision instance. --- ### tenantId? ```ts optional tenantId?: TenantId; ``` The tenant ID of the decision instance. --- ## Type Alias: DecisionInstanceGetQueryResult ```ts type DecisionInstanceGetQueryResult = DecisionInstanceResult & object; ``` ## Type Declaration ### evaluatedInputs ```ts evaluatedInputs: EvaluatedDecisionInputItem[]; ``` The evaluated inputs of the decision instance. ### matchedRules ```ts matchedRules: MatchedDecisionRuleItem[]; ``` The matched rules of the decision instance. --- ## Type Alias: DecisionInstanceKey ```ts type DecisionInstanceKey = CamundaKey<"DecisionInstanceKey">; ``` System-generated key for a deployed decision instance. --- ## Type Alias: DecisionInstanceResult ```ts type DecisionInstanceResult = object; ``` ## Properties ### decisionDefinitionId ```ts decisionDefinitionId: DecisionDefinitionId; ``` The ID of the DMN decision. --- ### decisionDefinitionKey ```ts decisionDefinitionKey: DecisionDefinitionKey; ``` The key of the decision. --- ### decisionDefinitionName ```ts decisionDefinitionName: string; ``` The name of the DMN decision. --- ### decisionDefinitionType ```ts decisionDefinitionType: DecisionDefinitionTypeEnum; ``` --- ### decisionDefinitionVersion ```ts decisionDefinitionVersion: number; ``` The version of the decision. --- ### decisionEvaluationInstanceKey ```ts decisionEvaluationInstanceKey: DecisionEvaluationInstanceKey; ``` --- ### decisionEvaluationKey ```ts decisionEvaluationKey: DecisionEvaluationKey; ``` The key of the decision evaluation where this instance was created. --- ### elementInstanceKey ```ts elementInstanceKey: ElementInstanceKey | null; ``` The key of the element instance this decision instance is linked to. --- ### evaluationDate ```ts evaluationDate: string; ``` The evaluation date of the decision instance. --- ### evaluationFailure ```ts evaluationFailure: string | null; ``` The evaluation failure of the decision instance. --- ### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey | null; ``` The key of the process definition. --- ### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey | null; ``` The key of the process instance. --- ### result ```ts result: string; ``` The result of the decision instance. --- ### rootDecisionDefinitionKey ```ts rootDecisionDefinitionKey: DecisionDefinitionKey; ``` The key of the root decision definition. --- ### rootProcessInstanceKey ```ts rootProcessInstanceKey: ProcessInstanceKey | null; ``` The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. --- ### state ```ts state: DecisionInstanceStateEnum; ``` --- ### tenantId ```ts tenantId: TenantId; ``` The tenant ID of the decision instance. --- ## Type Alias: DecisionInstanceSearchQuery ```ts type DecisionInstanceSearchQuery = SearchQueryRequest & object; ``` ## Type Declaration ### filter? ```ts optional filter?: DecisionInstanceFilter; ``` The decision instance search filters. ### sort? ```ts optional sort?: DecisionInstanceSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: DecisionInstanceSearchQueryResult ```ts type DecisionInstanceSearchQueryResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: DecisionInstanceResult[]; ``` The matching decision instances. --- ## Type Alias: DecisionInstanceSearchQuerySortRequest ```ts type DecisionInstanceSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: | "decisionDefinitionId" | "decisionDefinitionKey" | "decisionDefinitionName" | "decisionDefinitionType" | "decisionDefinitionVersion" | "decisionEvaluationInstanceKey" | "decisionEvaluationKey" | "elementInstanceKey" | "evaluationDate" | "evaluationFailure" | "processDefinitionKey" | "processInstanceKey" | "rootDecisionDefinitionKey" | "state" | "tenantId"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: DecisionInstanceStateEnum ```ts type DecisionInstanceStateEnum = (typeof DecisionInstanceStateEnum)[keyof typeof DecisionInstanceStateEnum]; ``` The state of the decision instance. UNSPECIFIED and UNKNOWN are deprecated and should not be used anymore, for removal in 8.10 --- ## Type Alias: DecisionInstanceStateExactMatch ```ts type DecisionInstanceStateExactMatch = DecisionInstanceStateEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: DecisionInstanceStateFilterProperty ```ts type DecisionInstanceStateFilterProperty = | DecisionInstanceStateExactMatch | AdvancedDecisionInstanceStateFilter; ``` DecisionInstanceStateEnum property with full advanced search capabilities. --- ## Type Alias: DecisionRequirementsFilter ```ts type DecisionRequirementsFilter = object; ``` Decision requirements search filter. ## Properties ### decisionRequirementsId? ```ts optional decisionRequirementsId?: string; ``` the DMN ID of the decision requirements. --- ### decisionRequirementsKey? ```ts optional decisionRequirementsKey?: DecisionRequirementsKey; ``` --- ### decisionRequirementsName? ```ts optional decisionRequirementsName?: string; ``` The DMN name of the decision requirements. --- ### resourceName? ```ts optional resourceName?: string; ``` The name of the resource from which the decision requirements were parsed --- ### tenantId? ```ts optional tenantId?: TenantId; ``` The tenant ID of the decision requirements. --- ### version? ```ts optional version?: number; ``` The assigned version of the decision requirements. --- ## Type Alias: DecisionRequirementsKey ```ts type DecisionRequirementsKey = CamundaKey<"DecisionRequirementsKey">; ``` System-generated key for a deployed decision requirements definition. --- ## Type Alias: DecisionRequirementsKeyExactMatch ```ts type DecisionRequirementsKeyExactMatch = DecisionRequirementsKey; ``` Exact match Matches the value exactly. --- ## Type Alias: DecisionRequirementsKeyFilterProperty ```ts type DecisionRequirementsKeyFilterProperty = | DecisionRequirementsKeyExactMatch | AdvancedDecisionRequirementsKeyFilter; ``` DecisionRequirementsKey property with full advanced search capabilities. --- ## Type Alias: DecisionRequirementsResult ```ts type DecisionRequirementsResult = object; ``` ## Properties ### decisionRequirementsId ```ts decisionRequirementsId: string; ``` The DMN ID of the decision requirements. --- ### decisionRequirementsKey ```ts decisionRequirementsKey: DecisionRequirementsKey; ``` The assigned key, which acts as a unique identifier for this decision requirements. --- ### decisionRequirementsName ```ts decisionRequirementsName: string; ``` The DMN name of the decision requirements. --- ### resourceName ```ts resourceName: string; ``` The name of the resource from which this decision requirements was parsed. --- ### tenantId ```ts tenantId: TenantId; ``` The tenant ID of the decision requirements. --- ### version ```ts version: number; ``` The assigned version of the decision requirements. --- ## Type Alias: DecisionRequirementsSearchQuery ```ts type DecisionRequirementsSearchQuery = SearchQueryRequest & object; ``` ## Type Declaration ### filter? ```ts optional filter?: DecisionRequirementsFilter; ``` The decision definition search filters. ### sort? ```ts optional sort?: DecisionRequirementsSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: DecisionRequirementsSearchQueryResult ```ts type DecisionRequirementsSearchQueryResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: DecisionRequirementsResult[]; ``` The matching decision requirements. --- ## Type Alias: DecisionRequirementsSearchQuerySortRequest ```ts type DecisionRequirementsSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: | "decisionRequirementsKey" | "decisionRequirementsName" | "version" | "decisionRequirementsId" | "tenantId"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: DeleteAuthorizationData ```ts type DeleteAuthorizationData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### authorizationKey ```ts authorizationKey: AuthorizationKey; ``` The key of the authorization to delete. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/authorizations/{authorizationKey}"; ``` --- ## Type Alias: DeleteAuthorizationError ```ts type DeleteAuthorizationError = DeleteAuthorizationErrors[keyof DeleteAuthorizationErrors]; ``` --- ## Type Alias: DeleteAuthorizationErrors ```ts type DeleteAuthorizationErrors = object; ``` ## Properties ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 404 ```ts 404: ProblemDetail; ``` The authorization with the authorizationKey was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: DeleteAuthorizationResponse ```ts type DeleteAuthorizationResponse = DeleteAuthorizationResponses[keyof DeleteAuthorizationResponses]; ``` --- ## Type Alias: DeleteAuthorizationResponses ```ts type DeleteAuthorizationResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The authorization was deleted successfully. --- ## Type Alias: DeleteDecisionInstanceData ```ts type DeleteDecisionInstanceData = object; ``` ## Properties ### body? ```ts optional body?: DeleteDecisionInstanceRequest; ``` --- ### path ```ts path: object; ``` #### decisionEvaluationKey ```ts decisionEvaluationKey: DecisionEvaluationKey; ``` The key of the decision evaluation to delete. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/decision-instances/{decisionEvaluationKey}/deletion"; ``` --- ## Type Alias: DeleteDecisionInstanceError ```ts type DeleteDecisionInstanceError = DeleteDecisionInstanceErrors[keyof DeleteDecisionInstanceErrors]; ``` --- ## Type Alias: DeleteDecisionInstanceErrors ```ts type DeleteDecisionInstanceErrors = object; ``` ## Properties ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The decision instance is not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: DeleteDecisionInstanceRequest ```ts type DeleteDecisionInstanceRequest = { operationReference?: OperationReference; } | null; ``` --- ## Type Alias: DeleteDecisionInstanceResponse ```ts type DeleteDecisionInstanceResponse = DeleteDecisionInstanceResponses[keyof DeleteDecisionInstanceResponses]; ``` --- ## Type Alias: DeleteDecisionInstanceResponses ```ts type DeleteDecisionInstanceResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The decision instance is marked for deletion. --- ## Type Alias: DeleteDecisionInstancesBatchOperationData ```ts type DeleteDecisionInstancesBatchOperationData = object; ``` ## Properties ### body ```ts body: DecisionInstanceDeletionBatchOperationRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/decision-instances/deletion"; ``` --- ## Type Alias: DeleteDecisionInstancesBatchOperationError ```ts type DeleteDecisionInstancesBatchOperationError = DeleteDecisionInstancesBatchOperationErrors[keyof DeleteDecisionInstancesBatchOperationErrors]; ``` --- ## Type Alias: DeleteDecisionInstancesBatchOperationErrors ```ts type DeleteDecisionInstancesBatchOperationErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The decision instance batch operation failed. More details are provided in the response body. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: DeleteDecisionInstancesBatchOperationResponse ```ts type DeleteDecisionInstancesBatchOperationResponse = DeleteDecisionInstancesBatchOperationResponses[keyof DeleteDecisionInstancesBatchOperationResponses]; ``` --- ## Type Alias: DeleteDecisionInstancesBatchOperationResponses ```ts type DeleteDecisionInstancesBatchOperationResponses = object; ``` ## Properties ### 200 ```ts 200: BatchOperationCreatedResult; ``` The batch operation request was created. --- ## Type Alias: DeleteDocumentData ```ts type DeleteDocumentData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### documentId ```ts documentId: DocumentId; ``` The ID of the document to delete. --- ### query? ```ts optional query?: object; ``` #### storeId? ```ts optional storeId?: string; ``` The ID of the document store to delete the document from. --- ### url ```ts url: "/documents/{documentId}"; ``` --- ## Type Alias: DeleteDocumentError ```ts type DeleteDocumentError = DeleteDocumentErrors[keyof DeleteDocumentErrors]; ``` --- ## Type Alias: DeleteDocumentErrors ```ts type DeleteDocumentErrors = object; ``` ## Properties ### 404 ```ts 404: ProblemDetail; ``` The document with the given ID was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: DeleteDocumentResponse ```ts type DeleteDocumentResponse = DeleteDocumentResponses[keyof DeleteDocumentResponses]; ``` --- ## Type Alias: DeleteDocumentResponses ```ts type DeleteDocumentResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The document was deleted successfully. --- ## Type Alias: DeleteGlobalClusterVariableData ```ts type DeleteGlobalClusterVariableData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### name ```ts name: ClusterVariableName; ``` The name of the cluster variable --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/cluster-variables/global/{name}"; ``` --- ## Type Alias: DeleteGlobalClusterVariableError ```ts type DeleteGlobalClusterVariableError = DeleteGlobalClusterVariableErrors[keyof DeleteGlobalClusterVariableErrors]; ``` --- ## Type Alias: DeleteGlobalClusterVariableErrors ```ts type DeleteGlobalClusterVariableErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Cluster variable not found --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: DeleteGlobalClusterVariableResponse ```ts type DeleteGlobalClusterVariableResponse = DeleteGlobalClusterVariableResponses[keyof DeleteGlobalClusterVariableResponses]; ``` --- ## Type Alias: DeleteGlobalClusterVariableResponses ```ts type DeleteGlobalClusterVariableResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` Cluster variable deleted successfully --- ## Type Alias: DeleteGlobalTaskListenerData ```ts type DeleteGlobalTaskListenerData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### id ```ts id: GlobalListenerId; ``` The id of the global user task listener to delete. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/global-task-listeners/{id}"; ``` --- ## Type Alias: DeleteGlobalTaskListenerError ```ts type DeleteGlobalTaskListenerError = DeleteGlobalTaskListenerErrors[keyof DeleteGlobalTaskListenerErrors]; ``` --- ## Type Alias: DeleteGlobalTaskListenerErrors ```ts type DeleteGlobalTaskListenerErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The global user task listener was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: DeleteGlobalTaskListenerResponse ```ts type DeleteGlobalTaskListenerResponse = DeleteGlobalTaskListenerResponses[keyof DeleteGlobalTaskListenerResponses]; ``` --- ## Type Alias: DeleteGlobalTaskListenerResponses ```ts type DeleteGlobalTaskListenerResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The global listener was deleted successfully. --- ## Type Alias: DeleteGroupData ```ts type DeleteGroupData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### groupId ```ts groupId: GroupId; ``` The group ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/groups/{groupId}"; ``` --- ## Type Alias: DeleteGroupError ```ts type DeleteGroupError = DeleteGroupErrors[keyof DeleteGroupErrors]; ``` --- ## Type Alias: DeleteGroupErrors ```ts type DeleteGroupErrors = object; ``` ## Properties ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 404 ```ts 404: ProblemDetail; ``` The group with the given ID was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: DeleteGroupResponse ```ts type DeleteGroupResponse = DeleteGroupResponses[keyof DeleteGroupResponses]; ``` --- ## Type Alias: DeleteGroupResponses ```ts type DeleteGroupResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The group was deleted successfully. --- ## Type Alias: DeleteMappingRuleData ```ts type DeleteMappingRuleData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### mappingRuleId ```ts mappingRuleId: MappingRuleId; ``` The ID of the mapping rule to delete. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/mapping-rules/{mappingRuleId}"; ``` --- ## Type Alias: DeleteMappingRuleError ```ts type DeleteMappingRuleError = DeleteMappingRuleErrors[keyof DeleteMappingRuleErrors]; ``` --- ## Type Alias: DeleteMappingRuleErrors ```ts type DeleteMappingRuleErrors = object; ``` ## Properties ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 404 ```ts 404: ProblemDetail; ``` The mapping rule with the mappingRuleId was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: DeleteMappingRuleResponse ```ts type DeleteMappingRuleResponse = DeleteMappingRuleResponses[keyof DeleteMappingRuleResponses]; ``` --- ## Type Alias: DeleteMappingRuleResponses ```ts type DeleteMappingRuleResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The mapping rule was deleted successfully. --- ## Type Alias: DeleteProcessInstanceData ```ts type DeleteProcessInstanceData = object; ``` ## Properties ### body? ```ts optional body?: DeleteProcessInstanceRequest; ``` --- ### path ```ts path: object; ``` #### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The key of the process instance to delete. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-instances/{processInstanceKey}/deletion"; ``` --- ## Type Alias: DeleteProcessInstanceError ```ts type DeleteProcessInstanceError = DeleteProcessInstanceErrors[keyof DeleteProcessInstanceErrors]; ``` --- ## Type Alias: DeleteProcessInstanceErrors ```ts type DeleteProcessInstanceErrors = object; ``` ## Properties ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The process instance is not found. --- ### 409 ```ts 409: ProblemDetail; ``` The process instance is not in a completed or terminated state and cannot be deleted. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: DeleteProcessInstanceRequest ```ts type DeleteProcessInstanceRequest = { operationReference?: OperationReference; } | null; ``` --- ## Type Alias: DeleteProcessInstanceResponse ```ts type DeleteProcessInstanceResponse = DeleteProcessInstanceResponses[keyof DeleteProcessInstanceResponses]; ``` --- ## Type Alias: DeleteProcessInstanceResponses ```ts type DeleteProcessInstanceResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The process instance is marked for deletion. --- ## Type Alias: DeleteProcessInstancesBatchOperationData ```ts type DeleteProcessInstancesBatchOperationData = object; ``` ## Properties ### body ```ts body: ProcessInstanceDeletionBatchOperationRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-instances/deletion"; ``` --- ## Type Alias: DeleteProcessInstancesBatchOperationError ```ts type DeleteProcessInstancesBatchOperationError = DeleteProcessInstancesBatchOperationErrors[keyof DeleteProcessInstancesBatchOperationErrors]; ``` --- ## Type Alias: DeleteProcessInstancesBatchOperationErrors ```ts type DeleteProcessInstancesBatchOperationErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The process instance batch operation failed. More details are provided in the response body. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: DeleteProcessInstancesBatchOperationResponse ```ts type DeleteProcessInstancesBatchOperationResponse = DeleteProcessInstancesBatchOperationResponses[keyof DeleteProcessInstancesBatchOperationResponses]; ``` --- ## Type Alias: DeleteProcessInstancesBatchOperationResponses ```ts type DeleteProcessInstancesBatchOperationResponses = object; ``` ## Properties ### 200 ```ts 200: BatchOperationCreatedResult; ``` The batch operation request was created. --- ## Type Alias: DeleteResourceData ```ts type DeleteResourceData = object; ``` ## Properties ### body? ```ts optional body?: DeleteResourceRequest; ``` --- ### path ```ts path: object; ``` #### resourceKey ```ts resourceKey: ResourceKey; ``` The key of the resource to delete. This can be the key of a process definition, the key of a decision requirements definition or the key of a form definition --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/resources/{resourceKey}/deletion"; ``` --- ## Type Alias: DeleteResourceError ```ts type DeleteResourceError = DeleteResourceErrors[keyof DeleteResourceErrors]; ``` --- ## Type Alias: DeleteResourceErrors ```ts type DeleteResourceErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 404 ```ts 404: ProblemDetail; ``` The resource is not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: DeleteResourceRequest ```ts type DeleteResourceRequest = { deleteHistory?: boolean; operationReference?: OperationReference; } | null; ``` ## Union Members ### Type Literal ```ts { deleteHistory?: boolean; operationReference?: OperationReference; } ``` #### deleteHistory? ```ts optional deleteHistory?: boolean; ``` Indicates if the historic data of a process resource should be deleted via a batch operation asynchronously. This flag is only effective for process resources. For other resource types (decisions, forms, generic resources), this flag is ignored and no history will be deleted. In those cases, the `batchOperation` field in the response will not be populated. #### operationReference? ```ts optional operationReference?: OperationReference; ``` --- `null` --- ## Type Alias: DeleteResourceResponse ```ts type DeleteResourceResponse = object; ``` ## Properties ### batchOperation ```ts batchOperation: BatchOperationCreatedResult | null; ``` The batch operation created for asynchronously deleting the historic data. This field is only populated when the request `deleteHistory` is set to `true` and the resource is a process definition. For other resource types (decisions, forms, generic resources), this field will be `null`. --- ### resourceKey ```ts resourceKey: ResourceKey; ``` The system-assigned key for this resource, requested to be deleted. --- ## Type Alias: DeleteResourceResponse2 ```ts type DeleteResourceResponse2 = DeleteResourceResponses[keyof DeleteResourceResponses]; ``` --- ## Type Alias: DeleteResourceResponses ```ts type DeleteResourceResponses = object; ``` ## Properties ### 200 ```ts 200: DeleteResourceResponse; ``` The resource is deleted. --- ## Type Alias: DeleteRoleData ```ts type DeleteRoleData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### roleId ```ts roleId: RoleId; ``` The role ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/roles/{roleId}"; ``` --- ## Type Alias: DeleteRoleError ```ts type DeleteRoleError = DeleteRoleErrors[keyof DeleteRoleErrors]; ``` --- ## Type Alias: DeleteRoleErrors ```ts type DeleteRoleErrors = object; ``` ## Properties ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 404 ```ts 404: ProblemDetail; ``` The role with the ID was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: DeleteRoleResponse ```ts type DeleteRoleResponse = DeleteRoleResponses[keyof DeleteRoleResponses]; ``` --- ## Type Alias: DeleteRoleResponses ```ts type DeleteRoleResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The role was deleted successfully. --- ## Type Alias: DeleteTenantClusterVariableData ```ts type DeleteTenantClusterVariableData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### name ```ts name: ClusterVariableName; ``` The name of the cluster variable #### tenantId ```ts tenantId: TenantId; ``` The tenant ID --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/cluster-variables/tenants/{tenantId}/{name}"; ``` --- ## Type Alias: DeleteTenantClusterVariableError ```ts type DeleteTenantClusterVariableError = DeleteTenantClusterVariableErrors[keyof DeleteTenantClusterVariableErrors]; ``` --- ## Type Alias: DeleteTenantClusterVariableErrors ```ts type DeleteTenantClusterVariableErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Cluster variable not found --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: DeleteTenantClusterVariableResponse ```ts type DeleteTenantClusterVariableResponse = DeleteTenantClusterVariableResponses[keyof DeleteTenantClusterVariableResponses]; ``` --- ## Type Alias: DeleteTenantClusterVariableResponses ```ts type DeleteTenantClusterVariableResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` Cluster variable deleted successfully --- ## Type Alias: DeleteTenantData ```ts type DeleteTenantData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### tenantId ```ts tenantId: TenantId; ``` The unique identifier of the tenant. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/tenants/{tenantId}"; ``` --- ## Type Alias: DeleteTenantError ```ts type DeleteTenantError = DeleteTenantErrors[keyof DeleteTenantErrors]; ``` --- ## Type Alias: DeleteTenantErrors ```ts type DeleteTenantErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Not found. The tenant was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: DeleteTenantResponse ```ts type DeleteTenantResponse = DeleteTenantResponses[keyof DeleteTenantResponses]; ``` --- ## Type Alias: DeleteTenantResponses ```ts type DeleteTenantResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The tenant was deleted successfully. --- ## Type Alias: DeleteUserData ```ts type DeleteUserData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### username ```ts username: Username; ``` The username of the user to delete. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/users/{username}"; ``` --- ## Type Alias: DeleteUserError ```ts type DeleteUserError = DeleteUserErrors[keyof DeleteUserErrors]; ``` --- ## Type Alias: DeleteUserErrors ```ts type DeleteUserErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 404 ```ts 404: ProblemDetail; ``` The user is not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: DeleteUserResponse ```ts type DeleteUserResponse = DeleteUserResponses[keyof DeleteUserResponses]; ``` --- ## Type Alias: DeleteUserResponses ```ts type DeleteUserResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The user was deleted successfully. --- ## Type Alias: DeploymentConfigurationResponse ```ts type DeploymentConfigurationResponse = object; ``` Configuration for deployment characteristics. ## Properties ### contextPath ```ts contextPath: string; ``` The servlet context path for the deployment. --- ### isEnterprise ```ts isEnterprise: boolean; ``` Whether this is an enterprise deployment. --- ### isMultiTenancyEnabled ```ts isMultiTenancyEnabled: boolean; ``` Whether multi-tenancy is enabled. --- ### maxRequestSize ```ts maxRequestSize: number; ``` The maximum HTTP request size in bytes. --- ## Type Alias: DeploymentDecisionRequirementsResult ```ts type DeploymentDecisionRequirementsResult = object; ``` Deployed decision requirements. ## Properties ### decisionRequirementsId ```ts decisionRequirementsId: string; ``` The id of the deployed decision requirements. --- ### decisionRequirementsKey ```ts decisionRequirementsKey: DecisionRequirementsKey; ``` The assigned decision requirements key, which acts as a unique identifier for this decision requirements. --- ### decisionRequirementsName ```ts decisionRequirementsName: string; ``` The name of the deployed decision requirements. --- ### resourceName ```ts resourceName: string; ``` The name of the resource. --- ### tenantId ```ts tenantId: TenantId; ``` The tenant ID of the deployed decision requirements. --- ### version ```ts version: number; ``` The version of the deployed decision requirements. --- ## Type Alias: DeploymentDecisionResult ```ts type DeploymentDecisionResult = object; ``` A deployed decision. ## Properties ### decisionDefinitionId ```ts decisionDefinitionId: DecisionDefinitionId; ``` The dmn decision ID, as parsed during deployment, together with the version forms a unique identifier for a specific decision. --- ### decisionDefinitionKey ```ts decisionDefinitionKey: DecisionDefinitionKey; ``` The assigned decision key, which acts as a unique identifier for this decision. --- ### decisionRequirementsId ```ts decisionRequirementsId: string; ``` The dmn ID of the decision requirements graph that this decision is part of, as parsed during deployment. --- ### decisionRequirementsKey ```ts decisionRequirementsKey: DecisionRequirementsKey; ``` The assigned key of the decision requirements graph that this decision is part of. --- ### name ```ts name: string; ``` The DMN name of the decision, as parsed during deployment. --- ### tenantId ```ts tenantId: TenantId; ``` The tenant ID of the deployed decision. --- ### version ```ts version: number; ``` The assigned decision version. --- ## Type Alias: DeploymentFormResult ```ts type DeploymentFormResult = object; ``` A deployed form. ## Properties ### formId ```ts formId: FormId; ``` The form ID, as parsed during deployment, together with the version forms a unique identifier for a specific form. --- ### formKey ```ts formKey: FormKey; ``` The assigned key, which acts as a unique identifier for this form. --- ### resourceName ```ts resourceName: string; ``` The name of the resource. --- ### tenantId ```ts tenantId: TenantId; ``` --- ### version ```ts version: number; ``` The version of the deployed form. --- ## Type Alias: DeploymentKey ```ts type DeploymentKey = CamundaKey<"DeploymentKey">; ``` Key for a deployment. --- ## Type Alias: DeploymentKeyExactMatch ```ts type DeploymentKeyExactMatch = DeploymentKey; ``` Exact match Matches the value exactly. --- ## Type Alias: DeploymentKeyFilterProperty ```ts type DeploymentKeyFilterProperty = | DeploymentKeyExactMatch | AdvancedDeploymentKeyFilter; ``` DeploymentKey property with full advanced search capabilities. --- ## Type Alias: DeploymentMetadataResult ```ts type DeploymentMetadataResult = object; ``` ## Properties ### decisionDefinition ```ts decisionDefinition: DeploymentDecisionResult | null; ``` Deployed decision. --- ### decisionRequirements ```ts decisionRequirements: | DeploymentDecisionRequirementsResult | null; ``` Deployed decision requirement definition. --- ### form ```ts form: DeploymentFormResult | null; ``` Deployed form. --- ### processDefinition ```ts processDefinition: DeploymentProcessResult | null; ``` Deployed process. --- ### resource ```ts resource: DeploymentResourceResult | null; ``` Deployed resource. --- ## Type Alias: DeploymentProcessResult ```ts type DeploymentProcessResult = object; ``` A deployed process. ## Properties ### processDefinitionId ```ts processDefinitionId: ProcessDefinitionId; ``` The bpmn process ID, as parsed during deployment, together with the version forms a unique identifier for a specific process definition. --- ### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` The assigned key, which acts as a unique identifier for this process. --- ### processDefinitionVersion ```ts processDefinitionVersion: number; ``` The assigned process version. --- ### resourceName ```ts resourceName: string; ``` The resource name from which this process was parsed. --- ### tenantId ```ts tenantId: TenantId; ``` The tenant ID of the deployed process. --- ## Type Alias: DeploymentResourceResult ```ts type DeploymentResourceResult = object; ``` A deployed Resource. ## Properties ### resourceId ```ts resourceId: string; ``` The resource id of the deployed resource. --- ### resourceKey ```ts resourceKey: ResourceKey; ``` The assigned key, which acts as a unique identifier for this Resource. --- ### resourceName ```ts resourceName: string; ``` The name of the deployed resource. --- ### tenantId ```ts tenantId: TenantId; ``` --- ### version ```ts version: number; ``` The description of the deployed resource. --- ## Type Alias: DeploymentResult ```ts type DeploymentResult = object; ``` ## Properties ### deploymentKey ```ts deploymentKey: DeploymentKey; ``` The unique key identifying the deployment. --- ### deployments ```ts deployments: DeploymentMetadataResult[]; ``` Items deployed by the request. --- ### tenantId ```ts tenantId: TenantId; ``` The tenant ID associated with the deployment. --- ## Type Alias: DirectAncestorKeyInstruction ```ts type DirectAncestorKeyInstruction = object; ``` Provides a concrete key to use as ancestor scope for the created element instance. ## Properties ### ancestorElementInstanceKey ```ts ancestorElementInstanceKey: ElementInstanceKey; ``` The key of the ancestor scope the element instance should be created in. Set to -1 to create the new element instance within an existing element instance of the flow scope. If multiple instances of the target element's flow scope exist, choose one specifically with this property by providing its key. --- ### ancestorScopeType ```ts ancestorScopeType: string; ``` The type of ancestor scope instruction. --- ## Type Alias: DocumentCreationBatchResponse ```ts type DocumentCreationBatchResponse = object; ``` ## Properties ### createdDocuments ```ts createdDocuments: DocumentReference[]; ``` Documents that failed creation. --- ### failedDocuments ```ts failedDocuments: DocumentCreationFailureDetail[]; ``` Documents that were successfully created. --- ## Type Alias: DocumentCreationFailureDetail ```ts type DocumentCreationFailureDetail = object; ``` ## Properties ### detail ```ts detail: string; ``` A human-readable explanation specific to this occurrence of the problem. --- ### fileName ```ts fileName: string; ``` The name of the file that failed to upload. --- ### status ```ts status: number; ``` The HTTP status code of the failure. --- ### title ```ts title: string; ``` A short, human-readable summary of the problem type. --- ## Type Alias: DocumentId ```ts type DocumentId = CamundaKey<"DocumentId">; ``` Document Id that uniquely identifies a document. --- ## Type Alias: DocumentLink ```ts type DocumentLink = object; ``` ## Properties ### expiresAt ```ts expiresAt: string; ``` The date and time when the link expires. --- ### url ```ts url: string; ``` The link to the document. --- ## Type Alias: DocumentLinkRequest ```ts type DocumentLinkRequest = object; ``` ## Properties ### timeToLive? ```ts optional timeToLive?: number; ``` The time-to-live of the document link in ms. --- ## Type Alias: DocumentMetadata ```ts type DocumentMetadata = object; ``` Information about the document. ## Properties ### contentType? ```ts optional contentType?: string; ``` The content type of the document. --- ### customProperties? ```ts optional customProperties?: object; ``` Custom properties of the document. #### Index Signature ```ts [key: string]: unknown ``` --- ### expiresAt? ```ts optional expiresAt?: string; ``` The date and time when the document expires. --- ### fileName? ```ts optional fileName?: string; ``` The name of the file. --- ### processDefinitionId? ```ts optional processDefinitionId?: ProcessDefinitionId; ``` The ID of the process definition that created the document. --- ### processInstanceKey? ```ts optional processInstanceKey?: ProcessInstanceKey; ``` The key of the process instance that created the document. --- ### size? ```ts optional size?: number; ``` The size of the document in bytes. --- ## Type Alias: DocumentMetadataResponse ```ts type DocumentMetadataResponse = object; ``` Information about the document that is returned in responses. ## Properties ### contentType ```ts contentType: string; ``` The content type of the document. --- ### customProperties ```ts customProperties: object; ``` Custom properties of the document. #### Index Signature ```ts [key: string]: unknown ``` --- ### expiresAt ```ts expiresAt: string | null; ``` The date and time when the document expires. --- ### fileName ```ts fileName: string; ``` The name of the file. --- ### processDefinitionId ```ts processDefinitionId: ProcessDefinitionId | null; ``` The ID of the process definition that created the document. --- ### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey | null; ``` The key of the process instance that created the document. --- ### size ```ts size: number; ``` The size of the document in bytes. --- ## Type Alias: DocumentReference ```ts type DocumentReference = object; ``` ## Properties ### camunda.document.type ```ts camunda.document.type: "camunda"; ``` Document discriminator. Always set to "camunda". --- ### contentHash ```ts contentHash: string | null; ``` The hash of the document. --- ### documentId ```ts documentId: DocumentId; ``` The ID of the document. --- ### metadata ```ts metadata: DocumentMetadataResponse; ``` --- ### storeId ```ts storeId: string; ``` The ID of the document store. --- ## Type Alias: Either # Type Alias: Either\ ```ts type Either = Left | Right; ``` ## Type Parameters ### E `E` ### A `A` --- ## Type Alias: ElementId ```ts type ElementId = CamundaKey<"ElementId">; ``` The model-defined id of an element. --- ## Type Alias: ElementIdExactMatch ```ts type ElementIdExactMatch = ElementId; ``` Exact match Matches the value exactly. --- ## Type Alias: ElementIdFilterProperty ```ts type ElementIdFilterProperty = ElementIdExactMatch | AdvancedElementIdFilter; ``` ElementId property with full advanced search capabilities. --- ## Type Alias: ElementInstanceFilter ```ts type ElementInstanceFilter = object; ``` Element instance filter. ## Properties ### elementId? ```ts optional elementId?: ElementIdFilterProperty; ``` The element ID for this element instance. --- ### elementInstanceKey? ```ts optional elementInstanceKey?: ElementInstanceKey; ``` The assigned key, which acts as a unique identifier for this element instance. --- ### elementInstanceScopeKey? ```ts optional elementInstanceScopeKey?: | ElementInstanceKey | ProcessInstanceKey; ``` The scope key of this element instance. If provided with a process instance key it will return element instances that are immediate children of the process instance. If provided with an element instance key it will return element instances that are immediate children of the element instance. --- ### elementName? ```ts optional elementName?: StringFilterProperty; ``` The element name. This only works for data created with 8.8 and onwards. Instances from prior versions don't contain this data and cannot be found. --- ### endDate? ```ts optional endDate?: DateTimeFilterProperty; ``` The end date of this element instance. --- ### hasIncident? ```ts optional hasIncident?: boolean; ``` Shows whether this element instance has an incident related to. --- ### incidentKey? ```ts optional incidentKey?: IncidentKey; ``` The key of incident if field incident is true. --- ### processDefinitionId? ```ts optional processDefinitionId?: ProcessDefinitionId; ``` The process definition ID associated to this element instance. --- ### processDefinitionKey? ```ts optional processDefinitionKey?: ProcessDefinitionKey; ``` The process definition key associated to this element instance. --- ### processInstanceKey? ```ts optional processInstanceKey?: ProcessInstanceKey; ``` The process instance key associated to this element instance. --- ### startDate? ```ts optional startDate?: DateTimeFilterProperty; ``` The start date of this element instance. --- ### state? ```ts optional state?: ElementInstanceStateFilterProperty; ``` State of element instance as defined set of values. --- ### tenantId? ```ts optional tenantId?: TenantId; ``` --- ### type? ```ts optional type?: | "UNSPECIFIED" | "PROCESS" | "SUB_PROCESS" | "EVENT_SUB_PROCESS" | "AD_HOC_SUB_PROCESS" | "AD_HOC_SUB_PROCESS_INNER_INSTANCE" | "START_EVENT" | "INTERMEDIATE_CATCH_EVENT" | "INTERMEDIATE_THROW_EVENT" | "BOUNDARY_EVENT" | "END_EVENT" | "SERVICE_TASK" | "RECEIVE_TASK" | "USER_TASK" | "MANUAL_TASK" | "TASK" | "EXCLUSIVE_GATEWAY" | "INCLUSIVE_GATEWAY" | "PARALLEL_GATEWAY" | "EVENT_BASED_GATEWAY" | "SEQUENCE_FLOW" | "MULTI_INSTANCE_BODY" | "CALL_ACTIVITY" | "BUSINESS_RULE_TASK" | "SCRIPT_TASK" | "SEND_TASK" | "UNKNOWN"; ``` Type of element as defined set of values. --- ## Type Alias: ElementInstanceKey ```ts type ElementInstanceKey = CamundaKey<"ElementInstanceKey">; ``` System-generated key for a element instance. --- ## Type Alias: ElementInstanceKeyExactMatch ```ts type ElementInstanceKeyExactMatch = ElementInstanceKey; ``` Exact match Matches the value exactly. --- ## Type Alias: ElementInstanceKeyFilterProperty ```ts type ElementInstanceKeyFilterProperty = | ElementInstanceKeyExactMatch | AdvancedElementInstanceKeyFilter; ``` ElementInstanceKey property with full advanced search capabilities. --- ## Type Alias: ElementInstanceResult ```ts type ElementInstanceResult = object; ``` ## Properties ### elementId ```ts elementId: ElementId; ``` The element ID for this element instance. --- ### elementInstanceKey ```ts elementInstanceKey: ElementInstanceKey; ``` The assigned key, which acts as a unique identifier for this element instance. --- ### elementName ```ts elementName: string; ``` The element name for this element instance. --- ### endDate ```ts endDate: string | null; ``` Date when element instance finished. --- ### hasIncident ```ts hasIncident: boolean; ``` Shows whether this element instance has an incident. If true also an incidentKey is provided. --- ### incidentKey ```ts incidentKey: IncidentKey | null; ``` Incident key associated with this element instance. --- ### processDefinitionId ```ts processDefinitionId: ProcessDefinitionId; ``` The process definition ID associated to this element instance. --- ### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` The process definition key associated to this element instance. --- ### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The process instance key associated to this element instance. --- ### rootProcessInstanceKey ```ts rootProcessInstanceKey: ProcessInstanceKey | null; ``` The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. --- ### startDate ```ts startDate: string; ``` Date when element instance started. --- ### state ```ts state: ElementInstanceStateEnum; ``` State of element instance as defined set of values. --- ### tenantId ```ts tenantId: TenantId; ``` The tenant ID of the incident. --- ### type ```ts type: | "UNSPECIFIED" | "PROCESS" | "SUB_PROCESS" | "EVENT_SUB_PROCESS" | "AD_HOC_SUB_PROCESS" | "AD_HOC_SUB_PROCESS_INNER_INSTANCE" | "START_EVENT" | "INTERMEDIATE_CATCH_EVENT" | "INTERMEDIATE_THROW_EVENT" | "BOUNDARY_EVENT" | "END_EVENT" | "SERVICE_TASK" | "RECEIVE_TASK" | "USER_TASK" | "MANUAL_TASK" | "TASK" | "EXCLUSIVE_GATEWAY" | "INCLUSIVE_GATEWAY" | "PARALLEL_GATEWAY" | "EVENT_BASED_GATEWAY" | "SEQUENCE_FLOW" | "MULTI_INSTANCE_BODY" | "CALL_ACTIVITY" | "BUSINESS_RULE_TASK" | "SCRIPT_TASK" | "SEND_TASK" | "UNKNOWN"; ``` Type of element as defined set of values. --- ## Type Alias: ElementInstanceSearchQuery ```ts type ElementInstanceSearchQuery = SearchQueryRequest & object; ``` Element instance search request. ## Type Declaration ### filter? ```ts optional filter?: ElementInstanceFilter; ``` The element instance search filters. ### sort? ```ts optional sort?: ElementInstanceSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: ElementInstanceSearchQueryResult ```ts type ElementInstanceSearchQueryResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: ElementInstanceResult[]; ``` The matching element instances. --- ## Type Alias: ElementInstanceSearchQuerySortRequest ```ts type ElementInstanceSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: | "elementInstanceKey" | "processInstanceKey" | "processDefinitionKey" | "processDefinitionId" | "startDate" | "endDate" | "elementId" | "elementName" | "type" | "state" | "incidentKey" | "tenantId"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: ElementInstanceStateEnum ```ts type ElementInstanceStateEnum = (typeof ElementInstanceStateEnum)[keyof typeof ElementInstanceStateEnum]; ``` Element states --- ## Type Alias: ElementInstanceStateExactMatch ```ts type ElementInstanceStateExactMatch = ElementInstanceStateEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: ElementInstanceStateFilterProperty ```ts type ElementInstanceStateFilterProperty = | ElementInstanceStateExactMatch | AdvancedElementInstanceStateFilter; ``` ElementInstanceStateEnum property with full advanced search capabilities. --- ## Type Alias: EndCursor ```ts type EndCursor = CamundaKey<"EndCursor">; ``` The end cursor in a search query result set. --- ## Type Alias: EntityTypeExactMatch ```ts type EntityTypeExactMatch = AuditLogEntityTypeEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: EntityTypeFilterProperty ```ts type EntityTypeFilterProperty = EntityTypeExactMatch | AdvancedEntityTypeFilter; ``` AuditLogEntityTypeEnum property with full advanced search capabilities. --- ## Type Alias: EvaluateConditionalResult ```ts type EvaluateConditionalResult = object; ``` ## Properties ### conditionalEvaluationKey ```ts conditionalEvaluationKey: ConditionalEvaluationKey; ``` The unique key of the conditional evaluation operation. --- ### processInstances ```ts processInstances: ProcessInstanceReference[]; ``` List of process instances created. If no root-level conditional start events evaluated to true, the list will be empty. --- ### tenantId ```ts tenantId: TenantId; ``` The tenant ID of the conditional evaluation operation. --- ## Type Alias: EvaluateConditionalsData ```ts type EvaluateConditionalsData = object; ``` ## Properties ### body ```ts body: ConditionalEvaluationInstruction; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/conditionals/evaluation"; ``` --- ## Type Alias: EvaluateConditionalsError ```ts type EvaluateConditionalsError = EvaluateConditionalsErrors[keyof EvaluateConditionalsErrors]; ``` --- ## Type Alias: EvaluateConditionalsErrors ```ts type EvaluateConditionalsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` The client is not authorized to start process instances for the specified process definition. If a processDefinitionKey is not provided, this indicates that the client is not authorized to start process instances for at least one of the matched process definitions. --- ### 404 ```ts 404: ProblemDetail; ``` The process definition was not found for the given processDefinitionKey. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: EvaluateConditionalsResponse ```ts type EvaluateConditionalsResponse = EvaluateConditionalsResponses[keyof EvaluateConditionalsResponses]; ``` --- ## Type Alias: EvaluateConditionalsResponses ```ts type EvaluateConditionalsResponses = object; ``` ## Properties ### 200 ```ts 200: EvaluateConditionalResult; ``` Successfully evaluated root-level conditional start events. --- ## Type Alias: EvaluateDecisionData ```ts type EvaluateDecisionData = object; ``` ## Properties ### body ```ts body: DecisionEvaluationInstruction; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/decision-definitions/evaluation"; ``` --- ## Type Alias: EvaluateDecisionError ```ts type EvaluateDecisionError = EvaluateDecisionErrors[keyof EvaluateDecisionErrors]; ``` --- ## Type Alias: EvaluateDecisionErrors ```ts type EvaluateDecisionErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 404 ```ts 404: ProblemDetail; ``` The decision is not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: EvaluateDecisionResponse ```ts type EvaluateDecisionResponse = EvaluateDecisionResponses[keyof EvaluateDecisionResponses]; ``` --- ## Type Alias: EvaluateDecisionResponses ```ts type EvaluateDecisionResponses = object; ``` ## Properties ### 200 ```ts 200: EvaluateDecisionResult; ``` The decision was evaluated. --- ## Type Alias: EvaluateDecisionResult ```ts type EvaluateDecisionResult = object; ``` ## Properties ### decisionDefinitionId ```ts decisionDefinitionId: DecisionDefinitionId; ``` The ID of the decision which was evaluated. --- ### decisionDefinitionKey ```ts decisionDefinitionKey: DecisionDefinitionKey; ``` The unique key identifying the decision which was evaluated. --- ### decisionDefinitionName ```ts decisionDefinitionName: string; ``` The name of the decision which was evaluated. --- ### decisionDefinitionVersion ```ts decisionDefinitionVersion: number; ``` The version of the decision which was evaluated. --- ### decisionEvaluationKey ```ts decisionEvaluationKey: DecisionEvaluationKey; ``` The unique key identifying this decision evaluation. --- ### ~~decisionInstanceKey~~ ```ts decisionInstanceKey: DecisionInstanceKey; ``` Deprecated, please refer to `decisionEvaluationKey`. #### Deprecated --- ### decisionRequirementsId ```ts decisionRequirementsId: string; ``` The ID of the decision requirements graph that the decision which was evaluated is part of. --- ### decisionRequirementsKey ```ts decisionRequirementsKey: DecisionRequirementsKey; ``` The unique key identifying the decision requirements graph that the decision which was evaluated is part of. --- ### evaluatedDecisions ```ts evaluatedDecisions: EvaluatedDecisionResult[]; ``` Decisions that were evaluated within the requested decision evaluation. --- ### failedDecisionDefinitionId ```ts failedDecisionDefinitionId: DecisionDefinitionId | null; ``` The ID of the decision which failed during evaluation. --- ### failureMessage ```ts failureMessage: string | null; ``` Message describing why the decision which was evaluated failed. --- ### output ```ts output: string; ``` JSON document that will instantiate the result of the decision which was evaluated. --- ### tenantId ```ts tenantId: TenantId; ``` The tenant ID of the evaluated decision. --- ## Type Alias: EvaluateExpressionData ```ts type EvaluateExpressionData = object; ``` ## Properties ### body ```ts body: ExpressionEvaluationRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/expression/evaluation"; ``` --- ## Type Alias: EvaluateExpressionError ```ts type EvaluateExpressionError = EvaluateExpressionErrors[keyof EvaluateExpressionErrors]; ``` --- ## Type Alias: EvaluateExpressionErrors ```ts type EvaluateExpressionErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: EvaluateExpressionResponse ```ts type EvaluateExpressionResponse = EvaluateExpressionResponses[keyof EvaluateExpressionResponses]; ``` --- ## Type Alias: EvaluateExpressionResponses ```ts type EvaluateExpressionResponses = object; ``` ## Properties ### 200 ```ts 200: ExpressionEvaluationResult; ``` Expression evaluated successfully --- ## Type Alias: EvaluatedDecisionInputItem ```ts type EvaluatedDecisionInputItem = object; ``` A decision input that was evaluated within this decision evaluation. ## Properties ### inputId ```ts inputId: string; ``` The identifier of the decision input. --- ### inputName ```ts inputName: string; ``` The name of the decision input. --- ### inputValue ```ts inputValue: string; ``` The value of the decision input. --- ## Type Alias: EvaluatedDecisionOutputItem ```ts type EvaluatedDecisionOutputItem = object; ``` The evaluated decision outputs. ## Properties ### outputId ```ts outputId: string; ``` The ID of the evaluated decison output item. --- ### outputName ```ts outputName: string; ``` The name of the of the evaluated decison output item. --- ### outputValue ```ts outputValue: string; ``` The value of the evaluated decison output item. --- ### ruleId ```ts ruleId: string | null; ``` The ID of the matched rule. --- ### ruleIndex ```ts ruleIndex: number | null; ``` The index of the matched rule. --- ## Type Alias: EvaluatedDecisionResult ```ts type EvaluatedDecisionResult = object; ``` A decision that was evaluated. ## Properties ### decisionDefinitionId ```ts decisionDefinitionId: DecisionDefinitionId; ``` The ID of the decision which was evaluated. --- ### decisionDefinitionKey ```ts decisionDefinitionKey: DecisionDefinitionKey; ``` The unique key identifying the decision which was evaluate. --- ### decisionDefinitionName ```ts decisionDefinitionName: string; ``` The name of the decision which was evaluated. --- ### decisionDefinitionType ```ts decisionDefinitionType: string; ``` The type of the decision which was evaluated. --- ### decisionDefinitionVersion ```ts decisionDefinitionVersion: number; ``` The version of the decision which was evaluated. --- ### decisionEvaluationInstanceKey ```ts decisionEvaluationInstanceKey: DecisionEvaluationInstanceKey; ``` The unique key identifying this decision evaluation instance. --- ### evaluatedInputs ```ts evaluatedInputs: EvaluatedDecisionInputItem[]; ``` The decision inputs that were evaluated within this decision evaluation. --- ### matchedRules ```ts matchedRules: MatchedDecisionRuleItem[]; ``` The decision rules that matched within this decision evaluation. --- ### output ```ts output: string; ``` JSON document that will instantiate the result of the decision which was evaluated. --- ### tenantId ```ts tenantId: TenantId; ``` The tenant ID of the evaluated decision. --- ## Type Alias: ExpressionEvaluationRequest ```ts type ExpressionEvaluationRequest = object; ``` ## Properties ### expression ```ts expression: string; ``` The expression to evaluate (e.g., "=x + y") --- ### tenantId? ```ts optional tenantId?: string; ``` Required when the expression references tenant-scoped cluster variables --- ### variables? ```ts optional variables?: | { [key: string]: unknown; } | null; ``` Optional variables for expression evaluation. These variables are only used for the current evaluation and do not persist beyond it. --- ## Type Alias: ExpressionEvaluationResult ```ts type ExpressionEvaluationResult = object; ``` ## Properties ### expression ```ts expression: string; ``` The evaluated expression --- ### result ```ts result: unknown; ``` The result value. Its type can vary. --- ### warnings ```ts warnings: ExpressionEvaluationWarningItem[]; ``` List of warnings generated during expression evaluation --- ## Type Alias: ExpressionEvaluationWarningItem ```ts type ExpressionEvaluationWarningItem = object; ``` ## Properties ### message ```ts message: string; ``` The warning message --- ## Type Alias: FailJobData ```ts type FailJobData = object; ``` ## Properties ### body? ```ts optional body?: JobFailRequest; ``` --- ### path ```ts path: object; ``` #### jobKey ```ts jobKey: JobKey; ``` The key of the job to fail. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/jobs/{jobKey}/failure"; ``` --- ## Type Alias: FailJobError ```ts type FailJobError = FailJobErrors[keyof FailJobErrors]; ``` --- ## Type Alias: FailJobErrors ```ts type FailJobErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 404 ```ts 404: ProblemDetail; ``` The job with the given jobKey is not found. It was completed by another worker, or the process instance itself was canceled. --- ### 409 ```ts 409: ProblemDetail; ``` The job with the given key is in the wrong state (i.e: not ACTIVATED or ACTIVATABLE). The job was failed by another worker with retries = 0, and the process is now in an incident state. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: FailJobResponse ```ts type FailJobResponse = FailJobResponses[keyof FailJobResponses]; ``` --- ## Type Alias: FailJobResponses ```ts type FailJobResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The job is failed. --- ## Type Alias: FormId ```ts type FormId = CamundaKey<"FormId">; ``` The user-defined id for the form --- ## Type Alias: FormKey ```ts type FormKey = CamundaKey<"FormKey">; ``` System-generated key for a deployed form. --- ## Type Alias: FormKeyExactMatch ```ts type FormKeyExactMatch = FormKey; ``` Exact match Matches the value exactly. --- ## Type Alias: FormKeyFilterProperty ```ts type FormKeyFilterProperty = FormKeyExactMatch | AdvancedFormKeyFilter; ``` FormKey property with full advanced search capabilities. --- ## Type Alias: FormResult ```ts type FormResult = object; ``` ## Properties ### formId ```ts formId: FormId; ``` The user-provided identifier of the form. --- ### formKey ```ts formKey: FormKey; ``` The assigned key, which acts as a unique identifier for this form. --- ### schema ```ts schema: string; ``` The form schema as a JSON document serialized as a string. --- ### tenantId ```ts tenantId: TenantId; ``` The tenant ID of the form. --- ### version ```ts version: number; ``` The version of the the deployed form. --- ## Type Alias: GetAgentInstanceData ```ts type GetAgentInstanceData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### agentInstanceKey ```ts agentInstanceKey: AgentInstanceKey; ``` The key of the agent instance to retrieve. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/agent-instances/{agentInstanceKey}"; ``` --- ## Type Alias: GetAgentInstanceError ```ts type GetAgentInstanceError = GetAgentInstanceErrors[keyof GetAgentInstanceErrors]; ``` --- ## Type Alias: GetAgentInstanceErrors ```ts type GetAgentInstanceErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The agent instance with the given key was not found. More details are provided in the response body. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: GetAgentInstanceResponse ```ts type GetAgentInstanceResponse = GetAgentInstanceResponses[keyof GetAgentInstanceResponses]; ``` --- ## Type Alias: GetAgentInstanceResponses ```ts type GetAgentInstanceResponses = object; ``` ## Properties ### 200 ```ts 200: AgentInstanceResult; ``` The agent instance is successfully returned. --- ## Type Alias: GetAuditLogData ```ts type GetAuditLogData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### auditLogKey ```ts auditLogKey: AuditLogKey; ``` The audit log key. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/audit-logs/{auditLogKey}"; ``` --- ## Type Alias: GetAuditLogError ```ts type GetAuditLogError = GetAuditLogErrors[keyof GetAuditLogErrors]; ``` --- ## Type Alias: GetAuditLogErrors ```ts type GetAuditLogErrors = object; ``` ## Properties ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The audit log with the given key was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetAuditLogResponse ```ts type GetAuditLogResponse = GetAuditLogResponses[keyof GetAuditLogResponses]; ``` --- ## Type Alias: GetAuditLogResponses ```ts type GetAuditLogResponses = object; ``` ## Properties ### 200 ```ts 200: AuditLogResult; ``` The audit log entry is successfully returned. --- ## Type Alias: GetAuthenticationData ```ts type GetAuthenticationData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/authentication/me"; ``` --- ## Type Alias: GetAuthenticationError ```ts type GetAuthenticationError = GetAuthenticationErrors[keyof GetAuthenticationErrors]; ``` --- ## Type Alias: GetAuthenticationErrors ```ts type GetAuthenticationErrors = object; ``` ## Properties ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetAuthenticationResponse ```ts type GetAuthenticationResponse = GetAuthenticationResponses[keyof GetAuthenticationResponses]; ``` --- ## Type Alias: GetAuthenticationResponses ```ts type GetAuthenticationResponses = object; ``` ## Properties ### 200 ```ts 200: CamundaUserResult; ``` The current user is successfully returned. --- ## Type Alias: GetAuthorizationData ```ts type GetAuthorizationData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### authorizationKey ```ts authorizationKey: AuthorizationKey; ``` The key of the authorization to get. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/authorizations/{authorizationKey}"; ``` --- ## Type Alias: GetAuthorizationError ```ts type GetAuthorizationError = GetAuthorizationErrors[keyof GetAuthorizationErrors]; ``` --- ## Type Alias: GetAuthorizationErrors ```ts type GetAuthorizationErrors = object; ``` ## Properties ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The authorization with the given key was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetAuthorizationResponse ```ts type GetAuthorizationResponse = GetAuthorizationResponses[keyof GetAuthorizationResponses]; ``` --- ## Type Alias: GetAuthorizationResponses ```ts type GetAuthorizationResponses = object; ``` ## Properties ### 200 ```ts 200: AuthorizationResult; ``` The authorization was successfully returned. --- ## Type Alias: GetBatchOperationData ```ts type GetBatchOperationData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### batchOperationKey ```ts batchOperationKey: BatchOperationKey; ``` The key (or operate legacy ID) of the batch operation. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/batch-operations/{batchOperationKey}"; ``` --- ## Type Alias: GetBatchOperationError ```ts type GetBatchOperationError = GetBatchOperationErrors[keyof GetBatchOperationErrors]; ``` --- ## Type Alias: GetBatchOperationErrors ```ts type GetBatchOperationErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 404 ```ts 404: ProblemDetail; ``` The batch operation is not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetBatchOperationResponse ```ts type GetBatchOperationResponse = GetBatchOperationResponses[keyof GetBatchOperationResponses]; ``` --- ## Type Alias: GetBatchOperationResponses ```ts type GetBatchOperationResponses = object; ``` ## Properties ### 200 ```ts 200: BatchOperationResponse; ``` The batch operation was found. --- ## Type Alias: GetDecisionDefinitionData ```ts type GetDecisionDefinitionData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### decisionDefinitionKey ```ts decisionDefinitionKey: DecisionDefinitionKey; ``` The assigned key of the decision definition, which acts as a unique identifier for this decision. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/decision-definitions/{decisionDefinitionKey}"; ``` --- ## Type Alias: GetDecisionDefinitionError ```ts type GetDecisionDefinitionError = GetDecisionDefinitionErrors[keyof GetDecisionDefinitionErrors]; ``` --- ## Type Alias: GetDecisionDefinitionErrors ```ts type GetDecisionDefinitionErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The decision definition with the given key was not found. More details are provided in the response body. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetDecisionDefinitionResponse ```ts type GetDecisionDefinitionResponse = GetDecisionDefinitionResponses[keyof GetDecisionDefinitionResponses]; ``` --- ## Type Alias: GetDecisionDefinitionResponses ```ts type GetDecisionDefinitionResponses = object; ``` ## Properties ### 200 ```ts 200: DecisionDefinitionResult; ``` The decision definition is successfully returned. --- ## Type Alias: GetDecisionDefinitionXmlData ```ts type GetDecisionDefinitionXmlData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### decisionDefinitionKey ```ts decisionDefinitionKey: DecisionDefinitionKey; ``` The assigned key of the decision definition, which acts as a unique identifier for this decision. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/decision-definitions/{decisionDefinitionKey}/xml"; ``` --- ## Type Alias: GetDecisionDefinitionXmlError ```ts type GetDecisionDefinitionXmlError = GetDecisionDefinitionXmlErrors[keyof GetDecisionDefinitionXmlErrors]; ``` --- ## Type Alias: GetDecisionDefinitionXmlErrors ```ts type GetDecisionDefinitionXmlErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The decision definition with the given key was not found. More details are provided in the response body. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetDecisionDefinitionXmlResponse ```ts type GetDecisionDefinitionXmlResponse = GetDecisionDefinitionXmlResponses[keyof GetDecisionDefinitionXmlResponses]; ``` --- ## Type Alias: GetDecisionDefinitionXmlResponses ```ts type GetDecisionDefinitionXmlResponses = object; ``` ## Properties ### 200 ```ts 200: string; ``` The XML of the decision definition is successfully returned. --- ## Type Alias: GetDecisionInstanceData ```ts type GetDecisionInstanceData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### decisionEvaluationInstanceKey ```ts decisionEvaluationInstanceKey: DecisionEvaluationInstanceKey; ``` The assigned key of the decision instance, which acts as a unique identifier for this decision instance. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/decision-instances/{decisionEvaluationInstanceKey}"; ``` --- ## Type Alias: GetDecisionInstanceError ```ts type GetDecisionInstanceError = GetDecisionInstanceErrors[keyof GetDecisionInstanceErrors]; ``` --- ## Type Alias: GetDecisionInstanceErrors ```ts type GetDecisionInstanceErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The decision instance with the given key was not found. More details are provided in the response body. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetDecisionInstanceResponse ```ts type GetDecisionInstanceResponse = GetDecisionInstanceResponses[keyof GetDecisionInstanceResponses]; ``` --- ## Type Alias: GetDecisionInstanceResponses ```ts type GetDecisionInstanceResponses = object; ``` ## Properties ### 200 ```ts 200: DecisionInstanceGetQueryResult; ``` The decision instance is successfully returned. --- ## Type Alias: GetDecisionRequirementsData ```ts type GetDecisionRequirementsData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### decisionRequirementsKey ```ts decisionRequirementsKey: DecisionRequirementsKey; ``` The assigned key of the decision requirements, which acts as a unique identifier for this decision requirements. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/decision-requirements/{decisionRequirementsKey}"; ``` --- ## Type Alias: GetDecisionRequirementsError ```ts type GetDecisionRequirementsError = GetDecisionRequirementsErrors[keyof GetDecisionRequirementsErrors]; ``` --- ## Type Alias: GetDecisionRequirementsErrors ```ts type GetDecisionRequirementsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The decision requirements with the given key was not found. More details are provided in the response body. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetDecisionRequirementsResponse ```ts type GetDecisionRequirementsResponse = GetDecisionRequirementsResponses[keyof GetDecisionRequirementsResponses]; ``` --- ## Type Alias: GetDecisionRequirementsResponses ```ts type GetDecisionRequirementsResponses = object; ``` ## Properties ### 200 ```ts 200: DecisionRequirementsResult; ``` The decision requirements is successfully returned. --- ## Type Alias: GetDecisionRequirementsXmlData ```ts type GetDecisionRequirementsXmlData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### decisionRequirementsKey ```ts decisionRequirementsKey: DecisionRequirementsKey; ``` The assigned key of the decision requirements, which acts as a unique identifier for this decision. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/decision-requirements/{decisionRequirementsKey}/xml"; ``` --- ## Type Alias: GetDecisionRequirementsXmlError ```ts type GetDecisionRequirementsXmlError = GetDecisionRequirementsXmlErrors[keyof GetDecisionRequirementsXmlErrors]; ``` --- ## Type Alias: GetDecisionRequirementsXmlErrors ```ts type GetDecisionRequirementsXmlErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The decision requirements with the given key was not found. More details are provided in the response body. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetDecisionRequirementsXmlResponse ```ts type GetDecisionRequirementsXmlResponse = GetDecisionRequirementsXmlResponses[keyof GetDecisionRequirementsXmlResponses]; ``` --- ## Type Alias: GetDecisionRequirementsXmlResponses ```ts type GetDecisionRequirementsXmlResponses = object; ``` ## Properties ### 200 ```ts 200: string; ``` The XML of the decision requirements is successfully returned. --- ## Type Alias: GetDocumentData ```ts type GetDocumentData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### documentId ```ts documentId: DocumentId; ``` The ID of the document to download. --- ### query? ```ts optional query?: object; ``` #### contentHash? ```ts optional contentHash?: string; ``` The hash of the document content that was computed by the document store during upload. The hash is part of the document reference that is returned when uploading a document. If the client fails to provide the correct hash, the request will be rejected. #### storeId? ```ts optional storeId?: string; ``` The ID of the document store to download the document from. --- ### url ```ts url: "/documents/{documentId}"; ``` --- ## Type Alias: GetDocumentError ```ts type GetDocumentError = GetDocumentErrors[keyof GetDocumentErrors]; ``` --- ## Type Alias: GetDocumentErrors ```ts type GetDocumentErrors = object; ``` ## Properties ### 404 ```ts 404: ProblemDetail; ``` The document with the given ID was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetDocumentResponse ```ts type GetDocumentResponse = GetDocumentResponses[keyof GetDocumentResponses]; ``` --- ## Type Alias: GetDocumentResponses ```ts type GetDocumentResponses = object; ``` ## Properties ### 200 ```ts 200: Blob | File; ``` The document was downloaded successfully. --- ## Type Alias: GetElementInstanceData ```ts type GetElementInstanceData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### elementInstanceKey ```ts elementInstanceKey: ElementInstanceKey; ``` The assigned key of the element instance, which acts as a unique identifier for this element instance. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/element-instances/{elementInstanceKey}"; ``` --- ## Type Alias: GetElementInstanceError ```ts type GetElementInstanceError = GetElementInstanceErrors[keyof GetElementInstanceErrors]; ``` --- ## Type Alias: GetElementInstanceErrors ```ts type GetElementInstanceErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The element instance with the given key was not found. More details are provided in the response body. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetElementInstanceResponse ```ts type GetElementInstanceResponse = GetElementInstanceResponses[keyof GetElementInstanceResponses]; ``` --- ## Type Alias: GetElementInstanceResponses ```ts type GetElementInstanceResponses = object; ``` ## Properties ### 200 ```ts 200: ElementInstanceResult; ``` The element instance is successfully returned. --- ## Type Alias: GetFormByKeyData ```ts type GetFormByKeyData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### formKey ```ts formKey: FormKey; ``` The form key. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/forms/{formKey}"; ``` --- ## Type Alias: GetFormByKeyError ```ts type GetFormByKeyError = GetFormByKeyErrors[keyof GetFormByKeyErrors]; ``` --- ## Type Alias: GetFormByKeyErrors ```ts type GetFormByKeyErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The form with the given key was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetFormByKeyResponse ```ts type GetFormByKeyResponse = GetFormByKeyResponses[keyof GetFormByKeyResponses]; ``` --- ## Type Alias: GetFormByKeyResponses ```ts type GetFormByKeyResponses = object; ``` ## Properties ### 200 ```ts 200: FormResult; ``` The form is successfully returned. --- ## Type Alias: GetGlobalClusterVariableData ```ts type GetGlobalClusterVariableData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### name ```ts name: ClusterVariableName; ``` The name of the cluster variable --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/cluster-variables/global/{name}"; ``` --- ## Type Alias: GetGlobalClusterVariableError ```ts type GetGlobalClusterVariableError = GetGlobalClusterVariableErrors[keyof GetGlobalClusterVariableErrors]; ``` --- ## Type Alias: GetGlobalClusterVariableErrors ```ts type GetGlobalClusterVariableErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Cluster variable not found --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetGlobalClusterVariableResponse ```ts type GetGlobalClusterVariableResponse = GetGlobalClusterVariableResponses[keyof GetGlobalClusterVariableResponses]; ``` --- ## Type Alias: GetGlobalClusterVariableResponses ```ts type GetGlobalClusterVariableResponses = object; ``` ## Properties ### 200 ```ts 200: ClusterVariableResult; ``` Cluster variable found --- ## Type Alias: GetGlobalJobStatisticsData ```ts type GetGlobalJobStatisticsData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path? ```ts optional path?: never; ``` --- ### query ```ts query: object; ``` #### from ```ts from: string; ``` Start of the time window to filter metrics. ISO 8601 date-time format. #### jobType? ```ts optional jobType?: string; ``` Optional job type to limit the aggregation to a single job type. #### to ```ts to: string; ``` End of the time window to filter metrics. ISO 8601 date-time format. --- ### url ```ts url: "/jobs/statistics/global"; ``` --- ## Type Alias: GetGlobalJobStatisticsError ```ts type GetGlobalJobStatisticsError = GetGlobalJobStatisticsErrors[keyof GetGlobalJobStatisticsErrors]; ``` --- ## Type Alias: GetGlobalJobStatisticsErrors ```ts type GetGlobalJobStatisticsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetGlobalJobStatisticsResponse ```ts type GetGlobalJobStatisticsResponse = GetGlobalJobStatisticsResponses[keyof GetGlobalJobStatisticsResponses]; ``` --- ## Type Alias: GetGlobalJobStatisticsResponses ```ts type GetGlobalJobStatisticsResponses = object; ``` ## Properties ### 200 ```ts 200: GlobalJobStatisticsQueryResult; ``` Global job metrics --- ## Type Alias: GetGlobalTaskListenerData ```ts type GetGlobalTaskListenerData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### id ```ts id: GlobalListenerId; ``` The id of the global user task listener. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/global-task-listeners/{id}"; ``` --- ## Type Alias: GetGlobalTaskListenerError ```ts type GetGlobalTaskListenerError = GetGlobalTaskListenerErrors[keyof GetGlobalTaskListenerErrors]; ``` --- ## Type Alias: GetGlobalTaskListenerErrors ```ts type GetGlobalTaskListenerErrors = object; ``` ## Properties ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The global user task listener with the given id was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetGlobalTaskListenerResponse ```ts type GetGlobalTaskListenerResponse = GetGlobalTaskListenerResponses[keyof GetGlobalTaskListenerResponses]; ``` --- ## Type Alias: GetGlobalTaskListenerResponses ```ts type GetGlobalTaskListenerResponses = object; ``` ## Properties ### 200 ```ts 200: GlobalTaskListenerResult; ``` The global user task listener is successfully returned. --- ## Type Alias: GetGroupData ```ts type GetGroupData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### groupId ```ts groupId: GroupId; ``` The group ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/groups/{groupId}"; ``` --- ## Type Alias: GetGroupError ```ts type GetGroupError = GetGroupErrors[keyof GetGroupErrors]; ``` --- ## Type Alias: GetGroupErrors ```ts type GetGroupErrors = object; ``` ## Properties ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The group with the given ID was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetGroupResponse ```ts type GetGroupResponse = GetGroupResponses[keyof GetGroupResponses]; ``` --- ## Type Alias: GetGroupResponses ```ts type GetGroupResponses = object; ``` ## Properties ### 200 ```ts 200: GroupResult; ``` The group is successfully returned. --- ## Type Alias: GetIncidentData ```ts type GetIncidentData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### incidentKey ```ts incidentKey: IncidentKey; ``` The assigned key of the incident, which acts as a unique identifier for this incident. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/incidents/{incidentKey}"; ``` --- ## Type Alias: GetIncidentError ```ts type GetIncidentError = GetIncidentErrors[keyof GetIncidentErrors]; ``` --- ## Type Alias: GetIncidentErrors ```ts type GetIncidentErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The incident with the given key was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetIncidentResponse ```ts type GetIncidentResponse = GetIncidentResponses[keyof GetIncidentResponses]; ``` --- ## Type Alias: GetIncidentResponses ```ts type GetIncidentResponses = object; ``` ## Properties ### 200 ```ts 200: IncidentResult; ``` The incident is successfully returned. --- ## Type Alias: GetJobErrorStatisticsData ```ts type GetJobErrorStatisticsData = object; ``` ## Properties ### body ```ts body: JobErrorStatisticsQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/jobs/statistics/errors"; ``` --- ## Type Alias: GetJobErrorStatisticsError ```ts type GetJobErrorStatisticsError = GetJobErrorStatisticsErrors[keyof GetJobErrorStatisticsErrors]; ``` --- ## Type Alias: GetJobErrorStatisticsErrors ```ts type GetJobErrorStatisticsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetJobErrorStatisticsResponse ```ts type GetJobErrorStatisticsResponse = GetJobErrorStatisticsResponses[keyof GetJobErrorStatisticsResponses]; ``` --- ## Type Alias: GetJobErrorStatisticsResponses ```ts type GetJobErrorStatisticsResponses = object; ``` ## Properties ### 200 ```ts 200: JobErrorStatisticsQueryResult; ``` The job error statistics result. --- ## Type Alias: GetJobTimeSeriesStatisticsData ```ts type GetJobTimeSeriesStatisticsData = object; ``` ## Properties ### body ```ts body: JobTimeSeriesStatisticsQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/jobs/statistics/time-series"; ``` --- ## Type Alias: GetJobTimeSeriesStatisticsError ```ts type GetJobTimeSeriesStatisticsError = GetJobTimeSeriesStatisticsErrors[keyof GetJobTimeSeriesStatisticsErrors]; ``` --- ## Type Alias: GetJobTimeSeriesStatisticsErrors ```ts type GetJobTimeSeriesStatisticsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetJobTimeSeriesStatisticsResponse ```ts type GetJobTimeSeriesStatisticsResponse = GetJobTimeSeriesStatisticsResponses[keyof GetJobTimeSeriesStatisticsResponses]; ``` --- ## Type Alias: GetJobTimeSeriesStatisticsResponses ```ts type GetJobTimeSeriesStatisticsResponses = object; ``` ## Properties ### 200 ```ts 200: JobTimeSeriesStatisticsQueryResult; ``` The job time-series statistics result. --- ## Type Alias: GetJobTypeStatisticsData ```ts type GetJobTypeStatisticsData = object; ``` ## Properties ### body ```ts body: JobTypeStatisticsQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/jobs/statistics/by-types"; ``` --- ## Type Alias: GetJobTypeStatisticsError ```ts type GetJobTypeStatisticsError = GetJobTypeStatisticsErrors[keyof GetJobTypeStatisticsErrors]; ``` --- ## Type Alias: GetJobTypeStatisticsErrors ```ts type GetJobTypeStatisticsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetJobTypeStatisticsResponse ```ts type GetJobTypeStatisticsResponse = GetJobTypeStatisticsResponses[keyof GetJobTypeStatisticsResponses]; ``` --- ## Type Alias: GetJobTypeStatisticsResponses ```ts type GetJobTypeStatisticsResponses = object; ``` ## Properties ### 200 ```ts 200: JobTypeStatisticsQueryResult; ``` The job type statistics result. --- ## Type Alias: GetJobWorkerStatisticsData ```ts type GetJobWorkerStatisticsData = object; ``` ## Properties ### body ```ts body: JobWorkerStatisticsQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/jobs/statistics/by-workers"; ``` --- ## Type Alias: GetJobWorkerStatisticsError ```ts type GetJobWorkerStatisticsError = GetJobWorkerStatisticsErrors[keyof GetJobWorkerStatisticsErrors]; ``` --- ## Type Alias: GetJobWorkerStatisticsErrors ```ts type GetJobWorkerStatisticsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetJobWorkerStatisticsResponse ```ts type GetJobWorkerStatisticsResponse = GetJobWorkerStatisticsResponses[keyof GetJobWorkerStatisticsResponses]; ``` --- ## Type Alias: GetJobWorkerStatisticsResponses ```ts type GetJobWorkerStatisticsResponses = object; ``` ## Properties ### 200 ```ts 200: JobWorkerStatisticsQueryResult; ``` The job worker statistics result. --- ## Type Alias: GetLicenseData ```ts type GetLicenseData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/license"; ``` --- ## Type Alias: GetLicenseError ```ts type GetLicenseError = GetLicenseErrors[keyof GetLicenseErrors]; ``` --- ## Type Alias: GetLicenseErrors ```ts type GetLicenseErrors = object; ``` ## Properties ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetLicenseResponse ```ts type GetLicenseResponse = GetLicenseResponses[keyof GetLicenseResponses]; ``` --- ## Type Alias: GetLicenseResponses ```ts type GetLicenseResponses = object; ``` ## Properties ### 200 ```ts 200: LicenseResponse; ``` Obtains the current status of the Camunda license. --- ## Type Alias: GetMappingRuleData ```ts type GetMappingRuleData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### mappingRuleId ```ts mappingRuleId: MappingRuleId; ``` The ID of the mapping rule to get. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/mapping-rules/{mappingRuleId}"; ``` --- ## Type Alias: GetMappingRuleError ```ts type GetMappingRuleError = GetMappingRuleErrors[keyof GetMappingRuleErrors]; ``` --- ## Type Alias: GetMappingRuleErrors ```ts type GetMappingRuleErrors = object; ``` ## Properties ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 404 ```ts 404: ProblemDetail; ``` The mapping rule with the mappingRuleId was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetMappingRuleResponse ```ts type GetMappingRuleResponse = GetMappingRuleResponses[keyof GetMappingRuleResponses]; ``` --- ## Type Alias: GetMappingRuleResponses ```ts type GetMappingRuleResponses = object; ``` ## Properties ### 200 ```ts 200: MappingRuleResult; ``` The mapping rule was returned successfully. --- ## Type Alias: GetProcessDefinitionData ```ts type GetProcessDefinitionData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` The assigned key of the process definition, which acts as a unique identifier for this process definition. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-definitions/{processDefinitionKey}"; ``` --- ## Type Alias: GetProcessDefinitionError ```ts type GetProcessDefinitionError = GetProcessDefinitionErrors[keyof GetProcessDefinitionErrors]; ``` --- ## Type Alias: GetProcessDefinitionErrors ```ts type GetProcessDefinitionErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The process definition with the given key was not found. More details are provided in the response body. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetProcessDefinitionInstanceStatisticsData ```ts type GetProcessDefinitionInstanceStatisticsData = object; ``` ## Properties ### body? ```ts optional body?: ProcessDefinitionInstanceStatisticsQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-definitions/statistics/process-instances"; ``` --- ## Type Alias: GetProcessDefinitionInstanceStatisticsError ```ts type GetProcessDefinitionInstanceStatisticsError = GetProcessDefinitionInstanceStatisticsErrors[keyof GetProcessDefinitionInstanceStatisticsErrors]; ``` --- ## Type Alias: GetProcessDefinitionInstanceStatisticsErrors ```ts type GetProcessDefinitionInstanceStatisticsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetProcessDefinitionInstanceStatisticsResponse ```ts type GetProcessDefinitionInstanceStatisticsResponse = GetProcessDefinitionInstanceStatisticsResponses[keyof GetProcessDefinitionInstanceStatisticsResponses]; ``` --- ## Type Alias: GetProcessDefinitionInstanceStatisticsResponses ```ts type GetProcessDefinitionInstanceStatisticsResponses = object; ``` ## Properties ### 200 ```ts 200: ProcessDefinitionInstanceStatisticsQueryResult; ``` The process definition instance statistic result. --- ## Type Alias: GetProcessDefinitionInstanceVersionStatisticsData ```ts type GetProcessDefinitionInstanceVersionStatisticsData = object; ``` ## Properties ### body ```ts body: ProcessDefinitionInstanceVersionStatisticsQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-definitions/statistics/process-instances-by-version"; ``` --- ## Type Alias: GetProcessDefinitionInstanceVersionStatisticsError ```ts type GetProcessDefinitionInstanceVersionStatisticsError = GetProcessDefinitionInstanceVersionStatisticsErrors[keyof GetProcessDefinitionInstanceVersionStatisticsErrors]; ``` --- ## Type Alias: GetProcessDefinitionInstanceVersionStatisticsErrors ```ts type GetProcessDefinitionInstanceVersionStatisticsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetProcessDefinitionInstanceVersionStatisticsResponse ```ts type GetProcessDefinitionInstanceVersionStatisticsResponse = GetProcessDefinitionInstanceVersionStatisticsResponses[keyof GetProcessDefinitionInstanceVersionStatisticsResponses]; ``` --- ## Type Alias: GetProcessDefinitionInstanceVersionStatisticsResponses ```ts type GetProcessDefinitionInstanceVersionStatisticsResponses = object; ``` ## Properties ### 200 ```ts 200: ProcessDefinitionInstanceVersionStatisticsQueryResult; ``` The process definition instance version statistic result. --- ## Type Alias: GetProcessDefinitionMessageSubscriptionStatisticsData ```ts type GetProcessDefinitionMessageSubscriptionStatisticsData = object; ``` ## Properties ### body? ```ts optional body?: ProcessDefinitionMessageSubscriptionStatisticsQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-definitions/statistics/message-subscriptions"; ``` --- ## Type Alias: GetProcessDefinitionMessageSubscriptionStatisticsError ```ts type GetProcessDefinitionMessageSubscriptionStatisticsError = GetProcessDefinitionMessageSubscriptionStatisticsErrors[keyof GetProcessDefinitionMessageSubscriptionStatisticsErrors]; ``` --- ## Type Alias: GetProcessDefinitionMessageSubscriptionStatisticsErrors ```ts type GetProcessDefinitionMessageSubscriptionStatisticsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetProcessDefinitionMessageSubscriptionStatisticsResponse ```ts type GetProcessDefinitionMessageSubscriptionStatisticsResponse = GetProcessDefinitionMessageSubscriptionStatisticsResponses[keyof GetProcessDefinitionMessageSubscriptionStatisticsResponses]; ``` --- ## Type Alias: GetProcessDefinitionMessageSubscriptionStatisticsResponses ```ts type GetProcessDefinitionMessageSubscriptionStatisticsResponses = object; ``` ## Properties ### 200 ```ts 200: ProcessDefinitionMessageSubscriptionStatisticsQueryResult; ``` The process definition message subscription statistics result. --- ## Type Alias: GetProcessDefinitionResponse ```ts type GetProcessDefinitionResponse = GetProcessDefinitionResponses[keyof GetProcessDefinitionResponses]; ``` --- ## Type Alias: GetProcessDefinitionResponses ```ts type GetProcessDefinitionResponses = object; ``` ## Properties ### 200 ```ts 200: ProcessDefinitionResult; ``` The process definition is successfully returned. --- ## Type Alias: GetProcessDefinitionStatisticsData ```ts type GetProcessDefinitionStatisticsData = object; ``` ## Properties ### body? ```ts optional body?: ProcessDefinitionElementStatisticsQuery; ``` --- ### path ```ts path: object; ``` #### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` The assigned key of the process definition, which acts as a unique identifier for this process definition. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-definitions/{processDefinitionKey}/statistics/element-instances"; ``` --- ## Type Alias: GetProcessDefinitionStatisticsError ```ts type GetProcessDefinitionStatisticsError = GetProcessDefinitionStatisticsErrors[keyof GetProcessDefinitionStatisticsErrors]; ``` --- ## Type Alias: GetProcessDefinitionStatisticsErrors ```ts type GetProcessDefinitionStatisticsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetProcessDefinitionStatisticsResponse ```ts type GetProcessDefinitionStatisticsResponse = GetProcessDefinitionStatisticsResponses[keyof GetProcessDefinitionStatisticsResponses]; ``` --- ## Type Alias: GetProcessDefinitionStatisticsResponses ```ts type GetProcessDefinitionStatisticsResponses = object; ``` ## Properties ### 200 ```ts 200: ProcessDefinitionElementStatisticsQueryResult; ``` The process definition statistics result. --- ## Type Alias: GetProcessDefinitionXmlData ```ts type GetProcessDefinitionXmlData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` The assigned key of the process definition, which acts as a unique identifier for this process definition. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-definitions/{processDefinitionKey}/xml"; ``` --- ## Type Alias: GetProcessDefinitionXmlError ```ts type GetProcessDefinitionXmlError = GetProcessDefinitionXmlErrors[keyof GetProcessDefinitionXmlErrors]; ``` --- ## Type Alias: GetProcessDefinitionXmlErrors ```ts type GetProcessDefinitionXmlErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The process definition with the given key was not found. More details are provided in the response body. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetProcessDefinitionXmlResponse ```ts type GetProcessDefinitionXmlResponse = GetProcessDefinitionXmlResponses[keyof GetProcessDefinitionXmlResponses]; ``` --- ## Type Alias: GetProcessDefinitionXmlResponses ```ts type GetProcessDefinitionXmlResponses = object; ``` ## Properties ### 200 ```ts 200: string; ``` The XML of the process definition is successfully returned. --- ### 204 ```ts 204: string; ``` The process definition was found but does not have XML. --- ## Type Alias: GetProcessInstanceCallHierarchyData ```ts type GetProcessInstanceCallHierarchyData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The key of the process instance to fetch the hierarchy for. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-instances/{processInstanceKey}/call-hierarchy"; ``` --- ## Type Alias: GetProcessInstanceCallHierarchyError ```ts type GetProcessInstanceCallHierarchyError = GetProcessInstanceCallHierarchyErrors[keyof GetProcessInstanceCallHierarchyErrors]; ``` --- ## Type Alias: GetProcessInstanceCallHierarchyErrors ```ts type GetProcessInstanceCallHierarchyErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The process instance is not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetProcessInstanceCallHierarchyResponse ```ts type GetProcessInstanceCallHierarchyResponse = GetProcessInstanceCallHierarchyResponses[keyof GetProcessInstanceCallHierarchyResponses]; ``` --- ## Type Alias: GetProcessInstanceCallHierarchyResponses ```ts type GetProcessInstanceCallHierarchyResponses = object; ``` ## Properties ### 200 ```ts 200: ProcessInstanceCallHierarchyEntry[]; ``` The call hierarchy is successfully returned. --- ## Type Alias: GetProcessInstanceData ```ts type GetProcessInstanceData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The process instance key. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-instances/{processInstanceKey}"; ``` --- ## Type Alias: GetProcessInstanceError ```ts type GetProcessInstanceError = GetProcessInstanceErrors[keyof GetProcessInstanceErrors]; ``` --- ## Type Alias: GetProcessInstanceErrors ```ts type GetProcessInstanceErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The process instance with the given key was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetProcessInstanceResponse ```ts type GetProcessInstanceResponse = GetProcessInstanceResponses[keyof GetProcessInstanceResponses]; ``` --- ## Type Alias: GetProcessInstanceResponses ```ts type GetProcessInstanceResponses = object; ``` ## Properties ### 200 ```ts 200: ProcessInstanceResult; ``` The process instance is successfully returned. --- ## Type Alias: GetProcessInstanceSequenceFlowsData ```ts type GetProcessInstanceSequenceFlowsData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The assigned key of the process instance, which acts as a unique identifier for this process instance. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-instances/{processInstanceKey}/sequence-flows"; ``` --- ## Type Alias: GetProcessInstanceSequenceFlowsError ```ts type GetProcessInstanceSequenceFlowsError = GetProcessInstanceSequenceFlowsErrors[keyof GetProcessInstanceSequenceFlowsErrors]; ``` --- ## Type Alias: GetProcessInstanceSequenceFlowsErrors ```ts type GetProcessInstanceSequenceFlowsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetProcessInstanceSequenceFlowsResponse ```ts type GetProcessInstanceSequenceFlowsResponse = GetProcessInstanceSequenceFlowsResponses[keyof GetProcessInstanceSequenceFlowsResponses]; ``` --- ## Type Alias: GetProcessInstanceSequenceFlowsResponses ```ts type GetProcessInstanceSequenceFlowsResponses = object; ``` ## Properties ### 200 ```ts 200: ProcessInstanceSequenceFlowsQueryResult; ``` The process instance sequence flows result. --- ## Type Alias: GetProcessInstanceStatisticsByDefinitionData ```ts type GetProcessInstanceStatisticsByDefinitionData = object; ``` ## Properties ### body ```ts body: IncidentProcessInstanceStatisticsByDefinitionQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/incidents/statistics/process-instances-by-definition"; ``` --- ## Type Alias: GetProcessInstanceStatisticsByDefinitionError ```ts type GetProcessInstanceStatisticsByDefinitionError = GetProcessInstanceStatisticsByDefinitionErrors[keyof GetProcessInstanceStatisticsByDefinitionErrors]; ``` --- ## Type Alias: GetProcessInstanceStatisticsByDefinitionErrors ```ts type GetProcessInstanceStatisticsByDefinitionErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetProcessInstanceStatisticsByDefinitionResponse ```ts type GetProcessInstanceStatisticsByDefinitionResponse = GetProcessInstanceStatisticsByDefinitionResponses[keyof GetProcessInstanceStatisticsByDefinitionResponses]; ``` --- ## Type Alias: GetProcessInstanceStatisticsByDefinitionResponses ```ts type GetProcessInstanceStatisticsByDefinitionResponses = object; ``` ## Properties ### 200 ```ts 200: IncidentProcessInstanceStatisticsByDefinitionQueryResult; ``` The process instance incident statistics grouped by process definition are successfully returned. --- ## Type Alias: GetProcessInstanceStatisticsByErrorData ```ts type GetProcessInstanceStatisticsByErrorData = object; ``` ## Properties ### body? ```ts optional body?: IncidentProcessInstanceStatisticsByErrorQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/incidents/statistics/process-instances-by-error"; ``` --- ## Type Alias: GetProcessInstanceStatisticsByErrorError ```ts type GetProcessInstanceStatisticsByErrorError = GetProcessInstanceStatisticsByErrorErrors[keyof GetProcessInstanceStatisticsByErrorErrors]; ``` --- ## Type Alias: GetProcessInstanceStatisticsByErrorErrors ```ts type GetProcessInstanceStatisticsByErrorErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetProcessInstanceStatisticsByErrorResponse ```ts type GetProcessInstanceStatisticsByErrorResponse = GetProcessInstanceStatisticsByErrorResponses[keyof GetProcessInstanceStatisticsByErrorResponses]; ``` --- ## Type Alias: GetProcessInstanceStatisticsByErrorResponses ```ts type GetProcessInstanceStatisticsByErrorResponses = object; ``` ## Properties ### 200 ```ts 200: IncidentProcessInstanceStatisticsByErrorQueryResult; ``` The statistics about process instances with incident, grouped by error hash code are successfully returned. --- ## Type Alias: GetProcessInstanceStatisticsData ```ts type GetProcessInstanceStatisticsData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The assigned key of the process instance, which acts as a unique identifier for this process instance. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-instances/{processInstanceKey}/statistics/element-instances"; ``` --- ## Type Alias: GetProcessInstanceStatisticsError ```ts type GetProcessInstanceStatisticsError = GetProcessInstanceStatisticsErrors[keyof GetProcessInstanceStatisticsErrors]; ``` --- ## Type Alias: GetProcessInstanceStatisticsErrors ```ts type GetProcessInstanceStatisticsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetProcessInstanceStatisticsResponse ```ts type GetProcessInstanceStatisticsResponse = GetProcessInstanceStatisticsResponses[keyof GetProcessInstanceStatisticsResponses]; ``` --- ## Type Alias: GetProcessInstanceStatisticsResponses ```ts type GetProcessInstanceStatisticsResponses = object; ``` ## Properties ### 200 ```ts 200: ProcessInstanceElementStatisticsQueryResult; ``` The process instance statistics result. --- ## Type Alias: GetResourceContentBinaryData ```ts type GetResourceContentBinaryData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### resourceKey ```ts resourceKey: ResourceKey; ``` The unique key identifying the resource. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/resources/{resourceKey}/content/binary"; ``` --- ## Type Alias: GetResourceContentBinaryError ```ts type GetResourceContentBinaryError = GetResourceContentBinaryErrors[keyof GetResourceContentBinaryErrors]; ``` --- ## Type Alias: GetResourceContentBinaryErrors ```ts type GetResourceContentBinaryErrors = object; ``` ## Properties ### 404 ```ts 404: ProblemDetail; ``` A resource with the given key was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetResourceContentBinaryResponse ```ts type GetResourceContentBinaryResponse = GetResourceContentBinaryResponses[keyof GetResourceContentBinaryResponses]; ``` --- ## Type Alias: GetResourceContentBinaryResponses ```ts type GetResourceContentBinaryResponses = object; ``` ## Properties ### 200 ```ts 200: Blob | File; ``` The resource content is successfully returned. --- ## Type Alias: GetResourceContentData ```ts type GetResourceContentData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### resourceKey ```ts resourceKey: ResourceKey; ``` The unique key identifying the RPA resource. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/resources/{resourceKey}/content"; ``` --- ## Type Alias: GetResourceContentError ```ts type GetResourceContentError = GetResourceContentErrors[keyof GetResourceContentErrors]; ``` --- ## Type Alias: GetResourceContentErrors ```ts type GetResourceContentErrors = object; ``` ## Properties ### 404 ```ts 404: ProblemDetail; ``` A resource with the given key was not found. --- ### 406 ```ts 406: ProblemDetail; ``` The resource exists but is not an RPA resource. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetResourceContentResponse ```ts type GetResourceContentResponse = GetResourceContentResponses[keyof GetResourceContentResponses]; ``` --- ## Type Alias: GetResourceContentResponses ```ts type GetResourceContentResponses = object; ``` ## Properties ### 200 ```ts 200: object; ``` The resource content is successfully returned. #### Index Signature ```ts [key: string]: unknown ``` --- ## Type Alias: GetResourceData ```ts type GetResourceData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### resourceKey ```ts resourceKey: ResourceKey; ``` The unique key identifying the resource. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/resources/{resourceKey}"; ``` --- ## Type Alias: GetResourceError ```ts type GetResourceError = GetResourceErrors[keyof GetResourceErrors]; ``` --- ## Type Alias: GetResourceErrors ```ts type GetResourceErrors = object; ``` ## Properties ### 404 ```ts 404: ProblemDetail; ``` A resource with the given key was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetResourceResponse ```ts type GetResourceResponse = GetResourceResponses[keyof GetResourceResponses]; ``` --- ## Type Alias: GetResourceResponses ```ts type GetResourceResponses = object; ``` ## Properties ### 200 ```ts 200: ResourceResult; ``` The resource is successfully returned. --- ## Type Alias: GetRoleData ```ts type GetRoleData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### roleId ```ts roleId: RoleId; ``` The role ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/roles/{roleId}"; ``` --- ## Type Alias: GetRoleError ```ts type GetRoleError = GetRoleErrors[keyof GetRoleErrors]; ``` --- ## Type Alias: GetRoleErrors ```ts type GetRoleErrors = object; ``` ## Properties ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The role with the given ID was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetRoleResponse ```ts type GetRoleResponse = GetRoleResponses[keyof GetRoleResponses]; ``` --- ## Type Alias: GetRoleResponses ```ts type GetRoleResponses = object; ``` ## Properties ### 200 ```ts 200: RoleResult; ``` The role is successfully returned. --- ## Type Alias: GetStartProcessFormData ```ts type GetStartProcessFormData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` The process key. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-definitions/{processDefinitionKey}/form"; ``` --- ## Type Alias: GetStartProcessFormError ```ts type GetStartProcessFormError = GetStartProcessFormErrors[keyof GetStartProcessFormErrors]; ``` --- ## Type Alias: GetStartProcessFormErrors ```ts type GetStartProcessFormErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Not found --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetStartProcessFormResponse ```ts type GetStartProcessFormResponse = GetStartProcessFormResponses[keyof GetStartProcessFormResponses]; ``` --- ## Type Alias: GetStartProcessFormResponses ```ts type GetStartProcessFormResponses = object; ``` ## Properties ### 200 ```ts 200: FormResult; ``` The form is successfully returned. --- ### 204 ```ts 204: void; ``` The process was found, but no form is associated with it. --- ## Type Alias: GetStatusData ```ts type GetStatusData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/status"; ``` --- ## Type Alias: GetStatusErrors ```ts type GetStatusErrors = object; ``` ## Properties ### 503 ```ts 503: unknown; ``` The cluster is DOWN and does not have any partition with a healthy leader. --- ## Type Alias: GetStatusResponse ```ts type GetStatusResponse = GetStatusResponses[keyof GetStatusResponses]; ``` --- ## Type Alias: GetStatusResponses ```ts type GetStatusResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The cluster is UP and has at least one partition with a healthy leader. --- ## Type Alias: GetSystemConfigurationData ```ts type GetSystemConfigurationData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/system/configuration"; ``` --- ## Type Alias: GetSystemConfigurationError ```ts type GetSystemConfigurationError = GetSystemConfigurationErrors[keyof GetSystemConfigurationErrors]; ``` --- ## Type Alias: GetSystemConfigurationErrors ```ts type GetSystemConfigurationErrors = object; ``` ## Properties ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetSystemConfigurationResponse ```ts type GetSystemConfigurationResponse = GetSystemConfigurationResponses[keyof GetSystemConfigurationResponses]; ``` --- ## Type Alias: GetSystemConfigurationResponses ```ts type GetSystemConfigurationResponses = object; ``` ## Properties ### 200 ```ts 200: SystemConfigurationResponse; ``` Current system configuration grouped by feature area. --- ## Type Alias: GetTenantClusterVariableData ```ts type GetTenantClusterVariableData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### name ```ts name: ClusterVariableName; ``` The name of the cluster variable #### tenantId ```ts tenantId: TenantId; ``` The tenant ID --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/cluster-variables/tenants/{tenantId}/{name}"; ``` --- ## Type Alias: GetTenantClusterVariableError ```ts type GetTenantClusterVariableError = GetTenantClusterVariableErrors[keyof GetTenantClusterVariableErrors]; ``` --- ## Type Alias: GetTenantClusterVariableErrors ```ts type GetTenantClusterVariableErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Cluster variable not found --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetTenantClusterVariableResponse ```ts type GetTenantClusterVariableResponse = GetTenantClusterVariableResponses[keyof GetTenantClusterVariableResponses]; ``` --- ## Type Alias: GetTenantClusterVariableResponses ```ts type GetTenantClusterVariableResponses = object; ``` ## Properties ### 200 ```ts 200: ClusterVariableResult; ``` Cluster variable found --- ## Type Alias: GetTenantData ```ts type GetTenantData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### tenantId ```ts tenantId: TenantId; ``` The unique identifier of the tenant. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/tenants/{tenantId}"; ``` --- ## Type Alias: GetTenantError ```ts type GetTenantError = GetTenantErrors[keyof GetTenantErrors]; ``` --- ## Type Alias: GetTenantErrors ```ts type GetTenantErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Tenant not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetTenantResponse ```ts type GetTenantResponse = GetTenantResponses[keyof GetTenantResponses]; ``` --- ## Type Alias: GetTenantResponses ```ts type GetTenantResponses = object; ``` ## Properties ### 200 ```ts 200: TenantResult; ``` The tenant was retrieved successfully. --- ## Type Alias: GetTopologyData ```ts type GetTopologyData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/topology"; ``` --- ## Type Alias: GetTopologyError ```ts type GetTopologyError = GetTopologyErrors[keyof GetTopologyErrors]; ``` --- ## Type Alias: GetTopologyErrors ```ts type GetTopologyErrors = object; ``` ## Properties ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetTopologyResponse ```ts type GetTopologyResponse = GetTopologyResponses[keyof GetTopologyResponses]; ``` --- ## Type Alias: GetTopologyResponses ```ts type GetTopologyResponses = object; ``` ## Properties ### 200 ```ts 200: TopologyResponse; ``` Obtains the current topology of the cluster the gateway is part of. --- ## Type Alias: GetUsageMetricsData ```ts type GetUsageMetricsData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path? ```ts optional path?: never; ``` --- ### query ```ts query: object; ``` #### endTime ```ts endTime: string; ``` The end date for usage metrics, including this date. Value in ISO 8601 format. #### startTime ```ts startTime: string; ``` The start date for usage metrics, including this date. Value in ISO 8601 format. #### tenantId? ```ts optional tenantId?: TenantId; ``` Restrict results to a specific tenant ID. If not provided, results for all tenants are returned. #### withTenants? ```ts optional withTenants?: boolean; ``` Whether to return tenant metrics in addition to the total metrics or not. Default false. --- ### url ```ts url: "/system/usage-metrics"; ``` --- ## Type Alias: GetUsageMetricsError ```ts type GetUsageMetricsError = GetUsageMetricsErrors[keyof GetUsageMetricsErrors]; ``` --- ## Type Alias: GetUsageMetricsErrors ```ts type GetUsageMetricsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetUsageMetricsResponse ```ts type GetUsageMetricsResponse = GetUsageMetricsResponses[keyof GetUsageMetricsResponses]; ``` --- ## Type Alias: GetUsageMetricsResponses ```ts type GetUsageMetricsResponses = object; ``` ## Properties ### 200 ```ts 200: UsageMetricsResponse; ``` The usage metrics search result. --- ## Type Alias: GetUserData ```ts type GetUserData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### username ```ts username: Username; ``` The username of the user. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/users/{username}"; ``` --- ## Type Alias: GetUserError ```ts type GetUserError = GetUserErrors[keyof GetUserErrors]; ``` --- ## Type Alias: GetUserErrors ```ts type GetUserErrors = object; ``` ## Properties ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The user with the given username was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetUserResponse ```ts type GetUserResponse = GetUserResponses[keyof GetUserResponses]; ``` --- ## Type Alias: GetUserResponses ```ts type GetUserResponses = object; ``` ## Properties ### 200 ```ts 200: UserResult; ``` The user is successfully returned. --- ## Type Alias: GetUserTaskData ```ts type GetUserTaskData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### userTaskKey ```ts userTaskKey: UserTaskKey; ``` The user task key. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/user-tasks/{userTaskKey}"; ``` --- ## Type Alias: GetUserTaskError ```ts type GetUserTaskError = GetUserTaskErrors[keyof GetUserTaskErrors]; ``` --- ## Type Alias: GetUserTaskErrors ```ts type GetUserTaskErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The user task with the given key was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetUserTaskFormData ```ts type GetUserTaskFormData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### userTaskKey ```ts userTaskKey: UserTaskKey; ``` The user task key. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/user-tasks/{userTaskKey}/form"; ``` --- ## Type Alias: GetUserTaskFormError ```ts type GetUserTaskFormError = GetUserTaskFormErrors[keyof GetUserTaskFormErrors]; ``` --- ## Type Alias: GetUserTaskFormErrors ```ts type GetUserTaskFormErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Not found --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetUserTaskFormResponse ```ts type GetUserTaskFormResponse = GetUserTaskFormResponses[keyof GetUserTaskFormResponses]; ``` --- ## Type Alias: GetUserTaskFormResponses ```ts type GetUserTaskFormResponses = object; ``` ## Properties ### 200 ```ts 200: FormResult; ``` The form is successfully returned. --- ### 204 ```ts 204: void; ``` The user task was found, but no form is associated with it. --- ## Type Alias: GetUserTaskResponse ```ts type GetUserTaskResponse = GetUserTaskResponses[keyof GetUserTaskResponses]; ``` --- ## Type Alias: GetUserTaskResponses ```ts type GetUserTaskResponses = object; ``` ## Properties ### 200 ```ts 200: UserTaskResult; ``` The user task is successfully returned. --- ## Type Alias: GetVariableData ```ts type GetVariableData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### variableKey ```ts variableKey: VariableKey; ``` The variable key. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/variables/{variableKey}"; ``` --- ## Type Alias: GetVariableError ```ts type GetVariableError = GetVariableErrors[keyof GetVariableErrors]; ``` --- ## Type Alias: GetVariableErrors ```ts type GetVariableErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Not found --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: GetVariableResponse ```ts type GetVariableResponse = GetVariableResponses[keyof GetVariableResponses]; ``` --- ## Type Alias: GetVariableResponses ```ts type GetVariableResponses = object; ``` ## Properties ### 200 ```ts 200: VariableResult; ``` The variable is successfully returned. --- ## Type Alias: GlobalJobStatisticsQueryResult ```ts type GlobalJobStatisticsQueryResult = object; ``` Global job statistics query result. ## Properties ### completed ```ts completed: StatusMetric; ``` --- ### created ```ts created: StatusMetric; ``` --- ### failed ```ts failed: StatusMetric; ``` --- ### isIncomplete ```ts isIncomplete: boolean; ``` True if some data is missing because internal limits were reached and some metrics were not recorded. --- ## Type Alias: GlobalListenerBase ```ts type GlobalListenerBase = object; ``` ## Properties ### afterNonGlobal? ```ts optional afterNonGlobal?: boolean; ``` Whether the listener should run after model-level listeners. --- ### priority? ```ts optional priority?: number; ``` The priority of the listener. Higher priority listeners are executed before lower priority ones. --- ### retries? ```ts optional retries?: number; ``` Number of retries for the listener job. --- ### type? ```ts optional type?: string; ``` The name of the job type, used as a reference to specify which job workers request the respective listener job. --- ## Type Alias: GlobalListenerId ```ts type GlobalListenerId = CamundaKey<"GlobalListenerId">; ``` The user-defined id for the global listener --- ## Type Alias: GlobalListenerSourceEnum ```ts type GlobalListenerSourceEnum = (typeof GlobalListenerSourceEnum)[keyof typeof GlobalListenerSourceEnum]; ``` How the global listener was defined. --- ## Type Alias: GlobalListenerSourceExactMatch ```ts type GlobalListenerSourceExactMatch = GlobalListenerSourceEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: GlobalListenerSourceFilterProperty ```ts type GlobalListenerSourceFilterProperty = | GlobalListenerSourceExactMatch | AdvancedGlobalListenerSourceFilter; ``` Global listener source property with full advanced search capabilities. --- ## Type Alias: GlobalTaskListenerBase ```ts type GlobalTaskListenerBase = GlobalListenerBase & object; ``` ## Type Declaration ### eventTypes? ```ts optional eventTypes?: GlobalTaskListenerEventTypes; ``` --- ## Type Alias: GlobalTaskListenerEventTypeEnum ```ts type GlobalTaskListenerEventTypeEnum = (typeof GlobalTaskListenerEventTypeEnum)[keyof typeof GlobalTaskListenerEventTypeEnum]; ``` The event type that triggers the user task listener. --- ## Type Alias: GlobalTaskListenerEventTypeExactMatch ```ts type GlobalTaskListenerEventTypeExactMatch = GlobalTaskListenerEventTypeEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: GlobalTaskListenerEventTypeFilterProperty ```ts type GlobalTaskListenerEventTypeFilterProperty = | GlobalTaskListenerEventTypeExactMatch | AdvancedGlobalTaskListenerEventTypeFilter; ``` Global listener event type property with full advanced search capabilities. --- ## Type Alias: GlobalTaskListenerEventTypes ```ts type GlobalTaskListenerEventTypes = GlobalTaskListenerEventTypeEnum[]; ``` List of user task event types that trigger the listener. --- ## Type Alias: GlobalTaskListenerResult ```ts type GlobalTaskListenerResult = GlobalTaskListenerBase & object; ``` ## Type Declaration ### eventTypes ```ts eventTypes: GlobalTaskListenerEventTypes; ``` ### id ```ts id: GlobalListenerId; ``` ### source ```ts source: GlobalListenerSourceEnum; ``` --- ## Type Alias: GlobalTaskListenerSearchQueryFilterRequest ```ts type GlobalTaskListenerSearchQueryFilterRequest = object; ``` Global listener filter request. ## Properties ### afterNonGlobal? ```ts optional afterNonGlobal?: boolean; ``` Whether the listener runs after model-level listeners. --- ### eventTypes? ```ts optional eventTypes?: GlobalTaskListenerEventTypeFilterProperty[]; ``` Event types of the global listener. --- ### id? ```ts optional id?: StringFilterProperty; ``` Id of the global listener. --- ### priority? ```ts optional priority?: IntegerFilterProperty; ``` Priority of the global listener. --- ### retries? ```ts optional retries?: IntegerFilterProperty; ``` Number of retries of the global listener. --- ### source? ```ts optional source?: GlobalListenerSourceFilterProperty; ``` How the global listener was defined. --- ### type? ```ts optional type?: StringFilterProperty; ``` Job type of the global listener. --- ## Type Alias: GlobalTaskListenerSearchQueryRequest ```ts type GlobalTaskListenerSearchQueryRequest = SearchQueryRequest & object; ``` Global listener search query request. ## Type Declaration ### filter? ```ts optional filter?: GlobalTaskListenerSearchQueryFilterRequest; ``` The global listener search filters. ### sort? ```ts optional sort?: GlobalTaskListenerSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: GlobalTaskListenerSearchQueryResult ```ts type GlobalTaskListenerSearchQueryResult = SearchQueryResponse & object; ``` Global listener search query response. ## Type Declaration ### items ```ts items: GlobalTaskListenerResult[]; ``` The matching global listeners. --- ## Type Alias: GlobalTaskListenerSearchQuerySortRequest ```ts type GlobalTaskListenerSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: "id" | "type" | "afterNonGlobal" | "priority" | "source"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: GroupClientResult ```ts type GroupClientResult = object; ``` ## Properties ### clientId ```ts clientId: ClientId; ``` The ID of the client. --- ## Type Alias: GroupClientSearchQueryRequest ```ts type GroupClientSearchQueryRequest = SearchQueryRequest & object; ``` ## Type Declaration ### sort? ```ts optional sort?: GroupClientSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: GroupClientSearchQuerySortRequest ```ts type GroupClientSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: "clientId"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: GroupClientSearchResult ```ts type GroupClientSearchResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: GroupClientResult[]; ``` The matching client IDs. --- ## Type Alias: GroupCreateRequest ```ts type GroupCreateRequest = object; ``` ## Properties ### description? ```ts optional description?: string; ``` The description of the new group. --- ### groupId ```ts groupId: GroupId; ``` The ID of the new group. --- ### name ```ts name: string; ``` The display name of the new group. --- ## Type Alias: GroupCreateResult ```ts type GroupCreateResult = object; ``` ## Properties ### description ```ts description: string | null; ``` The description of the created group. --- ### groupId ```ts groupId: GroupId; ``` The ID of the created group. --- ### name ```ts name: string; ``` The display name of the created group. --- ## Type Alias: GroupFilter ```ts type GroupFilter = object; ``` Group filter request ## Properties ### groupId? ```ts optional groupId?: StringFilterProperty; ``` The group ID search filters. --- ### name? ```ts optional name?: string; ``` The group name search filters. --- ## Type Alias: GroupId ```ts type GroupId = CamundaKey<"GroupId">; ``` The unique identifier of a group. --- ## Type Alias: GroupMappingRuleSearchResult ```ts type GroupMappingRuleSearchResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: MappingRuleResult[]; ``` The matching mapping rules. --- ## Type Alias: GroupResult ```ts type GroupResult = object; ``` Group search response item. ## Properties ### description ```ts description: string | null; ``` The group description. --- ### groupId ```ts groupId: GroupId; ``` The group ID. --- ### name ```ts name: string; ``` The group name. --- ## Type Alias: GroupRoleSearchResult ```ts type GroupRoleSearchResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: RoleResult[]; ``` The matching roles. --- ## Type Alias: GroupSearchQueryRequest ```ts type GroupSearchQueryRequest = SearchQueryRequest & object; ``` Group search request. ## Type Declaration ### filter? ```ts optional filter?: GroupFilter; ``` The group search filters. ### sort? ```ts optional sort?: GroupSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: GroupSearchQueryResult ```ts type GroupSearchQueryResult = SearchQueryResponse & object; ``` Group search response. ## Type Declaration ### items ```ts items: GroupResult[]; ``` The matching groups. --- ## Type Alias: GroupSearchQuerySortRequest ```ts type GroupSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: "name" | "groupId"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: GroupUpdateRequest ```ts type GroupUpdateRequest = object; ``` ## Properties ### description? ```ts optional description?: string; ``` The new description of the group. --- ### name ```ts name: string; ``` The new name of the group. --- ## Type Alias: GroupUpdateResult ```ts type GroupUpdateResult = object; ``` ## Properties ### description ```ts description: string | null; ``` The description of the group. --- ### groupId ```ts groupId: GroupId; ``` The unique group ID. --- ### name ```ts name: string; ``` The name of the group. --- ## Type Alias: GroupUserResult ```ts type GroupUserResult = object; ``` ## Properties ### username ```ts username: Username; ``` --- ## Type Alias: GroupUserSearchQueryRequest ```ts type GroupUserSearchQueryRequest = SearchQueryRequest & object; ``` ## Type Declaration ### sort? ```ts optional sort?: GroupUserSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: GroupUserSearchQuerySortRequest ```ts type GroupUserSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: "username"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: GroupUserSearchResult ```ts type GroupUserSearchResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: GroupUserResult[]; ``` The matching members. --- ## Type Alias: IncidentErrorTypeEnum ```ts type IncidentErrorTypeEnum = (typeof IncidentErrorTypeEnum)[keyof typeof IncidentErrorTypeEnum]; ``` Incident error type with a defined set of values. --- ## Type Alias: IncidentErrorTypeExactMatch ```ts type IncidentErrorTypeExactMatch = IncidentErrorTypeEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: IncidentErrorTypeFilterProperty ```ts type IncidentErrorTypeFilterProperty = | IncidentErrorTypeExactMatch | AdvancedIncidentErrorTypeFilter; ``` IncidentErrorTypeEnum with full advanced search capabilities. --- ## Type Alias: IncidentFilter ```ts type IncidentFilter = object; ``` Incident search filter. ## Properties ### creationTime? ```ts optional creationTime?: DateTimeFilterProperty; ``` Date of incident creation. --- ### elementId? ```ts optional elementId?: StringFilterProperty; ``` The element ID associated to this incident. --- ### elementInstanceKey? ```ts optional elementInstanceKey?: ElementInstanceKeyFilterProperty; ``` The element instance key associated to this incident. --- ### errorMessage? ```ts optional errorMessage?: StringFilterProperty; ``` The error message of this incident. --- ### errorType? ```ts optional errorType?: IncidentErrorTypeFilterProperty; ``` Incident error type with a defined set of values. --- ### incidentKey? ```ts optional incidentKey?: BasicStringFilterProperty; ``` The assigned key, which acts as a unique identifier for this incident. --- ### jobKey? ```ts optional jobKey?: JobKeyFilterProperty; ``` The job key, if exists, associated with this incident. --- ### processDefinitionId? ```ts optional processDefinitionId?: StringFilterProperty; ``` The process definition ID associated to this incident. --- ### processDefinitionKey? ```ts optional processDefinitionKey?: ProcessDefinitionKeyFilterProperty; ``` The process definition key associated to this incident. --- ### processInstanceKey? ```ts optional processInstanceKey?: ProcessInstanceKeyFilterProperty; ``` The process instance key associated to this incident. --- ### state? ```ts optional state?: IncidentStateFilterProperty; ``` State of this incident with a defined set of values. --- ### tenantId? ```ts optional tenantId?: StringFilterProperty; ``` The tenant ID of the incident. --- ## Type Alias: IncidentKey ```ts type IncidentKey = CamundaKey<"IncidentKey">; ``` System-generated key for a incident. --- ## Type Alias: IncidentProcessInstanceStatisticsByDefinitionFilter ```ts type IncidentProcessInstanceStatisticsByDefinitionFilter = object; ``` Filter for the incident process instance statistics by definition query. ## Properties ### errorHashCode ```ts errorHashCode: number; ``` The error hash code of the incidents to filter the process instance statistics by. --- ## Type Alias: IncidentProcessInstanceStatisticsByDefinitionQuery ```ts type IncidentProcessInstanceStatisticsByDefinitionQuery = object; ``` ## Properties ### filter ```ts filter: IncidentProcessInstanceStatisticsByDefinitionFilter; ``` Filter criteria for the aggregated process instance statistics. --- ### page? ```ts optional page?: OffsetPagination; ``` Pagination parameters for the aggregated process instance statistics. --- ### sort? ```ts optional sort?: IncidentProcessInstanceStatisticsByDefinitionQuerySortRequest[]; ``` Sorting criteria for process instance statistics grouped by process definition. --- ## Type Alias: IncidentProcessInstanceStatisticsByDefinitionQueryResult ```ts type IncidentProcessInstanceStatisticsByDefinitionQueryResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: IncidentProcessInstanceStatisticsByDefinitionResult[]; ``` Statistics of active process instances with incidents, grouped by process definition for the specified error hash code. --- ## Type Alias: IncidentProcessInstanceStatisticsByDefinitionQuerySortRequest ```ts type IncidentProcessInstanceStatisticsByDefinitionQuerySortRequest = object; ``` ## Properties ### field ```ts field: "activeInstancesWithErrorCount" | "processDefinitionKey" | "tenantId"; ``` The aggregated field by which the process instance statistics are sorted. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: IncidentProcessInstanceStatisticsByDefinitionResult ```ts type IncidentProcessInstanceStatisticsByDefinitionResult = object; ``` ## Properties ### activeInstancesWithErrorCount ```ts activeInstancesWithErrorCount: number; ``` The number of active process instances that currently have an incident with the specified error hash code. --- ### processDefinitionId ```ts processDefinitionId: ProcessDefinitionId; ``` --- ### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` --- ### processDefinitionName ```ts processDefinitionName: string; ``` The name of the process definition. --- ### processDefinitionVersion ```ts processDefinitionVersion: number; ``` The version of the process definition. --- ### tenantId ```ts tenantId: TenantId; ``` --- ## Type Alias: IncidentProcessInstanceStatisticsByErrorQuery ```ts type IncidentProcessInstanceStatisticsByErrorQuery = object; ``` ## Properties ### page? ```ts optional page?: OffsetPagination; ``` Pagination parameters for process instance statistics grouped by incident error. --- ### sort? ```ts optional sort?: IncidentProcessInstanceStatisticsByErrorQuerySortRequest[]; ``` Sorting criteria for process instance statistics grouped by incident error. --- ## Type Alias: IncidentProcessInstanceStatisticsByErrorQueryResult ```ts type IncidentProcessInstanceStatisticsByErrorQueryResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: IncidentProcessInstanceStatisticsByErrorResult[]; ``` Statistics of active process instances grouped by incident error. --- ## Type Alias: IncidentProcessInstanceStatisticsByErrorQuerySortRequest ```ts type IncidentProcessInstanceStatisticsByErrorQuerySortRequest = object; ``` ## Properties ### field ```ts field: "errorMessage" | "activeInstancesWithErrorCount"; ``` The field to sort the incident error statistics by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: IncidentProcessInstanceStatisticsByErrorResult ```ts type IncidentProcessInstanceStatisticsByErrorResult = object; ``` ## Properties ### activeInstancesWithErrorCount ```ts activeInstancesWithErrorCount: number; ``` The number of active process instances that currently have an active incident with this error. --- ### errorHashCode ```ts errorHashCode: number; ``` The hash code identifying a specific incident error.. --- ### errorMessage ```ts errorMessage: string; ``` The error message associated with the incident error hash code. --- ## Type Alias: IncidentResolutionRequest ```ts type IncidentResolutionRequest = object; ``` ## Properties ### operationReference? ```ts optional operationReference?: OperationReference; ``` --- ## Type Alias: IncidentResult ```ts type IncidentResult = object; ``` ## Properties ### creationTime ```ts creationTime: string; ``` The creation time of the incident. --- ### elementId ```ts elementId: ElementId; ``` The element ID associated to this incident. --- ### elementInstanceKey ```ts elementInstanceKey: ElementInstanceKey; ``` The element instance key associated to this incident. --- ### errorMessage ```ts errorMessage: string; ``` Error message which describes the error in more detail. --- ### errorType ```ts errorType: IncidentErrorTypeEnum; ``` The type of the incident error. --- ### incidentKey ```ts incidentKey: IncidentKey; ``` The assigned key, which acts as a unique identifier for this incident. --- ### jobKey ```ts jobKey: JobKey | null; ``` The job key, if exists, associated with this incident. --- ### processDefinitionId ```ts processDefinitionId: ProcessDefinitionId; ``` The process definition ID associated to this incident. --- ### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` The process definition key associated to this incident. --- ### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The process instance key associated to this incident. --- ### rootProcessInstanceKey ```ts rootProcessInstanceKey: ProcessInstanceKey | null; ``` The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. --- ### state ```ts state: IncidentStateEnum; ``` The incident state. --- ### tenantId ```ts tenantId: TenantId; ``` The tenant ID of the incident. --- ## Type Alias: IncidentSearchQuery ```ts type IncidentSearchQuery = SearchQueryRequest & object; ``` ## Type Declaration ### filter? ```ts optional filter?: IncidentFilter; ``` The incident search filters. ### sort? ```ts optional sort?: IncidentSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: IncidentSearchQueryResult ```ts type IncidentSearchQueryResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: IncidentResult[]; ``` The matching incidents. --- ## Type Alias: IncidentSearchQuerySortRequest ```ts type IncidentSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: | "incidentKey" | "processDefinitionKey" | "processDefinitionId" | "processInstanceKey" | "errorType" | "elementId" | "elementInstanceKey" | "creationTime" | "state" | "jobKey" | "tenantId"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: IncidentStateEnum ```ts type IncidentStateEnum = (typeof IncidentStateEnum)[keyof typeof IncidentStateEnum]; ``` Incident states with a defined set of values. --- ## Type Alias: IncidentStateExactMatch ```ts type IncidentStateExactMatch = IncidentStateEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: IncidentStateFilterProperty ```ts type IncidentStateFilterProperty = | IncidentStateExactMatch | AdvancedIncidentStateFilter; ``` IncidentStateEnum with full advanced search capabilities. --- ## Type Alias: InferredAncestorKeyInstruction ```ts type InferredAncestorKeyInstruction = object; ``` Instructs the engine to derive the ancestor scope key from the source element's hierarchy. The engine traverses the source element's ancestry to find an instance that matches one of the target element's flow scopes, ensuring the target is activated in the correct scope. ## Properties ### ancestorScopeType ```ts ancestorScopeType: string; ``` The type of ancestor scope instruction. --- ## Type Alias: IntegerFilterProperty ```ts type IntegerFilterProperty = number | AdvancedIntegerFilter; ``` Integer property with advanced search capabilities. --- ## Type Alias: Job # Type Alias: Job\ ```ts type Job = EnrichedActivatedJob & object; ``` ## Type Declaration ### customHeaders ```ts customHeaders: InferOrUnknown; ``` ### variables ```ts variables: InferOrUnknown; ``` ## Type Parameters ### In `In` _extends_ `z.ZodTypeAny` \| `undefined` ### Headers `Headers` _extends_ `z.ZodTypeAny` \| `undefined` --- ## Type Alias: JobActionReceipt ```ts type JobActionReceipt = typeof JobActionReceipt; ``` Unique receipt symbol returned by job action methods. --- ## Type Alias: JobActionReceipt(Type-aliases) ```ts type JobActionReceipt = "JOB_ACTION_RECEIPT"; ``` Unique receipt symbol returned by job action methods. --- ## Type Alias: JobActivationRequest ```ts type JobActivationRequest = object; ``` ## Properties ### fetchVariable? ```ts optional fetchVariable?: string[]; ``` A list of variables to fetch as the job variables; if empty, all visible variables at the time of activation for the scope of the job will be returned. --- ### maxJobsToActivate ```ts maxJobsToActivate: number; ``` The maximum jobs to activate by this request. --- ### requestTimeout? ```ts optional requestTimeout?: number; ``` The request will be completed when at least one job is activated or after the requestTimeout (in ms). If the requestTimeout = 0, a default timeout is used. If the requestTimeout < 0, long polling is disabled and the request is completed immediately, even when no job is activated. --- ### tenantFilter? ```ts optional tenantFilter?: TenantFilterEnum; ``` The tenant filtering strategy - determines whether to use provided tenant IDs or assigned tenant IDs from the authenticated principal's authorized tenants. --- ### tenantIds? ```ts optional tenantIds?: TenantId[]; ``` A list of IDs of tenants for which to activate jobs. --- ### timeout ```ts timeout: number; ``` A job returned after this call will not be activated by another call until the timeout (in ms) has been reached. --- ### type ```ts type: string; ``` The job type, as defined in the BPMN process (e.g. ) --- ### worker? ```ts optional worker?: string; ``` The name of the worker activating the jobs, mostly used for logging purposes. --- ## Type Alias: JobActivationResult ```ts type JobActivationResult = object; ``` The list of activated jobs ## Properties ### jobs ```ts jobs: ActivatedJobResult[]; ``` The activated jobs. --- ## Type Alias: JobChangeset ```ts type JobChangeset = object; ``` JSON object with changed job attribute values. The job cannot be completed or failed with this endpoint, use the complete job or fail job endpoints instead. ## Properties ### retries? ```ts optional retries?: number | null; ``` The new number of retries for the job. --- ### timeout? ```ts optional timeout?: number | null; ``` The new timeout for the job in milliseconds. --- ## Type Alias: JobCompletionRequest ```ts type JobCompletionRequest = object; ``` ## Properties ### result? ```ts optional result?: JobResult; ``` --- ### variables? ```ts optional variables?: | { [key: string]: unknown; } | null; ``` The variables to complete the job with. --- ## Type Alias: JobErrorRequest ```ts type JobErrorRequest = object; ``` ## Properties ### errorCode ```ts errorCode: string; ``` The error code that will be matched with an error catch event. --- ### errorMessage? ```ts optional errorMessage?: string | null; ``` An error message that provides additional context. --- ### variables? ```ts optional variables?: | { [key: string]: unknown; } | null; ``` JSON object that will instantiate the variables at the local scope of the error catch event that catches the thrown error. --- ## Type Alias: JobErrorStatisticsFilter ```ts type JobErrorStatisticsFilter = object; ``` Job error statistics search filter. ## Properties ### errorCode? ```ts optional errorCode?: StringFilterProperty; ``` Optional error code filter with advanced search capabilities. --- ### errorMessage? ```ts optional errorMessage?: StringFilterProperty; ``` Optional error message filter with advanced search capabilities. --- ### from ```ts from: string; ``` Start of the time window to filter metrics. ISO 8601 date-time format. --- ### jobType ```ts jobType: string; ``` Job type to return error metrics for. --- ### to ```ts to: string; ``` End of the time window to filter metrics. ISO 8601 date-time format. --- ## Type Alias: JobErrorStatisticsItem ```ts type JobErrorStatisticsItem = object; ``` Aggregated error metrics for a single error type and message combination. ## Properties ### errorCode ```ts errorCode: string; ``` The error code identifier. --- ### errorMessage ```ts errorMessage: string; ``` The error message. --- ### workers ```ts workers: number; ``` Number of distinct workers that encountered this error. --- ## Type Alias: JobErrorStatisticsQuery ```ts type JobErrorStatisticsQuery = object; ``` Job error statistics query. ## Properties ### filter ```ts filter: JobErrorStatisticsFilter; ``` --- ### page? ```ts optional page?: CursorForwardPagination; ``` Search cursor pagination. --- ## Type Alias: JobErrorStatisticsQueryResult ```ts type JobErrorStatisticsQueryResult = SearchQueryResponse & object; ``` Job error statistics query result. ## Type Declaration ### items ```ts items: JobErrorStatisticsItem[]; ``` The list of per-error statistics items. ### page ```ts page: SearchQueryPageResponse; ``` --- ## Type Alias: JobFailRequest ```ts type JobFailRequest = object; ``` ## Properties ### errorMessage? ```ts optional errorMessage?: string; ``` An optional error message describing why the job failed; if not provided, an empty string is used. --- ### retries? ```ts optional retries?: number; ``` The amount of retries the job should have left --- ### retryBackOff? ```ts optional retryBackOff?: number; ``` An optional retry back off for the failed job. The job will not be retryable before the current time plus the back off time. The default is 0 which means the job is retryable immediately. --- ### variables? ```ts optional variables?: object; ``` JSON object that will instantiate the variables at the local scope of the job's associated task. #### Index Signature ```ts [key: string]: unknown ``` --- ## Type Alias: JobFilter ```ts type JobFilter = object; ``` Job search filter. ## Properties ### creationTime? ```ts optional creationTime?: DateTimeFilterProperty; ``` When the job was created. Field is present for jobs created after 8.9. --- ### deadline? ```ts optional deadline?: DateTimeFilterProperty | null; ``` When the job can next be activated. --- ### deniedReason? ```ts optional deniedReason?: StringFilterProperty; ``` The reason provided by the user task listener for denying the work. --- ### elementId? ```ts optional elementId?: StringFilterProperty; ``` The element ID associated with the job. --- ### elementInstanceKey? ```ts optional elementInstanceKey?: ElementInstanceKeyFilterProperty; ``` The element instance key associated with the job. --- ### endTime? ```ts optional endTime?: DateTimeFilterProperty; ``` When the job ended. --- ### errorCode? ```ts optional errorCode?: StringFilterProperty; ``` The error code provided for the failed job. --- ### errorMessage? ```ts optional errorMessage?: StringFilterProperty; ``` The error message that provides additional context for a failed job. --- ### hasFailedWithRetriesLeft? ```ts optional hasFailedWithRetriesLeft?: boolean; ``` Indicates whether the job has failed with retries left. --- ### isDenied? ```ts optional isDenied?: boolean | null; ``` Indicates whether the user task listener denies the work. --- ### jobKey? ```ts optional jobKey?: JobKeyFilterProperty; ``` The key, a unique identifier for the job. --- ### kind? ```ts optional kind?: JobKindFilterProperty; ``` The kind of the job. --- ### lastUpdateTime? ```ts optional lastUpdateTime?: DateTimeFilterProperty; ``` When the job was last updated. Field is present for jobs created after 8.9. --- ### listenerEventType? ```ts optional listenerEventType?: JobListenerEventTypeFilterProperty; ``` The listener event type of the job. --- ### processDefinitionId? ```ts optional processDefinitionId?: StringFilterProperty; ``` The process definition ID associated with the job. --- ### processDefinitionKey? ```ts optional processDefinitionKey?: ProcessDefinitionKeyFilterProperty; ``` The process definition key associated with the job. --- ### processInstanceKey? ```ts optional processInstanceKey?: ProcessInstanceKeyFilterProperty; ``` The process instance key associated with the job. --- ### retries? ```ts optional retries?: IntegerFilterProperty; ``` The number of retries left. --- ### state? ```ts optional state?: JobStateFilterProperty; ``` The state of the job. --- ### tenantId? ```ts optional tenantId?: StringFilterProperty; ``` The tenant ID. --- ### type? ```ts optional type?: StringFilterProperty; ``` The type of the job. --- ### worker? ```ts optional worker?: StringFilterProperty; ``` The name of the worker for this job. --- ## Type Alias: JobKey ```ts type JobKey = CamundaKey<"JobKey">; ``` System-generated key for a job. --- ## Type Alias: JobKeyExactMatch ```ts type JobKeyExactMatch = JobKey; ``` Exact match Matches the value exactly. --- ## Type Alias: JobKeyFilterProperty ```ts type JobKeyFilterProperty = JobKeyExactMatch | AdvancedJobKeyFilter; ``` JobKey property with full advanced search capabilities. --- ## Type Alias: JobKindEnum ```ts type JobKindEnum = (typeof JobKindEnum)[keyof typeof JobKindEnum]; ``` The job kind. --- ## Type Alias: JobKindExactMatch ```ts type JobKindExactMatch = JobKindEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: JobKindFilterProperty ```ts type JobKindFilterProperty = JobKindExactMatch | AdvancedJobKindFilter; ``` JobKindEnum property with full advanced search capabilities. --- ## Type Alias: JobListenerEventTypeEnum ```ts type JobListenerEventTypeEnum = (typeof JobListenerEventTypeEnum)[keyof typeof JobListenerEventTypeEnum]; ``` The listener event type of the job. --- ## Type Alias: JobListenerEventTypeExactMatch ```ts type JobListenerEventTypeExactMatch = JobListenerEventTypeEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: JobListenerEventTypeFilterProperty ```ts type JobListenerEventTypeFilterProperty = | JobListenerEventTypeExactMatch | AdvancedJobListenerEventTypeFilter; ``` JobListenerEventTypeEnum property with full advanced search capabilities. --- ## Type Alias: JobMetricsConfigurationResponse ```ts type JobMetricsConfigurationResponse = object; ``` Configuration for job metrics collection and export. ## Properties ### enabled ```ts enabled: boolean; ``` Whether job metrics export is enabled. --- ### exportInterval ```ts exportInterval: string; ``` The interval at which job metrics are exported, as an ISO 8601 duration. --- ### maxJobTypeLength ```ts maxJobTypeLength: number; ``` The maximum length of the job type used in job metrics labels. --- ### maxTenantIdLength ```ts maxTenantIdLength: number; ``` The maximum length of the tenant ID used in job metrics labels. --- ### maxUniqueKeys ```ts maxUniqueKeys: number; ``` The maximum number of unique metric keys tracked for job metrics. --- ### maxWorkerNameLength ```ts maxWorkerNameLength: number; ``` The maximum length of the worker name used in job metrics labels. --- ## Type Alias: JobResult ```ts type JobResult = | (object & JobResultUserTask) | (object & JobResultAdHocSubProcess); ``` The result of the completed job as determined by the worker. --- ## Type Alias: JobResultActivateElement ```ts type JobResultActivateElement = object; ``` Instruction to activate a single BPMN element within an ad‑hoc sub‑process, optionally providing variables scoped to that element. ## Properties ### elementId? ```ts optional elementId?: ElementId; ``` The element ID to activate. --- ### variables? ```ts optional variables?: | { [key: string]: unknown; } | null; ``` Variables for the element. --- ## Type Alias: JobResultAdHocSubProcess ```ts type JobResultAdHocSubProcess = { activateElements?: JobResultActivateElement[]; isCancelRemainingInstances?: boolean; isCompletionConditionFulfilled?: boolean; type?: string; } | null; ``` Job result details for an ad‑hoc sub‑process, including elements to activate and flags indicating completion or cancellation behavior. ## Union Members ### Type Literal ```ts { activateElements?: JobResultActivateElement[]; isCancelRemainingInstances?: boolean; isCompletionConditionFulfilled?: boolean; type?: string; } ``` #### activateElements? ```ts optional activateElements?: JobResultActivateElement[]; ``` Indicates which elements need to be activated in the ad-hoc subprocess. #### isCancelRemainingInstances? ```ts optional isCancelRemainingInstances?: boolean; ``` Indicates whether the remaining instances of the ad-hoc subprocess should be canceled. #### isCompletionConditionFulfilled? ```ts optional isCompletionConditionFulfilled?: boolean; ``` Indicates whether the completion condition of the ad-hoc subprocess is fulfilled. #### type? ```ts optional type?: string; ``` Used to distinguish between different types of job results. --- `null` --- ## Type Alias: JobResultCorrections ```ts type JobResultCorrections = { assignee?: string | null; candidateGroups?: string[] | null; candidateUsers?: string[] | null; dueDate?: string | null; followUpDate?: string | null; priority?: number | null; } | null; ``` JSON object with attributes that were corrected by the worker. The following attributes can be corrected, additional attributes will be ignored: - `assignee` - clear by providing an empty String - `dueDate` - clear by providing an empty String - `followUpDate` - clear by providing an empty String - `candidateGroups` - clear by providing an empty list - `candidateUsers` - clear by providing an empty list - `priority` - minimum 0, maximum 100, default 50 Providing any of those attributes with a `null` value or omitting it preserves the persisted attribute's value. ## Union Members ### Type Literal ```ts { assignee?: string | null; candidateGroups?: string[] | null; candidateUsers?: string[] | null; dueDate?: string | null; followUpDate?: string | null; priority?: number | null; } ``` #### assignee? ```ts optional assignee?: string | null; ``` Assignee of the task. #### candidateGroups? ```ts optional candidateGroups?: string[] | null; ``` The list of candidate groups of the task. #### candidateUsers? ```ts optional candidateUsers?: string[] | null; ``` The list of candidate users of the task. #### dueDate? ```ts optional dueDate?: string | null; ``` The due date of the task. #### followUpDate? ```ts optional followUpDate?: string | null; ``` The follow-up date of the task. #### priority? ```ts optional priority?: number | null; ``` The priority of the task. --- `null` --- ## Type Alias: JobResultUserTask ```ts type JobResultUserTask = { corrections?: JobResultCorrections; denied?: boolean | null; deniedReason?: string | null; type?: string; } | null; ``` Job result details for a user task completion, optionally including a denial reason and corrected task properties. ## Union Members ### Type Literal ```ts { corrections?: JobResultCorrections; denied?: boolean | null; deniedReason?: string | null; type?: string; } ``` #### corrections? ```ts optional corrections?: JobResultCorrections; ``` #### denied? ```ts optional denied?: boolean | null; ``` Indicates whether the worker denies the work, i.e. explicitly doesn't approve it. For example, a user task listener can deny the completion of a task by setting this flag to true. In this example, the completion of a task is represented by a job that the worker can complete as denied. As a result, the completion request is rejected and the task remains active. Defaults to false. #### deniedReason? ```ts optional deniedReason?: string | null; ``` The reason provided by the user task listener for denying the work. #### type? ```ts optional type?: string; ``` Used to distinguish between different types of job results. --- `null` --- ## Type Alias: JobSearchQuery ```ts type JobSearchQuery = SearchQueryRequest & object; ``` Job search request. ## Type Declaration ### filter? ```ts optional filter?: JobFilter; ``` The job search filters. ### sort? ```ts optional sort?: JobSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: JobSearchQueryResult ```ts type JobSearchQueryResult = SearchQueryResponse & object; ``` Job search response. ## Type Declaration ### items ```ts items: JobSearchResult[]; ``` The matching jobs. --- ## Type Alias: JobSearchQuerySortRequest ```ts type JobSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: | "deadline" | "deniedReason" | "elementId" | "elementInstanceKey" | "endTime" | "errorCode" | "errorMessage" | "hasFailedWithRetriesLeft" | "isDenied" | "jobKey" | "kind" | "listenerEventType" | "processDefinitionId" | "processDefinitionKey" | "processInstanceKey" | "retries" | "state" | "tenantId" | "type" | "worker"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: JobSearchResult ```ts type JobSearchResult = object; ``` ## Properties ### creationTime ```ts creationTime: string | null; ``` When the job was created. Field is present for jobs created after 8.9. --- ### customHeaders ```ts customHeaders: object; ``` A set of custom headers defined during modelling. #### Index Signature ```ts [key: string]: string ``` --- ### deadline ```ts deadline: string | null; ``` If the job has been activated, when it will next be available to be activated. --- ### deniedReason ```ts deniedReason: string | null; ``` The reason provided by the user task listener for denying the work. --- ### elementId ```ts elementId: ElementId | null; ``` The element ID associated with the job. May be missing on job failure. --- ### elementInstanceKey ```ts elementInstanceKey: ElementInstanceKey; ``` The element instance key associated with the job. --- ### endTime ```ts endTime: string | null; ``` End date of the job. This is `null` if the job is not in an end state yet. --- ### errorCode ```ts errorCode: string | null; ``` The error code provided for a failed job. --- ### errorMessage ```ts errorMessage: string | null; ``` The error message that provides additional context for a failed job. --- ### hasFailedWithRetriesLeft ```ts hasFailedWithRetriesLeft: boolean; ``` Indicates whether the job has failed with retries left. --- ### isDenied ```ts isDenied: boolean | null; ``` Indicates whether the user task listener denies the work. --- ### jobKey ```ts jobKey: JobKey; ``` The key, a unique identifier for the job. --- ### kind ```ts kind: JobKindEnum; ``` --- ### lastUpdateTime ```ts lastUpdateTime: string | null; ``` When the job was last updated. Field is present for jobs created after 8.9. --- ### listenerEventType ```ts listenerEventType: JobListenerEventTypeEnum; ``` --- ### processDefinitionId ```ts processDefinitionId: ProcessDefinitionId; ``` The process definition ID associated with the job. --- ### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` The process definition key associated with the job. --- ### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The process instance key associated with the job. --- ### retries ```ts retries: number; ``` The amount of retries left to this job. --- ### rootProcessInstanceKey ```ts rootProcessInstanceKey: ProcessInstanceKey | null; ``` The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. --- ### state ```ts state: JobStateEnum; ``` --- ### tenantId ```ts tenantId: TenantId; ``` --- ### type ```ts type: string; ``` The type of the job. --- ### worker ```ts worker: string; ``` The name of the worker of this job. --- ## Type Alias: JobStateEnum ```ts type JobStateEnum = (typeof JobStateEnum)[keyof typeof JobStateEnum]; ``` The state of the job. --- ## Type Alias: JobStateExactMatch ```ts type JobStateExactMatch = JobStateEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: JobStateFilterProperty ```ts type JobStateFilterProperty = JobStateExactMatch | AdvancedJobStateFilter; ``` JobStateEnum property with full advanced search capabilities. --- ## Type Alias: JobTimeSeriesStatisticsFilter ```ts type JobTimeSeriesStatisticsFilter = object; ``` Job time-series statistics search filter. ## Properties ### from ```ts from: string; ``` Start of the time window to filter metrics. ISO 8601 date-time format. --- ### jobType ```ts jobType: string; ``` Job type to return time-series metrics for. --- ### resolution? ```ts optional resolution?: string; ``` Time bucket resolution as an ISO 8601 duration (for example `PT1M` for 1 minute, `PT1H` for 1 hour). If omitted, the server chooses a sensible default. --- ### to ```ts to: string; ``` End of the time window to filter metrics. ISO 8601 date-time format. --- ## Type Alias: JobTimeSeriesStatisticsItem ```ts type JobTimeSeriesStatisticsItem = object; ``` Aggregated job metrics for a single time bucket. ## Properties ### completed ```ts completed: StatusMetric; ``` --- ### created ```ts created: StatusMetric; ``` --- ### failed ```ts failed: StatusMetric; ``` --- ### time ```ts time: string; ``` ISO 8601 timestamp representing the start of this time bucket. --- ## Type Alias: JobTimeSeriesStatisticsQuery ```ts type JobTimeSeriesStatisticsQuery = object; ``` Job time-series statistics query. ## Properties ### filter ```ts filter: JobTimeSeriesStatisticsFilter; ``` --- ### page? ```ts optional page?: CursorForwardPagination; ``` Search cursor pagination. --- ## Type Alias: JobTimeSeriesStatisticsQueryResult ```ts type JobTimeSeriesStatisticsQueryResult = SearchQueryResponse & object; ``` Job time-series statistics query result. ## Type Declaration ### items ```ts items: JobTimeSeriesStatisticsItem[]; ``` The list of time-bucketed statistics items, ordered ascending by time. ### page ```ts page: SearchQueryPageResponse; ``` --- ## Type Alias: JobTypeStatisticsFilter ```ts type JobTypeStatisticsFilter = object; ``` Job type statistics search filter. ## Properties ### from ```ts from: string; ``` Start of the time window to filter metrics. ISO 8601 date-time format. --- ### jobType? ```ts optional jobType?: StringFilterProperty; ``` Optional job type filter with advanced search capabilities. Supports exact match, pattern matching, and other operators. --- ### to ```ts to: string; ``` End of the time window to filter metrics. ISO 8601 date-time format. --- ## Type Alias: JobTypeStatisticsItem ```ts type JobTypeStatisticsItem = object; ``` Statistics for a single job type. ## Properties ### completed ```ts completed: StatusMetric; ``` --- ### created ```ts created: StatusMetric; ``` --- ### failed ```ts failed: StatusMetric; ``` --- ### jobType ```ts jobType: string; ``` The job type identifier. --- ### workers ```ts workers: number; ``` Number of distinct workers observed for this job type. --- ## Type Alias: JobTypeStatisticsQuery ```ts type JobTypeStatisticsQuery = object; ``` Job type statistics query. ## Properties ### filter? ```ts optional filter?: JobTypeStatisticsFilter; ``` --- ### page? ```ts optional page?: CursorForwardPagination; ``` Search cursor pagination. --- ## Type Alias: JobTypeStatisticsQueryResult ```ts type JobTypeStatisticsQueryResult = SearchQueryResponse & object; ``` Job type statistics query result. ## Type Declaration ### items ```ts items: JobTypeStatisticsItem[]; ``` The list of job type statistics items. ### page ```ts page: SearchQueryPageResponse; ``` --- ## Type Alias: JobUpdateRequest ```ts type JobUpdateRequest = object; ``` ## Properties ### changeset ```ts changeset: JobChangeset; ``` --- ### operationReference? ```ts optional operationReference?: OperationReference; ``` --- ## Type Alias: JobWorkerStatisticsFilter ```ts type JobWorkerStatisticsFilter = object; ``` Job worker statistics search filter. ## Properties ### from ```ts from: string; ``` Start of the time window to filter metrics. ISO 8601 date-time format. --- ### jobType ```ts jobType: string; ``` Job type to return worker metrics for. --- ### to ```ts to: string; ``` End of the time window to filter metrics. ISO 8601 date-time format. --- ## Type Alias: JobWorkerStatisticsItem ```ts type JobWorkerStatisticsItem = object; ``` Statistics for a single worker within a job type. ## Properties ### completed ```ts completed: StatusMetric; ``` --- ### created ```ts created: StatusMetric; ``` --- ### failed ```ts failed: StatusMetric; ``` --- ### worker ```ts worker: string; ``` The name of the worker activating the jobs, mostly used for logging purposes. --- ## Type Alias: JobWorkerStatisticsQuery ```ts type JobWorkerStatisticsQuery = object; ``` Job worker statistics query. ## Properties ### filter ```ts filter: JobWorkerStatisticsFilter; ``` --- ### page? ```ts optional page?: CursorForwardPagination; ``` Search cursor pagination. --- ## Type Alias: JobWorkerStatisticsQueryResult ```ts type JobWorkerStatisticsQueryResult = SearchQueryResponse & object; ``` Job worker statistics query result. ## Type Declaration ### items ```ts items: JobWorkerStatisticsItem[]; ``` The list of per-worker statistics items. ### page ```ts page: SearchQueryPageResponse; ``` --- ## Type Alias: LicenseResponse ```ts type LicenseResponse = object; ``` The response of a license request. ## Properties ### expiresAt ```ts expiresAt: string | null; ``` The date when the Camunda license expires --- ### isCommercial ```ts isCommercial: boolean; ``` Will be false when a license contains a non-commerical=true property --- ### licenseType ```ts licenseType: string; ``` Will return the license type property of the Camunda license --- ### validLicense ```ts validLicense: boolean; ``` True if the Camunda license is valid, false if otherwise --- ## Type Alias: LikeFilter ```ts type LikeFilter = string; ``` Checks if the property matches the provided like value. Supported wildcard characters are: - `*`: matches zero, one, or multiple characters. - `?`: matches one, single character. Wildcard characters can be escaped with backslash, for instance: `\*`. --- ## Type Alias: LimitPagination ```ts type LimitPagination = object; ``` Limit-based pagination ## Properties ### limit? ```ts optional limit?: number; ``` The maximum number of items to return in one request. --- ## Type Alias: LongKey ```ts type LongKey = string; ``` Zeebe Engine resource key (Java long serialized as string) --- ## Type Alias: Loose # Type Alias: Loose\ ```ts type Loose = IsBrandedKey extends true ? string : T extends CancelablePromise ? CancelablePromise : T extends Promise ? Promise : T extends infer U[] ? Loose[] : T extends ReadonlyArray ? ReadonlyArray : T extends (...a) => infer R ? (...a) => Loose : T extends object ? { [K in keyof T]: Loose } : T; ``` ## Type Parameters ### T `T` --- ## Type Alias: MappingRuleCreateRequest ```ts type MappingRuleCreateRequest = MappingRuleCreateUpdateRequest & object; ``` ## Type Declaration ### mappingRuleId ```ts mappingRuleId: MappingRuleId; ``` The unique ID of the mapping rule. --- ## Type Alias: MappingRuleCreateResult ```ts type MappingRuleCreateResult = MappingRuleCreateUpdateResult; ``` --- ## Type Alias: MappingRuleCreateUpdateRequest ```ts type MappingRuleCreateUpdateRequest = object; ``` ## Properties ### claimName ```ts claimName: string; ``` The name of the claim to map. --- ### claimValue ```ts claimValue: string; ``` The value of the claim to map. --- ### name ```ts name: string; ``` The name of the mapping rule. --- ## Type Alias: MappingRuleCreateUpdateResult ```ts type MappingRuleCreateUpdateResult = object; ``` ## Properties ### claimName ```ts claimName: string; ``` The name of the claim to map. --- ### claimValue ```ts claimValue: string; ``` The value of the claim to map. --- ### mappingRuleId ```ts mappingRuleId: MappingRuleId; ``` The unique ID of the mapping rule. --- ### name ```ts name: string; ``` The name of the mapping rule. --- ## Type Alias: MappingRuleFilter ```ts type MappingRuleFilter = object; ``` Mapping rule search filter. ## Properties ### claimName? ```ts optional claimName?: string; ``` The claim name to match against a token. --- ### claimValue? ```ts optional claimValue?: string; ``` The value of the claim to match. --- ### mappingRuleId? ```ts optional mappingRuleId?: MappingRuleId; ``` The ID of the mapping rule. --- ### name? ```ts optional name?: string; ``` The name of the mapping rule. --- ## Type Alias: MappingRuleId ```ts type MappingRuleId = CamundaKey<"MappingRuleId">; ``` The unique identifier of a mapping rule. --- ## Type Alias: MappingRuleResult ```ts type MappingRuleResult = object; ``` ## Properties ### claimName ```ts claimName: string; ``` The name of the claim to map. --- ### claimValue ```ts claimValue: string; ``` The value of the claim to map. --- ### mappingRuleId ```ts mappingRuleId: MappingRuleId; ``` The ID of the mapping rule. --- ### name ```ts name: string; ``` The name of the mapping rule. --- ## Type Alias: MappingRuleSearchQueryRequest ```ts type MappingRuleSearchQueryRequest = SearchQueryRequest & object; ``` ## Type Declaration ### filter? ```ts optional filter?: MappingRuleFilter; ``` The mapping rule search filters. ### sort? ```ts optional sort?: MappingRuleSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: MappingRuleSearchQueryResult ```ts type MappingRuleSearchQueryResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: MappingRuleResult[]; ``` The matching mapping rules. --- ## Type Alias: MappingRuleSearchQuerySortRequest ```ts type MappingRuleSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: "mappingRuleId" | "claimName" | "claimValue" | "name"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: MappingRuleUpdateRequest ```ts type MappingRuleUpdateRequest = MappingRuleCreateUpdateRequest; ``` --- ## Type Alias: MappingRuleUpdateResult ```ts type MappingRuleUpdateResult = MappingRuleCreateUpdateResult; ``` --- ## Type Alias: MatchedDecisionRuleItem ```ts type MatchedDecisionRuleItem = object; ``` A decision rule that matched within this decision evaluation. ## Properties ### evaluatedOutputs ```ts evaluatedOutputs: EvaluatedDecisionOutputItem[]; ``` The evaluated decision outputs. --- ### ruleId ```ts ruleId: string; ``` The ID of the matched rule. --- ### ruleIndex ```ts ruleIndex: number; ``` The index of the matched rule. --- ## Type Alias: MessageCorrelationRequest ```ts type MessageCorrelationRequest = object; ``` ## Properties ### correlationKey? ```ts optional correlationKey?: string; ``` The correlation key of the message. --- ### name ```ts name: string; ``` The message name as defined in the BPMN process --- ### tenantId? ```ts optional tenantId?: TenantId; ``` the tenant for which the message is published --- ### variables? ```ts optional variables?: object; ``` The message variables as JSON document #### Index Signature ```ts [key: string]: unknown ``` --- ## Type Alias: MessageCorrelationResult ```ts type MessageCorrelationResult = object; ``` The message key of the correlated message, as well as the first process instance key it correlated with. ## Properties ### messageKey ```ts messageKey: MessageKey; ``` The key of the correlated message. --- ### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The key of the first process instance the message correlated with --- ### tenantId ```ts tenantId: TenantId; ``` The tenant ID of the correlated message --- ## Type Alias: MessageKey ```ts type MessageKey = CamundaKey<"MessageKey">; ``` System-generated key for an message. --- ## Type Alias: MessagePublicationRequest ```ts type MessagePublicationRequest = object; ``` ## Properties ### correlationKey? ```ts optional correlationKey?: string; ``` The correlation key of the message. --- ### messageId? ```ts optional messageId?: string; ``` The unique ID of the message. This is used to ensure only one message with the given ID will be published during the lifetime of the message (if `timeToLive` is set). --- ### name ```ts name: string; ``` The name of the message. --- ### tenantId? ```ts optional tenantId?: TenantId; ``` The tenant of the message sender. --- ### timeToLive? ```ts optional timeToLive?: number; ``` Timespan (in ms) to buffer the message on the broker. --- ### variables? ```ts optional variables?: object; ``` The message variables as JSON document. #### Index Signature ```ts [key: string]: unknown ``` --- ## Type Alias: MessagePublicationResult ```ts type MessagePublicationResult = object; ``` The message key of the published message. ## Properties ### messageKey ```ts messageKey: MessageKey; ``` The key of the published message. --- ### tenantId ```ts tenantId: TenantId; ``` The tenant ID of the message. --- ## Type Alias: MessageSubscriptionFilter ```ts type MessageSubscriptionFilter = object; ``` Message subscription search filter. ## Properties ### correlationKey? ```ts optional correlationKey?: StringFilterProperty; ``` The correlation key of the message subscription. --- ### elementId? ```ts optional elementId?: StringFilterProperty; ``` The element ID associated with this message subscription. --- ### elementInstanceKey? ```ts optional elementInstanceKey?: ElementInstanceKeyFilterProperty; ``` The element instance key associated with this message subscription. --- ### inboundConnectorType? ```ts optional inboundConnectorType?: StringFilterProperty; ``` Filter by inbound connector type extracted from the `inbound.type` zeebe:property. --- ### lastUpdatedDate? ```ts optional lastUpdatedDate?: DateTimeFilterProperty; ``` The last updated date of the message subscription. --- ### messageName? ```ts optional messageName?: StringFilterProperty; ``` The name of the message associated with the message subscription. --- ### messageSubscriptionKey? ```ts optional messageSubscriptionKey?: MessageSubscriptionKeyFilterProperty; ``` The message subscription key associated with this message subscription. --- ### messageSubscriptionState? ```ts optional messageSubscriptionState?: MessageSubscriptionStateFilterProperty; ``` The message subscription state. --- ### messageSubscriptionType? ```ts optional messageSubscriptionType?: MessageSubscriptionTypeFilterProperty; ``` The type of message subscription to filter by. When omitted, both `START_EVENT` and `PROCESS_EVENT` are returned. Only available for data created with Camunda 8.10 or later. --- ### processDefinitionId? ```ts optional processDefinitionId?: StringFilterProperty; ``` The process definition ID associated with this message subscription. --- ### processDefinitionKey? ```ts optional processDefinitionKey?: ProcessDefinitionKeyFilterProperty; ``` The process definition key associated with this correlated message subscription. This only works for data created with 8.9 and later. --- ### processDefinitionName? ```ts optional processDefinitionName?: StringFilterProperty; ``` The name of the process definition associated with this message subscription. --- ### processDefinitionVersion? ```ts optional processDefinitionVersion?: IntegerFilterProperty; ``` The version of the process definition associated with this message subscription. --- ### processInstanceKey? ```ts optional processInstanceKey?: ProcessInstanceKeyFilterProperty; ``` The process instance key associated with this message subscription. --- ### tenantId? ```ts optional tenantId?: StringFilterProperty; ``` The unique external tenant ID. --- ### toolName? ```ts optional toolName?: StringFilterProperty; ``` Filter by tool name extracted from the `io.camunda.tool:name` zeebe:property. --- ## Type Alias: MessageSubscriptionKey ```ts type MessageSubscriptionKey = CamundaKey<"MessageSubscriptionKey">; ``` System-generated key for a message subscription. --- ## Type Alias: MessageSubscriptionKeyExactMatch ```ts type MessageSubscriptionKeyExactMatch = MessageSubscriptionKey; ``` Exact match Matches the value exactly. --- ## Type Alias: MessageSubscriptionKeyFilterProperty ```ts type MessageSubscriptionKeyFilterProperty = | MessageSubscriptionKeyExactMatch | AdvancedMessageSubscriptionKeyFilter; ``` MessageSubscriptionKey property with full advanced search capabilities. --- ## Type Alias: MessageSubscriptionResult ```ts type MessageSubscriptionResult = object; ``` ## Properties ### correlationKey ```ts correlationKey: string | null; ``` The correlation key of the message subscription. --- ### elementId ```ts elementId: ElementId; ``` The element ID associated with this message subscription. --- ### elementInstanceKey ```ts elementInstanceKey: ElementInstanceKey | null; ``` The element instance key associated with this message subscription. Only populated for intermediate event entities. --- ### inboundConnectorType ```ts inboundConnectorType: string | null; ``` Inbound connector type extracted from the `inbound.type` zeebe:property. Null when the property is absent. --- ### lastUpdatedDate ```ts lastUpdatedDate: string; ``` The last updated date of the message subscription. --- ### messageName ```ts messageName: string; ``` The name of the message associated with the message subscription. --- ### messageSubscriptionKey ```ts messageSubscriptionKey: MessageSubscriptionKey; ``` The message subscription key associated with this message subscription. --- ### messageSubscriptionState ```ts messageSubscriptionState: MessageSubscriptionStateEnum; ``` --- ### messageSubscriptionType ```ts messageSubscriptionType: MessageSubscriptionTypeEnum; ``` --- ### processDefinitionId ```ts processDefinitionId: ProcessDefinitionId; ``` The process definition ID associated with this message subscription. --- ### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey | null; ``` The process definition key associated with this message subscription. --- ### processDefinitionName ```ts processDefinitionName: string | null; ``` The name of the process definition associated with this message subscription. --- ### processDefinitionVersion ```ts processDefinitionVersion: number | null; ``` The version of the process definition associated with this message subscription. --- ### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey | null; ``` The process instance key associated with this message subscription. Only populated for intermediate event entities. --- ### rootProcessInstanceKey ```ts rootProcessInstanceKey: ProcessInstanceKey | null; ``` The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. --- ### tenantId ```ts tenantId: TenantId; ``` --- ### toolName ```ts toolName: string | null; ``` Tool name extracted from the `io.camunda.tool:name` zeebe:property. Null when the property is absent. --- ### toolProperties ```ts toolProperties: object; ``` The subset of `zeebe:properties` extension properties whose keys start with the `io.camunda.tool:` prefix, extracted from the BPMN element associated with this subscription. Empty object when no matching properties are defined. #### Index Signature ```ts [key: string]: string ``` --- ## Type Alias: MessageSubscriptionSearchQuery ```ts type MessageSubscriptionSearchQuery = SearchQueryRequest & object; ``` ## Type Declaration ### filter? ```ts optional filter?: MessageSubscriptionFilter; ``` The incident search filters. ### sort? ```ts optional sort?: MessageSubscriptionSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: MessageSubscriptionSearchQueryResult ```ts type MessageSubscriptionSearchQueryResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: MessageSubscriptionResult[]; ``` The matching message subscriptions. --- ## Type Alias: MessageSubscriptionSearchQuerySortRequest ```ts type MessageSubscriptionSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: | "messageSubscriptionKey" | "processDefinitionId" | "processDefinitionName" | "processDefinitionVersion" | "processInstanceKey" | "elementId" | "elementInstanceKey" | "messageSubscriptionState" | "messageSubscriptionType" | "lastUpdatedDate" | "messageName" | "correlationKey" | "tenantId" | "toolName" | "inboundConnectorType"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: MessageSubscriptionStateEnum ```ts type MessageSubscriptionStateEnum = (typeof MessageSubscriptionStateEnum)[keyof typeof MessageSubscriptionStateEnum]; ``` The state of message subscription. --- ## Type Alias: MessageSubscriptionStateExactMatch ```ts type MessageSubscriptionStateExactMatch = MessageSubscriptionStateEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: MessageSubscriptionStateFilterProperty ```ts type MessageSubscriptionStateFilterProperty = | MessageSubscriptionStateExactMatch | AdvancedMessageSubscriptionStateFilter; ``` MessageSubscriptionStateEnum with full advanced search capabilities. --- ## Type Alias: MessageSubscriptionTypeEnum ```ts type MessageSubscriptionTypeEnum = (typeof MessageSubscriptionTypeEnum)[keyof typeof MessageSubscriptionTypeEnum]; ``` The type of message subscription. `START_EVENT` is definition-scoped (process start events). Always has a value; only captured from Camunda 8.10 onwards. `PROCESS_EVENT` is instance-scoped (intermediate catch events). Pre-8.10 entries have no value stored; the API returns `PROCESS_EVENT` as a default for those entries. --- ## Type Alias: MessageSubscriptionTypeExactMatch ```ts type MessageSubscriptionTypeExactMatch = MessageSubscriptionTypeEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: MessageSubscriptionTypeFilterProperty ```ts type MessageSubscriptionTypeFilterProperty = | MessageSubscriptionTypeExactMatch | AdvancedMessageSubscriptionTypeFilter; ``` MessageSubscriptionTypeEnum with full advanced search capabilities. --- ## Type Alias: MigrateProcessInstanceData ```ts type MigrateProcessInstanceData = object; ``` ## Properties ### body ```ts body: ProcessInstanceMigrationInstruction; ``` --- ### path ```ts path: object; ``` #### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The key of the process instance that should be migrated. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-instances/{processInstanceKey}/migration"; ``` --- ## Type Alias: MigrateProcessInstanceError ```ts type MigrateProcessInstanceError = MigrateProcessInstanceErrors[keyof MigrateProcessInstanceErrors]; ``` --- ## Type Alias: MigrateProcessInstanceErrors ```ts type MigrateProcessInstanceErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 404 ```ts 404: ProblemDetail; ``` The process instance is not found. --- ### 409 ```ts 409: ProblemDetail; ``` The process instance migration failed. More details are provided in the response body. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: MigrateProcessInstanceMappingInstruction ```ts type MigrateProcessInstanceMappingInstruction = object; ``` The mapping instructions describe how to map elements from the source process definition to the target process definition. ## Properties ### sourceElementId ```ts sourceElementId: ElementId; ``` The element id to migrate from. --- ### targetElementId ```ts targetElementId: ElementId; ``` The element id to migrate into. --- ## Type Alias: MigrateProcessInstanceResponse ```ts type MigrateProcessInstanceResponse = MigrateProcessInstanceResponses[keyof MigrateProcessInstanceResponses]; ``` --- ## Type Alias: MigrateProcessInstanceResponses ```ts type MigrateProcessInstanceResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The process instance is migrated. --- ## Type Alias: MigrateProcessInstancesBatchOperationData ```ts type MigrateProcessInstancesBatchOperationData = object; ``` ## Properties ### body ```ts body: ProcessInstanceMigrationBatchOperationRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-instances/migration"; ``` --- ## Type Alias: MigrateProcessInstancesBatchOperationError ```ts type MigrateProcessInstancesBatchOperationError = MigrateProcessInstancesBatchOperationErrors[keyof MigrateProcessInstancesBatchOperationErrors]; ``` --- ## Type Alias: MigrateProcessInstancesBatchOperationErrors ```ts type MigrateProcessInstancesBatchOperationErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The process instance batch operation failed. More details are provided in the response body. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: MigrateProcessInstancesBatchOperationResponse ```ts type MigrateProcessInstancesBatchOperationResponse = MigrateProcessInstancesBatchOperationResponses[keyof MigrateProcessInstancesBatchOperationResponses]; ``` --- ## Type Alias: MigrateProcessInstancesBatchOperationResponses ```ts type MigrateProcessInstancesBatchOperationResponses = object; ``` ## Properties ### 200 ```ts 200: BatchOperationCreatedResult; ``` The batch operation request was created. --- ## Type Alias: ModifyProcessInstanceData ```ts type ModifyProcessInstanceData = object; ``` ## Properties ### body ```ts body: ProcessInstanceModificationInstruction; ``` --- ### path ```ts path: object; ``` #### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The key of the process instance that should be modified. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-instances/{processInstanceKey}/modification"; ``` --- ## Type Alias: ModifyProcessInstanceError ```ts type ModifyProcessInstanceError = ModifyProcessInstanceErrors[keyof ModifyProcessInstanceErrors]; ``` --- ## Type Alias: ModifyProcessInstanceErrors ```ts type ModifyProcessInstanceErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 404 ```ts 404: ProblemDetail; ``` The process instance is not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: ModifyProcessInstanceResponse ```ts type ModifyProcessInstanceResponse = ModifyProcessInstanceResponses[keyof ModifyProcessInstanceResponses]; ``` --- ## Type Alias: ModifyProcessInstanceResponses ```ts type ModifyProcessInstanceResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The process instance is modified. --- ## Type Alias: ModifyProcessInstanceVariableInstruction ```ts type ModifyProcessInstanceVariableInstruction = object; ``` Instruction describing which variables to create or update. ## Properties ### scopeId? ```ts optional scopeId?: string; ``` The id of the element in which scope the variables should be created. Leave empty to create the variables in the global scope of the process instance. --- ### variables ```ts variables: object; ``` JSON document that will instantiate the variables at the scope defined by the scopeId. It must be a JSON object, as variables will be mapped in a key-value fashion. #### Index Signature ```ts [key: string]: unknown ``` --- ## Type Alias: ModifyProcessInstancesBatchOperationData ```ts type ModifyProcessInstancesBatchOperationData = object; ``` ## Properties ### body ```ts body: ProcessInstanceModificationBatchOperationRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-instances/modification"; ``` --- ## Type Alias: ModifyProcessInstancesBatchOperationError ```ts type ModifyProcessInstancesBatchOperationError = ModifyProcessInstancesBatchOperationErrors[keyof ModifyProcessInstancesBatchOperationErrors]; ``` --- ## Type Alias: ModifyProcessInstancesBatchOperationErrors ```ts type ModifyProcessInstancesBatchOperationErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The process instance batch operation failed. More details are provided in the response body. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: ModifyProcessInstancesBatchOperationResponse ```ts type ModifyProcessInstancesBatchOperationResponse = ModifyProcessInstancesBatchOperationResponses[keyof ModifyProcessInstancesBatchOperationResponses]; ``` --- ## Type Alias: ModifyProcessInstancesBatchOperationResponses ```ts type ModifyProcessInstancesBatchOperationResponses = object; ``` ## Properties ### 200 ```ts 200: BatchOperationCreatedResult; ``` The batch operation request was created. --- ## Type Alias: OffsetPagination ```ts type OffsetPagination = object; ``` Offset-based pagination ## Properties ### from? ```ts optional from?: number; ``` The index of items to start searching from. --- ### limit? ```ts optional limit?: number; ``` The maximum number of items to return in one request. --- ## Type Alias: OperationReference ```ts type OperationReference = number; ``` A reference key chosen by the user that will be part of all records resulting from this operation. Must be > 0 if provided. --- ## Type Alias: OperationTypeExactMatch ```ts type OperationTypeExactMatch = AuditLogOperationTypeEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: OperationTypeFilterProperty ```ts type OperationTypeFilterProperty = | OperationTypeExactMatch | AdvancedOperationTypeFilter; ``` AuditLogOperationTypeEnum property with full advanced search capabilities. --- ## Type Alias: OwnerTypeEnum ```ts type OwnerTypeEnum = (typeof OwnerTypeEnum)[keyof typeof OwnerTypeEnum]; ``` The type of the owner of permissions. --- ## Type Alias: Partition ```ts type Partition = object; ``` Provides information on a partition within a broker node. ## Properties ### health ```ts health: "healthy" | "unhealthy" | "dead"; ``` Describes the current health of the partition. --- ### partitionId ```ts partitionId: number; ``` The unique ID of this partition. --- ### role ```ts role: "leader" | "follower" | "inactive"; ``` Describes the Raft role of the broker for a given partition. --- ## Type Alias: PermissionTypeEnum ```ts type PermissionTypeEnum = (typeof PermissionTypeEnum)[keyof typeof PermissionTypeEnum]; ``` Specifies the type of permissions. --- ## Type Alias: PinClockData ```ts type PinClockData = object; ``` ## Properties ### body ```ts body: ClockPinRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/clock"; ``` --- ## Type Alias: PinClockError ```ts type PinClockError = PinClockErrors[keyof PinClockErrors]; ``` --- ## Type Alias: PinClockErrors ```ts type PinClockErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: PinClockResponse ```ts type PinClockResponse = PinClockResponses[keyof PinClockResponses]; ``` --- ## Type Alias: PinClockResponses ```ts type PinClockResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The clock was successfully pinned. --- ## Type Alias: ProblemDetail ```ts type ProblemDetail = object; ``` A Problem detail object as described in [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457). There may be additional properties specific to the problem type. ## Properties ### detail ```ts detail: string; ``` An explanation of the problem in more detail. --- ### instance ```ts instance: string; ``` A URI path identifying the origin of the problem. --- ### status ```ts status: number; ``` The HTTP status code for this problem. --- ### title ```ts title: string; ``` A summary of the problem type. --- ### type ```ts type: string; ``` A URI identifying the problem type. --- ## Type Alias: ProcessDefinitionElementStatisticsQuery ```ts type ProcessDefinitionElementStatisticsQuery = object; ``` Process definition element statistics request. ## Properties ### filter? ```ts optional filter?: ProcessDefinitionStatisticsFilter; ``` The process definition statistics search filters. --- ## Type Alias: ProcessDefinitionElementStatisticsQueryResult ```ts type ProcessDefinitionElementStatisticsQueryResult = object; ``` Process definition element statistics query response. ## Properties ### items ```ts items: ProcessElementStatisticsResult[]; ``` The element statistics. --- ## Type Alias: ProcessDefinitionFilter ```ts type ProcessDefinitionFilter = object; ``` Process definition search filter. ## Properties ### hasStartForm? ```ts optional hasStartForm?: boolean; ``` Indicates whether the start event of the process has an associated Form Key. --- ### isLatestVersion? ```ts optional isLatestVersion?: boolean; ``` Whether to only return the latest version of each process definition. When using this filter, pagination functionality is limited, you can only paginate forward using `after` and `limit`. The response contains no `startCursor` in the `page`, and requests ignore the `from` and `before` in the `page`. When using this filter, sorting is limited to `processDefinitionId` and `tenantId` fields only. --- ### name? ```ts optional name?: StringFilterProperty; ``` Name of this process definition. --- ### processDefinitionId? ```ts optional processDefinitionId?: StringFilterProperty; ``` Process definition ID of this process definition. --- ### processDefinitionKey? ```ts optional processDefinitionKey?: ProcessDefinitionKey; ``` The key for this process definition. --- ### resourceName? ```ts optional resourceName?: string; ``` Resource name of this process definition. --- ### tenantId? ```ts optional tenantId?: TenantId; ``` Tenant ID of this process definition. --- ### version? ```ts optional version?: number; ``` Version of this process definition. --- ### versionTag? ```ts optional versionTag?: string; ``` Version tag of this process definition. --- ## Type Alias: ProcessDefinitionId ```ts type ProcessDefinitionId = CamundaKey<"ProcessDefinitionId">; ``` Id of a process definition, from the model. Only ids of process definitions that are deployed are useful. --- ## Type Alias: ProcessDefinitionIdExactMatch ```ts type ProcessDefinitionIdExactMatch = ProcessDefinitionId; ``` Exact match Matches the value exactly. --- ## Type Alias: ProcessDefinitionIdFilterProperty ```ts type ProcessDefinitionIdFilterProperty = | ProcessDefinitionIdExactMatch | AdvancedProcessDefinitionIdFilter; ``` ProcessDefinitionId property with full advanced search capabilities. --- ## Type Alias: ProcessDefinitionInstanceStatisticsQuery ```ts type ProcessDefinitionInstanceStatisticsQuery = object; ``` ## Properties ### page? ```ts optional page?: OffsetPagination; ``` Search cursor pagination. --- ### sort? ```ts optional sort?: ProcessDefinitionInstanceStatisticsQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: ProcessDefinitionInstanceStatisticsQueryResult ```ts type ProcessDefinitionInstanceStatisticsQueryResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: ProcessDefinitionInstanceStatisticsResult[]; ``` The process definition instance statistics result. --- ## Type Alias: ProcessDefinitionInstanceStatisticsQuerySortRequest ```ts type ProcessDefinitionInstanceStatisticsQuerySortRequest = object; ``` ## Properties ### field ```ts field: | "processDefinitionId" | "activeInstancesWithIncidentCount" | "activeInstancesWithoutIncidentCount"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: ProcessDefinitionInstanceStatisticsResult ```ts type ProcessDefinitionInstanceStatisticsResult = object; ``` Process definition instance statistics response. ## Properties ### activeInstancesWithIncidentCount ```ts activeInstancesWithIncidentCount: number; ``` Total number of currently active process instances of this definition that have at least one incident. --- ### activeInstancesWithoutIncidentCount ```ts activeInstancesWithoutIncidentCount: number; ``` Total number of currently active process instances of this definition that do not have incidents. --- ### hasMultipleVersions ```ts hasMultipleVersions: boolean; ``` Indicates whether multiple versions of this process definition instance are deployed. --- ### latestProcessDefinitionName ```ts latestProcessDefinitionName: string | null; ``` Name of the latest deployed process definition instance version. --- ### processDefinitionId ```ts processDefinitionId: ProcessDefinitionId; ``` --- ### tenantId ```ts tenantId: TenantId; ``` --- ## Type Alias: ProcessDefinitionInstanceVersionStatisticsFilter ```ts type ProcessDefinitionInstanceVersionStatisticsFilter = object; ``` Process definition instance version statistics search filter. ## Properties ### processDefinitionId ```ts processDefinitionId: ProcessDefinitionId; ``` The ID of the process definition to retrieve version statistics for. --- ### tenantId? ```ts optional tenantId?: TenantId; ``` Tenant ID of this process definition. --- ## Type Alias: ProcessDefinitionInstanceVersionStatisticsQuery ```ts type ProcessDefinitionInstanceVersionStatisticsQuery = object; ``` ## Properties ### filter ```ts filter: ProcessDefinitionInstanceVersionStatisticsFilter; ``` The process definition instance version statistics search filters. --- ### page? ```ts optional page?: OffsetPagination; ``` Pagination criteria. --- ### sort? ```ts optional sort?: ProcessDefinitionInstanceVersionStatisticsQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: ProcessDefinitionInstanceVersionStatisticsQueryResult ```ts type ProcessDefinitionInstanceVersionStatisticsQueryResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: ProcessDefinitionInstanceVersionStatisticsResult[]; ``` The process definition instance version statistics result. --- ## Type Alias: ProcessDefinitionInstanceVersionStatisticsQuerySortRequest ```ts type ProcessDefinitionInstanceVersionStatisticsQuerySortRequest = object; ``` ## Properties ### field ```ts field: | "processDefinitionId" | "processDefinitionKey" | "processDefinitionName" | "processDefinitionVersion" | "activeInstancesWithIncidentCount" | "activeInstancesWithoutIncidentCount"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: ProcessDefinitionInstanceVersionStatisticsResult ```ts type ProcessDefinitionInstanceVersionStatisticsResult = object; ``` Process definition instance version statistics response. ## Properties ### activeInstancesWithIncidentCount ```ts activeInstancesWithIncidentCount: number; ``` The number of active process instances for this version that currently have incidents. --- ### activeInstancesWithoutIncidentCount ```ts activeInstancesWithoutIncidentCount: number; ``` The number of active process instances for this version that do not have any incidents. --- ### processDefinitionId ```ts processDefinitionId: ProcessDefinitionId; ``` The ID associated with the process definition. --- ### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` The unique key of the process definition. --- ### processDefinitionName ```ts processDefinitionName: string | null; ``` The name of the process definition. --- ### processDefinitionVersion ```ts processDefinitionVersion: number; ``` The version number of the process definition. --- ### tenantId ```ts tenantId: TenantId; ``` The tenant ID associated with the process definition. --- ## Type Alias: ProcessDefinitionKey ```ts type ProcessDefinitionKey = CamundaKey<"ProcessDefinitionKey">; ``` System-generated key for a deployed process definition. --- ## Type Alias: ProcessDefinitionKeyExactMatch ```ts type ProcessDefinitionKeyExactMatch = ProcessDefinitionKey; ``` Exact match Matches the value exactly. --- ## Type Alias: ProcessDefinitionKeyFilterProperty ```ts type ProcessDefinitionKeyFilterProperty = | ProcessDefinitionKeyExactMatch | AdvancedProcessDefinitionKeyFilter; ``` ProcessDefinitionKey property with full advanced search capabilities. --- ## Type Alias: ProcessDefinitionMessageSubscriptionStatisticsQuery ```ts type ProcessDefinitionMessageSubscriptionStatisticsQuery = object; ``` ## Properties ### filter? ```ts optional filter?: MessageSubscriptionFilter; ``` The message subscription filters. --- ### page? ```ts optional page?: CursorForwardPagination; ``` Search cursor pagination. --- ## Type Alias: ProcessDefinitionMessageSubscriptionStatisticsQueryResult ```ts type ProcessDefinitionMessageSubscriptionStatisticsQueryResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: ProcessDefinitionMessageSubscriptionStatisticsResult[]; ``` The matching process definition message subscription statistics. --- ## Type Alias: ProcessDefinitionMessageSubscriptionStatisticsResult ```ts type ProcessDefinitionMessageSubscriptionStatisticsResult = object; ``` ## Properties ### activeSubscriptions ```ts activeSubscriptions: number; ``` The total number of active message subscriptions for this process definition key. --- ### processDefinitionId ```ts processDefinitionId: ProcessDefinitionId; ``` The process definition ID associated with this message subscription. --- ### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` The process definition key associated with this message subscription. --- ### processInstancesWithActiveSubscriptions ```ts processInstancesWithActiveSubscriptions: number; ``` The number of process instances with active message subscriptions. --- ### tenantId ```ts tenantId: TenantId; ``` The tenant ID associated with this message subscription. --- ## Type Alias: ProcessDefinitionResult ```ts type ProcessDefinitionResult = object; ``` ## Properties ### hasStartForm ```ts hasStartForm: boolean; ``` Indicates whether the start event of the process has an associated Form Key. --- ### name ```ts name: string | null; ``` Name of this process definition. --- ### processDefinitionId ```ts processDefinitionId: ProcessDefinitionId; ``` Process definition ID of this process definition. --- ### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` The key for this process definition. --- ### resourceName ```ts resourceName: string; ``` Resource name for this process definition. --- ### tenantId ```ts tenantId: TenantId; ``` Tenant ID of this process definition. --- ### version ```ts version: number; ``` Version of this process definition. --- ### versionTag ```ts versionTag: string | null; ``` Version tag of this process definition. --- ## Type Alias: ProcessDefinitionSearchQuery ```ts type ProcessDefinitionSearchQuery = SearchQueryRequest & object; ``` ## Type Declaration ### filter? ```ts optional filter?: ProcessDefinitionFilter; ``` The process definition search filters. ### sort? ```ts optional sort?: ProcessDefinitionSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: ProcessDefinitionSearchQueryResult ```ts type ProcessDefinitionSearchQueryResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: ProcessDefinitionResult[]; ``` The matching process definitions. --- ## Type Alias: ProcessDefinitionSearchQuerySortRequest ```ts type ProcessDefinitionSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: | "processDefinitionKey" | "name" | "resourceName" | "version" | "versionTag" | "processDefinitionId" | "tenantId"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: ProcessDefinitionStatisticsFilter ```ts type ProcessDefinitionStatisticsFilter = BaseProcessInstanceFilterFields & object; ``` Process definition statistics search filter. ## Type Declaration ### $or? ```ts optional $or?: BaseProcessInstanceFilterFields[]; ``` Defines a list of alternative filter groups combined using OR logic. Each object in the array is evaluated independently, and the filter matches if any one of them is satisfied. Top-level fields and the `$or` clause are combined using AND logic — meaning: (top-level filters) AND (any of the `$or` filters) must match. _Example:_ ```json { "state": "ACTIVE", "tenantId": 123, "$or": [ { "processDefinitionId": "process_v1" }, { "processDefinitionId": "process_v2", "hasIncident": true } ] } ``` This matches process instances that: - are in _ACTIVE_ state - have tenant id equal to _123_ - and match either: - `processDefinitionId` is _process_v1_, or - `processDefinitionId` is _process_v2_ and `hasIncident` is _true_ Note: Using complex `$or` conditions may impact performance, use with caution in high-volume environments. --- ## Type Alias: ProcessElementStatisticsResult ```ts type ProcessElementStatisticsResult = object; ``` Process element statistics response. ## Properties ### active ```ts active: number; ``` The total number of active instances of the element. --- ### canceled ```ts canceled: number; ``` The total number of canceled instances of the element. --- ### completed ```ts completed: number; ``` The total number of completed instances of the element. --- ### elementId ```ts elementId: ElementId; ``` The element ID for which the results are aggregated. --- ### incidents ```ts incidents: number; ``` The total number of incidents for the element. --- ## Type Alias: ProcessInstanceCallHierarchyEntry ```ts type ProcessInstanceCallHierarchyEntry = object; ``` ## Properties ### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` The key of the process definition. --- ### processDefinitionName ```ts processDefinitionName: string; ``` The name of the process definition (fall backs to the process definition id if not available). --- ### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The key of the process instance. --- ## Type Alias: ProcessInstanceCancellationBatchOperationRequest ```ts type ProcessInstanceCancellationBatchOperationRequest = object; ``` The process instance filter that defines which process instances should be canceled. ## Properties ### filter ```ts filter: ProcessInstanceFilter; ``` The process instance filter. --- ### operationReference? ```ts optional operationReference?: OperationReference; ``` --- ## Type Alias: ProcessInstanceCreationInstruction ```ts type ProcessInstanceCreationInstruction = | ProcessInstanceCreationInstructionByKey | ProcessInstanceCreationInstructionById; ``` Instructions for creating a process instance. The process definition can be specified either by id or by key. --- ## Type Alias: ProcessInstanceCreationInstructionById ```ts type ProcessInstanceCreationInstructionById = object; ``` Process creation by id ## Properties ### awaitCompletion? ```ts optional awaitCompletion?: boolean; ``` Wait for the process instance to complete. If the process instance does not complete within the request timeout limit, a 504 response status will be returned. The process instance will continue to run in the background regardless of the timeout. Disabled by default. --- ### businessId? ```ts optional businessId?: BusinessId; ``` --- ### fetchVariables? ```ts optional fetchVariables?: string[]; ``` List of variables by name to be included in the response when awaitCompletion is set to true. If empty, all visible variables in the root scope will be returned. --- ### operationReference? ```ts optional operationReference?: OperationReference; ``` --- ### processDefinitionId ```ts processDefinitionId: ProcessDefinitionId; ``` The BPMN process id of the process definition to start an instance of. --- ### processDefinitionVersion? ```ts optional processDefinitionVersion?: number; ``` The version of the process. By default, the latest version of the process is used. --- ### requestTimeout? ```ts optional requestTimeout?: number; ``` Timeout (in ms) the request waits for the process to complete. By default or when set to 0, the generic request timeout configured in the cluster is applied. --- ### runtimeInstructions? ```ts optional runtimeInstructions?: ProcessInstanceCreationRuntimeInstruction[]; ``` Runtime instructions (alpha). List of instructions that affect the runtime behavior of the process instance. Refer to specific instruction types for more details. This parameter is an alpha feature and may be subject to change in future releases. --- ### startInstructions? ```ts optional startInstructions?: ProcessInstanceCreationStartInstruction[]; ``` List of start instructions. By default, the process instance will start at the start event. If provided, the process instance will apply start instructions after it has been created. --- ### tags? ```ts optional tags?: TagSet; ``` --- ### tenantId? ```ts optional tenantId?: TenantId; ``` The tenant id of the process definition. If multi-tenancy is enabled, provide the tenant id of the process definition to start a process instance of. If multi-tenancy is disabled, don't provide this parameter. --- ### variables? ```ts optional variables?: object; ``` JSON object that will instantiate the variables for the root variable scope of the process instance. #### Index Signature ```ts [key: string]: unknown ``` --- ## Type Alias: ProcessInstanceCreationInstructionByKey ```ts type ProcessInstanceCreationInstructionByKey = object; ``` Process creation by key ## Properties ### awaitCompletion? ```ts optional awaitCompletion?: boolean; ``` Wait for the process instance to complete. If the process instance does not complete within the request timeout limit, a 504 response status will be returned. The process instance will continue to run in the background regardless of the timeout. Disabled by default. --- ### businessId? ```ts optional businessId?: BusinessId; ``` --- ### fetchVariables? ```ts optional fetchVariables?: string[]; ``` List of variables by name to be included in the response when awaitCompletion is set to true. If empty, all visible variables in the root scope will be returned. --- ### operationReference? ```ts optional operationReference?: OperationReference; ``` --- ### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` The unique key identifying the process definition, for example, returned for a process in the deploy resources endpoint. --- ### processDefinitionVersion? ```ts optional processDefinitionVersion?: number; ``` As the version is already identified by the `processDefinitionKey`, the value of this field is ignored. It's here for backwards-compatibility only as previous releases accepted it in request bodies. --- ### requestTimeout? ```ts optional requestTimeout?: number; ``` Timeout (in ms) the request waits for the process to complete. By default or when set to 0, the generic request timeout configured in the cluster is applied. --- ### runtimeInstructions? ```ts optional runtimeInstructions?: ProcessInstanceCreationRuntimeInstruction[]; ``` Runtime instructions (alpha). List of instructions that affect the runtime behavior of the process instance. Refer to specific instruction types for more details. This parameter is an alpha feature and may be subject to change in future releases. --- ### startInstructions? ```ts optional startInstructions?: ProcessInstanceCreationStartInstruction[]; ``` List of start instructions. By default, the process instance will start at the start event. If provided, the process instance will apply start instructions after it has been created. --- ### tags? ```ts optional tags?: TagSet; ``` --- ### tenantId? ```ts optional tenantId?: TenantId; ``` The tenant id of the process definition. If multi-tenancy is enabled, provide the tenant id of the process definition to start a process instance of. If multi-tenancy is disabled, don't provide this parameter. --- ### variables? ```ts optional variables?: object; ``` Set of variables as JSON object to instantiate in the root variable scope of the process instance. Can include nested complex objects. #### Index Signature ```ts [key: string]: unknown ``` --- ## Type Alias: ProcessInstanceCreationRuntimeInstruction ```ts type ProcessInstanceCreationRuntimeInstruction = object & ProcessInstanceCreationTerminateInstruction; ``` ## Type Declaration ### type ```ts type: "TERMINATE_PROCESS_INSTANCE"; ``` --- ## Type Alias: ProcessInstanceCreationStartInstruction ```ts type ProcessInstanceCreationStartInstruction = object; ``` ## Properties ### elementId ```ts elementId: ElementId; ``` Future extensions might include: - different types of start instructions - ability to set local variables for different flow scopes For now, however, the start instruction is implicitly a "startBeforeElement" instruction --- ## Type Alias: ProcessInstanceCreationTerminateInstruction ```ts type ProcessInstanceCreationTerminateInstruction = object; ``` Terminates the process instance after a specific BPMN element is completed or terminated. ## Properties ### afterElementId ```ts afterElementId: ElementId; ``` The id of the element that, once completed or terminated, will cause the process to be terminated. --- ### type? ```ts optional type?: string; ``` The type of the runtime instruction --- ## Type Alias: ProcessInstanceDeletionBatchOperationRequest ```ts type ProcessInstanceDeletionBatchOperationRequest = object; ``` The process instance filter that defines which process instances should be deleted. ## Properties ### filter ```ts filter: ProcessInstanceFilter; ``` The process instance filter. --- ### operationReference? ```ts optional operationReference?: OperationReference; ``` --- ## Type Alias: ProcessInstanceElementStatisticsQueryResult ```ts type ProcessInstanceElementStatisticsQueryResult = object; ``` Process instance element statistics query response. ## Properties ### items ```ts items: ProcessElementStatisticsResult[]; ``` The element statistics. --- ## Type Alias: ProcessInstanceFilter ```ts type ProcessInstanceFilter = ProcessInstanceFilterFields & object; ``` Process instance search filter. ## Type Declaration ### $or? ```ts optional $or?: ProcessInstanceFilterFields[]; ``` Defines a list of alternative filter groups combined using OR logic. Each object in the array is evaluated independently, and the filter matches if any one of them is satisfied. Top-level fields and the `$or` clause are combined using AND logic — meaning: (top-level filters) AND (any of the `$or` filters) must match. _Example:_ ```json { "state": "ACTIVE", "tenantId": 123, "$or": [ { "processDefinitionId": "process_v1" }, { "processDefinitionId": "process_v2", "hasIncident": true } ] } ``` This matches process instances that: - are in _ACTIVE_ state - have tenant id equal to _123_ - and match either: - `processDefinitionId` is _process_v1_, or - `processDefinitionId` is _process_v2_ and `hasIncident` is _true_ Note: Using complex `$or` conditions may impact performance, use with caution in high-volume environments. --- ## Type Alias: ProcessInstanceFilterFields ```ts type ProcessInstanceFilterFields = BaseProcessInstanceFilterFields & object; ``` Process instance search filter. ## Type Declaration ### processDefinitionId? ```ts optional processDefinitionId?: StringFilterProperty; ``` The process definition id. ### processDefinitionKey? ```ts optional processDefinitionKey?: ProcessDefinitionKeyFilterProperty; ``` The process definition key. ### processDefinitionName? ```ts optional processDefinitionName?: StringFilterProperty; ``` The process definition name. ### processDefinitionVersion? ```ts optional processDefinitionVersion?: IntegerFilterProperty; ``` The process definition version. ### processDefinitionVersionTag? ```ts optional processDefinitionVersionTag?: StringFilterProperty; ``` The process definition version tag. --- ## Type Alias: ProcessInstanceIncidentResolutionBatchOperationRequest ```ts type ProcessInstanceIncidentResolutionBatchOperationRequest = object; ``` The process instance filter that defines which process instances should have their incidents resolved. ## Properties ### filter ```ts filter: ProcessInstanceFilter; ``` The process instance filter. --- ### operationReference? ```ts optional operationReference?: OperationReference; ``` --- ## Type Alias: ProcessInstanceKey ```ts type ProcessInstanceKey = CamundaKey<"ProcessInstanceKey">; ``` System-generated key for a process instance. --- ## Type Alias: ProcessInstanceKeyExactMatch ```ts type ProcessInstanceKeyExactMatch = ProcessInstanceKey; ``` Exact match Matches the value exactly. --- ## Type Alias: ProcessInstanceKeyFilterProperty ```ts type ProcessInstanceKeyFilterProperty = | ProcessInstanceKeyExactMatch | AdvancedProcessInstanceKeyFilter; ``` ProcessInstanceKey property with full advanced search capabilities. --- ## Type Alias: ProcessInstanceMigrationBatchOperationPlan ```ts type ProcessInstanceMigrationBatchOperationPlan = object; ``` The migration instructions describe how to migrate a process instance from one process definition to another. ## Properties ### mappingInstructions ```ts mappingInstructions: MigrateProcessInstanceMappingInstruction[]; ``` The mapping instructions. --- ### targetProcessDefinitionKey ```ts targetProcessDefinitionKey: ProcessDefinitionKey; ``` The target process definition key. --- ## Type Alias: ProcessInstanceMigrationBatchOperationRequest ```ts type ProcessInstanceMigrationBatchOperationRequest = object; ``` ## Properties ### filter ```ts filter: ProcessInstanceFilter; ``` The process instance filter. --- ### migrationPlan ```ts migrationPlan: ProcessInstanceMigrationBatchOperationPlan; ``` The migration plan. --- ### operationReference? ```ts optional operationReference?: OperationReference; ``` --- ## Type Alias: ProcessInstanceMigrationInstruction ```ts type ProcessInstanceMigrationInstruction = object; ``` The migration instructions describe how to migrate a process instance from one process definition to another. ## Properties ### mappingInstructions ```ts mappingInstructions: MigrateProcessInstanceMappingInstruction[]; ``` Element mappings from the source process instance to the target process instance. --- ### operationReference? ```ts optional operationReference?: OperationReference; ``` --- ### targetProcessDefinitionKey ```ts targetProcessDefinitionKey: ProcessDefinitionKey; ``` The key of process definition to migrate the process instance to. --- ## Type Alias: ProcessInstanceModificationActivateInstruction ```ts type ProcessInstanceModificationActivateInstruction = object; ``` Instruction describing an element to activate. ## Properties ### ancestorElementInstanceKey? ```ts optional ancestorElementInstanceKey?: ElementInstanceKey; ``` The key of the ancestor scope the element instance should be created in. Set to -1 to create the new element instance within an existing element instance of the flow scope. If multiple instances of the target element's flow scope exist, choose one specifically with this property by providing its key. --- ### elementId ```ts elementId: ElementId; ``` The id of the element to activate. --- ### variableInstructions? ```ts optional variableInstructions?: ModifyProcessInstanceVariableInstruction[]; ``` Instructions describing which variables to create or update. --- ## Type Alias: ProcessInstanceModificationBatchOperationRequest ```ts type ProcessInstanceModificationBatchOperationRequest = object; ``` The process instance filter to define on which process instances tokens should be moved, and new element instances should be activated or terminated. ## Properties ### filter ```ts filter: ProcessInstanceFilter; ``` The process instance filter. --- ### moveInstructions ```ts moveInstructions: ProcessInstanceModificationMoveBatchOperationInstruction[]; ``` Instructions for moving tokens between elements. --- ### operationReference? ```ts optional operationReference?: OperationReference; ``` --- ## Type Alias: ProcessInstanceModificationInstruction ```ts type ProcessInstanceModificationInstruction = object; ``` ## Properties ### activateInstructions? ```ts optional activateInstructions?: ProcessInstanceModificationActivateInstruction[]; ``` Instructions describing which elements to activate in which scopes and which variables to create or update. --- ### moveInstructions? ```ts optional moveInstructions?: ProcessInstanceModificationMoveInstruction[]; ``` Instructions describing which elements to move from one scope to another. --- ### operationReference? ```ts optional operationReference?: OperationReference; ``` --- ### terminateInstructions? ```ts optional terminateInstructions?: ProcessInstanceModificationTerminateInstruction[]; ``` Instructions describing which elements to terminate. --- ## Type Alias: ProcessInstanceModificationMoveBatchOperationInstruction ```ts type ProcessInstanceModificationMoveBatchOperationInstruction = object; ``` Instructions describing a move operation. This instruction will terminate all active element instances at `sourceElementId` and activate a new element instance for each terminated one at `targetElementId`. The new element instances are created in the parent scope of the source element instances. ## Properties ### sourceElementId ```ts sourceElementId: ElementId; ``` The source element ID. --- ### targetElementId ```ts targetElementId: ElementId; ``` The target element ID. --- ## Type Alias: ProcessInstanceModificationMoveInstruction ```ts type ProcessInstanceModificationMoveInstruction = object; ``` Instruction describing a move operation. This instruction will terminate active element instances based on the sourceElementInstruction and activate a new element instance for each terminated one at targetElementId. Note that, for multi-instance activities, only the multi-instance body instances will activate new element instances at the target id. ## Properties ### ancestorScopeInstruction? ```ts optional ancestorScopeInstruction?: AncestorScopeInstruction; ``` --- ### sourceElementInstruction ```ts sourceElementInstruction: SourceElementInstruction; ``` --- ### targetElementId ```ts targetElementId: ElementId; ``` The target element id. --- ### variableInstructions? ```ts optional variableInstructions?: ModifyProcessInstanceVariableInstruction[]; ``` Instructions describing which variables to create or update. --- ## Type Alias: ProcessInstanceModificationTerminateByIdInstruction ```ts type ProcessInstanceModificationTerminateByIdInstruction = object; ``` Instruction describing which elements to terminate. The element instances are determined at runtime by the given id. ## Properties ### elementId ```ts elementId: ElementId; ``` The id of the elements to terminate. The element instances are determined at runtime. --- ## Type Alias: ProcessInstanceModificationTerminateByKeyInstruction ```ts type ProcessInstanceModificationTerminateByKeyInstruction = object; ``` Instruction providing the key of the element instance to terminate. ## Properties ### elementInstanceKey ```ts elementInstanceKey: ElementInstanceKey; ``` The key of the element instance to terminate. --- ## Type Alias: ProcessInstanceModificationTerminateInstruction ```ts type ProcessInstanceModificationTerminateInstruction = | ProcessInstanceModificationTerminateByIdInstruction | ProcessInstanceModificationTerminateByKeyInstruction; ``` Instruction describing which elements to terminate. --- ## Type Alias: ProcessInstanceReference ```ts type ProcessInstanceReference = object; ``` ## Properties ### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` The key of the process definition. --- ### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The key of the created process instance. --- ## Type Alias: ProcessInstanceResult ```ts type ProcessInstanceResult = object; ``` Process instance search response item. ## Properties ### businessId ```ts businessId: BusinessId | null; ``` The business id associated with this process instance. --- ### endDate ```ts endDate: string | null; ``` The completion or termination time of the process instance. --- ### hasIncident ```ts hasIncident: boolean; ``` Whether this process instance has a related incident or not. --- ### parentElementInstanceKey ```ts parentElementInstanceKey: ElementInstanceKey | null; ``` The parent element instance key. --- ### parentProcessInstanceKey ```ts parentProcessInstanceKey: ProcessInstanceKey | null; ``` The parent process instance key. --- ### processDefinitionId ```ts processDefinitionId: ProcessDefinitionId; ``` --- ### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` The process definition key. --- ### processDefinitionName ```ts processDefinitionName: string | null; ``` The process definition name. --- ### processDefinitionVersion ```ts processDefinitionVersion: number; ``` The process definition version. --- ### processDefinitionVersionTag ```ts processDefinitionVersionTag: string | null; ``` The process definition version tag. --- ### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The key of this process instance. --- ### rootProcessInstanceKey ```ts rootProcessInstanceKey: ProcessInstanceKey | null; ``` The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. --- ### startDate ```ts startDate: string; ``` The start time of the process instance. --- ### state ```ts state: ProcessInstanceStateEnum; ``` --- ### tags ```ts tags: TagSet; ``` --- ### tenantId ```ts tenantId: TenantId; ``` --- ## Type Alias: ProcessInstanceSearchQuery ```ts type ProcessInstanceSearchQuery = SearchQueryRequest & object; ``` Process instance search request. ## Type Declaration ### filter? ```ts optional filter?: ProcessInstanceFilter; ``` The process instance search filters. ### sort? ```ts optional sort?: ProcessInstanceSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: ProcessInstanceSearchQueryResult ```ts type ProcessInstanceSearchQueryResult = SearchQueryResponse & object; ``` Process instance search response. ## Type Declaration ### items ```ts items: ProcessInstanceResult[]; ``` The matching process instances. --- ## Type Alias: ProcessInstanceSearchQuerySortRequest ```ts type ProcessInstanceSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: | "processInstanceKey" | "processDefinitionId" | "processDefinitionName" | "processDefinitionVersion" | "processDefinitionVersionTag" | "processDefinitionKey" | "parentProcessInstanceKey" | "parentElementInstanceKey" | "startDate" | "endDate" | "state" | "hasIncident" | "tenantId" | "businessId"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: ProcessInstanceSequenceFlowResult ```ts type ProcessInstanceSequenceFlowResult = object; ``` Process instance sequence flow result. ## Properties ### elementId ```ts elementId: ElementId; ``` The element id for this sequence flow, as provided in the BPMN process. --- ### processDefinitionId ```ts processDefinitionId: ProcessDefinitionId; ``` The process definition id. --- ### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` The process definition key. --- ### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The key of this process instance. --- ### rootProcessInstanceKey ```ts rootProcessInstanceKey: ProcessInstanceKey | null; ``` The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. --- ### sequenceFlowId ```ts sequenceFlowId: string; ``` The sequence flow id. --- ### tenantId ```ts tenantId: TenantId; ``` --- ## Type Alias: ProcessInstanceSequenceFlowsQueryResult ```ts type ProcessInstanceSequenceFlowsQueryResult = object; ``` Process instance sequence flows query response. ## Properties ### items ```ts items: ProcessInstanceSequenceFlowResult[]; ``` The sequence flows. --- ## Type Alias: ProcessInstanceStateEnum ```ts type ProcessInstanceStateEnum = (typeof ProcessInstanceStateEnum)[keyof typeof ProcessInstanceStateEnum]; ``` Process instance states --- ## Type Alias: ProcessInstanceStateExactMatch ```ts type ProcessInstanceStateExactMatch = ProcessInstanceStateEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: ProcessInstanceStateFilterProperty ```ts type ProcessInstanceStateFilterProperty = | ProcessInstanceStateExactMatch | AdvancedProcessInstanceStateFilter; ``` ProcessInstanceStateEnum property with full advanced search capabilities. --- ## Type Alias: PublishMessageData ```ts type PublishMessageData = object; ``` ## Properties ### body ```ts body: MessagePublicationRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/messages/publication"; ``` --- ## Type Alias: PublishMessageError ```ts type PublishMessageError = PublishMessageErrors[keyof PublishMessageErrors]; ``` --- ## Type Alias: PublishMessageErrors ```ts type PublishMessageErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: PublishMessageResponse ```ts type PublishMessageResponse = PublishMessageResponses[keyof PublishMessageResponses]; ``` --- ## Type Alias: PublishMessageResponses ```ts type PublishMessageResponses = object; ``` ## Properties ### 200 ```ts 200: MessagePublicationResult; ``` The message was published. --- ## Type Alias: ResetClockData ```ts type ResetClockData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/clock/reset"; ``` --- ## Type Alias: ResetClockError ```ts type ResetClockError = ResetClockErrors[keyof ResetClockErrors]; ``` --- ## Type Alias: ResetClockErrors ```ts type ResetClockErrors = object; ``` ## Properties ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: ResetClockResponse ```ts type ResetClockResponse = ResetClockResponses[keyof ResetClockResponses]; ``` --- ## Type Alias: ResetClockResponses ```ts type ResetClockResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The clock was successfully reset to the system time. --- ## Type Alias: ResolveIncidentData ```ts type ResolveIncidentData = object; ``` ## Properties ### body? ```ts optional body?: IncidentResolutionRequest; ``` --- ### path ```ts path: object; ``` #### incidentKey ```ts incidentKey: IncidentKey; ``` Key of the incident to resolve. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/incidents/{incidentKey}/resolution"; ``` --- ## Type Alias: ResolveIncidentError ```ts type ResolveIncidentError = ResolveIncidentErrors[keyof ResolveIncidentErrors]; ``` --- ## Type Alias: ResolveIncidentErrors ```ts type ResolveIncidentErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 404 ```ts 404: ProblemDetail; ``` The incident with the incidentKey is not found. --- ### 409 ```ts 409: ProblemDetail; ``` The incident cannot be resolved due to an invalid state. For example, the associated job may have no retries left. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: ResolveIncidentResponse ```ts type ResolveIncidentResponse = ResolveIncidentResponses[keyof ResolveIncidentResponses]; ``` --- ## Type Alias: ResolveIncidentResponses ```ts type ResolveIncidentResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The incident is marked as resolved. --- ## Type Alias: ResolveIncidentsBatchOperationData ```ts type ResolveIncidentsBatchOperationData = object; ``` ## Properties ### body? ```ts optional body?: ProcessInstanceIncidentResolutionBatchOperationRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-instances/incident-resolution"; ``` --- ## Type Alias: ResolveIncidentsBatchOperationError ```ts type ResolveIncidentsBatchOperationError = ResolveIncidentsBatchOperationErrors[keyof ResolveIncidentsBatchOperationErrors]; ``` --- ## Type Alias: ResolveIncidentsBatchOperationErrors ```ts type ResolveIncidentsBatchOperationErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The process instance batch operation failed. More details are provided in the response body. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: ResolveIncidentsBatchOperationResponse ```ts type ResolveIncidentsBatchOperationResponse = ResolveIncidentsBatchOperationResponses[keyof ResolveIncidentsBatchOperationResponses]; ``` --- ## Type Alias: ResolveIncidentsBatchOperationResponses ```ts type ResolveIncidentsBatchOperationResponses = object; ``` ## Properties ### 200 ```ts 200: BatchOperationCreatedResult; ``` The batch operation request was created. --- ## Type Alias: ResolveProcessInstanceIncidentsData ```ts type ResolveProcessInstanceIncidentsData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The key of the process instance to resolve incidents for. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-instances/{processInstanceKey}/incident-resolution"; ``` --- ## Type Alias: ResolveProcessInstanceIncidentsError ```ts type ResolveProcessInstanceIncidentsError = ResolveProcessInstanceIncidentsErrors[keyof ResolveProcessInstanceIncidentsErrors]; ``` --- ## Type Alias: ResolveProcessInstanceIncidentsErrors ```ts type ResolveProcessInstanceIncidentsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 404 ```ts 404: ProblemDetail; ``` The process instance is not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: ResolveProcessInstanceIncidentsResponse ```ts type ResolveProcessInstanceIncidentsResponse = ResolveProcessInstanceIncidentsResponses[keyof ResolveProcessInstanceIncidentsResponses]; ``` --- ## Type Alias: ResolveProcessInstanceIncidentsResponses ```ts type ResolveProcessInstanceIncidentsResponses = object; ``` ## Properties ### 200 ```ts 200: BatchOperationCreatedResult; ``` The batch operation request for incident resolution was created. --- ## Type Alias: ResourceFilter ```ts type ResourceFilter = object; ``` Resource search filter. ## Properties ### deploymentKey? ```ts optional deploymentKey?: DeploymentKeyFilterProperty; ``` Deployment key of this resource. --- ### resourceId? ```ts optional resourceId?: StringFilterProperty; ``` Resource ID of this resource. --- ### resourceKey? ```ts optional resourceKey?: ResourceKeyFilterProperty; ``` The key for this resource. --- ### resourceName? ```ts optional resourceName?: StringFilterProperty; ``` Resource name of this resource. --- ### tenantId? ```ts optional tenantId?: TenantId; ``` Tenant ID of this resource. --- ### version? ```ts optional version?: IntegerFilterProperty; ``` Version of this resource. --- ### versionTag? ```ts optional versionTag?: StringFilterProperty; ``` Version tag of this resource. --- ## Type Alias: ResourceKey ```ts type ResourceKey = | ProcessDefinitionKey | DecisionRequirementsKey | FormKey | DecisionDefinitionKey; ``` The system-assigned key for this resource. --- ## Type Alias: ResourceKeyExactMatch ```ts type ResourceKeyExactMatch = ResourceKey; ``` Exact match Matches the value exactly. --- ## Type Alias: ResourceKeyFilterProperty ```ts type ResourceKeyFilterProperty = | ResourceKeyExactMatch | AdvancedResourceKeyFilter; ``` ResourceKey property with full advanced search capabilities. --- ## Type Alias: ResourceResult ```ts type ResourceResult = object; ``` ## Properties ### resourceId ```ts resourceId: string; ``` The resource ID of this resource. --- ### resourceKey ```ts resourceKey: ResourceKey; ``` The unique key of this resource. --- ### resourceName ```ts resourceName: string; ``` The resource name from which this resource was parsed. --- ### tenantId ```ts tenantId: TenantId; ``` The tenant ID of this resource. --- ### version ```ts version: number; ``` The assigned resource version. --- ### versionTag ```ts versionTag: string | null; ``` The version tag of this resource. --- ## Type Alias: ResourceSearchQuery ```ts type ResourceSearchQuery = SearchQueryRequest & object; ``` ## Type Declaration ### filter? ```ts optional filter?: ResourceFilter; ``` The resource search filters. ### sort? ```ts optional sort?: ResourceSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: ResourceSearchQueryResult ```ts type ResourceSearchQueryResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: ResourceResult[]; ``` The matching resources. --- ## Type Alias: ResourceSearchQuerySortRequest ```ts type ResourceSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: | "resourceKey" | "resourceName" | "resourceId" | "version" | "versionTag" | "deploymentKey" | "tenantId"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: ResourceTypeEnum ```ts type ResourceTypeEnum = (typeof ResourceTypeEnum)[keyof typeof ResourceTypeEnum]; ``` The type of resource to add/remove permissions to/from. --- ## Type Alias: Result # Type Alias: Result\ ```ts type Result = | { ok: true; value: T; } | { error: E; ok: false; }; ``` ## Type Parameters ### T `T` ### E `E` = `unknown` --- ## Type Alias: ResumeBatchOperationData ```ts type ResumeBatchOperationData = object; ``` ## Properties ### body? ```ts optional body?: unknown; ``` --- ### path ```ts path: object; ``` #### batchOperationKey ```ts batchOperationKey: BatchOperationKey; ``` The key (or operate legacy ID) of the batch operation. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/batch-operations/{batchOperationKey}/resumption"; ``` --- ## Type Alias: ResumeBatchOperationError ```ts type ResumeBatchOperationError = ResumeBatchOperationErrors[keyof ResumeBatchOperationErrors]; ``` --- ## Type Alias: ResumeBatchOperationErrors ```ts type ResumeBatchOperationErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Not found. The batch operation was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: ResumeBatchOperationResponse ```ts type ResumeBatchOperationResponse = ResumeBatchOperationResponses[keyof ResumeBatchOperationResponses]; ``` --- ## Type Alias: ResumeBatchOperationResponses ```ts type ResumeBatchOperationResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The batch operation resume request was created. --- ## Type Alias: RoleClientResult ```ts type RoleClientResult = object; ``` ## Properties ### clientId ```ts clientId: ClientId; ``` The ID of the client. --- ## Type Alias: RoleClientSearchQueryRequest ```ts type RoleClientSearchQueryRequest = SearchQueryRequest & object; ``` ## Type Declaration ### sort? ```ts optional sort?: RoleClientSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: RoleClientSearchQuerySortRequest ```ts type RoleClientSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: "clientId"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: RoleClientSearchResult ```ts type RoleClientSearchResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: RoleClientResult[]; ``` The matching clients. --- ## Type Alias: RoleCreateRequest ```ts type RoleCreateRequest = object; ``` ## Properties ### description? ```ts optional description?: string; ``` The description of the new role. --- ### name ```ts name: string; ``` The display name of the new role. --- ### roleId ```ts roleId: RoleId; ``` The ID of the new role. --- ## Type Alias: RoleCreateResult ```ts type RoleCreateResult = object; ``` ## Properties ### description ```ts description: string | null; ``` The description of the created role. --- ### name ```ts name: string; ``` The display name of the created role. --- ### roleId ```ts roleId: RoleId; ``` The ID of the created role. --- ## Type Alias: RoleFilter ```ts type RoleFilter = object; ``` Role filter request ## Properties ### name? ```ts optional name?: string; ``` The role name search filters. --- ### roleId? ```ts optional roleId?: RoleId; ``` The role ID search filters. --- ## Type Alias: RoleGroupResult ```ts type RoleGroupResult = object; ``` ## Properties ### groupId ```ts groupId: GroupId; ``` The id of the group. --- ## Type Alias: RoleGroupSearchQueryRequest ```ts type RoleGroupSearchQueryRequest = SearchQueryRequest & object; ``` ## Type Declaration ### sort? ```ts optional sort?: RoleGroupSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: RoleGroupSearchQuerySortRequest ```ts type RoleGroupSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: "groupId"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: RoleGroupSearchResult ```ts type RoleGroupSearchResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: RoleGroupResult[]; ``` The matching groups. --- ## Type Alias: RoleId ```ts type RoleId = CamundaKey<"RoleId">; ``` The unique identifier of a role. --- ## Type Alias: RoleMappingRuleSearchResult ```ts type RoleMappingRuleSearchResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: MappingRuleResult[]; ``` The matching mapping rules. --- ## Type Alias: RoleResult ```ts type RoleResult = object; ``` Role search response item. ## Properties ### description ```ts description: string | null; ``` The description of the role. --- ### name ```ts name: string; ``` The role name. --- ### roleId ```ts roleId: RoleId; ``` The role id. --- ## Type Alias: RoleSearchQueryRequest ```ts type RoleSearchQueryRequest = SearchQueryRequest & object; ``` Role search request. ## Type Declaration ### filter? ```ts optional filter?: RoleFilter; ``` The role search filters. ### sort? ```ts optional sort?: RoleSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: RoleSearchQueryResult ```ts type RoleSearchQueryResult = SearchQueryResponse & object; ``` Role search response. ## Type Declaration ### items ```ts items: RoleResult[]; ``` The matching roles. --- ## Type Alias: RoleSearchQuerySortRequest ```ts type RoleSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: "name" | "roleId"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: RoleUpdateRequest ```ts type RoleUpdateRequest = object; ``` ## Properties ### description? ```ts optional description?: string; ``` The description of the new role. --- ### name ```ts name: string; ``` The display name of the new role. --- ## Type Alias: RoleUpdateResult ```ts type RoleUpdateResult = object; ``` ## Properties ### description ```ts description: string | null; ``` The description of the updated role. --- ### name ```ts name: string; ``` The display name of the updated role. --- ### roleId ```ts roleId: RoleId; ``` The ID of the updated role. --- ## Type Alias: RoleUserResult ```ts type RoleUserResult = object; ``` ## Properties ### username ```ts username: Username; ``` --- ## Type Alias: RoleUserSearchQueryRequest ```ts type RoleUserSearchQueryRequest = SearchQueryRequest & object; ``` ## Type Declaration ### sort? ```ts optional sort?: RoleUserSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: RoleUserSearchQuerySortRequest ```ts type RoleUserSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: "username"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: RoleUserSearchResult ```ts type RoleUserSearchResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: RoleUserResult[]; ``` The matching users. --- ## Type Alias: ScopeKey ```ts type ScopeKey = ProcessInstanceKey | ElementInstanceKey; ``` System-generated key for a scope. A scope can hold variables and represents either an element instance in a BPMN process or the process instance itself. --- ## Type Alias: ScopeKeyExactMatch ```ts type ScopeKeyExactMatch = ScopeKey; ``` Exact match Matches the value exactly. --- ## Type Alias: ScopeKeyFilterProperty ```ts type ScopeKeyFilterProperty = ScopeKeyExactMatch | AdvancedScopeKeyFilter; ``` ScopeKey property with full advanced search capabilities. Filter by the key of the element instance or process instance that defines the scope of a variable. --- ## Type Alias: SdkError ```ts type SdkError = | HttpSdkError | ValidationSdkError | AuthSdkError | NetworkSdkError | CancelSdkError; ``` --- ## Type Alias: SearchAgentInstancesData ```ts type SearchAgentInstancesData = object; ``` ## Properties ### body? ```ts optional body?: AgentInstanceSearchQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/agent-instances/search"; ``` --- ## Type Alias: SearchAgentInstancesError ```ts type SearchAgentInstancesError = SearchAgentInstancesErrors[keyof SearchAgentInstancesErrors]; ``` --- ## Type Alias: SearchAgentInstancesErrors ```ts type SearchAgentInstancesErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchAgentInstancesResponse ```ts type SearchAgentInstancesResponse = SearchAgentInstancesResponses[keyof SearchAgentInstancesResponses]; ``` --- ## Type Alias: SearchAgentInstancesResponses ```ts type SearchAgentInstancesResponses = object; ``` ## Properties ### 200 ```ts 200: AgentInstanceSearchQueryResult; ``` The agent instance search result. --- ## Type Alias: SearchAuditLogsData ```ts type SearchAuditLogsData = object; ``` ## Properties ### body? ```ts optional body?: AuditLogSearchQueryRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/audit-logs/search"; ``` --- ## Type Alias: SearchAuditLogsError ```ts type SearchAuditLogsError = SearchAuditLogsErrors[keyof SearchAuditLogsErrors]; ``` --- ## Type Alias: SearchAuditLogsErrors ```ts type SearchAuditLogsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: unknown; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchAuditLogsResponse ```ts type SearchAuditLogsResponse = SearchAuditLogsResponses[keyof SearchAuditLogsResponses]; ``` --- ## Type Alias: SearchAuditLogsResponses ```ts type SearchAuditLogsResponses = object; ``` ## Properties ### 200 ```ts 200: AuditLogSearchQueryResult; ``` The audit logs search result. --- ## Type Alias: SearchAuthorizationsData ```ts type SearchAuthorizationsData = object; ``` ## Properties ### body? ```ts optional body?: AuthorizationSearchQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/authorizations/search"; ``` --- ## Type Alias: SearchAuthorizationsError ```ts type SearchAuthorizationsError = SearchAuthorizationsErrors[keyof SearchAuthorizationsErrors]; ``` --- ## Type Alias: SearchAuthorizationsErrors ```ts type SearchAuthorizationsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchAuthorizationsResponse ```ts type SearchAuthorizationsResponse = SearchAuthorizationsResponses[keyof SearchAuthorizationsResponses]; ``` --- ## Type Alias: SearchAuthorizationsResponses ```ts type SearchAuthorizationsResponses = object; ``` ## Properties ### 200 ```ts 200: AuthorizationSearchResult; ``` The authorization search result. --- ## Type Alias: SearchBatchOperationItemsData ```ts type SearchBatchOperationItemsData = object; ``` ## Properties ### body? ```ts optional body?: BatchOperationItemSearchQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/batch-operation-items/search"; ``` --- ## Type Alias: SearchBatchOperationItemsError ```ts type SearchBatchOperationItemsError = SearchBatchOperationItemsErrors[keyof SearchBatchOperationItemsErrors]; ``` --- ## Type Alias: SearchBatchOperationItemsErrors ```ts type SearchBatchOperationItemsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchBatchOperationItemsResponse ```ts type SearchBatchOperationItemsResponse = SearchBatchOperationItemsResponses[keyof SearchBatchOperationItemsResponses]; ``` --- ## Type Alias: SearchBatchOperationItemsResponses ```ts type SearchBatchOperationItemsResponses = object; ``` ## Properties ### 200 ```ts 200: BatchOperationItemSearchQueryResult; ``` The batch operation search result. --- ## Type Alias: SearchBatchOperationsData ```ts type SearchBatchOperationsData = object; ``` ## Properties ### body? ```ts optional body?: BatchOperationSearchQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/batch-operations/search"; ``` --- ## Type Alias: SearchBatchOperationsError ```ts type SearchBatchOperationsError = SearchBatchOperationsErrors[keyof SearchBatchOperationsErrors]; ``` --- ## Type Alias: SearchBatchOperationsErrors ```ts type SearchBatchOperationsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchBatchOperationsResponse ```ts type SearchBatchOperationsResponse = SearchBatchOperationsResponses[keyof SearchBatchOperationsResponses]; ``` --- ## Type Alias: SearchBatchOperationsResponses ```ts type SearchBatchOperationsResponses = object; ``` ## Properties ### 200 ```ts 200: BatchOperationSearchQueryResult; ``` The batch operation search result. --- ## Type Alias: SearchClientsForGroupData ```ts type SearchClientsForGroupData = object; ``` ## Properties ### body? ```ts optional body?: GroupClientSearchQueryRequest; ``` --- ### path ```ts path: object; ``` #### groupId ```ts groupId: GroupId; ``` The group ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/groups/{groupId}/clients/search"; ``` --- ## Type Alias: SearchClientsForGroupError ```ts type SearchClientsForGroupError = SearchClientsForGroupErrors[keyof SearchClientsForGroupErrors]; ``` --- ## Type Alias: SearchClientsForGroupErrors ```ts type SearchClientsForGroupErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The group with the given ID was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchClientsForGroupResponse ```ts type SearchClientsForGroupResponse = SearchClientsForGroupResponses[keyof SearchClientsForGroupResponses]; ``` --- ## Type Alias: SearchClientsForGroupResponses ```ts type SearchClientsForGroupResponses = object; ``` ## Properties ### 200 ```ts 200: GroupClientSearchResult; ``` The clients assigned to the group. --- ## Type Alias: SearchClientsForRoleData ```ts type SearchClientsForRoleData = object; ``` ## Properties ### body? ```ts optional body?: RoleClientSearchQueryRequest; ``` --- ### path ```ts path: object; ``` #### roleId ```ts roleId: RoleId; ``` The role ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/roles/{roleId}/clients/search"; ``` --- ## Type Alias: SearchClientsForRoleError ```ts type SearchClientsForRoleError = SearchClientsForRoleErrors[keyof SearchClientsForRoleErrors]; ``` --- ## Type Alias: SearchClientsForRoleErrors ```ts type SearchClientsForRoleErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The role with the given ID was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchClientsForRoleResponse ```ts type SearchClientsForRoleResponse = SearchClientsForRoleResponses[keyof SearchClientsForRoleResponses]; ``` --- ## Type Alias: SearchClientsForRoleResponses ```ts type SearchClientsForRoleResponses = object; ``` ## Properties ### 200 ```ts 200: RoleClientSearchResult; ``` The clients with the assigned role. --- ## Type Alias: SearchClientsForTenantData ```ts type SearchClientsForTenantData = object; ``` ## Properties ### body? ```ts optional body?: TenantClientSearchQueryRequest; ``` --- ### path ```ts path: object; ``` #### tenantId ```ts tenantId: TenantId; ``` The unique identifier of the tenant. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/tenants/{tenantId}/clients/search"; ``` --- ## Type Alias: SearchClientsForTenantResponse ```ts type SearchClientsForTenantResponse = SearchClientsForTenantResponses[keyof SearchClientsForTenantResponses]; ``` --- ## Type Alias: SearchClientsForTenantResponses ```ts type SearchClientsForTenantResponses = object; ``` ## Properties ### 200 ```ts 200: TenantClientSearchResult; ``` The search result of users for the tenant. --- ## Type Alias: SearchClusterVariablesData ```ts type SearchClusterVariablesData = object; ``` ## Properties ### body? ```ts optional body?: ClusterVariableSearchQueryRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: object; ``` #### truncateValues? ```ts optional truncateValues?: boolean; ``` When true (default), long variable values in the response are truncated. When false, full variable values are returned. --- ### url ```ts url: "/cluster-variables/search"; ``` --- ## Type Alias: SearchClusterVariablesError ```ts type SearchClusterVariablesError = SearchClusterVariablesErrors[keyof SearchClusterVariablesErrors]; ``` --- ## Type Alias: SearchClusterVariablesErrors ```ts type SearchClusterVariablesErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchClusterVariablesResponse ```ts type SearchClusterVariablesResponse = SearchClusterVariablesResponses[keyof SearchClusterVariablesResponses]; ``` --- ## Type Alias: SearchClusterVariablesResponses ```ts type SearchClusterVariablesResponses = object; ``` ## Properties ### 200 ```ts 200: ClusterVariableSearchQueryResult; ``` The cluster variable search result. --- ## Type Alias: SearchCorrelatedMessageSubscriptionsData ```ts type SearchCorrelatedMessageSubscriptionsData = object; ``` ## Properties ### body? ```ts optional body?: CorrelatedMessageSubscriptionSearchQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/correlated-message-subscriptions/search"; ``` --- ## Type Alias: SearchCorrelatedMessageSubscriptionsError ```ts type SearchCorrelatedMessageSubscriptionsError = SearchCorrelatedMessageSubscriptionsErrors[keyof SearchCorrelatedMessageSubscriptionsErrors]; ``` --- ## Type Alias: SearchCorrelatedMessageSubscriptionsErrors ```ts type SearchCorrelatedMessageSubscriptionsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchCorrelatedMessageSubscriptionsResponse ```ts type SearchCorrelatedMessageSubscriptionsResponse = SearchCorrelatedMessageSubscriptionsResponses[keyof SearchCorrelatedMessageSubscriptionsResponses]; ``` --- ## Type Alias: SearchCorrelatedMessageSubscriptionsResponses ```ts type SearchCorrelatedMessageSubscriptionsResponses = object; ``` ## Properties ### 200 ```ts 200: CorrelatedMessageSubscriptionSearchQueryResult; ``` The correlated message subscriptions search result. --- ## Type Alias: SearchDecisionDefinitionsData ```ts type SearchDecisionDefinitionsData = object; ``` ## Properties ### body? ```ts optional body?: DecisionDefinitionSearchQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/decision-definitions/search"; ``` --- ## Type Alias: SearchDecisionDefinitionsError ```ts type SearchDecisionDefinitionsError = SearchDecisionDefinitionsErrors[keyof SearchDecisionDefinitionsErrors]; ``` --- ## Type Alias: SearchDecisionDefinitionsErrors ```ts type SearchDecisionDefinitionsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchDecisionDefinitionsResponse ```ts type SearchDecisionDefinitionsResponse = SearchDecisionDefinitionsResponses[keyof SearchDecisionDefinitionsResponses]; ``` --- ## Type Alias: SearchDecisionDefinitionsResponses ```ts type SearchDecisionDefinitionsResponses = object; ``` ## Properties ### 200 ```ts 200: DecisionDefinitionSearchQueryResult; ``` The decision definition search result. --- ## Type Alias: SearchDecisionInstancesData ```ts type SearchDecisionInstancesData = object; ``` ## Properties ### body? ```ts optional body?: DecisionInstanceSearchQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/decision-instances/search"; ``` --- ## Type Alias: SearchDecisionInstancesError ```ts type SearchDecisionInstancesError = SearchDecisionInstancesErrors[keyof SearchDecisionInstancesErrors]; ``` --- ## Type Alias: SearchDecisionInstancesErrors ```ts type SearchDecisionInstancesErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchDecisionInstancesResponse ```ts type SearchDecisionInstancesResponse = SearchDecisionInstancesResponses[keyof SearchDecisionInstancesResponses]; ``` --- ## Type Alias: SearchDecisionInstancesResponses ```ts type SearchDecisionInstancesResponses = object; ``` ## Properties ### 200 ```ts 200: DecisionInstanceSearchQueryResult; ``` The decision instance search result. --- ## Type Alias: SearchDecisionRequirementsData ```ts type SearchDecisionRequirementsData = object; ``` ## Properties ### body? ```ts optional body?: DecisionRequirementsSearchQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/decision-requirements/search"; ``` --- ## Type Alias: SearchDecisionRequirementsError ```ts type SearchDecisionRequirementsError = SearchDecisionRequirementsErrors[keyof SearchDecisionRequirementsErrors]; ``` --- ## Type Alias: SearchDecisionRequirementsErrors ```ts type SearchDecisionRequirementsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchDecisionRequirementsResponse ```ts type SearchDecisionRequirementsResponse = SearchDecisionRequirementsResponses[keyof SearchDecisionRequirementsResponses]; ``` --- ## Type Alias: SearchDecisionRequirementsResponses ```ts type SearchDecisionRequirementsResponses = object; ``` ## Properties ### 200 ```ts 200: DecisionRequirementsSearchQueryResult; ``` The decision requirements search result. --- ## Type Alias: SearchElementInstanceIncidentsData ```ts type SearchElementInstanceIncidentsData = object; ``` ## Properties ### body ```ts body: IncidentSearchQuery; ``` --- ### path ```ts path: object; ``` #### elementInstanceKey ```ts elementInstanceKey: ElementInstanceKey; ``` The unique key of the element instance to search incidents for. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/element-instances/{elementInstanceKey}/incidents/search"; ``` --- ## Type Alias: SearchElementInstanceIncidentsError ```ts type SearchElementInstanceIncidentsError = SearchElementInstanceIncidentsErrors[keyof SearchElementInstanceIncidentsErrors]; ``` --- ## Type Alias: SearchElementInstanceIncidentsErrors ```ts type SearchElementInstanceIncidentsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The element instance with the given key was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchElementInstanceIncidentsResponse ```ts type SearchElementInstanceIncidentsResponse = SearchElementInstanceIncidentsResponses[keyof SearchElementInstanceIncidentsResponses]; ``` --- ## Type Alias: SearchElementInstanceIncidentsResponses ```ts type SearchElementInstanceIncidentsResponses = object; ``` ## Properties ### 200 ```ts 200: IncidentSearchQueryResult; ``` The element instance incident search result. --- ## Type Alias: SearchElementInstancesData ```ts type SearchElementInstancesData = object; ``` ## Properties ### body? ```ts optional body?: ElementInstanceSearchQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/element-instances/search"; ``` --- ## Type Alias: SearchElementInstancesError ```ts type SearchElementInstancesError = SearchElementInstancesErrors[keyof SearchElementInstancesErrors]; ``` --- ## Type Alias: SearchElementInstancesErrors ```ts type SearchElementInstancesErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchElementInstancesResponse ```ts type SearchElementInstancesResponse = SearchElementInstancesResponses[keyof SearchElementInstancesResponses]; ``` --- ## Type Alias: SearchElementInstancesResponses ```ts type SearchElementInstancesResponses = object; ``` ## Properties ### 200 ```ts 200: ElementInstanceSearchQueryResult; ``` The element instance search result. --- ## Type Alias: SearchGlobalTaskListenersData ```ts type SearchGlobalTaskListenersData = object; ``` ## Properties ### body? ```ts optional body?: GlobalTaskListenerSearchQueryRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/global-task-listeners/search"; ``` --- ## Type Alias: SearchGlobalTaskListenersError ```ts type SearchGlobalTaskListenersError = SearchGlobalTaskListenersErrors[keyof SearchGlobalTaskListenersErrors]; ``` --- ## Type Alias: SearchGlobalTaskListenersErrors ```ts type SearchGlobalTaskListenersErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchGlobalTaskListenersResponse ```ts type SearchGlobalTaskListenersResponse = SearchGlobalTaskListenersResponses[keyof SearchGlobalTaskListenersResponses]; ``` --- ## Type Alias: SearchGlobalTaskListenersResponses ```ts type SearchGlobalTaskListenersResponses = object; ``` ## Properties ### 200 ```ts 200: GlobalTaskListenerSearchQueryResult; ``` The global user task listener search result. --- ## Type Alias: SearchGroupIdsForTenantData ```ts type SearchGroupIdsForTenantData = object; ``` ## Properties ### body? ```ts optional body?: TenantGroupSearchQueryRequest; ``` --- ### path ```ts path: object; ``` #### tenantId ```ts tenantId: TenantId; ``` The unique identifier of the tenant. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/tenants/{tenantId}/groups/search"; ``` --- ## Type Alias: SearchGroupIdsForTenantResponse ```ts type SearchGroupIdsForTenantResponse = SearchGroupIdsForTenantResponses[keyof SearchGroupIdsForTenantResponses]; ``` --- ## Type Alias: SearchGroupIdsForTenantResponses ```ts type SearchGroupIdsForTenantResponses = object; ``` ## Properties ### 200 ```ts 200: TenantGroupSearchResult; ``` The search result of groups for the tenant. --- ## Type Alias: SearchGroupsData ```ts type SearchGroupsData = object; ``` ## Properties ### body? ```ts optional body?: GroupSearchQueryRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/groups/search"; ``` --- ## Type Alias: SearchGroupsError ```ts type SearchGroupsError = SearchGroupsErrors[keyof SearchGroupsErrors]; ``` --- ## Type Alias: SearchGroupsErrors ```ts type SearchGroupsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchGroupsForRoleData ```ts type SearchGroupsForRoleData = object; ``` ## Properties ### body? ```ts optional body?: RoleGroupSearchQueryRequest; ``` --- ### path ```ts path: object; ``` #### roleId ```ts roleId: RoleId; ``` The role ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/roles/{roleId}/groups/search"; ``` --- ## Type Alias: SearchGroupsForRoleError ```ts type SearchGroupsForRoleError = SearchGroupsForRoleErrors[keyof SearchGroupsForRoleErrors]; ``` --- ## Type Alias: SearchGroupsForRoleErrors ```ts type SearchGroupsForRoleErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The role with the given ID was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchGroupsForRoleResponse ```ts type SearchGroupsForRoleResponse = SearchGroupsForRoleResponses[keyof SearchGroupsForRoleResponses]; ``` --- ## Type Alias: SearchGroupsForRoleResponses ```ts type SearchGroupsForRoleResponses = object; ``` ## Properties ### 200 ```ts 200: RoleGroupSearchResult; ``` The groups with assigned role. --- ## Type Alias: SearchGroupsResponse ```ts type SearchGroupsResponse = SearchGroupsResponses[keyof SearchGroupsResponses]; ``` --- ## Type Alias: SearchGroupsResponses ```ts type SearchGroupsResponses = object; ``` ## Properties ### 200 ```ts 200: GroupSearchQueryResult; ``` The groups search result. --- ## Type Alias: SearchIncidentsData ```ts type SearchIncidentsData = object; ``` ## Properties ### body? ```ts optional body?: IncidentSearchQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/incidents/search"; ``` --- ## Type Alias: SearchIncidentsError ```ts type SearchIncidentsError = SearchIncidentsErrors[keyof SearchIncidentsErrors]; ``` --- ## Type Alias: SearchIncidentsErrors ```ts type SearchIncidentsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchIncidentsResponse ```ts type SearchIncidentsResponse = SearchIncidentsResponses[keyof SearchIncidentsResponses]; ``` --- ## Type Alias: SearchIncidentsResponses ```ts type SearchIncidentsResponses = object; ``` ## Properties ### 200 ```ts 200: IncidentSearchQueryResult; ``` The incident search result. --- ## Type Alias: SearchJobsData ```ts type SearchJobsData = object; ``` ## Properties ### body? ```ts optional body?: JobSearchQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/jobs/search"; ``` --- ## Type Alias: SearchJobsError ```ts type SearchJobsError = SearchJobsErrors[keyof SearchJobsErrors]; ``` --- ## Type Alias: SearchJobsErrors ```ts type SearchJobsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchJobsResponse ```ts type SearchJobsResponse = SearchJobsResponses[keyof SearchJobsResponses]; ``` --- ## Type Alias: SearchJobsResponses ```ts type SearchJobsResponses = object; ``` ## Properties ### 200 ```ts 200: JobSearchQueryResult; ``` The job search result. --- ## Type Alias: SearchMappingRuleData ```ts type SearchMappingRuleData = object; ``` ## Properties ### body? ```ts optional body?: MappingRuleSearchQueryRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/mapping-rules/search"; ``` --- ## Type Alias: SearchMappingRuleError ```ts type SearchMappingRuleError = SearchMappingRuleErrors[keyof SearchMappingRuleErrors]; ``` --- ## Type Alias: SearchMappingRuleErrors ```ts type SearchMappingRuleErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchMappingRuleResponse ```ts type SearchMappingRuleResponse = SearchMappingRuleResponses[keyof SearchMappingRuleResponses]; ``` --- ## Type Alias: SearchMappingRuleResponses ```ts type SearchMappingRuleResponses = object; ``` ## Properties ### 200 ```ts 200: MappingRuleSearchQueryResult; ``` The mapping rule search result. --- ## Type Alias: SearchMappingRulesForGroupData ```ts type SearchMappingRulesForGroupData = object; ``` ## Properties ### body? ```ts optional body?: MappingRuleSearchQueryRequest; ``` --- ### path ```ts path: object; ``` #### groupId ```ts groupId: GroupId; ``` The group ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/groups/{groupId}/mapping-rules/search"; ``` --- ## Type Alias: SearchMappingRulesForGroupError ```ts type SearchMappingRulesForGroupError = SearchMappingRulesForGroupErrors[keyof SearchMappingRulesForGroupErrors]; ``` --- ## Type Alias: SearchMappingRulesForGroupErrors ```ts type SearchMappingRulesForGroupErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The group with the given ID was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchMappingRulesForGroupResponse ```ts type SearchMappingRulesForGroupResponse = SearchMappingRulesForGroupResponses[keyof SearchMappingRulesForGroupResponses]; ``` --- ## Type Alias: SearchMappingRulesForGroupResponses ```ts type SearchMappingRulesForGroupResponses = object; ``` ## Properties ### 200 ```ts 200: GroupMappingRuleSearchResult; ``` The mapping rules assigned to the group. --- ## Type Alias: SearchMappingRulesForRoleData ```ts type SearchMappingRulesForRoleData = object; ``` ## Properties ### body? ```ts optional body?: MappingRuleSearchQueryRequest; ``` --- ### path ```ts path: object; ``` #### roleId ```ts roleId: RoleId; ``` The role ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/roles/{roleId}/mapping-rules/search"; ``` --- ## Type Alias: SearchMappingRulesForRoleError ```ts type SearchMappingRulesForRoleError = SearchMappingRulesForRoleErrors[keyof SearchMappingRulesForRoleErrors]; ``` --- ## Type Alias: SearchMappingRulesForRoleErrors ```ts type SearchMappingRulesForRoleErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The role with the given ID was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchMappingRulesForRoleResponse ```ts type SearchMappingRulesForRoleResponse = SearchMappingRulesForRoleResponses[keyof SearchMappingRulesForRoleResponses]; ``` --- ## Type Alias: SearchMappingRulesForRoleResponses ```ts type SearchMappingRulesForRoleResponses = object; ``` ## Properties ### 200 ```ts 200: RoleMappingRuleSearchResult; ``` The mapping rules with assigned role. --- ## Type Alias: SearchMappingRulesForTenantData ```ts type SearchMappingRulesForTenantData = object; ``` ## Properties ### body? ```ts optional body?: MappingRuleSearchQueryRequest; ``` --- ### path ```ts path: object; ``` #### tenantId ```ts tenantId: TenantId; ``` The unique identifier of the tenant. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/tenants/{tenantId}/mapping-rules/search"; ``` --- ## Type Alias: SearchMappingRulesForTenantResponse ```ts type SearchMappingRulesForTenantResponse = SearchMappingRulesForTenantResponses[keyof SearchMappingRulesForTenantResponses]; ``` --- ## Type Alias: SearchMappingRulesForTenantResponses ```ts type SearchMappingRulesForTenantResponses = object; ``` ## Properties ### 200 ```ts 200: TenantMappingRuleSearchResult; ``` The search result of MappingRules for the tenant. --- ## Type Alias: SearchMessageSubscriptionsData ```ts type SearchMessageSubscriptionsData = object; ``` ## Properties ### body? ```ts optional body?: MessageSubscriptionSearchQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/message-subscriptions/search"; ``` --- ## Type Alias: SearchMessageSubscriptionsError ```ts type SearchMessageSubscriptionsError = SearchMessageSubscriptionsErrors[keyof SearchMessageSubscriptionsErrors]; ``` --- ## Type Alias: SearchMessageSubscriptionsErrors ```ts type SearchMessageSubscriptionsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchMessageSubscriptionsResponse ```ts type SearchMessageSubscriptionsResponse = SearchMessageSubscriptionsResponses[keyof SearchMessageSubscriptionsResponses]; ``` --- ## Type Alias: SearchMessageSubscriptionsResponses ```ts type SearchMessageSubscriptionsResponses = object; ``` ## Properties ### 200 ```ts 200: MessageSubscriptionSearchQueryResult; ``` The message subscription search result. --- ## Type Alias: SearchProcessDefinitionsData ```ts type SearchProcessDefinitionsData = object; ``` ## Properties ### body? ```ts optional body?: ProcessDefinitionSearchQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-definitions/search"; ``` --- ## Type Alias: SearchProcessDefinitionsError ```ts type SearchProcessDefinitionsError = SearchProcessDefinitionsErrors[keyof SearchProcessDefinitionsErrors]; ``` --- ## Type Alias: SearchProcessDefinitionsErrors ```ts type SearchProcessDefinitionsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchProcessDefinitionsResponse ```ts type SearchProcessDefinitionsResponse = SearchProcessDefinitionsResponses[keyof SearchProcessDefinitionsResponses]; ``` --- ## Type Alias: SearchProcessDefinitionsResponses ```ts type SearchProcessDefinitionsResponses = object; ``` ## Properties ### 200 ```ts 200: ProcessDefinitionSearchQueryResult; ``` The process definition search result. --- ## Type Alias: SearchProcessInstanceIncidentsData ```ts type SearchProcessInstanceIncidentsData = object; ``` ## Properties ### body? ```ts optional body?: IncidentSearchQuery; ``` --- ### path ```ts path: object; ``` #### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The assigned key of the process instance, which acts as a unique identifier for this process instance. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-instances/{processInstanceKey}/incidents/search"; ``` --- ## Type Alias: SearchProcessInstanceIncidentsError ```ts type SearchProcessInstanceIncidentsError = SearchProcessInstanceIncidentsErrors[keyof SearchProcessInstanceIncidentsErrors]; ``` --- ## Type Alias: SearchProcessInstanceIncidentsErrors ```ts type SearchProcessInstanceIncidentsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The process instance with the given key was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchProcessInstanceIncidentsResponse ```ts type SearchProcessInstanceIncidentsResponse = SearchProcessInstanceIncidentsResponses[keyof SearchProcessInstanceIncidentsResponses]; ``` --- ## Type Alias: SearchProcessInstanceIncidentsResponses ```ts type SearchProcessInstanceIncidentsResponses = object; ``` ## Properties ### 200 ```ts 200: IncidentSearchQueryResult; ``` The process instance search result. --- ## Type Alias: SearchProcessInstancesData ```ts type SearchProcessInstancesData = object; ``` ## Properties ### body? ```ts optional body?: ProcessInstanceSearchQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/process-instances/search"; ``` --- ## Type Alias: SearchProcessInstancesError ```ts type SearchProcessInstancesError = SearchProcessInstancesErrors[keyof SearchProcessInstancesErrors]; ``` --- ## Type Alias: SearchProcessInstancesErrors ```ts type SearchProcessInstancesErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchProcessInstancesResponse ```ts type SearchProcessInstancesResponse = SearchProcessInstancesResponses[keyof SearchProcessInstancesResponses]; ``` --- ## Type Alias: SearchProcessInstancesResponses ```ts type SearchProcessInstancesResponses = object; ``` ## Properties ### 200 ```ts 200: ProcessInstanceSearchQueryResult; ``` The process instance search result. --- ## Type Alias: SearchQueryPageRequest ```ts type SearchQueryPageRequest = | LimitPagination | OffsetPagination | CursorForwardPagination | CursorBackwardPagination; ``` Pagination criteria. Can use offset-based pagination (from/limit) OR cursor-based pagination (after/before + limit), but not both. --- ## Type Alias: SearchQueryPageResponse ```ts type SearchQueryPageResponse = object; ``` Pagination information about the search results. ## Properties ### endCursor ```ts endCursor: EndCursor | null; ``` The cursor value for getting the next page of results. Use this in the `after` field of an ensuing request. --- ### hasMoreTotalItems ```ts hasMoreTotalItems: boolean; ``` Indicates whether the `totalItems` value has been capped due to system limits. When true, `totalItems` is a lower bound and the actual number of matching items is greater than the reported value. --- ### startCursor ```ts startCursor: StartCursor | null; ``` The cursor value for getting the previous page of results. Use this in the `before` field of an ensuing request. --- ### totalItems ```ts totalItems: number; ``` Total items matching the criteria. --- ## Type Alias: SearchQueryRequest ```ts type SearchQueryRequest = object; ``` ## Properties ### page? ```ts optional page?: SearchQueryPageRequest; ``` Pagination criteria. --- ## Type Alias: SearchQueryResponse ```ts type SearchQueryResponse = object; ``` ## Properties ### page ```ts page: SearchQueryPageResponse; ``` --- ## Type Alias: SearchResourcesData ```ts type SearchResourcesData = object; ``` ## Properties ### body? ```ts optional body?: ResourceSearchQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/resources/search"; ``` --- ## Type Alias: SearchResourcesError ```ts type SearchResourcesError = SearchResourcesErrors[keyof SearchResourcesErrors]; ``` --- ## Type Alias: SearchResourcesErrors ```ts type SearchResourcesErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchResourcesResponse ```ts type SearchResourcesResponse = SearchResourcesResponses[keyof SearchResourcesResponses]; ``` --- ## Type Alias: SearchResourcesResponses ```ts type SearchResourcesResponses = object; ``` ## Properties ### 200 ```ts 200: ResourceSearchQueryResult; ``` The resource search result. --- ## Type Alias: SearchRolesData ```ts type SearchRolesData = object; ``` ## Properties ### body? ```ts optional body?: RoleSearchQueryRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/roles/search"; ``` --- ## Type Alias: SearchRolesError ```ts type SearchRolesError = SearchRolesErrors[keyof SearchRolesErrors]; ``` --- ## Type Alias: SearchRolesErrors ```ts type SearchRolesErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchRolesForGroupData ```ts type SearchRolesForGroupData = object; ``` ## Properties ### body? ```ts optional body?: RoleSearchQueryRequest; ``` --- ### path ```ts path: object; ``` #### groupId ```ts groupId: GroupId; ``` The group ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/groups/{groupId}/roles/search"; ``` --- ## Type Alias: SearchRolesForGroupError ```ts type SearchRolesForGroupError = SearchRolesForGroupErrors[keyof SearchRolesForGroupErrors]; ``` --- ## Type Alias: SearchRolesForGroupErrors ```ts type SearchRolesForGroupErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The group with the given ID was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchRolesForGroupResponse ```ts type SearchRolesForGroupResponse = SearchRolesForGroupResponses[keyof SearchRolesForGroupResponses]; ``` --- ## Type Alias: SearchRolesForGroupResponses ```ts type SearchRolesForGroupResponses = object; ``` ## Properties ### 200 ```ts 200: GroupRoleSearchResult; ``` The roles assigned to the group. --- ## Type Alias: SearchRolesForTenantData ```ts type SearchRolesForTenantData = object; ``` ## Properties ### body? ```ts optional body?: RoleSearchQueryRequest; ``` --- ### path ```ts path: object; ``` #### tenantId ```ts tenantId: TenantId; ``` The unique identifier of the tenant. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/tenants/{tenantId}/roles/search"; ``` --- ## Type Alias: SearchRolesForTenantResponse ```ts type SearchRolesForTenantResponse = SearchRolesForTenantResponses[keyof SearchRolesForTenantResponses]; ``` --- ## Type Alias: SearchRolesForTenantResponses ```ts type SearchRolesForTenantResponses = object; ``` ## Properties ### 200 ```ts 200: TenantRoleSearchResult; ``` The search result of roles for the tenant. --- ## Type Alias: SearchRolesResponse ```ts type SearchRolesResponse = SearchRolesResponses[keyof SearchRolesResponses]; ``` --- ## Type Alias: SearchRolesResponses ```ts type SearchRolesResponses = object; ``` ## Properties ### 200 ```ts 200: RoleSearchQueryResult; ``` The roles search result. --- ## Type Alias: SearchTenantsData ```ts type SearchTenantsData = object; ``` ## Properties ### body? ```ts optional body?: TenantSearchQueryRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/tenants/search"; ``` --- ## Type Alias: SearchTenantsError ```ts type SearchTenantsError = SearchTenantsErrors[keyof SearchTenantsErrors]; ``` --- ## Type Alias: SearchTenantsErrors ```ts type SearchTenantsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Not found --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchTenantsResponse ```ts type SearchTenantsResponse = SearchTenantsResponses[keyof SearchTenantsResponses]; ``` --- ## Type Alias: SearchTenantsResponses ```ts type SearchTenantsResponses = object; ``` ## Properties ### 200 ```ts 200: TenantSearchQueryResult; ``` The tenants search result --- ## Type Alias: SearchUserTaskAuditLogsData ```ts type SearchUserTaskAuditLogsData = object; ``` ## Properties ### body? ```ts optional body?: UserTaskAuditLogSearchQueryRequest; ``` --- ### path ```ts path: object; ``` #### userTaskKey ```ts userTaskKey: UserTaskKey; ``` The key of the user task. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/user-tasks/{userTaskKey}/audit-logs/search"; ``` --- ## Type Alias: SearchUserTaskAuditLogsError ```ts type SearchUserTaskAuditLogsError = SearchUserTaskAuditLogsErrors[keyof SearchUserTaskAuditLogsErrors]; ``` --- ## Type Alias: SearchUserTaskAuditLogsErrors ```ts type SearchUserTaskAuditLogsErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchUserTaskAuditLogsResponse ```ts type SearchUserTaskAuditLogsResponse = SearchUserTaskAuditLogsResponses[keyof SearchUserTaskAuditLogsResponses]; ``` --- ## Type Alias: SearchUserTaskAuditLogsResponses ```ts type SearchUserTaskAuditLogsResponses = object; ``` ## Properties ### 200 ```ts 200: AuditLogSearchQueryResult; ``` The user task audit log search result. --- ## Type Alias: SearchUserTaskEffectiveVariablesData ```ts type SearchUserTaskEffectiveVariablesData = object; ``` ## Properties ### body? ```ts optional body?: object; ``` User task effective variable search query request. Uses offset-based pagination only. #### filter? ```ts optional filter?: UserTaskVariableFilter; ``` The user task variable search filters. #### page? ```ts optional page?: OffsetPagination; ``` Pagination parameters. #### sort? ```ts optional sort?: UserTaskVariableSearchQuerySortRequest[]; ``` Sort field criteria. --- ### path ```ts path: object; ``` #### userTaskKey ```ts userTaskKey: UserTaskKey; ``` The key of the user task. --- ### query? ```ts optional query?: object; ``` #### truncateValues? ```ts optional truncateValues?: boolean; ``` When true (default), long variable values in the response are truncated. When false, full variable values are returned. --- ### url ```ts url: "/user-tasks/{userTaskKey}/effective-variables/search"; ``` --- ## Type Alias: SearchUserTaskEffectiveVariablesError ```ts type SearchUserTaskEffectiveVariablesError = SearchUserTaskEffectiveVariablesErrors[keyof SearchUserTaskEffectiveVariablesErrors]; ``` --- ## Type Alias: SearchUserTaskEffectiveVariablesErrors ```ts type SearchUserTaskEffectiveVariablesErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchUserTaskEffectiveVariablesResponse ```ts type SearchUserTaskEffectiveVariablesResponse = SearchUserTaskEffectiveVariablesResponses[keyof SearchUserTaskEffectiveVariablesResponses]; ``` --- ## Type Alias: SearchUserTaskEffectiveVariablesResponses ```ts type SearchUserTaskEffectiveVariablesResponses = object; ``` ## Properties ### 200 ```ts 200: VariableSearchQueryResult; ``` The user task effective variable search result. --- ## Type Alias: SearchUserTaskVariablesData ```ts type SearchUserTaskVariablesData = object; ``` ## Properties ### body? ```ts optional body?: UserTaskVariableSearchQueryRequest; ``` --- ### path ```ts path: object; ``` #### userTaskKey ```ts userTaskKey: UserTaskKey; ``` The key of the user task. --- ### query? ```ts optional query?: object; ``` #### truncateValues? ```ts optional truncateValues?: boolean; ``` When true (default), long variable values in the response are truncated. When false, full variable values are returned. --- ### url ```ts url: "/user-tasks/{userTaskKey}/variables/search"; ``` --- ## Type Alias: SearchUserTaskVariablesError ```ts type SearchUserTaskVariablesError = SearchUserTaskVariablesErrors[keyof SearchUserTaskVariablesErrors]; ``` --- ## Type Alias: SearchUserTaskVariablesErrors ```ts type SearchUserTaskVariablesErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchUserTaskVariablesResponse ```ts type SearchUserTaskVariablesResponse = SearchUserTaskVariablesResponses[keyof SearchUserTaskVariablesResponses]; ``` --- ## Type Alias: SearchUserTaskVariablesResponses ```ts type SearchUserTaskVariablesResponses = object; ``` ## Properties ### 200 ```ts 200: VariableSearchQueryResult; ``` The user task variable search result. --- ## Type Alias: SearchUserTasksData ```ts type SearchUserTasksData = object; ``` ## Properties ### body? ```ts optional body?: UserTaskSearchQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/user-tasks/search"; ``` --- ## Type Alias: SearchUserTasksError ```ts type SearchUserTasksError = SearchUserTasksErrors[keyof SearchUserTasksErrors]; ``` --- ## Type Alias: SearchUserTasksErrors ```ts type SearchUserTasksErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchUserTasksResponse ```ts type SearchUserTasksResponse = SearchUserTasksResponses[keyof SearchUserTasksResponses]; ``` --- ## Type Alias: SearchUserTasksResponses ```ts type SearchUserTasksResponses = object; ``` ## Properties ### 200 ```ts 200: UserTaskSearchQueryResult; ``` The user task search result. --- ## Type Alias: SearchUsersData ```ts type SearchUsersData = object; ``` ## Properties ### body? ```ts optional body?: UserSearchQueryRequest; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/users/search"; ``` --- ## Type Alias: SearchUsersError ```ts type SearchUsersError = SearchUsersErrors[keyof SearchUsersErrors]; ``` --- ## Type Alias: SearchUsersErrors ```ts type SearchUsersErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchUsersForGroupData ```ts type SearchUsersForGroupData = object; ``` ## Properties ### body? ```ts optional body?: GroupUserSearchQueryRequest; ``` --- ### path ```ts path: object; ``` #### groupId ```ts groupId: GroupId; ``` The group ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/groups/{groupId}/users/search"; ``` --- ## Type Alias: SearchUsersForGroupError ```ts type SearchUsersForGroupError = SearchUsersForGroupErrors[keyof SearchUsersForGroupErrors]; ``` --- ## Type Alias: SearchUsersForGroupErrors ```ts type SearchUsersForGroupErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The group with the given ID was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchUsersForGroupResponse ```ts type SearchUsersForGroupResponse = SearchUsersForGroupResponses[keyof SearchUsersForGroupResponses]; ``` --- ## Type Alias: SearchUsersForGroupResponses ```ts type SearchUsersForGroupResponses = object; ``` ## Properties ### 200 ```ts 200: GroupUserSearchResult; ``` The users assigned to the group. --- ## Type Alias: SearchUsersForRoleData ```ts type SearchUsersForRoleData = object; ``` ## Properties ### body? ```ts optional body?: RoleUserSearchQueryRequest; ``` --- ### path ```ts path: object; ``` #### roleId ```ts roleId: RoleId; ``` The role ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/roles/{roleId}/users/search"; ``` --- ## Type Alias: SearchUsersForRoleError ```ts type SearchUsersForRoleError = SearchUsersForRoleErrors[keyof SearchUsersForRoleErrors]; ``` --- ## Type Alias: SearchUsersForRoleErrors ```ts type SearchUsersForRoleErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The role with the given ID was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchUsersForRoleResponse ```ts type SearchUsersForRoleResponse = SearchUsersForRoleResponses[keyof SearchUsersForRoleResponses]; ``` --- ## Type Alias: SearchUsersForRoleResponses ```ts type SearchUsersForRoleResponses = object; ``` ## Properties ### 200 ```ts 200: RoleUserSearchResult; ``` The users with the assigned role. --- ## Type Alias: SearchUsersForTenantData ```ts type SearchUsersForTenantData = object; ``` ## Properties ### body? ```ts optional body?: TenantUserSearchQueryRequest; ``` --- ### path ```ts path: object; ``` #### tenantId ```ts tenantId: TenantId; ``` The unique identifier of the tenant. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/tenants/{tenantId}/users/search"; ``` --- ## Type Alias: SearchUsersForTenantResponse ```ts type SearchUsersForTenantResponse = SearchUsersForTenantResponses[keyof SearchUsersForTenantResponses]; ``` --- ## Type Alias: SearchUsersForTenantResponses ```ts type SearchUsersForTenantResponses = object; ``` ## Properties ### 200 ```ts 200: TenantUserSearchResult; ``` The search result of users for the tenant. --- ## Type Alias: SearchUsersResponse ```ts type SearchUsersResponse = SearchUsersResponses[keyof SearchUsersResponses]; ``` --- ## Type Alias: SearchUsersResponses ```ts type SearchUsersResponses = object; ``` ## Properties ### 200 ```ts 200: UserSearchResult; ``` The user search result. --- ## Type Alias: SearchVariablesData ```ts type SearchVariablesData = object; ``` ## Properties ### body? ```ts optional body?: VariableSearchQuery; ``` --- ### path? ```ts optional path?: never; ``` --- ### query? ```ts optional query?: object; ``` #### truncateValues? ```ts optional truncateValues?: boolean; ``` When true (default), long variable values in the response are truncated. When false, full variable values are returned. --- ### url ```ts url: "/variables/search"; ``` --- ## Type Alias: SearchVariablesError ```ts type SearchVariablesError = SearchVariablesErrors[keyof SearchVariablesErrors]; ``` --- ## Type Alias: SearchVariablesErrors ```ts type SearchVariablesErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: SearchVariablesResponse ```ts type SearchVariablesResponse = SearchVariablesResponses[keyof SearchVariablesResponses]; ``` --- ## Type Alias: SearchVariablesResponses ```ts type SearchVariablesResponses = object; ``` ## Properties ### 200 ```ts 200: VariableSearchQueryResult; ``` The variable search result. --- ## Type Alias: SetVariableRequest ```ts type SetVariableRequest = object; ``` ## Properties ### local? ```ts optional local?: boolean; ``` If set to `true`, the variables are merged strictly into the local scope (as specified by the `elementInstanceKey`). Otherwise, the variables are propagated to upper scopes and set at the outermost one. Let's consider the following example: There are two scopes '1' and '2'. Scope '1' is the parent scope of '2'. The effective variables of the scopes are: 1 => { "foo" : 2 } 2 => { "bar" : 1 } An update request with elementInstanceKey as '2', variables { "foo": 5 }, and local set to `true` leaves scope '1' unchanged and adjusts scope '2' to { "bar": 1, "foo": 5 }. By default, with local set to `false`, scope '1' will be { "foo": 5 } and scope '2' will be { "bar": 1 }. --- ### operationReference? ```ts optional operationReference?: OperationReference; ``` --- ### variables ```ts variables: object; ``` JSON object representing the variables to set in the element’s scope. #### Index Signature ```ts [key: string]: unknown ``` --- ## Type Alias: SignalBroadcastRequest ```ts type SignalBroadcastRequest = object; ``` ## Properties ### signalName ```ts signalName: string; ``` The name of the signal to broadcast. --- ### tenantId? ```ts optional tenantId?: TenantId; ``` The ID of the tenant that owns the signal. --- ### variables? ```ts optional variables?: object; ``` The signal variables as a JSON object. #### Index Signature ```ts [key: string]: unknown ``` --- ## Type Alias: SignalBroadcastResult ```ts type SignalBroadcastResult = object; ``` ## Properties ### signalKey ```ts signalKey: SignalKey; ``` The key of the broadcasted signal. --- ### tenantId ```ts tenantId: TenantId; ``` The tenant ID of the signal that was broadcast. --- ## Type Alias: SignalKey ```ts type SignalKey = CamundaKey<"SignalKey">; ``` System-generated key for an signal. --- ## Type Alias: SortOrderEnum ```ts type SortOrderEnum = (typeof SortOrderEnum)[keyof typeof SortOrderEnum]; ``` The order in which to sort the related field. --- ## Type Alias: SourceElementIdInstruction ```ts type SourceElementIdInstruction = object; ``` Defines an instruction with a sourceElementId. The move instruction with this sourceType will terminate all active element instances with the sourceElementId and activate a new element instance for each terminated one at targetElementId. ## Properties ### sourceElementId ```ts sourceElementId: ElementId; ``` The id of the source element for the move instruction. --- ### sourceType ```ts sourceType: string; ``` The type of source element instruction. --- ## Type Alias: SourceElementInstanceKeyInstruction ```ts type SourceElementInstanceKeyInstruction = object; ``` Defines an instruction with a sourceElementInstanceKey. The move instruction with this sourceType will terminate one active element instance with the sourceElementInstanceKey and activate a new element instance at targetElementId. ## Properties ### sourceElementInstanceKey ```ts sourceElementInstanceKey: ElementInstanceKey; ``` The source element instance key for the move instruction. --- ### sourceType ```ts sourceType: string; ``` The type of source element instruction. --- ## Type Alias: SourceElementInstruction ```ts type SourceElementInstruction = | (object & SourceElementIdInstruction) | (object & SourceElementInstanceKeyInstruction); ``` Defines the source element identifier for the move instruction. It can either be a sourceElementId, or sourceElementInstanceKey. --- ## Type Alias: StartCursor ```ts type StartCursor = CamundaKey<"StartCursor">; ``` The start cursor in a search query result set. --- ## Type Alias: StatusMetric ```ts type StatusMetric = object; ``` Metric for a single job status. ## Properties ### count ```ts count: number; ``` Number of jobs in this status. --- ### lastUpdatedAt ```ts lastUpdatedAt: string | null; ``` ISO 8601 timestamp of the last update for this status. --- ## Type Alias: StringFilterProperty ```ts type StringFilterProperty = string | AdvancedStringFilter; ``` String property with full advanced search capabilities. --- ## Type Alias: SuspendBatchOperationData ```ts type SuspendBatchOperationData = object; ``` ## Properties ### body? ```ts optional body?: unknown; ``` --- ### path ```ts path: object; ``` #### batchOperationKey ```ts batchOperationKey: BatchOperationKey; ``` The key (or operate legacy ID) of the batch operation. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/batch-operations/{batchOperationKey}/suspension"; ``` --- ## Type Alias: SuspendBatchOperationError ```ts type SuspendBatchOperationError = SuspendBatchOperationErrors[keyof SuspendBatchOperationErrors]; ``` --- ## Type Alias: SuspendBatchOperationErrors ```ts type SuspendBatchOperationErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Not found. The batch operation was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: SuspendBatchOperationResponse ```ts type SuspendBatchOperationResponse = SuspendBatchOperationResponses[keyof SuspendBatchOperationResponses]; ``` --- ## Type Alias: SuspendBatchOperationResponses ```ts type SuspendBatchOperationResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The batch operation pause request was created. --- ## Type Alias: SystemConfigurationResponse ```ts type SystemConfigurationResponse = object; ``` Envelope for all system configuration sections. Each property represents a feature area. ## Properties ### authentication ```ts authentication: AuthenticationConfigurationResponse; ``` --- ### cloud ```ts cloud: CloudConfigurationResponse; ``` --- ### components ```ts components: ComponentsConfigurationResponse; ``` --- ### deployment ```ts deployment: DeploymentConfigurationResponse; ``` --- ### jobMetrics ```ts jobMetrics: JobMetricsConfigurationResponse; ``` --- ## Type Alias: Tag ```ts type Tag = CamundaKey<"Tag">; ``` A tag. Needs to start with a letter; then alphanumerics, `_`, `-`, `:`, or `.`; length ≤ 100. --- ## Type Alias: TagSet ```ts type TagSet = Tag[] & object; ``` List of tags. Tags need to start with a letter; then alphanumerics, `_`, `-`, `:`, or `.`; length ≤ 100. ## Type Declaration ### length ```ts readonly length: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10; ``` --- ## Type Alias: TenantClientResult ```ts type TenantClientResult = object; ``` ## Properties ### clientId ```ts clientId: ClientId; ``` The ID of the client. --- ## Type Alias: TenantClientSearchQueryRequest ```ts type TenantClientSearchQueryRequest = SearchQueryRequest & object; ``` ## Type Declaration ### sort? ```ts optional sort?: TenantClientSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: TenantClientSearchQuerySortRequest ```ts type TenantClientSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: "clientId"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: TenantClientSearchResult ```ts type TenantClientSearchResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: TenantClientResult[]; ``` The matching clients. --- ## Type Alias: TenantCreateRequest ```ts type TenantCreateRequest = object; ``` ## Properties ### description? ```ts optional description?: string; ``` The description of the tenant. --- ### name ```ts name: string; ``` The name of the tenant. --- ### tenantId ```ts tenantId: TenantId; ``` The unique ID for the tenant. Must be 31 characters or less and match `^[\w.-]{1,31}$` (word characters, `.`, `-`). The literal `` is also accepted as the default-tenant alias. --- ## Type Alias: TenantCreateResult ```ts type TenantCreateResult = object; ``` ## Properties ### description ```ts description: string | null; ``` The description of the tenant. --- ### name ```ts name: string; ``` The name of the tenant. --- ### tenantId ```ts tenantId: TenantId; ``` The unique identifier of the created tenant. --- ## Type Alias: TenantFilter ```ts type TenantFilter = object; ``` Tenant filter request ## Properties ### name? ```ts optional name?: string; ``` The name of the tenant. --- ### tenantId? ```ts optional tenantId?: TenantId; ``` The unique identifier of the tenant. --- ## Type Alias: TenantFilterEnum ```ts type TenantFilterEnum = (typeof TenantFilterEnum)[keyof typeof TenantFilterEnum]; ``` The tenant filtering strategy for job activation. Determines whether to use tenant IDs provided in the request or tenant IDs assigned to the authenticated principal. --- ## Type Alias: TenantGroupResult ```ts type TenantGroupResult = object; ``` ## Properties ### groupId ```ts groupId: GroupId; ``` The group ID. --- ## Type Alias: TenantGroupSearchQueryRequest ```ts type TenantGroupSearchQueryRequest = SearchQueryRequest & object; ``` ## Type Declaration ### sort? ```ts optional sort?: TenantGroupSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: TenantGroupSearchQuerySortRequest ```ts type TenantGroupSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: "groupId"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: TenantGroupSearchResult ```ts type TenantGroupSearchResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: TenantGroupResult[]; ``` The matching groups. --- ## Type Alias: TenantId ```ts type TenantId = CamundaKey<"TenantId">; ``` The unique identifier of the tenant. --- ## Type Alias: TenantMappingRuleSearchResult ```ts type TenantMappingRuleSearchResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: MappingRuleResult[]; ``` The matching mapping rules. --- ## Type Alias: TenantResult ```ts type TenantResult = object; ``` Tenant search response item. ## Properties ### description ```ts description: string | null; ``` The tenant description. --- ### name ```ts name: string; ``` The tenant name. --- ### tenantId ```ts tenantId: TenantId; ``` The unique identifier of the tenant. --- ## Type Alias: TenantRoleSearchResult ```ts type TenantRoleSearchResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: RoleResult[]; ``` The matching roles. --- ## Type Alias: TenantSearchQueryRequest ```ts type TenantSearchQueryRequest = SearchQueryRequest & object; ``` Tenant search request ## Type Declaration ### filter? ```ts optional filter?: TenantFilter; ``` The tenant search filters. ### sort? ```ts optional sort?: TenantSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: TenantSearchQueryResult ```ts type TenantSearchQueryResult = SearchQueryResponse & object; ``` Tenant search response. ## Type Declaration ### items ```ts items: TenantResult[]; ``` The matching tenants. --- ## Type Alias: TenantSearchQuerySortRequest ```ts type TenantSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: "key" | "name" | "tenantId"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: TenantUpdateRequest ```ts type TenantUpdateRequest = object; ``` ## Properties ### description? ```ts optional description?: string; ``` The new description of the tenant. --- ### name ```ts name: string; ``` The new name of the tenant. --- ## Type Alias: TenantUpdateResult ```ts type TenantUpdateResult = object; ``` ## Properties ### description ```ts description: string | null; ``` The description of the tenant. --- ### name ```ts name: string; ``` The name of the tenant. --- ### tenantId ```ts tenantId: TenantId; ``` The unique identifier of the updated tenant. --- ## Type Alias: TenantUserResult ```ts type TenantUserResult = object; ``` ## Properties ### username ```ts username: Username; ``` --- ## Type Alias: TenantUserSearchQueryRequest ```ts type TenantUserSearchQueryRequest = SearchQueryRequest & object; ``` ## Type Declaration ### sort? ```ts optional sort?: TenantUserSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: TenantUserSearchQuerySortRequest ```ts type TenantUserSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: "username"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: TenantUserSearchResult ```ts type TenantUserSearchResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: TenantUserResult[]; ``` The matching users. --- ## Type Alias: ThreadedJob ```ts type ThreadedJob = Omit; ``` The job object received by a threaded handler. Same shape as EnrichedActivatedJob but without the logger (not available across threads). --- ## Type Alias: ThreadedJobHandler ```ts type ThreadedJobHandler = ( job, client ) => Promise | JobActionReceipt; ``` Handler function signature for threaded job workers. Import this type in your handler module for full intellisense on `job` and `client`: ```ts const handler: ThreadedJobHandler = async (job, client) => { // full intellisense for job.variables, job.complete(), client.publishMessage(), etc. return job.complete({ result: "done" }); }; export default handler; ``` ## Parameters ### job [`ThreadedJob`](ThreadedJob.md) ### client [`CamundaClient`](../classes/CamundaClient.md) ## Returns \| `Promise`\<[`JobActionReceipt`](JobActionReceipt.md)\> \| [`JobActionReceipt`](JobActionReceipt.md) --- ## Type Alias: ThrowJobErrorData ```ts type ThrowJobErrorData = object; ``` ## Properties ### body ```ts body: JobErrorRequest; ``` --- ### path ```ts path: object; ``` #### jobKey ```ts jobKey: JobKey; ``` The key of the job. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/jobs/{jobKey}/error"; ``` --- ## Type Alias: ThrowJobErrorError ```ts type ThrowJobErrorError = ThrowJobErrorErrors[keyof ThrowJobErrorErrors]; ``` --- ## Type Alias: ThrowJobErrorErrors ```ts type ThrowJobErrorErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 404 ```ts 404: ProblemDetail; ``` The job with the given key was not found or is not activated. --- ### 409 ```ts 409: ProblemDetail; ``` The job with the given key is in the wrong state currently. More details are provided in the response body. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: ThrowJobErrorResponse ```ts type ThrowJobErrorResponse = ThrowJobErrorResponses[keyof ThrowJobErrorResponses]; ``` --- ## Type Alias: ThrowJobErrorResponses ```ts type ThrowJobErrorResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` An error is thrown for the job. --- ## Type Alias: TopologyResponse ```ts type TopologyResponse = object; ``` The response of a topology request. ## Properties ### brokers ```ts brokers: BrokerInfo[]; ``` A list of brokers that are part of this cluster. --- ### clusterId ```ts clusterId: string | null; ``` The cluster Id. --- ### clusterSize ```ts clusterSize: number; ``` The number of brokers in the cluster. --- ### gatewayVersion ```ts gatewayVersion: string; ``` The version of the Zeebe Gateway. --- ### lastCompletedChangeId ```ts lastCompletedChangeId: string; ``` ID of the last completed change --- ### partitionsCount ```ts partitionsCount: number; ``` The number of partitions are spread across the cluster. --- ### replicationFactor ```ts replicationFactor: number; ``` The configured replication factor for this cluster. --- ## Type Alias: UnassignClientFromGroupData ```ts type UnassignClientFromGroupData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### clientId ```ts clientId: ClientId; ``` The client ID. #### groupId ```ts groupId: GroupId; ``` The group ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/groups/{groupId}/clients/{clientId}"; ``` --- ## Type Alias: UnassignClientFromGroupError ```ts type UnassignClientFromGroupError = UnassignClientFromGroupErrors[keyof UnassignClientFromGroupErrors]; ``` --- ## Type Alias: UnassignClientFromGroupErrors ```ts type UnassignClientFromGroupErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The group with the given ID was not found, or the client is not assigned to this group. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: UnassignClientFromGroupResponse ```ts type UnassignClientFromGroupResponse = UnassignClientFromGroupResponses[keyof UnassignClientFromGroupResponses]; ``` --- ## Type Alias: UnassignClientFromGroupResponses ```ts type UnassignClientFromGroupResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The client was unassigned successfully from the group. --- ## Type Alias: UnassignClientFromTenantData ```ts type UnassignClientFromTenantData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### clientId ```ts clientId: ClientId; ``` The unique identifier of the application. #### tenantId ```ts tenantId: TenantId; ``` The unique identifier of the tenant. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/tenants/{tenantId}/clients/{clientId}"; ``` --- ## Type Alias: UnassignClientFromTenantError ```ts type UnassignClientFromTenantError = UnassignClientFromTenantErrors[keyof UnassignClientFromTenantErrors]; ``` --- ## Type Alias: UnassignClientFromTenantErrors ```ts type UnassignClientFromTenantErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The tenant does not exist or the client was not assigned to it. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: UnassignClientFromTenantResponse ```ts type UnassignClientFromTenantResponse = UnassignClientFromTenantResponses[keyof UnassignClientFromTenantResponses]; ``` --- ## Type Alias: UnassignClientFromTenantResponses ```ts type UnassignClientFromTenantResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The client was successfully unassigned from the tenant. --- ## Type Alias: UnassignGroupFromTenantData ```ts type UnassignGroupFromTenantData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### groupId ```ts groupId: GroupId; ``` The unique identifier of the group. #### tenantId ```ts tenantId: TenantId; ``` The unique identifier of the tenant. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/tenants/{tenantId}/groups/{groupId}"; ``` --- ## Type Alias: UnassignGroupFromTenantError ```ts type UnassignGroupFromTenantError = UnassignGroupFromTenantErrors[keyof UnassignGroupFromTenantErrors]; ``` --- ## Type Alias: UnassignGroupFromTenantErrors ```ts type UnassignGroupFromTenantErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Not found. The tenant or group was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: UnassignGroupFromTenantResponse ```ts type UnassignGroupFromTenantResponse = UnassignGroupFromTenantResponses[keyof UnassignGroupFromTenantResponses]; ``` --- ## Type Alias: UnassignGroupFromTenantResponses ```ts type UnassignGroupFromTenantResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The group was successfully unassigned from the tenant. --- ## Type Alias: UnassignMappingRuleFromGroupData ```ts type UnassignMappingRuleFromGroupData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### groupId ```ts groupId: GroupId; ``` The group ID. #### mappingRuleId ```ts mappingRuleId: MappingRuleId; ``` The mapping rule ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/groups/{groupId}/mapping-rules/{mappingRuleId}"; ``` --- ## Type Alias: UnassignMappingRuleFromGroupError ```ts type UnassignMappingRuleFromGroupError = UnassignMappingRuleFromGroupErrors[keyof UnassignMappingRuleFromGroupErrors]; ``` --- ## Type Alias: UnassignMappingRuleFromGroupErrors ```ts type UnassignMappingRuleFromGroupErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The group or mapping rule with the given ID was not found, or the mapping rule is not assigned to this group. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: UnassignMappingRuleFromGroupResponse ```ts type UnassignMappingRuleFromGroupResponse = UnassignMappingRuleFromGroupResponses[keyof UnassignMappingRuleFromGroupResponses]; ``` --- ## Type Alias: UnassignMappingRuleFromGroupResponses ```ts type UnassignMappingRuleFromGroupResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The mapping rule was unassigned successfully from the group. --- ## Type Alias: UnassignMappingRuleFromTenantData ```ts type UnassignMappingRuleFromTenantData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### mappingRuleId ```ts mappingRuleId: MappingRuleId; ``` The unique identifier of the mapping rule. #### tenantId ```ts tenantId: TenantId; ``` The unique identifier of the tenant. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/tenants/{tenantId}/mapping-rules/{mappingRuleId}"; ``` --- ## Type Alias: UnassignMappingRuleFromTenantError ```ts type UnassignMappingRuleFromTenantError = UnassignMappingRuleFromTenantErrors[keyof UnassignMappingRuleFromTenantErrors]; ``` --- ## Type Alias: UnassignMappingRuleFromTenantErrors ```ts type UnassignMappingRuleFromTenantErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Not found. The tenant or mapping rule was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: UnassignMappingRuleFromTenantResponse ```ts type UnassignMappingRuleFromTenantResponse = UnassignMappingRuleFromTenantResponses[keyof UnassignMappingRuleFromTenantResponses]; ``` --- ## Type Alias: UnassignMappingRuleFromTenantResponses ```ts type UnassignMappingRuleFromTenantResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The mapping rule was successfully unassigned from the tenant. --- ## Type Alias: UnassignRoleFromClientData ```ts type UnassignRoleFromClientData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### clientId ```ts clientId: ClientId; ``` The client ID. #### roleId ```ts roleId: RoleId; ``` The role ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/roles/{roleId}/clients/{clientId}"; ``` --- ## Type Alias: UnassignRoleFromClientError ```ts type UnassignRoleFromClientError = UnassignRoleFromClientErrors[keyof UnassignRoleFromClientErrors]; ``` --- ## Type Alias: UnassignRoleFromClientErrors ```ts type UnassignRoleFromClientErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The role or client with the given ID or username was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: UnassignRoleFromClientResponse ```ts type UnassignRoleFromClientResponse = UnassignRoleFromClientResponses[keyof UnassignRoleFromClientResponses]; ``` --- ## Type Alias: UnassignRoleFromClientResponses ```ts type UnassignRoleFromClientResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The role was unassigned successfully from the client. --- ## Type Alias: UnassignRoleFromGroupData ```ts type UnassignRoleFromGroupData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### groupId ```ts groupId: GroupId; ``` The group ID. #### roleId ```ts roleId: RoleId; ``` The role ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/roles/{roleId}/groups/{groupId}"; ``` --- ## Type Alias: UnassignRoleFromGroupError ```ts type UnassignRoleFromGroupError = UnassignRoleFromGroupErrors[keyof UnassignRoleFromGroupErrors]; ``` --- ## Type Alias: UnassignRoleFromGroupErrors ```ts type UnassignRoleFromGroupErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The role or group with the given ID was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: UnassignRoleFromGroupResponse ```ts type UnassignRoleFromGroupResponse = UnassignRoleFromGroupResponses[keyof UnassignRoleFromGroupResponses]; ``` --- ## Type Alias: UnassignRoleFromGroupResponses ```ts type UnassignRoleFromGroupResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The role was unassigned successfully from the group. --- ## Type Alias: UnassignRoleFromMappingRuleData ```ts type UnassignRoleFromMappingRuleData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### mappingRuleId ```ts mappingRuleId: MappingRuleId; ``` The mapping rule ID. #### roleId ```ts roleId: RoleId; ``` The role ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/roles/{roleId}/mapping-rules/{mappingRuleId}"; ``` --- ## Type Alias: UnassignRoleFromMappingRuleError ```ts type UnassignRoleFromMappingRuleError = UnassignRoleFromMappingRuleErrors[keyof UnassignRoleFromMappingRuleErrors]; ``` --- ## Type Alias: UnassignRoleFromMappingRuleErrors ```ts type UnassignRoleFromMappingRuleErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The role or mapping rule with the given ID was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: UnassignRoleFromMappingRuleResponse ```ts type UnassignRoleFromMappingRuleResponse = UnassignRoleFromMappingRuleResponses[keyof UnassignRoleFromMappingRuleResponses]; ``` --- ## Type Alias: UnassignRoleFromMappingRuleResponses ```ts type UnassignRoleFromMappingRuleResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The role was unassigned successfully from the mapping rule. --- ## Type Alias: UnassignRoleFromTenantData ```ts type UnassignRoleFromTenantData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### roleId ```ts roleId: RoleId; ``` The unique identifier of the role. #### tenantId ```ts tenantId: TenantId; ``` The unique identifier of the tenant. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/tenants/{tenantId}/roles/{roleId}"; ``` --- ## Type Alias: UnassignRoleFromTenantError ```ts type UnassignRoleFromTenantError = UnassignRoleFromTenantErrors[keyof UnassignRoleFromTenantErrors]; ``` --- ## Type Alias: UnassignRoleFromTenantErrors ```ts type UnassignRoleFromTenantErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Not found. The tenant or role was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: UnassignRoleFromTenantResponse ```ts type UnassignRoleFromTenantResponse = UnassignRoleFromTenantResponses[keyof UnassignRoleFromTenantResponses]; ``` --- ## Type Alias: UnassignRoleFromTenantResponses ```ts type UnassignRoleFromTenantResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The role was successfully unassigned from the tenant. --- ## Type Alias: UnassignRoleFromUserData ```ts type UnassignRoleFromUserData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### roleId ```ts roleId: RoleId; ``` The role ID. #### username ```ts username: Username; ``` The user username. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/roles/{roleId}/users/{username}"; ``` --- ## Type Alias: UnassignRoleFromUserError ```ts type UnassignRoleFromUserError = UnassignRoleFromUserErrors[keyof UnassignRoleFromUserErrors]; ``` --- ## Type Alias: UnassignRoleFromUserErrors ```ts type UnassignRoleFromUserErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The role or user with the given ID or username was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: UnassignRoleFromUserResponse ```ts type UnassignRoleFromUserResponse = UnassignRoleFromUserResponses[keyof UnassignRoleFromUserResponses]; ``` --- ## Type Alias: UnassignRoleFromUserResponses ```ts type UnassignRoleFromUserResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The role was unassigned successfully from the user. --- ## Type Alias: UnassignUserFromGroupData ```ts type UnassignUserFromGroupData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### groupId ```ts groupId: GroupId; ``` The group ID. #### username ```ts username: Username; ``` The user username. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/groups/{groupId}/users/{username}"; ``` --- ## Type Alias: UnassignUserFromGroupError ```ts type UnassignUserFromGroupError = UnassignUserFromGroupErrors[keyof UnassignUserFromGroupErrors]; ``` --- ## Type Alias: UnassignUserFromGroupErrors ```ts type UnassignUserFromGroupErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The group or user with the given ID was not found, or the user is not assigned to this group. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: UnassignUserFromGroupResponse ```ts type UnassignUserFromGroupResponse = UnassignUserFromGroupResponses[keyof UnassignUserFromGroupResponses]; ``` --- ## Type Alias: UnassignUserFromGroupResponses ```ts type UnassignUserFromGroupResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The user was unassigned successfully from the group. --- ## Type Alias: UnassignUserFromTenantData ```ts type UnassignUserFromTenantData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### tenantId ```ts tenantId: TenantId; ``` The unique identifier of the tenant. #### username ```ts username: Username; ``` The unique identifier of the user. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/tenants/{tenantId}/users/{username}"; ``` --- ## Type Alias: UnassignUserFromTenantError ```ts type UnassignUserFromTenantError = UnassignUserFromTenantErrors[keyof UnassignUserFromTenantErrors]; ``` --- ## Type Alias: UnassignUserFromTenantErrors ```ts type UnassignUserFromTenantErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Not found. The tenant or user was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: UnassignUserFromTenantResponse ```ts type UnassignUserFromTenantResponse = UnassignUserFromTenantResponses[keyof UnassignUserFromTenantResponses]; ``` --- ## Type Alias: UnassignUserFromTenantResponses ```ts type UnassignUserFromTenantResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The user was successfully unassigned from the tenant. --- ## Type Alias: UnassignUserTaskData ```ts type UnassignUserTaskData = object; ``` ## Properties ### body? ```ts optional body?: never; ``` --- ### path ```ts path: object; ``` #### userTaskKey ```ts userTaskKey: UserTaskKey; ``` The key of the user task. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/user-tasks/{userTaskKey}/assignee"; ``` --- ## Type Alias: UnassignUserTaskError ```ts type UnassignUserTaskError = UnassignUserTaskErrors[keyof UnassignUserTaskErrors]; ``` --- ## Type Alias: UnassignUserTaskErrors ```ts type UnassignUserTaskErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 404 ```ts 404: ProblemDetail; ``` The user task with the given key was not found. --- ### 409 ```ts 409: ProblemDetail; ``` The user task with the given key is in the wrong state currently. More details are provided in the response body. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ### 504 ```ts 504: ProblemDetail; ``` The request timed out between the gateway and the broker. For these endpoints, this often happens when user task listeners are configured and the corresponding listener job is not completed within the request timeout. Common causes include no available job workers for the listener type, busy or crashed job workers, or delayed job completion. As with any gateway timeout, general timeout causes (for example transient network issues) can also result in a 504 response. Troubleshooting: - verify that job workers for the listener type are running and healthy - check worker logs for crashes, retries, and completion failures - check network connectivity between workers, gateway, and broker - retry with backoff after transient failures - fail without retries if a problem persists --- ## Type Alias: UnassignUserTaskResponse ```ts type UnassignUserTaskResponse = UnassignUserTaskResponses[keyof UnassignUserTaskResponses]; ``` --- ## Type Alias: UnassignUserTaskResponses ```ts type UnassignUserTaskResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The user task was unassigned successfully. --- ## Type Alias: UpdateAgentInstanceData ```ts type UpdateAgentInstanceData = object; ``` ## Properties ### body ```ts body: AgentInstanceUpdateRequest; ``` --- ### path ```ts path: object; ``` #### agentInstanceKey ```ts agentInstanceKey: AgentInstanceKey; ``` The key of the agent instance to update. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/agent-instances/{agentInstanceKey}"; ``` --- ## Type Alias: UpdateAgentInstanceError ```ts type UpdateAgentInstanceError = UpdateAgentInstanceErrors[keyof UpdateAgentInstanceErrors]; ``` --- ## Type Alias: UpdateAgentInstanceErrors ```ts type UpdateAgentInstanceErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The agent instance with the given key was not found. More details are provided in the response body. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: UpdateAgentInstanceResponse ```ts type UpdateAgentInstanceResponse = UpdateAgentInstanceResponses[keyof UpdateAgentInstanceResponses]; ``` --- ## Type Alias: UpdateAgentInstanceResponses ```ts type UpdateAgentInstanceResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The agent instance was updated successfully. --- ## Type Alias: UpdateAuthorizationData ```ts type UpdateAuthorizationData = object; ``` ## Properties ### body ```ts body: AuthorizationRequest; ``` --- ### path ```ts path: object; ``` #### authorizationKey ```ts authorizationKey: AuthorizationKey; ``` The key of the authorization to delete. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/authorizations/{authorizationKey}"; ``` --- ## Type Alias: UpdateAuthorizationError ```ts type UpdateAuthorizationError = UpdateAuthorizationErrors[keyof UpdateAuthorizationErrors]; ``` --- ## Type Alias: UpdateAuthorizationErrors ```ts type UpdateAuthorizationErrors = object; ``` ## Properties ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 404 ```ts 404: ProblemDetail; ``` The authorization with the authorizationKey was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: UpdateAuthorizationResponse ```ts type UpdateAuthorizationResponse = UpdateAuthorizationResponses[keyof UpdateAuthorizationResponses]; ``` --- ## Type Alias: UpdateAuthorizationResponses ```ts type UpdateAuthorizationResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The authorization was updated successfully. --- ## Type Alias: UpdateClusterVariableRequest ```ts type UpdateClusterVariableRequest = object; ``` ## Properties ### value ```ts value: object; ``` The new value of the cluster variable. Can be any JSON object or primitive value. Will be serialized as a JSON string in responses. #### Index Signature ```ts [key: string]: unknown ``` --- ## Type Alias: UpdateGlobalClusterVariableData ```ts type UpdateGlobalClusterVariableData = object; ``` ## Properties ### body ```ts body: UpdateClusterVariableRequest; ``` --- ### path ```ts path: object; ``` #### name ```ts name: ClusterVariableName; ``` The name of the cluster variable --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/cluster-variables/global/{name}"; ``` --- ## Type Alias: UpdateGlobalClusterVariableError ```ts type UpdateGlobalClusterVariableError = UpdateGlobalClusterVariableErrors[keyof UpdateGlobalClusterVariableErrors]; ``` --- ## Type Alias: UpdateGlobalClusterVariableErrors ```ts type UpdateGlobalClusterVariableErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Cluster variable not found --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: UpdateGlobalClusterVariableResponse ```ts type UpdateGlobalClusterVariableResponse = UpdateGlobalClusterVariableResponses[keyof UpdateGlobalClusterVariableResponses]; ``` --- ## Type Alias: UpdateGlobalClusterVariableResponses ```ts type UpdateGlobalClusterVariableResponses = object; ``` ## Properties ### 200 ```ts 200: ClusterVariableResult; ``` Cluster variable updated successfully --- ## Type Alias: UpdateGlobalTaskListenerData ```ts type UpdateGlobalTaskListenerData = object; ``` ## Properties ### body ```ts body: UpdateGlobalTaskListenerRequest; ``` --- ### path ```ts path: object; ``` #### id ```ts id: GlobalListenerId; ``` The id of the global user task listener to update. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/global-task-listeners/{id}"; ``` --- ## Type Alias: UpdateGlobalTaskListenerError ```ts type UpdateGlobalTaskListenerError = UpdateGlobalTaskListenerErrors[keyof UpdateGlobalTaskListenerErrors]; ``` --- ## Type Alias: UpdateGlobalTaskListenerErrors ```ts type UpdateGlobalTaskListenerErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The global user task listener was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: UpdateGlobalTaskListenerRequest ```ts type UpdateGlobalTaskListenerRequest = GlobalTaskListenerBase; ``` --- ## Type Alias: UpdateGlobalTaskListenerResponse ```ts type UpdateGlobalTaskListenerResponse = UpdateGlobalTaskListenerResponses[keyof UpdateGlobalTaskListenerResponses]; ``` --- ## Type Alias: UpdateGlobalTaskListenerResponses ```ts type UpdateGlobalTaskListenerResponses = object; ``` ## Properties ### 200 ```ts 200: GlobalTaskListenerResult; ``` The global listener was updated successfully. --- ## Type Alias: UpdateGroupData ```ts type UpdateGroupData = object; ``` ## Properties ### body ```ts body: GroupUpdateRequest; ``` --- ### path ```ts path: object; ``` #### groupId ```ts groupId: GroupId; ``` The group ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/groups/{groupId}"; ``` --- ## Type Alias: UpdateGroupError ```ts type UpdateGroupError = UpdateGroupErrors[keyof UpdateGroupErrors]; ``` --- ## Type Alias: UpdateGroupErrors ```ts type UpdateGroupErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 404 ```ts 404: ProblemDetail; ``` The group with the given ID was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: UpdateGroupResponse ```ts type UpdateGroupResponse = UpdateGroupResponses[keyof UpdateGroupResponses]; ``` --- ## Type Alias: UpdateGroupResponses ```ts type UpdateGroupResponses = object; ``` ## Properties ### 200 ```ts 200: GroupUpdateResult; ``` The group was updated successfully. --- ## Type Alias: UpdateJobData ```ts type UpdateJobData = object; ``` ## Properties ### body ```ts body: JobUpdateRequest; ``` --- ### path ```ts path: object; ``` #### jobKey ```ts jobKey: JobKey; ``` The key of the job to update. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/jobs/{jobKey}"; ``` --- ## Type Alias: UpdateJobError ```ts type UpdateJobError = UpdateJobErrors[keyof UpdateJobErrors]; ``` --- ## Type Alias: UpdateJobErrors ```ts type UpdateJobErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 404 ```ts 404: ProblemDetail; ``` The job with the jobKey is not found. --- ### 409 ```ts 409: ProblemDetail; ``` The job with the given key is in the wrong state currently. More details are provided in the response body. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: UpdateJobResponse ```ts type UpdateJobResponse = UpdateJobResponses[keyof UpdateJobResponses]; ``` --- ## Type Alias: UpdateJobResponses ```ts type UpdateJobResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The job was updated successfully. --- ## Type Alias: UpdateMappingRuleData ```ts type UpdateMappingRuleData = object; ``` ## Properties ### body? ```ts optional body?: MappingRuleUpdateRequest; ``` --- ### path ```ts path: object; ``` #### mappingRuleId ```ts mappingRuleId: MappingRuleId; ``` The ID of the mapping rule to update. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/mapping-rules/{mappingRuleId}"; ``` --- ## Type Alias: UpdateMappingRuleError ```ts type UpdateMappingRuleError = UpdateMappingRuleErrors[keyof UpdateMappingRuleErrors]; ``` --- ## Type Alias: UpdateMappingRuleErrors ```ts type UpdateMappingRuleErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` The request to update a mapping rule was denied. More details are provided in the response body. --- ### 404 ```ts 404: ProblemDetail; ``` The request to update a mapping rule was denied. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: UpdateMappingRuleResponse ```ts type UpdateMappingRuleResponse = UpdateMappingRuleResponses[keyof UpdateMappingRuleResponses]; ``` --- ## Type Alias: UpdateMappingRuleResponses ```ts type UpdateMappingRuleResponses = object; ``` ## Properties ### 200 ```ts 200: MappingRuleUpdateResult; ``` The mapping rule was updated successfully. --- ## Type Alias: UpdateRoleData ```ts type UpdateRoleData = object; ``` ## Properties ### body ```ts body: RoleUpdateRequest; ``` --- ### path ```ts path: object; ``` #### roleId ```ts roleId: RoleId; ``` The role ID. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/roles/{roleId}"; ``` --- ## Type Alias: UpdateRoleError ```ts type UpdateRoleError = UpdateRoleErrors[keyof UpdateRoleErrors]; ``` --- ## Type Alias: UpdateRoleErrors ```ts type UpdateRoleErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 404 ```ts 404: ProblemDetail; ``` The role with the ID is not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: UpdateRoleResponse ```ts type UpdateRoleResponse = UpdateRoleResponses[keyof UpdateRoleResponses]; ``` --- ## Type Alias: UpdateRoleResponses ```ts type UpdateRoleResponses = object; ``` ## Properties ### 200 ```ts 200: RoleUpdateResult; ``` The role was updated successfully. --- ## Type Alias: UpdateTenantClusterVariableData ```ts type UpdateTenantClusterVariableData = object; ``` ## Properties ### body ```ts body: UpdateClusterVariableRequest; ``` --- ### path ```ts path: object; ``` #### name ```ts name: ClusterVariableName; ``` The name of the cluster variable #### tenantId ```ts tenantId: TenantId; ``` The tenant ID --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/cluster-variables/tenants/{tenantId}/{name}"; ``` --- ## Type Alias: UpdateTenantClusterVariableError ```ts type UpdateTenantClusterVariableError = UpdateTenantClusterVariableErrors[keyof UpdateTenantClusterVariableErrors]; ``` --- ## Type Alias: UpdateTenantClusterVariableErrors ```ts type UpdateTenantClusterVariableErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 401 ```ts 401: ProblemDetail; ``` The request lacks valid authentication credentials. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Cluster variable not found --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ## Type Alias: UpdateTenantClusterVariableResponse ```ts type UpdateTenantClusterVariableResponse = UpdateTenantClusterVariableResponses[keyof UpdateTenantClusterVariableResponses]; ``` --- ## Type Alias: UpdateTenantClusterVariableResponses ```ts type UpdateTenantClusterVariableResponses = object; ``` ## Properties ### 200 ```ts 200: ClusterVariableResult; ``` Cluster variable updated successfully --- ## Type Alias: UpdateTenantData ```ts type UpdateTenantData = object; ``` ## Properties ### body ```ts body: TenantUpdateRequest; ``` --- ### path ```ts path: object; ``` #### tenantId ```ts tenantId: TenantId; ``` The unique identifier of the tenant. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/tenants/{tenantId}"; ``` --- ## Type Alias: UpdateTenantError ```ts type UpdateTenantError = UpdateTenantErrors[keyof UpdateTenantErrors]; ``` --- ## Type Alias: UpdateTenantErrors ```ts type UpdateTenantErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` Not found. The tenant was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: UpdateTenantResponse ```ts type UpdateTenantResponse = UpdateTenantResponses[keyof UpdateTenantResponses]; ``` --- ## Type Alias: UpdateTenantResponses ```ts type UpdateTenantResponses = object; ``` ## Properties ### 200 ```ts 200: TenantUpdateResult; ``` The tenant was updated successfully. --- ## Type Alias: UpdateUserData ```ts type UpdateUserData = object; ``` ## Properties ### body ```ts body: UserUpdateRequest; ``` --- ### path ```ts path: object; ``` #### username ```ts username: Username; ``` The username of the user to update. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/users/{username}"; ``` --- ## Type Alias: UpdateUserError ```ts type UpdateUserError = UpdateUserErrors[keyof UpdateUserErrors]; ``` --- ## Type Alias: UpdateUserErrors ```ts type UpdateUserErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 403 ```ts 403: ProblemDetail; ``` Forbidden. The request is not allowed. --- ### 404 ```ts 404: ProblemDetail; ``` The user was not found. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ## Type Alias: UpdateUserResponse ```ts type UpdateUserResponse = UpdateUserResponses[keyof UpdateUserResponses]; ``` --- ## Type Alias: UpdateUserResponses ```ts type UpdateUserResponses = object; ``` ## Properties ### 200 ```ts 200: UserUpdateResult; ``` The user was updated successfully. --- ## Type Alias: UpdateUserTaskData ```ts type UpdateUserTaskData = object; ``` ## Properties ### body? ```ts optional body?: UserTaskUpdateRequest; ``` --- ### path ```ts path: object; ``` #### userTaskKey ```ts userTaskKey: UserTaskKey; ``` The key of the user task to update. --- ### query? ```ts optional query?: never; ``` --- ### url ```ts url: "/user-tasks/{userTaskKey}"; ``` --- ## Type Alias: UpdateUserTaskError ```ts type UpdateUserTaskError = UpdateUserTaskErrors[keyof UpdateUserTaskErrors]; ``` --- ## Type Alias: UpdateUserTaskErrors ```ts type UpdateUserTaskErrors = object; ``` ## Properties ### 400 ```ts 400: ProblemDetail; ``` The provided data is not valid. --- ### 404 ```ts 404: ProblemDetail; ``` The user task with the given key was not found. --- ### 409 ```ts 409: ProblemDetail; ``` The user task with the given key is in the wrong state currently. More details are provided in the response body. --- ### 500 ```ts 500: ProblemDetail; ``` An internal error occurred while processing the request. --- ### 503 ```ts 503: ProblemDetail; ``` The service is currently unavailable. This may happen only on some requests where the system creates backpressure to prevent the server's compute resources from being exhausted, avoiding more severe failures. In this case, the title of the error object contains `RESOURCE_EXHAUSTED`. Clients are recommended to eventually retry those requests after a backoff period. You can learn more about the backpressure mechanism here: https://docs.camunda.io/docs/components/zeebe/technical-concepts/internal-processing/#handling-backpressure . --- ### 504 ```ts 504: ProblemDetail; ``` The request timed out between the gateway and the broker. For these endpoints, this often happens when user task listeners are configured and the corresponding listener job is not completed within the request timeout. Common causes include no available job workers for the listener type, busy or crashed job workers, or delayed job completion. As with any gateway timeout, general timeout causes (for example transient network issues) can also result in a 504 response. Troubleshooting: - verify that job workers for the listener type are running and healthy - check worker logs for crashes, retries, and completion failures - check network connectivity between workers, gateway, and broker - retry with backoff after transient failures - fail without retries if a problem persists --- ## Type Alias: UpdateUserTaskResponse ```ts type UpdateUserTaskResponse = UpdateUserTaskResponses[keyof UpdateUserTaskResponses]; ``` --- ## Type Alias: UpdateUserTaskResponses ```ts type UpdateUserTaskResponses = object; ``` ## Properties ### 204 ```ts 204: void; ``` The user task was updated successfully. --- ## Type Alias: UsageMetricsResponse ```ts type UsageMetricsResponse = UsageMetricsResponseItem & object; ``` ## Type Declaration ### activeTenants ```ts activeTenants: number; ``` The amount of active tenants. ### tenants ```ts tenants: object; ``` The usage metrics by tenants. Only available if request `withTenants` query parameter was `true`. #### Index Signature ```ts [key: string]: UsageMetricsResponseItem ``` --- ## Type Alias: UsageMetricsResponseItem ```ts type UsageMetricsResponseItem = object; ``` ## Properties ### assignees ```ts assignees: number; ``` The amount of unique active task users. --- ### decisionInstances ```ts decisionInstances: number; ``` The amount of executed decision instances. --- ### processInstances ```ts processInstances: number; ``` The amount of created root process instances. --- ## Type Alias: UseSourceParentKeyInstruction ```ts type UseSourceParentKeyInstruction = object; ``` Instructs the engine to use the source's direct parent key as the ancestor scope key for the target element. This is a simpler alternative to `inferred` that skips hierarchy traversal and directly uses the source's parent key. This is useful when the source and target elements are siblings within the same flow scope. ## Properties ### ancestorScopeType ```ts ancestorScopeType: string; ``` The type of ancestor scope instruction. --- ## Type Alias: UserCreateResult ```ts type UserCreateResult = object; ``` ## Properties ### email ```ts email: string | null; ``` The email of the user. --- ### name ```ts name: string | null; ``` The name of the user. --- ### username ```ts username: Username; ``` The username of the created user. --- ## Type Alias: UserFilter ```ts type UserFilter = object; ``` User search filter. ## Properties ### email? ```ts optional email?: StringFilterProperty; ``` The email of the user. --- ### name? ```ts optional name?: StringFilterProperty; ``` The name of the user. --- ### username? ```ts optional username?: StringFilterProperty; ``` The username of the user. --- ## Type Alias: UserRequest ```ts type UserRequest = object; ``` ## Properties ### email? ```ts optional email?: string; ``` The email of the user. --- ### name? ```ts optional name?: string; ``` The name of the user. --- ### password ```ts password: string; ``` The password of the user. --- ### username ```ts username: Username; ``` The username of the new user. --- ## Type Alias: UserResult ```ts type UserResult = object; ``` ## Properties ### email ```ts email: string | null; ``` The email of the user. --- ### name ```ts name: string | null; ``` The name of the user. --- ### username ```ts username: Username; ``` The username of the user. --- ## Type Alias: UserSearchQueryRequest ```ts type UserSearchQueryRequest = SearchQueryRequest & object; ``` ## Type Declaration ### filter? ```ts optional filter?: UserFilter; ``` The user search filters. ### sort? ```ts optional sort?: UserSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: UserSearchQuerySortRequest ```ts type UserSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: "username" | "name" | "email"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: UserSearchResult ```ts type UserSearchResult = SearchQueryResponse & object; ``` ## Type Declaration ### items ```ts items: UserResult[]; ``` The matching users. --- ## Type Alias: UserTaskAssignmentRequest ```ts type UserTaskAssignmentRequest = object; ``` ## Properties ### action? ```ts optional action?: string | null; ``` A custom action value that will be accessible from user task events resulting from this endpoint invocation. If not provided, it will default to "assign". --- ### allowOverride? ```ts optional allowOverride?: boolean | null; ``` By default, the task is reassigned if it was already assigned. Set this to `false` to return an error in such cases. The task must then first be unassigned to be assigned again. Use this when you have users picking from group task queues to prevent race conditions. --- ### assignee? ```ts optional assignee?: string; ``` The assignee for the user task. The assignee must not be empty or `null`. --- ## Type Alias: UserTaskAuditLogFilter ```ts type UserTaskAuditLogFilter = object; ``` The user task audit log search filters. ## Properties ### actorId? ```ts optional actorId?: StringFilterProperty; ``` The actor ID search filter. --- ### actorType? ```ts optional actorType?: AuditLogActorTypeFilterProperty; ``` The actor type search filter. --- ### operationType? ```ts optional operationType?: OperationTypeFilterProperty; ``` The audit log operation type search filter. --- ### result? ```ts optional result?: AuditLogResultFilterProperty; ``` The audit log result search filter. --- ### timestamp? ```ts optional timestamp?: DateTimeFilterProperty; ``` The audit log timestamp filter. --- ## Type Alias: UserTaskAuditLogSearchQueryRequest ```ts type UserTaskAuditLogSearchQueryRequest = SearchQueryRequest & object; ``` User task search query request. ## Type Declaration ### filter? ```ts optional filter?: UserTaskAuditLogFilter; ``` ### sort? ```ts optional sort?: AuditLogSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: UserTaskCompletionRequest ```ts type UserTaskCompletionRequest = object; ``` ## Properties ### action? ```ts optional action?: string | null; ``` A custom action value that will be accessible from user task events resulting from this endpoint invocation. If not provided, it will default to "complete". --- ### variables? ```ts optional variables?: | { [key: string]: unknown; } | null; ``` The variables to complete the user task with. --- ## Type Alias: UserTaskEffectiveVariableSearchQueryRequest ```ts type UserTaskEffectiveVariableSearchQueryRequest = object; ``` User task effective variable search query request. Uses offset-based pagination only. ## Properties ### filter? ```ts optional filter?: UserTaskVariableFilter; ``` The user task variable search filters. --- ### page? ```ts optional page?: OffsetPagination; ``` Pagination parameters. --- ### sort? ```ts optional sort?: UserTaskVariableSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: UserTaskFilter ```ts type UserTaskFilter = object; ``` User task filter request. ## Properties ### assignee? ```ts optional assignee?: StringFilterProperty; ``` The assignee of the user task. --- ### candidateGroup? ```ts optional candidateGroup?: StringFilterProperty; ``` The candidate group for this user task. --- ### candidateUser? ```ts optional candidateUser?: StringFilterProperty; ``` The candidate user for this user task. --- ### completionDate? ```ts optional completionDate?: DateTimeFilterProperty; ``` The user task completion date. --- ### creationDate? ```ts optional creationDate?: DateTimeFilterProperty; ``` The user task creation date. --- ### dueDate? ```ts optional dueDate?: DateTimeFilterProperty; ``` The user task due date. --- ### elementId? ```ts optional elementId?: ElementId; ``` The element ID of the user task. --- ### elementInstanceKey? ```ts optional elementInstanceKey?: ElementInstanceKey; ``` The key of the element instance. --- ### followUpDate? ```ts optional followUpDate?: DateTimeFilterProperty; ``` The user task follow-up date. --- ### localVariables? ```ts optional localVariables?: VariableValueFilterProperty[]; ``` The local variables of the user task. --- ### name? ```ts optional name?: StringFilterProperty; ``` The task name. This only works for data created with 8.8 and onwards. Instances from prior versions don't contain this data and cannot be found. --- ### priority? ```ts optional priority?: IntegerFilterProperty; ``` The priority of the user task. --- ### processDefinitionId? ```ts optional processDefinitionId?: ProcessDefinitionIdFilterProperty; ``` The ID of the process definition. --- ### processDefinitionKey? ```ts optional processDefinitionKey?: ProcessDefinitionKeyFilterProperty; ``` The key of the process definition. --- ### processInstanceKey? ```ts optional processInstanceKey?: ProcessInstanceKeyFilterProperty; ``` The key of the process instance. --- ### processInstanceVariables? ```ts optional processInstanceVariables?: VariableValueFilterProperty[]; ``` The variables of the process instance. --- ### state? ```ts optional state?: UserTaskStateFilterProperty; ``` The user task state. --- ### tags? ```ts optional tags?: TagSet; ``` --- ### tenantId? ```ts optional tenantId?: StringFilterProperty; ``` Tenant ID of this user task. --- ### userTaskKey? ```ts optional userTaskKey?: UserTaskKey; ``` The key for this user task. --- ## Type Alias: UserTaskKey ```ts type UserTaskKey = CamundaKey<"UserTaskKey">; ``` System-generated key for a user task. --- ## Type Alias: UserTaskProperties ```ts type UserTaskProperties = object; ``` Contains properties of a user task. ## Properties ### action ```ts action: string; ``` The action performed on the user task. --- ### assignee ```ts assignee: string | null; ``` The user assigned to the task. --- ### candidateGroups ```ts candidateGroups: string[]; ``` The groups eligible to claim the task. --- ### candidateUsers ```ts candidateUsers: string[]; ``` The users eligible to claim the task. --- ### changedAttributes ```ts changedAttributes: string[]; ``` The attributes that were changed in the task. --- ### dueDate ```ts dueDate: string | null; ``` The due date of the user task in ISO 8601 format. --- ### followUpDate ```ts followUpDate: string | null; ``` The follow-up date of the user task in ISO 8601 format. --- ### formKey ```ts formKey: FormKey | null; ``` The key of the form associated with the user task. --- ### priority ```ts priority: number | null; ``` The priority of the user task. --- ### userTaskKey ```ts userTaskKey: UserTaskKey | null; ``` The unique key identifying the user task. --- ## Type Alias: UserTaskResult ```ts type UserTaskResult = object; ``` ## Properties ### assignee ```ts assignee: string | null; ``` The assignee of the user task. --- ### candidateGroups ```ts candidateGroups: string[]; ``` The candidate groups for this user task. --- ### candidateUsers ```ts candidateUsers: string[]; ``` The candidate users for this user task. --- ### completionDate ```ts completionDate: string | null; ``` The completion date of a user task. --- ### creationDate ```ts creationDate: string; ``` The creation date of a user task. --- ### customHeaders ```ts customHeaders: object; ``` Custom headers for the user task. #### Index Signature ```ts [key: string]: string ``` --- ### dueDate ```ts dueDate: string | null; ``` The due date of a user task. --- ### elementId ```ts elementId: ElementId; ``` The element ID of the user task. --- ### elementInstanceKey ```ts elementInstanceKey: ElementInstanceKey; ``` The key of the element instance. --- ### externalFormReference ```ts externalFormReference: string | null; ``` The external form reference. --- ### followUpDate ```ts followUpDate: string | null; ``` The follow date of a user task. --- ### formKey ```ts formKey: FormKey | null; ``` The key of the form. --- ### name ```ts name: string | null; ``` The name for this user task. --- ### priority ```ts priority: number; ``` The priority of a user task. The higher the value the higher the priority. --- ### processDefinitionId ```ts processDefinitionId: ProcessDefinitionId; ``` The ID of the process definition. --- ### processDefinitionKey ```ts processDefinitionKey: ProcessDefinitionKey; ``` The key of the process definition. --- ### processDefinitionVersion ```ts processDefinitionVersion: number; ``` The version of the process definition. --- ### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The key of the process instance. --- ### processName ```ts processName: string | null; ``` The name of the process definition. This is `null` if the process has no name defined. --- ### rootProcessInstanceKey ```ts rootProcessInstanceKey: ProcessInstanceKey | null; ``` The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. --- ### state ```ts state: UserTaskStateEnum; ``` --- ### tags ```ts tags: TagSet; ``` --- ### tenantId ```ts tenantId: TenantId; ``` --- ### userTaskKey ```ts userTaskKey: UserTaskKey; ``` The key of the user task. --- ## Type Alias: UserTaskSearchQuery ```ts type UserTaskSearchQuery = SearchQueryRequest & object; ``` User task search query request. ## Type Declaration ### filter? ```ts optional filter?: UserTaskFilter; ``` The user task search filters. ### sort? ```ts optional sort?: UserTaskSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: UserTaskSearchQueryResult ```ts type UserTaskSearchQueryResult = SearchQueryResponse & object; ``` User task search query response. ## Type Declaration ### items ```ts items: UserTaskResult[]; ``` The matching user tasks. --- ## Type Alias: UserTaskSearchQuerySortRequest ```ts type UserTaskSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: | "creationDate" | "completionDate" | "followUpDate" | "dueDate" | "priority" | "name"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: UserTaskStateEnum ```ts type UserTaskStateEnum = (typeof UserTaskStateEnum)[keyof typeof UserTaskStateEnum]; ``` The state of the user task. Note: FAILED state is only for legacy job-worker-based tasks. --- ## Type Alias: UserTaskStateExactMatch ```ts type UserTaskStateExactMatch = UserTaskStateEnum; ``` Exact match Matches the value exactly. --- ## Type Alias: UserTaskStateFilterProperty ```ts type UserTaskStateFilterProperty = | UserTaskStateExactMatch | AdvancedUserTaskStateFilter; ``` UserTaskStateEnum property with full advanced search capabilities. --- ## Type Alias: UserTaskUpdateRequest ```ts type UserTaskUpdateRequest = object; ``` ## Properties ### action? ```ts optional action?: string | null; ``` A custom action value that will be accessible from user task events resulting from this endpoint invocation. If not provided, it will default to "update". --- ### changeset? ```ts optional changeset?: Changeset; ``` --- ## Type Alias: UserTaskVariableFilter ```ts type UserTaskVariableFilter = object; ``` The user task variable search filters. ## Properties ### name? ```ts optional name?: StringFilterProperty; ``` Name of the variable. --- ## Type Alias: UserTaskVariableSearchQueryRequest ```ts type UserTaskVariableSearchQueryRequest = SearchQueryRequest & object; ``` User task search query request. ## Type Declaration ### filter? ```ts optional filter?: UserTaskVariableFilter; ``` The user task variable search filters. ### sort? ```ts optional sort?: UserTaskVariableSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: UserTaskVariableSearchQuerySortRequest ```ts type UserTaskVariableSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: | "value" | "name" | "tenantId" | "variableKey" | "scopeKey" | "processInstanceKey"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: UserUpdateRequest ```ts type UserUpdateRequest = object; ``` ## Properties ### email? ```ts optional email?: string; ``` The email of the user. --- ### name? ```ts optional name?: string; ``` The name of the user. --- ### password? ```ts optional password?: string; ``` The password of the user. If blank, the password is unchanged. --- ## Type Alias: UserUpdateResult ```ts type UserUpdateResult = object; ``` ## Properties ### email ```ts email: string | null; ``` The email of the user. --- ### name ```ts name: string | null; ``` The name of the user. --- ### username ```ts username: Username; ``` The username of the updated user. --- ## Type Alias: Username ```ts type Username = CamundaKey<"Username">; ``` The unique name of a user. --- ## Type Alias: ValidationMode ```ts type ValidationMode = "none" | "warn" | "strict" | "fanatical"; ``` --- ## Type Alias: VariableFilter ```ts type VariableFilter = object; ``` Variable filter request. ## Properties ### isTruncated? ```ts optional isTruncated?: boolean; ``` Whether the value is truncated or not. --- ### name? ```ts optional name?: StringFilterProperty; ``` Name of the variable. --- ### processInstanceKey? ```ts optional processInstanceKey?: ProcessInstanceKeyFilterProperty; ``` The key of the process instance of this variable. --- ### scopeKey? ```ts optional scopeKey?: ScopeKeyFilterProperty; ``` The key of the scope that defines where this variable is directly defined. This can be a process instance key (for process-level variables) or an element instance key (for local variables scoped to tasks, subprocesses, gateways, events, etc.). Use this filter to find variables directly defined in specific scopes. Note that this does not include variables from parent scopes that would be visible through the scope hierarchy. --- ### tenantId? ```ts optional tenantId?: TenantId; ``` Tenant ID of this variable. --- ### value? ```ts optional value?: StringFilterProperty; ``` The value of the variable. Variable values in filters need to be in serialized JSON format. For example, a variable with string value `myValue` can be found with the filter value `"myValue"`. Consider appropriate escaping for special characters in JSON strings when constructing filter values. --- ### variableKey? ```ts optional variableKey?: VariableKeyFilterProperty; ``` The key for this variable. --- ## Type Alias: VariableKey ```ts type VariableKey = CamundaKey<"VariableKey">; ``` System-generated key for a variable. --- ## Type Alias: VariableKeyExactMatch ```ts type VariableKeyExactMatch = VariableKey; ``` Exact match Matches the value exactly. --- ## Type Alias: VariableKeyFilterProperty ```ts type VariableKeyFilterProperty = | VariableKeyExactMatch | AdvancedVariableKeyFilter; ``` VariableKey property with full advanced search capabilities. --- ## Type Alias: VariableResult ```ts type VariableResult = VariableResultBase & object; ``` Variable search response item. ## Type Declaration ### value ```ts value: string; ``` Full value of this variable. --- ## Type Alias: VariableResultBase ```ts type VariableResultBase = object; ``` Variable response item. ## Properties ### name ```ts name: string; ``` Name of this variable. --- ### processInstanceKey ```ts processInstanceKey: ProcessInstanceKey; ``` The key of the process instance of this variable. --- ### rootProcessInstanceKey ```ts rootProcessInstanceKey: ProcessInstanceKey | null; ``` The key of the root process instance. The root process instance is the top-level ancestor in the process instance hierarchy. This field is only present for data belonging to process instance hierarchies created in version 8.9 or later. --- ### scopeKey ```ts scopeKey: ScopeKey; ``` The key of the scope where this variable is directly defined. For process-level variables, this is the process instance key. For local variables, this is the key of the specific element instance (task, subprocess, gateway, event, etc.) where the variable is directly defined. --- ### tenantId ```ts tenantId: TenantId; ``` Tenant ID of this variable. --- ### variableKey ```ts variableKey: VariableKey; ``` The key for this variable. --- ## Type Alias: VariableSearchQuery ```ts type VariableSearchQuery = SearchQueryRequest & object; ``` Variable search query request. ## Type Declaration ### filter? ```ts optional filter?: VariableFilter; ``` The variable search filters. ### sort? ```ts optional sort?: VariableSearchQuerySortRequest[]; ``` Sort field criteria. --- ## Type Alias: VariableSearchQueryResult ```ts type VariableSearchQueryResult = SearchQueryResponse & object; ``` Variable search query response. ## Type Declaration ### items ```ts items: VariableSearchResult[]; ``` The matching variables. --- ## Type Alias: VariableSearchQuerySortRequest ```ts type VariableSearchQuerySortRequest = object; ``` ## Properties ### field ```ts field: | "value" | "name" | "tenantId" | "variableKey" | "scopeKey" | "processInstanceKey"; ``` The field to sort by. --- ### order? ```ts optional order?: SortOrderEnum; ``` --- ## Type Alias: VariableSearchResult ```ts type VariableSearchResult = VariableResultBase & object; ``` Variable search response item. ## Type Declaration ### isTruncated ```ts isTruncated: boolean; ``` Whether the value is truncated or not. ### value ```ts value: string; ``` Value of this variable. Can be truncated. --- ## Type Alias: VariableValueFilterProperty ```ts type VariableValueFilterProperty = object; ``` ## Properties ### name ```ts name: string; ``` Name of the variable. --- ### value ```ts value: StringFilterProperty; ``` The value of the variable. Variable values in filters need to be in serialized JSON format. For example, a variable with string value `myValue` can be found with the filter value `"myValue"`. Consider appropriate escaping for special characters in JSON strings when constructing filter values. --- ## Type Alias: WebappComponent ```ts type WebappComponent = "operate" | "tasklist" | "admin"; ``` A Camunda webapp component name. --- ## Type Alias: activateAdHocSubProcessActivitiesInput ```ts type activateAdHocSubProcessActivitiesInput = activateAdHocSubProcessActivitiesBody & object; ``` ## Type Declaration ### adHocSubProcessInstanceKey ```ts adHocSubProcessInstanceKey: activateAdHocSubProcessActivitiesPathParam_adHocSubProcessInstanceKey; ``` --- ## Type Alias: activateJobsInput ```ts type activateJobsInput = activateJobsBody; ``` --- ## Type Alias: assignClientToGroupInput ```ts type assignClientToGroupInput = object; ``` ## Properties ### clientId ```ts clientId: assignClientToGroupPathParam_clientId; ``` --- ### groupId ```ts groupId: assignClientToGroupPathParam_groupId; ``` --- ## Type Alias: assignClientToTenantInput ```ts type assignClientToTenantInput = object; ``` ## Properties ### clientId ```ts clientId: assignClientToTenantPathParam_clientId; ``` --- ### tenantId ```ts tenantId: assignClientToTenantPathParam_tenantId; ``` --- ## Type Alias: assignGroupToTenantInput ```ts type assignGroupToTenantInput = object; ``` ## Properties ### groupId ```ts groupId: assignGroupToTenantPathParam_groupId; ``` --- ### tenantId ```ts tenantId: assignGroupToTenantPathParam_tenantId; ``` --- ## Type Alias: assignMappingRuleToGroupInput ```ts type assignMappingRuleToGroupInput = object; ``` ## Properties ### groupId ```ts groupId: assignMappingRuleToGroupPathParam_groupId; ``` --- ### mappingRuleId ```ts mappingRuleId: assignMappingRuleToGroupPathParam_mappingRuleId; ``` --- ## Type Alias: assignMappingRuleToTenantInput ```ts type assignMappingRuleToTenantInput = object; ``` ## Properties ### mappingRuleId ```ts mappingRuleId: assignMappingRuleToTenantPathParam_mappingRuleId; ``` --- ### tenantId ```ts tenantId: assignMappingRuleToTenantPathParam_tenantId; ``` --- ## Type Alias: assignRoleToClientInput ```ts type assignRoleToClientInput = object; ``` ## Properties ### clientId ```ts clientId: assignRoleToClientPathParam_clientId; ``` --- ### roleId ```ts roleId: assignRoleToClientPathParam_roleId; ``` --- ## Type Alias: assignRoleToGroupInput ```ts type assignRoleToGroupInput = object; ``` ## Properties ### groupId ```ts groupId: assignRoleToGroupPathParam_groupId; ``` --- ### roleId ```ts roleId: assignRoleToGroupPathParam_roleId; ``` --- ## Type Alias: assignRoleToMappingRuleInput ```ts type assignRoleToMappingRuleInput = object; ``` ## Properties ### mappingRuleId ```ts mappingRuleId: assignRoleToMappingRulePathParam_mappingRuleId; ``` --- ### roleId ```ts roleId: assignRoleToMappingRulePathParam_roleId; ``` --- ## Type Alias: assignRoleToTenantInput ```ts type assignRoleToTenantInput = object; ``` ## Properties ### roleId ```ts roleId: assignRoleToTenantPathParam_roleId; ``` --- ### tenantId ```ts tenantId: assignRoleToTenantPathParam_tenantId; ``` --- ## Type Alias: assignRoleToUserInput ```ts type assignRoleToUserInput = object; ``` ## Properties ### roleId ```ts roleId: assignRoleToUserPathParam_roleId; ``` --- ### username ```ts username: assignRoleToUserPathParam_username; ``` --- ## Type Alias: assignUserTaskInput ```ts type assignUserTaskInput = assignUserTaskBody & object; ``` ## Type Declaration ### userTaskKey ```ts userTaskKey: assignUserTaskPathParam_userTaskKey; ``` --- ## Type Alias: assignUserToGroupInput ```ts type assignUserToGroupInput = object; ``` ## Properties ### groupId ```ts groupId: assignUserToGroupPathParam_groupId; ``` --- ### username ```ts username: assignUserToGroupPathParam_username; ``` --- ## Type Alias: assignUserToTenantInput ```ts type assignUserToTenantInput = object; ``` ## Properties ### tenantId ```ts tenantId: assignUserToTenantPathParam_tenantId; ``` --- ### username ```ts username: assignUserToTenantPathParam_username; ``` --- ## Type Alias: broadcastSignalInput ```ts type broadcastSignalInput = broadcastSignalBody; ``` --- ## Type Alias: cancelBatchOperationInput ```ts type cancelBatchOperationInput = cancelBatchOperationBody & object; ``` ## Type Declaration ### batchOperationKey ```ts batchOperationKey: cancelBatchOperationPathParam_batchOperationKey; ``` --- ## Type Alias: cancelProcessInstanceInput ```ts type cancelProcessInstanceInput = cancelProcessInstanceBody & object; ``` ## Type Declaration ### processInstanceKey ```ts processInstanceKey: cancelProcessInstancePathParam_processInstanceKey; ``` --- ## Type Alias: cancelProcessInstancesBatchOperationInput ```ts type cancelProcessInstancesBatchOperationInput = cancelProcessInstancesBatchOperationBody; ``` --- ## Type Alias: completeJobInput ```ts type completeJobInput = completeJobBody & object; ``` ## Type Declaration ### jobKey ```ts jobKey: completeJobPathParam_jobKey; ``` --- ## Type Alias: completeUserTaskInput ```ts type completeUserTaskInput = completeUserTaskBody & object; ``` ## Type Declaration ### userTaskKey ```ts userTaskKey: completeUserTaskPathParam_userTaskKey; ``` --- ## Type Alias: correlateMessageInput ```ts type correlateMessageInput = correlateMessageBody; ``` --- ## Type Alias: createAdminUserInput ```ts type createAdminUserInput = createAdminUserBody; ``` --- ## Type Alias: createAgentInstanceInput ```ts type createAgentInstanceInput = createAgentInstanceBody; ``` --- ## Type Alias: createAuthorizationInput ```ts type createAuthorizationInput = createAuthorizationBody; ``` --- ## Type Alias: createDeploymentInput ```ts type createDeploymentInput = Omit & object; ``` ## Type Declaration ### resources ```ts resources: File[]; ``` --- ## Type Alias: createDocumentInput ```ts type createDocumentInput = createDocumentBody & object; ``` ## Type Declaration ### documentId? ```ts optional documentId?: createDocumentQueryParam_documentId; ``` ### storeId? ```ts optional storeId?: createDocumentQueryParam_storeId; ``` --- ## Type Alias: createDocumentLinkInput ```ts type createDocumentLinkInput = createDocumentLinkBody & object; ``` ## Type Declaration ### contentHash? ```ts optional contentHash?: createDocumentLinkQueryParam_contentHash; ``` ### documentId ```ts documentId: createDocumentLinkPathParam_documentId; ``` ### storeId? ```ts optional storeId?: createDocumentLinkQueryParam_storeId; ``` --- ## Type Alias: createDocumentsInput ```ts type createDocumentsInput = createDocumentsBody & object; ``` ## Type Declaration ### storeId? ```ts optional storeId?: createDocumentsQueryParam_storeId; ``` --- ## Type Alias: createElementInstanceVariablesInput ```ts type createElementInstanceVariablesInput = createElementInstanceVariablesBody & object; ``` ## Type Declaration ### elementInstanceKey ```ts elementInstanceKey: createElementInstanceVariablesPathParam_elementInstanceKey; ``` --- ## Type Alias: createGlobalClusterVariableInput ```ts type createGlobalClusterVariableInput = createGlobalClusterVariableBody; ``` --- ## Type Alias: createGlobalTaskListenerInput ```ts type createGlobalTaskListenerInput = createGlobalTaskListenerBody; ``` --- ## Type Alias: createGroupInput ```ts type createGroupInput = createGroupBody; ``` --- ## Type Alias: createMappingRuleInput ```ts type createMappingRuleInput = createMappingRuleBody; ``` --- ## Type Alias: createProcessInstanceInput ```ts type createProcessInstanceInput = createProcessInstanceBody; ``` --- ## Type Alias: createRoleInput ```ts type createRoleInput = createRoleBody; ``` --- ## Type Alias: createTenantClusterVariableInput ```ts type createTenantClusterVariableInput = createTenantClusterVariableBody & object; ``` ## Type Declaration ### tenantId ```ts tenantId: createTenantClusterVariablePathParam_tenantId; ``` --- ## Type Alias: createTenantInput ```ts type createTenantInput = createTenantBody; ``` --- ## Type Alias: createUserInput ```ts type createUserInput = createUserBody; ``` --- ## Type Alias: deleteAuthorizationInput ```ts type deleteAuthorizationInput = object; ``` ## Properties ### authorizationKey ```ts authorizationKey: deleteAuthorizationPathParam_authorizationKey; ``` --- ## Type Alias: deleteDecisionInstanceInput ```ts type deleteDecisionInstanceInput = deleteDecisionInstanceBody & object; ``` ## Type Declaration ### decisionEvaluationKey ```ts decisionEvaluationKey: deleteDecisionInstancePathParam_decisionEvaluationKey; ``` --- ## Type Alias: deleteDecisionInstancesBatchOperationInput ```ts type deleteDecisionInstancesBatchOperationInput = deleteDecisionInstancesBatchOperationBody; ``` --- ## Type Alias: deleteDocumentInput ```ts type deleteDocumentInput = object; ``` ## Properties ### documentId ```ts documentId: deleteDocumentPathParam_documentId; ``` --- ### storeId? ```ts optional storeId?: deleteDocumentQueryParam_storeId; ``` --- ## Type Alias: deleteGlobalClusterVariableInput ```ts type deleteGlobalClusterVariableInput = object; ``` ## Properties ### name ```ts name: deleteGlobalClusterVariablePathParam_name; ``` --- ## Type Alias: deleteGlobalTaskListenerInput ```ts type deleteGlobalTaskListenerInput = object; ``` ## Properties ### id ```ts id: deleteGlobalTaskListenerPathParam_id; ``` --- ## Type Alias: deleteGroupInput ```ts type deleteGroupInput = object; ``` ## Properties ### groupId ```ts groupId: deleteGroupPathParam_groupId; ``` --- ## Type Alias: deleteMappingRuleInput ```ts type deleteMappingRuleInput = object; ``` ## Properties ### mappingRuleId ```ts mappingRuleId: deleteMappingRulePathParam_mappingRuleId; ``` --- ## Type Alias: deleteProcessInstanceInput ```ts type deleteProcessInstanceInput = deleteProcessInstanceBody & object; ``` ## Type Declaration ### processInstanceKey ```ts processInstanceKey: deleteProcessInstancePathParam_processInstanceKey; ``` --- ## Type Alias: deleteProcessInstancesBatchOperationInput ```ts type deleteProcessInstancesBatchOperationInput = deleteProcessInstancesBatchOperationBody; ``` --- ## Type Alias: deleteResourceInput ```ts type deleteResourceInput = deleteResourceBody & object; ``` ## Type Declaration ### resourceKey ```ts resourceKey: deleteResourcePathParam_resourceKey; ``` --- ## Type Alias: deleteRoleInput ```ts type deleteRoleInput = object; ``` ## Properties ### roleId ```ts roleId: deleteRolePathParam_roleId; ``` --- ## Type Alias: deleteTenantClusterVariableInput ```ts type deleteTenantClusterVariableInput = object; ``` ## Properties ### name ```ts name: deleteTenantClusterVariablePathParam_name; ``` --- ### tenantId ```ts tenantId: deleteTenantClusterVariablePathParam_tenantId; ``` --- ## Type Alias: deleteTenantInput ```ts type deleteTenantInput = object; ``` ## Properties ### tenantId ```ts tenantId: deleteTenantPathParam_tenantId; ``` --- ## Type Alias: deleteUserInput ```ts type deleteUserInput = object; ``` ## Properties ### username ```ts username: deleteUserPathParam_username; ``` --- ## Type Alias: evaluateConditionalsInput ```ts type evaluateConditionalsInput = evaluateConditionalsBody; ``` --- ## Type Alias: evaluateDecisionInput ```ts type evaluateDecisionInput = evaluateDecisionBody; ``` --- ## Type Alias: evaluateExpressionInput ```ts type evaluateExpressionInput = evaluateExpressionBody; ``` --- ## Type Alias: failJobInput ```ts type failJobInput = failJobBody & object; ``` ## Type Declaration ### jobKey ```ts jobKey: failJobPathParam_jobKey; ``` --- ## Type Alias: getAgentInstanceConsistency ```ts type getAgentInstanceConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getAgentInstanceInput ```ts type getAgentInstanceInput = object; ``` ## Properties ### agentInstanceKey ```ts agentInstanceKey: getAgentInstancePathParam_agentInstanceKey; ``` --- ## Type Alias: getAuditLogConsistency ```ts type getAuditLogConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getAuditLogInput ```ts type getAuditLogInput = object; ``` ## Properties ### auditLogKey ```ts auditLogKey: getAuditLogPathParam_auditLogKey; ``` --- ## Type Alias: getAuthenticationInput ```ts type getAuthenticationInput = void; ``` --- ## Type Alias: getAuthorizationConsistency ```ts type getAuthorizationConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getAuthorizationInput ```ts type getAuthorizationInput = object; ``` ## Properties ### authorizationKey ```ts authorizationKey: getAuthorizationPathParam_authorizationKey; ``` --- ## Type Alias: getBatchOperationConsistency ```ts type getBatchOperationConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getBatchOperationInput ```ts type getBatchOperationInput = object; ``` ## Properties ### batchOperationKey ```ts batchOperationKey: getBatchOperationPathParam_batchOperationKey; ``` --- ## Type Alias: getDecisionDefinitionConsistency ```ts type getDecisionDefinitionConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getDecisionDefinitionInput ```ts type getDecisionDefinitionInput = object; ``` ## Properties ### decisionDefinitionKey ```ts decisionDefinitionKey: getDecisionDefinitionPathParam_decisionDefinitionKey; ``` --- ## Type Alias: getDecisionDefinitionXmlConsistency ```ts type getDecisionDefinitionXmlConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getDecisionDefinitionXmlInput ```ts type getDecisionDefinitionXmlInput = object; ``` ## Properties ### decisionDefinitionKey ```ts decisionDefinitionKey: getDecisionDefinitionXmlPathParam_decisionDefinitionKey; ``` --- ## Type Alias: getDecisionInstanceConsistency ```ts type getDecisionInstanceConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getDecisionInstanceInput ```ts type getDecisionInstanceInput = object; ``` ## Properties ### decisionEvaluationInstanceKey ```ts decisionEvaluationInstanceKey: getDecisionInstancePathParam_decisionEvaluationInstanceKey; ``` --- ## Type Alias: getDecisionRequirementsConsistency ```ts type getDecisionRequirementsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getDecisionRequirementsInput ```ts type getDecisionRequirementsInput = object; ``` ## Properties ### decisionRequirementsKey ```ts decisionRequirementsKey: getDecisionRequirementsPathParam_decisionRequirementsKey; ``` --- ## Type Alias: getDecisionRequirementsXmlConsistency ```ts type getDecisionRequirementsXmlConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getDecisionRequirementsXmlInput ```ts type getDecisionRequirementsXmlInput = object; ``` ## Properties ### decisionRequirementsKey ```ts decisionRequirementsKey: getDecisionRequirementsXmlPathParam_decisionRequirementsKey; ``` --- ## Type Alias: getDocumentInput ```ts type getDocumentInput = object; ``` ## Properties ### contentHash? ```ts optional contentHash?: getDocumentQueryParam_contentHash; ``` --- ### documentId ```ts documentId: getDocumentPathParam_documentId; ``` --- ### storeId? ```ts optional storeId?: getDocumentQueryParam_storeId; ``` --- ## Type Alias: getElementInstanceConsistency ```ts type getElementInstanceConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getElementInstanceInput ```ts type getElementInstanceInput = object; ``` ## Properties ### elementInstanceKey ```ts elementInstanceKey: getElementInstancePathParam_elementInstanceKey; ``` --- ## Type Alias: getFormByKeyConsistency ```ts type getFormByKeyConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getFormByKeyInput ```ts type getFormByKeyInput = object; ``` ## Properties ### formKey ```ts formKey: getFormByKeyPathParam_formKey; ``` --- ## Type Alias: getGlobalClusterVariableConsistency ```ts type getGlobalClusterVariableConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getGlobalClusterVariableInput ```ts type getGlobalClusterVariableInput = object; ``` ## Properties ### name ```ts name: getGlobalClusterVariablePathParam_name; ``` --- ## Type Alias: getGlobalJobStatisticsConsistency ```ts type getGlobalJobStatisticsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getGlobalJobStatisticsInput ```ts type getGlobalJobStatisticsInput = object; ``` ## Properties ### from ```ts from: getGlobalJobStatisticsQueryParam_from; ``` --- ### jobType? ```ts optional jobType?: getGlobalJobStatisticsQueryParam_jobType; ``` --- ### to ```ts to: getGlobalJobStatisticsQueryParam_to; ``` --- ## Type Alias: getGlobalTaskListenerConsistency ```ts type getGlobalTaskListenerConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getGlobalTaskListenerInput ```ts type getGlobalTaskListenerInput = object; ``` ## Properties ### id ```ts id: getGlobalTaskListenerPathParam_id; ``` --- ## Type Alias: getGroupConsistency ```ts type getGroupConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getGroupInput ```ts type getGroupInput = object; ``` ## Properties ### groupId ```ts groupId: getGroupPathParam_groupId; ``` --- ## Type Alias: getIncidentConsistency ```ts type getIncidentConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getIncidentInput ```ts type getIncidentInput = object; ``` ## Properties ### incidentKey ```ts incidentKey: getIncidentPathParam_incidentKey; ``` --- ## Type Alias: getJobErrorStatisticsConsistency ```ts type getJobErrorStatisticsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getJobErrorStatisticsInput ```ts type getJobErrorStatisticsInput = getJobErrorStatisticsBody; ``` --- ## Type Alias: getJobTimeSeriesStatisticsConsistency ```ts type getJobTimeSeriesStatisticsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getJobTimeSeriesStatisticsInput ```ts type getJobTimeSeriesStatisticsInput = getJobTimeSeriesStatisticsBody; ``` --- ## Type Alias: getJobTypeStatisticsConsistency ```ts type getJobTypeStatisticsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getJobTypeStatisticsInput ```ts type getJobTypeStatisticsInput = getJobTypeStatisticsBody; ``` --- ## Type Alias: getJobWorkerStatisticsConsistency ```ts type getJobWorkerStatisticsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getJobWorkerStatisticsInput ```ts type getJobWorkerStatisticsInput = getJobWorkerStatisticsBody; ``` --- ## Type Alias: getLicenseInput ```ts type getLicenseInput = void; ``` --- ## Type Alias: getMappingRuleConsistency ```ts type getMappingRuleConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getMappingRuleInput ```ts type getMappingRuleInput = object; ``` ## Properties ### mappingRuleId ```ts mappingRuleId: getMappingRulePathParam_mappingRuleId; ``` --- ## Type Alias: getProcessDefinitionConsistency ```ts type getProcessDefinitionConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getProcessDefinitionInput ```ts type getProcessDefinitionInput = object; ``` ## Properties ### processDefinitionKey ```ts processDefinitionKey: getProcessDefinitionPathParam_processDefinitionKey; ``` --- ## Type Alias: getProcessDefinitionInstanceStatisticsConsistency ```ts type getProcessDefinitionInstanceStatisticsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions< _DataOf >; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getProcessDefinitionInstanceStatisticsInput ```ts type getProcessDefinitionInstanceStatisticsInput = getProcessDefinitionInstanceStatisticsBody; ``` --- ## Type Alias: getProcessDefinitionInstanceVersionStatisticsConsistency ```ts type getProcessDefinitionInstanceVersionStatisticsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions< _DataOf >; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getProcessDefinitionInstanceVersionStatisticsInput ```ts type getProcessDefinitionInstanceVersionStatisticsInput = getProcessDefinitionInstanceVersionStatisticsBody; ``` --- ## Type Alias: getProcessDefinitionMessageSubscriptionStatisticsConsistency ```ts type getProcessDefinitionMessageSubscriptionStatisticsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions< _DataOf >; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getProcessDefinitionMessageSubscriptionStatisticsInput ```ts type getProcessDefinitionMessageSubscriptionStatisticsInput = getProcessDefinitionMessageSubscriptionStatisticsBody; ``` --- ## Type Alias: getProcessDefinitionStatisticsConsistency ```ts type getProcessDefinitionStatisticsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions< _DataOf >; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getProcessDefinitionStatisticsInput ```ts type getProcessDefinitionStatisticsInput = getProcessDefinitionStatisticsBody & object; ``` ## Type Declaration ### processDefinitionKey ```ts processDefinitionKey: getProcessDefinitionStatisticsPathParam_processDefinitionKey; ``` --- ## Type Alias: getProcessDefinitionXmlConsistency ```ts type getProcessDefinitionXmlConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getProcessDefinitionXmlInput ```ts type getProcessDefinitionXmlInput = object; ``` ## Properties ### processDefinitionKey ```ts processDefinitionKey: getProcessDefinitionXmlPathParam_processDefinitionKey; ``` --- ## Type Alias: getProcessInstanceCallHierarchyConsistency ```ts type getProcessInstanceCallHierarchyConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions< _DataOf >; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getProcessInstanceCallHierarchyInput ```ts type getProcessInstanceCallHierarchyInput = object; ``` ## Properties ### processInstanceKey ```ts processInstanceKey: getProcessInstanceCallHierarchyPathParam_processInstanceKey; ``` --- ## Type Alias: getProcessInstanceConsistency ```ts type getProcessInstanceConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getProcessInstanceInput ```ts type getProcessInstanceInput = object; ``` ## Properties ### processInstanceKey ```ts processInstanceKey: getProcessInstancePathParam_processInstanceKey; ``` --- ## Type Alias: getProcessInstanceSequenceFlowsConsistency ```ts type getProcessInstanceSequenceFlowsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions< _DataOf >; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getProcessInstanceSequenceFlowsInput ```ts type getProcessInstanceSequenceFlowsInput = object; ``` ## Properties ### processInstanceKey ```ts processInstanceKey: getProcessInstanceSequenceFlowsPathParam_processInstanceKey; ``` --- ## Type Alias: getProcessInstanceStatisticsByDefinitionConsistency ```ts type getProcessInstanceStatisticsByDefinitionConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions< _DataOf >; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getProcessInstanceStatisticsByDefinitionInput ```ts type getProcessInstanceStatisticsByDefinitionInput = getProcessInstanceStatisticsByDefinitionBody; ``` --- ## Type Alias: getProcessInstanceStatisticsByErrorConsistency ```ts type getProcessInstanceStatisticsByErrorConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions< _DataOf >; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getProcessInstanceStatisticsByErrorInput ```ts type getProcessInstanceStatisticsByErrorInput = getProcessInstanceStatisticsByErrorBody; ``` --- ## Type Alias: getProcessInstanceStatisticsConsistency ```ts type getProcessInstanceStatisticsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions< _DataOf >; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getProcessInstanceStatisticsInput ```ts type getProcessInstanceStatisticsInput = object; ``` ## Properties ### processInstanceKey ```ts processInstanceKey: getProcessInstanceStatisticsPathParam_processInstanceKey; ``` --- ## Type Alias: getResourceConsistency ```ts type getResourceConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getResourceContentBinaryConsistency ```ts type getResourceContentBinaryConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getResourceContentBinaryInput ```ts type getResourceContentBinaryInput = object; ``` ## Properties ### resourceKey ```ts resourceKey: getResourceContentBinaryPathParam_resourceKey; ``` --- ## Type Alias: getResourceContentConsistency ```ts type getResourceContentConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getResourceContentInput ```ts type getResourceContentInput = object; ``` ## Properties ### resourceKey ```ts resourceKey: getResourceContentPathParam_resourceKey; ``` --- ## Type Alias: getResourceInput ```ts type getResourceInput = object; ``` ## Properties ### resourceKey ```ts resourceKey: getResourcePathParam_resourceKey; ``` --- ## Type Alias: getRoleConsistency ```ts type getRoleConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getRoleInput ```ts type getRoleInput = object; ``` ## Properties ### roleId ```ts roleId: getRolePathParam_roleId; ``` --- ## Type Alias: getStartProcessFormConsistency ```ts type getStartProcessFormConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getStartProcessFormInput ```ts type getStartProcessFormInput = object; ``` ## Properties ### processDefinitionKey ```ts processDefinitionKey: getStartProcessFormPathParam_processDefinitionKey; ``` --- ## Type Alias: getStatusInput ```ts type getStatusInput = void; ``` --- ## Type Alias: getSystemConfigurationInput ```ts type getSystemConfigurationInput = void; ``` --- ## Type Alias: getTenantClusterVariableConsistency ```ts type getTenantClusterVariableConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getTenantClusterVariableInput ```ts type getTenantClusterVariableInput = object; ``` ## Properties ### name ```ts name: getTenantClusterVariablePathParam_name; ``` --- ### tenantId ```ts tenantId: getTenantClusterVariablePathParam_tenantId; ``` --- ## Type Alias: getTenantConsistency ```ts type getTenantConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getTenantInput ```ts type getTenantInput = object; ``` ## Properties ### tenantId ```ts tenantId: getTenantPathParam_tenantId; ``` --- ## Type Alias: getTopologyInput ```ts type getTopologyInput = void; ``` --- ## Type Alias: getUsageMetricsConsistency ```ts type getUsageMetricsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getUsageMetricsInput ```ts type getUsageMetricsInput = object; ``` ## Properties ### endTime ```ts endTime: getUsageMetricsQueryParam_endTime; ``` --- ### startTime ```ts startTime: getUsageMetricsQueryParam_startTime; ``` --- ### tenantId? ```ts optional tenantId?: getUsageMetricsQueryParam_tenantId; ``` --- ### withTenants? ```ts optional withTenants?: getUsageMetricsQueryParam_withTenants; ``` --- ## Type Alias: getUserConsistency ```ts type getUserConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getUserInput ```ts type getUserInput = object; ``` ## Properties ### username ```ts username: getUserPathParam_username; ``` --- ## Type Alias: getUserTaskConsistency ```ts type getUserTaskConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getUserTaskFormConsistency ```ts type getUserTaskFormConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getUserTaskFormInput ```ts type getUserTaskFormInput = object; ``` ## Properties ### userTaskKey ```ts userTaskKey: getUserTaskFormPathParam_userTaskKey; ``` --- ## Type Alias: getUserTaskInput ```ts type getUserTaskInput = object; ``` ## Properties ### userTaskKey ```ts userTaskKey: getUserTaskPathParam_userTaskKey; ``` --- ## Type Alias: getVariableConsistency ```ts type getVariableConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: getVariableInput ```ts type getVariableInput = object; ``` ## Properties ### variableKey ```ts variableKey: getVariablePathParam_variableKey; ``` --- ## Type Alias: migrateProcessInstanceInput ```ts type migrateProcessInstanceInput = migrateProcessInstanceBody & object; ``` ## Type Declaration ### processInstanceKey ```ts processInstanceKey: migrateProcessInstancePathParam_processInstanceKey; ``` --- ## Type Alias: migrateProcessInstancesBatchOperationInput ```ts type migrateProcessInstancesBatchOperationInput = migrateProcessInstancesBatchOperationBody; ``` --- ## Type Alias: modifyProcessInstanceInput ```ts type modifyProcessInstanceInput = modifyProcessInstanceBody & object; ``` ## Type Declaration ### processInstanceKey ```ts processInstanceKey: modifyProcessInstancePathParam_processInstanceKey; ``` --- ## Type Alias: modifyProcessInstancesBatchOperationInput ```ts type modifyProcessInstancesBatchOperationInput = modifyProcessInstancesBatchOperationBody; ``` --- ## Type Alias: pinClockInput ```ts type pinClockInput = pinClockBody; ``` --- ## Type Alias: publishMessageInput ```ts type publishMessageInput = publishMessageBody; ``` --- ## Type Alias: resetClockInput ```ts type resetClockInput = void; ``` --- ## Type Alias: resolveIncidentInput ```ts type resolveIncidentInput = resolveIncidentBody & object; ``` ## Type Declaration ### incidentKey ```ts incidentKey: resolveIncidentPathParam_incidentKey; ``` --- ## Type Alias: resolveIncidentsBatchOperationInput ```ts type resolveIncidentsBatchOperationInput = resolveIncidentsBatchOperationBody; ``` --- ## Type Alias: resolveProcessInstanceIncidentsInput ```ts type resolveProcessInstanceIncidentsInput = object; ``` ## Properties ### processInstanceKey ```ts processInstanceKey: resolveProcessInstanceIncidentsPathParam_processInstanceKey; ``` --- ## Type Alias: resumeBatchOperationInput ```ts type resumeBatchOperationInput = resumeBatchOperationBody & object; ``` ## Type Declaration ### batchOperationKey ```ts batchOperationKey: resumeBatchOperationPathParam_batchOperationKey; ``` --- ## Type Alias: searchAgentInstancesConsistency ```ts type searchAgentInstancesConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchAgentInstancesInput ```ts type searchAgentInstancesInput = searchAgentInstancesBody; ``` --- ## Type Alias: searchAuditLogsConsistency ```ts type searchAuditLogsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchAuditLogsInput ```ts type searchAuditLogsInput = searchAuditLogsBody; ``` --- ## Type Alias: searchAuthorizationsConsistency ```ts type searchAuthorizationsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchAuthorizationsInput ```ts type searchAuthorizationsInput = searchAuthorizationsBody; ``` --- ## Type Alias: searchBatchOperationItemsConsistency ```ts type searchBatchOperationItemsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchBatchOperationItemsInput ```ts type searchBatchOperationItemsInput = searchBatchOperationItemsBody; ``` --- ## Type Alias: searchBatchOperationsConsistency ```ts type searchBatchOperationsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchBatchOperationsInput ```ts type searchBatchOperationsInput = searchBatchOperationsBody; ``` --- ## Type Alias: searchClientsForGroupConsistency ```ts type searchClientsForGroupConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchClientsForGroupInput ```ts type searchClientsForGroupInput = searchClientsForGroupBody & object; ``` ## Type Declaration ### groupId ```ts groupId: searchClientsForGroupPathParam_groupId; ``` --- ## Type Alias: searchClientsForRoleConsistency ```ts type searchClientsForRoleConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchClientsForRoleInput ```ts type searchClientsForRoleInput = searchClientsForRoleBody & object; ``` ## Type Declaration ### roleId ```ts roleId: searchClientsForRolePathParam_roleId; ``` --- ## Type Alias: searchClientsForTenantConsistency ```ts type searchClientsForTenantConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchClientsForTenantInput ```ts type searchClientsForTenantInput = searchClientsForTenantBody & object; ``` ## Type Declaration ### tenantId ```ts tenantId: searchClientsForTenantPathParam_tenantId; ``` --- ## Type Alias: searchClusterVariablesConsistency ```ts type searchClusterVariablesConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchClusterVariablesInput ```ts type searchClusterVariablesInput = searchClusterVariablesBody & object; ``` ## Type Declaration ### truncateValues? ```ts optional truncateValues?: searchClusterVariablesQueryParam_truncateValues; ``` --- ## Type Alias: searchCorrelatedMessageSubscriptionsConsistency ```ts type searchCorrelatedMessageSubscriptionsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions< _DataOf >; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchCorrelatedMessageSubscriptionsInput ```ts type searchCorrelatedMessageSubscriptionsInput = searchCorrelatedMessageSubscriptionsBody; ``` --- ## Type Alias: searchDecisionDefinitionsConsistency ```ts type searchDecisionDefinitionsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchDecisionDefinitionsInput ```ts type searchDecisionDefinitionsInput = searchDecisionDefinitionsBody; ``` --- ## Type Alias: searchDecisionInstancesConsistency ```ts type searchDecisionInstancesConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchDecisionInstancesInput ```ts type searchDecisionInstancesInput = searchDecisionInstancesBody; ``` --- ## Type Alias: searchDecisionRequirementsConsistency ```ts type searchDecisionRequirementsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchDecisionRequirementsInput ```ts type searchDecisionRequirementsInput = searchDecisionRequirementsBody; ``` --- ## Type Alias: searchElementInstanceIncidentsConsistency ```ts type searchElementInstanceIncidentsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions< _DataOf >; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchElementInstanceIncidentsInput ```ts type searchElementInstanceIncidentsInput = searchElementInstanceIncidentsBody & object; ``` ## Type Declaration ### elementInstanceKey ```ts elementInstanceKey: searchElementInstanceIncidentsPathParam_elementInstanceKey; ``` --- ## Type Alias: searchElementInstancesConsistency ```ts type searchElementInstancesConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchElementInstancesInput ```ts type searchElementInstancesInput = searchElementInstancesBody; ``` --- ## Type Alias: searchGlobalTaskListenersConsistency ```ts type searchGlobalTaskListenersConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchGlobalTaskListenersInput ```ts type searchGlobalTaskListenersInput = searchGlobalTaskListenersBody; ``` --- ## Type Alias: searchGroupIdsForTenantConsistency ```ts type searchGroupIdsForTenantConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchGroupIdsForTenantInput ```ts type searchGroupIdsForTenantInput = searchGroupIdsForTenantBody & object; ``` ## Type Declaration ### tenantId ```ts tenantId: searchGroupIdsForTenantPathParam_tenantId; ``` --- ## Type Alias: searchGroupsConsistency ```ts type searchGroupsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchGroupsForRoleConsistency ```ts type searchGroupsForRoleConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchGroupsForRoleInput ```ts type searchGroupsForRoleInput = searchGroupsForRoleBody & object; ``` ## Type Declaration ### roleId ```ts roleId: searchGroupsForRolePathParam_roleId; ``` --- ## Type Alias: searchGroupsInput ```ts type searchGroupsInput = searchGroupsBody; ``` --- ## Type Alias: searchIncidentsConsistency ```ts type searchIncidentsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchIncidentsInput ```ts type searchIncidentsInput = searchIncidentsBody; ``` --- ## Type Alias: searchJobsConsistency ```ts type searchJobsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchJobsInput ```ts type searchJobsInput = searchJobsBody; ``` --- ## Type Alias: searchMappingRuleConsistency ```ts type searchMappingRuleConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchMappingRuleInput ```ts type searchMappingRuleInput = searchMappingRuleBody; ``` --- ## Type Alias: searchMappingRulesForGroupConsistency ```ts type searchMappingRulesForGroupConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchMappingRulesForGroupInput ```ts type searchMappingRulesForGroupInput = searchMappingRulesForGroupBody & object; ``` ## Type Declaration ### groupId ```ts groupId: searchMappingRulesForGroupPathParam_groupId; ``` --- ## Type Alias: searchMappingRulesForRoleConsistency ```ts type searchMappingRulesForRoleConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchMappingRulesForRoleInput ```ts type searchMappingRulesForRoleInput = searchMappingRulesForRoleBody & object; ``` ## Type Declaration ### roleId ```ts roleId: searchMappingRulesForRolePathParam_roleId; ``` --- ## Type Alias: searchMappingRulesForTenantConsistency ```ts type searchMappingRulesForTenantConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions< _DataOf >; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchMappingRulesForTenantInput ```ts type searchMappingRulesForTenantInput = searchMappingRulesForTenantBody & object; ``` ## Type Declaration ### tenantId ```ts tenantId: searchMappingRulesForTenantPathParam_tenantId; ``` --- ## Type Alias: searchMessageSubscriptionsConsistency ```ts type searchMessageSubscriptionsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchMessageSubscriptionsInput ```ts type searchMessageSubscriptionsInput = searchMessageSubscriptionsBody; ``` --- ## Type Alias: searchProcessDefinitionsConsistency ```ts type searchProcessDefinitionsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchProcessDefinitionsInput ```ts type searchProcessDefinitionsInput = searchProcessDefinitionsBody; ``` --- ## Type Alias: searchProcessInstanceIncidentsConsistency ```ts type searchProcessInstanceIncidentsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions< _DataOf >; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchProcessInstanceIncidentsInput ```ts type searchProcessInstanceIncidentsInput = searchProcessInstanceIncidentsBody & object; ``` ## Type Declaration ### processInstanceKey ```ts processInstanceKey: searchProcessInstanceIncidentsPathParam_processInstanceKey; ``` --- ## Type Alias: searchProcessInstancesConsistency ```ts type searchProcessInstancesConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchProcessInstancesInput ```ts type searchProcessInstancesInput = searchProcessInstancesBody; ``` --- ## Type Alias: searchResourcesConsistency ```ts type searchResourcesConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchResourcesInput ```ts type searchResourcesInput = searchResourcesBody; ``` --- ## Type Alias: searchRolesConsistency ```ts type searchRolesConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchRolesForGroupConsistency ```ts type searchRolesForGroupConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchRolesForGroupInput ```ts type searchRolesForGroupInput = searchRolesForGroupBody & object; ``` ## Type Declaration ### groupId ```ts groupId: searchRolesForGroupPathParam_groupId; ``` --- ## Type Alias: searchRolesForTenantConsistency ```ts type searchRolesForTenantConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchRolesForTenantInput ```ts type searchRolesForTenantInput = searchRolesForTenantBody & object; ``` ## Type Declaration ### tenantId ```ts tenantId: searchRolesForTenantPathParam_tenantId; ``` --- ## Type Alias: searchRolesInput ```ts type searchRolesInput = searchRolesBody; ``` --- ## Type Alias: searchTenantsConsistency ```ts type searchTenantsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchTenantsInput ```ts type searchTenantsInput = searchTenantsBody; ``` --- ## Type Alias: searchUserTaskAuditLogsConsistency ```ts type searchUserTaskAuditLogsConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchUserTaskAuditLogsInput ```ts type searchUserTaskAuditLogsInput = searchUserTaskAuditLogsBody & object; ``` ## Type Declaration ### userTaskKey ```ts userTaskKey: searchUserTaskAuditLogsPathParam_userTaskKey; ``` --- ## Type Alias: searchUserTaskEffectiveVariablesConsistency ```ts type searchUserTaskEffectiveVariablesConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions< _DataOf >; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchUserTaskEffectiveVariablesInput ```ts type searchUserTaskEffectiveVariablesInput = searchUserTaskEffectiveVariablesBody & object; ``` ## Type Declaration ### truncateValues? ```ts optional truncateValues?: searchUserTaskEffectiveVariablesQueryParam_truncateValues; ``` ### userTaskKey ```ts userTaskKey: searchUserTaskEffectiveVariablesPathParam_userTaskKey; ``` --- ## Type Alias: searchUserTaskVariablesConsistency ```ts type searchUserTaskVariablesConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchUserTaskVariablesInput ```ts type searchUserTaskVariablesInput = searchUserTaskVariablesBody & object; ``` ## Type Declaration ### truncateValues? ```ts optional truncateValues?: searchUserTaskVariablesQueryParam_truncateValues; ``` ### userTaskKey ```ts userTaskKey: searchUserTaskVariablesPathParam_userTaskKey; ``` --- ## Type Alias: searchUserTasksConsistency ```ts type searchUserTasksConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchUserTasksInput ```ts type searchUserTasksInput = searchUserTasksBody; ``` --- ## Type Alias: searchUsersConsistency ```ts type searchUsersConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchUsersForGroupConsistency ```ts type searchUsersForGroupConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchUsersForGroupInput ```ts type searchUsersForGroupInput = searchUsersForGroupBody & object; ``` ## Type Declaration ### groupId ```ts groupId: searchUsersForGroupPathParam_groupId; ``` --- ## Type Alias: searchUsersForRoleConsistency ```ts type searchUsersForRoleConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchUsersForRoleInput ```ts type searchUsersForRoleInput = searchUsersForRoleBody & object; ``` ## Type Declaration ### roleId ```ts roleId: searchUsersForRolePathParam_roleId; ``` --- ## Type Alias: searchUsersForTenantConsistency ```ts type searchUsersForTenantConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchUsersForTenantInput ```ts type searchUsersForTenantInput = searchUsersForTenantBody & object; ``` ## Type Declaration ### tenantId ```ts tenantId: searchUsersForTenantPathParam_tenantId; ``` --- ## Type Alias: searchUsersInput ```ts type searchUsersInput = searchUsersBody; ``` --- ## Type Alias: searchVariablesConsistency ```ts type searchVariablesConsistency = object; ``` Management of eventual consistency \* ## Properties ### consistency ```ts consistency: ConsistencyOptions<_DataOf>; ``` Management of eventual consistency tolerance. Set waitUpToMs to 0 to ignore eventual consistency. pollInterval is 500ms by default. --- ## Type Alias: searchVariablesInput ```ts type searchVariablesInput = searchVariablesBody & object; ``` ## Type Declaration ### truncateValues? ```ts optional truncateValues?: searchVariablesQueryParam_truncateValues; ``` --- ## Type Alias: suspendBatchOperationInput ```ts type suspendBatchOperationInput = suspendBatchOperationBody & object; ``` ## Type Declaration ### batchOperationKey ```ts batchOperationKey: suspendBatchOperationPathParam_batchOperationKey; ``` --- ## Type Alias: throwJobErrorInput ```ts type throwJobErrorInput = throwJobErrorBody & object; ``` ## Type Declaration ### jobKey ```ts jobKey: throwJobErrorPathParam_jobKey; ``` --- ## Type Alias: unassignClientFromGroupInput ```ts type unassignClientFromGroupInput = object; ``` ## Properties ### clientId ```ts clientId: unassignClientFromGroupPathParam_clientId; ``` --- ### groupId ```ts groupId: unassignClientFromGroupPathParam_groupId; ``` --- ## Type Alias: unassignClientFromTenantInput ```ts type unassignClientFromTenantInput = object; ``` ## Properties ### clientId ```ts clientId: unassignClientFromTenantPathParam_clientId; ``` --- ### tenantId ```ts tenantId: unassignClientFromTenantPathParam_tenantId; ``` --- ## Type Alias: unassignGroupFromTenantInput ```ts type unassignGroupFromTenantInput = object; ``` ## Properties ### groupId ```ts groupId: unassignGroupFromTenantPathParam_groupId; ``` --- ### tenantId ```ts tenantId: unassignGroupFromTenantPathParam_tenantId; ``` --- ## Type Alias: unassignMappingRuleFromGroupInput ```ts type unassignMappingRuleFromGroupInput = object; ``` ## Properties ### groupId ```ts groupId: unassignMappingRuleFromGroupPathParam_groupId; ``` --- ### mappingRuleId ```ts mappingRuleId: unassignMappingRuleFromGroupPathParam_mappingRuleId; ``` --- ## Type Alias: unassignMappingRuleFromTenantInput ```ts type unassignMappingRuleFromTenantInput = object; ``` ## Properties ### mappingRuleId ```ts mappingRuleId: unassignMappingRuleFromTenantPathParam_mappingRuleId; ``` --- ### tenantId ```ts tenantId: unassignMappingRuleFromTenantPathParam_tenantId; ``` --- ## Type Alias: unassignRoleFromClientInput ```ts type unassignRoleFromClientInput = object; ``` ## Properties ### clientId ```ts clientId: unassignRoleFromClientPathParam_clientId; ``` --- ### roleId ```ts roleId: unassignRoleFromClientPathParam_roleId; ``` --- ## Type Alias: unassignRoleFromGroupInput ```ts type unassignRoleFromGroupInput = object; ``` ## Properties ### groupId ```ts groupId: unassignRoleFromGroupPathParam_groupId; ``` --- ### roleId ```ts roleId: unassignRoleFromGroupPathParam_roleId; ``` --- ## Type Alias: unassignRoleFromMappingRuleInput ```ts type unassignRoleFromMappingRuleInput = object; ``` ## Properties ### mappingRuleId ```ts mappingRuleId: unassignRoleFromMappingRulePathParam_mappingRuleId; ``` --- ### roleId ```ts roleId: unassignRoleFromMappingRulePathParam_roleId; ``` --- ## Type Alias: unassignRoleFromTenantInput ```ts type unassignRoleFromTenantInput = object; ``` ## Properties ### roleId ```ts roleId: unassignRoleFromTenantPathParam_roleId; ``` --- ### tenantId ```ts tenantId: unassignRoleFromTenantPathParam_tenantId; ``` --- ## Type Alias: unassignRoleFromUserInput ```ts type unassignRoleFromUserInput = object; ``` ## Properties ### roleId ```ts roleId: unassignRoleFromUserPathParam_roleId; ``` --- ### username ```ts username: unassignRoleFromUserPathParam_username; ``` --- ## Type Alias: unassignUserFromGroupInput ```ts type unassignUserFromGroupInput = object; ``` ## Properties ### groupId ```ts groupId: unassignUserFromGroupPathParam_groupId; ``` --- ### username ```ts username: unassignUserFromGroupPathParam_username; ``` --- ## Type Alias: unassignUserFromTenantInput ```ts type unassignUserFromTenantInput = object; ``` ## Properties ### tenantId ```ts tenantId: unassignUserFromTenantPathParam_tenantId; ``` --- ### username ```ts username: unassignUserFromTenantPathParam_username; ``` --- ## Type Alias: unassignUserTaskInput ```ts type unassignUserTaskInput = object; ``` ## Properties ### userTaskKey ```ts userTaskKey: unassignUserTaskPathParam_userTaskKey; ``` --- ## Type Alias: updateAgentInstanceInput ```ts type updateAgentInstanceInput = updateAgentInstanceBody & object; ``` ## Type Declaration ### agentInstanceKey ```ts agentInstanceKey: updateAgentInstancePathParam_agentInstanceKey; ``` --- ## Type Alias: updateAuthorizationInput ```ts type updateAuthorizationInput = updateAuthorizationBody & object; ``` ## Type Declaration ### authorizationKey ```ts authorizationKey: updateAuthorizationPathParam_authorizationKey; ``` --- ## Type Alias: updateGlobalClusterVariableInput ```ts type updateGlobalClusterVariableInput = updateGlobalClusterVariableBody & object; ``` ## Type Declaration ### name ```ts name: updateGlobalClusterVariablePathParam_name; ``` --- ## Type Alias: updateGlobalTaskListenerInput ```ts type updateGlobalTaskListenerInput = updateGlobalTaskListenerBody & object; ``` ## Type Declaration ### id ```ts id: updateGlobalTaskListenerPathParam_id; ``` --- ## Type Alias: updateGroupInput ```ts type updateGroupInput = updateGroupBody & object; ``` ## Type Declaration ### groupId ```ts groupId: updateGroupPathParam_groupId; ``` --- ## Type Alias: updateJobInput ```ts type updateJobInput = updateJobBody & object; ``` ## Type Declaration ### jobKey ```ts jobKey: updateJobPathParam_jobKey; ``` --- ## Type Alias: updateMappingRuleInput ```ts type updateMappingRuleInput = updateMappingRuleBody & object; ``` ## Type Declaration ### mappingRuleId ```ts mappingRuleId: updateMappingRulePathParam_mappingRuleId; ``` --- ## Type Alias: updateRoleInput ```ts type updateRoleInput = updateRoleBody & object; ``` ## Type Declaration ### roleId ```ts roleId: updateRolePathParam_roleId; ``` --- ## Type Alias: updateTenantClusterVariableInput ```ts type updateTenantClusterVariableInput = updateTenantClusterVariableBody & object; ``` ## Type Declaration ### name ```ts name: updateTenantClusterVariablePathParam_name; ``` ### tenantId ```ts tenantId: updateTenantClusterVariablePathParam_tenantId; ``` --- ## Type Alias: updateTenantInput ```ts type updateTenantInput = updateTenantBody & object; ``` ## Type Declaration ### tenantId ```ts tenantId: updateTenantPathParam_tenantId; ``` --- ## Type Alias: updateUserInput ```ts type updateUserInput = updateUserBody & object; ``` ## Type Declaration ### username ```ts username: updateUserPathParam_username; ``` --- ## Type Alias: updateUserTaskInput ```ts type updateUserTaskInput = updateUserTaskBody & object; ``` ## Type Declaration ### userTaskKey ```ts userTaskKey: updateUserTaskPathParam_userTaskKey; ``` --- ## Variable: AgentInstanceStatusEnum ```ts const AgentInstanceStatusEnum: object; ``` The current status of an agent instance. ## Type Declaration ### COMPLETED ```ts readonly COMPLETED: "COMPLETED" = 'COMPLETED'; ``` ### IDLE ```ts readonly IDLE: "IDLE" = 'IDLE'; ``` ### INITIALIZING ```ts readonly INITIALIZING: "INITIALIZING" = 'INITIALIZING'; ``` ### THINKING ```ts readonly THINKING: "THINKING" = 'THINKING'; ``` ### TOOL_CALLING ```ts readonly TOOL_CALLING: "TOOL_CALLING" = 'TOOL_CALLING'; ``` ### TOOL_DISCOVERY ```ts readonly TOOL_DISCOVERY: "TOOL_DISCOVERY" = 'TOOL_DISCOVERY'; ``` --- ## Variable: AuditLogActorTypeEnum ```ts const AuditLogActorTypeEnum: object; ``` The type of actor who performed the operation. ## Type Declaration ### ANONYMOUS ```ts readonly ANONYMOUS: "ANONYMOUS" = 'ANONYMOUS'; ``` ### CLIENT ```ts readonly CLIENT: "CLIENT" = 'CLIENT'; ``` ### UNKNOWN ```ts readonly UNKNOWN: "UNKNOWN" = 'UNKNOWN'; ``` ### USER ```ts readonly USER: "USER" = 'USER'; ``` --- ## Variable: AuditLogCategoryEnum ```ts const AuditLogCategoryEnum: object; ``` The category of the audit log operation. ## Type Declaration ### ADMIN ```ts readonly ADMIN: "ADMIN" = 'ADMIN'; ``` ### DEPLOYED_RESOURCES ```ts readonly DEPLOYED_RESOURCES: "DEPLOYED_RESOURCES" = 'DEPLOYED_RESOURCES'; ``` ### USER_TASKS ```ts readonly USER_TASKS: "USER_TASKS" = 'USER_TASKS'; ``` --- ## Variable: AuditLogEntityTypeEnum ```ts const AuditLogEntityTypeEnum: object; ``` The type of entity affected by the operation. ## Type Declaration ### AUTHORIZATION ```ts readonly AUTHORIZATION: "AUTHORIZATION" = 'AUTHORIZATION'; ``` ### BATCH ```ts readonly BATCH: "BATCH" = 'BATCH'; ``` ### CLIENT ```ts readonly CLIENT: "CLIENT" = 'CLIENT'; ``` ### DECISION ```ts readonly DECISION: "DECISION" = 'DECISION'; ``` ### GROUP ```ts readonly GROUP: "GROUP" = 'GROUP'; ``` ### INCIDENT ```ts readonly INCIDENT: "INCIDENT" = 'INCIDENT'; ``` ### JOB ```ts readonly JOB: "JOB" = 'JOB'; ``` ### MAPPING_RULE ```ts readonly MAPPING_RULE: "MAPPING_RULE" = 'MAPPING_RULE'; ``` ### PROCESS_INSTANCE ```ts readonly PROCESS_INSTANCE: "PROCESS_INSTANCE" = 'PROCESS_INSTANCE'; ``` ### RESOURCE ```ts readonly RESOURCE: "RESOURCE" = 'RESOURCE'; ``` ### ROLE ```ts readonly ROLE: "ROLE" = 'ROLE'; ``` ### TENANT ```ts readonly TENANT: "TENANT" = 'TENANT'; ``` ### USER ```ts readonly USER: "USER" = 'USER'; ``` ### USER_TASK ```ts readonly USER_TASK: "USER_TASK" = 'USER_TASK'; ``` ### VARIABLE ```ts readonly VARIABLE: "VARIABLE" = 'VARIABLE'; ``` --- ## Variable: AuditLogOperationTypeEnum ```ts const AuditLogOperationTypeEnum: object; ``` The type of operation performed. ## Type Declaration ### ASSIGN ```ts readonly ASSIGN: "ASSIGN" = 'ASSIGN'; ``` ### CANCEL ```ts readonly CANCEL: "CANCEL" = 'CANCEL'; ``` ### COMPLETE ```ts readonly COMPLETE: "COMPLETE" = 'COMPLETE'; ``` ### CREATE ```ts readonly CREATE: "CREATE" = 'CREATE'; ``` ### DELETE ```ts readonly DELETE: "DELETE" = 'DELETE'; ``` ### EVALUATE ```ts readonly EVALUATE: "EVALUATE" = 'EVALUATE'; ``` ### MIGRATE ```ts readonly MIGRATE: "MIGRATE" = 'MIGRATE'; ``` ### MODIFY ```ts readonly MODIFY: "MODIFY" = 'MODIFY'; ``` ### RESOLVE ```ts readonly RESOLVE: "RESOLVE" = 'RESOLVE'; ``` ### RESUME ```ts readonly RESUME: "RESUME" = 'RESUME'; ``` ### SUSPEND ```ts readonly SUSPEND: "SUSPEND" = 'SUSPEND'; ``` ### UNASSIGN ```ts readonly UNASSIGN: "UNASSIGN" = 'UNASSIGN'; ``` ### UNKNOWN ```ts readonly UNKNOWN: "UNKNOWN" = 'UNKNOWN'; ``` ### UPDATE ```ts readonly UPDATE: "UPDATE" = 'UPDATE'; ``` --- ## Variable: AuditLogResultEnum ```ts const AuditLogResultEnum: object; ``` The result status of the operation. ## Type Declaration ### FAIL ```ts readonly FAIL: "FAIL" = 'FAIL'; ``` ### SUCCESS ```ts readonly SUCCESS: "SUCCESS" = 'SUCCESS'; ``` --- ## Variable: BatchOperationItemStateEnum ```ts const BatchOperationItemStateEnum: object; ``` The batch operation item state. ## Type Declaration ### ACTIVE ```ts readonly ACTIVE: "ACTIVE" = 'ACTIVE'; ``` ### CANCELED ```ts readonly CANCELED: "CANCELED" = 'CANCELED'; ``` ### COMPLETED ```ts readonly COMPLETED: "COMPLETED" = 'COMPLETED'; ``` ### FAILED ```ts readonly FAILED: "FAILED" = 'FAILED'; ``` --- ## Variable: BatchOperationStateEnum ```ts const BatchOperationStateEnum: object; ``` The batch operation state. ## Type Declaration ### ACTIVE ```ts readonly ACTIVE: "ACTIVE" = 'ACTIVE'; ``` ### CANCELED ```ts readonly CANCELED: "CANCELED" = 'CANCELED'; ``` ### COMPLETED ```ts readonly COMPLETED: "COMPLETED" = 'COMPLETED'; ``` ### CREATED ```ts readonly CREATED: "CREATED" = 'CREATED'; ``` ### FAILED ```ts readonly FAILED: "FAILED" = 'FAILED'; ``` ### PARTIALLY_COMPLETED ```ts readonly PARTIALLY_COMPLETED: "PARTIALLY_COMPLETED" = 'PARTIALLY_COMPLETED'; ``` ### SUSPENDED ```ts readonly SUSPENDED: "SUSPENDED" = 'SUSPENDED'; ``` --- ## Variable: BatchOperationTypeEnum ```ts const BatchOperationTypeEnum: object; ``` The type of the batch operation. ## Type Declaration ### ADD_VARIABLE ```ts readonly ADD_VARIABLE: "ADD_VARIABLE" = 'ADD_VARIABLE'; ``` ### CANCEL_PROCESS_INSTANCE ```ts readonly CANCEL_PROCESS_INSTANCE: "CANCEL_PROCESS_INSTANCE" = 'CANCEL_PROCESS_INSTANCE'; ``` ### DELETE_DECISION_DEFINITION ```ts readonly DELETE_DECISION_DEFINITION: "DELETE_DECISION_DEFINITION" = 'DELETE_DECISION_DEFINITION'; ``` ### DELETE_DECISION_INSTANCE ```ts readonly DELETE_DECISION_INSTANCE: "DELETE_DECISION_INSTANCE" = 'DELETE_DECISION_INSTANCE'; ``` ### DELETE_PROCESS_DEFINITION ```ts readonly DELETE_PROCESS_DEFINITION: "DELETE_PROCESS_DEFINITION" = 'DELETE_PROCESS_DEFINITION'; ``` ### DELETE_PROCESS_INSTANCE ```ts readonly DELETE_PROCESS_INSTANCE: "DELETE_PROCESS_INSTANCE" = 'DELETE_PROCESS_INSTANCE'; ``` ### MIGRATE_PROCESS_INSTANCE ```ts readonly MIGRATE_PROCESS_INSTANCE: "MIGRATE_PROCESS_INSTANCE" = 'MIGRATE_PROCESS_INSTANCE'; ``` ### MODIFY_PROCESS_INSTANCE ```ts readonly MODIFY_PROCESS_INSTANCE: "MODIFY_PROCESS_INSTANCE" = 'MODIFY_PROCESS_INSTANCE'; ``` ### RESOLVE_INCIDENT ```ts readonly RESOLVE_INCIDENT: "RESOLVE_INCIDENT" = 'RESOLVE_INCIDENT'; ``` ### UPDATE_VARIABLE ```ts readonly UPDATE_VARIABLE: "UPDATE_VARIABLE" = 'UPDATE_VARIABLE'; ``` --- ## Variable: ClusterVariableScopeEnum ```ts const ClusterVariableScopeEnum: object; ``` The scope of a cluster variable. ## Type Declaration ### GLOBAL ```ts readonly GLOBAL: "GLOBAL" = 'GLOBAL'; ``` ### TENANT ```ts readonly TENANT: "TENANT" = 'TENANT'; ``` --- ## Variable: DecisionDefinitionTypeEnum ```ts const DecisionDefinitionTypeEnum: object; ``` The type of the decision. UNSPECIFIED is deprecated and should not be used anymore, for removal in 8.10 ## Type Declaration ### DECISION_TABLE ```ts readonly DECISION_TABLE: "DECISION_TABLE" = 'DECISION_TABLE'; ``` ### LITERAL_EXPRESSION ```ts readonly LITERAL_EXPRESSION: "LITERAL_EXPRESSION" = 'LITERAL_EXPRESSION'; ``` ### UNKNOWN ```ts readonly UNKNOWN: "UNKNOWN" = 'UNKNOWN'; ``` ### ~~UNSPECIFIED~~ ```ts readonly UNSPECIFIED: "UNSPECIFIED" = 'UNSPECIFIED'; ``` #### Deprecated since 8.9.0 --- ## Variable: DecisionInstanceStateEnum ```ts const DecisionInstanceStateEnum: object; ``` The state of the decision instance. UNSPECIFIED and UNKNOWN are deprecated and should not be used anymore, for removal in 8.10 ## Type Declaration ### EVALUATED ```ts readonly EVALUATED: "EVALUATED" = 'EVALUATED'; ``` ### FAILED ```ts readonly FAILED: "FAILED" = 'FAILED'; ``` ### ~~UNKNOWN~~ ```ts readonly UNKNOWN: "UNKNOWN" = 'UNKNOWN'; ``` #### Deprecated since 8.9.0 ### ~~UNSPECIFIED~~ ```ts readonly UNSPECIFIED: "UNSPECIFIED" = 'UNSPECIFIED'; ``` #### Deprecated since 8.9.0 --- ## Variable: ElementInstanceStateEnum ```ts const ElementInstanceStateEnum: object; ``` Element states ## Type Declaration ### ACTIVE ```ts readonly ACTIVE: "ACTIVE" = 'ACTIVE'; ``` ### COMPLETED ```ts readonly COMPLETED: "COMPLETED" = 'COMPLETED'; ``` ### TERMINATED ```ts readonly TERMINATED: "TERMINATED" = 'TERMINATED'; ``` --- ## Variable: GlobalListenerSourceEnum ```ts const GlobalListenerSourceEnum: object; ``` How the global listener was defined. ## Type Declaration ### API ```ts readonly API: "API" = 'API'; ``` ### CONFIGURATION ```ts readonly CONFIGURATION: "CONFIGURATION" = 'CONFIGURATION'; ``` --- ## Variable: GlobalTaskListenerEventTypeEnum ```ts const GlobalTaskListenerEventTypeEnum: object; ``` The event type that triggers the user task listener. ## Type Declaration ### all ```ts readonly all: "all" = 'all'; ``` ### assigning ```ts readonly assigning: "assigning" = 'assigning'; ``` ### canceling ```ts readonly canceling: "canceling" = 'canceling'; ``` ### completing ```ts readonly completing: "completing" = 'completing'; ``` ### creating ```ts readonly creating: "creating" = 'creating'; ``` ### updating ```ts readonly updating: "updating" = 'updating'; ``` --- ## Variable: IncidentErrorTypeEnum ```ts const IncidentErrorTypeEnum: object; ``` Incident error type with a defined set of values. ## Type Declaration ### AD_HOC_SUB_PROCESS_NO_RETRIES ```ts readonly AD_HOC_SUB_PROCESS_NO_RETRIES: "AD_HOC_SUB_PROCESS_NO_RETRIES" = 'AD_HOC_SUB_PROCESS_NO_RETRIES'; ``` ### CALLED_DECISION_ERROR ```ts readonly CALLED_DECISION_ERROR: "CALLED_DECISION_ERROR" = 'CALLED_DECISION_ERROR'; ``` ### CALLED_ELEMENT_ERROR ```ts readonly CALLED_ELEMENT_ERROR: "CALLED_ELEMENT_ERROR" = 'CALLED_ELEMENT_ERROR'; ``` ### CONDITION_ERROR ```ts readonly CONDITION_ERROR: "CONDITION_ERROR" = 'CONDITION_ERROR'; ``` ### DECISION_EVALUATION_ERROR ```ts readonly DECISION_EVALUATION_ERROR: "DECISION_EVALUATION_ERROR" = 'DECISION_EVALUATION_ERROR'; ``` ### EXECUTION_LISTENER_NO_RETRIES ```ts readonly EXECUTION_LISTENER_NO_RETRIES: "EXECUTION_LISTENER_NO_RETRIES" = 'EXECUTION_LISTENER_NO_RETRIES'; ``` ### EXTRACT_VALUE_ERROR ```ts readonly EXTRACT_VALUE_ERROR: "EXTRACT_VALUE_ERROR" = 'EXTRACT_VALUE_ERROR'; ``` ### FORM_NOT_FOUND ```ts readonly FORM_NOT_FOUND: "FORM_NOT_FOUND" = 'FORM_NOT_FOUND'; ``` ### IO_MAPPING_ERROR ```ts readonly IO_MAPPING_ERROR: "IO_MAPPING_ERROR" = 'IO_MAPPING_ERROR'; ``` ### JOB_NO_RETRIES ```ts readonly JOB_NO_RETRIES: "JOB_NO_RETRIES" = 'JOB_NO_RETRIES'; ``` ### MESSAGE_SIZE_EXCEEDED ```ts readonly MESSAGE_SIZE_EXCEEDED: "MESSAGE_SIZE_EXCEEDED" = 'MESSAGE_SIZE_EXCEEDED'; ``` ### RESOURCE_NOT_FOUND ```ts readonly RESOURCE_NOT_FOUND: "RESOURCE_NOT_FOUND" = 'RESOURCE_NOT_FOUND'; ``` ### TASK_LISTENER_NO_RETRIES ```ts readonly TASK_LISTENER_NO_RETRIES: "TASK_LISTENER_NO_RETRIES" = 'TASK_LISTENER_NO_RETRIES'; ``` ### UNHANDLED_ERROR_EVENT ```ts readonly UNHANDLED_ERROR_EVENT: "UNHANDLED_ERROR_EVENT" = 'UNHANDLED_ERROR_EVENT'; ``` ### UNKNOWN ```ts readonly UNKNOWN: "UNKNOWN" = 'UNKNOWN'; ``` ### UNSPECIFIED ```ts readonly UNSPECIFIED: "UNSPECIFIED" = 'UNSPECIFIED'; ``` --- ## Variable: IncidentStateEnum ```ts const IncidentStateEnum: object; ``` Incident states with a defined set of values. ## Type Declaration ### ACTIVE ```ts readonly ACTIVE: "ACTIVE" = 'ACTIVE'; ``` ### MIGRATED ```ts readonly MIGRATED: "MIGRATED" = 'MIGRATED'; ``` ### PENDING ```ts readonly PENDING: "PENDING" = 'PENDING'; ``` ### RESOLVED ```ts readonly RESOLVED: "RESOLVED" = 'RESOLVED'; ``` ### UNKNOWN ```ts readonly UNKNOWN: "UNKNOWN" = 'UNKNOWN'; ``` --- ## Variable: JobKindEnum ```ts const JobKindEnum: object; ``` The job kind. ## Type Declaration ### AD_HOC_SUB_PROCESS ```ts readonly AD_HOC_SUB_PROCESS: "AD_HOC_SUB_PROCESS" = 'AD_HOC_SUB_PROCESS'; ``` ### BPMN_ELEMENT ```ts readonly BPMN_ELEMENT: "BPMN_ELEMENT" = 'BPMN_ELEMENT'; ``` ### EXECUTION_LISTENER ```ts readonly EXECUTION_LISTENER: "EXECUTION_LISTENER" = 'EXECUTION_LISTENER'; ``` ### TASK_LISTENER ```ts readonly TASK_LISTENER: "TASK_LISTENER" = 'TASK_LISTENER'; ``` --- ## Variable: JobListenerEventTypeEnum ```ts const JobListenerEventTypeEnum: object; ``` The listener event type of the job. ## Type Declaration ### ASSIGNING ```ts readonly ASSIGNING: "ASSIGNING" = 'ASSIGNING'; ``` ### BEFORE_ALL ```ts readonly BEFORE_ALL: "BEFORE_ALL" = 'BEFORE_ALL'; ``` ### CANCELING ```ts readonly CANCELING: "CANCELING" = 'CANCELING'; ``` ### COMPLETING ```ts readonly COMPLETING: "COMPLETING" = 'COMPLETING'; ``` ### CREATING ```ts readonly CREATING: "CREATING" = 'CREATING'; ``` ### END ```ts readonly END: "END" = 'END'; ``` ### START ```ts readonly START: "START" = 'START'; ``` ### UNSPECIFIED ```ts readonly UNSPECIFIED: "UNSPECIFIED" = 'UNSPECIFIED'; ``` ### UPDATING ```ts readonly UPDATING: "UPDATING" = 'UPDATING'; ``` --- ## Variable: JobStateEnum ```ts const JobStateEnum: object; ``` The state of the job. ## Type Declaration ### CANCELED ```ts readonly CANCELED: "CANCELED" = 'CANCELED'; ``` ### COMPLETED ```ts readonly COMPLETED: "COMPLETED" = 'COMPLETED'; ``` ### CREATED ```ts readonly CREATED: "CREATED" = 'CREATED'; ``` ### ERROR_THROWN ```ts readonly ERROR_THROWN: "ERROR_THROWN" = 'ERROR_THROWN'; ``` ### FAILED ```ts readonly FAILED: "FAILED" = 'FAILED'; ``` ### MIGRATED ```ts readonly MIGRATED: "MIGRATED" = 'MIGRATED'; ``` ### RETRIES_UPDATED ```ts readonly RETRIES_UPDATED: "RETRIES_UPDATED" = 'RETRIES_UPDATED'; ``` ### TIMED_OUT ```ts readonly TIMED_OUT: "TIMED_OUT" = 'TIMED_OUT'; ``` --- ## Variable: MessageSubscriptionStateEnum ```ts const MessageSubscriptionStateEnum: object; ``` The state of message subscription. ## Type Declaration ### CORRELATED ```ts readonly CORRELATED: "CORRELATED" = 'CORRELATED'; ``` ### CREATED ```ts readonly CREATED: "CREATED" = 'CREATED'; ``` ### DELETED ```ts readonly DELETED: "DELETED" = 'DELETED'; ``` ### MIGRATED ```ts readonly MIGRATED: "MIGRATED" = 'MIGRATED'; ``` --- ## Variable: MessageSubscriptionTypeEnum ```ts const MessageSubscriptionTypeEnum: object; ``` The type of message subscription. `START_EVENT` is definition-scoped (process start events). Always has a value; only captured from Camunda 8.10 onwards. `PROCESS_EVENT` is instance-scoped (intermediate catch events). Pre-8.10 entries have no value stored; the API returns `PROCESS_EVENT` as a default for those entries. ## Type Declaration ### PROCESS_EVENT ```ts readonly PROCESS_EVENT: "PROCESS_EVENT" = 'PROCESS_EVENT'; ``` ### START_EVENT ```ts readonly START_EVENT: "START_EVENT" = 'START_EVENT'; ``` --- ## Variable: OwnerTypeEnum ```ts const OwnerTypeEnum: object; ``` The type of the owner of permissions. ## Type Declaration ### CLIENT ```ts readonly CLIENT: "CLIENT" = 'CLIENT'; ``` ### GROUP ```ts readonly GROUP: "GROUP" = 'GROUP'; ``` ### MAPPING_RULE ```ts readonly MAPPING_RULE: "MAPPING_RULE" = 'MAPPING_RULE'; ``` ### ROLE ```ts readonly ROLE: "ROLE" = 'ROLE'; ``` ### UNSPECIFIED ```ts readonly UNSPECIFIED: "UNSPECIFIED" = 'UNSPECIFIED'; ``` ### USER ```ts readonly USER: "USER" = 'USER'; ``` --- ## Variable: PermissionTypeEnum ```ts const PermissionTypeEnum: object; ``` Specifies the type of permissions. ## Type Declaration ### ACCESS ```ts readonly ACCESS: "ACCESS" = 'ACCESS'; ``` ### CANCEL_PROCESS_INSTANCE ```ts readonly CANCEL_PROCESS_INSTANCE: "CANCEL_PROCESS_INSTANCE" = 'CANCEL_PROCESS_INSTANCE'; ``` ### CLAIM ```ts readonly CLAIM: "CLAIM" = 'CLAIM'; ``` ### CLAIM_USER_TASK ```ts readonly CLAIM_USER_TASK: "CLAIM_USER_TASK" = 'CLAIM_USER_TASK'; ``` ### COMPLETE ```ts readonly COMPLETE: "COMPLETE" = 'COMPLETE'; ``` ### COMPLETE_USER_TASK ```ts readonly COMPLETE_USER_TASK: "COMPLETE_USER_TASK" = 'COMPLETE_USER_TASK'; ``` ### CREATE ```ts readonly CREATE: "CREATE" = 'CREATE'; ``` ### CREATE_BATCH_OPERATION_CANCEL_PROCESS_INSTANCE ```ts readonly CREATE_BATCH_OPERATION_CANCEL_PROCESS_INSTANCE: "CREATE_BATCH_OPERATION_CANCEL_PROCESS_INSTANCE" = 'CREATE_BATCH_OPERATION_CANCEL_PROCESS_INSTANCE'; ``` ### CREATE_BATCH_OPERATION_DELETE_DECISION_DEFINITION ```ts readonly CREATE_BATCH_OPERATION_DELETE_DECISION_DEFINITION: "CREATE_BATCH_OPERATION_DELETE_DECISION_DEFINITION" = 'CREATE_BATCH_OPERATION_DELETE_DECISION_DEFINITION'; ``` ### CREATE_BATCH_OPERATION_DELETE_DECISION_INSTANCE ```ts readonly CREATE_BATCH_OPERATION_DELETE_DECISION_INSTANCE: "CREATE_BATCH_OPERATION_DELETE_DECISION_INSTANCE" = 'CREATE_BATCH_OPERATION_DELETE_DECISION_INSTANCE'; ``` ### CREATE_BATCH_OPERATION_DELETE_PROCESS_DEFINITION ```ts readonly CREATE_BATCH_OPERATION_DELETE_PROCESS_DEFINITION: "CREATE_BATCH_OPERATION_DELETE_PROCESS_DEFINITION" = 'CREATE_BATCH_OPERATION_DELETE_PROCESS_DEFINITION'; ``` ### CREATE_BATCH_OPERATION_DELETE_PROCESS_INSTANCE ```ts readonly CREATE_BATCH_OPERATION_DELETE_PROCESS_INSTANCE: "CREATE_BATCH_OPERATION_DELETE_PROCESS_INSTANCE" = 'CREATE_BATCH_OPERATION_DELETE_PROCESS_INSTANCE'; ``` ### CREATE_BATCH_OPERATION_MIGRATE_PROCESS_INSTANCE ```ts readonly CREATE_BATCH_OPERATION_MIGRATE_PROCESS_INSTANCE: "CREATE_BATCH_OPERATION_MIGRATE_PROCESS_INSTANCE" = 'CREATE_BATCH_OPERATION_MIGRATE_PROCESS_INSTANCE'; ``` ### CREATE_BATCH_OPERATION_MODIFY_PROCESS_INSTANCE ```ts readonly CREATE_BATCH_OPERATION_MODIFY_PROCESS_INSTANCE: "CREATE_BATCH_OPERATION_MODIFY_PROCESS_INSTANCE" = 'CREATE_BATCH_OPERATION_MODIFY_PROCESS_INSTANCE'; ``` ### CREATE_BATCH_OPERATION_RESOLVE_INCIDENT ```ts readonly CREATE_BATCH_OPERATION_RESOLVE_INCIDENT: "CREATE_BATCH_OPERATION_RESOLVE_INCIDENT" = 'CREATE_BATCH_OPERATION_RESOLVE_INCIDENT'; ``` ### CREATE_DECISION_INSTANCE ```ts readonly CREATE_DECISION_INSTANCE: "CREATE_DECISION_INSTANCE" = 'CREATE_DECISION_INSTANCE'; ``` ### CREATE_PROCESS_INSTANCE ```ts readonly CREATE_PROCESS_INSTANCE: "CREATE_PROCESS_INSTANCE" = 'CREATE_PROCESS_INSTANCE'; ``` ### CREATE_TASK_LISTENER ```ts readonly CREATE_TASK_LISTENER: "CREATE_TASK_LISTENER" = 'CREATE_TASK_LISTENER'; ``` ### DELETE ```ts readonly DELETE: "DELETE" = 'DELETE'; ``` ### DELETE_DECISION_INSTANCE ```ts readonly DELETE_DECISION_INSTANCE: "DELETE_DECISION_INSTANCE" = 'DELETE_DECISION_INSTANCE'; ``` ### DELETE_DRD ```ts readonly DELETE_DRD: "DELETE_DRD" = 'DELETE_DRD'; ``` ### DELETE_FORM ```ts readonly DELETE_FORM: "DELETE_FORM" = 'DELETE_FORM'; ``` ### DELETE_PROCESS ```ts readonly DELETE_PROCESS: "DELETE_PROCESS" = 'DELETE_PROCESS'; ``` ### DELETE_PROCESS_INSTANCE ```ts readonly DELETE_PROCESS_INSTANCE: "DELETE_PROCESS_INSTANCE" = 'DELETE_PROCESS_INSTANCE'; ``` ### DELETE_RESOURCE ```ts readonly DELETE_RESOURCE: "DELETE_RESOURCE" = 'DELETE_RESOURCE'; ``` ### DELETE_TASK_LISTENER ```ts readonly DELETE_TASK_LISTENER: "DELETE_TASK_LISTENER" = 'DELETE_TASK_LISTENER'; ``` ### EVALUATE ```ts readonly EVALUATE: "EVALUATE" = 'EVALUATE'; ``` ### MODIFY_PROCESS_INSTANCE ```ts readonly MODIFY_PROCESS_INSTANCE: "MODIFY_PROCESS_INSTANCE" = 'MODIFY_PROCESS_INSTANCE'; ``` ### READ ```ts readonly READ: "READ" = 'READ'; ``` ### READ_DECISION_DEFINITION ```ts readonly READ_DECISION_DEFINITION: "READ_DECISION_DEFINITION" = 'READ_DECISION_DEFINITION'; ``` ### READ_DECISION_INSTANCE ```ts readonly READ_DECISION_INSTANCE: "READ_DECISION_INSTANCE" = 'READ_DECISION_INSTANCE'; ``` ### READ_JOB_METRIC ```ts readonly READ_JOB_METRIC: "READ_JOB_METRIC" = 'READ_JOB_METRIC'; ``` ### READ_PROCESS_DEFINITION ```ts readonly READ_PROCESS_DEFINITION: "READ_PROCESS_DEFINITION" = 'READ_PROCESS_DEFINITION'; ``` ### READ_PROCESS_INSTANCE ```ts readonly READ_PROCESS_INSTANCE: "READ_PROCESS_INSTANCE" = 'READ_PROCESS_INSTANCE'; ``` ### READ_TASK_LISTENER ```ts readonly READ_TASK_LISTENER: "READ_TASK_LISTENER" = 'READ_TASK_LISTENER'; ``` ### READ_USAGE_METRIC ```ts readonly READ_USAGE_METRIC: "READ_USAGE_METRIC" = 'READ_USAGE_METRIC'; ``` ### READ_USER_TASK ```ts readonly READ_USER_TASK: "READ_USER_TASK" = 'READ_USER_TASK'; ``` ### UPDATE ```ts readonly UPDATE: "UPDATE" = 'UPDATE'; ``` ### UPDATE_PROCESS_INSTANCE ```ts readonly UPDATE_PROCESS_INSTANCE: "UPDATE_PROCESS_INSTANCE" = 'UPDATE_PROCESS_INSTANCE'; ``` ### UPDATE_TASK_LISTENER ```ts readonly UPDATE_TASK_LISTENER: "UPDATE_TASK_LISTENER" = 'UPDATE_TASK_LISTENER'; ``` ### UPDATE_USER_TASK ```ts readonly UPDATE_USER_TASK: "UPDATE_USER_TASK" = 'UPDATE_USER_TASK'; ``` --- ## Variable: ProcessInstanceStateEnum ```ts const ProcessInstanceStateEnum: object; ``` Process instance states ## Type Declaration ### ACTIVE ```ts readonly ACTIVE: "ACTIVE" = 'ACTIVE'; ``` ### COMPLETED ```ts readonly COMPLETED: "COMPLETED" = 'COMPLETED'; ``` ### TERMINATED ```ts readonly TERMINATED: "TERMINATED" = 'TERMINATED'; ``` --- ## Variable: ResourceTypeEnum ```ts const ResourceTypeEnum: object; ``` The type of resource to add/remove permissions to/from. ## Type Declaration ### AUDIT_LOG ```ts readonly AUDIT_LOG: "AUDIT_LOG" = 'AUDIT_LOG'; ``` ### AUTHORIZATION ```ts readonly AUTHORIZATION: "AUTHORIZATION" = 'AUTHORIZATION'; ``` ### BATCH ```ts readonly BATCH: "BATCH" = 'BATCH'; ``` ### CLUSTER_VARIABLE ```ts readonly CLUSTER_VARIABLE: "CLUSTER_VARIABLE" = 'CLUSTER_VARIABLE'; ``` ### COMPONENT ```ts readonly COMPONENT: "COMPONENT" = 'COMPONENT'; ``` ### DECISION_DEFINITION ```ts readonly DECISION_DEFINITION: "DECISION_DEFINITION" = 'DECISION_DEFINITION'; ``` ### DECISION_REQUIREMENTS_DEFINITION ```ts readonly DECISION_REQUIREMENTS_DEFINITION: "DECISION_REQUIREMENTS_DEFINITION" = 'DECISION_REQUIREMENTS_DEFINITION'; ``` ### DOCUMENT ```ts readonly DOCUMENT: "DOCUMENT" = 'DOCUMENT'; ``` ### EXPRESSION ```ts readonly EXPRESSION: "EXPRESSION" = 'EXPRESSION'; ``` ### GLOBAL_LISTENER ```ts readonly GLOBAL_LISTENER: "GLOBAL_LISTENER" = 'GLOBAL_LISTENER'; ``` ### GROUP ```ts readonly GROUP: "GROUP" = 'GROUP'; ``` ### MAPPING_RULE ```ts readonly MAPPING_RULE: "MAPPING_RULE" = 'MAPPING_RULE'; ``` ### MESSAGE ```ts readonly MESSAGE: "MESSAGE" = 'MESSAGE'; ``` ### PROCESS_DEFINITION ```ts readonly PROCESS_DEFINITION: "PROCESS_DEFINITION" = 'PROCESS_DEFINITION'; ``` ### RESOURCE ```ts readonly RESOURCE: "RESOURCE" = 'RESOURCE'; ``` ### ROLE ```ts readonly ROLE: "ROLE" = 'ROLE'; ``` ### SYSTEM ```ts readonly SYSTEM: "SYSTEM" = 'SYSTEM'; ``` ### TENANT ```ts readonly TENANT: "TENANT" = 'TENANT'; ``` ### USER ```ts readonly USER: "USER" = 'USER'; ``` ### USER_TASK ```ts readonly USER_TASK: "USER_TASK" = 'USER_TASK'; ``` --- ## Variable: SPEC HASH # Variable: SPEC_HASH ```ts const SPEC_HASH: "sha256:f85a4345e56552ece2e63ba93164e70abb16973b17a4256803dc5d64346869c6"; ``` --- ## Variable: SortOrderEnum ```ts const SortOrderEnum: object; ``` The order in which to sort the related field. ## Type Declaration ### ASC ```ts readonly ASC: "ASC" = 'ASC'; ``` ### DESC ```ts readonly DESC: "DESC" = 'DESC'; ``` --- ## Variable: TenantFilterEnum ```ts const TenantFilterEnum: object; ``` The tenant filtering strategy for job activation. Determines whether to use tenant IDs provided in the request or tenant IDs assigned to the authenticated principal. ## Type Declaration ### ASSIGNED ```ts readonly ASSIGNED: "ASSIGNED" = 'ASSIGNED'; ``` ### PROVIDED ```ts readonly PROVIDED: "PROVIDED" = 'PROVIDED'; ``` --- ## Variable: UserTaskStateEnum ```ts const UserTaskStateEnum: object; ``` The state of the user task. Note: FAILED state is only for legacy job-worker-based tasks. ## Type Declaration ### ASSIGNING ```ts readonly ASSIGNING: "ASSIGNING" = 'ASSIGNING'; ``` ### CANCELED ```ts readonly CANCELED: "CANCELED" = 'CANCELED'; ``` ### CANCELING ```ts readonly CANCELING: "CANCELING" = 'CANCELING'; ``` ### COMPLETED ```ts readonly COMPLETED: "COMPLETED" = 'COMPLETED'; ``` ### COMPLETING ```ts readonly COMPLETING: "COMPLETING" = 'COMPLETING'; ``` ### CREATED ```ts readonly CREATED: "CREATED" = 'CREATED'; ``` ### CREATING ```ts readonly CREATING: "CREATING" = 'CREATING'; ``` ### FAILED ```ts readonly FAILED: "FAILED" = 'FAILED'; ``` ### UPDATING ```ts readonly UPDATING: "UPDATING" = 'UPDATING'; ``` --- ## TypeScript SDK API Reference # Camunda 8 Orchestration Cluster TypeScript SDK [![npm](https://img.shields.io/npm/v/@camunda8/orchestration-cluster-api)](https://www.npmjs.com/package/@camunda8/orchestration-cluster-api) [![npm downloads](https://img.shields.io/npm/dw/@camunda8/orchestration-cluster-api)](https://www.npmjs.com/package/@camunda8/orchestration-cluster-api) [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/camunda/orchestration-cluster-api-js/blob/main/LICENSE) [![GitHub release](https://img.shields.io/github/v/release/camunda/orchestration-cluster-api-js)](https://github.com/camunda/orchestration-cluster-api-js/releases) Type‑safe, promise‑based client for the Camunda 8 Orchestration Cluster REST API. ## Highlights - Strong TypeScript models (requests, responses, discriminated unions) - Branded key types to prevent mixing IDs at compile time - Optional request/response schema validation (Zod) via a single env variable - OAuth2 client‑credentials & Basic auth (token cache, early refresh, jittered retry, singleflight) - Optional mTLS (Node) with inline or \*\_PATH environment variables - Cancelable promises for all operations - Eventual consistency helper for polling endpoints - Immutable, deep‑frozen configuration accessible through a factory‑created client instance - Automatic body-level tenantId defaulting: if a request body supports an optional tenantId and you omit it, the SDK fills it from CAMUNDA_DEFAULT_TENANT_ID (path params are never auto-filled) - Automatic transient HTTP retry (429, 503, network) with exponential backoff + full jitter (configurable via CAMUNDA_SDK_HTTP_RETRY\*). Non-retryable 500s fail fast. - Per-method retry override: disable or customize retry policy on any individual API call without changing global settings ## Install ```bash npm install @camunda8/orchestration-cluster-api ``` Runtime support: - Node 20+ (native fetch & File; Node 18 needs global File polyfill) - Modern browsers (Chromium, Firefox, Safari) – global `fetch` & `File` available For older Node versions supply a fetch ponyfill AND a `File` shim (or upgrade). For legacy browsers, add a fetch polyfill (e.g. `whatwg-fetch`). ### Versioning This SDK has a different release cadence from the Camunda server. Features and fixes land in the SDK during a server release. The major version of the SDK signals a 1:1 type coherence with the server API for a Camunda minor release. SDK version `n.y.z` -> server version `8.n`, so the type surface of SDK version 9.y.z matches the API surface of Camunda 8.9. Using a later SDK version, for example: SDK version 10.y.z with Camunda 8.9, means that the SDK contains additive surfaces that are not guaranteed at runtime, and the compiler cannot warn of unsupported operations. Using an earlier SDK version, for example: SDK version 9.y.z with Camunda 8.10, results in slightly degraded compiler reasoning: exhaustiveness checks cannot be guaranteed by the compiler for any extended surfaces (principally, enums with added members). In the vast majority of use-cases, this will not be an issue; but you should be aware that using the matching SDK major version for the server minor version provides the strongest compiler guarantees about runtime reliability. **Recommended approach**: - Check the [CHANGELOG](https://github.com/camunda/orchestration-cluster-api-js/releases). - As a sanity check during server version upgrade, rebuild applications with the matching SDK major version to identify any affected runtime surfaces. ## Migrating from 8.8 SDK 9.x (for Camunda 8.9) introduces two categories of breaking type changes relative to SDK 8.x (for Camunda 8.8). Neither change affects runtime behavior — existing code that compiled against 8.x will run identically — but the compiler will flag type mismatches until you update. ### Search results: optional → required fields The search result types changed several fields from **optional** to **required**: **`SearchQueryPageResponse` (page metadata)** | Field | SDK 8.x (Camunda 8.8) | SDK 9.x (Camunda 8.9) | | ------------------- | ----------------------------- | ---------------------------------- | | `totalItems` | `totalItems?: number` | `totalItems: number` | | `hasMoreTotalItems` | `hasMoreTotalItems?: boolean` | `hasMoreTotalItems: boolean` | | `endCursor` | `endCursor?: EndCursor` | `endCursor: EndCursor \| null` | | `startCursor` | `startCursor?: StartCursor` | `startCursor: StartCursor \| null` | **`*SearchQueryResult` types (result containers)** | Field | SDK 8.x (Camunda 8.8) | SDK 9.x (Camunda 8.9) | | ------- | -------------------------------- | ------------------------------- | | `items` | `items?: T[]` | `items: T[]` | | `page` | `page?: SearchQueryPageResponse` | `page: SearchQueryPageResponse` | This reflects upstream OpenAPI spec changes where these fields are now always present in the response, with `null` indicating "no value" for cursors rather than being absent. **What to change**: Update code that checks for these fields using optional chaining or `undefined` comparisons. The `items` array and `page` object are now always present, so optional chaining on them is unnecessary: ```ts // Before (8.x) — checking for undefined if (result.page?.endCursor !== undefined) { nextPage(result.page.endCursor); } const count = result.items?.length ?? 0; // After (9.x) — page and items are always present; check cursors for null if (result.page.endCursor !== null) { nextPage(result.page.endCursor); } const count = result.items.length; ``` If you have a custom `PagedResponse` type, update its `page` shape to match: ```ts // Before (8.x) type PagedResponse = { items?: T[]; page?: { totalItems?: number; endCursor?: string; startCursor?: string; hasMoreTotalItems?: boolean; }; }; // After (9.x) type PagedResponse = { items: T[]; page: { totalItems: number; endCursor: EndCursor | null; startCursor: StartCursor | null; hasMoreTotalItems: boolean; }; }; ``` ### Branded key types for `tenantId` The `tenantId` field on request types (e.g. `CreateDeploymentData`) changed from `string` to the branded `TenantId` type. A plain `string` is no longer assignable: ```ts // Before (8.x) — plain string worked await camunda.createDeployment({ tenantId: "my-tenant", resources: [file], }); // After (9.x) — use the branded type helper await camunda.createDeployment({ tenantId: TenantId.assumeExists("my-tenant"), resources: [file], }); ``` `TenantId.assumeExists()` validates the string against the tenant ID pattern and returns a branded value. The branded value is just a string at runtime, but `assumeExists()` performs validation and can throw if the input is malformed. See [Branded Keys](#branded-keys) for more on this pattern. > **Tip**: If your tenant ID comes from a validated source (environment variable, config file), call `TenantId.assumeExists()` once at startup and pass the branded value throughout your application. ## Migrating from 8.9 SDK 10.x (for Camunda 8.10) promotes several identifier and name fields from plain `string` to **branded types** via `CamundaKey`. The wire format and runtime API are unchanged — branded values are still plain strings at runtime and are assignable anywhere a `string` is expected (template literals, logging, JSON serialization). Callers need to brand values using `.assumeExists()` (which performs validation) to satisfy the new types. ### New branded types | Brand | Used for | | --------------------- | -------------------------- | | `RoleId` | Role identifiers | | `GroupId` | Group identifiers | | `ClientId` | OAuth client identifiers | | `MappingRuleId` | Mapping-rule identifiers | | `ClusterVariableName` | Cluster variable names | | `AgentInstanceKey` | Agent-instance system keys | ### Migration ```ts // v9 — plain strings were accepted // await camunda.assignRoleToGroup({ // roleId: 'developer', // groupId: 'engineering', // }); // v10 — use the branded type helpers at the boundary await camunda.assignRoleToGroup({ roleId: RoleId.assumeExists("developer"), groupId: GroupId.assumeExists("engineering"), }); ``` Each branded type has an `.assumeExists()` method that validates the string and returns the branded value. Validation runs at call time and can throw if the input is malformed, so call it once at the boundary (startup, config parsing, API response) and pass the branded value through your application. See [Branded Keys](#branded-keys) for more on this pattern. ### What does NOT change - The wire format is unchanged — all values are still strings on the wire. - No method signatures changed name or arity. - Branded values are assignable anywhere a `string` is expected (template literals, logging, JSON serialization), so existing string-handling code continues to work. ## Quick Start (Zero‑Config – Recommended) Keep configuration out of application code. Let the factory read `CAMUNDA_*` variables from the environment (12‑factor style). This makes rotation, secret management, and environment promotion safer & simpler. ```ts // Zero‑config construction: reads CAMUNDA_* from process.env. If no configuration is present, defaults to Camunda 8 Run on localhost. const camunda = createCamundaClient(); const topology = await camunda.getTopology(); console.log("Brokers:", topology.brokers?.length ?? 0); ``` Typical `.env` (example): ```bash CAMUNDA_REST_ADDRESS=https://cluster.example # SDK will use https://cluster.example/v2/... unless /v2 already present CAMUNDA_AUTH_STRATEGY=OAUTH CAMUNDA_CLIENT_ID=*** CAMUNDA_CLIENT_SECRET=*** CAMUNDA_DEFAULT_TENANT_ID= # optional: override default tenant resolution CAMUNDA_SDK_HTTP_RETRY_MAX_ATTEMPTS=4 # optional: total attempts (initial + 3 retries) CAMUNDA_SDK_HTTP_RETRY_BASE_DELAY_MS=100 # optional: base backoff (ms) CAMUNDA_SDK_HTTP_RETRY_MAX_DELAY_MS=2000 # optional: cap (ms) ``` > Prefer environment / secret manager injection over hard‑coding values in source. Treat the SDK like a leaf dependency: construct once near process start, pass the instance where needed. > **Why zero‑config?** > > - Separation of concerns: business code depends on an interface, not on secret/constants wiring. > - 12‑Factor alignment: config lives in the environment → simpler promotion (dev → staging → prod). > - Secret rotation & incident response: rotate credentials without a code change or redeploy of application containers built with baked‑in values. > - Immutable start: single hydration pass prevents drift / mid‑request mutations. > - Test ergonomics: swap an `.env.test` (or injected vars) without touching source; create multiple clients for multi‑tenant tests. > - Security review: fewer code paths handling secrets; scanners & vault tooling work at the boundary. > - Deploy portability: same artifact runs everywhere; only the environment differs. > - Observability clarity: configuration diffing is an ops concern, not an application code diff. ### Advanced: Programmatic Overrides Use only when you must supply or mutate configuration dynamically (e.g. multi‑tenant routing, tests, ephemeral preview environments) or in the browser. Keys mirror their `CAMUNDA_*` env names. ```ts const camunda = createCamundaClient({ config: { CAMUNDA_REST_ADDRESS: "https://cluster.example", CAMUNDA_AUTH_STRATEGY: "BASIC", CAMUNDA_BASIC_AUTH_USERNAME: "alice", CAMUNDA_BASIC_AUTH_PASSWORD: "secret", }, }); ``` ### Advanced: Custom Fetch Implementation Inject a custom `fetch` to add tracing, mock responses, instrumentation, circuit breakers, etc. ```ts const camunda = createCamundaClient({ fetch: (input, init) => { // inspect / modify request here return fetch(input, init); }, }); ``` ### Reconfiguration At Runtime (Rare) You can call `client.configure({ config: { ... } })` to re‑hydrate. The exposed `client.getConfig()` stays `Readonly` and deep‑frozen. Prefer creating a new client instead of mutating a shared one in long‑lived services. ## Validation This allows you to validate that requests to the API from your application and responses from the API have the expected types and shape declared in the type system. This protects your application from runtime bugs or errors in the type system leading to undefined states hitting your business logic. Recommended to use `fanatical` or `strict` in development and then switch to `strict` or `warn` in production. Or you can just YOLO it and leave it on `none` all the time. Controlled by `CAMUNDA_SDK_VALIDATION` (or `config` override). Grammar: ``` none | warn | strict | req:[,res:] | res:[,req:] = none|warn|strict|fanatical ``` Examples: ```bash CAMUNDA_SDK_VALIDATION=warn # warn on both CAMUNDA_SDK_VALIDATION=req:strict,res:warn # strict on requests, warn on responses CAMUNDA_SDK_VALIDATION=none ``` Behavior: - `none` - no validation performed - `warn` - emit warning on invalid shape - `strict` - fail on type mismatch or missing required fields - `fanatical` - fail on type mismatch, missing required fields, or unknown additional fields > **Note on `int64` fields**: The upstream OpenAPI spec declares some fields (e.g. `totalItems`, `timeout`, `timestamp`) as `integer` with `format: int64`. The TypeScript types map these to `number`. JSON responses also deserialize as `number` (with precision loss beyond `Number.MAX_SAFE_INTEGER`). The Zod schemas use `z.coerce.number().int()` for these fields, preserving the integer constraint while keeping the runtime type aligned with TypeScript. All validation modes (`none`, `warn`, `strict`, `fanatical`) return `number`. ## Per-Method Retry Override Every API method accepts an optional trailing `options` parameter that lets you override or disable the global retry policy for that single call. ### Disable Retry for a Single Call ```ts // This call will not retry on transient errors await camunda.completeJob({ jobKey }, { retry: false }); ``` ### Override Specific Retry Settings Pass a partial `HttpRetryPolicy` to override individual fields. Unspecified fields inherit from the global configuration. ```ts // More aggressive retry for this operation only await camunda.createProcessInstance( { processDefinitionId }, { retry: { maxAttempts: 8, maxDelayMs: 5000 } } ); // Minimal retry: single retry with short backoff await camunda.getTopology({ retry: { maxAttempts: 2, baseDelayMs: 50 } }); ``` ### How It Works | `options.retry` value | Behavior | | --------------------- | -------------------------------------------------------------------- | | omitted / `undefined` | Uses global policy (`CAMUNDA_SDK_HTTP_RETRY_*` env vars) | | `false` | Disables retry entirely (single attempt, no backoff) | | `{ maxAttempts: 5 }` | Merges with global policy — only the specified fields are overridden | The `HttpRetryPolicy` fields available for override: | Field | Type | Description | | ------------- | -------- | --------------------------------------- | | `maxAttempts` | `number` | Total attempts (initial + retries) | | `baseDelayMs` | `number` | Base delay for exponential backoff (ms) | | `maxDelayMs` | `number` | Maximum delay cap (ms) | ## Advanced HTTP Retry: Cockatiel Adapter (Optional) For advanced resilience patterns beyond per-method overrides — circuit breakers, timeouts, custom classification, combining policies — you can integrate [cockatiel](https://github.com/connor4312/cockatiel). > **Tip:** For most use cases, per-method retry override (above) is sufficient. Reach for Cockatiel when you need circuit breaking, hedging, or bulkhead controls. ### When To Use Cockatiel - You want circuit breaking, hedging, timeout, or bulkhead controls - You want to add custom classification (e.g. retry certain 5xx only on safe verbs) - You need to compose multiple resilience policies together ### Disable Built‑In HTTP Retries Set `CAMUNDA_SDK_HTTP_RETRY_MAX_ATTEMPTS=1` so the SDK does only the initial attempt; then wrap operations with cockatiel. ### Minimal Example (Single Operation) ```ts const client = createCamundaClient({ config: { CAMUNDA_REST_ADDRESS: "https://cluster.example", CAMUNDA_AUTH_STRATEGY: "NONE", CAMUNDA_SDK_HTTP_RETRY_MAX_ATTEMPTS: 1, // disable SDK automatic retries } as any, }); // Policy: up to 5 attempts total (1 + 4 retries) with exponential backoff & jitter const policy = retry(handleAll, { maxAttempts: 5, backoff: new ExponentialBackoff({ initialDelay: 100, maxDelay: 2000, jitter: true, }), }); // Wrap getTopology const origGetTopology = client.getTopology.bind(client); client.getTopology = (() => policy.execute(() => origGetTopology())) as any; const topo = await client.getTopology(); console.log(topo.brokers?.length); ``` ### Bulk Wrapping All Operations ```ts const client = createCamundaClient({ config: { CAMUNDA_REST_ADDRESS: "https://cluster.example", CAMUNDA_AUTH_STRATEGY: "OAUTH", CAMUNDA_CLIENT_ID: process.env.CAMUNDA_CLIENT_ID, CAMUNDA_CLIENT_SECRET: process.env.CAMUNDA_CLIENT_SECRET, CAMUNDA_OAUTH_URL: process.env.CAMUNDA_OAUTH_URL, CAMUNDA_TOKEN_AUDIENCE: "zeebe.camunda.io", CAMUNDA_SDK_HTTP_RETRY_MAX_ATTEMPTS: 1, } as any, }); const retryPolicy = retry(handleAll, { maxAttempts: 4, backoff: new ExponentialBackoff({ initialDelay: 150, maxDelay: 2500, jitter: true, }), }); const skip = new Set([ "logger", "configure", "getConfig", "withCorrelation", "deployResourcesFromFiles", ]); for (const key of Object.keys(client)) { const val: any = (client as any)[key]; if (typeof val === "function" && !key.startsWith("_") && !skip.has(key)) { const original = val.bind(client); (client as any)[key] = (...a: any[]) => retryPolicy.execute(() => original(...a)); } } // Now every public operation is wrapped. ``` ## Support Logger (Node Only) For diagnostics during support interactions you can enable an auxiliary file logger that captures a sanitized snapshot of environment & configuration plus selected runtime events. Enable by setting one of: ```bash CAMUNDA_SUPPORT_LOG_ENABLED=true # canonical ``` Optional override for output path (default is `./camunda-support.log` in the current working directory): ```bash CAMUNDA_SUPPORT_LOG_FILE_PATH=/var/log/camunda-support.log ``` Behavior: - File is created eagerly on first client construction (one per process; if the path exists a numeric suffix is appended to avoid clobbering). - Initial preamble includes SDK package version, timestamp, and redacted environment snapshot. - Secrets (client secret, passwords, mTLS private key, etc.) are automatically masked or truncated. - Designed to be low‑impact: append‑only, newline‑delimited JSON records may be added in future releases for deeper inspection (current version writes the preamble only unless additional events are wired). Recommended usage: ```bash CAMUNDA_SUPPORT_LOG_ENABLED=1 CAMUNDA_SDK_LOG_LEVEL=debug node app.js ``` Keep the file only as long as needed for troubleshooting; it may contain sensitive non‑secret operational metadata. Do not commit it to version control. To disable, unset the env variable or set `CAMUNDA_SUPPORT_LOG_ENABLED=false`. Refer to `./docs/CONFIG_REFERENCE.md` for the full list of related environment variables. ### Custom Classification Example Retry only network errors + 429/503, plus optionally 500 on safe GET endpoints you mark: ```ts const classify = handleWhen((err) => { const status = (err as any)?.status; if (status === 429 || status === 503) return true; if (status === 500 && (err as any).__opVerb === "GET") return true; // custom tagging optional return err?.name === "TypeError"; // network errors from fetch }); const policy = retry(classify, { maxAttempts: 5, backoff: new ExponentialBackoff({ initialDelay: 100, maxDelay: 2000, jitter: true, }), }); ``` ### Notes - Keep SDK retries disabled to prevent duplicate layers. - SDK synthesizes `Error` objects with a `status` for retry-significant HTTP responses (429, 503, 500), enabling classification. - You can tag errors (e.g. assign `err.__opVerb`) in a wrapper if verb-level logic is needed. - For per-operation retry customization without external dependencies, use the built-in [per-method retry override](#per-method-retry-override) instead. > Combine cockatiel retry with a circuit breaker, timeout, or bulkhead policy for more robust behavior in partial outages. ## Global Backpressure (Adaptive Concurrency) The client now includes an internal global backpressure manager that adaptively throttles the number of _initiating_ in‑flight operations when the cluster signals resource exhaustion. It complements (not replaces) per‑request HTTP retry. ### Signals Considered An HTTP response is treated as a backpressure signal when it is classified retryable **and** matches one of: - `429` (Too Many Requests) – always - `503` with `title === "RESOURCE_EXHAUSTED"` - `500` whose RFC 9457 / 7807 `detail` text contains `RESOURCE_EXHAUSTED` All other 5xx / 503 variants are treated as non‑retryable (fail fast) and do **not** influence the adaptive gate. ### How It Works 1. Normal state starts with effectively unlimited concurrency (no global semaphore enforced) until the first backpressure event. 2. On the first signal the manager boots with a provisional concurrency cap (e.g. 16) and immediately reduces it (soft state). 3. Repeated consecutive signals escalate severity to `severe`, applying a stronger reduction factor. 4. Successful (non‑backpressure) completions trigger passive recovery checks that gradually restore permits over time if the system stays quiet. 5. Quiet periods (no signals for a configurable decay interval) downgrade severity (`severe → soft → healthy`) and reset the consecutive counter when fully healthy. The policy is intentionally conservative: it only engages after genuine pressure signals and recovers gradually to avoid oscillation. ### Exempt Operations Certain operations that help drain work or complete execution are _exempt_ from gating so they are never queued behind initiating calls: - `completeJob` - `failJob` - `throwJobError` - `completeUserTask` These continue immediately even during severe backpressure to promote system recovery. ### Interaction With HTTP Retry Per‑request retry still performs exponential backoff + jitter for classified transient errors. The adaptive concurrency layer sits _outside_ retry: 1. A call acquires a permit (unless exempt) before its first attempt. 2. Internal retry re‑attempts happen _within_ the held permit. 3. On final success the permit is released and a healthy hint is recorded (possible gradual recovery). 4. On final failure (non‑retryable or attempts exhausted) the permit is released; a 429 on the terminal attempt still records backpressure. This design prevents noisy churn (permits would not shrink/expand per retry attempt) and focuses on admission control of distinct logical operations. ### Observability Enable debug logging (`CAMUNDA_SDK_LOG_LEVEL=debug` or `trace`) to see events emitted under the scoped logger `bp` (e.g. `backpressure.permits.scale`, `backpressure.permits.recover`, `backpressure.severity`). These are trace‑level; use `trace` for the most granular insight. ### Configuration Current release ships with defaults tuned for conservative behavior. Adaptive gating is controlled by a profile (no separate boolean toggle). Use the `LEGACY` profile for observe‑only mode (no global gating, still records severity). Otherwise choose a tuning profile and optionally override individual knobs. Tuning environment variables (all optional; defaults shown): | Variable | Default | Description | | ----------------------------------------------- | ---------- | ----------------------------------------------------------------------------------------------- | | `CAMUNDA_SDK_BACKPRESSURE_INITIAL_MAX` | `16` | Bootstrap concurrency cap once the first signal is observed (null/unlimited before any signal). | | `CAMUNDA_SDK_BACKPRESSURE_SOFT_FACTOR` | `70` | Percentage multiplier applied on each soft backpressure event (70 => 0.70x permits). | | `CAMUNDA_SDK_BACKPRESSURE_SEVERE_FACTOR` | `50` | Percentage multiplier when entering or re-triggering in severe state. | | `CAMUNDA_SDK_BACKPRESSURE_RECOVERY_INTERVAL_MS` | `1000` | Interval between passive recovery checks. | | `CAMUNDA_SDK_BACKPRESSURE_RECOVERY_STEP` | `1` | Permits regained per recovery interval until reaching the bootstrap cap. | | `CAMUNDA_SDK_BACKPRESSURE_DECAY_QUIET_MS` | `2000` | Quiet period to downgrade severity (`severe→soft→healthy`). | | `CAMUNDA_SDK_BACKPRESSURE_FLOOR` | `1` | Minimum concurrency floor while degraded. | | `CAMUNDA_SDK_BACKPRESSURE_SEVERE_THRESHOLD` | `3` | Consecutive signals required to enter severe state. | | `CAMUNDA_SDK_BACKPRESSURE_PROFILE` | `BALANCED` | Preset profile: BALANCED, CONSERVATIVE, AGGRESSIVE, LEGACY (LEGACY = observe-only, no gating). | #### Profiles Profiles supply coordinated defaults when you don't want to reason about individual knobs. Any explicitly set knob env var overrides the profile value. | Profile | initialMax | softFactor% | severeFactor% | recoveryIntervalMs | recoveryStep | quietDecayMs | floor | severeThreshold | Intended Use | | ------------ | ---------- | ----------- | ------------- | ------------------ | ------------ | ------------ | ----- | --------------- | --------------------------------------------------------------- | | BALANCED | 16 | 70 | 50 | 1000 | 1 | 2000 | 1 | 3 | General workloads with moderate spikes | | CONSERVATIVE | 12 | 60 | 40 | 1200 | 1 | 2500 | 1 | 2 | Protect cluster under tighter capacity / cost constraints | | AGGRESSIVE | 24 | 80 | 60 | 800 | 2 | 1500 | 2 | 4 | High throughput scenarios aiming to utilize headroom quickly | | LEGACY | n/a | 70 | 50 | 1000 | 1 | 2000 | 1 | 3 | Observe signals only (severity metrics) without adaptive gating | Select via: ```bash CAMUNDA_SDK_BACKPRESSURE_PROFILE=AGGRESSIVE ``` Then optionally override a single parameter, e.g.: ```bash CAMUNDA_SDK_BACKPRESSURE_PROFILE=AGGRESSIVE CAMUNDA_SDK_BACKPRESSURE_INITIAL_MAX=32 ``` If the profile name is unrecognized the SDK falls back to BALANCED silently (future versions may emit a warning). Factors use integer percentages to avoid floating point drift in env parsing; the SDK converts them to multipliers internally (e.g. `70` -> `0.7`). If you have concrete tuning needs, open an issue describing workload patterns (operation mix, baseline concurrency, observed broker limits) to help prioritize which knobs to surface. ### What Should I Set? If you're unsure about your workload shape, **don't set anything**. The default BALANCED profile activates automatically and outperforms no-gating (LEGACY) in most scenarios — on raw throughput alone, not just error reduction. Benchmark results against a single-node local cluster with multiple independent clients (no shared state between them): | Scenario | BALANCED | LEGACY (no gating) | | ----------------------------- | --------------- | ------------------ | | Single-client (1K processes) | **80.1 ops/s** | 67.8 ops/s | | Single-client sustained (10K) | **119.6 ops/s** | 87.8 ops/s | | Multi-client 3+2 spike | **86.3 ops/s** | 48.0 ops/s | | Stress 8 clients ×1000 | 76.3 ops/s | **106.4 ops/s** | BALANCED wins 3 of 4 on pure throughput. The only scenario where LEGACY is faster is extreme overload (800 concurrent requests against a single broker) — and in that case LEGACY accumulates 44,505 errors vs BALANCED's 15,527. The default just works. ## Job Workers (Polling API) The SDK provides a lightweight polling job worker for service task job types using `createJobWorker`. It activates jobs in batches (respecting a concurrency limit), validates variables (optional), and offers action helpers on each job. ### Minimal Example ```ts const client = createCamundaClient(); // Define schemas (optional) const Input = z.object({ orderId: z.string() }); const Output = z.object({ processed: z.boolean() }); const worker = client.createJobWorker({ jobType: "process-order", maxParallelJobs: 10, jobTimeoutMs: 15_000, // long‑poll timeout (server side requestTimeout) pollIntervalMs: 100, // delay between polls when no jobs / at capacity // Optional: only fetch specific variables during activation fetchVariables: ["orderId"], inputSchema: Input, // validates incoming variables if validateSchemas true outputSchema: Output, // validates variables passed to complete(...) validateSchemas: true, // set false for max throughput (skip Zod) autoStart: true, // default true; start polling immediately startupJitterMaxSeconds: 5, // random delay up to 5s before first poll (default 0) jobHandler: (job) => { // Access typed variables const vars = job.variables; // inferred from Input schema console.log(`Processing order: ${vars.orderId}`); // Do work... return job.complete({ processed: true }); }, }); // Later, on shutdown: process.on("SIGINT", () => { worker.stop(); }); ``` Note on variable fetching: - `fetchVariables: string[]` limits variables returned on activated jobs to the specified keys. If omitted, all visible variables at activation scope are returned. This maps to the REST API field `fetchVariable`. TypeScript inference: - When you provide `inputSchema`, the type of `fetchVariables` is constrained to the keys of the inferred `variables` type from that schema. Example: ```ts const Input = z.object({ orderId: z.string(), amount: z.number() }); client.createJobWorker({ jobType: "process-order", maxParallelJobs: 5, jobTimeoutMs: 30_000, inputSchema: Input, // Only allows 'orderId' | 'amount' here at compile-time fetchVariables: ["orderId", "amount"], jobHandler: async (job) => job.complete(), }); ``` - Without `inputSchema`, `fetchVariables` defaults to `string[]`. ### Job Handler Semantics Your `jobHandler` must ultimately invoke exactly one of: - `job.complete(variables?, result?)` OR `job.complete()` - `job.fail({ errorMessage, retries?, retryBackoff? })` - `job.cancelWorkflow({})` (cancels the process instance) - `job.error({ errorCode, errorMessage? })` (throws a business error) - `job.ignore()` (marks as done locally without reporting to broker – can be used for decoupled flows) Each action returns an opaque unique symbol receipt (`JobActionReceipt`). The handler's declared return type (`Promise`) is intentional: Why this design: - Enforces a single terminal code path: every successful handler path should end by returning the sentinal obtained by invoking an action. - Enables static reasoning: TypeScript can identify if your handler has a code path that does not acknowledge the job (catch unintended behavior early). - Makes test assertions simple: e.g. `expect(await job.complete()).toBe(JobActionReceipt)`. Acknowledgement lifecycle: - Calling any action (`complete`, `fail`, `cancelWorkflow`, `ignore`) sets `job.acknowledged = true` internally. This surfaces multiple job resolution code paths at runtime. - If the handler resolves (returns the symbol manually or via an action) without any acknowledgement having occurred, the worker logs `job.handler.noAction` and locally marks the job finished WITHOUT informing the broker (avoids a leak of the in-memory slot, but the broker will eventually time out and re-dispatch the job). Recommended usage: - Always invoke an action; if you truly mean to skip broker acknowledgement (for example: forwarding a job to another system which will complete it) use `job.ignore()`. Example patterns: ```ts // GOOD: explicit completion return job.complete({ variables: { processed: true } }); // GOOD: No-arg completion example, sentinel stored for ultimate return // biome-ignore lint/correctness/noUnreachable: intentional — showing multiple completion patterns const ack = await job.complete(); // ... return ack; // GOOD: explicit ignore const ack2 = await job.ignore(); ``` ### Job Corrections (User Task Listeners) When a job worker handles a [user task listener](https://docs.camunda.io/docs/components/concepts/user-task-listeners/), it can correct task properties (assignee, due date, candidate groups, etc.) by passing a `result` to `job.complete()`: ```ts const worker = client.createJobWorker({ jobType: "io.camunda:userTaskListener", jobTimeoutMs: 30_000, maxParallelJobs: 5, jobHandler: async (job) => { const result: JobResult = { type: "userTask", corrections: { assignee: "corrected-user", priority: 80, }, }; return job.complete({}, result); }, }); ``` To deny a task completion (reject the work): ```ts return job.complete( {}, { type: "userTask", denied: true, deniedReason: "Insufficient documentation", } ); ``` | Correctable attribute | Type | Clear value | | --------------------- | ------------------- | ----------------- | | `assignee` | `string` | Empty string `""` | | `dueDate` | `string` (ISO 8601) | Empty string `""` | | `followUpDate` | `string` (ISO 8601) | Empty string `""` | | `candidateUsers` | `string[]` | Empty array `[]` | | `candidateGroups` | `string[]` | Empty array `[]` | | `priority` | `number` (0–100) | — | Omitting an attribute or passing `null` preserves the persisted value. ### Concurrency & Backpressure Set `maxParallelJobs` to the maximum number of jobs you want actively processing concurrently. The worker will long‑poll for up to the remaining capacity each cycle. Global backpressure (adaptive concurrency) still applies to the underlying REST calls; activation itself is a normal operation. ### Validation If `validateSchemas` is true: - Incoming `variables` are parsed with `inputSchema` (fail => job is failed with a validation error message). - Incoming `customHeaders` parsed with `customHeadersSchema` if provided. - Completion payload `variables` parsed with `outputSchema` (warns & proceeds on failure). ### Graceful Shutdown Use `await worker.stopGracefully({ waitUpToMs?, checkIntervalMs? })` to drain without force‑cancelling the current activation request. ```ts // Attempt graceful drain for up to 8 seconds const { remainingJobs, timedOut } = await worker.stopGracefully({ waitUpToMs: 8000, }); if (timedOut) { console.warn("Graceful stop timed out; remaining jobs:", remainingJobs); } ``` Behavior: - Stops scheduling new polls immediately. - Lets any in‑flight activation finish (not cancelled proactively). - Waits for active jobs to acknowledge (complete/fail/cancelWorkflow/ignore). - On timeout: falls back to hard stop semantics (cancels activation) and logs `worker.gracefulStop.timeout` at debug. For immediate termination call `worker.stop()` (or `client.stopAllWorkers()`) which cancels the in‑flight activation if present. Activation cancellations during stop are logged at debug (`activation.cancelled`) instead of error noise. ### Multiple Workers You can register multiple workers on a single client instance—one per job type is typical. The client exposes `client.getWorkers()` for inspection and `client.stopAllWorkers()` for coordinated shutdown. ### Startup Jitter When deploying multiple application instances simultaneously (e.g. a rolling restart or scale-up), all workers start polling at the same time and can saturate the server with activation requests. Set `startupJitterMaxSeconds` to spread out the initial poll across a random window: ```ts client.createJobWorker({ jobType: "process-order", maxParallelJobs: 10, jobTimeoutMs: 30_000, startupJitterMaxSeconds: 5, // each instance delays 0–5s before first poll jobHandler: async (job) => job.complete(), }); ``` A value of `0` (the default) means no delay. ### Heritable Worker Defaults When running many workers with the same base configuration, you can set global defaults via environment variables (or equivalent keys in `CamundaOptions.config`). These apply to every worker created by the client (both `createJobWorker` and `createThreadedJobWorker`) unless the individual worker config explicitly overrides them. | Environment Variable | Worker Config Field | Type | | ------------------------------------------- | ------------------------- | ------ | | `CAMUNDA_WORKER_TIMEOUT` | `jobTimeoutMs` | number | | `CAMUNDA_WORKER_MAX_CONCURRENT_JOBS` | `maxParallelJobs` | number | | `CAMUNDA_WORKER_REQUEST_TIMEOUT` | `pollTimeoutMs` | number | | `CAMUNDA_WORKER_NAME` | `workerName` | string | | `CAMUNDA_WORKER_STARTUP_JITTER_MAX_SECONDS` | `startupJitterMaxSeconds` | number | **Precedence:** explicit worker config value > `CAMUNDA_WORKER_*` (from environment variables or `CamundaOptions.config` overrides) > hardcoded default (where applicable). Example — set defaults via environment: ```bash export CAMUNDA_WORKER_TIMEOUT=30000 export CAMUNDA_WORKER_MAX_CONCURRENT_JOBS=8 export CAMUNDA_WORKER_NAME=order-service ``` ```ts // Workers inherit timeout, concurrency, and name from environment const w1 = client.createJobWorker({ jobType: "validate-order", jobHandler: async (job) => job.complete(), }); const w2 = client.createJobWorker({ jobType: "ship-order", jobHandler: async (job) => job.complete(), }); // Per-worker override: this worker uses 32 concurrent jobs instead of the global 8 const w3 = client.createJobWorker({ jobType: "bulk-import", maxParallelJobs: 32, jobHandler: async (job) => job.complete(), }); ``` You can also pass defaults programmatically via the client constructor: ```ts const client = createCamundaClient({ config: { CAMUNDA_WORKER_TIMEOUT: 30000, CAMUNDA_WORKER_MAX_CONCURRENT_JOBS: 8, }, }); ``` ### Receipt Type (Unique Symbol) Action methods return a unique symbol (not a string) to avoid accidental misuse and allow internal metrics. If you store the receipt, annotate its type as `JobActionReceipt` to preserve uniqueness: ```ts const receipt: JobActionReceipt = await job.complete({ processed: true }); ``` If you ignore the return value you don’t need to import the symbol. ### When Not To Use The Worker - Extremely latency‑sensitive tasks where a push mechanism or streaming protocol is required. - Massive fan‑out requiring custom partitioning strategies (implement a custom activator loop instead). - Browser environments (long‑lived polling + secret handling often unsuitable). For custom strategies you can still call `client.activateJobs(...)`, manage concurrency yourself, and use `completeJob` / `failJob` directly. ### Guarantees & Caveats - Never increases latency for healthy clusters (no cap until first signal). - Cannot create fairness across multiple _processes_; it is per client instance in a single process. Scale your worker pool with that in mind. - Not a replacement for server‑side quotas or external rate limiters—it's a cooperative adaptive limiter. ### Opt‑Out To bypass adaptive concurrency while still collecting severity metrics use: ```bash CAMUNDA_SDK_BACKPRESSURE_PROFILE=LEGACY ``` This reverts to only per‑request retry for transient errors (no global gating) while keeping observability. ### Inspecting State Programmatically Call `client.getBackpressureState()` to obtain: ```ts const state = client.getBackpressureState(); // state.severity: 'healthy' | 'soft' | 'severe' // state.consecutive: number — consecutive backpressure signals observed // state.permitsMax: number | null — current concurrency cap (null => unlimited/not engaged) // state.permitsCurrent: number — currently acquired permits // state.waiters: number — queued operations waiting for a permit ``` ### Threaded Job Workers (Node.js Only) For CPU-intensive job handlers, `createThreadedJobWorker` offloads handler execution to a pool of Node.js `worker_threads`. Polling and I/O remain on the main event loop, while handler logic runs in parallel threads — dramatically improving throughput when the handler does CPU-bound work (JSON processing, validation, transformation, cryptography). #### When to use - Your handler spends significant time on CPU work (not just waiting for HTTP responses) - You observe that a single-threaded worker saturates one CPU core while throughput plateaus - You need to process more jobs per second without deploying additional instances If your handler is mostly I/O-bound (HTTP calls, database queries), the standard `createJobWorker` is sufficient. #### Handler module The handler must be a **separate file** (not an inline function) that exports a default async function: ```ts // my-handler.ts (or my-handler.js) const handler: ThreadedJobHandler = async (job, client) => { const { orderId } = job.variables; // CPU-intensive work here... const result = heavyComputation(orderId); return job.complete({ result }); }; export default handler; ``` Typing your handler as `ThreadedJobHandler` gives full intellisense for `job` (variables, action methods like `complete()`, `fail()`, `error()`) and `client` (every `CamundaClient` API method). The handler receives two arguments: 1. **`job`** — a proxy with the same shape as a regular job worker job (`variables`, `customHeaders`, `jobKey`, plus action methods: `complete()`, `fail()`, `error()`, `cancelWorkflow()`, `ignore()`) 2. **`client`** — a proxy to the `CamundaClient` on the main thread. You can call any SDK method (e.g. `client.publishMessage(...)`, `client.createProcessInstance(...)`) and it will be forwarded to the main thread and executed there. #### Minimal example ```ts const client = createCamundaClient(); const worker = client.createThreadedJobWorker({ jobType: "cpu-heavy-task", handlerModule: path.join( path.dirname(fileURLToPath(import.meta.url)), "my-handler.js" ), maxParallelJobs: 32, jobTimeoutMs: 30_000, }); ``` #### Configuration `createThreadedJobWorker` accepts all the same options as `createJobWorker` (except `jobHandler`), plus: | Option | Type | Default | Description | | ---------------- | -------- | --------------------------- | ---------------------------------------------------------------- | | `handlerModule` | `string` | (required) | Path to handler module (absolute or relative to `process.cwd()`) | | `threadPoolSize` | `number` | `os.availableParallelism()` | Number of worker threads in the pool | Other familiar options: `jobType`, `maxParallelJobs`, `jobTimeoutMs`, `pollIntervalMs`, `pollTimeoutMs`, `fetchVariables`, `inputSchema`, `outputSchema`, `customHeadersSchema`, `validateSchemas`, `autoStart`, `startupJitterMaxSeconds`, `workerName`. #### Lifecycle Threaded workers integrate with the same lifecycle as regular workers: ```ts // Returned by getWorkers() const allWorkers = client.getWorkers(); // Stopped by stopAllWorkers() client.stopAllWorkers(); ``` ```ts // Graceful shutdown (waits for in-flight jobs to finish) const { timedOut, remainingJobs } = await worker.stopGracefully({ waitUpToMs: 10_000, }); ``` #### Pool stats ```ts worker.poolSize; // number of threads worker.busyThreads; // threads currently processing a job worker.activeJobs; // total jobs dispatched but not yet completed ``` #### How it works 1. The main thread polls `activateJobs` using the same mechanism as `createJobWorker` 2. Activated jobs are serialized and dispatched to an idle thread via `MessageChannel` 3. The thread loads the handler module (lazy, on first job), creates a proxy for `job` action methods and `client` API calls 4. Action methods (`job.complete()`, `job.fail()`, etc.) and client calls are forwarded back to the main thread over the `MessagePort` and executed there 5. The result is relayed back, and the thread is marked idle for the next job #### Constraints - **Node.js only**: `worker_threads` is not available in browsers or Deno - **Handler must be a file module**: Inline functions cannot be transferred to threads - **Job variables must be JSON-serializable**: Functions and class instances on the job are stripped during transfer - **Client calls are async round-trips**: Each `client.xyz()` call crosses a thread boundary, adding a small amount of latency per call --- ## Authentication Set `CAMUNDA_AUTH_STRATEGY` to `NONE` (default), `BASIC`, or `OAUTH`. Basic: ``` CAMUNDA_AUTH_STRATEGY=BASIC CAMUNDA_BASIC_AUTH_USERNAME=alice CAMUNDA_BASIC_AUTH_PASSWORD=supersecret ``` OAuth (client credentials): ``` CAMUNDA_AUTH_STRATEGY=OAUTH CAMUNDA_CLIENT_ID=yourClientId CAMUNDA_CLIENT_SECRET=yourSecret CAMUNDA_OAUTH_URL=https://idp.example/oauth/token # if required by your deployment ``` Optional audience / retry / timeout vars are also read if present (see generated config reference). Auth helper features (automatic inside the client): - Disk + memory token cache - Early refresh with skew handling - Exponential backoff & jitter - Singleflight suppression of concurrent refreshes - Hook: `client.onAuthHeaders(h => ({ ...h, 'X-Trace': 'abc' }))` - Force refresh: `await client.forceAuthRefresh()` - Clear caches: `client.clearAuthCache({ disk: true, memory: true })` ### Token Caching & Persistence The SDK always keeps the active OAuth access token in memory. Optional disk persistence (Node only) is enabled by setting: ```bash CAMUNDA_OAUTH_CACHE_DIR=/path/to/cache ``` When present and running under Node, each distinct credential context (combination of `oauthUrl | clientId | audience | scope`) is hashed to a filename: ``` /camunda_oauth_token_cache_.json ``` Writes are atomic (`.tmp` + rename) and use file mode `0600` (owner read/write). On process start the SDK attempts to load the persisted file to avoid an unnecessary token fetch; if the token is near expiry it will still perform an early refresh (5s skew window plus additional safety buffer based on 5% or 30s minimum). Clearing / refreshing: - Programmatic clear: `client.clearAuthCache({ disk: true, memory: true })` - Memory only: `client.clearAuthCache({ memory: true, disk: false })` - Force new token (ignores freshness): `await client.forceAuthRefresh()` Disable disk persistence by simply omitting `CAMUNDA_OAUTH_CACHE_DIR` (memory cache still applies). For short‑lived or serverless functions you may prefer no disk cache to minimize I/O; for long‑running workers disk caching reduces cold‑start latency and load on the identity provider across restarts / rolling deploys. Security considerations: - Ensure the directory has restrictive ownership/permissions; the SDK creates files with `0600` but will not alter parent directory permissions. - Tokens are bearer credentials; treat the directory like a secrets store and avoid including it in container image layers or backups. - If you rotate credentials (client secret) the filename hash changes; old cache files become unused and can be pruned safely. Browser usage: There is no disk concept—if executed in a browser the SDK (when strategy OAUTH) attempts to store the token in `sessionStorage` (tab‑scoped). Closing the tab clears the cache; a new tab will fetch a fresh token. If you need a custom persistence strategy (e.g. Redis / encrypted keychain), wrap the client and periodically call `client.forceAuthRefresh()` while storing and re‑injecting the token via a headers hook; first measure whether the built‑in disk cache already meets your needs. ## Self-signed TLS / mTLS (Node only) The SDK supports custom TLS certificates via environment variables. This is useful for: - **Self-signed server certificates** — trust a CA that signed your server's certificate, without presenting a client identity. - **Mutual TLS (mTLS)** — present a client certificate and key to prove the client's identity. - **Both** — trust a custom CA _and_ present client credentials. ### Trusting a self-signed server certificate Set only the CA certificate to trust the server's self-signed certificate: ```bash # Path to PEM file: CAMUNDA_MTLS_CA_PATH=/path/to/ca.pem # Or inline PEM (must contain real newlines, not literal '\n'): CAMUNDA_MTLS_CA="$(cat /path/to/ca.pem)" ``` ### Mutual TLS (client certificate) To present a client certificate for mutual TLS, provide both the certificate and private key: ```bash CAMUNDA_MTLS_CERT_PATH=/path/to/client.crt CAMUNDA_MTLS_KEY_PATH=/path/to/client.key # Optional — passphrase if the key is encrypted: # CAMUNDA_MTLS_KEY_PASSPHRASE=secret ``` ### Full mTLS with custom CA Combine a custom CA with client credentials: ```bash CAMUNDA_MTLS_CA_PATH=/path/to/ca.pem CAMUNDA_MTLS_CERT_PATH=/path/to/client.crt CAMUNDA_MTLS_KEY_PATH=/path/to/client.key ``` Inline PEM values (`CAMUNDA_MTLS_CERT`, `CAMUNDA_MTLS_KEY`, `CAMUNDA_MTLS_CA`) take precedence over their `_PATH` counterparts. An `https.Agent` is attached to all outbound calls (including token fetches). ## Branded Keys Import branded key helpers directly: ```ts ProcessDefinitionKey, ProcessInstanceKey, } from "@camunda8/orchestration-cluster-api"; const defKey = ProcessDefinitionKey.assumeExists("2251799813686749"); // @ts-expect-error – cannot assign def key to instance key const bad: ProcessInstanceKey = defKey; ``` They are zero‑cost runtime strings with compile‑time separation. ## Cancelable Operations All methods return a `CancelablePromise`: ```ts const p = camunda.searchProcessInstances( { filter: { processDefinitionKey: defKey } }, { consistency: { waitUpToMs: 0 } } ); setTimeout(() => p.cancel(), 100); // best‑effort cancel try { await p; // resolves if not cancelled } catch (e) { if (isSdkError(e) && e.name === "CancelSdkError") { console.log("Operation cancelled"); } else throw e; } ``` Notes: - Rejects with `CancelSdkError`. - Cancellation classification runs first so aborted fetches are never downgraded to generic network errors. - Abort is immediate and idempotent; underlying fetch is signalled. ## Functional (fp-ts style) Surface (Opt-In Subpath) @experimental - this feature is not guaranteed to be tested or stable. > **Peer dependency:** `fp-ts` is an optional peer dependency. If you use real `fp-ts` functions > (e.g. `pipe`, `TE.match`) alongside this subpath, install it separately: > > ```sh > npm install fp-ts > ``` > > The `/fp` subpath works without `fp-ts` installed — it exposes structurally-compatible > `Either`/`TaskEither` shapes that interoperate with `fp-ts` but do not require it at runtime. The main entry stays minimal. To opt in to a TaskEither-style facade & helper combinators import from the dedicated subpath: ```ts createCamundaFpClient, retryTE, withTimeoutTE, eventuallyTE, isLeft, } from "@camunda8/orchestration-cluster-api/fp"; const fp = createCamundaFpClient(); const deployTE = fp.deployResourcesFromFiles(["./bpmn/process.bpmn"]); const deployed = await deployTE(); if (isLeft(deployed)) throw deployed.left; // DomainError union // Chain with fp-ts (optional) – the returned thunks are structurally compatible with TaskEither // import { pipe } from 'fp-ts/function'; import * as TE from 'fp-ts/TaskEither'; ``` Why a subpath? - Keeps base bundle lean for the 80% use case. - No hard dependency on `fp-ts` at runtime; only structural types. - Advanced users can compose with real `fp-ts` without pulling the effect model into the default import path. Exports available from `.../fp`: - `createCamundaFpClient` – typed facade (methods return `() => Promise>`). - Type guards: `isLeft`, `isRight`. - Error / type aliases: `DomainError`, `TaskEither`, `Either`, `Left`, `Right`, `Fpify`. - Combinators: `retryTE`, `withTimeoutTE`, `eventuallyTE`. DomainError union currently includes: - `CamundaValidationError` - `EventualConsistencyTimeoutError` - HTTP-like error objects (status/body/message) produced by transport - Generic `Error` You can refine left-channel typing later by mapping HTTP status codes or discriminator fields. ## Eventual Consistency Polling Some endpoints accept consistency management options. Pass a `consistency` block (where supported) with `waitUpToMs` and optional `pollIntervalMs` (default 500). If the condition is not met within timeout an `EventualConsistencyTimeoutError` is thrown. To consume eventual polling in a non‑throwing fashion set the client error mode before invoking an eventually consistent method: At present the canonical client operates in throwing mode. Non‑throwing adaptation (Result / fp-ts) is achieved via the functional wrappers rather than mutating the base client. ### Options `consistency` object fields (all optional except `waitUpToMs`): | Field | Type | Description | | ---------------- | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `waitUpToMs` | `number` | Maximum total time to wait before failing. `0` disables polling and returns the first response immediately. | | `pollIntervalMs` | `number` | Base delay between attempts (minimum enforced at 10ms). Defaults to `500` or the value of `CAMUNDA_SDK_EVENTUAL_POLL_DEFAULT_MS` if provided. | | `predicate` | `(result) => boolean \| Promise` | Custom success condition. If omitted, non-GET endpoints default to: first 2xx body whose `items` array (if present) is non-empty. | | `trace` | `boolean` | When true, logs each 200 response body (truncated ~1KB) before predicate evaluation and emits a success line with elapsed time when the predicate passes. Requires log level `debug` (or `trace`) to see output. | | `onAttempt` | `(info) => void` | Callback after each attempt: `{ attempt, elapsedMs, remainingMs, status, predicateResult, nextDelayMs }`. | | `onComplete` | `(info) => void` | Callback when predicate succeeds: `{ attempts, elapsedMs }`. Not called on timeout. | ### Trace Logging Enable by setting `trace: true` inside `consistency`. Output appears under the `eventual` log scope at level `debug` so you must raise the SDK log level (e.g. `CAMUNDA_SDK_LOG_LEVEL=debug`). Emitted lines (examples): ``` [camunda-sdk][debug][eventual] op=searchJobs attempt=3 trace body={"items":[]} [camunda-sdk][debug][eventual] op=searchJobs attempt=5 status=200 predicate=true elapsed=742ms totalAttempts=5 ``` Use this to understand convergence speed and data shape evolution during tests or to diagnose slow propagation. ### Example ```ts const jobs = await camunda.searchJobs( { filter: { type: "payment" }, }, { consistency: { waitUpToMs: 5000, pollIntervalMs: 200, trace: true, predicate: (r) => Array.isArray(r.items) && r.items.some((j) => j.state === "CREATED"), }, } ); ``` On timeout an `EventualConsistencyTimeoutError` includes diagnostic fields: `{ attempts, elapsedMs, lastStatus, lastResponse, operationId }`. ## Logging Per‑client logger; no global singleton. The level defaults from `CAMUNDA_SDK_LOG_LEVEL` (default `error`). ```ts const client = createCamundaClient({ log: { level: "info", transport: (evt) => { // evt: { level, scope, ts, args, code?, data? } console.log(JSON.stringify(evt)); }, }, }); const log = client.logger("worker"); log.debug(() => ["expensive detail only if enabled", { meta: 1 }]); log.code("info", "WORK_START", "Starting work loop", { pid: process.pid }); ``` Lazy args (functions with zero arity) are only invoked if the level is enabled. Update log level / transport at runtime via `client.configure({ log: { level: 'debug' } })`. ### Default Behaviour Without any explicit `log` option: - Level = `error` (unless `CAMUNDA_SDK_LOG_LEVEL` is set) - Transport = console (`console.error` / `console.warn` / `console.log`) - Only `error` level internal events are emitted (e.g. strict validation failure summaries, fatal auth issues) - No info/debug/trace noise by default To silence everything set level to `silent`: ```bash CAMUNDA_SDK_LOG_LEVEL=silent ``` To enable debug logs via env: ```bash CAMUNDA_SDK_LOG_LEVEL=debug ``` ### Unsafe Deep Diagnostics (`silly`) Setting `CAMUNDA_SDK_LOG_LEVEL=silly` enables the deepest diagnostics. In addition to everything at `trace`, the SDK will emit HTTP request and response body previews for all HTTP methods under the `telemetry` scope (log line contains `http.body`). This can leak sensitive information (secrets, PII). A warning (`log.level.silly.enabled`) is emitted on client construction. Use only for short‑lived local debugging; never enable in production or share captured logs externally. Body output is truncated (max ~4KB) and form-data parts identify uploaded files as `[File]`. ### Bring Your Own Logger Provide a `transport` function to forward structured `LogEvent` objects into any logging library. #### Pino ```ts const p = pino(); const client = createCamundaClient({ log: { level: 'info', transport: e => { const lvl = e.level === 'trace' ? 'debug' : e.level; // map trace p.child({ scope: e.scope, code: e.code }).[lvl]({ ts: e.ts, data: e.data, args: e.args }, e.args.filter(a=>typeof a==='string').join(' ')); } } }); ``` #### Winston ```ts const w = winston.createLogger({ transports: [new winston.transports.Console()], }); const client = createCamundaClient({ log: { level: "debug", transport: (e) => { const lvl = e.level === "trace" ? "silly" : e.level; // winston has 'silly' w.log({ level: lvl, message: e.args.filter((a) => typeof a === "string").join(" "), scope: e.scope, code: e.code, data: e.data, ts: e.ts, }); }, }, }); ``` #### loglevel ```ts log.setLevel("info"); // host app level const client = createCamundaClient({ log: { level: "info", transport: (e) => { if (e.level === "silent") return; const method = ( ["error", "warn", "info", "debug"].includes(e.level) ? e.level : "debug" ) as "error" | "warn" | "info" | "debug"; (log as any)[method]( `[${e.scope}]`, e.code ? `${e.code}:` : "", ...e.args ); }, }, }); ``` #### Notes - Map `trace` to the nearest available level if your logger lacks it. - Use `log.code(level, code, msg, data)` for machine-parsable events. - Redact secrets before logging if you add token contents to custom messages. - Reconfigure later: `client.configure({ log: { level: 'warn' } })` updates only that client. - When the effective level is `debug` (or `trace`), the client emits a lazy `config.hydrated` event on construction and `config.reconfigured` on `configure()`, each containing the redacted effective configuration `{ config: { CAMUNDA_... } }`. Secrets are already masked using the SDK's redaction rules. ## Errors May throw: - Network / fetch failures - Non‑2xx HTTP responses - Validation errors (strict mode) - `EventualConsistencyTimeoutError` - `CancelSdkError` on cancellation ### Typed Error Handling All SDK-thrown operational errors normalize to a discriminated union (`SdkError`) when they originate from HTTP, network, auth, or validation layers. Use the guard `isSdkError` to narrow inside a catch: ```ts createCamundaClient, isSdkError, } from "@camunda8/orchestration-cluster-api"; const client = createCamundaClient(); try { await client.getTopology(); } catch (e) { if (isSdkError(e)) { switch (e.name) { case "HttpSdkError": console.error("HTTP failure", e.status, e.operationId); break; case "ValidationSdkError": console.error("Validation issue on", e.operationId, e.side, e.issues); break; case "AuthSdkError": console.error("Auth problem", e.message, e.status); break; case "CancelSdkError": console.error("Operation cancelled", e.operationId); break; case "NetworkSdkError": console.error("Network layer error", e.message); break; } return; } // Non-SDK (programmer) error; rethrow or wrap throw e; } ``` Guarantees: - HTTP errors expose `status` and optional `operationId`. - If the server returns RFC 9457 / RFC 7807 Problem Details JSON (`type`, `title`, `status`, `detail`, `instance`) these fields are passed through on the `HttpSdkError` when present. - Validation errors expose `side` and `operationId`. - Classification is best-effort; unknown shapes fall back to `NetworkSdkError`. > Advanced: You can still layer your own domain errors on top (e.g. translate certain status codes) by mapping `SdkError` into custom discriminants. ### Functional / Non‑Throwing Variant - EXPERIMENTAL _Note that this feature is experimental and subject to change._ If you prefer FP‑style explicit error handling instead of exceptions, use the result client wrapper: ```ts createCamundaResultClient, isOk, } from "@camunda8/orchestration-cluster-api"; const camundaR = createCamundaResultClient(); const res = await camundaR.createDeployment({ resources: [file] }); if (isOk(res)) { console.log("Deployment key", res.value.deploymentKey); } else { console.error("Deployment failed", res.error); } ``` API surface differences: - All async operation methods return `Promise>` where `Result = { ok: true; value: T } | { ok: false; error: unknown }`. - No exceptions are thrown for HTTP / validation errors (cancellation and programmer errors like invalid argument sync throws are still converted to `{ ok:false }`). - The original throwing client is available via `client.inner` if you need to mix styles. Helpers: ```ts ``` When to use: - Integrating with algebraic effects / functional pipelines. - Avoiding try/catch nesting in larger orchestration flows. - Converting to libraries expecting an Either/Result pattern. ### fp-ts Adapter (TaskEither / Either) - EXPERIMENTAL _Note that this feature is experimental and subject to change._ For projects using `fp-ts`, wrap the throwing client in a lazy `TaskEither` facade: ```ts const fp = createCamundaFpClient(); const deployTE = fp.createDeployment({ resources: [file] }); // TaskEither pipe( deployTE(), // invoke the task (returns Promise) (then) => then // typical usage would use TE.match / TE.fold; shown expanded for clarity ); // With helpers const task = fp.createDeployment({ resources: [file] }); const either = await task(); if (either._tag === "Right") { console.log(either.right.deployments.length); } else { console.error("Error", either.left); } ``` Notes: - No runtime dependency on `fp-ts`; adapter implements a minimal `Either` shape. Structural typing lets you lift into real `fp-ts` functions (`fromEither`, etc.). - Each method becomes a function returning `() => Promise>` (a `TaskEither` shape). Invoke it later to execute. - Cancellation: calling `.cancel()` on the original promise isn’t surfaced; if you need cancellation use the base client directly. - For richer interop, you can map the returned factory to `TE.tryCatch` in userland. ## Pagination Search endpoints expose typed request bodies that include pagination fields. Provide the desired page object; auto‑pagination is not (yet) bundled. ## Configuration Reference Generated doc enumerating all supported environment variables (types, defaults, conditional requirements, redaction rules) is produced at build time: ``` ./docs/CONFIG_REFERENCE.md ``` ## Deploying Resources (File-only) The deployment endpoint requires each resource to have a filename (extension used to infer type: `.bpmn`, `.dmn`, `.form` / `.json`). Extensions influence server classification; incorrect or missing extensions may yield unexpected results. Pass an array of `File` objects (NOT plain `Blob`). ### Browser ```ts const bpmnXml = `...`; const file = new File([bpmnXml], "order-process.bpmn", { type: "application/xml", }); const result = await camunda.createDeployment({ resources: [file] }); console.log(result.deployments.length); ``` From an existing Blob: ```ts const blob: Blob = getBlob(); const file = new File([blob], "model.bpmn"); await camunda.createDeployment({ resources: [file] }); ``` ### Node (Recommended Convenience) Use the built-in helper `deployResourcesFromFiles(...)` to read local files and create `File` objects automatically. It returns the enriched `ExtendedDeploymentResult` (adds typed arrays: `processes`, `decisions`, `decisionRequirements`, `forms`, `resources`). ```ts const result = await camunda.deployResourcesFromFiles([ "./bpmn/order-process.bpmn", "./dmn/discount.dmn", "./forms/order.form", ]); console.log(result.processes.map((p) => p.processDefinitionId)); console.log(result.decisions.length); ``` With explicit tenant (overriding tenant from configuration): ```ts await camunda.deployResourcesFromFiles(["./bpmn/order-process.bpmn"], { tenantId: "tenant-a", }); ``` Error handling: ```ts try { await camunda.deployResourcesFromFiles([]); // throws (empty array) } catch (e) { console.error("Deployment failed:", e); } ``` Manual construction alternative (if you need custom logic): ```ts const bpmnXml = ''; const file = new File([Buffer.from(bpmnXml)], "order-process.bpmn", { type: "application/xml", }); await camunda.createDeployment({ resources: [file] }); ``` Helper behavior: - Dynamically imports `node:fs/promises` & `node:path` (tree-shaken from browser bundles) - Validates Node environment (throws in browsers) - Lightweight MIME inference: `.bpmn|.dmn|.xml -> application/xml`, `.json|.form -> application/json`, fallback `application/octet-stream` - Rejects empty path list Empty arrays are rejected. Always use correct extensions so the server can classify each resource. ## Testing Patterns Create isolated clients per test file: ```ts const client = createCamundaClient({ config: { CAMUNDA_REST_ADDRESS: "http://localhost:8080", CAMUNDA_AUTH_STRATEGY: "NONE", }, }); ``` Inject a mock fetch: ```ts const client = createCamundaClient({ fetch: async (_input, _init) => new Response(JSON.stringify({ ok: true }), { status: 200 }), }); ``` ## API Documentation Generate an HTML API reference site with TypeDoc (public entry points only): ```bash npm run docs:api ``` Output: static site in `docs/api` (open `docs/api/index.html` in a browser or serve the folder, e.g. `npx http-server docs/api`). Entry points: `src/index.ts`, `src/logger.ts`, `src/fp/index.ts`. Internal generated code, scripts, tests are excluded and private / protected members are filtered. Regenerate after changing public exports. ## Contributing We welcome issues and pull requests. Please read the [CONTRIBUTING.md](https://github.com/camunda/orchestration-cluster-api-js/blob/main/CONTRIBUTING.md) guide before opening a PR to understand: - Deterministic builds policy (no committed timestamps) – see CONTRIBUTING - Commit message conventions (Conventional Commits with enforced subject length) - Release workflow & how to dry‑run semantic‑release locally - Testing strategy (unit vs integration) - Performance and security considerations ## License Apache 2.0 --- ## Function: createLogger() ```ts function createLogger(opts?): Logger; ``` ## Parameters ### opts? [`CreateLoggerOptions`](../../index/interfaces/CreateLoggerOptions.md) = `{}` ## Returns [`Logger`](../interfaces/Logger.md) --- ## logger ## Interfaces - [LogEvent](interfaces/LogEvent.md) - [Logger](interfaces/Logger.md) ## Type Aliases - [LogLevel](type-aliases/LogLevel.md) - [LogTransport](type-aliases/LogTransport.md) ## Functions - [createLogger](functions/createLogger.md) --- ## Interface: LogEvent ## Properties ### args ```ts args: any[]; ``` --- ### code? ```ts optional code?: string; ``` --- ### data? ```ts optional data?: any; ``` --- ### level ```ts level: LogLevel; ``` --- ### scope ```ts scope: string; ``` --- ### ts ```ts ts: number; ``` --- ## Interface: Logger ## Methods ### code() ```ts code( level, code, msg, data?): void; ``` #### Parameters ##### level [`LogLevel`](../type-aliases/LogLevel.md) ##### code `string` ##### msg `string` ##### data? `any` #### Returns `void` --- ### debug() ```ts debug(...a): void; ``` #### Parameters ##### a ...`any`[] #### Returns `void` --- ### error() ```ts error(...a): void; ``` #### Parameters ##### a ...`any`[] #### Returns `void` --- ### info() ```ts info(...a): void; ``` #### Parameters ##### a ...`any`[] #### Returns `void` --- ### level() ```ts level(): LogLevel; ``` #### Returns [`LogLevel`](../type-aliases/LogLevel.md) --- ### scope() ```ts scope(child): Logger; ``` #### Parameters ##### child `string` #### Returns `Logger` --- ### setLevel() ```ts setLevel(level): void; ``` #### Parameters ##### level [`LogLevel`](../type-aliases/LogLevel.md) #### Returns `void` --- ### setTransport() ```ts setTransport(t?): void; ``` #### Parameters ##### t? [`LogTransport`](../type-aliases/LogTransport.md) #### Returns `void` --- ### silly() ```ts silly(...a): void; ``` #### Parameters ##### a ...`any`[] #### Returns `void` --- ### trace() ```ts trace(...a): void; ``` #### Parameters ##### a ...`any`[] #### Returns `void` --- ### warn() ```ts warn(...a): void; ``` #### Parameters ##### a ...`any`[] #### Returns `void` --- ## Type Alias: LogLevel ```ts type LogLevel = | "silent" | "error" | "warn" | "info" | "debug" | "trace" | "silly"; ``` --- ## Type Alias: LogTransport ```ts type LogTransport = (e) => void; ``` ## Parameters ### e [`LogEvent`](../interfaces/LogEvent.md) ## Returns `void` --- ## @camunda8/orchestration-cluster-api ## Modules - [fp](fp/index.md) - [index](index/index.md) - [logger](logger/index.md) --- ## Manage backpressure with the TypeScript SDK Learn how to manage backpressure with the TypeScript SDK. ## Manage backpressure The Orchestration Cluster REST API client implements adaptive backpressure management. - It automatically handles retries and backoff in response to backpressure signals from the server. - The SDK applies global backpressure management to throttle operations when the server returns backpressure. :::info To learn more about backpressure in Camunda 8, see [backpressure](/self-managed/components/orchestration-cluster/zeebe/operations/backpressure.md). ::: ### Enable backpressure management This behavior is controlled by the `CAMUNDA_SDK_BACKPRESSURE_PROFILE` environment variable and is enabled by default. - When enabled, SDK calls do not fail when the server responds with a backpressure signal. Instead, the SDK retries these calls after an increasing backoff period. Additional SDK calls made during backpressure are delayed proactively rather than each needing to receive a backpressure signal. - When a retried operation succeeds, the SDK reduces its internal backpressure status and removes throttling. This allows the SDK to match the server’s capacity and optimize throughput. - This adaptive mechanism can lead to 2x–5x higher performance under load. See [Patterns to use today to make Camunda 8 apps even more reliable](https://www.camundacon.com/event-session/camundacon-new-york-2025/patterns-to-use-today-to-make-camunda-8-apps-even-more-reliable?on_demand=true) for a demonstration. ### Disable backpressure management To disable SDK backpressure management and throw immediately when the server responds with a backpressure signal, set `CAMUNDA_SDK_BACKPRESSURE_PROFILE=LEGACY`. --- ## TypeScript SDK Use the TypeScript SDK to connect to Camunda 8, deploy process models, and work with the Orchestration Cluster API. ## About this SDK The TypeScript SDK provides typed access to Camunda 8 APIs. - It includes IntelliSense support and works in both JavaScript and TypeScript projects. - The [Camunda 8 TypeScript SDK for Node.js](https://github.com/camunda/camunda-8-js-sdk) is available via [npm](https://www.npmjs.com/package/@camunda8/sdk). ### When to use this package Use the [`@camunda8/sdk`](https://www.npmjs.com/package/@camunda8/sdk) package if: - You need to use the gRPC API for job streaming. - Your server target is Camunda 8.7 or earlier. - You want to migrate an existing application to the Orchestration Cluster API. :::info If you do not need to use gRPC or the V1 APIs, use the [Orchestration Cluster API TypeScript client](oca-client.md). ::: ## Prerequisites The following prerequisites are required to use the TypeScript SDK: | Prerequisite | Description | | :----------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Node.js | The SDK runs in Node.js and cannot run in a web browser due to [technical limitations](https://github.com/camunda/camunda-8-js-sdk/issues/79).If you want to write an application in the web browser, use `@camunda8/orchestration-cluster-api`.See [Get started with the Orchestration Cluster API TypeScript client](./oca-client.md). | ## Get started Get started with the Orchestration Cluster API. 1. Create a new Node.js project that uses TypeScript: ```bash npm init -y npm install -D typescript npx tsc --init ``` 2. Install the SDK as a dependency: ```bash npm i @camunda8/sdk ``` :::info - A complete working version of the quickstart code is [available on GitHub](https://github.com/camunda-community-hub/c8-sdk-demo). - For earlier versions using the v1 APIs, refer to the [SDK README file](https://github.com/camunda/camunda-8-js-sdk). ::: ## Configure the connection Choose one of the following configuration options: - Explicit configuration in code - Zero-configuration constructor with environment variables The recommended configuration is via the zero-configuration constructor, with all values for configuration supplied via environment variables. This makes rotation, secret management, and environment promotion safer and simpler. **The environment variables you must set are outlined below. Replace these with your secrets and URLs.** :::info To configure a client and capture these values when creating the client, see [setting up client connection credentials](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client). ::: ### Self-managed configuration Minimal configuration: ```bash # Self-Managed with Orchestration Cluster API export ZEEBE_REST_ADDRESS='http://localhost:8080/v2' ``` With OAuth: ```bash export ZEEBE_REST_ADDRESS='http://localhost:8080/v2' export ZEEBE_GRPC_ADDRESS='grpc://localhost:26500' export ZEEBE_CLIENT_ID='zeebe' export ZEEBE_CLIENT_SECRET='zecret' export CAMUNDA_OAUTH_URL='http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token' ``` If you are running with multi-tenancy enabled: ```bash export CAMUNDA_TENANT_ID='my-tenant' # tenant used by default if none set ``` ### Camunda SaaS configuration ```bash export ZEEBE_REST_ADDRESS='5c34c0a7-...-125615f7a9b9.syd-1.zeebe.camunda.io' export ZEEBE_GRPC_ADDRESS='grpcs://5c34c0a7-...-125615f7a9b9.syd-1.zeebe.camunda.io:443' export ZEEBE_CLIENT_ID='yvvURO...' export ZEEBE_CLIENT_SECRET='iJJu-SHg...' export CAMUNDA_OAUTH_URL='https://login.cloud.camunda.io/oauth/token' ``` :::caution To set these values explicitly in code (not recommended), pass them with the same key names to the `Camunda8` constructor. ::: ## Use the SDK 1. Create a file `index.ts` in your IDE. 2. Import the SDK: ```typescript const clientFactory = new Camunda8(); ``` 3. Get an Orchestration API client. This is used to deploy process models and start process instances: ```typescript const camunda = clientFactory.getOrchestrationClusterApiClient(); ``` ### Deploy a process model Next, deploy a process model. Network operations are asynchronous and methods that operate over the network return promises. Wrap the main function in an `async` function: ```typescript async function main() { const deployResponse = await camunda.deployResourcesFromFiles([ path.join(process.cwd(), "process.bpmn"), ]); console.log( `[Camunda] Deployed process ${deployResponse.processes[0].processDefinitionId}` ); } main(); // remember to invoke the function ``` Paste the process model XML below into a file named `process.bpmn`: ```xml Flow_0yqo0wz Flow_03qgl0x Flow_0yqo0wz Flow_0qugen1 Flow_0qugen1 Flow_03qgl0x ``` For reference, this is the model you use in this example: ![Example BPMN process model](../img/process-model.png) Run the program to deploy the process model to Camunda: ```bash npx tsx index.ts ``` If your configuration is correct, you should see output similar to the following: ``` [Camunda] Deployed process c8-sdk-demo ``` ### Create a service worker Outside the main function, add the following code: ```typescript console.log("Starting worker..."); const worker = camunda.createJobWorker({ jobType: "service-task", workerName: "test-worker", maxParallelJobs: 20, pollIntervalMs: 1000, pollTimeoutMs: 50_000, jobTimeoutMs: 5000, jobHandler: (job) => { console.log( `[worker]: Completing job ${job.jobKey} from process ${job.processInstanceKey}\n` ); return job.complete({ serviceTaskOutcome: "We did it!", }); }, }); ``` This code starts a service task worker that runs in an asynchronous loop and invokes `jobHandler` when a job of type `service-task` becomes available. The handler must return a job completion function such as `fail`, `complete`, `error`, or `ignore`. The type system enforces this to ensure every code path responds to Zeebe after taking a job. The `job.complete` function can take an object with variables to update. ### Create a programmatic user task worker The process has a [user task](/guides/getting-started-orchestrate-human-tasks.md) after the service task. The service task worker completes the service task job. You complete the user task using the Tasklist API client. Add the following code below the service worker: ```typescript // User task poller const last = new Set(); const userTaskPoller = camunda.searchUserTasks( { filter: { state: "CREATED", }, }, { // To set up a subscription, set waitUpToMs to Infinity consistency: { waitUpToMs: Infinity, pollIntervalMs: 1_000, // predicate now becomes a polling subscription function predicate: async (results) => { // polling memoization - handles idempotency with eventually consistent mutation const current = results.items.filter( (item) => !last.has(item.userTaskKey) ); last.clear(); results.items.forEach((task) => last.add(task.userTaskKey)); for (const userTask of current) { console.log( `[usertask poller]: Claiming task ${userTask.userTaskKey} from process ${userTask.processInstanceKey}\n` ); await camunda.assignUserTask({ userTaskKey: userTask.userTaskKey, assignee: "jwulf", }); console.log( `[usertask poller]: Completing user task ${userTask.userTaskKey} from process ${userTask.processInstanceKey}\n` ); await camunda.completeUserTask({ userTaskKey: userTask.userTaskKey, variables: { userTaskStatus: "Got done", }, }); } return false; // return false to keep polling }, }, } ); ``` You now have an asynchronously polling service and user task worker. The final step is to create a process instance. ### Create a process instance There are two options for creating a process instance: - For long-running processes, use `createProcessInstance`. It returns as soon as the process instance is created with the process instance ID. - For the shorter-running process we are using, set `awaitCompletion: true`. It awaits the completion of the process and returns with the final variable values. 1. Locate the following line in the `main` function: ```typescript console.log( `[Zeebe] Deployed process ${res.deployments[0].process.bpmnProcessId}` ); ``` 2. Inside the `main` function, add the following: ```typescript const result = await camunda.createProcessInstanceWithResult({ processDefinitionId, variables: { userTaskStatus: "Needs doing", }, awaitCompletion: true, }); console.log( `[Camunda] Finished Process Instance ${result.processInstanceKey}` ); console.log( `[Camunda] userTaskStatus is "${result.variables.userTaskStatus}"` ); console.log( `[Camunda] serviceTaskOutcome is "${result.variables.serviceTaskOutcome}"` ); worker.stop(); userTaskPoller.catch((e) => e); // Swallow cancel exception userTaskPoller.cancel(); // Cancel poller to exit app ``` 3. Run the program with the following command: ```bash npx tsx index.ts ``` You see output similar to the following: ``` [Camunda] Deployed process c8-sdk-demo [worker]: Completing job 4503599632829668 from process 4503599632829662 [usertask poller]: Claiming task 4503599632829678 from process 4503599632829662 [usertask poller]: Completing user task 4503599632829678 from process 4503599632829662 [Camunda] Finished Process Instance 4503599632829662 [Camunda] userTaskStatus is "Got done" [Camunda] serviceTaskOutcome is "We did it!" ``` The program continues running until you press `Ctrl+C` because both the service worker and the user task poller run in continuous loops. To explore more SDK functionality, use the examples below. ### Retrieve a process instance When you create a long-running process instance, you typically use `createProcessInstance` and get back the process instance key of the running process immediately instead of waiting for it to complete. To examine the process instance status, use the process instance key to query the Operate API. You can also check completed process instances in the same way. In the following example, you query the process instance created earlier. 1. Locate the following line in the `main` function: ```typescript console.log( `[Camunda] serviceTaskOutcome is "${result.variables.serviceTaskOutcome}"` ); ``` 2. Add the following after this line and inside the `main` function: ```typescript const historicalProcessInstance = await camunda.getProcessInstance( { processInstanceKey: result.processInstanceKey, }, { consistency: { waitUpToMs: 5000 } } ); console.log("[Camunda]", JSON.stringify(historicalProcessInstance, null, 2)); ``` When you run the program now, you should see additional output similar to the following: ``` { processInstanceKey: 4503599632829662, processVersion: 1, processDefinitionId: 'c8-sdk-demo', startDate: '2025-11-08T09:11:06.157+0000', endDate: '2025-11-08T09:11:12.403+0000', state: 'COMPLETED', processDefinitionKey: 2251799814900879, } ``` The state may appear as `ACTIVE` rather than `COMPLETED`. This happens because the data read over the API is historical data from the Zeebe exporter, and lags behind the actual state of the system. It is _eventually consistent_. ## Further resources See the [complete API documentation for the SDK](https://camunda.github.io/camunda-8-js-sdk/) and the [Orchestration Cluster API client](https://camunda.github.io/orchestration-cluster-api-js/classes/index.CamundaClient.html). --- ## Manage Orchestration Cluster API data consistency Learn how to manage eventually consistent data when using the Orchestration Cluster API. ## About eventual consistency Data in Camunda 8 is [eventually consistent](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-data-fetching.md#data-consistency). - To ensure that your applications behave explicitly and deterministically at runtime under different load scenarios, Orchestration Cluster API methods that access eventually consistent data take a required second parameter `consistency`. - This parameter lets you either ignore eventual consistency or manage how your application interacts with it. For example, if you search for a process instance immediately after you create it, the response may contain the new instance. If the data is not yet available, the search may return an empty result or a 404 error. ```typescript const camunda = createCamundaClient(); async function main() { const deploymentResponse = await camunda.deployResourcesFromFile([ "./process.bpmn", ]); const { processDefinitionKey } = deploymentResponse.processes[0]; const { processInstanceKey } = await camunda.createProcessInstance({ processDefinitionKey, }); // May return the process instance, but more likely will return an empty set const runningProcessInstance = await camunda.searchProcessInstances( { filter: { processInstanceKey, }, }, { consistencyManagement: { waitUpToMs: 0 } } ); console.log(JSON.stringify(runningProcessInstance, null, 2)); } main(); ``` To ignore eventual consistency, set `waitUpToMs` to 0. The operation returns immediately with the current API response. In this scenario, you are more likely to receive an empty set for a search operation or a 404 error for a get operation than to find the process instance you just created. Even though the system already created the process instance and returned its key, you might still receive a result such as: ```json { "items": [] } ``` In some situations, you may want to wait for eventual consistency to settle. The SDK provides you with an ergonomic surface for this. ## Manage eventual consistency If `waitUpToMs` is set to a value greater than `0` (for example, `10_000`), the SDK polls every 500 ms for up to that duration and returns a value as soon as one is available. For search operations, this is when the result set has a length > 0. For get operations, it is when the API returns 200 rather than 404. If no results appear within the specified time, the operation will throw an `EventualConsistencyTimeoutError`. This means eventually consistent operations return either a value or an error: ```typescript // get, 0: Value or 'NOT_FOUND' exception await camunda.getProcessInstance( { processInstanceKey, }, { consistency: { waitForMs: 0 } } ); // get, > 0: Value or EventualConsistencyTimeoutError await camunda.getProcessInstance( { processInstanceKey, }, { consistency: { waitForMs: 1_000 } } ); // search, 0: Value, including empty set await camunda.searchProcessInstances( { processInstanceKey, }, { consistency: { waitForMs: 0 } } ); // search, >0: Expected value or EventualConsistencyTimeoutError await camunda.searchProcessInstances( { processInstanceKey, }, { consistency: { waitForMs: 1_000 } } ); ``` Change the polling interval using the `pollIntervalMs` parameter. For query operations, optionally provide a custom predicate via the `predicate` parameter. The predicate function receives the current result set, and returns a boolean: `false` to continue polling or `true` to accept and propagate the current results. You can use this for advanced client-side filtering or to build a subscription mechanism. Eventually consistent operations return a cancelable promise. Calling `cancel` stops polling and cancels any in-flight network operation, then throws a `CancelSdkError`. --- ## Migrate to the Orchestration Cluster API(Typescript) Migrate an existing Camunda 8 TypeScript application from `@camunda8/sdk` to use the `@camunda8/orchestration-cluster-api`. ## Choose a client option For existing applications using `@camunda8/sdk`, the SDK includes the Orchestration Cluster API client by depending on the `@camunda8/orchestration-cluster-api` package and normalizing configuration to ensure forward compatibility without requiring configuration changes. Use the following guidance to choose between the SDK-bundled client and the focused `@camunda8/orchestration-cluster-api` package. Use the bundled client if: - You depend on APIs not supported by the focused client, such as gRPC. - Your application must use APIs that are not supported in 8.9, such as the earlier Operate query API. - You do not care about application size and do not want to modify your environment configuration. Import the focused client directly alongside the SDK if: - You do not use the gRPC API. - Your application targets Camunda 8.9 or later. - You intend to migrate completely to the Orchestration Cluster API and remove all other API usage in your application. - You do not mind changing or extending your application configuration. ### Key differences Using the bundled client in `@camunda8/sdk`: - Will always pull in as dependencies all the other API clients and their dependencies. - Uses the same configuration variables across the various clients (the SDK normalizes configuration). ```typescript const clientFactory = new Camunda8(); // Get a strongly typed CamundaClient const camunda = clientFactory.getOrchestrationClusterApiClient(); // Get a loosely typed CamundaClient const camundaLoose = clientFactory.getOrchestrationClusterApiClient(); ``` Using the focused client in `@camunda8/orchestration-cluster-api`: - Allows you to reduce the application dependency to the Orchestration Cluster API client only. - Requires you to change the environment configuration (the focused client uses distinct configuration). ```typescript createCamundaClient, createCamundaClientLoose, } from "@camunda8/orchestration-cluster-api"; // Get a strongly-typed CamundaClient const camunda = createCamundaClient(); // Get a loosely-typed CamundaClient const camundaLoose = createCamundaClientLoose(); ``` As a middle ground, you can use the bundled client initially and then switch to the focused client when you are ready to update the configuration. ## Strong vs. loose typing A feature of the Orchestration Cluster API is strong domain types. Request and response fields such as `ProcessDefinitionKey` and `ProcessDefinitionId` are distinct types, leading to better static analysis, enhanced IDE completion, more powerful refactoring, and early detection of runtime bugs. The client implements this using [nominal typing](https://en.wikipedia.org/wiki/Nominal_type_system). You can pass a `ProcessDefinitionId` wherever a string is expected, but cannot pass a free string or a `ProcessDefinitionKey` where a `ProcessDefinitionId` is expected — even though structurally they are all type `string`. ```typescript const camunda = new Camunda8().getOrchestrationClusterApiClient(); async function main() { const deploymentResponse = await camunda.deployResourcesFromFile([ "./process.bpmn", ]); const { processDefinitionKey } = deploymentResponse.processes[0]; // nominal type is ProcessDefinitionKey console.log(typeof processDefinitionKey); // works — structural type is 'string' await camunda.createProcessInstance({ processDefinitionId: processDefinitionKey, }); // error — incompatible types } main(); ``` This approach makes output from the Orchestration Cluster API client compatible with input fields of earlier clients, but the reverse is not true. The earlier clients accept the structural types of the new client, and do not examine the nominal types at all. ```typescript const factory = new Camunda8(); const camunda = new factory.getOrchestrationClusterApiClient(); const camundaLegacy = new factory.getCamundaRestClient(); async function main() { const deploymentResponse = await camunda.deployResourcesFromFile([ "./process.bpmn", ]); const { processDefinitionKey } = deploymentResponse.processes[0]; // nominal type is ProcessDefinitionKey console.log(typeof processDefinitionKey); // structural type is 'string' const { processInstanceKey } = await camundaLegacy.createProcessInstance({ processDefinitionKey, }); // works — structural type is string const runningProcessInstance = await camunda.searchProcessInstances( { filter: { processInstanceKey, // fails — legacy type is `string`, client requires `ProcessInstanceKey` }, }, { consistencyManagement: { waitUpToMs: 10_000 } } ); } main(); ``` ## Integrate the strongly typed client To handle this, you can either manage the type system boundary or erase nominal typing. The new client provides lifters to deal with interoperability. For example: ```typescript const factory = new Camunda8() const camunda = new factory.getOrchestrationClusterApiClient() const legacyCamunda = new factory.getCamundaRestClient() async function main() { const deploymentResponse = await legacyCamunda.deployResourcesFromFile(['./process.bpmn']) const { processDefinitionKey } = deploymentResponse.processes[0] console.log(typeof processDefinitionKey) // structural type is 'string' const {processInstanceKey} = await legacyCamunda.createProcessInstance({ processDefinitionKey }) // works — structural type is string const const runningProcessInstance = await camunda.searchProcessInstances({ filter: { processInstanceKey: processInstanceKey as OrchestrationLifters.ProcessInstanceKey // cast to `ProcessInstanceKey` } }, { consistencyManagement: { waitUpToMs: 10_000 } }) } main() ``` Rather than casting in multiple places, you can do it once via assignment with the `assumeExists` lifter. The lifter will cast the type and also apply runtime constraint validation. ```typescript const camunda = new Camunda8().getOrchestrationClusterApi(); async function cancelRunningProcessInstances(processDefinitionKey: string) { // lift to nominal type `ProcessInstanceKey`. Will throw if passed invalid format. const _processDefinitionKey = OrchestrationLifters.ProcessDefinitionKey.assumeExists( processDefinitionKey ); const processes = await camunda.searchProcessInstances( { filter: { processDefinitionKey: _processDefinitionKey, }, }, { consistencyManagement: { waitUpToMs: 10_000 } } ); for (const process in processes.items) { await camunda.cancelProcess(process.processInstanceKey); } } ``` :::info This approach is the preferred method. This allows you to move the type and constraint validation boundary from the server API to the boundaries of your application. See [the presentation on patterns](https://www.camundacon.com/event-session/camundacon-new-york-2025/patterns-to-use-today-to-make-camunda-8-apps-even-more-reliable?on_demand=true) for more information (refer to the third demo). ::: ## Integrate the loosely-typed client The SDK provides a legacy-compatible loosely-typed client. This erases the domain typing (all fields remain type `string`). This enables you to use the new client without managing type interaction with your existing code. ```typescript const factory = new Camunda8() const camunda = new factory.getOrchestrationClusterApiClientLoose() const legacyCamunda = new factory.getCamundaRestClient() async function main() { const deploymentResponse = await legacyCamunda.deployResourcesFromFile(['./process.bpmn']) const { processDefinitionKey } = deploymentResponse.processes[0] const {processInstanceKey} = await legacyCamunda.createProcessInstance({ processDefinitionKey }) // string const const runningProcessInstance = await camunda.searchProcessInstances({ filter: { processInstanceKey // accepts string type } }, { consistencyManagement: { waitUpToMs: 10_000 } }) } main() ``` You can use loosely-typed in the first instance, then progressively migrate to the strongly typed variant. ## Configuration The Orchestration Cluster API client uses the `ZEEBE_REST_ADDRESS` configuration value to connect to the server. ## Refactor API calls The Orchestration Cluster API has command and query operations. All operations searching data using the v1 component APIs can be refactored to use the equivalent Orchestration Cluster API method. Some method signatures have changed, mostly in the names of fields. Your IDE intellisense will guide you in refactoring to the new signatures. ## Data access and consistency Search and get operations require a second parameter to manage eventual consistency. :::info See the examples in the [TypeScript SDK guide](./camunda8-sdk.md) and the [manage Orchestration Cluster API data consistency](./eventual-consistency.md) overview. ::: ## Manage backpressure The new client has enhanced backpressure management. :::info See [manage backpressure using the TypeScript SDK](./backpressure.md). ::: --- ## Orchestration Cluster API TypeScript client Use the Orchestration Cluster API TypeScript client to connect to Camunda 8, deploy process models, and interact with the Orchestration Cluster REST API. ## About this client The `@camunda8/orchestration-cluster-api` package provides focused support for the Orchestration Cluster REST API. ### When to use this package Use the [`@camunda8/orchestration-cluster-api`](https://www.npmjs.com/package/@camunda8/orchestration-cluster-api) package if: - You are starting a new project. - You do not need the gRPC API. - You are using Camunda 8.9 or later. - You are developing an application for the web browser. ### Differences between the package and the SDK | Difference | Description | | :----------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Configuration keys | Environment variable configuration is different when using the package directly or using it via the SDK.The SDK wraps the Orchestration Cluster API client package and allows you to use the previous configuration keys.When using the package directly, you must use the environment variable configuration keys it requires. Keys that were prefixed with `ZEEBE_` in the SDK are now prefixed with `CAMUNDA_` in the Orchestration Cluster API client package configuration.See the [Configuration Reference](https://github.com/camunda/orchestration-cluster-api-js/blob/main/documentation/CONFIG_REFERENCE.md?plain=1) for a list of configuration parameters. | | ESM | The Orchestration Cluster API client is a dual ESM/CJS package, allowing you to use ESM and tree shake the package as a dependency. | ## Use the Orchestration Cluster API package The following example retrieves the cluster topology: 1. Install the package in your project: ```bash npm i @camunda8/orchestration-cluster-api ``` 2. Import it into your application: ```typescript const camunda = createCamundaClient(); async function main() { const response = await camunda.getTopology(); console.log(JSON.stringify(response, null, 2)); } main(); ``` The `createCamundaClient` function returns a strongly typed client. ## Example project See a [complete example project](https://github.com/camunda-community-hub/c8-sdk-demo) that demonstrates how to use the package. ## API documentation See the [package README](https://camunda.github.io/orchestration-cluster-api-js/) and the [full API documentation](https://camunda.github.io/orchestration-cluster-api-js/classes/index.CamundaClient.html) for more details. --- ## TypeScript SDK(Typescript) ## Get started Choose how you want to get started with Camunda 8 TypeScript development: ## Features and concepts Learn more about Camunda TypeScript SDK features and important concepts: :::info Learn more about the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). ::: --- ## Authentication(Web-modeler-api) All Web Modeler API requests require authentication. To authenticate, generate a [JSON Web Token (JWT)](https://jwt.io/introduction/) depending on your environment and include it in each request. :::note Clients using a valid generated token have access to all resources within an organization, similar to [super-user mode](/components/hub/workspace/modeler/collaboration/collaboration.md#super-user-mode). While there's no project-level access control enforced in the API, access is still dependent on the [CRUD operations assigned](#generate-a-token). ::: ## Generate a token 1. Create client credentials by clicking **Console > Organization > Administration API > Create new credentials**. 2. Add permissions to this client for **Web Modeler API** with the needed CRUD permissions. 3. Once you have created the client, capture the following values required to generate a token: | Name | Environment variable name | Default value | | ------------------------ | -------------------------------- | -------------------------------------------- | | Client ID | `CAMUNDA_CONSOLE_CLIENT_ID` | - | | Client Secret | `CAMUNDA_CONSOLE_CLIENT_SECRET` | - | | Authorization Server URL | `CAMUNDA_OAUTH_URL` | `https://login.cloud.camunda.io/oauth/token` | | Audience | `CAMUNDA_CONSOLE_OAUTH_AUDIENCE` | `api.cloud.camunda.io` | :::caution When client credentials are created, the `Client Secret` is only shown once. Save this `Client Secret` somewhere safe. ::: 4. Execute an authentication request to the token issuer: ```bash curl --request POST ${CAMUNDA_OAUTH_URL} \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'grant_type=client_credentials' \ --data-urlencode "audience=${CAMUNDA_CONSOLE_OAUTH_AUDIENCE}" \ --data-urlencode "client_id=${CAMUNDA_CONSOLE_CLIENT_ID}" \ --data-urlencode "client_secret=${CAMUNDA_CONSOLE_CLIENT_SECRET}" ``` A successful authentication response looks like the following: ```json { "access_token": "", "expires_in": 300, "refresh_expires_in": 0, "token_type": "Bearer", "not-before-policy": 0 } ``` 5. Capture the value of the `access_token` property and store it as your token. 1. [Add an M2M application in Management Identity](/self-managed/components/management-identity/application-user-group-role-management/applications.md). 2. [Add permissions to this application](/self-managed/components/management-identity/application-user-group-role-management/applications.md) for **Web Modeler API** with the needed [CRUD permissions](/self-managed/components/management-identity/access-management/access-management-overview.md#preset-permissions). 3. Capture the `Client ID` and `Client Secret` from the application in Management Identity. 4. [Generate a token](/self-managed/components/management-identity/authentication.md) to access the Web Modeler REST API. Provide the `client_id` and `client_secret` from the values you previously captured in Management Identity. ```shell curl --location --request POST 'http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode "client_id=${CLIENT_ID}" \ --data-urlencode "client_secret=${CLIENT_SECRET}" \ --data-urlencode 'grant_type=client_credentials' ``` A successful authentication response looks like the following: ```json { "access_token": "", "expires_in": 300, "refresh_expires_in": 0, "token_type": "Bearer", "not-before-policy": 0 } ``` 5. Capture the value of the `access_token` property and store it as your token. ## Use a token Include the previously captured token as an authorization header in each request: `Authorization: Bearer `. For example, to send a request to the Web Modeler API's `/info` endpoint: ```shell curl --header "Authorization: Bearer ${TOKEN}" \ https://modeler.cloud.camunda.io/api/v1/info ``` :::tip The `${WEB_MODELER_REST_URL}` variable below represents the URL of the Web Modeler API. You can configure this value in your Self-Managed installation. The default value is `http://localhost:8070`. ::: ```shell curl --header "Authorization: Bearer ${TOKEN}" \ ${WEB_MODELER_REST_URL}/api/v1/info ``` A successful response includes [information about the environment](https://modeler.camunda.io/swagger-ui/index.html#/Info/getInfo). For example: ```json { "version": "v1", "authorizedOrganization": "12345678-ABCD-DCBA-ABCD-123456789ABC", "createPermission": true, "readPermission": true, "updatePermission": true, "deletePermission": false } ``` ## Token expiration Access tokens expire according to the `expires_in` property of a successful authentication response. After this duration, in seconds, you must request a new access token. --- ## Web Modeler API ## About You can use the Web Modeler REST API to programmatically access your Web Modeler data. - Web Modeler provides a REST API at `/api/*`. - Requests and responses are in JSON notation. ## Authentication Clients can only access the Web Modeler REST API by passing a JWT access token in an authorization header `Authorization: Bearer `. Details are covered in the [Authentication](authentication.md) section. ## API reference A detailed API description is available as [OpenAPI](https://www.openapis.org/) specification at [https://modeler.camunda.io/swagger-ui/index.html](https://modeler.camunda.io/swagger-ui/index.html) for SaaS and at [http://localhost:8070/swagger-ui.html](http://localhost:8070/swagger-ui.html) for Self-Managed installations. ## API in Postman Work with this API in our [Postman collection](https://www.postman.com/camundateam/workspace/camunda-8-postman/collection/26079299-0bb668f4-af6a-4ab0-88a3-c78b900125ed?action=share&creator=11465105). ## Usage notes When using Web Modeler API: - You will not receive a warning when deleting a file, a folder, or a project. This is important, because deletion cannot be undone. - You will not receive a warning about breaking call activity links or business rule task links when moving files or folders to another project. Breaking these links is considered harmless. The broken links can be manually removed or restored in Web Modeler. This operation is also reversible - simply move the files or folders back to their original location. ## Rate limiting In SaaS, the Web Modeler API uses rate limiting to control traffic. The limit is 240 requests per minute. Surpassing this limit will result into a `HTTP 429 Too Many Requests` response. On Self-Managed instances no limits are enforced. ## FAQ ### What is the difference between _simplePath_ and _canonicalPath_? In Web Modeler you can have multiple files with the same name, multiple folders with the same name, and even multiple projects with the same name. Internally, duplicate names are disambiguated by unique IDs. The API gives you access to the names, as well as the IDs. For example, when requesting a file you will get the following information: - **simplePath** contains the human-readable path. This path may be ambiguous or may have ambiguous elements (e.g. folders) in it. - **canonicalPath** contains the unique path. It is a list of **PathElementDto** objects which contain the ID and the name of the element. Internally, the IDs are what matters. You can rename files or move files between folders and projects and the ID will stay the same. --- ## Tutorial(Web-modeler-api) In this tutorial, we'll step through examples to highlight the capabilities of the Web Modeler API, such as creating a new project, adding a collaborator to a project, viewing the details of a project, and deleting a project. This tutorial focuses on using the Web Modeler API in a Camunda 8 SaaS environment, but the same principles apply to a Self-Managed environment. ## Prerequisites - Create your first client by navigating to **Console > Organization > Administration API > Create new credentials**. Ensure you determine the scoped access for client credentials. For example, in this tutorial we will create a project, add a collaborator, and delete a project. Ensure you check the box for the Web Modeler scope. :::note Make sure you keep the generated client credentials in a safe place. The **Client secret** will not be shown again. For your convenience, you can also download the client information to your computer. ::: - In this tutorial, we utilize a JavaScript-written [GitHub repository](https://github.com/camunda/camunda-api-tutorials) to write and run requests. Clone this repo before getting started. - Ensure you have [Node.js](https://nodejs.org/en/download) installed as this will be used for methods that can be called by the CLI (outlined later in this guide). Run `npm install` to ensure you have updated dependencies. ## Getting started - A detailed API description can be found [here](https://modeler.cloud.camunda.io/swagger-ui/index.html) via Swagger. With a valid access token, this offers an interactive API experience against your Camunda 8 cluster. - You need authentication to access the API endpoints. Find more information [here](/apis-tools/web-modeler-api/authentication.md). ## Set up authentication If you're interested in how we use a library to handle auth for our code, or to get started, examine the `auth.js` file in the GitHub repository. This file contains a function named `getAccessToken` which executes an OAuth 2.0 protocol to retrieve authentication credentials based on your client ID and client secret. Then, we return the actual token that can be passed as an authorization header in each request. To set up your credentials, create an `.env` file which will be protected by the `.gitignore` file. You will need to add your `MODELER_CLIENT_ID`, `MODELER_CLIENT_SECRET`, `MODELER_AUDIENCE`, which is `modeler.cloud.camunda.io` in a Camunda 8 SaaS environment, and `MODELER_BASE_URL`, which is `https://modeler.camunda.io/api/v1`. These keys will be consumed by the `auth.js` file to execute the OAuth protocol, and should be saved when you generate your client credentials in [prerequisites](#prerequisites). :::tip Can't find your environment variables? When you create new client credentials as a [prerequisite](#prerequisites), your environment variables appear in a pop-up window. Your environment variables may appear as `CAMUNDA_CONSOLE_CLIENT_ID`, `CAMUNDA_CONSOLE_CLIENT_SECRET`, and `CAMUNDA_CONSOLE_OAUTH_AUDIENCE`. ::: Examine the existing `.env.example` file for an example of how your `.env` file should look upon completion. Do not place your credentials in the `.env.example` file, as this example file is not protected by the `.gitignore`. :::note In this tutorial, we will execute arguments to create a project, add a collaborator, and delete a project. You can examine the framework for processing these arguments in the `cli.js` file before getting started. ::: ## Create a new project (POST) and add a collaborator (PUT) First, let's script an API call to create a new project. To do this, take the following steps: 1. In the file named `modeler.js`, outline the authentication and authorization configuration in the first few lines. This will pull in your `.env` variables to obtain an access token before making any API calls: ```javascript const authorizationConfiguration = { clientId: process.env.MODELER_CLIENT_ID, clientSecret: process.env.MODELER_CLIENT_SECRET, audience: process.env.MODELER_AUDIENCE, }; ``` 2. Examine the function `async function createProject([projectName, adminEmail])` below this configuration. This is where you will script out your API call, defining a project name and the project administrator's email. 3. Within the function, you must first generate an access token for this request, so your function should now look like the following: ```javascript async function createProject([projectName, adminEmail]) { const accessToken = await getAccessToken(authorizationConfiguration); } ``` 4. Using your generated client credentials from [prerequisites](#prerequisites), capture your Web Modeler base URL beneath your call for an access token by defining `modelerApiUrl`: ```javascript const modelerApiUrl = process.env.MODELER_BASE_URL; ``` 5. On the next line, script the API endpoint to create your project: ```javascript const projectUrl = `${modelerApiUrl}/projects`; ``` 6. Configure your POST request to the appropriate endpoint, including an authorization header based on the previously acquired `accessToken`. You will also add a body to outline information about the new project: ```javascript const projectOptions = { method: "POST", url: projectUrl, headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, }, data: { name: projectName, }, }; ``` 7. Call the add project endpoint and capture the data for the new project: ```javascript try { const response = await axios(projectOptions); const newProject = response.data; console.log( `Project added! Name: ${newProject.name}. ID: ${newProject.id}.` ); ``` 8. Next, we'll add a collaborator to the project you just created. After calling the add project endpoint, add an endpoint to add a collaborator to the project: ```javascript const collaboratorUrl = `${modelerApiUrl}/collaborators`; ``` 9. Configure the API call, including a body with information about the project and the new collaborator: ```javascript const collaboratorOptions = { method: "PUT", url: collaboratorUrl, headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}` }, data: { email: adminEmail, projectId: newProject.id, role: "project_admin" } ``` 10. Call the add collaborator endpoint and process the results: ```javascript const collaboratorResponse = await axios(collaboratorOptions); if (collaboratorResponse.status === 204) { console.log(`Collaborator added! Email: ${adminEmail}.`); } else { console.error("Unable to add collaborator!"); } } catch (error) { // Emit an error from the server. console.error(error.message); } ``` 11. In your terminal, run `npm run cli modeler create` to create your project. :::note This `create` command is connected to the `createProject` function at the bottom of the `modeler.js` file, and executed by the `cli.js` file. While we create a project in this tutorial, you may add additional arguments depending on the API calls you would like to make. ::: ## View project details (GET) To view project details, take the following steps: 1. Outline your function, similar to the steps above: ```javascript async function viewProject([projectId]) { const accessToken = await getAccessToken(authorizationConfiguration); const modelerApiUrl = process.env.MODELER_BASE_URL; const url = `${modelerApiUrl}/projects/${projectId}`; } ``` 2. Configure the API call using the GET method: ```javascript const options = { method: "GET", url, headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, }, }; ``` 3. Process the results from the API call. For example: ```javascript try { const response = await axios(options); const project = response.data; console.log("Project:", project); } catch (error) { console.error(error.message); } ``` 4. In your terminal, run `npm run cli modeler view `, where `` is the ID output by the command to create a project. ## Delete a project To delete a project, take the following steps: 1. Outline your function, similar to the steps above: ```javascript async function deleteProject([projectId]) { const accessToken = await getAccessToken(authorizationConfiguration); const modelerApiUrl = process.env.MODELER_BASE_URL; const url = `${modelerApiUrl}/projects/${projectId}`; ``` 2. Configure the API call using the DELETE method: ```javascript const options = { method: "DELETE", url, headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, }, }; ``` 3. Process the results from the API call. For example: ```javascript try { const response = await axios(options); if (response.status === 204) { console.log(`Project ${projectId} was deleted!`); } else { console.error("Unable to delete project!"); } } catch (error) { console.error(error.message); } ``` 4. In your terminal, run `npm run cli modeler delete `, where `` is the ID output by the command to create a project. ## If you get stuck Having trouble configuring your API calls or want to examine an example of the completed tutorial? Navigate to the `completed` folder in the [GitHub repository](https://github.com/camunda/camunda-api-tutorials/tree/main/completed), where you can view an example `modeler.js` file. ## Next steps You can script several additional API calls as outlined in the [Web Modeler API reference material](/apis-tools/web-modeler-api/index.md). --- ## APIs & tools Camunda 8 APIs and official clients and SDKs. Use Camunda 8 APIs and clients to build, automate, and monitor your applications. Use the official Camunda clients and SDKs (Java, Spring, and Node.js) to simplify API usage and speed up development. Get started with the Camunda Java Client :::info Upgrade to Camunda 8.9 - Existing customer? Upgrade your APIs & tools to 8.9 using the [APIs & tools migration guide](/apis-tools/migration-manuals/migrate-to-89.md). - See [what's new in Camunda 8.9](/reference/announcements-release-notes/890/whats-new-in-89.md), [release announcements](/reference/announcements-release-notes/890/890-announcements.md), and [release notes](/reference/announcements-release-notes/890/890-release-notes.md). ::: ## APIs Use the following APIs for Camunda 8 integration and automation: ## API clients Camunda provides the following official clients to simplify API usage and speed up development: :::note community clients In addition to the core Camunda-maintained clients, there are a number of [community-maintained component clients](/apis-tools/community-clients/index.md). ::: ## Client and API compatibility Camunda clients and SDKs are **forward-compatible** with the Orchestration Cluster, meaning you can upgrade the cluster first and clients after. The Orchestration Cluster REST API is backward-compatible, ensuring no breaking changes to existing endpoints across versions. [Client and API compatibility guarantees](/reference/public-api.md#client-and-api-compatibility) ## Testing Use Camunda Process Test to test your process definitions and automations with a dedicated testing framework. [Camunda Process Test](/apis-tools/testing/getting-started.md) ## Upgrade to Camunda 8.9 If you are migrating from Camunda 7 or from v1 component REST APIs, see the migration guide for guidance. [Camunda 8.9 APIs & tools migration guide](/apis-tools/migration-manuals/migrate-to-89.md) --- ## Deprecated RPCs The following RPCs are exposed by the gateway service, but have been deprecated. ## `DeployProcess` RPC :::note Deprecated since 8, replaced by [DeployResource RPC](#deployresource-rpc). ::: :::note When multi-tenancy is enabled, processes are always deployed to the `` tenant. ::: Deploys one or more processes to Zeebe. Note that this is an atomic call, i.e. either all processes are deployed, or none of them are. ### Input: `DeployProcessRequest` ```protobuf message DeployProcessRequest { // List of process resources to deploy repeated ProcessRequestObject processes = 1; } message ProcessRequestObject { enum ResourceType { // FILE type means the gateway will try to detect the resource type // using the file extension of the name field FILE = 0; BPMN = 1; // extension 'bpmn' YAML = 2 [deprecated = true]; // extension 'yaml'; removed as of release 1.0 } // the resource basename, e.g. myProcess.bpmn string name = 1; // the resource type; if set to BPMN or YAML then the file extension // is ignored // As of release 1.0, YAML support was removed and BPMN is the only supported resource type. // The field was kept to not break clients. ResourceType type = 2 [deprecated = true]; // the process definition as a UTF8-encoded string bytes definition = 3; } ``` ### Output: `DeployProcessResponse` ```protobuf message DeployProcessResponse { // the unique key identifying the deployment int64 key = 1; // a list of deployed processes repeated ProcessMetadata processes = 2; } message ProcessMetadata { // the bpmn process ID, as parsed during deployment; together with the version forms a // unique identifier for a specific process definition string bpmnProcessId = 1; // the assigned process version int32 version = 2; // the assigned key, which acts as a unique identifier for this process int64 processKey = 3; // the resource name (see: ProcessRequestObject.name) from which this process was // parsed string resourceName = 4; } ``` ### Errors #### GRPC_STATUS_INVALID_ARGUMENT Returned if: - No resources given. - At least one resource is invalid. A resource is considered invalid if: - It is not a BPMN or YAML file (currently detected through the file extension). - The resource data is not deserializable (e.g. detected as BPMN, but it's broken XML). - The process is invalid (e.g. an event-based gateway has an outgoing sequence flow to a task.) --- ## Zeebe API RPCs The Zeebe client gRPC API is exposed through a single gateway service. The current version of the protocol buffer file can be found in the [Zeebe repository](https://github.com/camunda/camunda/blob/main/zeebe/gateway-protocol/src/main/proto/gateway.proto). ## Default service config Along with the gateway protocol definition, the gateway service also bundles a [default service configuration file](https://github.com/camunda/camunda/blob/main/zeebe/gateway-protocol-impl/src/main/resources/gateway-service-config.json). This file can be used as is, or as a template to create your own, and defines default retry strategies on a per-RPC basis: when to retry (based on error code), how often, how soon, etc. This file is also loaded by the [Camunda Java client](../java-client/getting-started.md) if `useDefaultRetryPolicy` is set to true. :::note Read more about [service configuration files](https://github.com/grpc/grpc/blob/master/doc/service_config.md). These files are especially useful when using the Camunda protocol in languages without, or with less feature-rich clients and SDKs. ::: Usage of this file largely depends on the gRPC bindings for your language of choice. For example, when using Java, you would programmatically configure your client using: ```java final ObjectMapper objectMapper = new ObjectMapper(); final File configFile = new File("gateway-service-config.json"); final Map serviceConfig = objectMapper.readValue( configFile, new TypeReference>() {}); final ManagedChannelBuilder channelBuilder = ManagedChannelBuilder.forAddress("localhost", 26500); channelBuilder.defaultServiceConfig(serviceConfig); channelBuilder.enableRetry(); ``` ## `ActivateJobs` RPC Iterates through all known partitions round-robin, activates up to the requested maximum, and streams them back to the client as they are activated. ### Input: `ActivateJobsRequest` ```protobuf message ActivateJobsRequest { // the job type, as defined in the BPMN process (e.g. ) string type = 1; // the name of the worker activating the jobs, mostly used for logging purposes string worker = 2; // a job returned after this call will not be activated by another call until the // timeout (in ms) has been reached int64 timeout = 3; // the maximum jobs to activate by this request int32 maxJobsToActivate = 4; // a list of variables to fetch as the job variables; if empty, all visible variables at // the time of activation for the scope of the job will be returned repeated string fetchVariable = 5; // The request will be completed when at least one job is activated or after the requestTimeout (in ms). // if the requestTimeout = 0, a default timeout is used. // if the requestTimeout < 0, long polling is disabled and the request is completed immediately, even when no job is activated. int64 requestTimeout = 6; // a list of IDs of tenants for which to activate jobs repeated string tenantIds = 7; } ``` If `requestTimeout` is set to `0`, the effective timeout depends on whether long polling is enabled: - If long polling is enabled, the gateway uses its configured long-polling timeout (`camunda.api.long-polling.timeout` / `zeebe.gateway.longPolling.timeout`, default 10,000 ms). - If long polling is disabled, the request falls back to a client-side timeout. For gRPC clients, this currently defaults to 10,000 ms. If `requestTimeout` is set to a value less than `0`, long polling is disabled and the request completes immediately, even when no job is activated. ### Output: `ActivateJobsResponse` ```protobuf message ActivateJobsResponse { // list of activated jobs repeated ActivatedJob jobs = 1; } message ActivatedJob { // Describes the kind of job. enum JobKind { BPMN_ELEMENT = 0; EXECUTION_LISTENER = 1; TASK_LISTENER = 2; } // Describes the listener event type of the job. enum ListenerEventType { ASSIGNING = 0; CANCELING = 1; COMPLETING = 2; CREATING = 3; END = 4; START = 5; UNSPECIFIED = 6; UPDATING = 7; } // the key, a unique identifier for the job int64 key = 1; // the type of the job (should match what was requested) string type = 2; // the job's process instance key int64 processInstanceKey = 3; // the bpmn process ID of the job process definition string bpmnProcessId = 4; // the version of the job process definition int32 processDefinitionVersion = 5; // the key of the job process definition int64 processDefinitionKey = 6; // the associated task element ID string elementId = 7; // the unique key identifying the associated task, unique within the scope of the // process instance int64 elementInstanceKey = 8; // a set of custom headers defined during modelling; returned as a serialized // JSON document string customHeaders = 9; // the name of the worker which activated this job string worker = 10; // the amount of retries left to this job (should always be positive) int32 retries = 11; // when the job can be activated again, sent as a UNIX epoch timestamp int64 deadline = 12; // JSON document, computed at activation time, consisting of all visible variables to // the task scope string variables = 13; // the ID of the tenant that owns the job string tenantId = 14; // the kind of the job. JobKind kind = 15; // the listener event type of the job. ListenerEventType listenerEventType = 16; } ``` ### Errors #### GRPC_STATUS_INVALID_ARGUMENT Returned if: - Type is blank (empty string, null) - Worker is blank (empty string, null) - Timeout less than 1 (ms) - maxJobsToActivate is less than 1 - If multi-tenancy is enabled, and `tenantIds` is empty (empty list) - If multi-tenancy is enabled, and an invalid tenant ID is provided. A tenant ID is considered invalid if: - The tenant ID is blank (empty string, null) - The tenant ID is longer than 31 characters - The tenant ID contains anything other than alphanumeric characters, dot (.), dash (-), or underscore (\_) - If multi-tenancy is disabled, and `tenantIds` is not empty (empty list), or has an ID other than `` #### GRPC_STATUS_PERMISSION_DENIED - If multi-tenancy is enabled, and an unauthorized tenant ID is provided ## `BroadcastSignal` RPC Broadcasts a [signal](/components/concepts/signals.md). ### Input: `BroadcastSignalRequest` ```protobuf message BroadcastSignalRequest { // The name of the signal string signalName = 1; // the signal variables as a JSON document; to be valid, the root of the document must be an // object, e.g. { "a": "foo" }. [ "foo" ] would not be valid. string variables = 2; // the ID of the tenant that owns the signal. string tenantId = 3; } ``` ### Output: `BroadcastSignalResponse` ```protobuf message BroadcastSignalResponse { // the unique ID of the signal that was broadcasted. int64 key = 1; // the tenant ID of the signal that was broadcasted. string tenantId = 2; } ``` ### Errors #### GRPC_STATUS_NOT_FOUND - If multi-tenancy is enabled, and `tenantId` is blank (empty string, null) - If multi-tenancy is enabled, and an invalid tenant ID is provided. A tenant ID is considered invalid if: - The tenant ID is blank (empty string, null) - The tenant ID is longer than 31 characters - The tenant ID contains anything other than alphanumeric characters, dot (.), dash (-), or underscore (\_) - If multi-tenancy is disabled, and `tenantId` is not blank (empty string, null), or has an ID other than `` #### GRPC_STATUS_PERMISSION_DENIED - If multi-tenancy is enabled, and an unauthorized tenant ID is provided ## `CancelProcessInstance` RPC Cancels a running process instance. ### Input: `CancelProcessInstanceRequest` ```protobuf message CancelProcessInstanceRequest { // the process instance key (as, for example, obtained from // CreateProcessInstanceResponse) int64 processInstanceKey = 1; } ``` ### Output: `CancelProcessInstanceResponse` ```protobuf message CancelProcessInstanceResponse { } ``` ### Errors #### GRPC_STATUS_NOT_FOUND Returned if: - No process instance exists with the given key. Note that since process instances are removed once they are finished, it could mean the instance did exist at some point. - No process instance exists with the given key for the tenants the user is authorized to work with. ## `CompleteJob` RPC Completes a job with the given payload, which allows completing the associated service task. ### Input: `CompleteJobRequest` ```protobuf message CompleteJobRequest { // the unique job identifier, as obtained from ActivateJobsResponse int64 jobKey = 1; // a JSON document representing the variables in the current task scope string variables = 2; // The result of the completed job as determined by the worker. // This functionality is currently supported only by user task listeners optional JobResult result = 3; } message JobResult{ // Indicates whether the worker denies the work, or explicitly doesn't approve it. // For example, a user task listener can deny the completion of a user task by setting this flag to true. // In this example, the completion of a task is represented by a job that the worker can complete as denied. // As a result, the completion request is rejected and the task remains active. // Defaults to false. // Only applicable for user task listener jobs. optional bool denied = 1; // Attributes that were corrected by the worker. // The following attributes can be corrected, additional attributes will be ignored: // * `assignee` - clear by providing an empty string // * `dueDate` - clear by providing an empty string // * `followUpDate` - clear by providing an empty string // * `candidateGroups` - clear by providing an empty list // * `candidateUsers` - clear by providing an empty list // * `priority` - minimum 0, maximum 100, default 50 // Omitting any of the attributes will preserve the persisted attribute's value. // Only applicable for user task listener jobs. optional JobResultCorrections corrections = 2; // The reason provided by the user task listener for denying the work. optional string deniedReason = 3; // Identifies the type of job result. Must be either "userTask" or "adHocSubprocess". // Defaults to "userTask" if not explicitly set. optional string type = 4; // The list of elements that should be activated after the job is completed. // Only applicable for ad-hoc subprocesses. repeated JobResultActivateElement activateElements = 5; } message JobResultCorrections { // The assignee of the task. optional string assignee = 1; // The due date of the task. optional string dueDate = 2; // The follow-up date of the task. optional string followUpDate = 3; // The list of candidate users of the task. optional StringList candidateUsers = 4; // The list of candidate groups of the task. optional StringList candidateGroups = 5; // The priority of the task. optional int32 priority = 6; } message JobResultActivateElement { // The id of the element to activate string elementId = 1; // JSON document of variables that will be created on the scope of the activated element. // It must be a JSON object, as variables will be mapped in a key-value fashion. // e.g. { "a": 1, "b": 2 } will create two variables, named "a" and // "b" respectively, with their associated values. [{ "a": 1, "b": 2 }] would not be a // valid argument, as the root of the JSON document is an array and not an object. string variables = 2; } message StringList { // Wrapper around a list of string values. repeated string values = 1; } ``` ### Output: `CompleteJobResponse` ```protobuf message CompleteJobResponse { } ``` ### Errors #### GRPC_STATUS_NOT_FOUND Returned if: - No job exists with the given job key. Note that since jobs are removed once completed, it could be that this job did exist at some point. - No job exists with the given job key for the tenants the user is authorized to work with. #### GRPC_STATUS_FAILED_PRECONDITION Returned if: - The job was marked as failed. In that case, the related [incident](/components/concepts/incidents.md) must be resolved before the job can be activated again and completed. ## `CreateProcessInstance` RPC Creates and starts an instance of the specified process. The process definition to use to create the instance can be specified either using its unique key (as returned by DeployProcess), or using the BPMN process ID and a version. Pass -1 as the version to use the latest deployed version. :::note Only processes with none start events can be started through this command. ::: :::note Start and runtime instructions have the same [limitations as process instance modification](/components/concepts/process-instance-modification.md#limitations), e.g., it is not possible to start at a sequence flow or terminate a process instance when a sequence flow completes. ::: ### Input: `CreateProcessInstanceRequest` ```protobuf message CreateProcessInstanceRequest { // the unique key identifying the process definition (e.g. returned from a process // in the DeployProcessResponse message) int64 processDefinitionKey = 1; // the BPMN process ID of the process definition string bpmnProcessId = 2; // the version of the process; set to -1 to use the latest version int32 version = 3; // JSON document that will instantiate the variables for the root variable scope of the // process instance; it must be a JSON object, as variables will be mapped in a // key-value fashion. e.g. { "a": 1, "b": 2 } will create two variables, named "a" and // "b" respectively, with their associated values. [{ "a": 1, "b": 2 }] would not be a // valid argument, as the root of the JSON document is an array and not an object. string variables = 4; // List of start instructions. If empty (default) the process instance // will start at the start event. If non-empty the process instance will apply start // instructions after it has been created repeated ProcessInstanceCreationStartInstruction startInstructions = 5; // the tenant id of the process definition string tenantId = 6; // a reference key chosen by the user and will be part of all records resulted from this operation optional uint64 operationReference = 7; // a list of runtime instruction that can modify the behavior of the process // instance during its execution // if empty (default), the process instance will be executed normally repeated ProcessInstanceCreationRuntimeInstruction runtimeInstructions = 8; } message ProcessInstanceCreationStartInstruction { // future extensions might include // - different types of start instructions // - ability to set local variables for different flow scopes // for now, however, the start instruction is implicitly a // "startBeforeElement" instruction // element ID string elementId = 1; } message ProcessInstanceCreationRuntimeInstruction { oneof instruction { TerminateProcessInstanceInstruction terminate = 1; } } message TerminateProcessInstanceInstruction { // the ID of the process element after which the process instance should be // terminated string afterElementId = 1; } ``` ### Output: `CreateProcessInstanceResponse` ```protobuf message CreateProcessInstanceResponse { // the key of the process definition which was used to create the process instance int64 processDefinitionKey = 1; // the BPMN process ID of the process definition which was used to create the process // instance string bpmnProcessId = 2; // the version of the process definition which was used to create the process instance int32 version = 3; // the unique identifier of the created process instance; to be used wherever a request // needs a process instance key (e.g. CancelProcessInstanceRequest) int64 processInstanceKey = 4; // the tenant identifier of the created process instance string tenantId = 5; } ``` ## `CreateProcessInstanceWithResult` RPC Similar to `CreateProcessInstance` RPC, creates and starts an instance of the specified process. Unlike `CreateProcessInstance` RPC, the response is returned when the process is completed. :::note Only processes with none start events can be started through this command. ::: :::note Start instructions have the same [limitations as process instance modification](/components/concepts/process-instance-modification.md#limitations), e.g., it is not possible to start at a sequence flow. ::: ### Input: `CreateProcessInstanceWithResultRequest` ```protobuf message CreateProcessInstanceWithResultRequest { CreateProcessInstanceRequest request = 1; // timeout (in ms). the request will be closed if the process is not completed // before the requestTimeout. // if requestTimeout = 0, uses the generic requestTimeout configured in the gateway. int64 requestTimeout = 2; // list of names of variables to be included in `CreateProcessInstanceWithResultResponse.variables` // if empty, all visible variables in the root scope will be returned. repeated string fetchVariables = 3; } ``` ### Output: `CreateProcessInstanceWithResultResponse` ```protobuf message CreateProcessInstanceWithResultResponse { // the key of the process definition which was used to create the process instance int64 processDefinitionKey = 1; // the BPMN process ID of the process definition which was used to create the process // instance string bpmnProcessId = 2; // the version of the process definition which was used to create the process instance int32 version = 3; // the unique identifier of the created process instance; to be used wherever a request // needs a process instance key (e.g. CancelProcessInstanceRequest) int64 processInstanceKey = 4; // JSON document // consists of visible variables in the root scope string variables = 5; // the tenant identifier of the process definition string tenantId = 6; } ``` ### Errors #### GRPC_STATUS_NOT_FOUND Returned if: - No process with the given key exists (if processKey was given). - No process with the given process ID exists (if bpmnProcessId was given but version was -1). - No process with the given process ID and version exists (if both bpmnProcessId and version were given). #### GRPC_STATUS_FAILED_PRECONDITION Returned if: - The process definition does not contain a none start event; only processes with none start event can be started manually. #### GRPC_STATUS_INVALID_ARGUMENT Returned if: - The given variables argument is not a valid JSON document; it is expected to be a valid JSON document where the root node is an object. - The given `businessId` exceeds the maximum length of 256 characters. - If multi-tenancy is enabled, and `tenantId` is blank (empty string, null) - If multi-tenancy is enabled, and an invalid tenant ID is provided. A tenant ID is considered invalid if: - The tenant ID is blank (empty string, null) - The tenant ID is longer than 31 characters - The tenant ID contains anything other than alphanumeric characters, dot (.), dash (-), or underscore (\_) - If multi-tenancy is disabled, and `tenantId` is not blank (empty string, null), or has an ID other than `` #### GRPC_STATUS_PERMISSION_DENIED - If multi-tenancy is enabled, and an unauthorized tenant ID is provided ## `DeleteResource` RPC ### Input `DeleteResourceRequest` ```protobuf message DeleteResourceRequest { // The key of the resource that should be deleted. This can either be the key // of a process definition, the key of a decision requirements definition or the key of a form. int64 resourceKey = 1; } ``` ### Output: `DeleteResourceResponse` ```protobuf message DeleteResourceResponse { } ``` ### Errors #### GRPC_STATUS_NOT_FOUND Returned if: - No resource exists with the given key. - No resource was found with the given key for the tenants the user is authorized to work with. #### GRPC_STATUS_FAILED_PRECONDITION Returned if: - The deleted resource is a process definition, and there are running instances for this process definition. ## `DeployResource` RPC Deploys one or more resources (e.g. processes, decision models or forms) to Zeebe. Note that this is an atomic call, i.e. either all resources are deployed, or none of them are. ### Input: `DeployResourceRequest` ```protobuf message DeployResourceRequest { // list of resources to deploy repeated Resource resources = 1; // the tenant id of the resources to deploy string tenantId = 2; } message Resource { // the resource name, e.g. myProcess.bpmn or myDecision.dmn string name = 1; // the file content as a UTF8-encoded string bytes content = 2; } ``` ### Output: `DeployResourceResponse` ```protobuf message DeployResourceResponse { // the unique key identifying the deployment int64 key = 1; // a list of deployed resources, e.g. processes repeated Deployment deployments = 2; // the tenant id of the deployed resources string tenantId = 3; } message Deployment { // each deployment has only one metadata oneof Metadata { // metadata of a deployed process ProcessMetadata process = 1; // metadata of a deployed decision DecisionMetadata decision = 2; // metadata of a deployed decision requirements DecisionRequirementsMetadata decisionRequirements = 3; // metadata of a deployed form FormMetadata form = 4; } } message ProcessMetadata { // the bpmn process ID, as parsed during deployment; together with the version forms a // unique identifier for a specific process definition string bpmnProcessId = 1; // the assigned process version int32 version = 2; // the assigned key, which acts as a unique identifier for this process int64 processDefinitionKey = 3; // the resource name (see: ProcessRequestObject.name) from which this process was // parsed string resourceName = 4; // the tenant id of the deployed process string tenantId = 5; } message DecisionMetadata { // the dmn decision ID, as parsed during deployment; together with the // versions forms a unique identifier for a specific decision string dmnDecisionId = 1; // the dmn name of the decision, as parsed during deployment string dmnDecisionName = 2; // the assigned decision version int32 version = 3; // the assigned decision key, which acts as a unique identifier for this // decision int64 decisionKey = 4; // the dmn ID of the decision requirements graph that this decision is part // of, as parsed during deployment string dmnDecisionRequirementsId = 5; // the assigned key of the decision requirements graph that this decision is // part of int64 decisionRequirementsKey = 6; // the tenant id of the deployed decision string tenantId = 7; } message DecisionRequirementsMetadata { // the dmn decision requirements ID, as parsed during deployment; together // with the versions forms a unique identifier for a specific decision string dmnDecisionRequirementsId = 1; // the dmn name of the decision requirements, as parsed during deployment string dmnDecisionRequirementsName = 2; // the assigned decision requirements version int32 version = 3; // the assigned decision requirements key, which acts as a unique identifier // for this decision requirements int64 decisionRequirementsKey = 4; // the resource name (see: Resource.name) from which this decision // requirements was parsed string resourceName = 5; // the tenant id of the deployed decision requirements string tenantId = 6; } message FormMetadata { // the form ID, as parsed during deployment; together with the // versions forms a unique identifier for a specific form string formId = 1; // the assigned form version int32 version = 2; // the assigned key, which acts as a unique identifier for this form int64 formKey = 3; // the resource name string resourceName = 4; // the tenant id of the deployed form string tenantId = 5; } ``` ### Errors #### GRPC_STATUS_INVALID_ARGUMENT Returned if: - No resources given. - At least one resource is invalid. A resource is considered invalid if: - The resource type is not supported (e.g. supported resources include BPMN and DMN files) - The content is not deserializable (e.g. detected as BPMN, but it's broken XML) - The content is invalid (e.g. an event-based gateway has an outgoing sequence flow to a task) - If multi-tenancy is enabled, and `tenantId` is blank (empty string, null) - If multi-tenancy is enabled, and an invalid tenant ID is provided. A tenant ID is considered invalid if: - The tenant ID is blank (empty string, null) - The tenant ID is longer than 31 characters - The tenant ID contains anything other than alphanumeric characters, dot (.), dash (-), or underscore (\_) - If multi-tenancy is disabled, and `tenantId` is not blank (empty string, null), or has an ID other than `` #### GRPC_STATUS_PERMISSION_DENIED - If multi-tenancy is enabled, and an unauthorized tenant ID is provided ## `EvaluateConditional` RPC Evaluates root-level conditional start events for process definitions. If the evaluation is successful, it will return the keys of all created process instances, along with their associated process definition key. Multiple root-level conditional start events of the same process definition can trigger if their conditions evaluate to true. ### Input: `EvaluateConditionalRequest` ```protobuf message EvaluateConditionalRequest { // Used to evaluate root-level conditional start events for a tenant with the given ID. // This will only evaluate root-level conditional start events of process definitions which belong to the tenant. string tenantId = 1; // Used to evaluate root-level conditional start events of the process definition with the given key. optional int64 processDefinitionKey = 2; // Serialized JSON object representing the variables to use for evaluation of the conditions and to pass to the process instances that have been triggered. string variables = 3; } ``` ### Output: `EvaluateConditionalResponse` ```protobuf message EvaluateConditionalResponse { // List of process instances created. If no root-level conditional start events evaluated to true, the list will be empty. repeated ProcessInstanceReference processInstances = 1; // The unique key of the conditional evaluation operation. int64 conditionalEvaluationKey = 2; // The tenant ID of the conditional evaluation operation. string tenantId = 3; } message ProcessInstanceReference { // The key of the process definition. int64 processDefinitionKey = 1; // The key of the created process instance. int64 processInstanceKey = 2; } ``` ### Errors #### GRPC_STATUS_INVALID_ARGUMENT Returned if: - The provided data is not valid #### GRPC_STATUS_NOT_FOUND Returned if: - The process definition was not found for the given processDefinitionKey #### GRPC_STATUS_PERMISSION_DENIED - The client is not authorized to start process instances for the specified process definition - If a processDefinitionKey is not provided, this indicates that the client is not authorized to start process instances for at least one of the matched process definitions ## `EvaluateDecision` RPC Evaluates a decision. You specify the decision to evaluate either by using its unique KEY (as returned by DeployResource), or using the decision ID. When using the decision ID, the latest deployed version of the decision is used. :::note When you specify both the decision ID and KEY, the ID is used to find the decision to be evaluated. ::: ### Input: `EvaluateDecisionRequest` ```protobuf message EvaluateDecisionRequest { // the unique key identifying the decision to be evaluated (e.g. returned // from a decision in the DeployResourceResponse message) int64 decisionKey = 1; // the ID of the decision to be evaluated string decisionId = 2; // JSON document that will instantiate the variables for the decision to be // evaluated; it must be a JSON object, as variables will be mapped in a // key-value fashion, e.g. { "a": 1, "b": 2 } will create two variables, // named "a" and "b" respectively, with their associated values. // [{ "a": 1, "b": 2 }] would not be a valid argument, as the root of the // JSON document is an array and not an object. string variables = 3; // the tenant identifier of the decision string tenantId = 4; } ``` ### Output: `EvaluateDecisionResponse` ```protobuf message EvaluateDecisionResponse { // the unique key identifying the decision which was evaluated (e.g. returned // from a decision in the DeployResourceResponse message) int64 decisionKey = 1; // the ID of the decision which was evaluated string decisionId = 2; // the name of the decision which was evaluated string decisionName = 3; // the version of the decision which was evaluated int32 decisionVersion = 4; // the ID of the decision requirements graph that the decision which was // evaluated is part of. string decisionRequirementsId = 5; // the unique key identifying the decision requirements graph that the // decision which was evaluated is part of. int64 decisionRequirementsKey = 6; // JSON document that will instantiate the result of the decision which was // evaluated; it will be a JSON object, as the result output will be mapped // in a key-value fashion, e.g. { "a": 1 }. string decisionOutput = 7; // a list of decisions that were evaluated within the requested decision evaluation repeated EvaluatedDecision evaluatedDecisions = 8; // an optional string indicating the ID of the decision which // failed during evaluation string failedDecisionId = 9; // an optional message describing why the decision which was evaluated failed string failureMessage = 10; // the tenant identifier of the evaluated decision string tenantId = 11; // the unique key identifying this decision evaluation int64 decisionInstanceKey = 12; } message EvaluatedDecision { // the unique key identifying the decision which was evaluated (e.g. returned // from a decision in the DeployResourceResponse message) int64 decisionKey = 1; // the ID of the decision which was evaluated string decisionId = 2; // the name of the decision which was evaluated string decisionName = 3; // the version of the decision which was evaluated int32 decisionVersion = 4; // the type of the decision which was evaluated string decisionType = 5; // JSON document that will instantiate the result of the decision which was // evaluated; it will be a JSON object, as the result output will be mapped // in a key-value fashion, e.g. { "a": 1 }. string decisionOutput = 6; // the decision rules that matched within this decision evaluation repeated MatchedDecisionRule matchedRules = 7; // the decision inputs that were evaluated within this decision evaluation repeated EvaluatedDecisionInput evaluatedInputs = 8; // the tenant identifier of the evaluated decision string tenantId = 9; } message EvaluatedDecisionInput { // the id of the evaluated decision input string inputId = 1; // the name of the evaluated decision input string inputName = 2; // the value of the evaluated decision input string inputValue = 3; } message EvaluatedDecisionOutput { // the ID of the evaluated decision output string outputId = 1; // the name of the evaluated decision output string outputName = 2; // the value of the evaluated decision output string outputValue = 3; } message MatchedDecisionRule { // the ID of the matched rule string ruleId = 1; // the index of the matched rule int32 ruleIndex = 2; // the evaluated decision outputs repeated EvaluatedDecisionOutput evaluatedOutputs = 3; } ``` ### Errors #### GRPC_STATUS_INVALID_ARGUMENT Returned if: - No decision with the given key exists (if decisionKey was given). - No decision with the given decision ID exists (if decisionId was given). - Both decision ID and decision KEY were provided, or are missing. - If multi-tenancy is enabled, and `tenantId` is blank (empty string, null) - If multi-tenancy is enabled, and an invalid tenant ID is provided. A tenant ID is considered invalid if: - The tenant ID is blank (empty string, null) - The tenant ID is longer than 31 characters - The tenant ID contains anything other than alphanumeric characters, dot (.), dash (-), or underscore (\_) - If multi-tenancy is disabled, and `tenantId` is not blank (empty string, null), or has an ID other than `` #### GRPC_STATUS_PERMISSION_DENIED - If multi-tenancy is enabled, and an unauthorized tenant ID is provided ## `FailJob` RPC Marks the job as failed. If the retries argument is positive and no retry back off is set, the job is immediately activatable again. If the retry back off is positive the job becomes activatable once the back off timeout has passed. If the retries argument is zero or negative, an incident is raised, tagged with the given errorMessage, and the job is not activatable until the incident is resolved. If the variables argument is set, the variables are merged into the process at the local scope of the job's associated task. ### Input: `FailJobRequest` ```protobuf message FailJobRequest { // the unique job identifier, as obtained when activating the job int64 jobKey = 1; // the amount of retries the job should have left int32 retries = 2; // an optional message describing why the job failed // this is particularly useful if a job runs out of retries and an incident is raised, // as it this message can help explain why an incident was raised string errorMessage = 3; // the backoff timeout (in ms) for the next retry int64 retryBackOff = 4; // JSON document that will instantiate the variables at the local scope of the // job's associated task; it must be a JSON object, as variables will be mapped in a // key-value fashion. e.g. { "a": 1, "b": 2 } will create two variables, named "a" and // "b" respectively, with their associated values. [{ "a": 1, "b": 2 }] would not be a // valid argument, as the root of the JSON document is an array and not an object. string variables = 5; } ``` ### Output: `FailJobResponse` ```protobuf message FailJobResponse { } ``` ### Errors #### GRPC_STATUS_NOT_FOUND Returned if: - No job was found with the given key. - No job was found with the given key for the tenants the user is authorized to work with. #### GRPC_STATUS_FAILED_PRECONDITION Returned if: - The job was not activated. - The job is already in a failed state, i.e. ran out of retries. ## `MigrateProcessInstance` RPC Migrates a process instance to a new process definition. The command can contain multiple mapping instructions to define mapping between the active process instance's elements and target process definition elements. Use the command to upgrade a process instance to a new version of a process or to a different process definition. E.g. keep your running instances up-to-date with the latest process improvements. ### Input: `MigrateProcessInstanceRequest` ```protobuf message MigrateProcessInstanceRequest { // key of the process instance to migrate int64 processInstanceKey = 1; // the migration plan that defines target process and element mappings MigrationPlan migrationPlan = 2; message MigrationPlan { // the key of process definition to migrate the process instance to int64 targetProcessDefinitionKey = 1; // the mapping instructions describe how to map elements from the source process definition to the target process definition repeated MappingInstruction mappingInstructions = 2; } message MappingInstruction { // the element ID to migrate from string sourceElementId = 1; // the element ID to migrate into string targetElementId = 2; } } ``` ### Output: `MigrateProcessInstanceResponse` ```protobuf message MigrateProcessInstanceResponse { } ``` ### Errors #### GRPC_STATUS_NOT_FOUND Returned if: - No process instance exists with the given key, or it is not active - No process definition exists with the given target definition key - No process instance exists with the given key for the tenants the user is authorized to work with. #### GRPC_STATUS_INVALID_ARGUMENT Returned if: - A `sourceElementId` does not refer to an element in the process instance's process definition - A `targetElementId` does not refer to an element in the target process definition - A `sourceElementId` is mapped by multiple mapping instructions. For example, the engine cannot determine how to migrate a process instance when the instructions are: [A->B, A->C]. #### GRPC_STATUS_FAILED_PRECONDITION Returned if: - Not all active elements in the given process instance are mapped to the elements in the target process definition - A mapping instruction changes the type of an element or event - A mapping instruction changes the implementation of a task - A mapping instruction refers to an unsupported element (i.e. some elements will be supported later on) - A mapping instruction refers to element in unsupported scenarios. (i.e. migrating active elements with event subscriptions will be supported later on) - A mapping instruction detaches a boundary event from an active element - Multiple mapping instructions refer to the same catch event - A mapping instruction changes a parallel multi-instance body to a sequential multi-instance body or vice versa ## `ModifyProcessInstance` RPC Modifies a running process instance. The command can contain multiple instructions to activate an element of the process, or to terminate an active instance of an element. Use the command to repair a process instance that is stuck on an element or took an unintended path. For example, because an external system is not available or doesn't respond as expected. ### Input: `ModifyProcessInstanceRequest` ```protobuf message ModifyProcessInstanceRequest { // the key of the process instance that should be modified int64 processInstanceKey = 1; // instructions describing which elements should be activated in which scopes, // and which variables should be created repeated ActivateInstruction activateInstructions = 2; // instructions describing which elements should be terminated repeated TerminateInstruction terminateInstructions = 3; message ActivateInstruction { // the ID of the element that should be activated string elementId = 1; // the key of the ancestor scope the element instance should be created in; // set to -1 to create the new element instance within an existing element // instance of the flow scope int64 ancestorElementInstanceKey = 2; // instructions describing which variables should be created repeated VariableInstruction variableInstructions = 3; } message VariableInstruction { // JSON document that will instantiate the variables for the root variable scope of the // process instance; it must be a JSON object, as variables will be mapped in a // key-value fashion. e.g. { "a": 1, "b": 2 } will create two variables, named "a" and // "b" respectively, with their associated values. [{ "a": 1, "b": 2 }] would not be a // valid argument, as the root of the JSON document is an array and not an object. string variables = 1; // the ID of the element in which scope the variables should be created; // leave empty to create the variables in the global scope of the process instance string scopeId = 2; } message TerminateInstruction { // the ID of the element that should be terminated int64 elementInstanceKey = 1; } } ``` ### Output: `ModifyProcessInstanceResponse` ```protobuf message ModifyProcessInstanceResponse { } ``` ### Errors #### GRPC_STATUS_NOT_FOUND Returned if: - No process instance exists with the given key, or it is not active. - No process instance was found with the given key for the tenants the user is authorized to work with. #### GRPC_STATUS_INVALID_ARGUMENT Returned if: - At least one activate instruction is invalid. An activate instruction is considered invalid if: - The process doesn't contain an element with the given ID. - A flow scope of the given element can't be created. - The given element has more than one active instance of its flow scope. - At least one variable instruction is invalid. A variable instruction is considered invalid if: - The process doesn't contain an element with the given scope ID. - The given element doesn't belong to the activating element's flow scope. - The given variables are not a valid JSON document. - At least one terminate instruction is invalid. A terminate instruction is considered invalid if: - No element instance exists with the given key, or it is not active. - The instructions would terminate all element instances of a process instance that was created by a call activity in the parent process. ## `PublishMessage` RPC Publishes a single message. Messages are published to specific partitions computed from their correlation keys. ### Input: `PublishMessageRequest` ```protobuf message PublishMessageRequest { // the name of the message string name = 1; // the correlation key of the message string correlationKey = 2; // how long the message should be buffered on the broker, in milliseconds int64 timeToLive = 3; // the unique ID of the message; can be omitted. only useful to ensure only one message // with the given ID will ever be published (during its lifetime) string messageId = 4; // the message variables as a JSON document; to be valid, the root of the document must be an // object, e.g. { "a": "foo" }. [ "foo" ] would not be valid. string variables = 5; // the tenant id of the message string tenantId = 6; } ``` ### Output: `PublishMessageResponse` ```protobuf message PublishMessageResponse { // the unique ID of the message that was published int64 key = 1; // the tenant id of the message string tenantId = 2; } ``` ### Errors #### GRPC_STATUS_ALREADY_EXISTS Returned if: - A message with the same ID was previously published (and is still alive). #### GRPC_STATUS_NOT_FOUND - If multi-tenancy is enabled, and `tenantId` is blank (empty string, null) - If multi-tenancy is enabled, and an invalid tenant ID is provided. A tenant ID is considered invalid if: - The tenant ID is blank (empty string, null) - The tenant ID is longer than 31 characters - The tenant ID contains anything other than alphanumeric characters, dot (.), dash (-), or underscore (\_) - If multi-tenancy is disabled, and `tenantId` is not blank (empty string, null), or has an ID other than `` #### GRPC_STATUS_PERMISSION_DENIED - If multi-tenancy is enabled, and an unauthorized tenant ID is provided ## `ResolveIncident` RPC Resolves a given incident. This simply marks the incident as resolved; most likely a call to UpdateJobRetries or SetVariables will be necessary to actually resolve the problem, followed by this call. ### Input: `ResolveIncidentRequest` ```protobuf message ResolveIncidentRequest { // the unique ID of the incident to resolve int64 incidentKey = 1; } ``` ### Output: `ResolveIncidentResponse` ```protobuf message ResolveIncidentResponse { } ``` ### Errors #### GRPC_STATUS_NOT_FOUND Returned if: - No incident with the given key exists. - No incident with the given key was found for the tenants the user is authorized to work with. ## `SetVariables` RPC Updates all the variables of a particular scope (e.g. process instance, flow element instance) from the given JSON document. ### Input: `SetVariablesRequest` ```protobuf message SetVariablesRequest { // the unique identifier of a particular element; can be the process instance key (as // obtained during instance creation), or a given element, such as a service task (see // elementInstanceKey on the job message) int64 elementInstanceKey = 1; // a JSON serialized document describing variables as key value pairs; the root of the document // must be an object string variables = 2; // if true, the variables will be merged strictly into the local scope (as indicated by // elementInstanceKey); this means the variables is not propagated to upper scopes. // for example, let's say we have two scopes, '1' and '2', with each having effective variables as: // 1 => `{ "foo" : 2 }`, and 2 => `{ "bar" : 1 }`. if we send an update request with // elementInstanceKey = 2, variables `{ "foo" : 5 }`, and local is true, then scope 1 will // be unchanged, and scope 2 will now be `{ "bar" : 1, "foo" 5 }`. if local was false, however, // then scope 1 would be `{ "foo": 5 }`, and scope 2 would be `{ "bar" : 1 }`. bool local = 3; } ``` ### Output: `SetVariablesResponse` ```protobuf message SetVariablesResponse { // the unique key of the set variables command int64 key = 1; } ``` ### Errors #### GRPC_STATUS_NOT_FOUND Returned if: - No element with the given `elementInstanceKey` exists. - No element with the given `elementInstanceKey` was found for the tenants the user is authorized to work with. #### GRPC_STATUS_INVALID_ARGUMENT Returned if: - The given payload is not a valid JSON document; all payloads are expected to be valid JSON documents where the root node is an object. ## `StreamActivatedJobs` RPC Opens a long living stream for the given job type, worker name, job timeout, and fetch variables. This will cause available jobs in the engine to be activated and pushed down this stream. See the [job worker's technical reference](/components/concepts/job-workers.md) for more on this. ### Input `StreamActivatedJobsRequest` ```protobuf message StreamActivatedJobsRequest { // the job type, as defined in the BPMN process (e.g. ) string type = 1; // the name of the worker activating the jobs, mostly used for logging purposes string worker = 2; // a job returned after this call will not be activated by another call until the // timeout (in ms) has been reached int64 timeout = 3; // a list of variables to fetch as the job variables; if empty, all visible variables at // the time of activation for the scope of the job will be returned repeated string fetchVariable = 5; // a list of identifiers of tenants for which to stream jobs repeated string tenantIds = 6; } ``` ### Output: a stream of `ActivatedJob` ```protobuf message ActivatedJob { // Describes the kind of job. enum JobKind { BPMN_ELEMENT = 0; EXECUTION_LISTENER = 1; TASK_LISTENER = 2; } // Describes the listener event type of the job. enum ListenerEventType { ASSIGNING = 0; CANCELING = 1; COMPLETING = 2; CREATING = 3; END = 4; START = 5; UNSPECIFIED = 6; UPDATING = 7; } // the key, a unique identifier for the job int64 key = 1; // the type of the job (should match what was requested) string type = 2; // the job's process instance key int64 processInstanceKey = 3; // the bpmn process ID of the job process definition string bpmnProcessId = 4; // the version of the job process definition int32 processDefinitionVersion = 5; // the key of the job process definition int64 processDefinitionKey = 6; // the associated task element ID string elementId = 7; // the unique key identifying the associated task, unique within the scope of the // process instance int64 elementInstanceKey = 8; // a set of custom headers defined during modelling; returned as a serialized // JSON document string customHeaders = 9; // the name of the worker which activated this job string worker = 10; // the amount of retries left to this job (should always be positive) int32 retries = 11; // when the job can be activated again, sent as a UNIX epoch timestamp int64 deadline = 12; // JSON document, computed at activation time, consisting of all visible variables to // the task scope string variables = 13; // the ID of the tenant that owns the job string tenantId = 14; // the kind of the job. JobKind kind = 15; // the listener event type of the job. ListenerEventType listenerEventType = 16; } ``` ### Errors #### GRPC_STATUS_INVALID_ARGUMENT Returned if: - Type is blank (empty string, null) - Timeout less than 1 (ms) - If multi-tenancy is enabled, and `tenantIds` is empty (empty list) - If multi-tenancy is enabled, and an invalid tenant ID is provided. A tenant ID is considered invalid if: - The tenant ID is blank (empty string, null) - The tenant ID is longer than 31 characters - The tenant ID contains anything other than alphanumeric characters, dot (.), dash (-), or underscore (\_) - If multi-tenancy is disabled, and `tenantIds` is not empty (empty list), or has an ID other than `` ## `ThrowError` RPC `ThrowError` reports a business error (i.e. non-technical) that occurs while processing a job. The error is handled in the process by an error catch event. If there is no error catch event with the specified `errorCode`, an incident is raised instead. Variables can be passed along with the thrown error to provide additional details that can be used in the process. ### Input: `ThrowErrorRequest` ```protobuf message ThrowErrorRequest { // the unique job identifier, as obtained when activating the job int64 jobKey = 1; // the error code that will be matched with an error catch event string errorCode = 2; // an optional error message that provides additional context string errorMessage = 3; // JSON document that will instantiate the variables at the local scope of the // error catch event that catches the thrown error; it must be a JSON object, as variables will be mapped in a // key-value fashion. e.g. { "a": 1, "b": 2 } will create two variables, named "a" and // "b" respectively, with their associated values. [{ "a": 1, "b": 2 }] would not be a // valid argument, as the root of the JSON document is an array and not an object. string variables = 4; } ``` ### Output: `ThrowErrorResponse` ```protobuf message ThrowErrorResponse { } ``` ### Errors #### GRPC_STATUS_NOT_FOUND Returned if: - No job was found with the given key. - No job was found with the given key for the tenants the user is authorized to work with. #### GRPC_STATUS_FAILED_PRECONDITION Returned if: - The job is already in a failed state, i.e. ran out of retries. ## `Topology` RPC Obtains the current topology of the cluster the gateway is part of. :::note The partition role can be one of `LEADER`, `FOLLOWER`, or `INACTIVE`, which [is defined here](../../components/zeebe/technical-concepts/partitions.md#roles). ::: :::note The partition health can be one of `HEALTHY`, `UNHEALTHY`, or `DEAD`, which [is defined here](../../components/zeebe/technical-concepts/health.md). ::: ### Input: `TopologyRequest` ```protobuf message TopologyRequest { } ``` ### Output: `TopologyResponse` ```protobuf message TopologyResponse { // list of brokers part of this cluster repeated BrokerInfo brokers = 1; // how many nodes are in the cluster int32 clusterSize = 2; // how many partitions are spread across the cluster int32 partitionsCount = 3; // configured replication factor for this cluster int32 replicationFactor = 4; // gateway version string gatewayVersion = 5; // the cluster's unique ID string clusterId = 6; } message BrokerInfo { // unique (within a cluster) node ID for the broker int32 nodeId = 1; // hostname of the broker string host = 2; // port for the broker int32 port = 3; // list of partitions managed or replicated on this broker repeated Partition partitions = 4; // broker version string version = 5; } message Partition { // Describes the Raft role of the broker for a given partition enum PartitionBrokerRole { LEADER = 0; FOLLOWER = 1; INACTIVE = 2; } // Describes the current health of the partition enum PartitionBrokerHealth { HEALTHY = 0; UNHEALTHY = 1; DEAD = 2; } // the unique ID of this partition int32 partitionId = 1; // the role of the broker for this partition PartitionBrokerRole role = 2; // the health of this partition PartitionBrokerHealth health = 3; } ``` ### Errors No specific errors. ## `UpdateJobRetries` RPC Updates the number of retries a job has left. This is mostly useful for jobs that have run out of retries, should the underlying problem be solved. ### Input: `UpdateJobRetriesRequest` ```protobuf message UpdateJobRetriesRequest { // the unique job identifier, as obtained through ActivateJobs int64 jobKey = 1; // the new amount of retries for the job; must be positive int32 retries = 2; } ``` ### Output: `UpdateJobRetriesResponse` ```protobuf message UpdateJobRetriesResponse { } ``` ### Errors #### GRPC_STATUS_NOT_FOUND Returned if: - No job exists with the given key. - No job was found with the given key for the tenants the user is authorized to work with. #### GRPC_STATUS_INVALID_ARGUMENT Returned if: - Retries is not greater than 0. ## `UpdateJobTimeout` RPC Updates the deadline of a job using the timeout (in milliseconds) provided. This can be used for extending or shortening the job deadline. The new deadline will be calculated from the current time, adding the timeout provided. ### Input: `UpdateJobTimeoutRequest` ```protobuf message UpdateJobTimeoutRequest { // the unique job identifier, as obtained from ActivateJobsResponse int64 jobKey = 1; // the duration of the new timeout in ms, starting from the current moment int64 timeout = 2; } ``` ### Output: `UpdateJobTimeoutResponse` ```protobuf message UpdateJobTimeoutResponse { } ``` ### Errors #### GRPC_STATUS_NOT_FOUND Returned if: - No job exists with the given key. - No job was found with the given key for the tenants the user is authorized to work with. #### GRPC_STATUS_INVALID_STATE Returned if: - The job is not active. --- ## Zeebe API (gRPC) ## About You can use the [gRPC](https://grpc.io/) high-performance, cross-platform remote procedure call (RPC) framework to communicate with the Orchestration Cluster. Zeebe clients use gRPC to communicate with the cluster. For example, you can use this API to activate jobs, cancel and create process instances, and more. ## Why use gRPC? Using this API may be beneficial if your use-case requires low-latency or high-throughput communication. Benefits of gRPC include: - Low-latency, high-throughput communication - Bidirectional streaming for efficient microservices integration - Ideal for scalable, event-driven process automation ## Key capabilities - Activate jobs - Create and cancel process instances - Manage workflows and more See [Zeebe API RPCs](gateway-service.md) for all available operations. Additionally, review [technical error handling](/apis-tools/zeebe-api/technical-error-handling.md) for a closer look at business logic errors, or [Postman](https://www.postman.com/camundateam/camunda-8-postman/collection/jzgs776/zeebe-api-grpc?action=share&creator=11465105) to experiment with the API. ## Authentication Authentication for the Zeebe API (gRPC) depends on your environment and how you deploy Camunda 8. You can find more details in the [Authentication guide](./zeebe-api-authentication.md). --- ## Technical error handling In the documentation above, the documented errors are business logic errors. These errors are a result of request processing logic, and not serialization, network, or other more general errors. These errors are described in this section. The gRPC API for Zeebe is exposed through an API gateway, which acts as a proxy for the cluster. Generally, this means the clients execute a remote call on the gateway, which is then translated to special binary protocol the gateway uses to communicate with nodes in the cluster. The nodes in the cluster are called brokers. Technical errors which occur between gateway and brokers (e.g. the gateway cannot deserialize the broker response, the broker is unavailable, etc.) are reported to the client using the following error codes: - `GRPC_STATUS_RESOURCE_EXHAUSTED`: When a broker receives more requests than it can handle, it signals backpressure and rejects requests with this error code. - In this case, it is possible to retry the requests with an appropriate retry strategy. - If you receive many such errors within a short time period, it indicates the broker is constantly under high load. - `GRPC_STATUS_UNAVAILABLE`: If the gateway itself is in an invalid state (e.g. out of memory). - `GRPC_STATUS_INTERNAL`: For any other internal errors that occurred between the gateway and the broker. This behavior applies to every request. In these cases, the client should retry with an appropriate retry policy (e.g. a combination of exponential backoff or jitter wrapped in a circuit breaker). As the gRPC server/client is based on generated code, keep in mind that any call made to the server can also return errors as described by the spec [here](https://grpc.io/docs/guides/error.html#error-status-codes). --- ## Authentication(Zeebe-api) This page describes the available authentication methods for accessing the Zeebe API (gRPC) in the Orchestration Cluster. Similar to the Orchestration Cluster REST API, the Zeebe API supports three authentication methods: - **No Authentication** - **Basic Authentication** - **OIDC-based Authentication** Please refer to the [Orchestration Cluster API authentication guide](./../orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md) for more details on each method and general guidance on usage. --- ## Tutorial(Zeebe-api-rest) In this tutorial, we'll step through examples to highlight the capabilities of the Zeebe API, such as assigning and unassigning a user to and from a Zeebe user task. ## Prerequisites - If you haven't done so already, [create a cluster](/components/hub/organization/manage-clusters/create-cluster.md). - Upon cluster creation, [create your first client](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client). Ensure you check the `Zeebe` client scope box. :::note Make sure you keep the generated client credentials in a safe place. The **Client secret** will not be shown again. For your convenience, you can also download the client information to your computer. ::: - In this tutorial, we utilize a JavaScript-written [GitHub repository](https://github.com/camunda/camunda-api-tutorials) to write and run requests. Clone this repo before getting started. - Ensure you have [Node.js](https://nodejs.org/en/download) installed as this will be used for methods that can be called by the CLI (outlined later in this guide). Run `npm install` to ensure you have updated dependencies. ## Getting started - You need authentication to access the API endpoints. Find more information [here](./zeebe-api-rest-authentication.md). ## Set up authentication If you're interested in how we use a library to handle auth for our code, or to get started, examine the `auth.js` file in the GitHub repository. This file contains a function named `getAccessToken` which executes an OAuth 2.0 protocol to retrieve authentication credentials based on your client id and client secret. Then, we return the actual token that can be passed as an authorization header in each request. To set up your credentials, create an `.env` file which will be protected by the `.gitignore` file. You will need to add your `ZEEBE_CLIENT_ID`, `ZEEBE_CLIENT_SECRET`, `ZEEBE_BASE_URL`, and `ZEEBE_AUDIENCE`, which is `zeebe.camunda.io` in a Camunda 8 SaaS environment. For example, your audience may be defined as `ZEEBE_AUDIENCE=zeebe.camunda.io`. These keys will be consumed by the `auth.js` file to execute the OAuth protocol, and should be saved when you generate your client credentials in [prerequisites](#prerequisites). Examine the existing `.env.example` file for an example of how your `.env` file should look upon completion. Do not place your credentials in the `.env.example` file, as this example file is not protected by the `.gitignore`. :::note In this tutorial, we will execute arguments to assign and unassign a user to and from a Zeebe user task. You can examine the framework for processing these arguments in the `cli.js` file before getting started. ::: ## Assign a Zeebe user task (POST) :::note In this tutorial, you will capture a **Zeebe user task** ID to assign and unassign users in this API. Camunda 8.5 introduced this new [user task](/components/modeler/bpmn/user-tasks/user-tasks.md) implementation type, and these Zeebe user tasks are different from job worker-based user tasks (which while still supported, are now deprecated with 8.6). See more details on task type differences in the [migrating to Zeebe user tasks documentation](/apis-tools/migration-manuals/migrate-to-camunda-user-tasks.md#task-type-differences). ::: First, let's script an API call to assign a Zeebe user task. To do this, take the following steps: 1. In the file named `zeebe.js`, outline the authentication and authorization configuration in the first few lines. This will pull in your `.env` variables to obtain an access token before making any API calls: ```javascript const authorizationConfiguration = { clientId: process.env.ZEEBE_CLIENT_ID, clientSecret: process.env.ZEEBE_CLIENT_SECRET, audience: process.env.ZEEBE_AUDIENCE, }; ``` 2. Examine the function `async function assignUser([userTaskKey, assignee])` below this configuration. This is where you will script out your API call. 3. Within the function, you must first generate an access token for this request, so your function should now look like the following: ```javascript async function assignUser([userTaskKey, assignee]) { const accessToken = await getAccessToken(authorizationConfiguration); } ``` 4. Using your generated client credentials from [prerequisites](#prerequisites), capture your Zeebe API URL beneath your call for an access token by defining `zeebeApiUrl`: `const zeebeApiUrl = process.env.ZEEBE_BASE_URL` 5. On the next line, script the API endpoint to assign a Zeebe user task.: ```javascript const url = `${ZeebeApiUrl}/user-tasks/${userTaskKey}/assignment`; ``` 6. Configure your POST request to the appropriate endpoint, including an authorization header based on the previously acquired `accessToken`: ```javascript const options = { method: "POST", url, headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, }, data: { // The body contains information about the new assignment. assignee: assignee, }, }; ``` 7. Call the assign endpoint, process the results from the API call, and emit an error message from the server if necessary: ```javascript try { // Call the assign endpoint. const response = await axios(options); // Process the results from the API call. if (response.status === 204) { console.log(`User task assigned to ${assignee}.`); } else { // Emit an unexpected error message. console.error("Unable to assign this user!"); } } catch (error) { // Emit an error from the server. console.error(error.message); } ``` 8. In your terminal, run `node cli.js zeebe assign `, where `` is the Zeebe user task ID you've captured from Tasklist, and `` is the assignee's email address. Include your own email address if you would like to see these results in your user interface. :::note This `assign` command is connected to the `assignUser` function at the bottom of the `zeebe.js` file, and executed by the `cli.js` file. While we will assign and unassign users in this tutorial, you may add additional arguments depending on the API calls you would like to make. ::: If you have a valid user and task ID, the assignment will now output. If you have an invalid API name or action name, or no arguments provided, or improper/insufficient credentials configured, an error message will output as outlined in the `cli.js` file. If no action is provided, it will default to "assign" everywhere, except when unassigning a user. ## Unassign a Zeebe user task (DELETE) To unassign a user from a Zeebe user task, you can use the same Zeebe user task ID from the previous exercise and take the following steps: 1. Outline your function, similar to the steps above: ```javascript async function unassignUser([userTaskKey]) { const accessToken = await getAccessToken(authorizationConfiguration); const ZeebeApiUrl = process.env.ZEEBE_BASE_URL; const url = `${ZeebeApiUrl}/user-tasks/${userTaskKey}/assignee`; } ``` 2. Configure the API call using the DELETE method: ```javascript const options = { method: "DELETE", url, headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, }, }; ``` 3. Process the results from the API call. For example: ```javascript try { // Call the delete endpoint. const response = await axios(options); // Process the results from the API call. if (response.status === 204) { console.log("User task has been unassigned!"); } else { // Emit an unexpected error message. console.error("Unable to unassign this user task!"); } } catch (error) { // Emit an error from the server. console.error(error.message); } ``` 4. In your terminal, run `node cli.js zeebe unassign `, where `` is the Zeebe user task ID. ## If you get stuck Having trouble configuring your API calls or want to examine an example of the completed tutorial? Navigate to the `completed` folder in the [GitHub repository](https://github.com/camunda/camunda-api-tutorials/tree/main/completed), where you can view an example `zeebe.js` file. ## Next steps You can script several additional API calls as outlined in the [Zeebe API reference material](./zeebe-api-rest-overview.md). --- ## Authentication(Zeebe-api-rest) :::warning The Zeebe REST API is **deprecated**. While it continues to function, new development should use the Orchestration Cluster REST API by referencing the [Orchestration Cluster REST API migration documentation](/apis-tools/migration-manuals/migrate-to-camunda-api.md). ::: The Zeebe REST API uses the same authentication mechanism as the [Orchestration Cluster REST API](../orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md). If your environment uses **OIDC-based authentication**, obtain an access token following [Using a token (OIDC/JWT)](../orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md#using-a-token-oidcjwt). If **no authentication is configured** (for example, for local development), see [No authentication (local development)](../orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md#no-authentication-local-development). When making requests to Zeebe, replace the base URL used in examples with your Zeebe API URL. Example: ```bash curl -X POST "$ZEEBE_BASE_URL/v1/process-instances" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"bpmnProcessId": "order-process", "variables": {"orderId": "12345"}}' ``` #### Token expiration Access tokens expire according to the `expires_in` property of a successful authentication response. After this duration, in seconds, you must request a new access token. --- ## Overview ## Introduction The Zeebe REST API is a REST API designed to interact with the Zeebe workflow engine. :::note Ensure you [authenticate](./zeebe-api-rest-authentication.md) before accessing the Zeebe REST API. ::: ## Context paths ### SaaS Find your **region Id** and **cluster Id** under **Connection information** in your client credentials (revealed when you click on your client under the **API** tab within your cluster). Example path: `https://${REGION}.api.camunda.io:443/${CLUSTER_ID}/v1/` ### Self-Managed Use the host and path defined for your [Zeebe Gateway](/reference/glossary.md#zeebe-gateway). For Ingress and routing details, see the [configuration guide](/self-managed/deployment/helm/configure/ingress/ingress-setup.md). The path used here is the default. Example path: `http://localhost:8080/v1/` ## API Explorer See [the interactive Zeebe REST API Explorer][zeebe-api-explorer] for specifications, example requests and responses, and code samples of interacting with the Tasklist REST API. [zeebe-api-explorer]: ./specifications/zeebe-rest-api.info.mdx --- ## Access control If authorization control is enabled for your Orchestration Cluster, users require the following authorizations to work with Admin. :::note If you already have another administration user, they can assign these [in the Admin UI](components/admin/authorization.md#create-an-authorization). See [the introduction to authorizations](components/concepts/access-control/authorizations.md#available-resources) for a list of all available authorizations. ::: ## Mandatory authorizations The following mandatory authorizations are required to work with Admin: | Authorization type | Resource type | Resource ID | Permission | | :--------------------- | :------------ | :--------------------------------------------------------------------------- | :--------- | | Admin component access | `Component` | `admin` or `identity` (deprecated) or `*` (for access to all web components) | `ACCESS` | ## Authorizations per resource The following authorizations are required to manage each User, Group, Role, Authorization, Mapping Rule, and Tenant resource: | Authorization type | Resource type | Resource ID | Permission | | :--------------------------------- | :---------------------------------------------------------------- | :------------------------------------------------------------------------------ | :------------------------------------------ | | Create/Read/Update/Delete resource | One of `User`, `Group`, `Authorization`, `Mapping Rule`, `Tenant` | ID of the resource or `*` (for access to all resources and to create resources) | Any of `CREATE`, `READ`, `UPDATE`, `DELETE` | ## Optional authorizations The following optional authorizations can also be defined: | Authorization type | Resource type | Resource ID | Permission | | :--------------------------------- | :---------------- | :--------------------------------- | :------------------------------------------------------------------------------------------- | | View audit log entries. | `AUDIT_LOG` | `ADMIN` or `*` for all categories. | `READ` | | Manage global user task listeners. | `GLOBAL_LISTENER` | `*` | `CREATE_TASK_LISTENER`, `READ_TASK_LISTENER`, `UPDATE_TASK_LISTENER`, `DELETE_TASK_LISTENER` | --- ## Introduction to Admin Use the integrated [Orchestration Cluster](../orchestration-cluster.md) Admin (formerly Orchestration Cluster Identity) to manage Camunda 8 authentication, authorization, and cluster administration. :::note This was renamed in 8.9 to reflect its expanded scope and to avoid confusion with [Management Identity](/self-managed/components/management-identity/overview.md). ::: ## About Admin The Orchestration Cluster Admin interface centralizes all key administrative jobs for a single cluster. This interface manages identity and access control for cluster components, including Zeebe, Operate, Tasklist, and Orchestration Cluster APIs, while also handling other core features such as cluster variables and the global user task listener, giving administrators one clear place to configure and operate their clusters end to end. Admin includes the following features: | Feature | Description | | :---------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Unified access management | Authentication and authorization are handled consistently across all Orchestration Cluster components and APIs. | | Flexible authentication | Admin supports multiple authentication modes, including no authentication, Basic authentication, and OpenID Connect (OIDC), depending on the deployment type. | | Tenant management | Multi-tenancy is managed directly within the Orchestration Cluster, allowing for clear separation of resources. | | [Cluster variables](cluster-variables.md) | Manage configuration values centrally across your cluster, making them available in FEEL expressions. | | [Global user task listeners](global-user-task-listeners.md) | Configure cluster-wide listeners that react to user task lifecycle events across all processes. | For details about authorization concepts, resources, and configuration, see [Orchestration Cluster authorizations](../concepts/access-control/authorizations.md). ## Manage access Depending on your setup, Admin allows you to manage Orchestration Cluster access as follows: | Entity | Description | Availability | | :--------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------- | :---------------- | | [Users](user.md) | Individuals who can access applications and perform actions based on their permissions. | All deployments | | [Groups](group.md) | Simplify access management by granting permissions collectively to groups of users. | All deployments | | [Roles](role.md) | Sets of permissions to define what actions can be performed on specific resources. Roles can be assigned to users and groups. | All deployments | | [Authorizations](authorization.md) | The specific permissions that connect users, groups, or roles with resources and actions (for example, `READ`, `UPDATE`, `DELETE`). | All deployments | | [Tenants](tenant.md) | Isolate data within a single cluster. This is useful for multi-tenancy applications. | Self-Managed only | :::info Admin in Self-Managed For documentation on deploying Admin as part of Camunda 8 Self-Managed, see [Admin in Self-Managed](/self-managed/components/orchestration-cluster/admin/overview.md). ::: --- ## Audit operations Audit [operations](../audit-log/overview.md) in Camunda 8 Admin. ## Prerequisites To follow the steps in this guide, you must be [authorized to view operations in the audit log](../audit-log/overview/access-control.md). ## Audit operations In Admin, you can audit all [`ADMIN` operations](../audit-log/overview/recorded-operations.md#admin-operations): 1. In the top navigation, click **Operations log**. 2. To sort the log, click a column header. ## Next steps - [Learn about the operation data structure in the operations log.](../audit-log/overview/operation-structure.md) - [Use the Audit Log REST API to programmatically access the audit log](../../apis-tools/orchestration-cluster-api-rest/specifications/search-audit-logs.api.mdx). --- ## Authorizations Use authorizations to control access to resources in your Orchestration Cluster. ## About authorizations An authorization grants an owner access to a resource and defines the specific permissions they have. - Owner: The entity that receives permissions, such as a [user](user.md), [group](group.md), [role](role.md), [client](client.md), or [mapping rule](mapping-rules.md). - In SaaS deployments, the username is the user's email address. - In Self-Managed deployments, the username must match [the value of the claim configured as `username-claim`](/self-managed/components/orchestration-cluster/admin/connect-external-identity-provider.md#step-4-configure-the-oidc-connection-details). - Resource: The object that the permissions apply to, such as a process definition, decision definition, or system. See the full list of [available resources](/components/concepts/access-control/authorizations.md#available-resources). Each authorization specifies which permissions the owner has for the resource (for example, `READ`, `UPDATE`, `DELETE`). For an authorization to apply, [enable it in your cluster configuration](/components/concepts/access-control/authorizations.md#configuration). To learn more, see [Orchestration Cluster authorizations](/components/concepts/access-control/authorizations.md). ## Create an authorization in Admin To create a new authorization: 1. Log in to Admin, and select the **Authorizations** tab. 2. Select a resource type from the list on the left, and select **Create authorization**. 3. Enter the following information: - **Owner type**: The entity to which you want to assign permissions, such as a user, group, role, client, or mapping rule. - **Owner ID**: The ID of the owner. - **Resource type**: The selected resource type. - **Resource scope**: Choose how this authorization is scoped: - By **Resource ID**, or - For `USER_TASK`, by **Resource property name** with the `PROPERTY` matcher. - **Resource ID**: The ID of the resource within the selected resource type. Use `*` to grant permissions for all resources of that type. - **Resource property name** _(USER_TASK only)_: The task property used when scoping access with the `PROPERTY` matcher. Supported values are: - `assignee` - `candidateUsers` - `candidateGroups` Only one of **Resource ID** or **Resource property name** can be specified. If you use a resource property, set the matcher to `PROPERTY`. 4. Select the permissions you want to grant. 5. Click **Create authorization**. The authorization is created, and the owner is granted the specified permissions. ## User task authorizations To support fine-grained access to user tasks in Tasklist and the Orchestration Cluster REST API, Admin provides a **USER_TASK** resource type with the following permissions: - `READ`: View the task and its properties. - `UPDATE`: Perform updates on the task (for example, change assignment, due dates, or candidate users or groups). - `CLAIM`: Claim a task from a pool of candidate users or groups. - `COMPLETE`: Complete the task, with or without variables. ### Configure property-based user task authorizations With property-based user task authorizations, you can grant permissions based on task assignment rather than a specific task ID. A user is authorized when their username or group membership matches a corresponding task property. To create a property-based user task authorization: 1. Log in to Admin, and select the **Authorizations** tab. 2. Create a new authorization for the `USER_TASK` resource type. 3. Specify the **Owner type** and **Owner ID** (for example, a role that represents task workers). 4. Set the matcher to `PROPERTY`. 5. Select the task property used to scope access: - `assignee` - `candidateUsers` - `candidateGroups` 6. Select the permissions to grant (for example `READ`, `CLAIM`, and `COMPLETE`). 7. Create the authorization. You can't combine multiple task properties in a single authorization. To cover all three properties (`assignee`, `candidateUsers`, `candidateGroups`), create one authorization per property. ### Authorization for user tasks You can control access to user tasks using a combination of process-level and task-level permissions: - Process-level permissions on the `Process Definition` resource, such as `READ_USER_TASK`, `CLAIM_USER_TASK`, `COMPLETE_USER_TASK`, and `UPDATE_USER_TASK`. - Task-level permissions on the `USER_TASK` resource, such as `READ`, `UPDATE`, `CLAIM`, and `COMPLETE`, which are typically scoped using property-based access control on task properties such as `assignee`, `candidateUsers`, and `candidateGroups`. When both process-level and task-level permissions exist, process-level permissions take precedence. If a user already has the required `Process Definition` permission for an operation (for example, `READ_USER_TASK`, `CLAIM_USER_TASK`, `COMPLETE_USER_TASK`, or `UPDATE_USER_TASK`), the system does not evaluate `USER_TASK` permissions for that operation. Task-level `USER_TASK` permissions are evaluated only when no effective process‑level permission exists for that user and process definition. For Tasklist-specific behavior and practical authorization patterns, see [User task authorization in Tasklist](../tasklist/user-task-authorization.md). ### Authorization examples #### Supervisor: broad process-level access To allow a supervisor to see and manage all user tasks for one or more processes: - Resource type: `PROCESS_DEFINITION` - Resource scope: by **Resource ID** - Resource ID: `*` (or a specific BPMN process ID) - Permissions: `READ_USER_TASK`, `UPDATE_USER_TASK`, `CLAIM_USER_TASK`, `COMPLETE_USER_TASK` This grants broad visibility and control over all user tasks for the selected processes, without needing task-level authorizations. #### Task worker: property-based access The default task worker role is created with property-based user task authorizations: - Role ID: `task-worker` - Resource type: `USER_TASK` - Resource scope: by **Resource property name** (`PROPERTY` matcher) - Property name: `assignee`, `candidateUsers`, or `candidateGroups` - Permissions: `READ`, `CLAIM`, `COMPLETE` This ensures that task workers can only see, claim, and complete tasks where they are the assignee, a candidate user, or in a candidate group. :::note Default roles, including task worker, are recreated each time the cluster starts and are not customizable. To adjust permissions, create and manage custom roles instead. ::: ## Change an existing authorization :::tip Partial wildcard matching, for example `my-resource*`, is not supported. ::: ## Update an authorization Authorizations cannot be updated after they are created. To edit an authorization, [delete](#delete-an-authorization) the existing one, and create a new authorization with the updated permissions. ## Delete an authorization Delete an authorization by completing the following steps: 1. Log in to Admin, and select the **Authorizations** tab. 2. Select the resource type of the authorization you want to delete. 3. In the list, find the authorization you want to remove and click **Delete**. 4. Confirm the deletion by clicking **Delete** in the confirmation dialog. The authorization is deleted, and the owner no longer has the permissions granted by it. :::caution Deleting an authorization is permanent and can't be undone. ::: --- ## Clients Configure and manage client access to a cluster so the client application has the permissions it requires. ## About client application access A client is an application that interacts with an Orchestration Cluster via its APIs. This guide describes how to manage client access in SaaS and in Self-Managed environments that use an [external OpenID Connect (OIDC) identity provider](../concepts/access-control/connect-to-identity-provider.md) for authentication. If you are using the Orchestration Cluster with [Basic authentication](/self-managed/concepts/authentication/authentication-to-orchestration-cluster.md#basic-authentication), both end users and machine-to-machine (m2m) applications are treated as users and must be [managed accordingly](user.md). The Admin UI does not display dedicated client options in Basic authentication setups for this reason. ## Manage clients in SaaS In Camunda 8 SaaS, client credentials are created and managed in [Camunda Hub](../hub/index.md). ### Step 1: Create client credentials in Camunda Hub Follow the [guide for creating client credentials in Camunda Hub](../hub/organization/manage-clusters/manage-api-clients.md#create-a-client). Copy the **client id** shown in the variables after you have created your client as this is required in the next step. ### Step 2: Configure authorizations in Admin If you have enabled [authorizations](/components/concepts/access-control/authorizations.md) on your cluster, the new client has no permissions by default, even after assigning scopes in Camunda Hub. You must grant fine-grained permissions in Admin: 1. Open the **Admin** application for your cluster. 2. Open the **Authorizations** tab. 3. Click **Create authorization**. 4. Set the **Owner type** to `Client`. 5. In the **Owner ID** field, enter the **Client ID** of the client you just created and copied. 6. Select the **Resource type**, **Resource ID**, and permissions the client needs. 7. Click **Create authorization**. If authorizations are disabled, your client will have full access based on the scopes you selected during creation. ## Manage clients in Self-Managed with OIDC authentication To configure a client application in a [Self-Managed environment with OIDC](/self-managed/components/orchestration-cluster/admin/connect-external-identity-provider.md), complete the following two steps: 1. Register your client application with your identity provider to obtain client credentials. 2. Configure authorizations for the client in the Orchestration Cluster Admin to grant the necessary permissions. After completing these steps, your client application can then authenticate with your IdP, obtain an access token, and use that token to make authorized API calls to the Camunda 8 orchestration cluster. ### Prerequisites Your Orchestration Cluster must be [configured to use a token claim as the client id](/self-managed/components/orchestration-cluster/admin/connect-external-identity-provider.md#step-1-configure-the-oidc-client-id-claim). ### Step 1: Create client credentials in your IdP Before configuring access in the Orchestration Cluster, you must register your client application in your OIDC-compatible identity provider (for example, EntraID, Keycloak, Okta). During the registration process, your identity provider will provide you with a **Client ID** and a **Client Secret**. Your application will use these credentials to authenticate and obtain an access token. ### Step 2: Configure authorizations in Admin Once you have your client credentials, you can configure the required permissions in the Admin component of your cluster. Log in to Admin and choose one of the following methods to grant authorizations. #### Authorization based on client ID This method is suitable when your client application requires a fixed set of permissions. Follow [the steps on how to create authorizations](/components/admin/authorization.md#create-an-authorization) with the following specifics: - As the **Owner type**, select `Client`. - In the **Owner ID** field, enter the **Client ID** that matches your client's value for the configured client id claim. You can also assign the client to existing [groups](./group.md) or [roles](./role.md) to inherit their permissions. #### Flexible authorization based on JWT claims with mapping rules This method is ideal when you need to dynamically assign permissions based on claims in the OIDC access token, such as scopes or custom claims. 1. [Create a mapping rule](/components/admin/mapping-rules.md#add-a-mapping-rule) that matches a claim from your client's access token. 2. [Create authorizations](/components/admin/authorization.md#create-an-authorization) for the mapping rule with the following specifics: - As the **Owner type**, select `Mapping Rule`. - In the **Owner ID** field, enter the **Mapping Rule ID** that you chose in the previous step. Alternatively, you can assign the mapping rule to [groups](./group.md) or [roles](./role.md) to inherit their permissions. Any client that authenticates with a token matching the criteria of the mapping rule will be granted the associated permissions. --- ## Cluster variables Use Admin to manage cluster variables, which store configuration values centrally across your cluster and make them available in [FEEL expressions](/components/modeler/feel/cluster-variable/overview.md). ## About cluster variables Cluster variables allow you to maintain environment-specific configurations, API endpoints, feature flags, and other shared values without hardcoding them into individual process definitions. Variables can be defined at the global (cluster-wide) or tenant level. :::tip To learn more about cluster variables, including scope resolution, data types, and FEEL expression usage, see the [cluster variables overview](/components/modeler/feel/cluster-variable/overview.md). ::: You can manage cluster variables through the Admin UI or the [Orchestration Cluster API](/apis-tools/orchestration-cluster-api-rest/specifications/create-global-cluster-variable.api.mdx). ## Manage global cluster variables Global cluster variables are available to all processes across the entire cluster. ### Create a global cluster variable 1. Log in to Admin in your cluster, and select the **Cluster Variables** tab. 2. Click **Create variable**. 3. Provide the following details: - **Name**: A unique identifier for the variable. - **Value**: The value of the variable, which can be a string, number, boolean, or JSON object. 4. Click **Create variable**. The variable is created and immediately available for use in FEEL expressions across all processes in the cluster using `camunda.vars.cluster.` or `camunda.vars.env.`. ### Update a global cluster variable 1. Log in to Admin in your cluster, and select the **Cluster Variables** tab. 2. Click the **pencil icon** next to the variable you want to update. 3. Update the variable value. 4. Click **Save**. The updated value takes effect for new evaluations of FEEL expressions that reference this variable. ### Delete a global cluster variable 1. Log in to Admin in your cluster, and select the **Cluster Variables** tab. 2. Click **Delete** next to the variable you want to delete. 3. Confirm the deletion by clicking **Delete** in the confirmation dialog. The variable is deleted and is no longer available in FEEL expressions. :::note Deleting a cluster variable does not affect process instances that have already resolved the variable value. ::: ## Manage tenant cluster variables When [multi-tenancy](/components/concepts/multi-tenancy.md) is enabled, you can define tenant-specific cluster variables that override global variables with the same name for processes running in that tenant. ### Create a tenant cluster variable 1. Log in to Admin in your cluster, and select the **Cluster Variables** tab. 2. Select the tenant for which you want to create a variable. 3. Click **Create variable**. 4. Provide the following details: - **Name**: A unique identifier for the variable. - **Value**: The value of the variable. 5. Click **Create variable**. The variable is created and available in FEEL expressions for processes running in the selected tenant using `camunda.vars.tenant.` or `camunda.vars.env.`. :::note If a global variable with the same name exists, the tenant-level variable takes precedence for processes running in this tenant. See [scope resolution](/components/modeler/feel/cluster-variable/scope-and-priority.md) for details. ::: ### Update a tenant cluster variable 1. Log in to Admin in your cluster, and select the **Cluster Variables** tab. 2. Select the tenant for which you want to update the variable. 3. Click the **pencil icon** next to the variable you want to update. 4. Update the variable value. 5. Click **Save**. ### Delete a tenant cluster variable 1. Log in to Admin in your cluster, and select the **Cluster Variables** tab. 2. Select the tenant for which you want to delete the variable. 3. Click **Delete** next to the variable you want to delete. 4. Confirm the deletion by clicking **Delete** in the confirmation dialog. ## See also - [Cluster variables overview](/components/modeler/feel/cluster-variable/overview.md) — learn about scopes, data types, and FEEL expression usage. - [Get started with cluster variables](/components/modeler/feel/cluster-variable/get-started.md) — tutorial for creating and using your first cluster variable. - [Orchestration Cluster API: Create global cluster variable](/apis-tools/orchestration-cluster-api-rest/specifications/create-global-cluster-variable.api.mdx) — manage cluster variables via API. --- ## Global user task listeners Use Admin to manage [global user task listeners](/components/concepts/global-user-task-listeners.md), which are cluster-wide listeners that react to user task lifecycle events across all processes. ## About global user task listeners Global user task listeners allow you to define listeners once for all processes in a cluster, instead of individually per user task. They are useful for centralizing audit logging, notifications, governance rules, and other cross-cutting concerns. :::tip To learn more about global user task listeners, including execution order, supported features, and configuration options, see the [global user task listeners concept page](/components/concepts/global-user-task-listeners.md). ::: You can manage global user task listeners through the Admin UI, [Unified Configuration](/components/concepts/global-user-task-listeners/configuration.md#configure-through-unified-configuration), or the [Orchestration Cluster API](/apis-tools/orchestration-cluster-api-rest/specifications/create-global-task-listener.api.mdx). ## Manage global user task listeners in Admin The Admin UI provides a user-friendly interface to manage global user task listeners in the **Global User Task Listeners** tab. Changes made through the Admin UI take effect immediately, without requiring a cluster restart. :::note The Admin UI uses the Orchestration Cluster API to manage listeners. Listeners created through the Admin UI have their `source` property set to `API`. ::: ### Create a global user task listener 1. Log in to Admin in your cluster, and select the **Global User Task Listeners** tab. 2. Click **Create listener**. 3. Provide the following details: - **Listener ID**: A unique identifier for the listener. - **Listener type**: The name of the listener type. Job workers use this to identify and process listener jobs. - **Event type**: The user task lifecycle events that trigger the listener, selected from the dropdown menu with the following supported values: "All events", "Assigning", "Canceling", "Completing", "Creating", and "Updating". - **Retries** (optional): Number of retries for the listener job. Defaults to `3`. - **Execution order**: When the global listener should be executed with respect to model-level ones. Supported values: "Before model-level listeners" or "After model-level listeners". - **Priority** (optional): The priority of the listener. Higher priority listeners are executed first. Defaults to `50`. 4. Click **Create**. The listener is created and immediately applies to new lifecycle events for both running and new process instances. ### Update a global user task listener 1. Log in to Admin in your cluster, and select the **Global User Task Listeners** tab. 2. Click the **pencil icon** next to the listener you want to update. 3. Update the listener details. 4. Click **Update**. The updated listener configuration applies immediately to new lifecycle events. ### Delete a global user task listener 1. Log in to Admin in your cluster, and select the **Global User Task Listeners** tab. 2. Click **Delete** next to the listener you want to delete. 3. Confirm the deletion by clicking **Delete** in the confirmation dialog. The listener is deleted and no longer triggers for new lifecycle events. In-progress listener jobs are not affected. ### Known limitations The Admin UI retrieves the listener list from secondary storage. Because secondary storage is eventually consistent, changes might not appear immediately. ## See also - [Global user task listeners](/components/concepts/global-user-task-listeners.md) — learn about execution order, supported features, and configuration options. - [User task listeners](/components/concepts/user-task-listeners.md) — learn about model-level user task listeners. - [Orchestration Cluster API: Create global task listener](/apis-tools/orchestration-cluster-api-rest/specifications/create-global-task-listener.api.mdx) — manage listeners via API. --- ## Groups A user group is a way to organize multiple [users](user.md) in one unit. Groups simplify access management by allowing you to assign permissions to a collection of users at once, rather than individually. You can grant permissions to a group by assigning [roles](role.md) to it or creating direct [authorizations](authorization.md). ## Create a group To create a group: 1. Log in to Admin in your cluster, and click on the **Groups** tab. 2. Click on the **Create group** button, and provide the following group details: - **Group ID**: The unique identifier for the group. - **Name**: The name of the group. - **Description**: A description of the group. 3. Click on the **Create group** button. The group is created and can now be assigned to roles or users. ![identity-create-group-tab](./img/create-group-tab.png) ## Update a group To update a group: 1. Log in to Admin in your cluster, and click on the **Groups** tab. 2. Click on the **pencil icon** next to the group you want to update. 3. Update the group details: - **Name**: The name of the group. - **Description**: A description of the group. 4. Click on the **Save** button. The group details are updated. ## Delete a group 1. Log in to Admin in your cluster, and click on the **Groups** tab. 2. Click on the **Delete** button next to the group you want to delete. 3. Confirm the deletion by clicking on the **Delete** button in the confirmation dialog. The group is deleted. Users and roles that were assigned to the group will not be affected, but they will no longer be part of the group. The authorizations that were granted to the group will also be removed. ## Assign authorizations to a group See the [authorization](./authorization.md) section to learn how to create authorizations for groups. ## Manage users ### Assign users to a group To assign users to a group: 1. Log in to Admin in your cluster, and click on the **Groups** tab. 2. Click on the group you want to assign users to. 3. Click on the **Users** tab. 4. Click on the **Assign user** button. 5. Type the username of the user you want to assign to the group, and click on the **Assign user** button. For SaaS deployments, the username field refers to the email address of the user. For Self-Managed deployments, the username field has to match [the value of the claim configured as `username-claim`](/self-managed/components/orchestration-cluster/admin/connect-external-identity-provider.md#step-4-configure-the-oidc-connection-details). :::note For Self-Managed deployments with Basic authentication, you must search for existing users. ::: The user is assigned to the group and inherits its permissions. ### Remove users from a group To remove users from a group: 1. Log in to Admin in your cluster, and click on the **Groups** tab. 2. Click on the group you want to remove users from. 3. Click on the **Users** tab. 4. Click on the **Remove** button next to the user you want to remove from the group. 5. Confirm the removal by clicking on the **Remove** button in the confirmation dialog. The user is removed from the group and loses any permissions that were granted through the group. ## Manage roles ### Assign roles to a group To assign roles to a group: 1. Log in to Admin in your cluster, and click on the **Groups** tab. 2. Click on the group you want to assign roles to. 3. Click on the **Roles** tab. 4. Click on the **Assign role** button. 5. Search for the ID of the role you want to assign to the group, and click on the **Assign role** button. The role is assigned to the group. Users in the group now have the permissions granted by that role. ### Remove roles from a group To remove roles from a group: 1. Log in to Admin in your cluster, and click on the **Groups** tab. 2. Click on the group you want to remove roles from. 3. Click on the **Roles** tab. 4. Click on the **Remove** button next to the role you want to remove from the group. 5. Confirm the removal by clicking on the **Remove** button in the confirmation dialog. The role is removed from the group. Users in the group will lose the permissions that were granted through that role. ## Manage clients :::note In Self-Managed deployment, [client management](client.md) is only available for OIDC authentication. ::: ### Assign client to a group To assign a client to a group: 1. Log in to Admin in your cluster, and click on the **Groups** tab. 2. Click on the group you want to assign a client to. 3. Click on the **Clients** tab. 4. Click on the **Assign client** button. 5. Type the ID of the client you want to assign to the group, and click on the **Assign client** button. The client is assigned to the group. ### Remove client from a group To remove a client from a group: 1. Log in to Admin in your cluster, and click on the **Groups** tab. 2. Click on the group you want to remove a client from. 3. Click on the **Clients** tab. 4. Click on the **Remove** button next to the client you want to remove from the group. 5. Confirm the removal by clicking on the **Remove** button in the confirmation dialog. The client is removed from the group. ## Manage mapping rules Self-Managed only :::note [Mapping rules](../concepts/access-control/mapping-rules.md) are only available for OIDC authentication. ::: ### Assign mapping rules to a group To assign a mapping rule to a group: 1. Log in to Admin in your cluster, and click on the **Groups** tab. 2. Click on the group you want to assign mapping rules to. 3. Click on the **Mapping rules** tab. 4. Click on the **Assign mapping rule** button. 5. Search for the ID of the mapping rule you want to assign to the group, and click on the **Assign mapping rule** button. The mapping rule is assigned to the group. ### Remove mapping rules from a group To remove a mapping rule from a group: 1. Log in to Admin in your cluster, and click on the **Groups** tab. 2. Click on the group you want to remove mapping rules from. 3. Click on the **Mapping rules** tab. 4. Click on the **Remove** button next to the mapping rule you want to remove from the group. 5. Confirm the removal by clicking on the **Remove** button in the confirmation dialog. The mapping rule is removed from the group. --- ## Mapping rules Self-Managed only Mapping rules provide flexible access to Orchestration Cluster resources based on claims in a user's or client's OIDC access token. :::info To learn more, see [mapping rules](../concepts/access-control/mapping-rules.md). ::: ## Create a mapping rule To create a mapping rule: 1. Log in to Admin in your cluster, and select the **Mapping Rules** tab. 2. Click **Create a mapping rule**, and enter the following details: - **Mapping Rule ID**: A unique identifier for the mapping rule. - **Mapping Rule name**: A user-friendly name. - **Claim name**: The name of a claim in the OIDC access token or a [JSONPath expression](https://www.rfc-editor.org/rfc/rfc9535) that points to a claim in the access token. - **Claim value**: The expected value of the claim so that the mapping rule matches an access token. 3. Click **Create mapping rule** to create the role. You can now assign the role to groups, roles, or tenants, or create and apply authorizations for it. ## Update a mapping rule To update a mapping rule: 1. Log in to Admin in your cluster, and select the **Mapping rules** tab. 2. Click the **pencil icon** next to the mapping rule you want to update. 3. Update the mapping rule details as required. 4. Click **Save** to update the mapping rule. ## Delete a mapping rule To delete a mapping rule: 1. Log in to Admin in your cluster, and select the **Mapping Rules** tab. 2. Click **Delete** next to the mapping rule you want to delete. 3. Confirm the deletion by clicking on the **Delete** button in the confirmation dialog. The mapping rule is deleted. ## Assign authorizations to a role See [authorizations](./authorization.md) to learn how to create authorizations for mapping rules. --- ## Roles A role is a collection of [authorizations](authorization.md) that defines a set of permissions. ## About roles Roles are used to grant users the system and data access required to fulfill a certain responsibility. A role can be assigned to [users](user.md) directly or as part of a [group](group.md) they belong to. :::info The Orchestration Cluster creates [a set of default roles](../concepts/access-control/authorizations.md#default-roles) on startup. If deleted, they're automatically recreated on cluster startup. ::: ## Create a role To create a role: 1. Log in to Admin in your cluster, and select the **Roles** tab. 2. Click **Create role**, and enter the following role details: - **Role ID**: The unique identifier for the role. - **Role name**: The name of the role. - **Description**: An optional description of the role. 3. Click **Create role**. The role is created and can now be assigned to users or groups. ## Delete a role To delete a role: 1. Log in to Admin in your cluster, and select the **Roles** tab. 2. Click **Delete** next to the role you want to delete. 3. Confirm the deletion by clicking **Delete** in the confirmation dialog. The role is deleted. The authorizations that were granted to the role are also removed. ## Assign authorizations to a role See [authorizations](./authorization.md) to learn how to create authorizations for roles. ## Manage users ### Assign users to a role To assign users to a role: 1. Log in to Admin in your cluster, and select the **Roles** tab. 2. Click on the role you want to assign users to. 3. Select the **Users** tab. 4. Click **Assign user**. 5. Type the username of the user you want to assign to the role, and click **Assign user**. For SaaS deployments, the username field refers to the email address of the user. For Self-Managed deployments, the username field has to match [the value of the claim configured as `username-claim`](/self-managed/components/orchestration-cluster/admin/connect-external-identity-provider.md#step-4-configure-the-oidc-connection-details). :::note For Self-Managed deployments with Basic authentication, you must search for existing users. ::: The user is assigned to the role and inherits its permissions. ### Remove users from a role To remove users from a role: 1. Log in to Admin in your cluster, and select the **Roles** tab. 2. Click on the role you want to remove users from. 3. Select the **Users** tab. 4. Click **Remove** next to the user you want to remove from the role. 5. Confirm the removal by clicking **Remove** in the confirmation dialog. The user is removed from the role and loses any permissions that were granted through it. ## Manage groups ### Assign groups to a role To assign groups to a role: 1. Log in to Admin in your cluster, and select the **Roles** tab. 2. Click on the role you want to assign users to. 3. Select the **Groups** tab. 4. Click **Assign group**. 5. Type the ID of the group you want to assign to the role, and click **Assign group**. The group is assigned to the role and inherits its permissions. ### Remove groups from a role To remove groups from a role: 1. Log in to Admin in your cluster, and select the **Roles** tab. 2. Click on the role you want to remove groups from. 3. Select the **Groups** tab. 4. Click **Remove** next to the group you want to remove from the role. 5. Confirm the removal by clicking **Remove** in the confirmation dialog. The group is removed from the role and loses any permissions that were granted through it. ## Manage clients :::note In a Self-Managed deployment, [client management](client.md) is only available for OIDC authentication. ::: ### Assign client to a role To assign a client to a role: 1. Log in to Admin in your cluster, and select the **Roles** tab. 2. Click on the role you want to assign a client to. 3. Select the **Clients** tab. 4. Click **Assign client**. 5. Type the ID of the client you want to assign to the role, and click **Assign client**. The client is assigned to the role. ### Remove client from a role To remove a client from a role: 1. Log in to Admin in your cluster, and select the **Roles** tab. 2. Click on the role you want to remove a client from. 3. Select the **Clients** tab. 4. Click **Remove** next to the client you want to remove from the role. 5. Confirm the removal by clicking **Remove** in the confirmation dialog. The client is removed from the role. ## Manage mapping rules Self-Managed only :::note [Mapping rules](../concepts/access-control/mapping-rules.md) are only available for OIDC authentication. ::: ### Assign mapping rules to a role To assign mapping rules to a role: 1. Log in to Admin in your cluster, and select the **Roles** tab. 2. Click on the role you want to assign mapping rules to. 3. Select the **Mapping rules** tab. 4. Click **Assign mapping rule**. 5. Search for the ID of the mapping rule you want to assign to the role, and click **Assign mapping rule**. The mapping rule is assigned to the role. ### Remove mapping rules from a role To remove a mapping rule from a role: 1. Log in to Admin in your cluster, and select the **Roles** tab. 2. Click on the role you want to remove mapping rules from. 3. Select the **Mapping rules** tab. 4. Click **Remove** next to the mapping rule you want to remove from the role. 5. Confirm the removal by clicking **Remove** in the confirmation dialog. The mapping rule is removed from the role. --- ## Tenants Self-Managed only Use Admin to manage Orchestration Cluster tenants and isolate data within a single cluster. ## About tenants A tenant is a logical boundary for data within a Camunda 8 installation. This enables multiple teams, departments, or clients to share a single environment while keeping data isolated. :::tip To learn more about tenants, see [multi-tenancy](../concepts/multi-tenancy.md). ::: You can manage your Orchestration Cluster tenants directly in [Admin](admin-introduction.md). - **Tenancy** is enabled by default. - **Tenancy checks** are disabled by default. All data maps to the `` tenant. This allows administrators to set up tenants and assignments before enforcing tenancy checks. To enable multi-tenancy checks, see [Self-Managed configuration properties](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#multi-tenancy). ## Create a tenant :::note The `` tenant is automatically created when Admin starts. ::: 1. Log in to Admin and open the **Tenants** tab. ![tenant-management-tab](./img/tenant-management-tab.png) 2. Click **Create tenant**. In the modal, provide the tenant **ID**, **name**, and optional **description**. Then click **Create tenant**. ![tenant-management-create-tenant-modal](./img/tenant-management-create-tenant-modal.png) 3. The tenant appears in the list. If not, refresh the page. ![tenant-management-new-tenant-in-table](./img/tenant-management-new-tenant-in-table.png) 4. Click the tenant to open details and manage assignments. ![tenant-management-tenant-details-users-tab](./img/tenant-management-tenant-details-users-tab.png) ## Update and delete a tenant Tenants cannot be updated after creation. To change a tenant's details, you must delete the tenant and then create a new tenant with the details you require. To delete a tenant, click on the **Delete** option in the list of tenants, and confirm the deletion. :::note The `` tenant is a system entity and cannot be deleted. ::: ## Tenant assignments You can assign the following entities to a tenant: - [Users](user.md) - [Groups](group.md) - [Roles](role.md) - [Mapping rules](mapping-rules.md) - [Clients](client.md) You can manage these assignments by selecting the relevant tab on the tenant details page. ### Assign users to a tenant 1. Select the **Users** tab. 2. Click **Assign user**. In the modal, enter the username and confirm. The username field has to match [the value of the claim configured as `username-claim`](/self-managed/components/orchestration-cluster/admin/connect-external-identity-provider.md#step-4-configure-the-oidc-connection-details). ![tenant-management-assign-users-modal](./img/tenant-management-assign-users-modal.png) 3. The user appears in the list after assignment. Refresh the page if needed. ![tenant-management-assigned-users](./img/tenant-management-assigned-users.png) ### Assign groups to a tenant 1. Select the **Groups** tab. 2. Click **Assign group**. Search for a group ID and confirm. ![tenant-management-assign-groups-modal](./img/tenant-management-assign-groups-modal.png) 3. The group appears in the list after assignment. Refresh the page if needed. ![tenant-management-assigned-groups](./img/tenant-management-assigned-groups.png) ### Assign roles to a tenant 1. Select the **Roles** tab. 2. Click **Assign role**. Search for a role ID and confirm. ![tenant-management-assign-roles-modal](./img/tenant-management-assign-roles-modal.png) 3. The role appears in the list after assignment. Refresh the page if needed. ![tenant-management-assigned-roles](./img/tenant-management-assigned-roles.png) ### Assign mapping rules to a tenant :::note Assignment of [mapping rules](../concepts/access-control/mapping-rules.md) is only available for [OIDC authentication in Self-Managed](../concepts/access-control/connect-to-identity-provider.md#self-managed). ::: 1. Select the **Mapping rules** tab. 2. Click **Assign mapping rule**. Search for a mapping rule ID and confirm. ![tenant-management-assign-mapping-rules-modal](./img/tenant-management-assign-mapping-rules-modal.png) 3. The mapping rule appears in the list after assignment. Refresh the page if needed. ![tenant-management-assigned-mapping-rules](./img/tenant-management-assigned-mapping-rules.png) ### Assign clients to a tenant 1. Select the **Clients** tab. 2. Click **Assign client**. Enter the client ID and confirm. ![tenant-management-assign-client-modal](./img/tenant-management-assign-client-modal.png) 3. The client appears in the list after assignment. Refresh the page if needed. ![tenant-management-assigned-clients](./img/tenant-management-assigned-clients.png) --- ## Users Users are individuals who are granted with access to an orchestration cluster and it's components like Operate, Tasklist and REST API. User management differs depending on whether you are using Camunda 8 SaaS or a Self-Managed installation. ## SaaS In a SaaS environment, user management is handled through [Camunda Hub](/components/hub/organization/manage-members/manage-users.md). From Camunda Hub, you can invite new users to your organization and manage their roles. For more advanced user management, you can configure [single sign-on (SSO)](/components/hub/organization/manage-organization-settings/external-sso.md) to integrate with your own identity provider. ## Self-Managed For Self-Managed deployments, user management depends on your authentication setup: - When using **Basic authentication**, users are managed through Admin. This involves creating, updating, and deleting them directly in your cluster. - If you have configured an external [OpenID Connect (OIDC) provider](/self-managed/components/orchestration-cluster/admin/connect-external-identity-provider.md), user management is handled by that provider. The following sections describe how to manage users in a Self-Managed environment with **Basic authentication** enabled. ### Create a user To create a user: 1. Log in to Admin in your cluster, and click on the **Users** tab. 2. Click on the **Create user** button, and provide the following user details: - **Username**: The username for the user. - **Name**: The name of the user. - **Email**: The email address of the user. - **Password**: The password for the user. 3. Click on the **Create user** button. The user is created, and can now log in to the Camunda 8 web applications. ![identity-create-user-tab](./img/create-user-tab.png) ### Update a user 1. Log in to Admin in your cluster, and click on the **Users** tab. 2. Click on the **pencil icon** next to the user you want to update. :::note You can also select the user, and click the three vertical dots > **Update**. ::: 3. Update the user details: - **Name**: The name of the user. - **Email**: The email address of the user. - **Password**: The password for the user. 4. Click on the **Save** button. The user details are updated, and the user can now use these credentials to log in. ![identity-update-user-tab](./img/update-user-tab.png) ### Delete a user 1. Log in to Admin in your cluster, and click on the **Users** tab. 2. Click on the **Delete** button next to the user you want to delete. :::note You can also select the user, and click the three vertical dots > **Delete**. ::: 3. Confirm the deletion by clicking on the **Delete** button in the confirmation dialog. The user is deleted, and can no longer log in to the Camunda 8 web applications. ### Assign authorizations to a user See the [authorization](./authorization.md) section to learn how to create authorizations for users. --- ## Benchmark Data Regarding maintenance of the by_models.csv file (in static\data\by_models.csv) that feeds into the component from the LiveBenchModelFilter file (in components\react-components\livebench-model-filter.js). # Benchmark Data This repo contains benchmark CSVs for LLMs. They are exported manually (copy-pasted) from: - [LiveBench](https://livebench.ai/#/) You can use the script file (in static\data\script\Clean_and_Extend_getlivebench_data.ipynb) to fetch the artificial analysis data faster. ## How to update 1. Open livebench site. 2. Copy-paste or download the CSV as provided. 3. fetch the input and output prices from the official source. 4. compute the 3 to 1 blended, 1 being input price and 3 the output. 5. fetch speed numbers from official website or open source and compare it with the number in the csv to place it next to a comparable 0 to 10 (integer) 6. Replace the old file in this repo. 7. Commit the change. ## Format - Keep exactly the same schema as exported. - Do not rename, reorder, or edit columns. - Leave missing values blank. - Commit the **raw CSV only** (no edits, no formatting). --- ## Agentic orchestration Orchestrate and integrate artificial intelligence (AI) agents into your end-to-end processes. Camunda agentic orchestration allows you to orchestrate AI agents within your BPMN-based workflows, enabling human tasks, deterministic rule sets, and AI-driven decisions to collaborate in a robust, end-to-end process. Agentic orchestration ensures your AI-driven processes are efficient, compliant, and aligned with business goals. Build and use AI agents to execute the non-deterministic parts of a process, integrated with the proven foundation of BPMN. ## Get started Get started with Camunda agentic orchestration by building and running your first AI agent. Build your first AI agent ## Learn the fundamentals Understand the fundamental concepts of Camunda agentic orchestration. ## Explore further resources Read about key capabilities and recommendations for using Camunda AI agents. --- ## AI agents Start building and integrating AI agents into your end-to-end processes. ## About AI agents An AI agent is a software program that autonomously gathers data and carries out tasks using this information, independently or on behalf of another system or person. - AI agents can perform a variety of functions, including making decisions, solving problems, interacting with external environments, and taking actions. - For example, you can use an AI agent to select and execute tasks within an ad-hoc sub-process, by evaluating the current process context and determining the relevant tasks and tools to use in response. ## Why tool documentation in ad-hoc sub-processes matters In an AI agent model, each BPMN activity inside an ad-hoc sub-process is effectively a tool exposed to the LLM. The activity name and its documentation are used by the LLM to decide what to do next. Clear, behavior-oriented descriptions help the LLM: - Select the right tool for the current goal. - Pass the right parameters in the expected format. - Avoid unsafe, redundant, or nonsensical actions. Poor or missing documentation increases the risk of: - Incorrect or ambiguous tool selection. - Repeated tool calls or skipped required steps. - Hallucinated behavior and responses that do not match process intent. ### Example: weak vs strong tool definition | Tool definition | Example | | :-------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Weak | **Name**: `Lookup`**Documentation**: `Find customer data` | | Strong | **Name**: `Resolve customer by legal company name`**Documentation**: `Use this tool when a document mentions a company and you need its internal customer ID. If multiple matches are returned, request human validation before continuing.` | A clear tool name and precise documentation make the expected behavior explicit, improving reliability during tool selection and execution. ## AI agent integration features Use the following Camunda 8 features to integrate AI agents into your processes: **Feature** **Description** [Ad-hoc sub-process](/components/modeler/bpmn/ad-hoc-subprocesses/ad-hoc-subprocesses.md) A special kind of embedded BPMN subprocess with an ad-hoc marker that allows a small part of your process decision-making to be handed over to a human or agent. [AI Agent connector](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md) Enables AI agents to integrate with an LLM to provide interaction/reasoning capabilities. This connector is designed for use with an ad-hoc sub-process in a feedback loop, providing automated user interaction and tool selection. [MCP Client connector](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-client.md) Connect an AI agent connector to tools exposed by [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) servers. [Ad-hoc tools schema resolver connector](/components/connectors/out-of-the-box-connectors/agentic-ai-ahsp-tools-schema-resolver.md) Can be used independently with other AI connectors for direct LLM interaction. Use this connector if you don’t want to use the AI agent connector but still want to resolve tools for an ad-hoc sub-process or debug tool definitions. [Vector database connector](/components/connectors/out-of-the-box-connectors/embeddings-vector-db.md) Allows embedding, storing, and retrieving LLM embeddings. Use this connector to build AI-based solutions such as context document search, long-term memory for LLMs, and agentic AI interaction. ## Integrate an AI agent into your process A common model for AI agent integration uses an ad-hoc sub-process and AI agent connector in a [tools feedback loop](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-subprocess-example.md). In this model, an AI agent is defined using an AI agent connector, with the tools available to the agent defined in an ad-hoc sub-process. The AI agent is able to understand the context and process goal, and uses the available tools to complete the goal. :::tip Learn more about this model in the [example AI Agent Sub-process connector integration](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-subprocess-example.md) and [guide to adding a tool for an AI agent](https://camunda.com/blog/2025/05/guide-to-adding-tool-ai-agent/). ::: :::info further resources Learn more about building and integrating AI agents in Camunda 8: - [Building Your First AI Agent in Camunda](https://camunda.com/blog/2025/05/step-by-step-guide-ai-task-agents-camunda/) - [Test your AI agents](/components/agentic-orchestration/evaluate-agents/test-ai-agents.md) - [Intelligent by Design: A Step-by-Step Guide to AI Task Agents in Camunda](https://camunda.com/blog/2025/05/step-by-step-guide-ai-task-agents-camunda/) - [Artificial Intelligence (AI) Agents: What You Need to Know](https://camunda.com/blog/2024/08/ai-agents-what-you-need-to-know/) - [Camunda AI agents](https://camunda.com/blog/tag/ai-agent/) ::: --- ## Camunda-provided LLM Run AI agents quickly in Camunda SaaS with Camunda-provided LLM. ## About Camunda-provided LLM is a Camunda-managed LLM provider option that comes with automatically configured credentials. With it, you can run AI agents in your processes right away without additional setup. Camunda-provided LLM is only available in Camunda 8 SaaS. It is not available in Camunda 8 Self-Managed. :::info Camunda-provided LLM is free to use within the provided budget, and is intended for testing and experimentation. When you're ready for production or need more control, switch to a customer-managed provider. ::: Camunda-provided LLM is available in Camunda SaaS for: - **SaaS trial organizations**: Includes Camunda-managed credentials and a free budget. - **SaaS enterprise organizations**: Includes a larger budget to support multiple proofs of concept. You must explicitly enable AI features. When you enable them, Camunda-provided LLM is enabled automatically. If Camunda-provided LLM is unavailable, disable AI features and then re-enable them. :::note Availability, budgets, and UI may vary by environment and rollout stage. ::: See [Trial vs. enterprise budgets](#trial-vs-enterprise-budgets) for more details. ## Set up Camunda-provided LLM Once Camunda-provided LLM is available in your organization, its credentials are populated automatically as cluster secrets. - If you are using an AI agent blueprint, no additional configuration is needed in most cases. Explore selected AI agent blueprints in the [Camunda Marketplace](https://marketplace.camunda.com/en-US/home). - If you are building your own agent from scratch, enable Camunda-provided LLM by configuring your [AI Agent connector](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md) with the following parameters: - **Provider**: `OpenAI Compatible`. - **API endpoint**: `{{secrets.CAMUNDA_PROVIDED_LLM_API_ENDPOINT}}`. - **API key**: `{{secrets.CAMUNDA_PROVIDED_LLM_API_KEY}}`. - **Model**: Select a model from the [list of supported models](#supported-models). For example `amazon.nova-pro-v1`. ## Supported models Camunda-provided LLM supports multiple Bedrock-backed models. When using the AI Agent connector, set the **Model** field to one of the following values: | Model | Value to set in **Model** | What it's good for | | :-------------------------- | :---------------------------- | :-------------------------------------------------------------------------------------------------------------------- | | Amazon Nova Pro v1 | `amazon.nova-pro-v1` | Best for balanced quality and cost across general-purpose AI agent scenarios. | | Anthropic Claude Haiku 4.5 | `anthropic.claude-haiku-4-5` | Best for lightweight assistants, short interactions, and lower-cost tasks that still need good instruction following. | | Anthropic Claude Opus 4.5 | `anthropic.claude-opus-4-5` | Best for advanced analysis and challenging multi-step tasks where maximum quality is the priority. | | Anthropic Claude Sonnet 4.6 | `anthropic.claude-sonnet-4-6` | Best for complex, high-stakes agent tasks where strong reasoning and reliable tool use matter most. | | DeepSeek v3.2 | `deepseek.v3.2` | Best for technical and coding-heavy workflows that need strong reasoning at moderate cost. | | OpenAI GPT-OSS 120B | `openai.gpt-oss-120b` | Best for higher-quality results than small open models while still controlling cost. | | OpenAI GPT-OSS 20B | `openai.gpt-oss-20b` | Best for budget-conscious experimentation and simpler automations with lower complexity. | | Qwen Qwen3 235B | `qwen.qwen3-235b` | Best for advanced reasoning and coding use cases where you want strong performance with good cost efficiency. | :::note When selecting a model, consider your process requirements, expected usage volume, and token budget. For model selection guidelines, see how to [choose the right LLM](./choose-right-model-agentic.md). ::: ## Trial vs. enterprise budgets The budgets, measured in **dollars (USD) spent**, differ depending on your SaaS plan: - **Trial**: A smaller budget intended for quick evaluation and early experiments by individuals and small teams. - **Enterprise**: A larger budget intended for broader team experimentation and proofs of concept. :::important Budgets are topped up automatically and enforced at the organization level (not per user). This means multiple users in the same organization draw from the same budget. ::: ### What the budget cover The Camunda-provided LLM budget covers LLM provider calls during AI agent execution: - **Trial budget**: Allows for a hundred to a few thousand agent runs, depending on the model used and the agent complexity. - **Enterprise budget**: Is significantly larger to support more extensive experimentation. Other Camunda AI features, such as Camunda Copilot, do not consume your Camunda-provided LLM budget and can be used independently. :::note The total cost of an agent run depends on how many LLM calls it makes, which can vary based on the agent’s design and task complexity. Cost also depends on the model used, since different models have different per-token pricing. ::: ### When budget is exhausted When your organization reaches its Camunda-provided LLM budget cap: - Additional LLM calls are **blocked**. - Your process execution may fail with an “out of budget” error, such as `COST_LIMIT_EXCEEDED`, depending on how your process handles errors. :::tip If your process model doesn’t handle LLM failures, an exhausted budget may result in incidents or failed instances. Consider adding BPMN error handling to provide a user-friendly fallback path. ::: ## Switch away from Camunda-provided LLM As you move from evaluation to production, you may want to switch to your own LLM provider. This gives you: - Direct control over provider choice. - Your own billing and quota management. - The ability to scale beyond the Camunda-provided LLM budget caps. :::important Before you begin - Ensure your organization has access to the LLM provider you plan to use. - Gather credentials and any required configuration. - Identify where your current AI agent models rely on Camunda-provided LLM defaults. ::: To switch away, follow these steps: 1. Add your LLM provider credentials in the appropriate Camunda location for managing secrets and credentials. 2. Update your AI Agent connector configuration to use the new LLM provider. 3. Re-deploy your process. 4. Test a process instance end-to-end and verify results. --- ## Choose the right LLM Choose the right Large Language Model (LLM) to ensure your AI agent reliably executes tasks in a Camunda process. This guide helps you evaluate and select the best LLMs based on your deployment requirements and business needs. It explains how to measure agent performance and shows how to leverage LiveBench’s standardized benchmarks to compare models effectively. ## Define your LLM needs Consider the following aspects regarding your model requirements and setup constraints: | Consideration | Description | | :------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Hosting** | Cloud-only vs. on-premises deployment. For compliance-heavy or air-gapped environments, self-hostable open-source models are preferred. | | **Data sensitivity** | Workflows handling Personally Identifiable Information (PII) or confidential data may require private deployments or self-hosting to meet data control requirements. | | **Cost vs. speed** | Larger models offer higher accuracy but often with higher latency and cost. Balance performance against Service Level Agreements (SLAs) and budgets. | | **Accuracy vs. openness** | Proprietary models often lead in benchmark accuracy. Open-source models provide flexibility, fine-tuning, and offline use cases. | ## Measure agent performance The ideal model should handle tools effectively, follow instructions consistently, and complete actions successfully. When evaluating models for your agentic process, focus on these three core capabilities: - **Tool usage:** How accurately does the model choose and use the right tools? Poor tool usage leads to failed tasks or wasted steps. - **Action completion:** Does the agent fully achieve the goals of each task without manual intervention? - **Instruction adherence:** Does the model follow your prompts, policies, and constraints as expected? ## Benchmark your candidate models Once you have defined your model needs, setup requirements, and peformance metrics, standardized benchmarks help you measure these aspects objectively. They use the same tasks and conditions for every model, enabling fair comparisons. ### Learn about LiveBench metrics One such benchmark is ​​[LiveBench](https://arxiv.org/abs/2406.19314), that evaluates LLMs across multiple skill areas. It avoids common pitfalls such as test data contamination or subjective scoring by using fresh tasks and objective ground-truth answers. Each LiveBench metric represents a core capability: | Metric | What it measures | When it matters | | :--------------------------------- | :------------------------------------------------------- | :------------------------------------------------ | | **Reasoning** | Logical thinking and stepwise problem-solving. | Strategic planning, multi-step workflows. | | **Math** | Numerical accuracy and quantitative reasoning. | Finance, analytics, reporting. | | **Coding** | Code generation and debugging. | Dev tools, automation scripts. | | **Data analysis** | Extracting insights from datasets, tables, or documents. | Research, reporting, content analysis. | | **Instruction following** | Compliance with formats, rules, and prompts. | Policy-driven workflows, SOP tasks. | | **Software engineering (agentic)** | Tool-assisted coding and autonomous dev work. | CI/CD, issue triage, automated PRs. | | **Language** | Context understanding, fluency, general knowledge. | Chatbots, documentation, natural language output. | Different models excel in different areas. For instance, a model might rank highly in reasoning but score average on coding tasks. Matching model strengths to your workflow requirements ensures better outcomes. ### Compare models with LiveBench Use the interactive tool below to generate a LiveBench benchmark comparison. It outputs a pre-filtered table based on your selected criteria. :::note - LiveBench benchmarks are used under a Creative Commons license. - [LiveBench rankings](https://livebench.ai/#/) update continuously. The table above shows the most recent evaluation results. - This tool is provided for illustration purposes only to help you choose the right LLM for your agentic processes. ::: ## Key takeaways Your model choice should align with: 1. **Practical constraints** like hosting, privacy, cost, and accuracy needs. 1. **Performance metrics** such as tool usage, action completion, and instruction adherence. 1. **LiveBench scores** for skills relevant to your workflow. :::important Model selection should always reflect your **use case**. For example, a math-heavy workflow may weight numerical accuracy higher, while a customer support bot might prioritize language skills and instruction-following. ::: A clear framework and benchmarked data helps you choose an LLM or foundation model to power your Camunda agent. ### Test with Camunda-provided LLM Camunda-provided LLM gives you access to [multiple models](./camunda-provided-llm.md#supported-models) for experimentation and evaluation, so you can test different options without setting up your own provider. :::important Camunda-provided LLM is only available in Camunda 8 SaaS. It is not available in Camunda 8 Self-Managed. ::: See [Camunda-provided LLM](./camunda-provided-llm.md) for more details. --- ## Design and architecture Plan and design your agentic orchestration solutions, and understand recommended architecture guidelines. ## Plan Follow these principles when planning your agentic orchestration solution: - **Problem first**: First, identify any problem you might have in a process, and only then determine whether an AI agent could help solve the problem. Do not use an AI agent where it is not really necessary, or just for the sake of it. - **Architect for composability**. Avoid becoming too dependant on a specific LLM model, for example by doing too much fine tuning. This allows you to more easily integrate newer LLM providers and models in the future that better suit your needs. - **Observability and governance**: Use [Operate](/components/operate/operate-introduction.md) and [Optimize](/components/optimize/what-is-optimize.md) for visibility into your agentic orchestration processes. ### Blend deterministic and dynamic orchestration Blending both deterministic and dynamic (AI-driven) process orchestration into your end-to-end processes allows you to take advantage of non-deterministic process orchestration without sacrificing predictability, customer experience, and compliance. For example, you could use an AI agent to enhance a Know Your Customer (KYC) process, where the AI agent: - Provides dynamic guidance and problem-solving to the person throughout the process. - Monitors for policy changes and dynamically changes the process execution in response. - Automatically adjusts risk level and takes action such as restricting account activity or dynamically adjusting spend limits. ### When to use deterministic or non-deterministic orchestration Agentic orchestration involves blending both deterministic and dynamic (AI-driven) process orchestration into your end-to-end processes. It is important to understand when to use each approach: |   | Deterministic | Dynamic | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Suitable for** | Clear, predictable paths.Repeatable, rules-based decisions.Fast, efficient execution.Regulation dictates execution sequence. | Flexible, context-aware choices.AI-driven task planning.Unstructured cases: classification, triage, or investigation.Adaptable to data and conditions. | | **Enabled by** | Advanced workflow patterns.Business/IT alignment on process and decision logic. | Dynamic task scheduling and tracking.Collaboration between AI and Human. | :::info To learn more about determining when and where to use AI agents within your automation strategy, download the [why agentic process orchestration belongs in your automation](https://page.camunda.com/wp-why-agentic-process-orchestration-belongs-in-your-automation-strategy) strategy guide. ::: ## Design and architecture Follow these principles when designing your agentic orchestration solution: - **Guardrail sandwich**: Apply guardrails in your process when using agents. For example, you could have one agent performing the task execution, with another agent following up to check the chain of thought and make sure every execution is compliant. If the execution is not compliant, route to a human for additional validation. - **Human-in-the-Loop escalation**: Provide an agent with an escalation path to a human - confidence levels are useful, but it is good to always provide deterministic outbreaks for agents. - **Prompt versioning**: Version every prompt, so you can revert to using a previous prompt when required. ### How execution works in an AI agent In Camunda agentic orchestration, decision-making and orchestration are intentionally split: - **LLM responsibility**: Interprets the system prompt, current user prompt, and available tool descriptions. It decides which tool to call, in what order, and with which parameters. - **Camunda responsibility**: Executes the selected BPMN activities, stores process state, applies retries and incident handling, and coordinates user tasks and other deterministic workflow logic. Think of the ad-hoc sub-process as a governed toolbox: - Each activity can be selected by the LLM as a tool. - Activities can be executed multiple times, in different orders, in parallel, or skipped. - The LLM chooses a path from the allowed options, while Camunda enforces process boundaries and execution reliability. This is a typical execution timeline: 1. A user submits a prompt. 1. The LLM evaluates the prompt together with the configured system prompt and available tool definitions. 1. The LLM chooses one or more tool calls. 1. Camunda activates and executes the corresponding BPMN activities. 1. Results are written to process variables and returned to the LLM context. 1. The loop repeats until the LLM returns a final response or the process routes to deterministic follow-up steps. ### Mixing agents with workflow patterns 1 **Process flow within tools**: Full BPMN control inside ad-hoc sub-processes for ultimate flexibility. 2 **Agents pivot instantly with external messages and timers**: During execution, agents can be influenced by events like external messages or timers, enabling on-the-fly adjustments. 3 **Event-driven agent reconfiguration**: Sub-workflows handle new data, guiding the next AI steps. 4 **Agents orchestrate sub-workflow**: A tool doesn't need to be a single tool - it can be a whole subprocess. 5 **Multi-agent orchestration**: Agents orchestrate other agents for streamlined, scalable solutions. --- ## Analyze your AI agents with Optimize Analyze and improve the performance of your AI agent executions using Optimize. ## About In this guide, you will: - Understand what data Optimize can analyze for AI agents. - Create reports and heatmaps, including token and tool usage. - Build a dashboard to track AI agent behavior over time. After completing this guide, you will be able to analyze AI agent executions in Optimize and build dashboards that track usage and performance trends. ## Prerequisites - You have access to [Optimize](/components/optimize/what-is-optimize.md). - You have deployed and run the [AI Agent Chat Quick Start](https://marketplace.camunda.com/en-US/apps/587865) model blueprint. This is needed for Optimize to fetch execution data to analyze. Consider running it using different prompts to trigger various AI agent tools. :::important This guide is a follow-up to [build your first AI agent](/guides/getting-started-agentic-orchestration.md), in which you use the same example AI agent process. We recommend completing that guide first. However, you can also apply this guide to other AI agent process implementations. ::: ## Step 1: Make your data available To make data available to Optimize, make sure it's scoped at the process level. If it's scoped to a lower level, for example, within a connector or tool-execution scope, extract it into process variables. :::important Optimize can only use variable data at the **process level**. ::: ### Example: Collect token usage In the AI Agent Chat Quick Start example, token usage data is not available at the process level, but in a nested scope. How you extract it depends on your AI agent implementation. The important aspect is that the target variables exist at the **process level** when the instance finishes. :::note Getting the token usage from the agent context only works with the AI Agent Sub-process when the **Include agent context** option in the **Response** section is enabled. ::: To surface this data, you can add a script task after the AI agent execution that copies the values into process variables as follows: 1. Add a [script task](/components/modeler/bpmn/script-tasks/script-tasks.md). 1. Configure its **Properties**: - Set **Name** to `Gather metrics` under the **General** section. - Select **FEEL expression** as **Implementation**. - Under **Script**: - Set **Result variable** to `tokenUsage`. - Set **FEEL expression** to `agent.context.metrics.tokenUsage`. - Under **Output mapping**, add two process variables: - `inputTokenUsage` with **Variable assignment value**: `agent.context.metrics.tokenUsage.inputTokenCount`. - `outputTokenUsage` with **Variable assignment value**: `agent.context.metrics.tokenUsage.outputTokenCount`. You should see something similar to the following: ## Step 2: Examine data in Optimize You can use Optimize reports and dashboards to examine data collected during process execution and identify areas for improvement in your AI agent processes. 1. Open Optimize. 1. Go to the **Dashboards** tab. 1. Select your AI agent process, **AI Agent Chat With Tools**, in the **Process dashboards and KPIs** section. 1. Verify that Optimize shows data for your executed process instances in the **Business Operations** section, including your AI agent process model diagram and other statistics below. 1. Explore the other metrics shown below in the **Business Reporting** and **Process Improvement** sections. See [getting started](/components/optimize/improve-processes-with-optimize.md) with Optimize for more details on using Optimize for business intelligence. ## Step 3: Create reports for token usage You can create reports for token usage across process instances and over time. 1. Go to the **Collections** tab. 1. Select **Report** from the **Create new** dropdown. 1. Select **AI Agent Chat With Tools** from the **Select one or more processes** dropdown. You can fetch data for all process model versions or customize it. 1. Choose a blank template. 1. Enable **Update preview automatically** to make it easier to see the report results as you configure it. 1. In the **Report setup** section, select **Variable** in the **View** option, and then select **tokenUsage**. 1. Click the pencil icon and select an aggregation that matches your goal. For example: - **Sum** to track total tokens across instances. - **Average** to track typical usage per process instance. 1. Save the report with a descriptive name. For example, **Token usage**. :::note You can create similar reports, targeting other goals and process variables, such as `inputTokenUsage` or `outputTokenUsage`. ::: ### Example: Set a target threshold If you have a token budget, you can set a target in the report. 1. Complete steps 1–6 in [Step 3: Create reports for token usage](#step-3-create-reports-for-token-usage). 1. In the **Visualization** settings, click the gear icon and enable **Set target** to configure a target value. For example, a maximum token usage threshold. 1. Set the target threshold to match your budget. For example, select the **below** option and set it to 10,000 tokens. 1. Save the report with a descriptive name. For example, **Token usage with threshold**. ## Step 4: Create reports for tool usage You can create reports for tool usage across process instances and over time. ### Example: Create a heatmap Use a heatmap to understand how long your AI agent spends in each task. 1. Go to the **Collections** tab. 1. Select **Report** from the **Create new** dropdown. 1. Select **Flow node** as the **View**. 1. Select **Duration** as the **Measure**. 1. (Optional) Filter the report by selecting **Flow node selection** in the **Filter flow nodes** dropdown. For example, select only tool tasks within the AI Agent connector. 1. In the **Visualization** settings, select **Heatmap**. Click the gear icon and enable the tooltip to show absolute values. 1. Save the report with a descriptive name. For example, **Tool usage heatmap**. You can see from the heatmap that the **Search recipe**, **Jokes API**, and **Get list of Tech Stuff** tools are the only ones that were called across all process executions, and that your AI agent spent the most time in **Get list of Tech Stuff**. ### Example: Create a report for tool call counts Create a bar chart to see how many times each tool is called. 1. Go to the **Collections** tab. 1. Select **Report** from the **Create new** dropdown. 1. Select **Flow node** as **View**. 1. Select **Count** as the **Measure**. 1. (Optional) Filter the report by selecting **Flow node selection** in the **Filter flow nodes** dropdown. For example, select only tool tasks within the AI Agent connector. 1. In the **Visualization** settings, select **Bar chart** or **Pie chart**. Then click the gear icon and enable both tooltips to show absolute and relative values. 1. Save the report with a descriptive name. For example, **Tool usage**. You can see from the pie chart that the AI agent called the Jokes API tool most often across all process executions. ### Example: Track trends over time Use a timeline report to analyze trends over time. For example, you can see how many times a tool is called per day over a one-week period. 1. Go to the **Collections** tab. 1. Select **Report** from the **Create new** dropdown. 1. Select **Flow node** as **View**. 1. Select **Count** as the **Measure**. 1. In **Group by**, select **Start date**. Then choose your preferred interval, for example, **Week**. 1. (Optional) Filter the report by selecting **Flow node selection** in the **Filter flow nodes** dropdown. For example, select only tool tasks within the AI Agent connector. 1. In the **Visualization** settings, select **Bar chart** or **Line chart**. Then click the gear icon and enable both tooltips to show absolute and relative values. 1. Save the report with a descriptive name. For example, **Tool usage over time**. ## Step 5: Build a dashboard You can create a dashboard, which is a collection of reports that you can view together, for your AI agent process. 1. Go to the **Collections** tab. 1. Select **Dashboard** from the **Create new** dropdown. 1. Click the plus icon to add tiles for the reports you created. 1. In the **Optimize report** section, add reports as needed. 1. Arrange the tiles to customize your dashboard layout. 1. Save the dashboard with a descriptive name. For example, **AI Agent Chat Quick Start dashboard**. ## Next steps Now that you know how to analyze your AI agents, you can: - [Monitor your AI agents](./monitor-ai-agents.md) with Operate. - Learn more about [Camunda agentic orchestration](/components/agentic-orchestration/agentic-orchestration-overview.md) and the [AI Agent connector](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md). --- ## Evaluate your AI agents Evaluate your AI agents by monitoring them in real time with Operate, improving performance over time with Optimize, and testing them with Camunda Process Test. --- ## Monitor your AI agents with Operate Monitor and troubleshoot your AI agent process instances in real time using Operate. ## About In this guide, you will: - Inspect an AI agent process instance in Operate. - Understand the agent's tool usage and metadata, such as tool call inputs and results. - Analyze the agent context and how it is stored. :::note Operate enables inspection of execution paths, tool usage, and agent metadata. However, certain runtime artifacts, such as document storage contents, may require additional configuration. ::: After completing this guide, you will be able to inspect, debug, and monitor AI agent executions in Camunda 8. ## Prerequisites - You have access to [Operate](/components/operate/operate-introduction.md). - You have the [AI Agent Chat Quick Start](https://marketplace.camunda.com/en-US/apps/587865) model blueprint deployed in [Modeler](/components/modeler/about-modeler.md). :::important This guide is a follow-up to [build your first AI agent](/guides/getting-started-agentic-orchestration.md), where you use the same example AI agent process. We recommend completing that guide first. However, you can also apply this guide to other AI agent process implementations. ::: ## Step 1: Run your AI agent process Run your process instance using a prompt to trigger the AI Agent connector. For example: 1. Enter "Tell me a joke" in the **How can I help you today?** field. 1. Click **Start instance**. ## Step 2: Open the process instance in Operate 1. Open [Operate](/components/operate/operate-introduction.md). 2. Locate the process instance created by your prompt. See [view a deployed process](/components/operate/userguide/basic-operate-navigation.md#view-a-deployed-process) for more details. 3. Open your process instance view by clicking on its process instance key. At this point, you should see the process progressing through your model: ## Step 3: Understand what Operate shows With Operate, you can track the agent activity and see which tool tasks are called. 1. To show how many times each BPMN element is triggered, select **Execution count** in the **Instance History** section. For this particular prompt example, you can see: - The AI Agent connector was triggered once. - Within it, the agent executed the **Jokes API** tool. 2. Select the **Jokes API** tool element: - In the bottom-left pane, you can see where the element belongs in the execution tree: - In the bottom-right pane, the element details are displayed, including the [**Variables**](/components/concepts/variables.md) and [**Input/Output Mappings**](/components/concepts/variables.md#inputoutput-variable-mappings) columns, among others. However, the actual tool inputs and results are stored in a **parent scope** and are accessible via the element's inner instance in the execution tree. See [Step 4: Inspect tool calls](#step-4-inspect-tool-calls) for more details. ## Step 4: Inspect tool calls Each tool execution produces an inner instance where you can find: - The inputs passed into the tool. - The results. To see the **Jokes API** tool input and results: 1. In the execution tree, select the **AI_Agent#innerInstance** parent element of the **Jokes API** tool. You will see: - The `toolCall` variable (the _input_). - The `toolCallResult` variable (the _results_). See [Tool call responses](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-tool-definitions.md#tool-call-responses) for more details. 2. To better inspect the results, click the pencil icon to enter edit mode for `toolCallResult`. 3. Click the two-arrow icon to open the JSON editor modal. With this, you can inspect the full payload of the variable value: ```json { "Java and C were telling jokes. It was C's turn, so he writes something on the wall, points to it and says \"Do you get the reference?\" But Java didn't." } ``` :::note If a tool is executed more than once, select the desired tool invocation in **Instance History**, then open the corresponding inner instance to view the actual inputs and results. ::: ## Step 5: Analyze the agent context Within the AI Agent connector, you can examine the agent context. To view it: 1. Select the **AI Agent** element in the execution tree. 2. To better inspect the value, click the pencil icon to enter edit mode for the `agentContext` variable. 3. Click the two-arrow icon to open the JSON editor modal. With this, you can inspect the full payload of the variable value. In the JSON payload, you can find information about: - Defined tools. - The conversation, including your prompts and agent's replies. - Tool calls invoked by the agent. - Tool call inputs and results. - Additional metadata, such as reasoning traces and token usage. Here’s a snippet of the example conversation stored in the agent’s context: ```json "type": "in-process", "conversationId": "3889288d-5904-485f-bdca-48ad1f1ef679", "messages": [ { "role": "system", "content": [ { "type": "text", "text": "You are a helpful, generic chat agent which can answer a wide amount of questions based on your knowledge and an optional set of available tools.\n\nIf tools are provided, you should prefer them instead of guessing an answer. You can call the same tool multiple times by providing different input values. Don't guess any tools which were not explicitely configured. If no tool matches the request, try to generate an answer. If you're not able to find a good answer, return with a message stating why you're not able to.\n\nIf you are prompted to interact with a person, never guess contact details, but use available user/person lookup tools instead and return with an error if you're not able to look up appropriate data.\n\nThinking, step by step, before you execute your tools, you think using the template ``" } ] }, { "role": "user", "content": [ { "type": "text", "text": "Tell me a joke" } ], "metadata": { "timestamp": "2026-04-06T09:53:19.224987296Z" } }, { "role": "assistant", "content": [ { "type": "text", "text": "\n\nThe user is asking for a joke. I have access to a Jokes_API function that can fetch a random joke from a REST API. This seems like the perfect tool to use for this request. The function doesn't require any parameters, so I can call it directly.\n\n\nThis is a straightforward request that matches exactly with one of my available tools. I should use the Jokes_API function to get a random joke for the user.\n\n" } ], "toolCalls": [ { "id": "tooluse_x83f1Vaj62lgkT9PMo6oqB", "name": "Jokes_API", "arguments": {} } ], ``` ## Step 6: Understand how agent memory is stored In Modeler, within the AI Agent sub-process, you can define how the conversation memory is stored using the **Memory storage type** field. By default, agent memory uses the **In Process** type, which stores it as part of the agent context. With this option, you can view it in Operate within the agent context, as you did in the previous step, [Analyze the agent context](#step-5-analyze-the-agent-context). With the **Camunda Document Storage** option instead: - You can't view the full conversation and chain-of-thought traces in Operate. Operate only shows a **document reference** and metadata. - Use this option for long conversations, where Operate variable limits might be exceeded. See [memory](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-subprocess.md#memory) for more details. :::note Agent memory storage - Use **In Process** for testing and debugging scenarios: Better visibility in Operate. - Use **Camunda Document Storage** for production scenarios: Better scalability and runtime behavior for long contexts. ::: ## Step 7: Review the results Go back to Operate. In the **User Feedback** element, you will see the execution count in green. This means the process instance execution is stopped there and waiting for action. In this case, the required action is to provide feedback on the agent results. To do so: 1. Select the **User Feedback** element. 2. Open [Tasklist](/components/tasklist/introduction-to-tasklist.md). 3. Select the user feedback task and assign to yourself by clicking **Assign to me**. 4. Analyze the result. You will see a joke, as requested in the prompt. 5. You can follow up with more prompts to continue testing your AI agent. 6. Select the **Are you satisfied with the result?** checkbox when you want to finish the process, then click **Complete task**. 7. Go back to Operate. You will see the process instance is now completed, and the end event has been triggered. ## Next steps Now that you know how to monitor your AI agents, you can: - [Analyze your AI agents](./analyze-ai-agents.md) with Optimize. - [Test your AI agents](./test-ai-agents.md) with Camunda Process Test, including handling non-deterministic flows and verifying AI-generated output. - Learn more about [Camunda agentic orchestration](/components/agentic-orchestration/agentic-orchestration-overview.md) and the [AI Agent connector](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md). --- ## Test your AI agents with CPT Test your AI agent processes in Camunda 8 with [Camunda Process Test (CPT)](/apis-tools/testing/getting-started.md). ## About AI agent processes are non-deterministic: the [AI Agent connector](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md) inside an [ad-hoc sub-process](/components/modeler/bpmn/ad-hoc-subprocesses/ad-hoc-subprocesses.md) decides at runtime which tools to invoke and in what order, and its free-text output varies across runs. In this guide, you will build integration tests that keep the AI agent and LLM interaction real while mocking external tool executions, using the following CPT features: - [Conditional behavior](/apis-tools/testing/utilities.md#conditional-behavior): Reacts to whichever tasks the agent activates, instead of blocking on a single hard-coded execution order. This addresses non-deterministic control flow. - [Judge](/apis-tools/testing/assertions.md#hasvariablesatisfiesjudge) and [semantic similarity assertions](/apis-tools/testing/assertions.md#hasvariablesimilarto): Verify AI-generated output. After completing this guide, you will be able to test your AI agents using CPT. ## Prerequisites - You use Camunda 8.9+. - You use the Camunda Process Test Spring Boot Starter. - You have [Camunda Process Test set up](/apis-tools/testing/getting-started.md). - You have downloaded the [AI Agent Chat With Tools](https://marketplace.camunda.com/en-US/apps/587865) process to your local machine. :::important This guide is a follow-up to [build your first AI agent](/guides/getting-started-agentic-orchestration.md), in which you use the same example AI agent process. We recommend completing that guide first. However, you can also apply this guide to other AI agent process implementations. ::: ## Step 1: Prepare the example AI agent blueprint Place the BPMN file and any associated forms for your AI agent process in the `src/main/resources` directory of your Spring Boot project. Create it if it does not already exist. You can organize files into subdirectories such as `bpmn/` and `forms/`. ## Step 2: Configure the LLM provider and connectors Judge assertions send a process variable and a natural language expectation to a configured LLM, which scores how well they match. The assertion passes if the score meets a configurable threshold. This avoids brittle string-matching on free-text AI output. For this testing style, first configure both the connector runtime and the judge LLM. The goal is to keep the AI agent and LLM interaction real while disabling outbound connector execution for the tool calls you want to control in the test. ### Configure the connector runtime Add the following connector runtime configuration to your test configuration, for example in `src/test/resources/application.yaml` or as inline properties on `@SpringBootTest`. For the full property reference, see the [CPT configuration docs](/apis-tools/testing/configuration.md). ```yaml camunda: process-test: assertion: timeout: PT1M connectors-enabled: true connectors-env-vars: CAMUNDA_CONNECTOR_POLLING_ENABLED: "false" CONNECTOR_OUTBOUND_DISCOVERY_DISABLED: "true" CONNECTOR_OUTBOUND_DISABLED: "io.camunda:http-json:1" ``` With this setup: - The assertion timeout is increased to one minute. AI agent processes involve LLM interactions and typically take longer than standard BPMN processes. - CPT starts the connector runtime needed by the AI agent process. - Outbound connector executions, such as the HTTP JSON connector, are disabled so tool behavior can be controlled by the test with conditional behavior. If your AI agent tools use different outbound connectors, adjust `CONNECTOR_OUTBOUND_DISABLED` accordingly. ### Configure the LLM provider Configure the LLM provider for the judge. The judge does not need the same provider or model as your AI agent. A lighter model often works well since the judge context is much smaller. ```yaml camunda: process-test: connectors-secrets: AWS_BEDROCK_ACCESS_KEY: ${AWS_LLM_BEDROCK_ACCESS_KEY} AWS_BEDROCK_SECRET_KEY: ${AWS_LLM_BEDROCK_SECRET_KEY} judge: chat-model: provider: "amazon-bedrock" model: "eu.anthropic.claude-haiku-4-5-20251001-v1:0" region: "eu-central-1" credentials: access-key: ${AWS_LLM_BEDROCK_ACCESS_KEY} secret-key: ${AWS_LLM_BEDROCK_SECRET_KEY} ``` Use this provider for [Ollama](https://ollama.com/). ```yaml camunda: process-test: judge: chat-model: provider: "openai-compatible" model: "gpt-oss:20b" base-url: "http://localhost:11434/v1" ``` :::tip Manage secrets safely Avoid committing credentials to your test configuration files. CPT properties support [Spring's external configuration](https://docs.spring.io/spring-boot/reference/features/external-config.html), so you can inject secrets through environment variables, CI/CD secret stores, or other techniques. See the [CPT configuration reference](/apis-tools/testing/configuration.md) for details. ::: The AI agent can still interact with the configured LLM provider, while the test controls the tool executions. For the full property reference, see [judge configuration](/apis-tools/testing/configuration.md#judge-configuration). ## Step 3: Set up the test class Add the `@Deployment` annotation to your Spring Boot application class to declare which resources CPT should deploy: ```java @SpringBootApplication @Deployment(resources = {"classpath*:/bpmn/**/*.bpmn", "classpath*:/forms/**/*.form"}) public class MyApplication {} ``` Then create a test class annotated with `@SpringBootTest` and `@CamundaSpringProcessTest`, and inject the `CamundaClient` and `CamundaProcessTestContext`: ```java @SpringBootTest(classes = MyApplication.class) @CamundaSpringProcessTest class AiAgentProcessTest { @Autowired private CamundaClient client; @Autowired private CamundaProcessTestContext processTestContext; } ``` For the full setup including dependencies and project structure, see [Getting started with Camunda Process Test](/apis-tools/testing/getting-started.md). ## Step 4: Handle non-deterministic flow paths The test uses the prompt `"Send Ervin a joke"`. In response, the agent: - Calls `ListUsers`, `LoadUserByID`, and `Jokes_API` in any order. - Presents an email for review via `AskHumanToSendEmail`. - Collects feedback through `User_Feedback`. With [conditional behavior](/apis-tools/testing/utilities.md#conditional-behavior), you can register background reactions that monitor the process state and execute actions as conditions are met, without blocking the test thread. Register behaviors before starting the process; they then react independently as the process progresses. Each behavior watches for a specific element to become active and then completes it with test data. If the agent never activates that element, the behavior simply never triggers and the test does not stall. ### Complete tool tasks Register a behavior for each tool task the agent might invoke. In this integration test, these behaviors stand in for external tool executions such as REST connector calls. First, define records for the tool call results: ```java record User(int id, String name, String username) {} record UserDetail(int id, String name, String username, String email) {} ``` Register a behavior that completes the `ListUsers` tool with a mock user list when the agent invokes it: ```java processTestContext .when( () -> assertThatProcessInstance(ProcessInstanceSelectors.byProcessId("ai-agent-chat-with-tools")) .hasActiveElements("ListUsers")) .as("complete ListUsers") .then( () -> processTestContext.completeJob( JobSelectors.byElementId("ListUsers"), Map.of("toolCallResult", List.of( new User(1, "Leanne Graham", "Bret"), new User(2, "Ervin Howell", "Antonette"))))); ``` Register a behavior that completes the `LoadUserByID` tool with Ervin's details: ```java processTestContext .when( () -> assertThatProcessInstance(ProcessInstanceSelectors.byProcessId("ai-agent-chat-with-tools")) .hasActiveElements("LoadUserByID")) .as("complete LoadUserByID") .then( () -> processTestContext.completeJob( JobSelectors.byElementId("LoadUserByID"), Map.of("toolCallResult", new UserDetail(2, "Ervin Howell", "Antonette", "123@abc.local")))); ``` Register a behavior that completes the `Jokes_API` tool. This behavior uses chained `.then()` calls to return different jokes on repeated invocations: ```java String firstJoke = "Why did the workflow cross the road? To get to the happy path."; String secondJoke = "Why did the BPMN diagram apply for a job? It had excellent flow experience."; processTestContext .when( () -> assertThatProcessInstance(ProcessInstanceSelectors.byProcessId("ai-agent-chat-with-tools")) .hasActiveElements("Jokes_API")) .as("complete jokes tool") .then( () -> processTestContext.completeJob( byElementId("Jokes_API"), Map.of("toolCallResult", firstJoke))) .then( () -> processTestContext.completeJob( byElementId("Jokes_API"), Map.of("toolCallResult", secondJoke))); ``` ### Complete user tasks The `AskHumanToSendEmail` user task requires human approval. Register a behavior that auto-approves the email when the task appears: ```java processTestContext .when( () -> assertThatProcessInstance(ProcessInstanceSelectors.byProcessId("ai-agent-chat-with-tools")) .hasActiveElements("AskHumanToSendEmail")) .as("approve email") .then( () -> processTestContext.completeUserTask( "AskHumanToSendEmail", Map.of("emailOk", true))); ``` :::important Each behavior's action should resolve the process state that the condition checks for. For example, if the condition checks for an active user task, the action should complete that task. Otherwise the behavior may execute repeatedly. ::: ### Handle repeated invocations Use chained `.then()` calls when a behavior should produce different results on repeated invocations. The first action is consumed on the first invocation, and the last action repeats for all subsequent invocations. In this example, the first feedback rejection sends the agent back with a follow-up request, and the second feedback loop approves the result: ```java processTestContext .when( () -> assertThatProcessInstance(ProcessInstanceSelectors.byProcessId("ai-agent-chat-with-tools")) .hasActiveElements("User_Feedback")) .as("feedback loop") .then( () -> processTestContext.completeUserTask( "User_Feedback", Map.of( "userSatisfied", false, "followUpInput", "This joke is bad, send Ervin a better joke"))) .then( () -> processTestContext.completeUserTask( "User_Feedback", Map.of("userSatisfied", true))); ``` For the full conditional behavior API, see [Utilities](/apis-tools/testing/utilities.md#conditional-behavior). ## Step 5: Verify the agent output You can use two types of assertions to verify the agent output: - **[Judge assertions](/apis-tools/testing/assertions.md#hasvariablesatisfiesjudge)** verify AI-generated output or tool execution results with a judge LLM that scores whether a value satisfies a natural-language expectation. - **[Semantic similarity assertions](/apis-tools/testing/assertions.md#hasvariablesimilarto)** verify AI-generated output against a reference text using embeddings and cosine similarity. They are a deterministic, lower-cost alternative to judge assertions. ### When to use judge vs. similarity | Assertion | Best for | Cost | | ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | | [Judge](#verify-with-judge-assertions) | Open-ended natural-language criteria, multi-part expectations, structured data, anything that needs reasoning. | One extra LLM call per assertion. Score and explanation depend on the configured judge model. | | [Semantic similarity](#verify-with-semantic-similarity) | Checks where a concrete reference text is close to a variable's actual content. Deterministic and fast. | One embedding call per value. No reasoning step, so it cannot evaluate criteria that aren't expressed in the wording. | :::tip - Use judge assertions when it feels natural to express the expectation in natural language. - Use similarity assertions when the expected answer is itself a sample string. ::: #### Limitations - Both judge and semantic similarity assertions operate on the **serialized JSON string** of a process variable. Neither can evaluate non-textual content such as [Camunda documents](/components/document-handling/getting-started.md) or other embedded binaries. In those cases, only metadata or encoded strings reach the assertion. - Semantic similarity assertions compare the serialized variable against the expected string using a vector space. Highly structured variables, such as JSON objects with many fields, may score lower than expected even when the semantic meaning matches. ### Verify with judge assertions Use a judge assertion to verify the agent output satisfies a natural language expectation. The following example registers the conditional behaviors from [Step 4](#step-4-handle-non-deterministic-flow-paths), starts the process with the prompt `"Send Ervin a joke"`, and then asserts that the agent completed the scenario correctly: ```java @Test void shouldSendErvinAJoke() { ProcessInstanceEvent processInstance = client.newCreateInstanceCommand() .bpmnProcessId("ai-agent-chat-with-tools") .latestVersion() .variables(Map.of("inputText", "Send Ervin a joke")) .send() .join(); assertThat(processInstance).isCompleted(); assertThat(processInstance) .hasVariableSatisfiesJudge( "agent", """ The agent correctly identified Ervin by calling the following tools: 1. ListUsers 2. LoadUserByID with id=2. Furthermore, the agent called AskHumanToSendEmail and the email should have been sent successfully! The mail must contain a joke. After the user rejected the first joke and asked for another one, the agent offered a second, different joke. """); } ``` The expectation is a plain-text description of what the agent should have done. The judge does not compare strings literally. It evaluates whether the actual variable content satisfies the expectation semantically, so different phrasing or formatting in the agent's output does not cause false failures. The judge evaluates matches using the following scoring scale: | Score | Meaning | | ----- | ---------------------------------------------------------------------------------------------------------------------- | | 1.0 | Fully satisfied semantically. Different wording or formatting that conveys the same meaning counts as fully satisfied. | | 0.75 | Satisfied in substance with only minor differences that do not affect correctness. | | 0.5 | Partially satisfied. Some required elements are present but others are missing or incorrect. | | 0.25 | Mostly not satisfied. Only marginal relevance. | | 0.0 | Not satisfied at all, or the actual value is empty. | The LLM may return any value between these anchor points (for example, 0.6 or 0.85). The default threshold is 0.5. This means the assertion passes when the response is at least partially satisfied according to the rubric, which is a practical default for AI-generated output that may vary in wording or completeness across runs. Use a higher threshold when the response must satisfy stricter semantic requirements. You can change the threshold globally in the [judge configuration](/apis-tools/testing/configuration.md#judge-configuration) or per assertion using `withJudgeConfig`. If the assertion fails, for example because the agent never called `LoadUserByID` or sent the email to the wrong address, the judge returns a low score with an explanation of which parts of the expectation were not met. This gives you a clear, human-readable failure message instead of a generic assertion error. #### Tune the judge evaluation Use `withJudgeConfig` to set a stricter threshold for individual assertions: ```java assertThat(processInstance) .withJudgeConfig(config -> config.withThreshold(0.8)) .hasVariableSatisfiesJudge( "agent", "The email body contains a joke addressed to Ervin."); ``` You can also replace the default evaluation criteria with a custom prompt. The custom prompt replaces only the evaluation criteria. The system still controls the expectation and value injection, the scoring rubric, and the JSON output format. Set a custom prompt globally in configuration: ```yaml camunda: process-test: judge: custom-prompt: "You are evaluating whether an AI agent correctly identified the intended recipient, used the right tools, and produced an appropriate email response." ``` Or override the prompt for a single assertion: ```java assertThat(processInstance) .withJudgeConfig(config -> config .withCustomPrompt("You are evaluating whether an AI agent correctly identified the intended recipient, used the right tools, and produced an appropriate email response.")) .hasVariableSatisfiesJudge("agent", "The email body contains a joke addressed to Ervin."); ``` For the full assertion API, see [Assertions](/apis-tools/testing/assertions.md#hasvariablesatisfiesjudge). ### Verify with semantic similarity assertions Use a semantic similarity assertion to verify the agent output. Semantic similarity assertions are a deterministic, lower-cost alternative to [judge assertions](#step-5-verify-with-judge-assertions). Instead of calling a judge LLM at assertion time, they convert both the actual variable value and the expected text to vector embeddings and compare them using cosine similarity. They work best when you can express the expected result as a concrete sample string. ### Configure the embedding model The embedding model does not need to match the AI agent's LLM or the judge model. Depending on your requirements, a lightweight model is often good enough for a good test result. Add the embedding model configuration to your test configuration alongside the CPT settings from [Step 2](#step-2-configure-the-llm-provider-and-connectors): ```yaml camunda: process-test: similarity: embedding-model: provider: "amazon-bedrock" model: "amazon.titan-embed-text-v2:0" region: "eu-central-1" dimensions: 256 credentials: access-key: ${AWS_LLM_BEDROCK_ACCESS_KEY} secret-key: ${AWS_LLM_BEDROCK_SECRET_KEY} ``` Use this provider for [Ollama](https://ollama.com/). ```yaml camunda: process-test: similarity: embedding-model: provider: "openai-compatible" model: "" base-url: "http://localhost:11434/v1" ``` For the full property reference, see [semantic similarity configuration](/apis-tools/testing/configuration.md#semantic-similarity-configuration). ### Add a similarity assertion With the embedding model configured, use `hasLocalVariableSimilarTo` as a complementary check on the `email_body` variable of the `AskHumanToSendEmail` task instance: ```java assertThat(processInstance) .hasLocalVariableSimilarTo( "AskHumanToSendEmail", "email_body", """ Hey Ervin! Here is a joke for you: Why did the workflow cross the road? To get to the happy path. """); ``` The assertion converts both strings to embeddings, applies the default text preprocessors (lowercase, Unicode NFC, and whitespace normalization), and compares cosine similarity against the default threshold of 0.5. Override the minimal success threshold for a single assertion if you require a higher precision for some assertions: ```java assertThat(processInstance) .withSemanticSimilarityConfig(config -> config.withThreshold(0.8)) .hasLocalVariableSimilarTo( "AskHumanToSendEmail", "email_body", """ Hey Ervin! Here is a joke for you: Why did the workflow cross the road? To get to the happy path. """); ``` ## Next steps Now that you know how to test your AI agents, you can: - Learn more about [Camunda agentic orchestration](/components/agentic-orchestration/agentic-orchestration-overview.md) and the [AI Agent connector](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md). - Dive into [Camunda Process Test assertions](/apis-tools/testing/assertions.md). - Review [judge](/apis-tools/testing/configuration.md#judge-configuration) and [semantic similarity](/apis-tools/testing/configuration.md#semantic-similarity-configuration) configurations for the full property references. - Explore [conditional behavior](/apis-tools/testing/utilities.md#conditional-behavior), including chained actions and lifecycle details. --- ## Expose a process as an MCP tool Expose a BPMN process as a callable [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) tool so that AI agents and LLM-powered applications can discover and invoke it. ## About You can configure a BPMN process as a callable MCP tool through the [Processes MCP Server](/apis-tools/processes-mcp/processes-mcp-overview.md). It is built into the Orchestration Cluster and automatically registers processes as MCP tools when they are deployed with the [MCP start event element template](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-start-event.md). ## Prerequisites - Access to [Web Modeler](/components/hub/workspace/modeler/launch-modeler.md) or [Desktop Modeler](/components/modeler/desktop-modeler/install-the-modeler.md). - An [Orchestration Cluster](/components/orchestration-cluster.md) running Camunda 8.10 or later. ## Step 1: Add an MCP start event to your process The [MCP start event element template](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-start-event.md) is an element template that you apply to a BPMN message start event. When deployed, it registers the process as an MCP tool. 1. Open your BPMN process in Modeler. 2. Select the start event (or add a new one). 3. In the properties panel, click the element template picker and select **MCP start event** from the **AI Tools** category. ![A BPMN message start event in Web Modeler with the MCP start event element template applied, showing the properties panel](img/mcp-start-event-modeler.png) ## Step 2: Configure the MCP tool metadata The properties you fill in become the MCP tool's metadata, which AI agents and LLMs use to decide when and how to call your process. Define them in clear and concise language. Vague or incomplete metadata leads to incorrect tool selection or missing arguments. | Property | Required | Description | | :------------------------ | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Name** | Yes | The MCP tool identifier used by clients to call this process. Alphanumeric characters, hyphens (`-`), underscores (`_`), and dots (`.`) **only**. Maximum 100 characters. | | **What it does** | Yes | A plain-language description of the process function, shown to LLMs as tool metadata. | | **Which inputs it needs** | Yes | A plain-language description of required and optional input parameters, their types, and any constraints. | | **When to use** | No | Specific situations or user intents that should trigger this tool. | | **When not to use** | No | Conditions or situations where this tool should not be invoked. | | **What the tool returns** | No | The outcomes, results, and variable names the process produces on completion. | ## Step 3: Design the process execution When an MCP client calls the tool: 1. The Processes MCP Server starts a new process instance with the tool call arguments mapped as process variables. 2. The server immediately returns the started process instance key to the MCP client. You can map the incoming tool call arguments from the LLM to the process variables your process expects using the **Output mapping** property. If you don't define any explicit output mapping, all incoming tool call arguments become process variables with the same names. ## Step 4: Deploy the process Deploy the process to your Orchestration Cluster. After deployment, the Processes MCP Server automatically registers the process as an MCP tool using the metadata you configured. :::important Version binding Only the latest deployed version of a process is exposed as an MCP tool. If you redeploy the process with a changed interface, existing MCP clients holding a cached reference to the old tool will receive a stale-tool error and must re-fetch the tool list. See [version binding](/apis-tools/processes-mcp/processes-mcp-version-binding.md) for more details. ::: ## Step 5: Connect an MCP client Connect any MCP-compliant client to the Processes MCP Server. See [Enable and connect](/apis-tools/processes-mcp/processes-mcp-setup.md) for endpoint URLs, authentication options, and other configuration details. ## Step 6: Verify After deployment, you can verify that your process is registered as an MCP tool in the Orchestration Cluster admin UI. See [MCP processes](/self-managed/components/orchestration-cluster/admin/mcp-processes.md) to learn how. --- ## LLM recommendations Recommendations and best practices for working with Large Language Models (LLMs) and effective prompts. ## General model requirements To implement an agentic process, you must choose a model that meets certain baseline requirements. These include: ### Tool calling support The model should be able to invoke external tools and work with work with tool-calling mechanisms, as part of its output. If a model cannot call tools, it won’t be suitable for an agentic workflow. ### Vendor compatibility The model must be available through at least one supported vendor or API. In practice, this means using a model from AWS Bedrock, Google Vertex AI, Azure OpenAI, OpenAI (or any platform compatible with the OpenAI API). Choosing a model from these ecosystems ensures it will integrate with Camunda’s connectors and the agentic orchestration framework. ### Plain text I/O The model should accept and return plain text. Agentic processes rely on text prompts and text-based replies (which may include JSON or other structured text). Avoid models that only produce non-text outputs or require special input formats. _Text in, text out_ is essential for simplicity and reliable tool integration. ## General recommendations for agentic processes As well as choosing the right model, you should follow best practices in designing your agentic process. These recommendations help your AI agent work effectively and safely within a workflow. ### Use detailed tool descriptions When defining tools for the agent to use be very specific about what each tool does and what input it expects: - The more context you give the AI about the tool’s purpose, the more accurately it will use that tool. - Write clear, instructive descriptions for each tool call. You can do so via [`fromAi` expressions](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-tool-definitions.md#ai-generated-parameters-via-fromai) in Camunda. For example, `fromAi(toolCall.emailBody, "Body of the email to be sent")`. ### Mind the context window Consider the model’s context size when designing your agent’s interactions: - For example, if your model has a 4k token limit, plan your prompt and tool usage so you don’t exceed that. Only include relevant information in the prompt and trim any unnecessary details. - When defining tools’ input and output, anticipate how large those could be. A large chunk of text returned from a tool can quickly consume the context window, leaving little room for the model’s reasoning or response. ### Avoid overfilling the prompt with tool output Be cautious that tool responses don’t unintentionally fill the entire context. - If a tool returns very large data, consider post-processing it before feeding it back into the model. - For example, you might take only a summary of a document rather than the full text. This prevents the model’s next prompt from being dominated by irrelevant or excessive content, which can degrade performance and increase cost. ### Sanitize tool outputs Sanitizing ensures the agent doesn’t accidentally get confused or manipulated by malformed tool data, and also reduces the risk of prompt injections coming from external tool results: - Always clean and validate the output from tools before the AI agent uses it in a prompt, by removing any irrelevant, sensitive, or potentially prompt-breaking content. This is important for both security and prompt clarity. - For example, if a web search tool returns HTML or script tags, strip those out or convert them to plain text. ### Account for limited memory and long processes Agentic workflows can be long-running. The AI model won’t “remember” everything forever, as its memory is essentially the prompt history within the context window. - If your process spans multiple steps or lengthy pauses, store important information outside the model’s short-term memory. Use Camunda’s document storage or your own persistent storage layer, such as a database, to save key data between steps. - For example, if the agent gathers info in an early step that’s needed much later, persist that info so it can be reloaded into the prompt when required. This way, the agent can retrieve past knowledge without relying on an ever-growing prompt history. :::note An agentic process might pause or wait for events, causing the context to reset between runs. By saving state to a database or Camunda document storage, you ensure nothing vital is lost when the process continues. Think of it as the agent’s long-term memory—use it for any details the agent might need beyond the current prompt. ::: ### Incorporate human feedback when appropriate Consider adding a “human in the loop” as one of the agent’s tools. In practice, this could be a special tool, such as `ask_human` or a review task, that the agent can invoke to get confirmation or guidance from a user. - This is especially useful for high-stakes decisions or if the AI is unsure how to proceed. Designing your process with a human feedback option means the agent can defer to a person instead of guessing. - For example, the workflow might include a step where an employee reviews the AI’s draft output or where the AI explicitly asks the user to clarify an ambiguous request. This supervision loop can greatly improve the quality and safety of the agent’s actions. ## Prompting recommendations Constructing effective prompts is critical for guiding the model in an agentic process. Keep the following guidelines in mind. ### Leverage vendor-specific best practices Each model has recommended prompting techniques. Refer to the official documentation for each model: - [Anthropic Claude](https://docs.anthropic.com/claude/docs) - [OpenAI GPT / reasoning models](https://platform.openai.com/docs/guides/prompt-engineering) - [Google Gemini](https://cloud.google.com/vertex-ai/generative-ai/docs/models) - [Cohere Command-R](https://docs.cohere.com/docs/the-cohere-platform) - [Meta Llama](https://llama.meta.com/docs/) - [Mistral / Mixtral / Codestral](https://docs.mistral.ai/) - [Alibaba Qwen](https://qwen.readthedocs.io/) ### Use chain-of-thought and examples for complex tasks Don’t hesitate to let the model “think out loud” or guide it through tricky scenarios. Chain-of-thought prompting asks the model to solve problems step by step, for example, by including a phrase like “Let’s reason this out step by step...” or using a hidden `` tag if supported. This approach helps improve reasoning accuracy. Also, provide a few in-context examples (few-shot prompting) to show how to handle edge cases, compliance rules, or specific output formats. Illustrate any structured output formats in the prompt, and if possible, configure the connector's [response format](../connectors/out-of-the-box-connectors/agentic-ai-aiagent.md) options to enforce JSON or parsed text responses. Clear examples and format guidance set expectations for the AI, ensuring consistency and reducing errors. ### Define when and how the agent should seek user input In your system or prompt instructions, make it clear when the AI should involve a human. For example, you could say, “If the user’s request is unclear or more information is needed, the assistant should ask a follow-up question via the customer communication tool.” Decide which points in the process need user feedback, such as after showing an intermediate result or when the AI is unsure about a critical decision. Specify this in the prompt so the model knows it is acceptable or expected to ask for clarification. The instructions should also guide the AI on using the human feedback tool. For example, “Before finalizing an answer, if confidence is low, call the `ask_human` tool to confirm the details.” Being explicit helps the agent make better decisions. ### Set a clear persona and objective in the system prompt Always start your prompt by defining the AI’s role and goal. For example, in an agentic process, the model could act as a specialized assistant: “You are OrderAgent, an AI assistant helping users track and modify their orders.” Include the persona’s traits and main objective. For example: “Your goal is to resolve customer inquiries using the tools provided while following all company guidelines.” Defining the persona and objective helps the model maintain a consistent tone and produce focused, coherent outputs. :::tip Prompt engineering is iterative. After writing an initial prompt, test it with your model and sample scenarios. If the agent’s behavior isn’t quite right, refine the wording or add another example. Small phrasing changes can have a big impact. Continue to experiment and refine to achieve reliable, compliant results for your specific agentic use case. ::: ### Example of a generic prompt ```text You are **OrderAgent**, a helpful AI assistant supporting order management. Your objective is to resolve requests by: 1. Using the available tools when external action is required. 2. Asking for clarification when input is incomplete or ambiguous. 3. Returning outputs in JSON format if requested by the connector. Let’s reason step by step. Comportments for tool usage: - **Direct actions:** - Use `cancel_order` when a clear and valid order ID is provided. - Use `send_email` only when communication with the customer is explicitly required. - **Chained actions:** - If cancelling an order also requires notifying the customer, first call `cancel_order`, then call `send_email`. - If a tool returns a status update that triggers a follow-up action (for example, an order is “on hold”), use the corresponding resolution tool in sequence. - **Ambiguity handling:** - If the order reference is missing, request clarification before proceeding with a tool. - If multiple orders match the request, return options and request the user (or `ask_human`) to disambiguate. - **Escalation to human (`ask_human`):** - If the requested action could have irreversible impact (e.g., “delete all orders”), always escalate. - If tool outputs are malformed, incomplete, or contradictory, escalate for review. - If confidence in the decision path is low (for example, conflicting data across tools), escalate rather than guessing. - **Unexpected tool outputs:** - If a tool returns irrelevant or excessive data (e.g., HTML instead of plain text), sanitize and summarize before continuing. - If output cannot be parsed or mapped correctly, escalate with `ask_human`. The goal is to use tools precisely, combine them logically when workflows require multiple steps, and defer to a human when safety, ambiguity, or unexpected results make autonomous resolution unreliable. ``` --- ## Add long-term memory to your AI agents Use Retrieval-Augmented Generation (RAG) with the [Vector Database connector](/components/connectors/out-of-the-box-connectors/embeddings-vector-db.md) to give your AI agents access to persistent, domain-specific knowledge that grows over time. ## When to use long-term memory A standard AI agent operates within a fixed context window. This works well for many tasks, but becomes limiting when the agent needs access to large or frequently updated knowledge. Long-term memory solves this by storing knowledge outside the agent in a vector database and retrieving only the most relevant fragments at runtime. Common use cases include: - **Policy and procedure lookup**: The agent answers questions about internal rules or processes by retrieving the relevant document sections on demand. - **Product and catalog search**: The agent finds product details, specifications, or pricing from a large catalog without loading it all into context. - **Support knowledge base**: Answers to previously resolved questions are stored and surfaced automatically when similar questions arise in the future. - **Compliance and audit**: The agent retrieves the exact policy text needed to justify or explain a decision, making its reasoning traceable. ## How it works The [Vector Database connector](/components/connectors/out-of-the-box-connectors/embeddings-vector-db.md) supports two operations that together implement long-term memory: - **Retrieve document**: Performs a semantic similarity search and returns the most relevant results from a vector index. Use this to let the agent query its knowledge base. - **Embed document**: Converts text into a vector embedding and stores it in the vector index. Use this to add new knowledge to the agent's memory. The LLM is responsible for generating natural language queries when retrieving, and for deciding what content is worth storing. The actual vector operations (encoding, indexing, and searching) are handled by the connector and the underlying vector store. To configure either operation, you need: - A supported [vector store](/components/connectors/out-of-the-box-connectors/embeddings-vector-db.md#vector-stores) and connection credentials. - A supported [embedding model](/components/connectors/out-of-the-box-connectors/embeddings-vector-db.md#embedding-models) and its provider credentials. - An index name that identifies the collection to read from or write to. ## Retrieve from the vector database To perform a semantic search from a vector database, you can use one of the following two approaches. ### Add a vector database query tool To let an agent query a vector database, add a **Vector Database connector task** with no incoming sequence flows inside the AI Agent's [ad-hoc sub-process](/components/modeler/bpmn/ad-hoc-subprocesses/ad-hoc-subprocesses.md). :::note Tasks with no incoming flows are treated as available [tools](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-tool-definitions.md) by the AI Agent connector. ::: Configure the task as follows: 1. Give the task a clear **Name** and write a descriptive **Element documentation** to help the LLM understand when to use this tool. The element documentation is passed to the LLM as the [tool description](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-tool-definitions.md#tool-definitions). 2. Set **Operation** to **Retrieve document**. 3. Set **Search query** using the [`fromAi()`](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-tool-definitions.md#ai-generated-parameters-via-fromai) function so the LLM generates the query dynamically at runtime: ```feel fromAi(toolCall.query, "The query you're making to the vector database.") ``` 4. Set **Max results** to control the maximum number of documents returned. For example, set it to five. 5. Configure the [**Embedding model**](/components/connectors/out-of-the-box-connectors/embeddings-vector-db.md#embedding-models) with your provider credentials. 6. Configure the [**Vector store**](/components/connectors/out-of-the-box-connectors/embeddings-vector-db.md#vector-stores) with your database connection details and **index name**. The index name identifies the collection of documents the agent searches. You can use different indexes for different knowledge domains. 7. In the **Output mapping** section, set the output **Result variable** to `toolCallResult`. ### Isolate content with dynamic index names When multiple agents, tenants, or conversations share the same vector database, you can isolate their content by using **dynamic index names**. Instead of hardcoding a single index, construct the index name at runtime using process variables. For example, by appending a tenant ID or conversation ID: ```feel "knowledge-base-" + tenantId ``` This ensures each scope reads and writes only its own documents, without requiring metadata-based filtering at query time. Because indexes are created on demand when documents are first embedded, a retrieval task may run before any documents have been stored for a given scope. This may result in an `index_not_found` error. See how to [handle missing or empty results](#handle-missing-or-empty-results). #### Handle missing or empty results To prevent process failures when no results are retrieved, you can set an error handler to inform the agent as follows. 1. In the **Error handling** section, set the **Error expression** to handle these scenarios. For example: ``` if contains(error.message, "index_not_found") then bpmnError("index_not_found", "The index does not exist") else null ``` 2. Add an [**error boundary event**](/components/modeler/bpmn/call-activities/call-activities.md#boundary-events) to the Vector Database connector: 3. In the boundary event's **Output mapping** section, add an output variable as follows: - Set **Process variable name** to `toolCallResult`. - Set **Variable assignment value** to: ``` { "searchResult": "Nothing was found" } ``` ### Prefetch context with a vector database retrieval Instead of letting the agent decide when to query the vector database via a tool, you can retrieve relevant context **before** the agent runs. This ensures the agent always has access to relevant knowledge from the first interaction, without requiring a tool call. This pattern is useful when: - The user's query is predictable enough to retrieve meaningful context upfront. - You want to reduce the number of tool calls and agent reasoning steps. - The agent should ground its first response in domain-specific knowledge without deciding whether to search. #### Configure the retrieval task 1. Add a **Vector Database connector task** in your process, sequenced before the AI Agent connector task. 2. Set **Operation** to **Retrieve document**. 3. Set **Search query** to the user's input. For example, if the user query is stored in a process variable: ```feel userQuery ``` 4. Set **Max results** to control the maximum number of documents returned. For example, set it to five. 5. Configure the [**Embedding model**](/components/connectors/out-of-the-box-connectors/embeddings-vector-db.md#embedding-models) with your provider credentials. 6. Configure the [**Vector store**](/components/connectors/out-of-the-box-connectors/embeddings-vector-db.md#vector-stores) with your database connection details and **index name**. The index name identifies the collection of documents the agent searches. You can use different indexes for different knowledge domains. 7. In the **Output mapping** section, set the output **Result variable**. For example, `retrievalResult`. ### Pass retrieved context to the agent Once the retrieval task completes, include its results in the AI Agent's user message. There are two approaches: #### Concatenate as text Build the user message by appending the retrieved text to the original query: ```feel userQuery + " Use the following context to inform your answer: " + " ".join(retrievalResult.searchResult) ``` This works well when the retrieved content is short and you want the agent to treat it as inline context. #### Attach as documents If the Vector Database connector returns structured document objects, you can add them to the user message's document list. This keeps the user query and the supporting documents separate, which can help the LLM distinguish between the question and the reference material. Refer to the [AI Agent connector documentation](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md) for details on how to structure the message input with documents. ### Prefetch vs. tool-based approach | Consideration | Prefetch | Tool-based retrieval | | ------------------ | ---------------------------------------------------- | ---------------------------------------------------------- | | **Agent autonomy** | Agent does not choose when to search | Agent decides if and when to search | | **Latency** | Retrieval runs once before the agent starts | Retrieval adds a tool-call round trip | | **Query control** | Uses the raw user query directly | LLM reformulates the query dynamically | | **Relevance** | Best when the user query maps well to stored content | Best when the agent needs to refine or decompose the query | :::tip You can combine both patterns: prefetch broad context to prime the agent, and still expose a retrieval tool for follow-up searches the agent initiates on its own. ::: ## Store in the vector database You can store knowledge in the vector database in two ways: - **Batch import**: Documents are embedded and stored before the agent starts processing, typically as part of a data preparation process. Use a Vector Database connector task in a separate BPMN process or script. - **Runtime ingestion**: New knowledge is added to the vector database as the agent encounters it. For example, when a human provides an answer that did not previously exist in the database. :::note Re-embedding the same document is **not idempotent**: if you store it again without deleting the existing chunks first, you’ll create **duplicate chunks** in the vector database. ::: For both approaches, add a **Vector Database connector task** and configure it as follows: 1. Set **Operation** to **Embed document**. 1. Set **Document source** to **Plain text**. 1. Provide the text to embed. This can be a process variable, a form output, or any string value. 1. Configure the same [**Embedding model**](/components/connectors/out-of-the-box-connectors/embeddings-vector-db.md#embedding-models) and [**Vector store**](/components/connectors/out-of-the-box-connectors/embeddings-vector-db.md#vector-stores) settings used by the retrieval method so both operations target the same index. :::important Make sure the embedding model configuration, including vector dimensions, matches your retrieval setup. ::: ## Gate memory writes with human approval Allowing an agent to write to its own knowledge database without human oversight can lead to incorrect or irrelevant data being stored. Besides, an effective pattern for building long-term memory is to combine a human escalation tool with runtime knowledge ingestion. When the agent can’t find an answer in the vector database, it escalates to a human. The human responds to the agent and decides whether it’s worth storing the answer in the vector database for future queries. Over time, this creates a self-improving knowledge base: as humans answer previously unknown questions, the agent's ability to resolve those questions autonomously increases and the rate of human escalations decreases. To implement this pattern: 1. Add a [user task](/components/modeler/bpmn/user-tasks/user-tasks.md) inside the AI Agent's ad-hoc sub-process. The agent will invoke it as a tool when it cannot resolve a query from its existing knowledge. 2. Configure the user task's **Input mapping** to pass the agent's question to the form using `fromAi()`. For example: ```feel fromAi(toolCall.question, "The question the agent needs a human to answer.") ``` 3. Add a [form](/components/modeler/forms/camunda-forms-reference.md) to the user task. It should capture the human's answer and include a decision checkbox for whether to store it in long-term memory. 4. Configure the user task's **Output mapping** to set the human's answer as `toolCallResult` so it is returned directly to the agent. 5. Add an [exclusive gateway](/components/modeler/bpmn/exclusive-gateways/exclusive-gateways.md) after the user task with two outgoing paths: - **Approved**: [Store in the vector database](#store-in-the-vector-database). - **Rejected**: Do not store. 6. Use the human's output variable as the gateway condition. --- ## Access control(Overview) Reference the permissions required to access audit log entries. ## About To access entries in the audit log, you must have the relevant authorizations to match your needs: | Authorization type | Resource type | Resource ID | Permission | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------- | :------------------------------------------------------------------------------------------------ | :---------------------- | | View audit log entries. | `AUDIT_LOG` | An operation category (`ADMIN`, `DEPLOYED_RESOURCES`, or `USER_TASKS`) or `*` for all categories. | `READ` | | View `DEPLOYED_RESOURCES` and `USER_TASKS` operation logs for instances of a specific process definition. This provides access to both the general and process instance-level operation logs in Operate. | `PROCESS_DEFINITION` | A process definition ID or `*` for all process definitions. | `READ_PROCESS_INSTANCE` | | View `USER_TASKS` operation logs for instances of a specific process definition. This provides access to the operation log in Operate and the task history in Tasklist. | `PROCESS_DEFINITION` | A process definition ID or `*` for all process definitions. | `READ_USER_TASK` | | View operations related to specific tasks the user has access to based on task properties. This provides access to task history records in Tasklist. | `USER_TASK` | A user task property (assignee, candidateUsers, candidateGroups). | `READ` | Learn more about the operation categories in [Recorded operations](./recorded-operations.md). --- ## Operation data structure Learn more about how operation data from the audit log is presented in different contexts. ## Applications Depending on the view you're using to access the audit log in [Operate](../../operate/userguide/audit-operations.md), [Admin](../../admin/audit-operations.md), or [Tasklist](../../tasklist/userguide/audit-task-history.md), you'll see a subset of the following operation details: | Property | Description | | :------------- | :-------------------------------------------------------------------------- | | Status | The status of the operation. | | Operation type | The type of operation applied. | | Entity type | The type of entity the operation was applied to. | | Entity key | The key and name of the entity the operation was applied to, if applicable. | | Parent entity | The key and name of the parent entity, if applicable. | | Related entity | The ID or name of the related entity, if applicable. | | Details | Details about the operation. | | Actor | The user, client, or agent that applied the operation. | | Date | The date and time at which the operation was applied. | ### Entity key Some audit log entries contain extra details about the entity in the **entity key** field: | Operation type | Entity type | Entity key | | :------------- | :--------------- | :------------ | | Create | Process instance | Process name | | Delete | Process instance | Process name | | Create | Variable | Variable name | | Create | Resource | Resource name | | Delete | Resource | Resource name | | Create | Decision | Decision name | | Delete | Decision | Decision name | ### Details Some audit log entries contain extra details about the operation in the **details** field: | Operation type | Entity type | Details | | :------------- | :------------ | :--------------------------------- | | Create | Batch | Batch operation type | | Assign | User task | Assignee | | Unassign | User task | Assignee | | Create | Authorization | Owner(entity type, entity name) | | Assign | Tenant | Assignee(entity type, entity name) | | Unassign | Tenant | Assignee(entity type, entity name) | | Assign | Role | Assignee(entity type, entity name) | | Unassign | Role | Assignee(entity type, entity name) | | Assign | Group | Assignee(entity type, entity name) | | Unassign | Group | Assignee(entity type, entity name) | ### Actor The following actor types can trigger operations: | Actor type | Identifier | | :--------- | :--------- | | User | Username | | Client | Client ID | Agents can perform operations on behalf of a user or client. In this case, you will see the agent's information in the record. ## REST API With the API, you can access more operation data than you can in the applications. See the [API response schema](../../../apis-tools/orchestration-cluster-api-rest/specifications/search-audit-logs.api.mdx#responses) for more information. --- ## Recorded operations Learn more about which operations are recorded in the audit log. ## Limitations and constraints The audit log contains operations performed using: - [Operate](../../operate/userguide/audit-operations.md), [Admin](../../admin/audit-operations.md), and [Tasklist](../../tasklist/userguide/audit-task-history.md) - [Orchestration Cluster REST API](../../../apis-tools/orchestration-cluster-api-rest/specifications/search-audit-logs.api.mdx) However, only operations that are authenticated, authorized, and reach execution with a success or execution‑time failure are recorded. Operations rejected before execution are not recorded in the audit log. Additionally, only user operations are tracked by default, not [client](../../zeebe/technical-concepts/architecture.md#clients) operations. Unlike the other constraints, you can configure this behavior. ## Recorded operations There are three categories of recorded operations: - `USER_TASKS` - `ADMIN` - `DEPLOYED_RESOURCES` ### `USER_TASKS` operations You can review the full history of user task actions, including assignment changes, completions, and updates. With this, you can resolve disputes, investigate SLA breaches, and validate that required steps were followed during case handling. These operations belong to the category `USER_TASKS`. The following operations are recorded in the audit log: | Operation type | Entity | Tracked rejections | | :------------- | :-------- | :----------------- | | Update | User task | INVALID_STATE | | Assign | User task | INVALID_STATE | | Unassign | User task | INVALID_STATE | | Complete | User task | INVALID_STATE | ### `ADMIN` operations You can track all changes to identity resources, like authorizations, users, and tenants. With this, you can detect misconfigurations and investigate potential unauthorized access to sensitive process data. These operations belong to the category `ADMIN`. The following operations are recorded in the audit log: | Operation type | Entity | Tracked rejections | | :------------- | :------------ | :----------------- | | Create | Authorization | – | | Update | Authorization | – | | Delete | Authorization | – | | Create | User | – | | Update | User | – | | Delete | User | – | | Create | Tenant | – | | Update | Tenant | – | | Delete | Tenant | – | | Assign | Tenant | – | | Unassign | Tenant | – | | Create | Role | – | | Update | Role | – | | Delete | Role | – | | Assign | Role | – | | Unassign | Role | – | | Create | Group | – | | Update | Group | – | | Delete | Group | – | | Assign | Group | – | | Unassign | Group | – | | Create | Mapping rule | – | | Update | Mapping rule | – | | Delete | Mapping rule | – | ### `DEPLOYED_RESOURCES` operations You can audit user and client actions that modified or influenced deployed resources and dependent entities, like process instances, batch operations, and variables. With this, you can identify manual corrections and confirm the sequence of actions that led to a process failure or escalation. These operations belong to the category `DEPLOYED_RESOURCES`. The following operations are recorded in the audit log: | Operation type | Entity | Tracked rejections | | :------------- | :--------------- | :------------------------------ | | Create | Process instance | – | | Cancel | Process instance | – | | Modify | Process instance | – | | Migrate | Process instance | INVALID_STATE, PROCESSING_ERROR | | Create | Variable | – | | Update | Variable | – | | Resolve | Incident | INVALID_STATE | | Create | Resource | – | | Delete | Resource | – | | Create | Batch | – | | Suspend | Batch | INVALID_STATE | | Resume | Batch | INVALID_STATE | | Cancel | Batch | INVALID_STATE | | Create | Decision | – | | Delete | Decision | – | | Evaluate | Decision | – | #### Batch operations While the operations for creating and managing batch operations are recorded in the audit log, the batch operation state changes aren't. For more information, learn how to [monitor batch operations](../../operate/userguide/monitor-batch-operations.md). ## Log scope `ADMIN` and `BATCH` operations are not scoped to a particular tenant. Instead, they're applied at a global scope because Identity-related operations don't belong to an individual tenant and batch operations may include items from multiple tenants. Keep this in mind when you filter by tenant ID with the [search audit logs API](/apis-tools/orchestration-cluster-api-rest/specifications/search-audit-logs.api.mdx) or the [Operate user interface](/components/operate/userguide/audit-operations.md). As these operations aren't scoped to a tenant, selecting a particular tenant ID will filter out these operations. --- ## Audit log View and audit a comprehensive record of operations across process, identity, and user task domains. ## About The audit log provides a record of operations, including who performed an operation, when it was performed, and on which entities the operation was performed. Use the audit log to: - **Prove compliance:** Produce defensible evidence of operation ownership and history during internal and external audits. - **Meet governance and regulatory requirements:** Validate if required steps were followed during case handling, and investigate unauthorized access to sensitive process data. - **Maintain operational integrity and transparency:** See a complete record of actions taken to resolve disputes and investigate SLA breaches. - **Troubleshoot issues:** Review user and client actions that modified or influenced process instances to confirm the sequence of actions that led to a process failure. ## Impact on secondary storage When the audit log is active, a record is written to [secondary storage](../../self-managed/concepts/secondary-storage/index.md) for every applicable operation instance. By default, only user operations are tracked, not [client](../zeebe/technical-concepts/architecture.md#clients) operations. With this default behavior, you can expect a 3.5% increase in disk usage. :::warning The audit log is enabled by default. Because of the increase in resource usage on secondary storage, you may see increased costs associated with this feature. ::: You can configure the audit log to fine tune log thoroughness and resource usage according to your needs: - [SaaS](../hub/organization/manage-clusters/configure-audit-log.md) - [Self-Managed](../../self-managed/concepts/audit-log/configure.md) ## Get started Start auditing operations in Operate, Tasklist, and Admin (formerly Orchestration Cluster Identity). ## Learn the fundamentals Learn fundamental concepts about how the audit log works and how to access its data. ## Explore further resources Once you have a foundational understanding of the audit log, explore these additional resources: - [Use the Camunda REST API to access the audit log](../../apis-tools/orchestration-cluster-api-rest/specifications/search-audit-logs.api.mdx) --- ## Deciding about your stack Our greenfield stack recommendation is a result of extensive discussions and evaluations. While not the only option, it is a solid choice if there are no specific reasons to choose an alternative. Your choice of programming language should align with your team's expertise; we suggest Java or JavaScript for their broad applicability and support, and have outlined the Java greenfield stack below with Camunda 8 SaaS. ## The Java greenfield stack ![greenfield stack architecture diagram](deciding-about-your-stack-assets/greenfield-architecture.png) This architecture diagram illustrates the flow of requests from a user's browser through Camunda SaaS, where workflows and decisions are orchestrated. The process then moves to the Spring Boot application, which is responsible for executing business logic, handling database interactions with PostgreSQL, and managing various components such as custom REST endpoints, BPMN/DMN definitions, and external task workers. ### Why this stack? - SaaS simplifies workflow engine integration. - Spring Boot is widely adopted for Java application development. - Flexible for both on-premises and cloud environments. Discover more in our [getting started guide using Spring](/guides/getting-started-example.md) or the [Camunda Spring Boot Starter instructions](../../../apis-tools/camunda-spring-boot-starter/getting-started.md). ### Set up the stack For a Java-based setup using Camunda 8 SaaS and Spring Boot, use the following stack: #### Camunda 8 SaaS account and cluster If you're new to Camunda SaaS, check out our [getting started guide](/guides/introduction-to-camunda-8.md#getting-started) to set up your environment. After signing up, create a cluster by following [creating a cluster in Camunda 8](/components/hub/organization/manage-clusters/create-cluster.md), which provides step-by-step instructions on setting up a new cluster in the Camunda 8 environment. #### Spring Boot Develop your own process solutions as [Spring Boot](https://spring.io/projects/spring-boot) applications. This involves setting up a new Spring Boot project, either manually or using tools like [Spring Initializr](https://start.spring.io/). Integrate the [Camunda Spring Boot Starter](../../../apis-tools/camunda-spring-boot-starter/getting-started.md) into the Spring Boot project by adding necessary dependencies to the project’s `pom.xml` file, and configure the application to use Camunda services. #### Maven Use [Maven](https://maven.apache.org/) to manage the build lifecycle of the application. #### IDE selection Select an Integrated Development Environment (IDE) that supports Java development, Maven, and Spring Boot. Frequently used options include Visual Studio Code, IntelliJ IDEA, or Eclipse. #### Java runtime Install and use OpenJDK 17 as your Java runtime environment. Download it from the [official JDK 17 download page](https://jdk.java.net/17/). #### Modeling Download and use Camunda Modeler for designing and modeling business processes. Modeler is available [here](https://camunda.org/download/modeler/). #### Code integration Incorporate all Java code and BPMN process models into the Spring Boot project, ensuring that they are structured correctly and referenced properly within the application. ### Run the process application: To run the process application, transfer the `jar` to the desired server. Start the application using the command `java -jar YourProcessApplication.jar`. Frequently, this deployment process is managed through Docker for ease of use. For a practical implementation, refer to our [example application on GitHub](https://github.com/camunda-community-hub/camunda-cloud-examples/tree/main/twitter-review-java-springboot), which demonstrates a typical setup for a Spring Boot-based process application with Camunda. ## Customize your stack ### Polyglot stacks You can develop process solutions as described with Java above also in any other programming language, including JavaScript. Use the [existing language clients and SDKs](/apis-tools/working-with-apis-tools.md) for doing this. ### Run Camunda 8 Self-Managed Run Camunda 8 on your Kubernetes cluster. For local development, a [Docker Compose configuration is available](/self-managed/deployment/docker/docker.md), though not for production use. Learn more in the [deployment docs](/self-managed/deployment/helm/install/quick-install.md). --- ## Run benchmarks Run your own benchmarks to validate [Camunda 8 sizing](./sizing-your-environment.md) for your specific workload. ## Reference benchmark scenario The sizing recommendations for [SaaS](sizing-saas.md) and [Self-Managed](sizing-self-managed.md) are based on a reference benchmark scenario. Your actual workload may differ significantly, so running your own benchmarks is the most reliable way to validate that your chosen configuration meets your needs. Camunda uses the following realistic benchmark scenario: - **Process model:** [bankCustomerComplaintDisputeHandling.bpmn](https://github.com/camunda/camunda/blob/main/load-tests/load-tester/src/main/resources/bpmn/realistic/bankCustomerComplaintDisputeHandling.bpmn) (a credit card fraud dispute handling process from the [Camunda Marketplace blueprint](https://marketplace.camunda.com/en-US/apps/449510/credit-card-fraud-dispute-handling)). - **Payload:** [realisticPayload.json](https://github.com/camunda/camunda/blob/main/load-tests/load-tester/src/main/resources/bpmn/realistic/realisticPayload.json) (~11 KB). - This setup produces approximately **101 tasks per second at 1 PI/s** due to internal sub-process instantiation (50 sub-process instances per root instance). :::note The official sizing numbers on this page are produced using the [load-tester](https://github.com/camunda/camunda/tree/main/load-tests/load-tester) tool from the Camunda monorepo. ::: ## Run your own benchmarks Use the [Camunda 8 Benchmark project (c8b)](https://github.com/camunda-community-hub/camunda-8-benchmark), a Spring Boot application, to run load tests against your cluster. ### Key features - Starts process instances at a configurable rate and **automatically adjusts based on backpressure**. - Completes tasks that appear in the process instances. - **Bring your own BPMN process model and payload**, which can be provided as URLs, such as GitHub Gists. - **Automatic job type discovery** from BPMN files. - Configurable **task completion delay** to simulate real worker behavior. - Built-in **Prometheus metrics and Grafana dashboards** for observability. ### Quick start Run the following command against your cluster: ```bash mvn spring-boot:run ``` With Docker: ```bash docker run camundacommunityhub/camunda-8-benchmark:main ``` Customize it with your own process and payload: ```bash benchmark.bpmnResource=url:https://your-gist-url/your-process.bpmn benchmark.payloadPath=url:https://your-gist-url/your-payload.json benchmark.processInstanceStartRate=25 benchmark.taskCompletionDelay=200 ``` :::important To run meaningful benchmarks, use a **properly sized environment**. SaaS trial clusters and local developer machines have limited resources and will hit bottlenecks too early. Use either a correctly sized Camunda SaaS cluster (with help from your Camunda representative) or a properly provisioned Self-Managed Kubernetes environment. ::: ## When to benchmark Running your own benchmarks when: - Your process models or payload sizes **differ significantly** from the reference scenario. - **Latency or cycle time requirements** are critical to your use case. - You are running Optimize with **payloads larger than the reference ~11 KB** or retention periods **exceeding 6 months**. Larger payloads and longer retention amplify Elasticsearch disk consumption and Optimize import times. - You are **upgrading from a pre-8.8 version** and want to validate resource requirements. - You are using **RDBMS (PostgreSQL) as secondary storage** and want to validate throughput differences. ## What to measure When running benchmarks, focus on these key metrics: - **Sustained throughput (tasks/second):** The rate your cluster can handle continuously without increasing backpressure. - **Backpressure rate:** Should remain below 10% for sustainable operation. - **Process instance latency (p99):** End-to-end time from instance creation to completion. Target depends on your SLO. - **Elasticsearch disk growth rate:** Helps you forecast disk capacity needs. - **Data availability latency:** The time between an event in the engine and its appearance in Operate/Tasklist. - Note: to measure this, you have to compare the time from starting an instance and its availability in query APIs using the Orchestration Cluster REST API - **CPU usage and throttling:** High CPU usage or frequent throttling indicates a need for more CPU resources or additional brokers. - **Memory usage:** Sustained high memory usage suggests the need for larger memory limits or additional nodes. --- ## Size your SaaS cluster Select the right Camunda 8 SaaS cluster size based on your needs. For an overview of the factors that influence sizing, see [Size your environment](./sizing-your-environment.md). ## Determine your cluster size Camunda 8 defines four [cluster sizes](/components/concepts/clusters.md#cluster-size) (1x, 2x, 3x, and 4x) you can select after choosing your [cluster type](/components/concepts/clusters.md#cluster-type). To do so, follow these steps: 1. Calculate your throughput and storage requirements using the guidance in [Size your environment](./sizing-your-environment.md). 2. Use the [sizing tables](#sizing-tables) to find the cluster size that meets your needs. :::note To increase the cluster size beyond 4x, [reach out to Camunda](https://camunda.com/contact-us/). This requires custom sizing and pricing. ::: ### Sizing tables | Cluster size | 1x | 2x | 3x | 4x | | :--------------------------------------------------- | ------------------------------: | ------------------------------: | ------------------------------: | ------------------------------: | | Max Throughput **Tasks/day** **\*** | 9 M | 18 M | 27 M | 36 M | | Max Throughput **Tasks/second** **\*** | 100 | 200 | 300 | 400 | | Max Throughput **Process Instances/second** **\*\*** | 5 | 10 | 15 | 20 | | Max Total Number of PI stored (in ES) **\*\*\*** | 200 k | 400 k | 600 k | 800 k | | Approximate resources provisioned **\*\*\*\*** | 11 vCPU, 22 GB mem, 192 GB disk | 22 vCPU, 44 GB mem, 384 GB disk | 33 vCPU, 66 GB mem, 576 GB disk | 44 vCPU, 88 GB mem, 768 GB disk | :::note The numbers in the tables were measured using Camunda 8 (version 8.8), [the benchmark project](https://github.com/camunda-community-hub/camunda-8-benchmark) running on its own Kubernetes cluster, and using a [realistic process](https://github.com/camunda/camunda/blob/main/load-tests/load-tester/src/main/resources/bpmn/realistic/bankCustomerComplaintDisputeHandling.bpmn) with this [payload](https://github.com/camunda/camunda/blob/main/load-tests/load-tester/src/main/resources/bpmn/realistic/reducedPayload.json) (~1.4 KB). To calculate day-based metrics, an equal distribution over 24 hours is assumed. ::: **\*** Tasks (including service, send, and user tasks, among others) completed per day are the primary metric, as this is easy to measure and strongly influences resource consumption. This number assumes a constant load throughout the day. Tasks/day and Tasks/second are scaled linearly. **\*\*** Because tasks are the primary resource driver, the number of process instances supported by a cluster is calculated assuming an average of 10 tasks per process. As a customers, you can calculate a more accurate process instance estimate using your anticipated number of tasks per process. **\*\*\*** Maximum total number of historical process instances within the retention period. For active process instances, this is limited mostly by Zeebe resources; for historical instances, it is limited mostly by Elasticsearch resources. Calculated assuming a typical set of process variables per process instance. Note that it makes a difference whether you add one or two strings (requiring ~1 KB of space) to your process instances or attach a full JSON document containing 1 MB, as this data must be stored in various places, influencing memory and disk requirements. If this number increases, you can still retain the runtime throughput, but Tasklist, Operate, and/or Optimize may lag behind. The provisioned disk size is calculated as the sum of the disk size used by Zeebe and Elasticsearch. **\*\*\*\*** These are the resource limits configured in the Kubernetes cluster and are subject to change. ## Data retention The maximum throughput numbers should be considered peak loads, and the data retention configuration considered when defining the amount of data kept for completed instances in your cluster. See [Camunda 8 SaaS data retention](/components/saas/data-retention.md) for the default retention times for Zeebe, Tasklist, Operate, and Optimize. - If process instances are completed and older than the configured retention time for an application, the data is removed. - If a process instance is older than the configured retention time but still active and incomplete, it continues to function at runtime and is _not_ removed. Camunda can adjust data retention on request (up to certain limits). Consider retention time adjustments and/or storage capacity increases if you plan to run more than \[max PI stored in ES\] / \[configured retention time\]. :::note Why is the total number of process instances stored that low? This is related to the limited resources provided to Elasticsearch, which can cause performance problems when too much data is stored there. By increasing the available memory for Elasticsearch, you can also increase that number. At the same time, even with this rather low number, you can always guarantee the throughput of the core workflow engine during peak loads, as this performance is not affected. You can also increase memory for Elasticsearch later if needed. ::: ## Next steps Validate your chosen configuration by [running your own benchmarks](sizing-benchmarks.md). --- ## Self-Managed resource planning Provisioning Camunda 8 on your Self-Managed cluster depends on several factors. Use [Kubernetes with Helm](/self-managed/deployment/helm/index.md) to deploy and manage your Self-Managed cluster. Use the configurations and guidance below as a baseline, then adjust based on your workload. For background on the factors that drive provisioning requirements, see [Size your environment](sizing-your-environment.md). ## Camunda 8.8+ resource consumption Camunda 8.8 introduced a streamlined architecture that consolidates the broker, gateway, Operate, Tasklist, and Identity into a single application, the [Orchestration Cluster](/components/orchestration-cluster.md). This changes how you think about resource consumption compared to older versions. If you are upgrading from a pre-8.8 version, expect different resource profiles: - The Orchestration Cluster requires **more CPU per broker** compared to 8.7 (approximately 75% more CPU, for example, 2 to 3.5 cores, to maintain equivalent throughput). - Throughput at the default 2 CPU cores drops ~35% compared to 8.7.x. - With properly aligned resources (3.5 CPU cores), 8.8.x achieves similar throughput to 8.7.x with **significantly lower latency** (approximately a 2x improvement). - The streamlined architecture reduces operational complexity (fewer pods to manage) but consolidates resource consumption into fewer, larger pods. All components are clustered to provide high-availability, fault-tolerance, and resilience. The Orchestration Cluster scales horizontally by adding more nodes (pods). This is limited by the [number of partitions](/components/zeebe/technical-concepts/partitions.md) configured for a cluster, as the work within one partition cannot be parallelized by design. Hence, you need to define enough partitions to utilize your hardware. The [number of partitions can be scaled up](/self-managed/components/orchestration-cluster/zeebe/operations/cluster-scaling.md) after the cluster is initially provisioned, but not yet scaled down. Camunda 8 runs on Kubernetes. Every component runs as a pod with assigned resources. These resources can be scaled vertically (assigned more or fewer resources dynamically) within certain limits. Vertical scaling does not always increase throughput, since the components depend on each other. :::note Camunda licensing does not depend on the provisioned hardware resources, making it easy to size according to your needs. ::: ## Baseline performance Considering this [baseline resource configuration](#baseline-resource-configuration), you can expect the following performance: | Metric | Value | | ----------------------------------------------- | ---------------------------------------------- | | Completed process instances per second | 51 (includes root and child process instances) | | Completed flow node instances (FNIs) per second | 560 | | Completed tasks per second | 100 | | Data availability (query API latency) | < 5 seconds | :::important These numbers were measured using Camunda's [load test application](https://github.com/camunda/camunda/tree/main/load-tests/load-tester) with a [realistic reference process](https://github.com/camunda/camunda/blob/main/load-tests/load-tester/src/main/resources/bpmn/realistic/bankCustomerComplaintDisputeHandling.bpmn) and [realistic payload](https://github.com/camunda/camunda/blob/main/load-tests/load-tester/src/main/resources/bpmn/realistic/realisticPayload.json) (~11 KB). For details on the testing methodology, see the [reliability testing documentation](https://github.com/camunda/camunda/blob/main/docs/testing/reliability-testing.md). ::: The realistic reference process starts one root process instance, which spawns 50 sub-process instances via call activities. It covers a wide variety of BPMN elements, including call activities, multi-instance, sub-processes, and DMN. The process is based on the [Credit Card Fraud Dispute Handling](https://marketplace.camunda.com/en-US/apps/449510/credit-card-fraud-dispute-handling) blueprint from the Camunda Marketplace. ## Baseline resource configuration The following configuration provides a baseline equivalent to a 1x SaaS cluster without Optimize enabled. | Component | | Request | Limit | | ------------------------- | ------------------- | ------: | ----: | | **Orchestration Cluster** | | | | | Brokers | 3 | | | | Partitions | 3 | | | | Replication factor | 3 | | | | | vCPU \[cores\] | 3 | 3 | | | Memory \[GB\] | 2 | 2 | | | Disk \[GB\] | | 128 | | **Connectors** | | | | | # | 1 | | | | | vCPU \[cores\] | 0.2 | 0.2 | | | Memory limit \[GB\] | 0.512 | 1 | | **Elastic** | | | | | #statefulset | 3 | | | | | vCPU \[cores\] | 3 | 3 | | | Memory limit \[GB\] | 2 | 2 | | | Disk request \[GB\] | | 128 | When Optimize is enabled, additional resources are needed, especially for Elasticsearch, because Optimize's importer reads from and writes to Elasticsearch indices. See [Impact of Optimize](sizing-your-environment.md#impact-of-optimize) for more details. | Component | | Request | Limit | | ------------------------- | ------------------- | ------: | ----: | | **Orchestration Cluster** | | | | | Brokers | 3 | | | | Partitions | 3 | | | | Replication factor | 3 | | | | | vCPU \[cores\] | 3 | 3 | | | Memory \[GB\] | 2 | 2 | | | Disk \[GB\] | | 128 | | **Connectors** | | | | | # | 1 | | | | | vCPU \[cores\] | 0.2 | 0.2 | | | Memory limit \[GB\] | 0.512 | 1 | | **Optimize** | | | | | # | 1 | | | | | vCPU \[cores\] | 0.6 | 2 | | | Memory limit \[GB\] | 1 | 2 | | **Elastic** | | | | | #statefulset | 3 | | | | | vCPU \[cores\] | 7 | 7 | | | Memory limit \[GB\] | 6 | 8 | | | Disk request \[GB\] | | 512 | :::note The numbers in the tables were measured using a [realistic process](https://github.com/camunda/camunda/blob/main/load-tests/load-tester/src/main/resources/bpmn/realistic/bankCustomerComplaintDisputeHandling.bpmn) with a [realistic payload](https://github.com/camunda/camunda/blob/main/load-tests/load-tester/src/main/resources/bpmn/realistic/realisticPayload.json) (~11 KB). To calculate day-based metrics, an equal distribution over 24 hours is assumed. ::: ## Scale your cluster Once you have a baseline configuration running, you can scale in several ways: ### Horizontal scaling Add more brokers and partitions to increase throughput capacity. Partitions can be [scaled up](/self-managed/components/orchestration-cluster/zeebe/operations/cluster-scaling.md) but not down, so avoid over-provisioning. When scaling horizontally, **secondary storage often becomes the limiting factor**. Adding brokers increases export volume to Elasticsearch/OpenSearch, if secondary storage isn't scaled accordingly, it will bottleneck overall throughput. See [Elasticsearch scaling](#elasticsearch-scaling) for guidance. ### Vertical scaling Increase CPU and memory per broker. Note that there are **diminishing returns** due to component interdependencies. For example, Elasticsearch indexing speed can bottleneck broker throughput). ### Elasticsearch scaling - **Memory:** Increase Elasticsearch memory to store more historical data without performance degradation. - **Nodes:** Add Elasticsearch statefulset replicas for more IOPS and query throughput. - **Disk:** Increase disk size based on your data retention requirements. With Optimize enabled and a realistic payload (~11 KB), Elasticsearch disk can fill rapidly (for example, 128 Gi in under 12 hours at 1 PI/s with 30-day retention). ## Secondary storage considerations The resource tables above assume Elasticsearch as the secondary storage backend. If you are using a different backend: - **OpenSearch:** Similar resource profile to Elasticsearch. The tables above generally apply. - **RDBMS (PostgreSQL, available from 8.9):** Replace the Elasticsearch resource block with appropriately sized PostgreSQL resources. Adjust throughput expectations **downward by approximately 30%** compared to the Elasticsearch-based tables. Unlike Elasticsearch, RDBMS scales primarily **vertically** (a larger instance) rather than horizontally, so plan your initial sizing with more headroom, as adding capacity later is more disruptive. :::note Optimize is not supported with RDBMS. If you need Optimize, you must also run Elasticsearch alongside your RDBMS. ::: See [Secondary storage](sizing-your-environment.md#secondary-storage) for more details. ## Next steps Validate your chosen configuration by [running your own benchmarks](sizing-benchmarks.md). --- ## Size your environment Understand the aspects relevant to Camunda 8 sizing. Once you do, use the sizing recommendations for [SaaS](sizing-saas.md) or [Self-Managed](sizing-self-managed.md) to select your appropriate configuration. ## Sizing requirements and influencing factors Consider the following aspects when planning and sizing Camunda SaaS or Self-Managed. ### Data availability latency Data availability latency is the time between an event occurring in the engine and it being queryable in Operate, Tasklist, or Optimize. Under heavy load or with Optimize enabled, this can lag from seconds to minutes. Data availability latency is influenced by: - **Exporter throughput:** The rate at which the Camunda Exporter can write events to Elasticsearch (ES). - **Elasticsearch indexing speed:** How quickly ES can index incoming documents. - **Elasticsearch disk usage:** High disk utilization (above ~70%) significantly increases indexing latency. Monitor ES disk usage and scale storage before hitting this threshold. ### Disk space The workflow engine stores data for each process instance, especially to persist the current state. In addition, it sends data to secondary storage (Elasticsearch, OpenSearch, or an RDBMS) for indexing, search, analytics, and long-term retention. You can configure retention times for data stored in secondary storage. ### Impact of Optimize Optimize is an optional component that provides process analytics and reporting. When enabled, it has significant implications for sizing. #### Why Optimize matters for sizing - Optimize reads data from Elasticsearch (exported by the Camunda Exporter) and writes it back to its own Elasticsearch indices for analytics and reporting. This creates additional load on Elasticsearch. - In Camunda 8.8+, the Camunda Exporter and the Elasticsearch exporter run in the same thread within the broker. This means Optimize export directly competes with core platform export for throughput. - Benchmarks show a **25-50% throughput reduction** when Optimize is enabled vs. disabled, depending on workload and payload size. #### What Optimize affects - **Throughput:** Fewer tasks/second achievable at the same hardware level when Optimize is running. - **Disk space:** Optimize stores significant amounts of data in Elasticsearch, especially with large payloads. In testing, 128 Gi of ES disk was consumed in under 12 hours at 1 PI/s with the realistic payload (~11 KB) and 30-day retention. - **Elasticsearch resources:** More CPU, memory, and disk are needed for ES when Optimize is enabled. - **Import time:** Optimize import time increases approximately linearly with payload size. Larger payloads (for example, 11 KB realistic vs. 0.5 KB typical) result in proportionally longer import times. - **Report loading times:** As historical data accumulates in Elasticsearch, Optimize report loading times increase approximately linearly. #### Mitigations - Consider running Optimize on a separate Elasticsearch instance to isolate its load from the core platform. - Use variable filtering to reduce the amount of data exported/imported by Optimize. - Tune retention periods: shorter retention means less data in ES, and better performance. - Disable variable import entirely if variables are not needed in Optimize reports. The sizing guidance for [Self-Managed](sizing-self-managed.md#baseline-resource-configuration) provides configurations with and without Optimize to help you plan accordingly. ### Latency and cycle time In some use cases, process cycle time (or even individual task cycle time) matters. For example, you might expose a REST endpoint that starts a process instance to calculate a customer score. The process runs four service tasks, and the REST request must return synchronously within 250 ms. While service-task duration depends on the work performed, you can measure the workflow engine’s own overhead. :::note The latency measurements below are approximate and were last validated against an earlier version of Camunda 8. Updated measurements for 8.8/8.9 are pending. With the 8.8 streamlined architecture and properly aligned resources (3.5 CPU cores per broker), latency is expected to improve by approximately 2x compared to the previous distributed deployment. Actual latency is highly environment-dependent — factors like network latency between workers and the cluster, disk I/O speed (commit latency), and cloud region placement significantly affect these numbers. ::: As a rough estimate, you can expect: - Single-digit millisecond processing time per process node. - Approximately 50 ms latency to process service tasks in remote workers when running worker code in the same cloud region as the Camunda cluster. Hence, executing four service tasks results in roughly 200-250 ms workflow engine overhead. As you push throughput toward the cluster’s limits, latency increases because requests compete for resources, especially disk writes. If cycle time and latency matter, leave enough headroom and avoid running the cluster near full utilization to prevent resource contention. :::tip A good rule of thumb is to size for about **20x your average load**. This gives you capacity for peaks and keeps latency low during normal operation. ::: | Indicator | Number | Calculation method | Notes | | :------------------------------------------------------------- | --------: | :----------------: | :----------------------------------------------------------------------------------------- | | Onboarding instances per year | 5,000,000 | | Business input. | | Expected process instances on peak day | 150,000 | | Business input. | | Process instances per second within business hours on peak day | 5.20 | / (8\*60\*60) | Only looking at the seconds within the eight business hours of a day. | | Process instances per second including buffer | 104.16 | \* 20 | Adding some buffer is recommended for critical, high-performance or low-latency use cases. | ### Payload size Each process instance can hold a payload, known as [process variables](/components/concepts/variables.md). The workflow engine must manage the variables for all running instances, and data from both running and completed process instances is forwarded to Operate and Tasklist. Process variable size affects resource requirements. For example, there’s a big difference between storing a few strings (around 1 KB) and storing a full 1 MB JSON document. That’s why payload size is a key sizing factor. Camunda's official benchmarks use two reference payloads: - [**Typical payload**](https://github.com/camunda/camunda/blob/main/load-tests/load-tester/src/main/resources/bpmn/typical_payload.json): Used for baseline measurements (~0.5 KB, 15 simple variables). - [**Realistic payload**](https://github.com/camunda/camunda/blob/main/load-tests/load-tester/src/main/resources/bpmn/realistic/realisticPayload.json): Used for the reference sizing benchmarks. This better represents real-world payloads (~11 KB). :::note Payload size has a multiplicative effect, affecting Zeebe storage, Elasticsearch export volume, Optimize import time, and query/report performance. An 11 KB payload vs. a 0.5 KB payload can change disk consumption by **10-20x**. ::: Consider these general rules for payload size: - The maximum [variable size per process instance is limited](/components/concepts/variables.md#variable-size-limitation), currently to roughly three MB. - Camunda does not recommend storing large amounts of data in your process context. Refer to our [best practices on handling data in processes](/components/best-practices/development/handling-data-in-processes.md) for more details. - Each [partition](/components/zeebe/technical-concepts/partitions.md) of the Zeebe installation can typically handle up to one GB of payload in total. Larger payloads can lead to slower processing. For example, one million process instances with four KB each is about 3.9 GB, so you need at least four partitions. In practice, you’d typically use six partitions, since the number of partitions is usually a multiple of the replication factor (three by default). ### Peak loads In most scenarios, your load will be volatile rather than constant. For example, your company might start 90% of its monthly process instances on a single day of the month. The **ability to handle those peaks is the more crucial requirement and should drive your decision**, rather than the average load. In this example, that single peak day defines your overall throughput requirements. In addition, sizing for peaks may mean you shouldn’t assume a full 24-hour day. Instead, you might size for just the eight business hours, or even the busiest two hours—depending on your workload. ### Secondary storage Starting with Camunda 8.9, the platform supports three secondary storage backends, each with different sizing implications. #### Elasticsearch (default) - The **most mature and most benchmarked** option. - Required if you use Optimize. - Provides full-text search capabilities used by Operate and Tasklist. :::important Sizing data provided throughout this guide assumes Elasticsearch unless stated otherwise. ::: #### OpenSearch - A drop-in alternative to Elasticsearch with a similar resource profile. - Supported for all components including Optimize. See [supported environments](/reference/supported-environments.md) for more details. - Sizing recommendations for Elasticsearch generally apply to OpenSearch as well. #### RDBMS - A different storage paradigm: a relational database instead of a document store. See the full list of [supported databases](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md#supported-rdbms). - A different resource profile: CPU/memory-oriented rather than disk/IOPS-oriented. - Write throughput is approximately **70% of Elasticsearch** on equivalent hardware. - **No Optimize support**: If you need Optimize, you must run Elasticsearch alongside RDBMS. - **Scales primarily vertically** rather than horizontally like Elasticsearch. Plan initial sizing with more headroom, as adding capacity is more disruptive. - Ideal for organizations that already operate a supported RDBMS at scale and want to avoid adding Elasticsearch to their infrastructure. ### Throughput Throughput defines how many process instances can be executed within a certain timeframe. It is typically easy to estimate the number of process instances per day you need to execute. However, hardware sizing depends more on the **number of BPMN tasks** in a process model. If you already know your future process model, you can use it to count the number of tasks in the process. For example, the following onboarding process contains five service tasks in a typical execution: :::tip If you don't yet know the number of service tasks, Camunda recommends assuming **10 service tasks** as a rule of thumb. ::: The number of tasks per process allows you to calculate the number of tasks per day. You can also convert this to tasks per second. For example: | Indicator | Number | Calculation method | Notes | | :--------------------------------- | --------: | :----------------: | :------------------------------------------- | | Onboarding instances per year | 5,000,000 | | Business input. | | Process instances per business day | 20,000 | / 250 | Average number of working days in a year. | | Tasks per day | 100,000 | \* 5 | Tasks in the process model as counted above. | | Tasks per second | 1.16 | / (24\*60\*60) | Seconds per day. | In most cases, Camunda defines throughput per day, as this time frame is easier to understand. However, in high-performance use cases, you might need to define the throughput per second. ## Plan non-production environments All clusters can be used for development, testing, integration, Q&A, and production. For typical integration or functional test environments, you can usually deploy a small cluster even if your production environment is sized larger. This is typically sufficient, as functional tests run much smaller workloads. Load or performance tests should ideally run on the same sizing configuration as your production cluster to yield reliable results. A typical customer setup consists of: - A production cluster. - An integration or pre-production cluster (equal in size to your anticipated production cluster if you want to run load tests or benchmarks). - A test cluster. - Development clusters. ## Next steps Now that you understand the factors that influence sizing: - **SaaS customers:** [Size your SaaS cluster](sizing-saas.md) to select the right cluster size. - **Self-Managed admins:** Provision your Kubernetes cluster using these [baseline resource settings](sizing-self-managed.md). - **Validate sizing:** [Run your own benchmarks](sizing-benchmarks.md) to test your specific workload. For current secondary storage benchmarks, see [RDBMS benchmark results](/self-managed/concepts/secondary-storage/rdbms-benchmark-results.md). --- ## Understanding human task management ## Using task assignment features The lifecycle of human task orchestration (like assigning, delegating, and completing tasks) is mostly a generic issue. There is no need to model common aspects into all your processes, if often makes models unreadable. Use Camunda task management features or implement your requirements in a generic way. ![Task assignment](understanding-human-tasks-management-assets/human-tasks.png) So every task can be assigned to either a group of people, or a specific individual. An individual can 'claim' a task, indicating that they are picking the task from the pool (to avoid multiple people working on the same task). As a general rule, you should assign human tasks, like [user tasks](/components/modeler/bpmn/user-tasks/user-tasks.md) or [manual tasks](/components/modeler/bpmn/manual-tasks/manual-tasks.md), in your business process to _groups of people_ instead of specific individuals. ```xml ``` Then, require individual members of that group to explicitly _claim tasks_ before working on them. This way, you avoid different people working on the same task at the same time. Refer to [`assign`](/apis-tools/orchestration-cluster-api-rest/specifications/assign-user-task.api.mdx). You can also directly claim tasks in Camunda Tasklist with the click of a button. ![Claim](understanding-human-tasks-management-assets/claim.png) While assigning users to groups is advised, it's not the only option. You could always assign a task to a _single person_ who is supposed to complete the task (e.g. the individual 'customer' of your process or a coworker having specific knowledge for the case). You will need to have access to the specific person relevant for your process instance, e.g. via a process variable: ```xml ``` ## Deciding about your task list frontend If you are orchestrating human tasks in your process, you must make up your mind on how exactly you want to let your users work on their tasks and interact with the workflow engine. You have basically three options: - [Camunda Tasklist](/components/tasklist/introduction-to-tasklist.md): The Tasklist application shipped with Camunda. This works out-of-the-box and has a low development effort. However, it is limited in terms of customizability and how much you can influence the user experience. - Custom task list application: You can develop a custom task list and adapt this to your needs without compromises. User tasks are shown inside your custom application, following your style guide and usability concept. You will use the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) in the background. This is very flexible, but requires additional development work. - Third party tasklist: If our organization already has a task list application rolled out to the field, you might want to use this for tasks created by Camunda. You will need to develop some synchronization mechanism. The upside of this approach is that your end users might not even notice that you introduce a new workflow engine. ### Considerations for developing custom task lists When building a custom tasklist/application, you must plan for the following aspects. You will need to - _Query_ for user tasks and _generate lists_ of those tasks. - _Filter the list_ along specific attributes like current assignee, candidate groups, etc. - _Select_ and _display_ the right forms for starting processes and completing tasks. - Use _custom/business value_ data in order to _filter_ with those values and _display_ them correlated with the task list and within forms. - _Authorize_ users to access those lists, filters, and forms. ### Considerations for using third party task lists When integrating a third party tasklist, you must plan for the following aspects. You will need to take care of: - _Creating_ tasks in the third party tasklist based on the user tasks created by Camunda. - _Completing_ tasks in Camunda and move on process execution based on user action in the third party tasklist. - _Cancelling_ tasks, triggered by Camunda or triggered by the user in the third-party tasklist. - Transferring _business data_ to be edited in the third-party tasklist back and forth. Your third party tasklist application also needs to allow for some programmatic control of the lifecycle of its tasks. The third-party application _must have_ the ability: - To programmatically _create_ a new task. - To _hook in code_ which programmatically informs other systems that the user is about to change a task's state. - To _manage custom attributes_ connected to a task and programmatically access them. Additionally, it _should have_ the ability - To programmatically _delete_ a task which was cancelled in Camunda. Without this possibility such tasks remain in the users tasklist and would need to be removed manually. Depending on the way you integrate the task completion mechanism, when the user tries to complete such tasks, they would immediately observe an error or the action would just not matter anymore and serve as a removal from the list. Transfer just the minimal amount of business data in between Camunda and your third-party tasklist application. For creating tasks, transfer just the taskId and important business data references/ids to your domain objects. As much as possible should be retrieved later, and just when needed (e.g. when displaying task forms to the user) by requesting data from the process engine or by requesting data directly from other systems. For completing tasks, transfer just the business data which originated from Camunda and was changed by the user. This means, in case you just maintain references, nothing needs to be transferred back. All other business data changed by the user will be directly transferred to the affected systems. ### Task lists may not look like task lists There are situations where you might want to show a user interface that does not look like a task list, even if it is fed by tasks. The following _example_ shows such a situation in the document _input management_ process of a company. Every document is handled by a separate process instance, but users typically look at complete mailings consisting of several such documents. In a customer scenario, there were people in charge of assessing the scanned mailing and distributing the individual documents to the responsible departments. It was important to do that in one step, as sometimes documents referred to each other. So you have several user tasks which are heavily _interdependent_ from a business point of view and should therefore be completed _in one step_ by the same person. The solution to this was a custom user interface that basically queries for human tasks, but show them grouped by mailings: ![custom tasklist mockup](understanding-human-tasks-management-assets/tasklist-mockup.png) 1 The custom tasklist shows each mailing as one "distribution task", even though they consist of several human tasks fetched from the workflow instance. 2 The custom user interface allows you to work on all four human tasks at once. By dragging and dropping a document within the tree, the user can choose to which department the document is delivered to. 3 In case the user detects a scanning problem, they can request a new scan of the mailing. But as soon as all documents are quality assured, the button **Distribute Mailing** gets enabled. By clicking on it, the system completes all four human tasks - one for each document - which moves forward the four process instances associated with the documents. --- ## Best Practices The Camunda Best Practices distill our experience with BPMN and DMN on the Camunda toolstack, incorporating insights from consulting, community feedback, and various interactions. They offer a blend of conceptual and practical guidance, representing our current practical project experience in a generalized context. While not definitive, these practices acknowledge that learning is ongoing and effectiveness may vary based on your specific situation. Please note that, like the core product, Camunda extends the same guarantee to Best Practices. However, we cannot ensure the absolute accuracy or timeliness of the information provided, and any liability for damages resulting from the application of these recommendations is disclaimed. ## Project management Best Practices ## Architecture Best Practices ## Development Best Practices ## Modeling Best Practices ## Operations Best Practices --- ## Element templates at scale To effectively manage large libraries of reusable building blocks ([element templates](/components/concepts/element-templates.md)), you can create a pipeline that: - Provisions the [dependencies of element templates](/components/modeler/element-templates/element-template-with-dependencies.md) to required clusters. - Makes templates available at design time to multiple [workspaces](/components/hub/workspace/modeler/collaboration/use-shared-project-for-organization-wide-collaboration.md) within an organization. ![Pipeline goal](./img/pipeline-goal.png) This guide covers conceptually what your pipeline needs to do, from obtaining credentials to runtime provisioning and template syncing. ## Prerequisites Before building your pipeline, ensure you have the following: | Prerequisite | Purpose | | ------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [Git Repository](https://en.wikipedia.org/wiki/Git) | Store all element templates | | Template state management | Maintain an authoritative inventory (for example, via Git or an IaC tool like Terraform) that defines which templates are applied to each cluster and which workspaces depend on them. This source acts as the single source of truth for template deployments. | | [Web Modeler API token](/apis-tools/web-modeler-api/authentication.md) | Access Web Modeler programmatically | | [Orchestration Cluster API client](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md) | Provision dependencies to clusters | For simplicity, this guide assumes: - One organization - One cluster - One [workspace](/components/hub/workspace/modeler/collaboration/use-shared-project-for-organization-wide-collaboration.md) - A pipeline handling runtime provisioning and template syncing ## Runtime provisioning ### Secrets You can use sensitive information in your element templates without exposing it in your BPMN processes by referencing secrets. These guides show you how to configure them depending on the environment you are using: - **SaaS**: Use the [Administration API](/apis-tools/administration-api/administration-api-reference.md) or [Camunda Hub UI](/components/hub/organization/manage-clusters/manage-secrets.md) to configure secrets. - **Self-Managed/local development**: Configure secrets outside the pipeline. See [connector secrets](/self-managed/components/connectors/connectors-configuration.md#secrets). ### Job Workers As part of the pipeline, you may spin up a service that will connect to a Camunda cluster to perform specific tasks. For example, you can use the [Spring Boot Camunda Starter](/apis-tools/camunda-spring-boot-starter/getting-started.md) to start a job worker. Recommended resources: - [Outbound connectors vs. job workers](/components/concepts/outbound-connectors-job-workers.md) - [Host custom connectors](/components/connectors/custom-built-connectors/host-custom-connector.md) ### Other dependencies The following dependency types are provisioned at runtime using the [Orchestration Cluster API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md): | Dependency | Purpose | | --------------------------------------------------------------------- | --------------------------- | | [Camunda forms](/components/modeler/forms/camunda-forms-reference.md) | Used in user tasks | | [RPA scripts](/components/rpa/overview.md) | Used in service tasks | | [BPMN processes](/components/modeler/bpmn/bpmn.md) | Used in call activities | | [DMN decisions](/components/modeler/dmn/dmn.md) | Used in business rule tasks | :::note RPA scripts are not supported in Web Modeler ::: To deploy dependencies, send a [POST request](/apis-tools/orchestration-cluster-api-rest/specifications/create-deployment.api.mdx) with the files. This works for SaaS, self-managed, and local development. For example: ```bash curl -L 'http://localhost:8080/v2/deployments' \ -H 'Accept: application/json' \ -F resources=@/path/to/your/form/user-signup.form ``` You will get a response containing the details of the deployed elements: ```json { "deployments": [ { "form": { "formKey": "KEY_OF_THE_FORM", "formId": "user-signup", "version": 1, "resourceName": "user-signup.form", "tenantId": "" } } ], "deploymentKey": "KEY_OF_THE_DEPLOYMENT", "tenantId": "" } ``` When referencing a dependency such as a form, Camunda recommends using a `versionTag` as your [binding type](/components/best-practices/modeling/choosing-the-resource-binding-type.md#supported-binding-types). This option ensures the right version of the target resource is always used. ## Making templates available in Web Modeler The pipeline can make templates available in Web Modeler using the [Web Modeler API](/apis-tools/web-modeler-api/index.md): 1. **Get project metadata**: Retrieve the project ID to know which content is available. This allows the pipeline to translate repository changes into CRUD operations. ```json title="POST /api/v1/projects/search" { "filter": { "name": "(PROJECT NAME)" }, "page": 0, "size": 50 } ``` Pagination is enforced for all `search` endpoints. Ensure you retrieve all relevant pages. Alternatively, a separate system of record can maintain project IDs. 2. **Get file metadata**: With the project ID, retrieve a list of files and metadata to check which repository files need to be created or updated. ```json { "filter": { "projectId": "(PROJECT ID)" }, "page": 0, "size": 50 } ``` 3. **Create or update files**: For each repository file, execute the appropriate request based on whether it needs to be [created](https://modeler.camunda.io/swagger-ui/index.html#/Files/createFile) or [updated](https://modeler.camunda.io/swagger-ui/index.html#/Files/patchFile). 4. **Publish versions**: For each file, determine if a new version is needed and publish it to the project using the [Versions](https://modeler.camunda.io/swagger-ui/index.html#/Versions) resource. This makes the templates available to BPMN diagrams inside the project. ## Making templates available in Desktop Modeler To set up your local environment: - Access the VCS repository containing the templates. - Choose how to [configure them](/components/modeler/desktop-modeler/element-templates/configuring-templates.md) depending on your needs. If your templates are reused across multiple projects, configuring them globally will make it easier to maintain. For project-specific templates, consider making them available only for that project to avoid exposing templates to projects that should not be using them. :::note If you are the template creator/maintainer, include a `README` file in your repository that lists the requirements for using your templates -- for example, which dependencies need to be provisioned in advance. ::: ## Next steps Refer to [Integrate Web Modeler in CI/CD](/components/hub/workspace/modeler/integrate-modeler-in-ci-cd.md) for additional CI/CD-related guidance. --- ## Connecting the workflow engine with your world One of your first tasks to build a process solution is to sketch the basic architecture of your solution. To do so, you need to answer the question of how to connect the workflow engine (Zeebe) with your application or with remote systems. The workflow engine is a remote system for your applications, just like a database. Your application connects with Zeebe via remote protocols (like [gRPC](https://grpc.io/) or REST), which is typically hidden from you, like when using a database driver based on ODBC or JDBC. With Camunda 8 and the Zeebe workflow engine, there are two basic options: 1. Write some **programming code** that typically leverages the client library for the programming language of your choice. 2. Use some **existing connector** which just needs a configuration. The trade-offs will be discussed later; let’s look at the two options first. ## Programming glue code To write code that connects to Zeebe, you typically embed [the Zeebe client library](../../../apis-tools/working-with-apis-tools.md) into your application. An application can of course also be a service or microservice. If you have multiple applications that connect to Zeebe, all of them will require the client library. If you want to use a programming language where no such client library exists, you can [generate a gRPC client yourself](https://camunda.com/blog/2018/11/grpc-generating-a-zeebe-python-client/). ![Clients to Zeebe](connecting-the-workflow-engine-with-your-world-assets/clients.png) Your application can basically do two things with the client: 1. **Actively call Zeebe**, for example, to start process instances, correlate messages, or deploy process definitions. 2. **Subscribe to tasks** created in the workflow engine in the context of BPMN service tasks. ### Calling Zeebe Using the Zeebe client’s API, you can communicate with the workflow engine. The two most important API calls are to start new process instances and to correlate messages to a process instance. **Start process instances using the** [**Java Client**](../../../apis-tools/java-client/getting-started.md)**:** ```java processInstance = zeebeClient.newCreateInstanceCommand() .bpmnProcessId("someProcess").latestVersion() .variables( someProcessVariablesAsMap ) .send() .exceptionally( throwable -> { throw new RuntimeException("Could not create new instance", throwable); }); ``` **Correlate messages to process instances using the Java Client**: ```java zeebeClient.newPublishMessageCommand() // .messageName("messageA") .messageId(uniqueMessageIdForDeduplication) .correlationKey(message.getCorrelationid()) .variables(singletonMap("paymentInfo", "YeahWeCouldAddSomething")) .send() .exceptionally( throwable -> { throw new RuntimeException("Could not publish message " + message, throwable); }); ``` **Correlate messages to process instances using the Node.js client**: ```js zbc.publishMessage({ name: "messageA", messageId: messageId, correlationKey: correlationId, variables: { valueToAddToWorkflowVariables: "here", status: "PROCESSED", }, timeToLive: Duration.seconds.of(10), }); ``` This allows you to connect Zeebe with any external system by writing some custom glue code. We will look at common technology examples to illustrate this in a minute. ### Subscribing to tasks using a job worker To implement service tasks of a process model, you can write code that subscribes to the workflow engine. In essence, you will write some glue code that is called whenever a service task is reached (which internally creates a job, hence the name). **Glue code in Java:** ```java class ExampleJobHandler implements JobHandler { public void handle(final JobClient client, final ActivatedJob job) { // here: business logic that is executed with every job client.newCompleteCommand(job.getKey()).send() .exceptionally( throwable -> { throw new RuntimeException("Could not complete job " + job, throwable); });; } } ``` **Glue code in Node.js:** ```js function handler(job, complete, worker) { // here: business logic that is executed with every job complete.success(); } ``` Now, this handler needs to be connected to Zeebe, which is generally done by subscriptions, which internally use long polling to retrieve jobs. **Open subscription via the Zeebe Java client:** ```java zeebeClient .newWorker() .jobType("serviceA") .handler(new ExampleJobHandler()) .timeout(Duration.ofSeconds(10)) .open()) {waitUntilSystemInput("exit");} ``` **Open subscription via the Zeebe Node.js client:** ```js zbc.createWorker({ taskType: "serviceA", taskHandler: handler, }); ``` You can also use integrations in certain programming frameworks, like the [Camunda Spring Boot Starter](../../../apis-tools/camunda-spring-boot-starter/getting-started.md) in the Java world, which starts the job worker and implements the subscription automatically in the background for your glue code. **A subscription for your glue code is opened automatically by the Spring integration:** ```java @JobWorker(type = "serviceA") public void handleJobFoo(final JobClient client, final ActivatedJob job) { // here: business logic that is executed with every job // you do not need to call "complete" on the job, as autoComplete is turned on above } ``` There is also documentation on [how to write a good job worker](../writing-good-workers/). ## Technology examples Most projects want to connect to specific technologies. Currently, most people ask for REST, messaging, or Kafka. REST and messaging are common core integration patterns. Kafka is different: it is optional infrastructure that you would typically introduce for event streaming or broader event-driven architectures, not because Zeebe requires it to execute workflows. ### REST You could build a piece of code that provides a REST endpoint in the language of choice and then starts a process instance. The [Ticket Booking Example](https://github.com/berndruecker/ticket-booking-camunda-cloud) contains an example using Java and Spring Boot for the [REST endpoint](https://github.com/berndruecker/ticket-booking-camunda-cloud/blob/master/booking-service-java/src/main/java/io/berndruecker/ticketbooking/rest/TicketBookingRestController.java#L35). Similarly, you can leverage the [Spring Boot extension](https://github.com/zeebe-io/spring-zeebe/) to startup job workers that will [execute outgoing REST calls](https://github.com/berndruecker/ticket-booking-camunda-cloud/blob/master/booking-service-java/src/main/java/io/berndruecker/ticketbooking/adapter/GenerateTicketAdapter.java#L29). ![REST example](connecting-the-workflow-engine-with-your-world-assets/rest-example.png) You can find [Spring Boot sample code for the REST endpoint](https://github.com/berndruecker/flowing-retail/blob/master/zeebe/java/checkout/src/main/java/io/flowing/retail/checkout/rest/ShopRestController.java) in the [Flowing Retail example](https://github.com/berndruecker/flowing-retail). ### Messaging You can do the same for messages, which is often [AMQP](https://en.wikipedia.org/wiki/Advanced_Message_Queuing_Protocol) nowadays. The [Ticket Booking Example](https://github.com/berndruecker/ticket-booking-camunda-cloud) contains an example for RabbitMQ, Java, and Spring Boot. It provides a message listener to correlate incoming messages with waiting process instances, and [glue code to send outgoing messages onto the message broker](https://github.com/berndruecker/ticket-booking-camunda-cloud/blob/master/booking-service-java/src/main/java/io/berndruecker/ticketbooking/adapter/RetrievePaymentAdapter.java). ![Messaging example](connecting-the-workflow-engine-with-your-world-assets/messaging-example.png) [Service integration patterns](../service-integration-patterns/) goes into details of if you want to use a send and receive task here, or prefer simply one service task (spoiler alert: send and receive tasks are used here because the payment service might be long-running; think about expired credit cards that need to be updated or wire transfers that need to happen). ### Apache Kafka Kafka is not required for Camunda 8 or Zeebe to work. Zeebe executes workflows itself, while Kafka can optionally be used as an event backbone around the workflow engine. Typical Kafka-based patterns are: - **Kafka to Zeebe**: Consume records from a Kafka topic and translate them into Zeebe API calls, such as starting a process instance or correlating a message. - **Zeebe to Kafka**: When a workflow reaches a service task or other integration point, write a record to Kafka so downstream systems can react asynchronously. You can implement these patterns with custom glue code. The [Flowing Retail example](https://github.com/berndruecker/flowing-retail) shows this using Java, Spring Boot, and Spring Cloud Streams. There is [code to subscribe to a Kafka topic and start new process instances for new records](https://github.com/berndruecker/flowing-retail/blob/master/kafka/java/order-zeebe/src/main/java/io/flowing/retail/kafka/order/messages/MessageListener.java#L39), and there is some glue code to create new records when a process instance executes a service task. Of course, you could also use other frameworks to achieve the same result. This means Kafka is a good fit if you already use Kafka, need loose coupling, or want to broadcast workflow-related events to multiple consumers. If you simply need to call a remote system from a workflow, a job worker or connector is often the more direct option. ![Kafka Example](connecting-the-workflow-engine-with-your-world-assets/kafka-example.png) ## Designing process solutions containing all glue code Typical applications will include multiple pieces of glue code in one codebase. ![Architecture with glue code](connecting-the-workflow-engine-with-your-world-assets/architecture.png) For example, the onboarding microservice shown in the figure above includes: - A REST endpoint that starts a process instance (1) - The process definition itself (2), probably auto-deployed to the workflow engine during the startup of the application. - Glue code subscribing to the two service tasks that shall call a remote REST API (3) and (4). A job worker will be started automatically as part of the application to handle the subscriptions. In this example, the application is written in Java, but again, it could be [any supported programming language](/apis-tools/working-with-apis-tools.md). As discussed in [writing good workers](../writing-good-workers/), you typically will bundle all workers within one process solution, but there are exceptions where it makes sense to have single workers as separate application. ## Connectors The glue code is relatively simple, but you need to write code. You might prefer using an out-of-the-box component, connecting Zeebe with the technology you need just by configuration. This component is called a **Connector**. A connector can be uni or bidirectional and is typically one dedicated application that implements the connection that translates in one or both directions of communication. Such a connector might also be helpful in case integrations are not that simple anymore. ![Connectors](connecting-the-workflow-engine-with-your-world-assets/connector.png) For example, the [HTTP connector](https://github.com/camunda-community-hub/zeebe-http-worker) is a one-way connector that contains a job worker that can process service tasks doing HTTP calls as visualized in the example in the following figure: ![REST connectors](connecting-the-workflow-engine-with-your-world-assets/rest-connector.png) Another example is the [Kafka connector](https://github.com/camunda-community-hub/kafka-connect-zeebe), as illustrated below. ![Kafka connector](connecting-the-workflow-engine-with-your-world-assets/kafka-connector.png) This is a bidirectional connector which contains a Kafka listener for forwarding Kafka records to Zeebe and also a job worker which creates Kafka records every time a service task is executed. In other words, the connector helps Kafka exchange events with Zeebe; it does not replace Zeebe's own workflow execution or state handling. This is illustrated by the following example: ![Kafka connector Details](connecting-the-workflow-engine-with-your-world-assets/kafka-connector-details.png) ### Out-of-the-box connectors As well as Camunda-maintained connectors, additional connectors are maintained by the community (made up of consultants, partners, customers, and enthusiastic individuals). You can find a list of connectors in the [Camunda Marketplace](https://marketplace.camunda.com/). ### Reusing your own integration logic by extracting connectors If you need to integrate with certain infrastructure regularly, for example your CRM system, you might also want to create your own CRM connector, run it centralized, and reuse it in various applications. In general, we recommend not to start such connectors too early. Don’t forget that such a connector gets hard to adjust once in production and reused across multiple applications. Also, it is often much harder to extract all configuration parameters correctly and fill them from within the process, than it would be to have bespoke glue code in the programming language of your choice. Therefore, only extract a full-blown connector if you understand exactly what you need. Don’t forget about the possibility to extract common glue code in a simple library that is then used at different places. :::note Updating a library that is used in various other applications can be harder than updating one central connector. In this case, the best approach depends on your scenario. ::: Whenever you have such glue code running and really understand the implications of making it a connector, as well as the value it will bring, it can make a lot of sense. ## Recommendation As a general rule of thumb, prefer custom glue code whenever you don’t have a good reason to go with an existing connector. A good reason to use connectors is if you need to solve complex integrations where little customization is needed, such as the [Camunda RPA bridge](https://docs.camunda.org/manual/latest/user-guide/camunda-bpm-rpa-bridge/) to connect RPA bots (soon to be available for Camunda 8). Good use of connectors are also scenarios where you don’t need custom glue code. For example, when orchestrating serverless functions on AWS with the [AWS Lambda connector](https://github.com/camunda-community-hub/zeebe-lambda-worker). This connector can be operated once and used in different processes. Some use cases also allow you to create a **reusable generic adapter**; for example, to send status events to your business intelligence system. But there are also common downsides with connectors. First, the possibilities are limited to what the creator of the connector has foreseen. In reality, you might have slightly different requirements and hit a limitation of a connector. Second, the connector requires you to operate this connector in addition to your own application. The complexity associated with this depends on your environment. Third, testing your glue code gets harder, as you can’t easily hook in mocks into such a connector as you could in your own glue code. --- ## Dealing with problems and exceptions ## Understanding workers :::caution Camunda 8 only The description of workers targets Camunda 8, even if [external tasks in Camunda 7](https://docs.camunda.org/manual/latest/user-guide/process-engine/external-tasks/) are conceptually similar. ::: First, let's briefly examine how a worker operates. Whenever a process instance arrives at a service task, a new job is created and pushed to an internal persistent queue within Camunda 8. A client application can subscribe to these jobs with the workflow engine by the task type name (which is comparable to a queue name). If there is no worker subscribed when a job is created, the job is simply put in a queue. If multiple workers are subscribed, they are competing consumers, and jobs are distributed among them. ![Worker concept](dealing-with-problems-and-exceptions-assets/worker-concept.png) Whenever the worker has finished whatever it needs to do (like invoking the REST endpoint), it sends another call to the workflow engine, which [can be one of these three](/components/concepts/job-workers.md#completing-or-failing-jobs): - [`CompleteJob`](/apis-tools/zeebe-api/gateway-service.md#completejob-rpc): The service task went well, the process instance can move on. - [`FailJob `](/apis-tools/zeebe-api/gateway-service.md#failjob-rpc): The service task failed, and the workflow engine should handle this failure. There are two possibilities: - `remaining retries > 0`: The job is retried. - `remaining retries <= 0`: An [incident](/components/concepts/incidents.md) is raised and the job is not retried until the incident is resolved. - [`ThrowError`](/apis-tools/zeebe-api/gateway-service.md#throwerror-rpc): A BPMN error is reported, which typically is handled on the BPMN level. As the glue code in the worker is external to the workflow engine, there is **no technical transaction spanning both components**. Technical transactions refer to ACID (atomic, consistent, isolated, durable) properties, mostly known from relational databases. If, for example, your application leverages those capabilities, your business logic is either successfully committed as a whole, or rolled back completely in case of any error. However, those ACID transactions cannot be applied to distributed systems (the talk [lost in transaction](https://www.youtube.com/watch?v=WRR26jJNh68) elaborates on this). In other words, things can get out of sync if either the job handler or the workflow engine fails. A typical example scenario is the following, where a worker calls a REST endpoint to invoke business logic: ![Typical call chain](dealing-with-problems-and-exceptions-assets/typical-call-chain.png) Technical ACID transaction will only be applied in the business application. The job worker mostly needs to handle exceptions on a technical level, e.g. to control retry behavior, or pass it on to the process level, where you might need to implement business transactions. ## Handling exceptions on a technical level ### Leveraging retries Using the [`FailJob `](/apis-tools/zeebe-api/gateway-service.md#failjob-rpc) API is pretty handy to leverage the built-in retry mechanism of Zeebe. The initial number of retries is set in the BPMN process model: ```xml ``` This number is typically decremented with every attempt to execute the service task. Note that you need to do that in your worker code. Example in Java: ```java @JobWorker(type = "retrieveMoney", autoComplete = false) public void retrieveMoney(final JobClient client, final ActivatedJob job) { try { // your code } catch (Exception ex) { jobClient.newFailCommand(job) .retries(job.getRetries()-1) // <1>: Decrement retries .errorMessage("Could not retrieve money due to: " + ex.getMessage()) // <2> .send() .exceptionally(t -> {throw new RuntimeException("Could not fail job: " + t.getMessage(), t);}); } } ``` 1 Decrement the retries by one. 2 Provide a meaningful error message, as this will be displayed to a human operator once an incident is created in Operate. Example in Node.js: ```js zbc.createWorker("retrieveMoney", (job) => { try { // ... } catch (e) { job.fail("Could not retrieve money due to: " + e.message, job.retries - 1); } }); ``` ### Using incidents Whenever a job fails with a retry count of `0`, an incident is raised. An incident requires human intervention, typically using Operate. Refer to [incidents in the Operate docs](/components/operate/userguide/resolve-incidents-update-variables.md). ### Writing idempotent workers Zeebe uses the **at-least-once strategy** for job handlers, which is a typical choice in distributed systems. This means that the process instance only advances in the happy case (the job was completed, the workflow engine received the complete job request and committed it). A typical failure case occurs when the worker who polled the job crashes and cannot complete the job anymore. [In this case, the workflow engine gives the job to another worker after a configured timeout](/components/concepts/job-workers.md#timeouts). This ensures that the job handler is executed at least once. But this can mean that the handler is executed more than once! You need to consider this in your handler code, as the handler might be called more than one time. The [technical term describing this is idempotency](https://en.wikipedia.org/wiki/Idempotence). For example, typical strategies are described in [3 common pitfalls in microservice integration — and how to avoid them](https://blog.bernd-ruecker.com/3-common-pitfalls-in-microservice-integration-and-how-to-avoid-them-3f27a442cd07). One possibility is to ask the service provider if it has already seen the same request. A more common approach is to implement the service provider in a way that allows for duplicate calls. There are two ways of mastering this: - **Natural idempotency**. Some methods can be executed as often as you want because they just flip some state. Example: `confirmCustomer()`. - **Business idempotency**. Sometimes you have business identifiers that allow you to detect duplicate calls (e.g. by keeping a database of records that you can check). Example: `createCustomer(email)`. If these approaches do not work, you will need to add a **custom idempotency handling** by using unique IDs or hashes. For example, you can generate a unique identifier and add it to the call. This way, a duplicate call can be easily spotted if you store that ID on the service provider side. If you leverage a workflow engine you probably can let it do the heavy lifting. Example: `charge(transactionId, amount)`. See this snippet of a process about how to support custom idempotency handling in a process model: Whatever strategy you use, make sure that you’ve considered idempotency consciously. ## Handling errors on the process level You often encounter deviations from the "happy path" (the default scenario with a positive outcome) which shall be modeled in the process model. ### Using BPMN error events A common way to resolve these deviations is using a BPMN error event, which allows a process model to react to errors within a task. For example: 1 We decide that we want to deal with an exception in the process: in case the invoice cannot be sent automatically... 2 ...we assign a task to a human user, who is now in charge of taking care of delivering the invoice. Learn more about the usage of [error events](/components/modeler/bpmn/error-events/error-events.md) in the user guide. ### Throwing and handling BPMN errors In BPMN process definitions, we can explicitly model an end event as an error. 1 In case the item is not available, we finish the process with an **error end event**. :::note You can mimic a BPMN error in your glue code by using the [`ThrowError`](/apis-tools/zeebe-api/gateway-service.md#throwerror-rpc) API. The consequences for the process are the same as if it were an explicit error end event. So, in case your 'purchase' activity is not a subprocess, but a service task, it could throw a BPMN Error informing the process that the good is unavailable. ::: Example in Java: ```java jobClient.newThrowErrorCommand(job) .errorCode("GOOD_UNAVAILABLE") .errorMessage() .send() .exceptionally(t -> {throw new RuntimeException("Could not throw BPMN error: " + t.getMessage(), t);}); ``` ### Thinking about unhandled BPMN exceptions It is crucial to understand that according to the BPMN spec, a BPMN error is either handled via the process or **terminates the process instance**. It does not lead to an incident being raised. Therefore, you can and normally should always handle the BPMN error. You can, of course, also handle it in a parent process scope like in the example below: 1 The boundary error event deals with the case that the item is unavailable. ### Distinguishing between exceptions and results As an alternative to throwing a Java exception, you can also write a problematic result into a process variable and model an XOR-Gateway later in the process flow to take a different path if that problem occurs. From a business perspective, the underlying problem then looks less like an error and more like a result of an activity, so as a rule of thumb we deal with _expected results_ of activities by means of gateways, but model exceptional errors, which _hinder us in reaching the expected result_ as boundary error events. 1 The task is to "check the customer's credit-worthiness", so we can reason that we _expect as a result_ to know whether the customer is credit-worthy or not. 2 We can therefore model an _exclusive gateway_ working on that result and decide via the subsequent process flow what to do with a customer who is not credit-worthy. Here, we just consider the order to be declined. 3 However, it could be that we _cannot reach a result_, because while we are trying to obtain knowledge about the customer's creditworthiness, we discover that the ID we have is not associated with any known real person. We can't obtain the expected result and therefore model a _boundary error event_. In the example, the consequence is just the same and we consider the order to be declined. ### Business vs. technical errors Note that you have two different ways of dealing with problems at your disposal now: - **Retrying**. You don't want to model the retrying, as you would have to add it to each and every service task. This will bloat the visual model and confuse business personnel. Instead, either retry or fall back to incidents as described above. This is hidden in the visual. - Branch out **separate paths**, as described with the error event. In this context, we found the terms **business error** and **technical error** can be confusing, as they emphasize the source of the error too much. This can lead to long discussions about whether a certain problem is technical or not, and if you are allowed to observe technical errors in a business process model. It's much more important to look at how you react to certain errors. Even a technical problem can qualify for a business reaction. In the above example, upon technical problems with the invoice service you can decide to manually send the invoice (business reaction) or to retry until the invoice service becomes available again (technical reaction). Or, for example, you could decide to continue a process in the event that a scoring service is not available, and simply give every customer a good rating instead of blocking progress. The error is clearly technical, but the reaction is a business decision. In general, we recommend talking about business reactions, which are modeled in your process, and technical reactions, which are handled generically using retries or incidents. ## Embracing business transactions and eventual consistency ### Technical vs business transactions Applications using databases can often leverage ACID (atomic, consistent, isolated, durable) capabilities of that database. This means that some business logic is either successfully committed as a whole, or rolled back completely in case of any error. It is normally referred to as "transactions". Those ACID transactions cannot be applied to distributed systems (the talk [lost in transaction](https://www.youtube.com/watch?v=WRR26jJNh68) elaborates on this), so if you call out to multiple services from a process, you end up with separate ACID transactions at play. The following illustrations are taken from the O'Reilly book [Practical Process Automation](https://processautomationbook.com/): ![Multiple ACID transactions](dealing-with-problems-and-exceptions-assets/multiple-acid-transactions.png) In the above example, the CRM system and the billing system have their local ACID transactions. The workflow engine itself also runs transactional. However, there cannot be a joined technical transaction. This requires a new way of dealing with consistency on the business level, which is referred to as **business transaction**: ![Businss vs technical transaction](dealing-with-problems-and-exceptions-assets/business-vs-technical-transaction.png) A **business transaction** marks a section in a process for which 'all or nothing' semantics (similar to a technical transaction) should apply, but from a business perspective. You might encounter inconsistent states in between (for example a new customer being present in the CRM system, but not yet in the billing system). ### Eventual consistency It is important to be aware that these temporary inconsistencies are possible. You also have to understand the failure scenarios they can cause. In the above example, you could have created a marketing campaign at a moment when a customer was already in the CRM system, but not yet in billing, so they got included in that list. Then, even if their order gets rejected and they never end up as an active customer, they might still receive an upgrade advertisement. You need to understand the effects of this happening. Furthermore, you have to think about a strategy to resolve inconsistencies. The term **eventual consistency** suggests that you need to take measures to get back to a consistent state eventually. In the onboarding example, this could mean you need to deactivate the customer in the CRM system if adding them to the billing system fails. This leads to the consistent state that the customer is not visible in any system anymore. ### Business strategies to handle inconsistency There are three basic strategies if a consistency problem occurs: - Ignore it. While it sounds strange to consider ignoring a consistency issue, it actually can be a valid strategy. It’s a question of how much business impact the inconsistency may have. - Apologize. This is an extension of the strategy to ignore. You don’t try to prevent inconsistencies, but you do make sure that you apologize when their effects come to light. - Resolve it. Tackle the problem head-on and actively resolve the inconsistency. This could be done by different means, such as the reconciliation jobs mentioned earlier, but this practice focuses on how BPMN can help by looking into the Saga pattern. Selecting the right strategy is a clear business decision, as none of them are right or wrong, but simply more or less well suited to the situation at hand. You should always think about the cost/value ratio. ### The Saga pattern and BPMN compensation The Saga pattern describes long-running transactions in distributed systems. The main idea is simple: when you can’t roll back tasks, you undo them. (The name Saga refers back to a paper written in the 1980s about long-lived transactions in databases.) Camunda supports this through BPMN compensation events, which can link tasks with their undo tasks. 1 Assume the customer was already added to the CRM system... 2 ...when an error occurred... 3 ...the process triggers the compensation to happen. This will roll back the business transaction. 4 All compensating activities of successfully completed tasks will be executed, in this case also this one. 5 As a result, the customer will be deactivated, as the API of the CRM system might not allow to simply delete it. --- ## Handling data in processes When using Camunda, you have access to a dynamic map of process variables, which lets you associate data to every single process instance (and local scopes in case of user tasks or parallel flows). Ensure you use these mechanisms in a lightweight and meaningful manner, storing just the relevant data in the process instance. Depending on your programming language, consider accessing your process variables in a type safe way, centralizing (simple and complex) type conversion and using constants for process variable names. ## Understanding data handling in Camunda When reading and interpreting a business process diagram, you quickly realize there is always data necessary for tasks, but also to drive the process through gateways to the correct next steps. Examine the following tweet approval process example: 1 The process instance starts with a freshly written `tweet` we need to remember. 2 We need to present this `tweet` so that the user can decide whether to `approve` it. 3 The gateway needs to have access to this information: was the tweet `approved`? 4 To publish the tweet, the service task again needs the `tweet` itself! Therefore, the tweet approval process needs two variables: | Variable name | Variable type | Sample value | | ------------- | ------------- | ---------------- | | `tweet` | String | "@Camunda rocks" | | `approved` | Boolean | true | In Camunda 8, [values are stored as JSON](/components/concepts/variables.md#variable-values). :::caution Camunda 7 handles variables slightly differently This best practice describes variable handling within Camunda 8. Process variables are handled slightly differently with Camunda 7. Consult the [Camunda 7 documentation](https://docs.camunda.org/manual/latest/user-guide/process-engine/variables/) for details. In essence, variable values are not handled as JSON and thus there are [different values](https://docs.camunda.org/manual/latest/user-guide/process-engine/variables/#supported-variable-values) supported. ::: You can dynamically create such variables by assigning an object of choice to a (string typed) variable name; for example, by passing a `Map` when [completing](/apis-tools/orchestration-cluster-api-rest/specifications/complete-user-task.api.mdx) the "Review tweet" task via the API: ``` // TODO: Double check! completeTask( taskId: "547811" variables: [ { name: "approved" value: true } ] ) ``` In Camunda, you do _not_ declare process variables in the process model. This allows for a lot of flexibility. Refer to recommendations below on how to overcome possible disadvantages of this approach. Consult the [docs about variables](/components/concepts/variables.md#variable-values) to learn more. Camunda does not treat BPMN **data objects** () as process variables. We recommend using them occasionally _for documentation_, but you need to [avoid excessive usage of data objects](../../modeling/creating-readable-process-models#avoiding-excessive-usage-of-data-objects). ## Storing just the relevant data Do not excessively use process variables. As a rule of thumb, store _as few variables as possible_ within Camunda. Please note the [technical limitations of variables sizes](/components/concepts/variables.md#variable-size-limitation). ### Storing references only If you have leading systems already storing the business relevant data... ![Hold references only](handling-data-in-processes-assets/hold-references-only.svg) ...then we suggest you store references only (e.g. ID's) to the objects stored there. So instead of holding the `tweet` and the `approved` variable, the process variables would now, for example, look more like the following: | Variable name | Variable type | Value | | ------------- | ------------- | ----- | | `tweetId` | Long | 8213 | ### Use cases for storing payload Store _payload_ (actual business data) as process variables, if you.... - ...have data only of interest within the process itself (e.g. for gateway decisions). In case of the tweet approval process, even if you are using a tweet domain object, it might still be meaningful to hold the approved value explicitly as a process variable, because it serves the purpose to guide the gateway decision in the process. It might not be true if you want to keep track in the tweet domain objects regarding the approval. | Variable name | Variable type | Value | | ------------- | ------------- | ----- | | `tweetId` | Long | 8213 | | `approved` | Boolean | true | - ...communicate in a _message oriented_ style. For example, retrieving data from one system and handing it over to another system via a process. When receiving external messages, consider storing just those parts of the payload relevant for you, and not the whole response. This not only serves the goal of having a lean process variables map, it also makes you more independent of changes in the service's message interface. - ...want to use the process engine as kind of _cache_. For example, you cannot query relevant customer data in every step for performance reasons. - ...need to _postpone data changes_ in the leading system to a later step in the process. For example, you only want to insert the Tweet in the Tweet Management Application if it is approved. - ...want to track the _historical development_ of the data going through your process. - ...don't have a leading system for this data. ## Using constants and data accessors Avoid the copy/paste of string representations of your process variable names across your code base. Collect the variable names for a process definition in _constants_. For example, in Java: ```java public interface TwitterDemoProcessConstants { String VAR_NAME_TWEET = "tweet"; String VAR_NAME_APPROVED = "approved"; } ``` This way, you have much more security against typos and can easily make use of refactoring mechanisms offered by your IDE. However, if you also want to solve necessary type conversions (casting) or probably even complex serialization logic, we recommend that you use a **Data Accessor** class. It comes in two flavors: - A **Process Data Accessor**: Knows the names and types of all process variables of a certain process definition. It serves as the central point to declare variables for that process. - A **Process Variable Accessor**: Encapsulates the access to exactly one variable. This is useful if you reuse certain variables in different processes. Consider, for example, the BPMN "Publish on Twitter" task in the Tweet Approval Process: 1 We use a **TweetPublicationDelegate** to implement the "Publish on Twitter" task: ```java public class PublishTweetJobHandler implements JobHandler { public void handle(JobClient client, ActivatedJob job) throws Exception { String tweet = job.getVariablesAsType(TwitterDemoProcessVariables.class).getTweet(); // ... ``` The `tweet` variable is accessed in a type safe way. This reusable **Process Data Accessor** class could, for example, be a simple object. The Java client API can automatically deserialize the process variables as JSON into this object, while all process variables that are not found in that class are ignored. ```java public class TwitterDemoProcessVariables { private String tweet; private boolean approved; public String getTweet() { return tweet; } public void setTweet(String tweet) { this.tweet = tweet; } } ``` The getters and setters could further take care of additional serialization and deserialization logic for complex objects. Your specific implementation approach might differ depending on the programming language and framework you are using. ## Complex data as entities There are some use cases when it is clever to _introduce entities alongside the process_ to store complex data in a relational database. You can observe this logically as _typed process context_ where you create custom tables for your custom process deployment. Then, you can even use **Data** **Accessor** classes to access these entities in a convenient way. You will only store a reference to the entity's primary key (typically an artificial UUID) as real process variable within Camunda. Some people refer to this as **externalized process context**. There are a couple of advantages of this approach: - You can do very _rich queries_ on structured process variables via typical SQL. - You can apply custom _data migration strategies_ when deploying new versions of your process or services, which require data changes. - Data can be designed and modeled properly, even graphically by, for example, leveraging UML. It requires additional complexity by adding the need for a relational database and code to handle this. --- ## Local development with element templates and Camunda 8 Run When working with [element templates](/components/concepts/element-templates.md) in your local development environment using [Camunda 8 Run with Docker Compose](/self-managed/quickstart/developer-quickstart/c8run.md), ensure all dependencies are provisioned before you start. This guide explains how to set up element templates in your local environment. ## Prerequisites - [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) installed on your local machine. - Basic knowledge of [element templates with dependencies](/components/modeler/element-templates/element-template-with-dependencies.md). - Familiarity with [custom connectors](/components/connectors/manage-connector-templates.md). ## Provisioning secrets If your element templates use secrets, you must provide these values to the connector runtime. Add secrets to the `connector-secrets.txt` file in the root directory of your Camunda 8 Run setup. Use the following format, with one secret per line: ``` NAME=VALUE ``` These secrets will then be available in the connector runtime using the format `secrets.NAME`. For example: ``` MY_TOKEN=value AWS_KEY=keyValue ... ``` In this case, the `MY_TOKEN` secret can be referenced as `secrets.MY_TOKEN`. This applies when custom connectors are deployed as part of the Camunda 8 Run Docker Compose setup. If you choose to run connectors differently, as described in the [custom connector hosting guide](/components/connectors/custom-built-connectors/host-custom-connector.md#wiring-your-connector-with-a-camunda-cluster), configure secrets as environment variables instead. ## Provisioning a custom connector runtime You can add a custom connector runtime to Camunda 8 Run by copying the `.jar` file containing all connector dependencies into the `custom_connectors` directory in the root folder of your Camunda 8 Run setup. This guide uses a generic [connector template](https://github.com/camunda/connector-template-outbound) as a reference. 1. Clone the repository and run the following command to generate a deployable file: ```bash mvn clean verify package ``` This command creates a file named `target/connector-template-0.1.0-SNAPSHOT-with-dependencies.jar`. 2. Copy the `.jar` file into the `custom_connectors` directory. 3. Start Camunda 8 Run with Docker Compose. For example, from the Docker Compose directory in your Camunda 8 Run setup, run: ```bash docker compose up -d ``` 4. Your connector is ready to execute jobs when a process references it. If you use a different [connector runtime environment](/components/connectors/custom-built-connectors/connector-sdk.md#runtime-environments), ensure that secrets are also exposed to that runtime. ## Provisioning other dependencies ### Using Desktop Modeler Deploy element template dependencies using [Desktop Modeler](/components/modeler/desktop-modeler/index.md) by following the [self-managed deployment guide](/self-managed/components/modeler/desktop-modeler/deploy-to-self-managed.md). This process applies to BPMN diagrams, forms, DMN diagrams, and RPA scripts. ### Using the Cluster API For an automated approach, write scripts that use the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) to deploy dependencies. To deploy additional dependencies—such as forms, DMN diagrams, or subprocesses—send a [POST request](/apis-tools/orchestration-cluster-api-rest/specifications/create-deployment.api.mdx) with the relevant files. For example: ``` curl -L 'http://localhost:8080/v2/deployments' \ -H 'Accept: application/json' \ -F resources=@/pathToYourForm/user-signup.form ``` You will get a response containing the details of the deployed elements: ```json { "deployments": [ { "form": { "formKey": "KEY_OF_THE_FORM", "formId": "user-signup", "version": 1, "resourceName": "user-signup.form", "tenantId": "" } } ], "deploymentKey": "KEY_OF_THE_DEPLOYMENT", "tenantId": "" } ``` You can use element templates that reference the `user-signup.form`. ## Configure element templates in Desktop Modeler To make your element templates available in Desktop Modeler, see the [configuration guide](/components/modeler/desktop-modeler/element-templates/configuring-templates.md). ## Additional resources and next steps - [Using element templates in Desktop Modeler](/components/modeler/desktop-modeler/element-templates/using-templates.md) - [Run your first local Camunda 8 project](/guides/getting-started-example.md) - [Available connectors](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md) --- ## Routing events to processes To start a new process instance or to route a message to an already running instance, you have to choose the appropriate technology option to do so, like using the existing API or using customized possibilities including SOAP, AMQP, or Kafka. Leverage the possibilities of the universe of your runtime (like Java or Node.js) and the frameworks of your choice to support the technologies or protocols you need. ## Choosing the right BPMN event ### Start events Several BPMN start events can be used to start a new process instance. | | None Event | Message Event | Timer Event | Signal Event | Conditional Event | | ----------------------- | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------- | --------------------------------------------------------------------- | -------------------------------------------------------------------------------- | | | ![none start](/img/bpmn-elements/none-start.svg) | ![message start](/img/bpmn-elements/message-start.svg) | ![timer start](/img/bpmn-elements/timer-start.svg) | ![signal start](/img/bpmn-elements/signal-start.svg) | ![conditional start](/img/bpmn-elements/conditional-start.svg) | | Use when | You have only **one start event** or a start event which is clearly standard. | You have to differentiate **several start events**. | You want to automatically start process instances **time controlled**. | You need to start **several process instances** at once. Rarely used. | When a specific **condition** is met, a process instance is created. | | Supported for Execution | ✔ | ✔ | ✔ | ✔ | Determine occurrence of condition externally yourself and use the message event. | | | [Learn more](/components/modeler/bpmn/none-events/none-events.md) | [Learn more](/components/modeler/bpmn/message-events/message-events.md) | [Learn more](/components/modeler/bpmn/timer-events/timer-events.md) | [Learn more](/components/modeler/bpmn/signal-events/signal-events.md) | | 1 This none start event indicates the typical starting point. Note that only _one_ such start event can exist in one process definition. 2 This message start event is defined to react to a specific message type... 3 ...hence you can have _multiple_ message start events in a process definition. In this example, both message start events seems to be exceptional cases - for equivalent cases we recommend to just use message instead of none start events. ### Intermediate events Several BPMN intermediate events (and the receive task) can be used to make a process instance _wait_ for and _react_ to certain triggers. | | Message Event | Receive Task | Timer Event | Signal Event | Conditional Event | | ----------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | | | ![message intermediate](/img/bpmn-elements/message-intermediate.svg) | ![task receive](/img/bpmn-elements/task-receive.svg) | ![timer intermediate](/img/bpmn-elements/timer-intermediate.svg) | ![signal intermediate](/img/bpmn-elements/signal-intermediate.svg) | ![conditional intermediate](/img/bpmn-elements/conditional-intermediate.svg) | | Use when | You route an incoming **message** to a specific and unique process instance. | As alternative to message events (to leverage BPMN boundary events, for example, for timeouts). | You want to make your process instance wait for a certain (point in) **time**. | You route an incoming **signal** to all process instances waiting for it. | When a specific **condition** is met, the waiting process instance moves on. | | Supported for Execution | ✔ | ✔ | ✔ | ✔ | Not yet supported in Camunda 8 | | | [Learn more](/components/modeler/bpmn/message-events/message-events.md) | [Learn more](/components/modeler/bpmn/receive-tasks/receive-tasks.md) | [Learn more](/components/modeler/bpmn/timer-events/timer-events.md) | [Learn more](/components/modeler/bpmn/signal-events/signal-events.md) | | Consider this example: 1 This intermediate message event causes the process instance to wait unconditionally for a _specific_ event... 2 ...whereas the intermediate message event attached to the boundary of an activity waits for an _optional_ event, potentially arriving while we are occupied with the activity. ## Reacting to process-internal events Events relevant for the process execution can occur from within the workflow engine itself. Consider the following loan application process - or at least the initial part with which the applicant's income is confirmed either via the employer or via the last income tax statement. 1 In case the employer does not confirm the income within three business days, a **timer event** triggers and a human clerk now tries to contact the employer and investigate the situation. 2 This could end with a successful income confirmation. However, it could also end with new findings regarding the applicant's employment status. We learn that the applicant is actually unemployed. 3 In this case, a **conditional event** watching this data (for example, a process variable changed by the user task) triggers and causes the process to reconsider the consequences of the new findings. A conditional event's condition expression is evaluated at it's "scope" creation time, too, and not just when variable data changes. For our example of a boundary conditional event, that means that the activity it is attached to could principally be left immediately via the boundary event. However, our process example evaluates the data via the exclusive gateway - therefore such a scenario is semantically impossible. ## Routing events from the outside to the workflow engine Most events actually occur somewhere external to the workflow engine and need to be routed to it. The core workflow engine is by design not concerned with the technical part of receiving external messages, but you can receive messages and route them to the workflow engine by the following ways: - Using API: Receive the message by means of your platform-specific activities such as connecting to a AMQP queue or processing a REST request and then route it to the process. - Using connectors: Configure a connector to receive messages such as Kafka records and rote it to the process. Note that this possibility works for Camunda 8 only. ### Starting process instance by BPMN process ID If you have only one starting point (none start event) in your process definition, you reference the process definition by the ID in the BPMN XML file. :::note This is the most common case and requires using the [`CreateProcessInstance`](/apis-tools/zeebe-api/gateway-service.md#createprocessinstance-rpc) API. ::: Example in Java: ```java processInstance = zeebeClient.newCreateInstanceCommand() .bpmnProcessId("invoice").latestVersion() .send() .exceptionally( throwable -> { throw new RuntimeException("Could not create new process instance", throwable); }); ``` Example in Node.js: ```js zbc.createWorkflowInstance({ bpmnProcessId: "invoice", }); ``` This starts a new process instance in the latest version of the process definition. You can also start a specific version of a process definition: ```java processInstance = zeebeClient.newCreateInstanceCommand() .bpmnProcessId("invoice").version(5) //... ``` or ```js zbc.createWorkflowInstance({ bpmnProcessId: "invoice", version: 6, }); ``` You can also use [`CreateProcessInstanceWithResult`](/apis-tools/zeebe-api/gateway-service.md#createprocessinstancewithresult-rpc) instead, if you want to block the execution until the process instance has completed. ### Starting process instance by message As soon as you have multiple possible starting points, you have to use named messages to start process instances. The API method is [`PublishMessage`](/apis-tools/zeebe-api/gateway-service.md#publishmessage-rpc): ```java client.newPublishMessageCommand() .messageName("message_invoiceReceived") // <1> .corrlationKey(invoiceId) // <2> .variables( // <3> //... ).send() .exceptionally( throwable -> { throw new RuntimeException("Could not publish message", throwable); }); ``` 1 Message name as defined in the BPMN. 2 Correlation key has to be provided, even if a start event does not require correlation. 3 _Payload_ delivered with the message. On one hand, now you do not have to know the key of the BPMN process. On the other hand, you cannot influence the version of the process definition used when starting a process instance by message. The message name for start events should be unique for the whole workflow engine - otherwise you might experience side effects you did not intend (like starting other processes too). ## Technology examples for messages sent by external systems In this section, we give examples for _technical messages_, which are received from other systems, typically by leveraging technologies like SOAP, REST, JMS, and others. 1 You will need a mechanism receiving that message and routing it to the workflow engine. That could be a direct API call to Camunda. It could also be a AMQP or Kafka consumer or a SOAP endpoint using the Camunda API internally. It could even be a hotfolder polled by some framework like Apache Camel. API examples for REST, AMQP, and Kafka are shown in [connecting the workflow engine with your world](../connecting-the-workflow-engine-with-your-world/). ## Using the Camunda BPMN framework If you use the **Camunda BPMN Framework** as described in the book ["Real Life BPMN"](https://page.camunda.com/wp-real-life-bpmn-book-excerpt) you will typically have message start events (even if you only have a single start event) to connect the surrounding human flows to the technical flow via messages: 1 This is a message start event, which allows you to show the collaboration between the human and the technical flows. However, it is the only the starting point of the technical pool and could be a none start event in terms of execution. If there is _exactly one message start event_ for the whole process definition, it can also be treated as if it were a none start event when starting a process instance. ## Sending messages to other processes If messages are exchanged between different processes deployed in the workflow engine you have to implement the communication yourself by writing some code that starts a new process instance. 1 Use some simple code on the sending side to route the message to a new process instance, for example by starting a new process instance by the BPMN ID in Java: ```java @JobWorker(type="routeInput") public void routeInput(@Variable String invoiceId) { Map variables = new HashMap(); variables.put("invoiceId", invoiceId); zeebeClient.newCreateInstanceCommand() .bpmnProcessId("invoice").latestVersion() .variables(variables) .send() .exceptionally( throwable -> { throw new RuntimeException("Could not create new process instance", throwable); }); } ``` 2 Use some simple code on the sending side to correlate the message to a running process instance, for example in Java: ```java @JobWorker(type="notifyOrder") public void notifyOrder(@Variable String orderId, @Variable String paymentInformation) { Map variables = new HashMap(); variables.put("paymentInformation", paymentInformation); zeebeClient.newPublishMessageCommand() .messageName("MsgPaymentReceived") .corrlationKey(orderId) .variables(variables) .send() .exceptionally( throwable -> { throw new RuntimeException("Could not publish message", throwable); }); } ``` ## Handling messages sent by a user Sometimes explicit "user tasks" are not an appropriate choice to involve a human user to participate in a process: the user does not want to observe a task in Tasklist, but rather have the possibility to actively trigger some action right at the time when it becomes necessary from a business perspective. The difference is which event gives the _active trigger_. 1 We did not model a user task in this process, as the user will not immediately be triggered. The user cannot do anything at the moment when the process enters this event. Instead, we made it wait for a "message" which is later triggered by a human user. 2 The accountant actually receives the "external trigger" by actively looking at new payments in the bank account. 3 Every new payment now has to be correlated to the right waiting process instance manually. In this situation it is often the better choice not to model a user task, but let the process wait for a "message" generated from a user. These scenarios are not directly supported by Camunda Tasklist. A custom search screen built for the accountant might allow you to observe and find orders waiting for a payment. By interacting with such a screen, the accountant communicates with those process instances all at once. When hitting a 'Paid' button, a piece of custom code using the API must now correlate the user's message to the affected process instance(s). --- ## Service integration patterns with BPMN When integrating systems and services, you can choose between various modeling possibilities in BPMN. This practice will give you an overview and advice on how to decide between alternatives. You will note that service tasks in general are a good choice, but there are also situations where you might want to switch to send and receive tasks or events. ## Understanding communication patterns Let's briefly examine the three typical communication patterns to integrate systems: - **Request/response using synchronous communication styles**: You use a synchronous protocol, like HTTP, and block for the result. - **Request/response using asynchronous communication styles**: You use asynchronous communication, for example, by sending messages via a message broker, but wait for a response message right after. Technically, these are two independent asynchronous messages, but the sender blocks until the response is received, hence logically making it a request/response. - **Asynchronous messages or events:** If a peer service needs a long time to process a request, the response is much later than the request, say hours instead of milliseconds. In this case, the response is typically handled as a separate message. Additionally, some of your services might also wait for messages or events that are not connected to a concrete request, especially in event-driven architectures. The following table gives a summary of the three options: | | Synchronous request/response | Asynchronous request/response | Asynchronous messages or events | | --------------------------------- | :--------------------------- | :---------------------------- | :------------------------------ | | **Business level** | Synchronous | Synchronous | Asynchronous | | **Technical communication style** | Synchronous | Asynchronous | Asynchronous | | **Example** | HTTP | AMQP, JMS | AMQP, Apache Kafka | You can dive more into communication styles in the webinar [Communication Between Loosely Coupled Microservices](https://page.camunda.com/wb-communication-between-microservices) ([slides](https://www.slideshare.net/BerndRuecker/webinar-communication-between-loosely-coupled-microservices), [recording](https://page.camunda.com/wb-communication-between-microservices) and [FAQ](https://blog.bernd-ruecker.com/communication-between-loosely-coupled-microservices-webinar-faq-a02708b3c8b5)). ## Integrating services with BPMN tasks Let’s look at using BPMN tasks to handle these communication patterns before diving into BPMN events later. ### Service task The [service task](/components/modeler/bpmn/service-tasks/service-tasks.md) is the typical element to implement synchronous request/response calls, such as REST, gRPC or SOAP. You should **always use service tasks for synchronous request/response**. ![Service task](service-integration-patterns-assets/service-task.png) ### Send task Technically, **send tasks behave exactly like service tasks**. However, the alternative symbol makes the meaning of sending a message easier to understand for some stakeholders. You **should use send tasks for sending asynchronous messages**, like AMQP messages or Kafka records. ![Send task](service-integration-patterns-assets/send-task.png) There is some gray area whenever you call a synchronous service that then sends an asynchronous message. A good example is email. Assume your process does a synchronous request/response call to a service that then sends an email to inform the customer. The call itself is synchronous because it gives you a confirmation (acknowledgement, or ACK for short) that the email has been sent. Now is the "inform customer" task in your process a service, or a send task? ![Asynchronous ACK](service-integration-patterns-assets/synchronous-ack.png) This question is not easy to answer and **depends on what your stakeholders understand more intuitively**. The more technical people are, the more you might tend towards a service task, as this is technically correct. The more you move towards the business side, the more you might tend to use a send task, as business people will consider sending an email an asynchronous message. In general, we tend to **let the business win** as it is vital that business stakeholders understand business processes. However, if you follow a microservice (or service-oriented architecture) mindset, you might argue that you don’t need to know exactly how customers are informed within the process. Hiding the information if the notification is synchronous or asynchronous is good to keep your process model independent of such choices, making it more robust whenever the implementation of the notification service changes. This is a very valid concern too, and might motivate for a service task. :::note In case you can’t easily reach a conclusion, save discussion time and just use a service task. ::: You could also argue to use send tasks to invoke synchronous request/response calls when you are not interested in the response. However, this is typically confusing, and we do not recommend this. ### Receive task A [receive task](/components/modeler/bpmn/receive-tasks/receive-tasks.md) waits for an asynchronous message. Receive tasks **should be used for incoming asynchronous messages or events**, like AMQP messages or Kafka records. ![Receive task](service-integration-patterns-assets/receive-task.png) Receive tasks can be used to receive the response in asynchronous request/response scenarios, which is discussed next. ### Service task vs. send/receive task combo For asynchronous request/response calls, you can use a send task for the request, and a following receive task to wait for the response: ![Send and receive task](service-integration-patterns-assets/send-and-receive-task.png) You can also use a service task, which is sometimes unknown even to advanced users. A service task can technically wait for a response that happens at any time, a process instance will wait in the service task, as it would in the receive task. ![Service task](service-integration-patterns-assets/service-task.png) Deciding between these options is not completely straightforward. You can find a table listing the decision criteria below. As a general rule-of-thumb, we recommend using **the service task as the default option for synchronous _and_ asynchronous request/response** calls. The beauty of service tasks is that you remove visual clutter from the diagram, which makes it easier to read for most stakeholders. This is ideal if the business problem requires a logically synchronous service invocation. It allows you to ignore the technical details about the protocol on the process model level. The typical counter-argument is that asynchronous technical protocols might lead to different failure scenarios that you have to care about. For example, when using a separate receive task, readers of the diagram almost immediately start to think about what happens if the response will not be received. But this also has the drawback that now business people might start discussing technical concerns, which is not necessarily good. Furthermore, this is a questionable argument, as synchronous REST service calls could also timeout. This is exactly the same situation, just hidden deeper in network abstraction layers, as every form of remote communication uses asynchronous messaging somewhere down in the network stack. On a technical level, you should always think about these failure scenarios. The talk [3 common pitfalls in microservice integration and how to avoid them](https://blog.bernd-ruecker.com/3-common-pitfalls-in-microservice-integration-and-how-to-avoid-them-3f27a442cd07) goes into more detail on this. On a business level, you should be aware of the business implications of technical failures, but not discuss or model all the nuts and bolts around it. However, there are also technical implications of this design choice that need to be considered. **Technical implications of using service tasks** You can keep a service task open and just complete it later when the response arrives, but in **to complete the service task, you need the _job instance key_** from Zeebe. This is an internal ID from the workflow engine. You can either: - Pass it around to the third party service which sends it back as part of the response message. - Build some kind of lookup table, where you map your own correlation information to the right job key. :::note Later versions of Zeebe might provide query possibilities for this job key based on user controlled data, which might open up more possibilities. ::: Using workflow engine internal IDs can lead to problems. For example, you might cancel and restart a process instance because of operational failures, which can lead to a new ID. Outstanding responses cannot be correlated anymore in such instances. Or, you might run multiple workflow engines which can lead to internal IDs only being unique within one workflow engine. All of this might not happen, but the nature of an internal ID is that it is internal and you have no control over it — which bears some risk. In practice, however, using the internal job instance key is not a big problem if you get responses in very short time frames (milliseconds). Whenever you have more long-running interactions, you should consider using send and receive tasks, or build your own lookup table that can also address the problems mentioned above. This is also balanced by the fact that service tasks are simply very handy. The concept is by far the easiest way to implement asynchronous request/response communication. The job instance key is generated for you and unique for every message interchange. You don’t have to think about race conditions or idempotency constraints yourself. [Timeout handling and retry logic](/components/concepts/job-workers.md#timeouts) is built into the service task implementation of Zeebe. There is also [a clear API to let the workflow engine know of technical or business errors](/components/concepts/job-workers.md#completing-or-failing-jobs). **Technical implications of using send and receive tasks** Using send and receive tasks means to use [the message concept built into Zeebe](/components/concepts/messages.md). This is a powerful concept to solve a lot of problems around cardinalities of subscriptions, correlation of the message to the right process instances, and verification of uniqueness of the message (idempotency). When using messages, you need to provide the correlation ID yourself. This means that the correlation ID is fully under your control, but it also means that you need to generate it yourself and make sure it is unique. You will most likely end up with generated UUIDs. You can leverage [message buffering](/components/concepts/messages.md#message-buffering) capabilities, which means that the process does not yet need to be ready to receive the message. You could, for example, do other things in between, but this also means that you will not get an exception right away if a message cannot be correlated, as it is simply buffered. This leaves you in charge of dealing with messages that can never be delivered. Retries are not built-in, so if you need to model a loop to retry the initial service call if no response is received. And (at least in the current Zeebe version), there is no possibility to trigger error events for a receive task, which means you need to model error messages as response payload or separate message types — both are discussed later in this post. A final note for high-performance environments: These powerful messaging capabilities do not come for free and require some overhead within the engine. For pure request/response calls that return within milliseconds, none of the features are truly required. If you are looking to build a high-performance scenario, using service tasks instead of message correlation for request/response calls, you can tune your overall performance or throughput. However, as with everything performance related, the devil is in the detail, so [reach out to us](/reference/contact.md) to discuss such a scenario in more depth. **Summary And recommendations** The following table summarizes the possibilities and recommendations. | Case | Synchronous request/response | Synchronous request/response | Asynchronous request/response | Asynchronous request/response | | :--------------------- | :-------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | BPMN element | Service task | Send task | Service task | Send + receive task | | | ![Service task](/img/bpmn-elements/task-service.svg) | ![Send task](/img/bpmn-elements/task-send.svg) | ![Service task](/img/bpmn-elements/task-service.svg) | ![Send and receive task](/img/bpmn-elements/send-and-receive-task.png) | | Technical implications | | Behaves like a service task | A unique correlation ID is generated for you. You don’t have to think about race conditions or idempotency. Timeout handling and retry logic are built-in. API to flag business or technical errors. | Correlation ID needs to be generated yourself, but is fully under control. Message buffering is possible but also necessary. Timeouts and retries need to be modeled. BPMN errors cannot be used. | | Assessment | Very intuitive. | Might be more intuitive for fire and forget semantics, but can also lead to discussions. | Removes visual noise which helps stakeholders to concentrate on core business logic, but requires use of internal job instance keys. | More visual clutter, but also more powerful options around correlation and modeling patterns. | | Recommendation | Default option, use unless it is confusing for business stakeholders (e.g. because of fire and forget semantics of a task). | Use for fire and forget semantics, unless it leads to unnecessary discussions, in this case use service task instead. | Use when response is within milliseconds and you can pass the Zeebe-internal job instance key around. | Use when the response will take time (> some seconds), or you need a correlation ID you can control. | ## Integrating services with BPMN events Instead of using send or receive **tasks**, you can also use send or receive **events** in BPMN. ![Events vs tasks](service-integration-patterns-assets/events-vs-tasks.png) Let's first explore when you want to do that, and afterwards look into some more advanced patterns that become possible with events. ### Tasks vs. events The **execution semantics of send and receive events is identical with send and receive tasks**, so you can express the very same thing with tasks or events. However, there is one small difference that might be relevant: **only tasks can have boundary events**, which allows to easily model when you want to cancel waiting for a message: ![Boundary events](service-integration-patterns-assets/boundary-event.png) Despite this, the whole visual representation is of course different. In general, tasks are easier understood by most stakeholders, as they are used very often in BPMN models. However, in certain contexts, such as event-driven architectures, events might be better suited as the concept of events is very common. Especially, if you apply domain-driven design (DDD) and discuss domain events all day long, it might be intuitive that events are clearly visible in your BPMN models. Another situation better suited for events is if you send events to your internal reporting system besides doing “the real” business logic. Our experience shows that the smaller event symbols are often unconsciously treated as less important by readers of the model, leading to models that are easier to understand. | | Send task | Receive task | Send event | Receive event | | :------------- | :----------------------- | :----------------------- | :---------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------- | | Recommendation | Prefer tasks over events | Prefer tasks over events | Use only if you consistently use events over tasks and have a good reason for doing so (e.g. event-driven architecture) | Use only if you consistently use events over tasks and have a good reason for doing so (e.g. event-driven architecture) | :::note The choice about events vs. commands also [needs to be reflected in the naming of the element](../../modeling/naming-bpmn-elements), as a task emphasizes the action (e.g. "wait for response") and the event reflects what happened (e.g. "response received"). ::: ### Handling different response messages Very often the response payload of the message will be examined to determine how to move on in the process. ![Gateway handling response](service-integration-patterns-assets/response-gateway.png) In this case, you receive exactly one type of message for the response. As an alternative, you could also use different message types, to which the process can react differently. For example, you might wait for the validation message, but also accept a cancellation or rejection message instead: ![Boundary message event to capture different response messages](service-integration-patterns-assets/response-boundary-message-events.png) This modeling has the advantage that it is much easier to note the expected flow of the process (also called the happy path), with exceptions deviating from it. On the other hand, this pattern mixes receive tasks and events in one model, which can confuse readers. Keep in mind that it only works for a limited number of non-happy messages. To avoid the task/event mixture you could use a so-called event-based gateway instead, this gateway waits for one of a list of possible message types to be received: ![Event based gateway to capture different response messages](service-integration-patterns-assets/response-event-based-gateway.png) We typically try to avoid the event-based gateway, as it is hard to understand for non-BPMN professionals. At the same time, it shares the downside of the first pattern with the decision gateway after the receive task: the happy path cannot be easily spotted. As a fourth possibility, you can add event subprocesses, which get activated whenever some event is received while the process is still active in some other area. In the above example, you could model the happy path and model all deviations as event subprocesses. ![Event subprocess to capture different response messages](service-integration-patterns-assets/response-event-subprocess.png) This pattern is pretty handy, but also needs some explanation to people new to BPMN. It has one downside you need to know: once your process instance moves to the subprocess, you can’t easily go back to the typical flow. To some extent this problem can be solved by advanced modeling patterns like shown in the [allow for order cancellation anytime](../../modeling/building-flexibility-into-bpmn-models/#allow-for-order-cancellation-any-time) example. At the same time, the event subprocess has a superpower worth mentioning: you can now wait for cancellation messages in whole chunks of your process — it could arrive anytime. | | Receive task with boundary events | Payload and XOR-gateway | Event-based gateway | Event subprocess | | ----------------- | ----------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | | | ![Boundary Events](service-integration-patterns-assets/response-boundary-message-events.png) | ![XOR Gateway](service-integration-patterns-assets/response-gateway.png) | ![Event-based Gateway](service-integration-patterns-assets/response-event-based-gateway.png) | ![Event Subprocess](service-integration-patterns-assets/response-event-subprocess.png) | | Understandability | Easy | Very easy | Hard | Medium | | Assessment | Limitation on how many message types are possible | Happy path not easily visible | | Might need some explanation for readers of the model | | Recommendation | Use when it is important to observe message types in the visual, limit to two boundary message events | Use when there are more response types or if the response type can be treated as a result | Try to avoid | Use if you need bigger scopes where you can react to events | ### Message type on the wire != BPMN message type There is one important detail worth mentioning in the context of message response patterns: The message type used in BPMN models does not have to be exactly the message type you get on the wire. When you correlate technical messages, e.g. from AMQP, you typically write a piece of glue code that receives the message and calls the workflow engine API. This is described in [connecting the workflow engine with your world](../connecting-the-workflow-engine-with-your-world/), including a code example. In this glue code you can do various transformations, for example: - Messages on different message queues could lead to the same BPMN message type, probably having some additional parameter in the payload indicating the origin. - Some message header or payload attributes could be used to select between different BPMN message types being used. It is probably not best practice to be as inconsistent as possible between technical message types and BPMN message types. Still, the flexibility of a custom mapping might be beneficial in some cases. ## Hiding technical complexity behind call activities Whenever technical details of one service integration become complicated, you can think of creating a separate process model for the technicalities of the call and use a [call activity](/components/modeler/bpmn/call-activities/call-activities.md) in the main process. An example is given in chapter 7 of [Practical Process Automation](https://processautomationbook.com/): ![Hiding technical details behind call activity](service-integration-patterns-assets/hiding-technical-details-behind-call-activity.png) In the customer scenario, a document storage service was long-running, but could not do a real callback or response message for technical reasons (in short, firewall limitations). As a result, the document storage service needed to be regularly polled for the response. In the customer scenario, this was done by a "document storage adapter" process that leveraged workflow engine features to implement the polling every minute, and especially the persistent waiting in between. In the main business process, this technical adapter process was simply invoked via a call activity, meaning no technicalities bloated that diagram. --- ## Testing process definitions Test your executable BPMN processes as you would any software. When possible, write fast automated unit tests using a localized and isolated workflow engine. Before releasing, verify your implementation with integration tests in an environment that closely mirrors your production setup, which may include human-driven, exploratory integration tests. This best practice uses the following process example for incoming invoices that need to be approved: 1 Invoices need to be approved. 2 The invoice sender is notified about a rejection. 3 Approved invoices get processed. 4 If the approval task takes too long, the process takes an alternative path—in this case, the invoice is automatically approved. 5 If an error occurs while communicating with the archive system (assume you have an unreliable legacy system), the process takes a detour to handle this situation manually. ## Testing scopes There are three typical test scopes used when building process solutions: 1. **Unit tests**: Testing glue code or programming code you developed for your process solution. How to unit test your software itself is not discussed here, as this is a common practice for software development. 2. **Process tests**: Testing the expected behavior of the process model, including glue code and specifically the data flowing through the process model. These tests should run frequently, so they should behave like unit tests (quick turnaround, no need for external resources). 3. **Integration tests**: Testing the system in a close-to-production environment to ensure it works correctly. This is typically done before releasing a new version of your system. These tests include _human-driven_, _exploratory_ tests. ![Scopes](testing-process-definitions-assets/scopes.png) ## Writing process tests in Java This section describes how to write process tests as unit tests in Java. We are working on additional information for writing tests in other languages, such as Node.js or C#. When using Java, most customers use Spring Boot, so we describe this approach in this best practice. While this is a common setup for customers, it is not the only one. Find more examples of plain Java process tests in [Getting Started with Camunda Process Test](../../../apis-tools/testing/getting-started.md). ### Technical setup using Spring :::caution - Camunda Process Test was introduced with **Camunda 8.8**. - You must use **JUnit 5** in every test class. The `@Test` annotation you import must be `org.junit.jupiter.api.Test`. ::: 1. Use [_JUnit 5_](http://junit.org) as your unit test framework. 2. Use the [Camunda Spring Boot Starter](../../../apis-tools/camunda-spring-boot-starter/getting-started.md). 3. Use `@CamundaSpringProcessTest` to start a process engine. 4. Ensure you have Docker installed locally to use [TestContainers](../../../apis-tools/testing/getting-started.md#prerequisites), which is the easiest way to run tests. 5. Use assertions from [Camunda Process Test](../../../apis-tools/testing/assertions.md) to verify that your expectations about the process state are met. 6. Use a mocking framework of your choice (such as [Mockito](http://mockito.org)) to mock service methods and verify that services are called as expected. 7. Use utilities from [Camunda Process Test](../../../apis-tools/testing/utilities.md) to mock job workers you don't want to run (for example, connectors). The following code shows an example test: ```java @SpringBootTest( properties = { "camunda.client.worker.defaults.enabled=false", // disable job workers and enable them selectively "camunda.client.worker.override.archive-invoice.enabled=true", }) @CamundaSpringProcessTest public class InvoiceApprovalTest { @Autowired private CamundaClient client; @Autowired private CamundaProcessTestContext processTestContext; @Autowired private ObjectMapper objectMapper; // Mock services that are called from the job workers @MockitoBean private ArchiveService archiveService; @MockitoBean private AccountingService accountingService; // Sample data used private final String invoiceJson = """ { "id": "INV-1001", "amount": 12000, "currency": "EUR", "supplier": { "id": "0815", "name": "Acme GmbH" }, "contactEmail": "accounting@acme.com" }"""; @Test public void happyPath() throws Exception { final HashMap variables = new HashMap(); variables.put("approver", "Zee"); variables.put("invoice", objectMapper.readTree(invoiceJson)); // After all preparations, start the process instance final var processInstance = client .newCreateInstanceCommand() .bpmnProcessId("Process_InvoiceApproval") .latestVersion() .variables(variables) .send() .join(); // assert the User Task was created assertThat(byElementId("UserTask_ApproveInvoice")).isCreated().hasAssignee("Zee"); // and simulate the user completing it processTestContext.completeUserTask(byElementId("UserTask_ApproveInvoice"), Map.of("approved", true)); // This should make the process instance execute to completion assertThat(processInstance) .hasCompletedElementsInOrder( byId("StartEvent_InvoiceReceived"), byId("UserTask_ApproveInvoice"), byId("ServiceTask_ArchiveInvoice"), byId("ServiceTask_AddInvoiceAccounting"), byId("EndEvent_InvoiceApproved")) .isCompleted(); // verify that side effects have happened Mockito.verify(archiveService).archiveInvoice("INV-1001", objectMapper.readTree(invoiceJson)); Mockito.verify(accountingService).addInvoiceToAccount("0815", "INV-1001"); } ``` :::note The complete source code for this example test is available on [GitHub](https://github.com/camunda/camunda/tree/main/testing/camunda-process-test-example/src/test/java/io/camunda/InvoiceApprovalTest.java). ::: ### Test scope and mocking In a test case like this, you want to test the executable BPMN process definition, plus all the glue code that logically belongs to the process definition in a broader sense. Typical examples of glue code you want to include in a process test are: - Worker code, typically connected to a service task - Expressions (FEEL) used in your process model for gateway decisions or input/output mappings - Other glue code, for example, your own Client API (probably exposed via REST) that performs data mapping before calling the Camunda Client. The following illustration shows this for the invoice approval example: ![Process test scope example](testing-process-definitions-assets/process-test-scope-example.png) Workflow engine-independent business code should _not_ be included in the tests. In the invoice approval example, the `ArchiveService` will be mocked, and the `ArchiveInvoiceWorker` will read and transform process variables and call this mock. This way, you can test the process model, the glue code, and the data flow in your process test without calling out to the real archive system. The following code examples highlight the important aspects around mocking. The `ArchiveInvoiceWorker` is executed as part of the test. It does input data mapping **(1)** and also translates a specific business exception into a BPMN error **(2)**: ```java @Component public class ArchiveInvoiceWorker { private final ArchiveService service; public ArchiveInvoiceWorker(final ArchiveService service) { this.service = service; } @JobWorker(type = "archive-invoice") public void handleJob( @Variable("invoiceId") final String invoiceId, // <1> @Variable("invoice") final JsonNode invoiceJson) { try { service.archiveInvoice(invoiceId, invoiceJson); } catch (WiredLegacyException e) { // <2> throw new BpmnError( "LEGACY_ERROR_ARCHIVE", "The archive system had a problem: " + e.getMessage()); } } } ``` The `ArchiveService` is considered a business service (it could, for example, wrap the archive system client SDK to make the appropriate remote calls) and should _not_ be executed during the test. This is why this interface is mocked in the test case: ```java @MockitoBean private ArchiveService archiveService; @Test public void happyPath() throws Exception { // ... // Using Mockito you can verify a business method was called with the expected parameters Mockito.verify(archiveService).archiveInvoice("INV-1001", objectMapper.readTree(invoiceJson)); } @Test void testArchiveSystemError() throws Exception { // Using Mockito you can define what should happen when a method is called, in this case an exception is thrown to simulate a business error doThrow(new WiredLegacyException()).when(archiveService).archiveInvoice(anyString(), any()); //... } ``` Some workers might not delegate to a proper service class, which you can easily mock. The prime example is connectors. The invoice process uses the REST connector to trigger the invoice rejection via some REST API. To avoid calling the REST endpoint, you can mock the job worker that would be provided by the connector runtime: ```java @Test public void testRejectionPath() throws Exception { processTestContext.mockJobWorker("io.camunda:http-json:1").thenComplete(); // ... } ``` You could also mock the REST endpoint, which we touch on later discussing integration tests. Some projects consider REST mocking part of the unit test scope, and this is generally also fine, even if we see it as integration test scope by default. You can use the same [utilities from Camunda Process Test](../../../apis-tools/testing/utilities.md) to mock other workers, where you simply do not want to run the job worker itself. Maybe the implementation is not clean, but beyond your control. However, we advise to use a proper service interface whenever possible instead of job worker mocking. ```java // Define the mock final JobWorkerMock addInvoiceJobWorkerMock = processTestContext .mockJobWorker("add-invoice-to-accounting") .withHandler( (jobClient, job) -> { jobClient .newCompleteCommand(job) // .variables(null) // We could now also simulate setting some response values .send() .join(); }); // ... drive the process ... // and assert: assertThat(addInvoiceJobWorkerMock.getInvocations()) .as("add-invoice-to-accounting job worker called") .isEqualTo(1); assertThat(addInvoiceJobWorkerMock.getActivatedJobs().get(0).getVariablesAsMap()) .containsEntry("invoiceId", "INV-1001"); ``` ### Drive the process and assert the state For tests, you drive the process from wait state to wait state and assert that you observe the expected process and variable states. For example, you might implement a test for the scenario when an invoice gets approved and processed without errors: ```java @Test public void happyPath() throws Exception { final HashMap variables = new HashMap(); variables.put("approver", "Zee"); variables.put("invoice", objectMapper.readTree(invoiceJson)); // Kick off the process instance // <1> final var processInstance = client .newCreateInstanceCommand() .bpmnProcessId("Process_InvoiceApproval") .latestVersion() .variables(variables) .send() .join(); // assert the User Task and simulate a human decision // <2> assertThat(byElementId("UserTask_ApproveInvoice")).isCreated().hasAssignee("Zee"); processTestContext.completeUserTask( byElementId("UserTask_ApproveInvoice"), Map.of("approved", true)); // This should make the process instance execute till the end // <3> assertThat(processInstance) .hasCompletedElementsInOrder( byId("StartEvent_InvoiceReceived"), byId("UserTask_ApproveInvoice"), byId("ServiceTask_ArchiveInvoice"), byId("ServiceTask_AddInvoiceAccounting"), byId("EndEvent_InvoiceApproved")) .isCompleted(); // verify that side effects have happened // <4> verify(archiveService).archiveInvoice("INV-1001", objectMapper.readTree(invoiceJson)); verify(accountingService).addInvoiceToAccount("0815", "INV-1001"); } ``` 1. Create a new process instance. You may want to use some glue code to start your process (e.g. the REST API facade), or also create helper methods within your test class. 2. Drive the process through its wait states, e.g. by completing a waiting user task. 3. Assert that your process is in the expected state. 4. Verify with your mocking library that your business service methods were called as expected. Be careful not to "overspecify" your test method by asserting too much. Your process definition will likely evolve in the future and such changes should break as little test code as possible, but just as much as necessary! As a rule of thumb _always_ assert that the expected _external effects_ of your process really took place (e.g. that business services were called as expected). Additionally, carefully choose which aspects of _internal process state_ are important enough so that you want your test method to warn about any related change later on. ### Testing your process in chunks Divide and conquer by _testing your process in chunks_. Consider the important chunks and paths the invoice approval process consists of: 1 The _happy path_: The invoice gets approved. 2 The invoice gets rejected. 3 A timeout on waiting for approval leads to an automatic approval. 4 An approved invoice can't get archived. #### Testing the happy path The happy path is kind of the default scenario with a positive outcome, so no exceptions or errors or deviations are experienced. Fully test the happy path in one (big) test method. This makes sure you have one consistent data flow in your process. Additionally, it is easy to read and to understand, making it a great starting point for new developers to understand your process and process test case. You were already exposed to the happy path in our example, which is the scenario that the invoice gets approved: ```java @Test public void happyPath() throws Exception { final HashMap variables = new HashMap(); variables.put("approver", "Zee"); variables.put("invoice", objectMapper.readTree(invoiceJson)); // Kick off the process instance // <1> final var processInstance = client .newCreateInstanceCommand() .bpmnProcessId("Process_InvoiceApproval") .latestVersion() .variables(variables) .send() .join(); // assert the User Task and simulate a human decision // <2> assertThat(byElementId("UserTask_ApproveInvoice")).isCreated().hasAssignee("Zee"); processTestContext.completeUserTask( byElementId("UserTask_ApproveInvoice"), Map.of("approved", true)); // This should make the process instance execute till the end // <3> assertThat(processInstance) .hasCompletedElementsInOrder( byId("StartEvent_InvoiceReceived"), byId("UserTask_ApproveInvoice"), byId("ServiceTask_ArchiveInvoice"), byId("ServiceTask_AddInvoiceAccounting"), byId("EndEvent_InvoiceApproved")) .isCompleted(); // verify that side effects have happened // <4> verify(archiveService).archiveInvoice("INV-1001", objectMapper.readTree(invoiceJson)); verify(accountingService).addInvoiceToAccount("0815", "INV-1001"); } ``` #### Testing detours Test _forks/detours_ from the happy path as well as _errors/exceptional_ paths as chunks in separate test methods. This allows to unit test in meaningful units. The tests for the exceptional paths are basically very similar to the happy path in our example. 2 The invoice gets rejected: ```java @Test public void testRejectionPath() throws Exception { final HashMap variables = new HashMap(); variables.put("approver", "Zee"); variables.put("invoice", objectMapper.readTree(invoiceJson)); // We skip HTTP for the simple unit test - mock the http connector processTestContext.mockJobWorker("io.camunda:http-json:1").thenComplete(); // Kick of the process instance final var processInstance = client .newCreateInstanceCommand() .bpmnProcessId("Process_InvoiceApproval") .latestVersion() .variables(variables) .send() .join(); // assert the User Task and simulate a human decision assertThat(byElementId("UserTask_ApproveInvoice")).isCreated().hasAssignee("Zee"); processTestContext.completeUserTask( byElementId("UserTask_ApproveInvoice"), Map.of( // "approved", false, // "rejectionReason", "it is a test case :-)")); // This should make the process instance execute till the end assertThat(processInstance) .hasCompletedElementsInOrder( byId("StartEvent_InvoiceReceived"), byId("UserTask_ApproveInvoice"), byId("Gateway_Approved"), byId("ServiceTask_SendRejection"), byId("EndEvent_InvoiceRejected")) .isCompleted(); } ``` 3 A timeout on waiting for approval leads to an automatic approval: ```java @Test public void testApprovalTimeout() throws Exception { final HashMap variables = new HashMap(); variables.put("approver", "Zee"); variables.put("invoice", objectMapper.readTree(invoiceJson)); final var processInstance = client .newCreateInstanceCommand() .bpmnProcessId("Process_InvoiceApproval") .latestVersion() .variables(variables) .send() .join(); // assert the User Task and simulate the timeout assertThat(processInstance).hasActiveElements("UserTask_ApproveInvoice"); processTestContext.increaseTime(Duration.ofDays(5)); // This should make the process instance auto approve and run till the end assertThat(processInstance) .isCompleted() .hasCompletedElementsInOrder( byId("StartEvent_InvoiceReceived"), byId("ServiceTask_ArchiveInvoice"), byId("ServiceTask_AddInvoiceAccounting"), byId("EndEvent_InvoiceApproved")) .hasTerminatedElements(byId("UserTask_ApproveInvoice")); } ``` 4 An approved invoice can't get archived: ```java @Test public void testArchiveSystemError() throws Exception { final HashMap variables = new HashMap(); variables.put("approver", "Zee"); variables.put("invoice", objectMapper.readTree(invoiceJson)); doThrow(new WiredLegacyException()).when(archiveService).archiveInvoice(anyString(), any()); final var processInstance = client .newCreateInstanceCommand() .bpmnProcessId("Process_InvoiceApproval") .latestVersion() .variables(variables) .send() .join(); // approve the request assertThat(byElementId("UserTask_ApproveInvoice")).isCreated(); processTestContext.completeUserTask(byElementId("UserTask_ApproveInvoice"), Map.of("approved", true)); // This should lead to the exception being thrown, causing the process to end up in the user task designed to handle the problem. assertThat(byElementId("UserTask_ManuallyArchiveInvoice")) .isCreated(); // The test for .hasCandidateGroup("archive-team") is probably not worth implementing // as it limits flexibility in model changes. processTestContext.completeUserTask(byElementId("UserTask_ManuallyArchiveInvoice")); assertThat(processInstance) .isCompleted() .hasCompletedElementsInOrder( byId("StartEvent_InvoiceReceived"), byId("UserTask_ApproveInvoice"), byId("UserTask_ManuallyArchiveInvoice"), byId("ServiceTask_AddInvoiceAccounting"), byId("EndEvent_InvoiceApproved")) .hasTerminatedElements(byId("ServiceTask_ArchiveInvoice")); verify(accountingService).addInvoiceToAccount("0815", "INV-1001"); } ``` ## Integration tests Test the process in a close-to-real-life environment. This verifies that it really works before releasing a new version of your process definition, which includes _human-driven_, _exploratory_ tests. Clearly _define your goals_ for integration tests! Goals could be: - End user & acceptance tests - Complete end-to-end tests - Performance & load tests, etc. Carefully consider _automating_ tests on scope 3. You need to look at the overall effort spent on writing test automation code and maintaining it when compared with executing human-driven tests for your software project's lifespan. The best choice depends very much on the frequency of regression test runs. Most effort is typically invested in setting up proper test data in surrounding systems. Configure your tests to be dedicated integration tests, and separate them from unit or process tests. You can use typical industry standard tools for integration testing together with Camunda. ### Mocking REST calls Especially when using the Connector framework, there might be relevant logic to test in configuration of a connector, especially the input and output data mapping. To test those, you typically want to mock the endpoint, rather than the job worker. In the invoice approval example, the `Send invoice rejection` task leverages an outbound REST connector. The service task might look like this in the BPMN XML: ```xml ``` You can mock the REST endpoint using the Spring Boot integration of [WireMock](http://wiremock.org/), allowing you to stub the endpoint in your JUnit test and make it accessible to the TestContainers runtime. 1. Add the required [WireMock Spring Boot](https://wiremock.org/docs/spring-boot/) dependency to your project (`org.wiremock.integrations:wiremock-spring-boot`). 2. Add the annotation `@EnableWireMock` to your test class to start the WireMock server. 3. Use the secrets in Camunda to configure the endpoint of the REST call, which is best practice anyway to configure the URL in the environment. In the test you need to set it to the URL containing of the hostname `host.testcontainers.internal` and the WireMock server port. 4. Make sure the connector runtime is enabled in the test case, so that the out-of-the-box REST connector is executed. 5. Expose the WireMock server port to the TestContainers runtime before running the test case. Here is the relevant source code: ```java @EnableWireMock @SpringBootTest( properties = { "camunda.client.worker.defaults.enabled=false", "camunda.process-test.connectors-enabled=true", "camunda.process-test.connectors-secrets.INVOICE_REJECTION_URL=" + "http://host.testcontainers.internal:${wiremock.server.port}" }) @CamundaSpringProcessTest public class InvoiceApprovalIntegrationTest { @Value("${wiremock.server.port}") private int wireMockPort; @BeforeEach void setup() { Testcontainers.exposeHostPorts(wireMockPort); } @Test public void testRejectionPath() throws Exception { // configure mock behavior stubFor(post("/reject").willReturn(aResponse().withStatus(200).withBody("ok"))); // Now drive the test case as in a unit test shown above ... // Verify the mock was called verify( postRequestedFor(urlEqualTo("/reject")) .withRequestBody( equalToJson( """ { "invoiceId": "INV-1001", "rejectionReason": "it is a test case :-)" }"""))); } ``` --- ## Writing good workers [Service tasks](/components/modeler/bpmn/service-tasks/service-tasks.md) within Camunda 8 require you to set a task type and implement [job workers](/components/concepts/job-workers.md) who perform whatever needs to be performed. This describes that you might want to: 1. Write all glue code in one application, separating different classes or functions for the different task types. 2. Think about idempotency and read or write as little data as possible from/to the process. 3. Write non-blocking (reactive, async) code for your workers if you need to parallelize work. Use blocking code only for use cases where all work can be executed in a serialized manner. Don’t think about configuring thread pools yourself. ## Organizing glue code and workers in process solutions Assume the following order fulfillment process, that needs to invoke three synchronous REST calls to the responsible systems (payment, inventory, and shipping) via custom glue code: ![order fulfillment example](writing-good-workers-assets/order-fulfillment-process.png) Should you create three different applications with a worker for one task type each, or would it be better to process all task types within one application? As a rule of thumb, we recommend implementing **all glue code in one application**, which then is the so-called **process solution** (as described in [Practical Process Automation](https://processautomationbook.com/)). This process solution might also include the BPMN process model itself, deployed during startup. Thus, you create a self-contained application that is easy to version, test, integrate, and deploy. ![Process solution](writing-good-workers-assets/process-solution.png) Figure taken from [Practical Process Automation](https://processautomationbook.com/) Thinking of Java, the three REST invocations might live in three classes within the same package (showing only two for brevity): ```java public class RetrieveMoneyWorker { @JobWorker(type = "retrieveMoney", autoComplete = false) public void retrieveMoney(final JobClient client, final ActivatedJob job) { // ... code } } ``` ```java public class FetchGoodsWorker { @JobWorker(type = "fetchGoods", autoComplete = false) public void fetchGoods(final JobClient client, final ActivatedJob job) { // ... code } } ``` You can also pull the glue code for all task types into one class. Technically, it does not make any difference and some people find that structure in their code easier. If in doubt, the default is to create one class per task type. There are exceptions when you might not want to have all glue code within one application: 1. You need to specifically control the load for one task type, like _scaling it out_ or _throttling it_. For example, if one service task is doing PDF generation, which is compute-intensive, you might need to scale it much more than all other glue code. On the other hand, it could also mean limiting the number of parallel generation jobs due to licensing limitations of your third-party PDF generation library. 2. You want to write glue code in different programming languages, for example, because writing specific logic in a specific language is much easier (like using Python for certain AI calculations or Java for certain mainframe integrations). In this case, you would spread your workers into different applications. Most often, you might still have a main process solution that will also still deploy the process model. Only specific workers are carved out. ## Thinking about transactions, exceptions and idempotency of workers Visit [dealing with problems and exceptions](../dealing-with-problems-and-exceptions/) to gain a better understanding of how workers deal with transactions and exceptions to the happy path, and find more details on how to write idempotent workers. ## Data minimization in workers If performance or efficiency matters in your scenario, there are two rules about data in your workers you should be aware of: 1. Minimize what data you read for your job. In your job client, you can define which process variables you will need in your worker, and only these will be read and transferred, saving resources on the broker as well as network bandwidth. 2. Minimize what data you write on job completion. You should explicitly not transmit the input variables of a job upon completion, which might happen easily if you simply reuse the map of variables you received as input for submitting the result. Not transmitting all variables saves resources and bandwidth, but serves another purpose as well: upon job completion, these variables are written to the process and might overwrite existing variables. If you have parallel paths in your process (e.g. [parallel gateway](/components/modeler/bpmn/parallel-gateways/parallel-gateways.md), [multiple instance](/components/modeler/bpmn/multi-instance/multi-instance.md)) this can lead to race conditions that you need to think about. The less data you write, the smaller the problem. While the easiest way is to avoid large variables, one option to keep things light during job activation is to use the `FetchVariables` parameter. Remember, by default, when this parameter is omitted, the job payload will contain _all_ variables visible within the scope ([see the variables documentation for more on that](../../concepts/variables.md). This could mean tens or more variables, of arbitrary size, and it can be difficult to estimate how much this will represent in general. We recommend you use the `FetchVariables` parameter, and only fetch the variables which your job handler needs. This will keep the amount of data transferred to a minimum, and will greatly help performance. ## Scaling workers If you need to process a lot of jobs, you need to think about optimizing your workers. Workers can control the number of jobs retrieved at once. In a busy system it makes sense to not only request one job, but probably 20 or even up to 50 jobs in one remote request to the workflow engine, and then start working on them locally. In a lesser utilized system, long polling is used to avoid delays when a job comes in. Long polling means the client’s request to fetch jobs is blocked until a job is received (or some timeout hits). Therefore, the client does not constantly need to ask. You will have jobs in your local application that need to be processed. The worst case in terms of scalability is that you process the jobs sequentially one after the other. While this sounds bad, it is still a valid approach for many use cases, as most projects do not need any parallel processing in the worker code as they simply do not care whether a job is executed a second earlier or later. Think of a business process that is executed only some hundred times per day and includes mostly human tasks — a sequential worker is totally sufficient. In this case, you can skip this paragraph section. However, you might need to do better and process jobs in parallel and utilize the full power of your worker’s CPUs. In such a case, you should read on and understand the difference between writing blocking and non-blocking code. ### Blocking / synchronous code and thread pools With blocking code a thread needs to wait (is blocked) until something finishes before it can move on. In the above example, making a REST call requires the client to wait for IO — the response. The CPU cannot compute anything during this time period, however, the thread cannot do anything else. Assume that your worker shall invoke 20 REST requests, each taking around 100ms, this will take 2s in total to process. Your throughput can’t go beyond 10 jobs per second with one thread. A common approach to scaling throughput beyond this limit is to leverage a thread pool. This works as blocked threads are not actively consuming CPU cores, so you can run more threads than CPU cores — since they are only waiting for I/O most of the time. In the above example with 100ms latency of REST calls, having a thread pool of 10 threads increases throughput to 100 jobs/second. The downside of using thread pools is that you need to have a good understanding of your code, thread pools in general, and the concrete libraries being used. Typically, we do not recommend configuring thread pools yourself. If you need to scale beyond the linear execution of jobs, leverage reactive programming. ### Non-blocking / reactive code Reactive programming uses a different approach to achieve parallel work: extract the waiting part from your code. With a reactive HTTP client you will write code to issue the REST request, but then not block for the response. Instead, you define a callback as to what happens if the request returns. Most of you know this from JavaScript programming. Thus, the runtime can optimize the utilization of threads itself, without you the developer even knowing. ### Recommendation In general, using reactive programming is favorable in most situations where parallel processing is important. However, we sometimes observe a lack of understanding and adoption in developer communities, which might hinder adoption in your environment. ## Performance best-practices Most of the business logic in your process models will likely end up being worked on as a job. As such, optimizing how jobs are handled in Zeebe can have a big impact on the performance of your system as a whole. Here are some best practices to keep things running smoothly. ### Reduce latency by enabling job streaming We recommend enabling [job streaming](../../concepts/job-workers.md#job-streaming) in order to reduce latency to a maximum. Essentially, when using long polling, your job workers have to periodically poll every partition in your Zeebe cluster to check if there are new jobs available. Additionally, they have to balance polling aggressively with minimizing their impact on the cluster, which still has to handle all requests, even when no jobs are available. In large clusters, this can add a noticeable delay in the order of seconds, which can be unacceptable for certain workloads. > [!Note] > You can read more about the difference between long polling and job streaming [in this blog post](https://camunda.com/blog/2024/03/reducing-job-activation-delay-zeebe/). As such, we recommend using job streaming if possible. ## Client library examples Let’s go through a few code examples using Java, Node.js, and C#, using the corresponding client libraries. All [code is available on GitHub](https://github.com/berndruecker/camunda-cloud-clients-parallel-job-execution) and a [walk through recording is available on YouTube](https://youtu.be/ZHKz9l5yG3Q). ### Java Using the [Java Client](https://github.com/camunda/camunda-platform-get-started/tree/master/java) you can write worker code like this: ```java client.newWorker().jobType("retrieveMoney") .handler((jobClient, job) -> { //... }).open(); ``` The [Camunda Spring Boot Starter](/apis-tools/camunda-spring-boot-starter/getting-started.md) provides a more elegant way of writing this, but also uses a normal worker from the Java client underneath. In this case, your code might look like this: ```java @JobWorker(type = "retrieveMoney", autoComplete = false) public void retrieveMoney(final JobClient client, final ActivatedJob job) { //... } ``` In the background, a worker starts a polling component and [a thread pool](https://github.com/camunda-cloud/zeebe/blob/d24b31493b8e22ad3405ee183adfd5a546b7742e/clients/java/src/main/java/io/camunda/zeebe/client/impl/ZeebeClientImpl.java#L179-L183) to [handle the polled jobs](https://github.com/camunda/camunda/blob/d24b31493b8e22ad3405ee183adfd5a546b7742e/clients/java/src/main/java/io/camunda/zeebe/client/impl/worker/JobPoller.java#L109-L111). The [**default thread pool size is one**](https://github.com/camunda-cloud/zeebe/blob/760074f59bc1bcfb483fab4645501430f362a475/clients/java/src/main/java/io/camunda/zeebe/client/impl/ZeebeClientBuilderImpl.java#L49). If you need more, you can enable a thread pool: ```java ZeebeClient client = ZeebeClient.newClientBuilder() .numJobWorkerExecutionThreads(5) .build(); ``` In the Camunda Spring Boot Starter, you can do this using a [configuration](/apis-tools/camunda-spring-boot-starter/configuration.md#execution-threads). Now, you can **leverage blocking code** for your REST call, for example, the `RestTemplate` inside Spring: ```java @JobWorker(type = "rest", autoComplete = false) public void blockingRestCall(final JobClient client, final ActivatedJob job) { LOGGER.info("Invoke REST call..."); String response = restTemplate.getForObject( // <-- blocking call PAYMENT_URL, String.class); LOGGER.info("...finished. Complete Job..."); client.newCompleteCommand(job.getKey()).send() .join(); // <-- this blocks to wait for the response LOGGER.info(counter.inc()); } ``` Doing so **limits** the degree of parallelism to the number of threads you have configured. You can [observe in the logs](https://github.com/berndruecker/camunda-cloud-clients-parallel-job-execution/blob/main/results/java-blocking-thread-1.log) that jobs are executed sequentially when running with one thread ([the code is available on GitHub)](https://github.com/berndruecker/camunda-cloud-clients-parallel-job-execution/blob/main/java-worker/src/main/java/io/berndruecker/experiments/cloudclient/java/RestInvocationWorker.java): ``` 10:57:00.258 [pool-4-thread-1] Invoke REST call… 10:57:00.258 [ault-executor-0] Activated 32 jobs for worker default and job type rest 10:57:00.398 [pool-4-thread-1] …finished. Complete Job… 10:57:00.446 [pool-4-thread-1] …completed (1). Current throughput (jobs/s ): 1 10:57:00.446 [pool-4-thread-1] Invoke REST call… 10:57:00.562 [pool-4-thread-1] …finished. Complete Job… 10:57:00.648 [pool-4-thread-1] …completed (2). Current throughput (jobs/s ): 2 10:57:00.648 [pool-4-thread-1] Invoke REST call… 10:57:00.764 [pool-4-thread-1] …finished. Complete Job…10:57:00.805 [pool-4-thread-1] …completed (3). Current throughput (jobs/s ): 3 ``` If you experience a large number of jobs, and these jobs are waiting for IO the whole time — as REST calls do — you should think about using **reactive programming**. For the REST call, this means for example the Spring WebClient: ```java @JobWorker(type = "rest", autoComplete = false) public void nonBlockingRestCall(final JobClient client, final ActivatedJob job) { LOGGER.info("Invoke REST call..."); Flux paymentResponseFlux = WebClient.create() .get().uri(PAYMENT_URL).retrieve() .bodyToFlux(String.class); // non-blocking, so we register the callbacks (for happy and exceptional case) paymentResponseFlux.subscribe( response -> { LOGGER.info("...finished. Complete Job..."); client.newCompleteCommand(job.getKey()).send() // non-blocking, so we register the callbacks (for happy and exceptional case) .thenApply(jobResponse -> { LOGGER.info(counter.inc()); return jobResponse;}) .exceptionally(t -> {throw new RuntimeException("Could not complete job: " + t.getMessage(), t);}); }, exception -> { LOGGER.info("...REST invocation problem: " + exception.getMessage()); client.newFailCommand(job.getKey()) .retries(1) .errorMessage("Could not invoke REST API: " + exception.getMessage()).send() .exceptionally(t -> {throw new RuntimeException("Could not fail job: " + t.getMessage(), t);}); } ); } ``` This code uses the reactive approach to use the Zeebe API: ``` client.newCompleteCommand(job.getKey()).send() .thenApply(jobResponse -> { counter.inc(); return jobResponse; }) .exceptionally(t -> { throw new RuntimeException("Could not complete job: " + t.getMessage(), t); }); ``` With this reactive glue code, you don’t need to worry about thread pools in the workers anymore, as this is handled under the hood from the frameworks or the Java runtime. [You can observe in the logs](https://github.com/berndruecker/camunda-cloud-clients-parallel-job-execution/blob/main/results/java-nonblocking.log) that many jobs are now executed in parallel — and even by the same thread in a loop within milliseconds. ``` 10:54:07.105 [pool-4-thread-1] Invoke REST call… […] 30–40 times! 10:54:07.421 [pool-4-thread-1] Invoke REST call… 10:54:07.451 [ctor-http-nio-3] …finished. Complete Job… 10:54:07.451 [ctor-http-nio-7] …finished. Complete Job… 10:54:07.451 [ctor-http-nio-2] …finished. Complete Job… 10:54:07.451 [ctor-http-nio-5] …finished. Complete Job… 10:54:07.451 [ctor-http-nio-1] …finished. Complete Job… 10:54:07.451 [ctor-http-nio-6] …finished. Complete Job… 10:54:07.451 [ctor-http-nio-4] …finished. Complete Job… […] 10:54:08.090 [pool-4-thread-1] Invoke REST call… 10:54:08.091 [pool-4-thread-1] Invoke REST call… […] 10:54:08.167 [ault-executor-2] …completed (56). Current throughput (jobs/s ): 56, Max: 56 10:54:08.167 [ault-executor-1] …completed (54). Current throughput (jobs/s ): 54, Max: 54 10:54:08.167 [ault-executor-0] …completed (55). Current throughput (jobs/s ): 55, Max: 55 ``` These observations yield the following recommendations: | | Blocking Code | Reactive Code | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- | | Parallelism | Some parallelism is possibly by a thread pool, which is used by the client library. The default thread pool size is one, which needs to be adjusted in the config in order to scale. | A processing loop combined with an internal thread pool, both are details of the framework and runtime platform. | | **Use when** | You don't have requirements to process jobs in parallel | You need to scale and have IO-intensive glue code (e.g. remote service calls like REST) | | | Your developers are not familiar with reactive programming | This should be the **default** if your developer are familiar with reactive programming. | ### Node.js client Using the [Node.js client](https://github.com/camunda/camunda-8-js-sdk), your worker code will look like this, assuming that you use Axios to do rest calls (but of course any other library is fine as well): ```js zbc.createWorker({ taskType: "rest", taskHandler: (job) => { console.log("Invoke REST call..."); axios .get(PAYMENT_URL) .then((response) => { console.log("...finished. Complete Job..."); job.complete().then((result) => { incCounter(); }); }) .catch((error) => { job.fail("Could not invoke REST API: " + error.message); }); }, }); ``` This is **reactive code**. And a really interesting observation is that reactive programming is so deep in the JavaScript language that it is impossible to write blocking code, even code that looks blocking is still [executed in a non-blocking fashion](https://github.com/berndruecker/camunda-cloud-clients-parallel-job-execution/blob/main/results/nodejs-blocking.log). Node.js code scales pretty well and there is no specific thread pool defined or necessary. The Camunda 8 Node.js client library also [uses reactive programming internally](https://github.com/camunda/camunda-8-js-sdk/blob/main/src/zeebe/zb/ZBWorker.ts#L27). This makes the recommendation very straight-forward: | | Reactive code | | ------------ | ------------------------------ | | Parallelism | Event loop provided by Node.js | | **Use when** | Always | ### C# Using the [C# client](https://github.com/camunda/camunda-platform-get-started/tree/master/csharp), you can write worker code like this: ```csharp zeebeClient.NewWorker() .JobType("payment") .Handler(JobHandler) .HandlerThreads(3) .Name("MyPaymentWorker") .Open() ``` You can observe that you can set a number of handler threads. Interestingly, this is a naming legacy. The C# client uses the [Dataflow Task Parallel Library (TPL)](https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/dataflow-task-parallel-library) to implement parallelism, so the thread count configures the degree of parallelism allowed to TPL in reality. Internally, this is implemented as a mixture of event loop and threading, which is an implementation detail of TPL. This is a great foundation to scale the worker. You need to provide a handler. For this handler, you have to make sure to write non-blocking code; the following example shows this for a REST call using the [HttpClient](https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=net-5.0) library: ```csharp private static async void NonBlockingJobHandler(IJobClient jobClient, IJob activatedJob) { Log.LogInformation("Invoke REST call..."); var response = await httpClient.GetAsync("/"); Log.LogInformation("...finished. Complete Job..."); var result = await jobClient.NewCompleteJobCommand(activatedJob).Send(); counter.inc(); } ``` The code is executed in parallel, [as you can observe in the logs](https://github.com/berndruecker/camunda-cloud-clients-parallel-job-execution/blob/main/results/dotnet-nonblocking.log). Interestingly, the following code runs even faster for me, but [that’s a topic for another discussion](https://stackoverflow.com/questions/21403023/performance-of-task-continuewith-in-non-async-method-vs-using-async-await): ```csharp private static void NonBlockingJobHandler(IJobClient jobClient, IJob activatedJob) { Log.LogInformation("Invoke REST call..."); var response = httpClient.GetAsync("/").ContinueWith( response => { Log.LogInformation("...finished. Complete Job..."); jobClient.NewCompleteJobCommand(activatedJob).Send().ContinueWith( result => { if (result.Exception==null) { counter.inc(); } else { Log.LogInformation("...could not do REST call because of: " + result.Exception); } }); }); } ``` In contrast to Node.js, you can also write **blocking code** in C# if you want to (or more probable: it happens by accident): ```csharp private static async void BlockingJobHandler(IJobClient jobClient, IJob activatedJob) { Log.LogInformation("Invoke REST call..."); var response = httpClient.GetAsync("/").Result; Log.LogInformation("...finished. Complete Job..."); await jobClient.NewCompleteJobCommand(activatedJob).Send(); counter.inc(); } ``` The degree of parallelism is down to one again, [according to the logs](https://github.com/berndruecker/camunda-cloud-clients-parallel-job-execution/blob/main/results/dotnet-blocking-thread-1.log). So C# is comparable to Java, just that the typically used C# libraries are reactive by default, whereas Java still knows just too many blocking libraries. The recommendations for C#: | | Blocking code | Reactive code | | ------------ | -------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | Parallelism | Some parallelism is possibly by a thread pool, which is used by the client library. | A processing loop combined with an internal thread pool, both are details of the framework and runtime platform. | | **Use when** | **Rarely**, and only if you don't have requirements to process jobs in parallel or might even want to reduce the level or parallelism. | This should be the **default** | | | Your developers are not familiar with reactive programming | You need to scale and have IO-intensive glue code (e.g. remote service calls like REST) | --- ## Doing a proper POC When evaluating your process automation approach, a **proof of concept (POC)** is often a good step to check if the process automation methodology, the standards of BPMN and DMN, as well as the Camunda technology suit your needs. It is vital for a POC to make up your mind about your goals, to select a suitable process, and to prepare it and carry it out properly. ## Understanding POC With a POC, you create a prototype application within no more than _three to five days_. The result of a POC is intended to be thrown away after having served its purpose: to try and show that your project will "fly" - including all aspects relevant for your specific situation. Such aspects might be: - Does Camunda _fit into your own architecture_? - Does the _development approach_ fit into your own organization's approaches? - How can you _model_ a specific business domain problem? - Which kind of _know how_ is needed for the business and development teams? - Which _effort_ will typically be needed for these kinds of projects? - What are the impacts of process applications for _operations_? Often, it does make sense to implement such a POC together with Camunda, our partners, or specialized consultants to get quick results and focused feedback with respect to your specific challenges. However, you should always at least _co-develop_ the POC yourself to really understand what is going on. A team size of two to four people has proven to be quite optimal. ## Defining and focusing on specific goals Before planning and carrying out a POC, you should consciously clarify the specific goals you want the POC to achieve. Typical goals might be: - To _verify_ the approach or the tool works under specific circumstances. - To _show_ a case that _convinces_ internal stakeholders that the approach makes sense. - To work through a complete _example_ and get specific _questions_ sorted out. - To _learn_ about Camunda and _understand_ how it works. :::note When selecting your goal, keep in mind the needs of all relevant stakeholders. ::: Do not just "collect" goals here, but try to make up your mind as to what really matters. Often, it is better to make a clear choice. For example, whether to show off a nice user interface at the end of the week or to have time to clarify all questions and to understand Camunda in depth, maybe even only using unit tests. ## Defining a scope relevant to your business Select a _useful_ and _suitable_ process, case, or decision given your goals. Typically, it should... - Be _relevant_ to your _core business_ stakeholders. - Make your organization's _return on investment_ on BPM more transparent. - Be _feasible_ within the POC time box. Avoid political mine fields when selecting the process for your POC. ## Planning the POC ### Involving the right people It does make sense to implement a POC together with the software vendor and/or specialized consultants to get _quick results_ and _focused feedback_ with respect to your specific challenges. However, you should always at least _co-develop_ the POC to really understand what is going on. When planning for your team, consider that successful process modeling requires not just knowledge about the business and the targeted technical solution, but experience with BPMN modeling and methodology as well as analytical and moderation skills. We therefore typically bring together _business people_ with _IT staff_ and internal _business analysts_, _train them properly_ and let them continue to _learn on the job_ by carrying out the POC together with an _experienced consultant_. A team size of up to a maximum of _four people_ has proven to be quite optimal. In case you want to access _system interfaces_ during your POC, also determine who will be a technically knowledgeable and available _contact person_ for that system. To integrate into existing _user interfaces_, you might need help from colleagues within your organization. Define a _moderator_ to avoid too many detours and keep your POC on track. ### Planning the technical environment Make the necessary technological choices. Typically, POCs _run on Camunda 8 SaaS_ unless your goal is to validate that Camunda 8 runs in your Kubernetes environment in a self-managed fashion. A simple test account is often sufficient, unless your goal is to do load or performance tests, for which you need bigger clusters. Reach out to us in such cases. To access _third party systems_ during your POC, set up proper test systems for those and verify that they are usable. Prepare a location in a _version control system_ where you can develop your POC. Having a shared repository with history does make sense also (or especially) in a 2-day POC! Collaboration is simplified if the Camunda consultant can also access that repository. It may be worth just creating a repository with weaker access limitations for the POC. If your organization cannot easily set up a repository for the POC, or access for externals is impossible, you can create a cloud repository. We typically recommend [GitHub](https://github.com/); a free account is sufficient. It gives you a Git repository and you can invite all necessary people for the POC. Afterwards, you can delete that repository. ### Selecting the time frame As already mentioned above, we typically plan no more than _a focused week_ for the POC workshop itself. Sometimes it also works well to split up the POC into two weeks of 2-3 days each, which allows everybody to reflect on the POC over the weekend. - Plan _1-3 days_ for _modeling_ the process with Camunda Modeler. - Plan _2-3 days_ for _implementing_ the process solution. When selecting the exact time frame, consider all the people involved, as well as any technical preparation you need to do up front. You also might want to plan for further steps, like a few more things you implement yourself internally in a second follow up week. ## Presenting the results Before presenting the results of your POC to a wider audience of stakeholders, select a _speaker_ who is comfortable with presenting, prepare a set of focused _slides_ illustrating your progress and the lessons learned, and _test_ your solution and presentation at least once up front. The speaker might also be your Camunda Consultant - they are used to presenting to a wide audience! ## Checklists ### Technical - _Cloud Access_: Make sure you have an account for Camunda 8 with an active subscription or trial account. - _Installations_: Make sure your _developer systems_, as well as any _target systems_ for the POC test and production you wish to use are set up. In particular install: - Camunda _Modeler_ (https://camunda.org/download/modeler/) - Java, Maven, and your favorite IDE (e.g. Eclipse) - Make sure _Maven_ runs and builds and it can access all necessary dependencies. [Download and build this project](https://github.com/camunda/camunda-platform-tutorials/tree/main/quick-start/microservice-orchestration/worker-java) to verify that your build runs. - _Developer Computers_: For maximum productivity, all participating developers should use the computer with which they work every day. Avoid using computers from a training room or shared laptops unless they allow a remote connection to the developer's personal computer. If the developer's computers are neither portable nor remotely accessible consider conducting the POC in the regular office space of the developers. If your company network is restricting access to Maven and Git repositories on the internet, consider using laptops that are not connected to the company network. Similarly, you should not force the external consultants to work on one of your computers. They will be twice as productive on their laptops and not lose time with software setup, configuration, and access restrictions. Obviously, you do not have to connect the consultant's laptop to your company network. Internet access and a shared code repository are enough to collaborate. - _Files_ or _Version Control System_: Make sure we can easily exchange files and code during the POC, preferably via your own version control system (e.g. Git or SVN) or at least via shared folders, USB sticks, or email attachments. - _Interfaces_: Clarify which technical systems' interfaces you want to access during your POC, make any _documentation_ for those available to the whole POC team, and make sure there is a technically knowledgeable _contact person_ for the interface available to the team during the POC. Set up a _test system_ and verify that it is usable. Verify with Camunda that everything is clear to the team, in particular from a technological perspective. ### Organizational Inform all POC team members and other relevant stakeholders about the following: - _Goals_ and the selected _scope_ for the POC - _Start_ and _end times_, as well as any additional preparation/meet-up times - _Names and roles_ of all involved _people_ - For onsite POCs: - Exact _location/address_ at which the POC is taking place as well as instructions about how to find together when arriving (for onsite POCs) - _Projector_, white-board, and flip-chart availability - _Internet_ availability for team members and external consultants - For remote POCs: - Exact meeting setup. For example, links to the meeting room, passwords, etc. In case you can't easily host meetings for external participants, your Camunda consultant can setup a Zoom or Microsoft Teams call. - Ideally, some chat capability (e.g. a temporary Slack account) Ideally, prepare a few _organizational_ and/or _project_ info slides to get everybody up to speed on day one of the workshop. --- ## Following the customer success path Following certain steps when evaluating and introducing process automation helps to make it a success. Ensure you review the appropriate best practices at the right time. ## Understanding the customer success path When introducing Camunda as a new process automation platform inside your company, the following process has shown to work best: 1 _Evaluation_: Take the philosophy of the evaluated products into greater consideration than working solely with feature matrices. Practical experience can be invaluable. You might be interested in our [Whitepaper: "Camunda compared to alternatives"](https://page.camunda.com/wp-camunda-compared-to-alternatives). 2 _Process selection_: It is very important to select a suitable pilot process. Use a relevant process where you can show benefits of BPM including a Return on Invest (ROI) calculation. However, avoid too big or too "political" processes to minimize the risk of failure due to avoidable reasons. Note that you can use this process in the proof of concept (PoC) or select a different process for the first PoC, depending on the goals you have. 3 _Proof of Concept_ (PoC): Model the process to a high standard. It should be clear, understandable, and precise, as it will have a high visibility. Include necessary technical proofs, like calling real services in your environment. Include human tasks if your process where appropriate. We suggest using Camunda Tasklist as a first step to save effort in developing your own tasklist, unless a tasklist is important for your overall proof. Include "eye candies" like reporting to make non-technical stakeholders happy. Concentrate on the important aspects to do the proof and prepare to throw away the code afterwards to start fresh for the pilot, as it is very valid for early POCs to be "hacky" in order to keep focus on the end goals. 4 _Development_: Model the process with the same standard described for a PoC. It should be clear, understandable, and precise. Again, the reason for this is that it will be the most visible part of the project. Develop the process application in an iterative manner to learn fast. Do proper testing to achieve a high quality. 5 _Operations_: Prepare for real operations, which includes setting up the real hardware as well as securing and monitoring the platform. 6 _Pilot review_ and _Pilot improvements_: Review the project after it has finished and gone live. Take some time to clean up, as the project normally serves as a "lighthouse" and "copy and paste" template for sequential projects, so it is worth the effort. It's better to plan time for this phase than try to make things perfect during early development, as you will have learned a lot once the pilot runs on the live system for a while. 7 _Next processes_: Try to avoid doing too many projects in parallel in the beginning to allow new learning to influence your future work. If you have parallel pilots, organize knowledge sharing between the teams. Ideally, let the team of the first pilot directly implement a sequential process. 8 _Custom BPM Platform_: In bigger organizations, you typically try to set up your custom BPM platform, meaning a common infrastructure for all upcoming Camunda projects. Try to do as little of this as possible during the first pilot and start building the platform afterwards, taking all learnings into account. At the same time, do what is necessary for the pilot project itself or for other stakeholders to feel comfortable (e.g. Enterprise Architecture). 9 _Process architecture_: BPM initiatives often start by drafting a process landscape and capture all relevant processes of the company. Try to avoid this, and do as little as possible during your first pilot project. Maybe do a quick process survey to capture relevant processes (by name) to identify a good candidate for the pilot. Especially do not model all processes in your company in depth before you experienced an "end-to-end" project, including automation of Camunda yourself. Then, you will have gained a deeper understanding of methodology and value around BPMN and DMN. ## Estimating effort When starting your BPM project, it is often necessary to roughly estimate the expected effort. A process model can serve as a central artifact for estimation purposes. Avoid too fine-grained estimations as they typically are not worth the effort. However, on a management level one often must have some estimations to secure budgets, get projects started, allocate needed resources, and communicate expected time frames. The success factor is to do estimations _on a very rough level_ and avoid spending too much time with details. More often than not, the details develop differently than expected anyway. We often note customers successfully estimate _T-Shirt size categories (S, M, L, XL and XXL)_. Such an approach is sufficient for us to make roughly informed decisions about priority and return on investment. ![T-Shirts](following-the-customer-success-path-assets/t-shirts.png) Having said that, your organization may demand that you _map_ such rough sizes to some measuring system already used; for example, _story points_ or _person days_. To preserve the rough character, consider mapping the sizes by using a series of sharply increasing numbers: | S | M | L | XL | XXL | | --- | --- | --- | --- | --- | | 2 | 5 | 13 | 50 | 200 | Much more important than concrete numbers is an educated gut feeling. Therefore, try to understand the influencing factors determining most of the effort by implementing your lighthouse process. ### Using the process model for estimation A process model can be seen as a central artifact for estimation purpose, as it indicates and visually maintains a lot of the influencing factors mentioned above. Here are the figures you could estimate: 1. Setting up development environment: **S** 2. Modeling and understanding requirements: **L** 3. Implementing the process solution: - Implement UI for PDF upload: **S** 1 - Implement forms: **S** 2 3 4 5 - Implement integrating the PDF archive: **S** 6 4. Going live: **M** Using the process model, you can also foresee potential effort drivers, for example: - The legacy archive is really hard to integrate. - The tasks need to be integrated into an existing legacy task list, which might not be straight forward to do. - The metadata from the PDF shall be extracted, and a specialized form be shown to the user. --- ## Building flexibility into BPMN models BPMN modeling primarily targets structured processes, often with the goal to automate as many steps as possible, increase efficiency, and decrease process execution costs. But sometimes we need ways to build flexibility into such process models to deal with expected or unexpected operational problems or to allow for humans to intervene. ## Understanding the required symbols To build flexibility into BPMN process models, one must understand BPMN symbols and modeling techniques. After introducing the main symbols, we can demonstrate more concrete examples. ### Use events as triggers BPMN events allow us to react to all kinds of information. We can use them to trigger flexible activities. In particular, BPMN events **catching** **messages**, **conditions**, and **timeouts** are useful in that context. ### Boundary events to add activities on triggers BPMN allows us to attach events to the boundary of activities to trigger some follow-up action. By modeling such an event as either **interrupting** or **non-interrupting**, we can decide to do the activities either _instead of_ the activity we attach the event to, or _in addition to_ it. ### Subprocesses with boundary events By attaching boundary events not just to individual activities, but also to subprocesses, we can flexibly define the area or scope for which we want to trigger some flexible activities. 1 While we are occupied with carrying out some area of activities, in a scope of our process... 2 ...an event might occur, which causes us... 3 ...to carry out this activity in addition to continuing with ordinary work. ### Event subprocesses Sometimes we need to build in flexible activities which are carried out at any point in time. In such cases, we can leverage BPMN's event-based subprocesses. ### Escalation events Sometimes we need highly flexible means to cancel scopes or trigger additional activities from within a scope. The BPMN escalation events can be particularly useful to implement such requirements. 1 As soon as we are finished with the first activity inside the scope... 2 ...we inform the surrounding scope about that and trigger an additional, essential activity... 3 ...but also continue with our second activity to complete the subprocess. 4 We can then already continue with the follow-up work regardless of whether that additional activity is already finished. ### Terminate end events To build flexibility into process models, it is also useful to remember that the termination end event just terminates the scope within which it is defined and therefore _not_ always the whole process instance. With that technique, it becomes possible to cancel some activities inside a subprocess while completing it successfully and leaving it via the typical outgoing path. 1 As soon as one of our two activities achieves the result, we can cancel the other one... 2 ...and successfully complete the subprocess and normally continue with our follow-up work. ## Examples ### Allow proactive order status communication Assume that for an order to be validated, the customer must determine the delivery date before we can confirm the order. If the order is not acceptable—due to consistency issues or customer related issues—it is declined. Some of our orders might be so important that we want to ensure we keep customers happy, even if not everything runs smoothly on our side. 1 Order managers can request proactive customer communication on demand. Assume they can communicate the reasons via a form, whereas the communication as such is carried out by the call center. 2 On a regular basis, we check based on some rules, whether the order is so important that we proactively communicate why the order is not yet confirmed. Again, the communication is carried out by the call center. ### Allow for order cancellation any time The customer might be allowed to request a cancellation until the order is confirmed. This request would have to be reviewed to determine whether we must accept the cancellation. 1 Whenever the customer requests a cancellation until the order is confirmed, we review that request and decide whether we have to accept the cancellation or not. 2 If we accept the cancellation, we must terminate the entire process. To do so, we need to use one trick: throw an error event that will end the current event subprocess, but not yet the order process. 3 This leads to another subprocess to be triggered, and this one is interrupting. Now, the process instance is really cancelled. ### Allow for order details to change, but repeat order validation :::caution Camunda 7 Only Condition events are [not yet supported in Camunda 8](/components/modeler/bpmn/bpmn-coverage.md) ::: If the customer changes the order details, the order must be validated again. --- ## Choosing the DMN hit policy **Hit policies** describe different ways (standardized by DMN) to evaluate the rules contained in a decision table. Different hit policies do not only lead to different results, but typically also require different modes of thinking and reason about the meaning of the entire table. Therefore, it's crucial to not just know the different DMN hit policies, but also to understand the motivations for their existence and the most typical cases for using them. ## Knowing the DMN hit policy basics A decision table consists of several **rules**, typically represented as rows. When reading such a row, we look at certain **input values** and deduct a certain result represented by **output values**. When using the simplest hit policy **"unique"** (**U**), such rules do **not overlap**: only a single rule must match. 1 We define an "input" value **season** here. For every single season ... 2 ... there is a **jacket** defined we want to use, the "output" of the rules here. 3 The hit policy "**Unique**" (indicated by the character **U**) enforces that rules do **not overlap**: only a single rule must match. Now consider that we build a decision table with **overlapping rules**. In other words, that means more than one rule may match a given set of input values. We then need one of the **alternative hit policy** indicators to unambiguously understand the decision logic according to which such rules are interpreted. The hit policy **indicator** is a single character shown in the decision table's top left cell, right beneath the decision's name. The character is the initial letter of one of the defined seven hit policies `U`**nique**, `A`**ny**, `P`**riority**, `F`**irst**, `C`**ollect**, `O`**utput order** and `R`**ule order**. Furthermore, the hit policy 'Collect' may also be used with one of four aggregation operators, actually giving us four more hit policies `C+` (**Sum**), `C<` (**Minimum**), `C<` (**Maximum**) and `C#` (**Number**). Eight of those eleven hit policies evaluate a decision table to a **single result**. Three hit policies evaluate a decision table to **multiple results**. ### Single result decision tables Such tables either return the output of only one rule or aggregate the output of many rules into one result. The hit policies to be considered are - `U`**nique**: Rules do not overlap. Only a single rule can match. - `F`**irst**: Rules are evaluated from top to bottom. Rules may overlap, but only the first match counts. - `P`**riority**: Rule outputs are prioritized. Rules may overlap, but only the match with the highest output priority counts. :::note Camunda does not yet support the hit policy **priority**. In essence, priorities are specified as an ordered list of output values in decreasing order of priority. Such priorities are therefore independent from rule sequence! Though not yet supported, you can mimic that behavior using hit policy "(**C**)ollect" and determining a priority yourself; for example, by means of an execution listener attached to the end of your business rule task. ::: - `A`**ny**: Multiple matching rules must not make a difference: all matching rules must lead to the same output. **Collect** and **aggregate**: The output of all matching rules is aggregated by means of an operator: - `C+`**Sum**: Add up all the matching rule's distinct outputs. - `C<`**Minimum**: Take the smallest value of all the matching rule's outputs. - `C>`**Maximum**: Take the largest value of all the matching rule's outputs. - `C#`**Number**: Return the number of all the matching rule's distinct outputs. ### Multiple result decision tables **Multiple result** tables may return the output of multiple rules. The hit policies for such tables are: - `C`**ollect**: All matching rules result in an arbitrarily ordered list of all the output entries. - `R`**ule order**: All matching rules result in a list of outputs ordered by the sequence of those rules in the decision table. - `O`**utput order**: All matching rules result in a list of outputs ordered by their (decreasing) output priority. :::note Camunda does not yet support the hit policy **output order**. In essence, output orders are specified as an ordered list of output values in decreasing order of priority. Such priorities are therefore independent from rule sequence! Though not yet supported, you can mimic that behavior using hit policy "(**C**)ollect" and determining an output order yourself; for example, by means of an execution listener attached to the end of your business rule task. ::: ## Understanding DMN hit policy use cases Most situations can be addressed using different hit policies. In that case, the hit policy will have an effect on the readability and maintainability of the table. Often it is worth trying different varieties until you have a feel for what will work best. ### Unique: granting categories of customers a specified discount Hit policy "**Unique**" will typically make it easy to build a decision table, which ensures your rules are "complete" - in the sense that the rules do not just not overlap but cover all possible input values - so that you do not "forget" anything. 1 The _input_ area of each row specifies a certain **segment** of possible input values. 2 This row, for example, expresses that _long time silver customers receive a 9% discount_. Such a use case fits to the hit policy "**Unique**". For such use cases, it is an advantage that this hit policy make your decision logic invalid in case you violate its requirement that your table rules never "overlap": after all, you must not produce ambiguous results. ### First: accepting a customer based on hard criteria Having said that, the hit policy "**First**" can sometimes make it easier for an organization to reason about decision logic dealing with some criteria that are "harder" (more "clearcut") than others. Furthermore, it can help to make a decision table layout more compact and therefore easier to interpret. 1 Assume that everybody in the organization knows that first rule: "Once on the blocklist, never again accepted." The layout and the hit policy of the decision table therefore supports the organization's way of doing business: once we know that single fact about a customer, we don't need to think further. 2 The following rules from row 2-4 are expressed in an "Accept" manner and might change more often over time. The organization's way of thinking is literally "from top to bottom". Once we find an acceptance rule, we can deal with the customer. 3 For execution in a decision engine, don't forget to add a rule not accepting any other customers as a last row. In scenarions dealing with **hard** **exclusion** and **inclusion** criteria, we often don't care that much if the rules overlap, but prefer to argue about very clearcut cases first and about more sophisticated ones later on. Furthermore, the organization's way of thinking and doing business might be better supported by a decision table using the hit policy **First**. Our experience so far tends to show that it can be more tricky and error prone to argue about a **First** hit policy decision table than it might occur to you at first sight. Therefore, be especially careful and always test your logic in case you are dealing with sensitive business! ### Collect: deciding which groups of people may review an order With hit policy **collect**, you do not care about the order or any interdependencies between your rules at all. Instead, you just "collect" independent rules and care about the question which rules are applicable to your specific case. Consider, for example, the question of "who is allowed" to carry out some action, as, for example, reviewing and deciding about incoming orders: As a result of this decision table, we will either get `["Sales"]` or `["Management"]` or a list of both groups `["Sales", "Management"]`. We could use this information to route the order into the applicable group's task lists or control access rights of a configurable software solution, etc. Of course, you could at any time introduce more rules and eventually also differentiate between more groups without changing your software solution. ### Sum: accepting a customer based on soft criteria Hit policy "collect" may be combined with operators such as **Sum (C+)**, leading to very different use cases. A very typical one is the requirement to evaluate a case based on manyfold factors influencing the overall result. Assume, for example, that we want to deal with customers we know nothing about. They receive a score of 0. But in case we know something about them, we also weigh in our knowledge: 1 The overall creditworthiness is deducted by throwing in many factors. 2 Here, for example, we give credit in case we made good experiences with the customer in the past. 3 A very low current income does not matter as long as the customer is not a stranger to us! 4 On the other hand, as soon as a customer has proof for a good income, they receive five points for "reasonable" income as well as 10 points extra for good income. Even if we had bad experience with a customer (which means they start from -15), we end up with an overall score of 0 in case the customer has a good income now, and start to accept the customer again. In scenarions dealing with **soft exclusion** and **inclusion** criteria, we need a mechanism to associate a weight to different scenarios. This is ideally supported by hit policy **Sum (C+)**. --- ## Choosing the resource binding type Camunda 8 offers version binding for linked processes, decisions, or forms. This allows you to deploy new versions without disrupting live processes, and prevents production outages. You can choose the binding type for the linked target resource for the following BPMN process elements: - [Call activities](/components/modeler/bpmn/call-activities/call-activities.md#defining-the-called-process) - [Business rule tasks](/components/modeler/bpmn/business-rule-tasks/business-rule-tasks.md#defining-a-called-decision) (if the DMN decision implementation is used) - [User tasks](/components/modeler/bpmn/user-tasks/user-tasks.md#user-task-forms) (if a Camunda Form is linked) The binding type determines the version of the target resource used at runtime. For example, for a call activity this would be the version of the called process to be instantiated. ## Supported binding types Camunda 8 supports the following binding types: Type Description latest Resolves to the latest deployed version of the target resource at the moment the process element is activated. You can use this option to easily change the target resource at runtime by deploying a new version of the resource. This allows for fast, iterative development, and easy hot fixes in production: Process instances that have not yet reached the process element use the newly deployed version of the target resource once the element is activated. If the process element has already been reached, you can use process instance modification to reactivate and relink it to the newly deployed target resource version. Be aware that using latest can lead to unexpected behavior if you deploy a new version of the target resource without ensuring backwards compatibility with every deployed process that depends on it.This might not be suited for production environments that require stability and a predictable behavior. deployment Resolves to the specific version of the target resource that was deployed together with the currently running version of the process in the same deployment. This option ensures predictable behavior by tying the two versions together, and allows you to deploy future versions of the target resource without disrupting ongoing process instances. It is ideal for self-contained projects without external or shared dependencies. To use the deployment binding option, create and deploy a process application in Web Modeler, or deploy multiple resources together via the Zeebe API. versionTag Resolves to the specific version of the target resource that is annotated with the given version tag. The version tag is a user-provided string (for example 1.2.0.Final) that makes it easy to identify a certain version of a resource and track it across multiple deployment stages (e.g. dev, test, prod). You can set the version tag for a BPMN process, DMN decision, or Form in the Modeler's properties panel. A version tag is different from the numeric process definition version assigned by the Orchestration Cluster. Using the versionTag binding option ensures that the right version of the target resource is always used, regardless of future deployments, by pinning the dependency to a specific version. The option is ideal for managing external or shared dependencies. Caution: If the target resource ID and version tag pair are not deployed, the process instance will have an incident. To avoid this situation, ensure the version tag defined in the call activity, business rule task, or user task matches the version tag in the dependent resource. Be aware that you can deploy a new version of a resource with an already existing version tag. In this case, the version tag reference will be updated and point to the latest deployed version. :::note If the binding type is not explicitly specified in your BPMN diagram, `latest` is used as the default. ::: --- ## Creating readable process models We create visual process models to better understand, discuss, and remember processes. Hence, it is crucial that models are easy to read and understand. The single most important thing is to use well-chosen labels. ## Essential practices ### Labeling BPMN elements Use [conventions for naming BPMN elements](naming-bpmn-elements.md); this will consistently inform the reader of the business semantics. The clarity and meaning of a process is often only as good as its labels. 1 _Start event_ labels informs the reader of how the process is _triggered_. 2 An _activity_ - labeled as "activity" - informs the reader of the piece of _work_ to be _carried out_. 3 _Gateway_ labels clarifies based on which condition(s) and along _which sequence flow_ the process proceeds. 4 Labeled _boundary events_ clearly express in which cases a process execustion might follow an _exceptional path_. 5 Labeled _end events_ characterize end _results_ of the process from a business perspective. ## Recommended practices ### Modeling symmetrically Try to model symmetrically. Identify related splitting and joining gateways and form easily recognizable _visual_, eventually _nested_, _blocks_ with those gateways. 1 The inclusive gateway splits the process flow into two paths which are ... 2 ... joined again with an inclusive gateway. Inside that block ... 3 another exclusive gateway splits the process flow into two more paths which are ... 4 ... joined again with an exclusive gateway. By explicitly showing _pairs of gateways_ "opening" and "closing" parts of the process diagram, and by positioning such gateway pairs _as symmetrically as possible_, the readability of process model is improved. The reader can easily recognize logical parts of the diagram and quickly jump to those parts the reader is momentarily interested in. ### Modeling from left to right Model process diagrams _from left to right_. By carefully positioning symbols from left to right, according to the typical point in time at which they occur, one can improve the readability of process models significantly: Modeling from left to right supports the reading direction (for western audience) and supports the human field of vision - which prefers wide screens. ### Creating readable sequence flows Consciously decide whether _overlapping sequence flows_ make your model more or less readable. On one hand, avoid overlapping sequence flows where the reader will not be able to follow the flow directions anymore. Use overlapping sequence flows where it is less confusing for the reader to observe just one line representing several sequence flows leading to the same target. Avoid sequence flows _violating the reading direction_, meaning no outgoing flows on the left or incoming flows on the right of a symbol. 1 The author could have made the five (!) sequence flows leading into the end event visible by separating them. However, by consciously choosing to partly overlap those flows, this model becomes less cluttered, therefore less confusing and easier to read. 2 The author could have attached the sequence flow, leaving this task on its left. However, this would have decreased readability, because the flow connection violates the reading direction. The same applies to incoming flows on the right of a symbol. _Avoid flows crossing each other_ and _flows crossing many pools or lanes_, wherever possible. Rearrange the order of lanes and paths to make your sequence flows more readable. Oftentimes, removing lanes can improve readability! Rearrange the order of pools in a collaboration diagram to avoid message flows crossing pools as much as possible. Often, you will find a "natural" order of pools reflecting the order of first involvement of parties in the end-to-end process. This order will often also lead to a minimum of crossing lines. _Avoid very long (multi page) sequence flows_, especially when flowing against the reading direction. The reader will lose any sense of what such lines actually mean. Instead, use link events to connect points which are not on the same page or screen anymore. 1 You observe a throwing link event here, which... 2 ...directly links to a catching link event just as if the sequence flow would have been connected. Avoid excessive use of link events. The example above serves to show the possible usage, but at the same time, it is too small to satisfy the usage of link events in real-world scenario! ### Modeling explicitly Make your models easier to understand by modeling _explicitly_, which most often means to either completely avoid certain more "implicit" BPMN constructs, or at least to use them cautiously. Always consider the central _goal of increased readability_ and understandability of the model when deciding whether to model explicitly or implicitly. When in doubt, it's best to favor an explicit style. #### Using gateways instead of conditional flows Model splitting the process flow by always using _gateway symbols_ such as instead of conditional flows . 1 For example, you could've left out this inclusive gateway by drawing two outgoing sequence flows directly out of the preceding task **Choose menu** and attaching conditions to those sequence flows (becoming conditional sequence flows ). However, experience shows that readers understand the flow semantics of gateways better, which is why we do not make use of this possibility. #### Modeling start and end events Model the trigger and the end status of processes by always explicitly showing the _start_ and _end event symbols_. :::caution Process models without start and end event cannot be executed on the Camunda workflow engine ::: 1 According to the BPMN standard, you could have left out the start event... 2 ...as long as you also leave out the end events of a process. However, you would have lost important information in your model, which is why we do not make use of this syntactical possibility. Be specific about the _state_ you reached with your event from a _business perspective_. Quite typically, you will reach "success" and "failure" like events from a business perspective: 1 'Invoice paid' better qualifies the "successful" business state than e.g. 'Invoice processed' would... 2 ...because in principle, you can call the failed state 'Invoice processed', too, but the reader of the diagram is much better informed by calling it 'Invoice rejected'. #### Separating splitting and joining gateways In general, avoid mixing up the split and join semantics of gateways by explicitly showing _two separate symbols_: 1 You could have modeled this join implicitly by leaving out the explicitly joining XOR gateway and directly connecting two incoming sequence flows to... 2 ...the subsequent splitting XOR gateway. Of course, BPMN would allow this for other gateway types, too. However, experience shows that readers will often overlook the join semantics of such gateways serving two purposes at the same time. The fact that readers will often overlook the join semantics of gateways serving to join as well as split the process flow at the same time, combined with the preference for [modeling symmetrically](#modeling-symmetrically), leads us to prefer _splitting and joining gateways modeled with separate symbols_. However, there are cases in which the readability of models can be improved with _implicit modeling_. Consider the following example: 1 The two incoming sequence flows to the task "Review tweet" could be merged with an XOR gateway, following explicit modeling. We argue that a merging XOR gateway directly behind the start event decreases the readability. A merging XOR gateway is a passive element and the reader expects the process to continue with an active element after the start event. #### Using XOR gateway markers Model the XOR gateway by explicitly showing the **X** symbol, even if some tools allow to draw a blank gateway. 1 You could have shown the splitting gateway... 2 ...as well as the joining gateway without the **X** symbol indicating that it is an exclusive gateway. The **X** marker makes a clearer difference to the other gateway types (inclusive, parallel, event-based, complex) which leads us to prefer _explicit XOR gateway markers_ in general. #### Splitting sequence flows with parallel gateways Always model splitting the process flow by explicitly showing the _gateway symbol_: 1 You could have modeled this parallel split implicitly by leaving out the gateway and drawing two outgoing sequence flows out of the preceding task **Choose menu**. However, the reader needs deeper BPMN knowledge in order to understand this model. Additionally, for joining the parallel flows... 2 ...you will always need the explicit symbol. The fact that readers of models using parallelization will likely need to understand the semantics of a parallel join combined with the preference for modeling symmetrically leads us to prefer _explicit parallel gateways_, too. #### Joining sequence flows with XOR gateways Model joining the process flow by explicitly showing the _XOR gateway symbol_ so the reader does not have to know BPMN details to understand how two incoming or outgoing sequence flows in a task behave. Additionally, this often supports the [symmetry of the model](#modeling-symmetrically) by explicitly showing a "relationship" of the splitting and joining _gateways forming a visual "block"_. 1 You could have modeled this join implicitly by leaving out the gateway and directly connecting the two incoming sequence flows to the subsequent task **Have lunch**. However, explicitly modeling the join better visualizes a block, the joining gateway semantically "belongs" to... 2 ...the earlier split. In case the reader is not interested in the details of dinner preparation but just in having dinner, it's easy to "jump" to the gateway, "closing" that logical part of the model. This is particularly helpful for models bigger than that example with many such (eventually nested) blocks. Consider the following model, showing two _nested blocks_ of gateways: 1 Now, you couldn't have modeled this join implicitly, because it's directly followed by an inclusive gateway with very different join semantics. _Consistency_ of joining techniques is another reason why we prefer explicitly joining sequence flows in general. There are always exceptions to the rule! There are cases in which the readability of models can be _improved_ with _implicit modeling_. So don't be dogmatic about explicit modeling; always aim for the most readable model. The following example shows a case of a model in which splitting and joining points do not form natural "blocks" anyway. In such cases, it can be preferable to make use of _implicit joining_ to improve the overall readability! ### Avoiding lanes Consider _avoiding lanes_ for most of your models all together. They tend to conflict with several of the best practices presented here, like [Modeling _Symmetrically_](#modeling-symmetrically), [Emphasizing the _Happy Path_](#emphasizing-the-happy-path) and [Creating Readable _Sequence Flows_](#creating-readable-sequence-flows). Apart from readability concerns, our experience also shows that lanes make it more difficult to change the resulting process models and therefore cause considerably _more effort in maintenance_. When modeling on an _operational level_, where showing the responsibility of roles matters most, we recommend to [use _collaboration diagrams_](#using-collaboration-diagrams) with several _separate pools_ for the process participants instead of lanes. However, the usage of lanes might be meaningful for: - _Strategic_ level models (refer to [BPMN Tutorial](https://camunda.com/bpmn/) and [Real-Life BPMN](https://www.amazon.com/Real-Life-BPMN-4th-introduction-DMN/dp/1086302095/) on details for modeling levels) - especially when they have a focus on _responsibilities and their borders_. - _Technical/executable_ models with a focus on _human work-flow_ and its ongoing "ping pong" between several participants. For these cases, also consider alternative methods to maintain and show roles: - As a _visible part_ of the _task name_, e.g. in between squared brackets []: _"Review tweet [Boss]"_. :::caution Camunda 7 Only During execution you can remove this part of the task name if you like by using simple mechanisms like shown in the [Task Name Beautifier](https://github.com/camunda/camunda-consulting/tree/master/snippets/task-name-beautifier) so it does not clutter your tasklist. ::: - As a _text annotation_ or a _custom artifact_ :::note Roles are part of your executable BPMN process model as _technical attributes_ anyway - even if hidden in the BPMN diagram. For example, they can be used during execution for assignment at runtime. ::: ## Helpful practices ### Emphasizing the happy path You may want to emphasize the _"happy path"_ leading to the delivery of a successful process result by placing the tasks, events, and gateways belonging to the happy path on a straight sequence flow in the center of your diagram - at least as often as possible. The _five_ BPMN symbols belonging to the happy path are put on a straight sequence flow in the center of the diagram. ### Avoid modeling retry behavior A common idea is to model retry behavior into your process models. This _should be avoided_ in general. The following process model shows a typical example of this anti pattern: All operations use cases put into the model can be handled via Camunda tooling, e.g. by [retrying](/components/concepts/job-workers.md#completing-or-failing-jobs) or [Camunda Operate](/components/operate/operate-introduction.md). ### Using collaboration diagrams If you model on an operational level (refer to [BPMN Tutorial](https://camunda.com/bpmn/) and [Real-Life BPMN](https://www.amazon.com/Real-Life-BPMN-4th-introduction-DMN/dp/1086302095/) on details for modeling levels) use _collaboration diagrams_ with several _separate pools_ for the process participants [instead of lanes](#avoiding-lanes) as operational models using lanes make it very hard for the individual process participant to identify the details of their process involvement. Furthermore, model just _one coherent process per pool_ (apart from event subprocesses, of course), even though BPMN in principle allows several processes per pool. This improves readability by constituting a clear visual border around every process and by providing a natural space for labeling that part of the end-to-end process in the pool's header. 1 The Team Assistance is responsible for initial "Invoice Collection" as well as "Invoice Clarification" - if applicable. Those two processes are modeled by using two separate pools for the team assistance, just as... 2 ...the approver can observe the "Invoice Approval" process in a separate pool and... 3 ...the managing director can observe the "Invoice Payment" process in a separate pool while the collaboration diagram as a whole shows the business analyst that the overall end-to-end process works. Using _collaboration diagrams_ with _separate pools_ for the process participants allows to explicitly show interaction and communication between them by means of message flow and further improves readability by transparently showing the participants their own involvement in the end-to-end-process. As a consequence, they do not need to fully read and understand the end-to-end process in order to read, understand, and agree to their own involvement by looking at their own pools. ### Showing interaction with systems Consciously decide how you want to model systems the process participants are interacting with. Use _data stores_ to show systems which primarily serve as a means to store and retrieve data. Use - depending on your needs _collapsed_ or _expanded_ - _pools_ for systems which are carrying out crucial activities in the process going way beyond storing and retrieving data. 1 A _collapsed pool_ is used to represent a system which supports the process and/or carries out process tasks on its own. The pool could be expanded later to model the internal system details, maybe even with the goal to execute a technical process flow directly with a BPMN capable process engine. 2 A _data store_ is used to represent a technical container meant to archive PDFs and store them for later retrieval. 3 Another _data store_ is used to represent a container which could be a physical storage place for paper invoices to be paid at the moment but could become a representation for business objects in a database with the object state "to be paid" in the future. When _choosing_ between those _two options_ for modeling systems (data stores, collapsed pools) keep in mind that only pools represent processes and therefore have the capability to be expanded and modeled in all their internal details later on. ### Avoiding excessive usage of data objects Avoid excessive use of _data objects_, but use them cautiously to show the _most important data related aspects_ of your process. Experience shows that many data objects and especially many data associations quickly clutter your process model and that visual noise reduces readability - especially for less experienced readers. You might find three practices helpful to find your own "right" amount of data visualization: 1 Cautiously use data objects and associations to show the _most important data related aspects_ of your process. We could have modeled that all the tasks in the "Payments Creation" process either read, update, or delete the "new payment", however we decided that we just want to point out that the process works on a new payment object. 2 Use data stores for _coupling processes via data_. We could have modeled a lot of other tasks in the process that either read or update the "payments", however, we decided to just point out the most important aspect for the process diagram, which is that the "Payments Creation" process of delivery service is loosely coupled with the "Payments Processing" via commonly shared data. 3 Here we decided that it's helpful to know that this message does not only inform an adjustment possibility was checked, but that it also delivers all the necessary details of the adjustment. ### Avoiding changes to symbol size and color Leave the _size of symbols as it is_ by default. For example, different sizes of tasks or events suggest that the bigger symbol is more important than the smaller one - an often unwarranted assumption. Instead of writing long labels, use short and consistent labels in line with your [naming conventions](naming-bpmn-elements.md) and move all additional information into BPMN annotations associated to your specific BPMN element. Furthermore, avoid _excessive use of colors_. Experience shows that colors are visually very strong instruments and psychologically very suggestive, but will typically suggest different things to different readers. Additionally, a colorful model often looks less professional. However, there are valid exceptions. For example, you could mark the _happy path_ through a process with a visually weak coloring: Another case for useful coloring might be to make a visual difference between _human_ and _technical flows_ within a bigger collaboration diagram by coloring the header bar on the left side of the pools. --- ## Modeling beyond the happy path First, model the happy path to the desired end result before collecting problems and exceptions, prioritizing them, and introducing them incrementally. Secondly, focus on one selected issue at a time, and choose the right techniques for modeling beyond the happy path. ## The happy path and beyond The happy path is kind of the default scenario with a positive outcome, so no exceptions, errors, or deviations are experienced. Typically, you want to model the happy path first, and therefore you should define the desired _end result_, find a suitable _start event_, and collect the _activities_ and external _dependencies_ which _always_ need to be considered to reach the result. When we have that, the diagram shows the _happy path_ of a business process (or of the selectively chosen part of the end-to-end business process): 1 _End Event_: It's often the easiest first step to agree upon the desired ("happy") end _result_ of a process. 2 _Start Event_: As a second step, one might agree upon a _trigger_ for the work leading to the end result. 3 _Activities_: After that, you can brainstorm and collect activities which _always_ need to be carried out to reach the result. 4 _Intermediate Events_: Optionally, you can brainstorm and collect _milestones_ (modeled as blank events) and important external _dependencies_ (e.g. modeled as message events). ### Modeling beyond the happy path by error scenarios As soon as you have this happy path, start modeling beyond the happy path. Focus on _one_ particular, selected problem at a time. 1. Try to _understand_ the worries for _the business_ in the light of the desired end result. 1. Identify the _undesired end result_ the process will reach in case the problem cannot be mitigated. This informs you about the _end event_ you will eventually reach because of the problem. 1. Identify the affected areas in the happy path. Can the problem occur at a _particular point_, _during_ (one or several) _activities_, or basically _all the time_? This will inform you about the most promising modeling technique for the problem: whether either _gateways_, _boundary events_, or _event-based subprocesses_ can serve you to fork off your "problem path". This best practice will guide you through practices that help you model beyond the happy path. ## Forking off at a particular point With BPMN gateways, we can deal with problems arising at a _particular point_ in our process. ### Dealing with results By using data-based gateways, we _actively decide_ "now and here" on the basis of our own _process data_ which path our process must move along. For example, we can therefore use an XOR gateway to fork off a "problem path," dealing with a problematic result of _our own activities_: 1 The _exclusive gateway_ deals with the potentially problematic result of incomplete order data. Note that we deal here with the procedural consequences of work which already took place in the preceding task, where we actually checked the order for completeness. 2 Again, the preceding task already dealt with the actual work of checking the customer's creditworthiness. The _result_ of the task is a "yes" or "no" (true or false). We can deal with data by means of a data-based gateway, which immediately redirects to the path our process must move along. 3 The _end event_ characterizes the undesired end result "order declined," which we now reach because of having modeled two problems. In the example, both of them lead to one and the same business outcome. ### Dealing with events By using event-based gateways, we _passively wait_ for _future events_ deciding about which path our process will have to move along. For example, we can therefore use use it to fork off a "problem path" dealing with an undesired event _outside of our own control_: 1 After having requested a delivery date (e.g. from wholesale), we use an _event-based gateway_ to passively wait for what happens next. We can not know "now and here", because it's outside of our own control. 2 The _intermediate message event_ allows us to deal with the undesired event that the ordered good is not deliverable. ### Dealing with missing results via timeouts By using event-based gateways, we can also deal with the situation that _nothing relevant_ for our process _happens_. We do this by defining a time period, after which we decide that we do not want to wait any longer: 1 The _intermediate timer event_ allows us to deal with the situation that nothing relevant for our process happened for a defined time period. In case we do not get an answer from wholesale, we inform the customer that the order is not deliverable at the moment. ## Forking off during (one or several) activities With BPMN boundary events, we can deal with problems arising _while we are actively occupied_ to carry out work in our process. ### Dealing with errors A typical case is that it turns out to be _impossible to achieve the result_ of an activity while working on it. We can then choose to interrupt our work and fork off a "problem path" to deal with the issue: 1 The _interrupting boundary error event_ allows us to deal with the fact that the order is not readable. As this prevents us from properly judging the completeness of the order, we cannot reach one of the expected results of our activity ("complete" or "not complete"), but instead deal with the problem by interrupting the activity and assuming the order to be declined. When modeling for business process automation, "dealing with errors" might be a highly technical concern. As a rule of thumb, we just want to show the _"business related" problems_ in a process model: those problems and errors which cause that our business process must move along a different path, because different work must be carried out as a reaction. An example for a typical technical concern would be that we currently cannot reach a system, which is why, for example, we want to re-attempt it another time later on. We do not show such purely technical problems in a business process diagram, not even in an executable one: (1) It would clutter the diagram, and (2) There are more suitable ways to deal with technical issues potentially occuring almost anywhere. Read our Best Practice about [dealing-with-problems-and-exceptions](../../development/dealing-with-problems-and-exceptions) from a more technical point of view to learn more about the border between business related shown in a process diagram and purely technical concerns not shown in a process diagram. ### Dealing with work on top of usual work Another typical use case for reacting to situations while we are actively occupied is that it sometimes turns out we need to do stuff _in addition to what we already do_: 1 We encapsulate part of our process into a subprocess to enable us to express that while we are occupied with that part of the process, additional work might pop up. 2 The _non-interrupting boundary timer event_ allows us to speed up order preparation in case it takes longer than two days; for example, by informing a responsible manager. ## Being able to react all the time A bit similar to boundary events, with BPMN event subprocesses we can deal with problems arising while we are actively occupied to carry out work. The main advantage when being compared with boundary events is that some issues can _occur almost anywhere_ on our way through the happy path. ### Dealing with issues occurring almost anywhere Some issues can occur almost anywhere on the way through our process. The event subprocess allows us to fork off a _problem path_ modeled separately from our main process to deal with such issues: 1 The _non-interrupting start message event_ of the event subprocess allows us to express that wherever we currently are on our way through order confirmation, it can happen that the customer requests information about the status of that process. 2 We should then provide the requested information without interferring with the order confirmation process itself. ### Dealing with canceling the process Another typical use case for event-based subprocesses is a cancellation requested by the customer: 1 The _interrupting start message event_ of the event subprocess allows us to express that wherever we currently are on our way through order confirmation, it can happen that the customer requests cancellation. 2 We should then interrupt the main process (which is already expressed by the nature of the start event) and inform an involved dealer. ## Boundary events as alternative for event based gateways ### Using receive tasks with boundary events The examples above leverage the _event based gateway_. BPMN also allows to model _receive tasks_ that wait for responses. This has the advantage that you now can leverage boundary events to deal with _missing results_ or other _events occuring while you are waiting_ for the response. This is an _alternative_ to the event-based gateways shown in the above models. 1 Instead of modeling an event for receiving a delivery date, we model a _task_ here. 2 The fact that we do not receive such an answer at all can now be modeled as an _interrupting boundary timer event_. We inform the customer about the status, but as the timer is interrupting, do not wait any longer for the delivery date. 3 It might turn out that the ordered good is not deliverable. This can be modeled as _boundary message event_. Upon that message we cancel any further waiting but inform the customer about the status instead. ### Modeling a multi phase escalation path Boundary events are particularly useful when you consider that you might want to remind your dealer that the answer is overdue and give them another chance for transmitting the delivery date before you give up waiting. First, consider how this could be achieved by using event-based gateways: 1 After having realized that the dealer's answer is late, we decide whether we want to remind the dealer and continue to wait - or not. We modeled here that we want to remind the dealer just once. 2 However, note that while we are reminding the dealer, we are strictly speaking not in a state "ready-to-receive" the dealer's answer! According to BPMN execution semantics, the dealer's message might get lost until we are back at the event-based gateway. While you might want to choose to ignore that when modeling for communication purposes only, you will need to get it right for executable models. To get the BPMN execution semantics above fully right, we would now need to attach the two possible answers of the dealer ("Delivery data fixed", "Ordered good not available") as boundary events to the task "Remind dealer", too! Quite a modeling construct, just to properly wait for the dealer's response, right? Therefore, consider the following alternative to this modeling issue using boundary events only: 1 Modeling a _non-interrupting boundary timer event_ directly at a task which waits for the response has the advantage that we never leave the "ready-to-receive" state and therefore avoid troubles with the strict interpretation of BPMN execution semantics. The second alternative is _very compact_ and avoids issues with _not being ready-to-receive_, but typically needs a _deeper understanding_ of BPMN symbols and their consequences for the token flow. Therefore, we sometimes also prefer event-based gateways for showing human flows, and ignore sophisticated token flow issues as discussed here. --- ## Modeling with situation patterns When modeling, you will sometimes realize that some situations share common characteristics. To save work for yourself and spread such knowledge within your organization, collect and document such patterns as soon as you understand their nature and have found a satisfying solution for modeling them. For a start, we collected some typical patterns for you, which we observe quite often in our modeling practice. You do not need to reinvent the wheel over and over again. ## Escalating a situation step by step You need something and hope that it happens. Such a hope for a result may materialize, but it does not have to! After some time, you will typically become impatient and try to do something to make it happen. But if it then still does not happen, there comes a point at which you will have to decide that you must accept a failure. We sometimes also call that very common pattern a **multi-step escalation**. **Example:** "A month ago, I ordered a pair of shoes with that new online shop! After two weeks of waiting: nothing. I contacted them to determine what's up. The clerk promised me that the shoes will leave the warehouse today! But again, nothing, so after another week I just canceled that order. Since then I did not hear a word." In this scenario, the shop clearly did not implement the escalation of the delay properly. They should have applied one of the following patterns in the order delivery process: ### Option 1: Using event-based gateways 1 After ordering the goods, the process passively waits for the success case by means of an event-based gateway: the goods should be delivered. However, in case this does not happen within a reasonable time, we make a first step of escalation: remind the dealer. 2 We still stay optimistic. Therefore, the process again passively waits for the success case by means of another event-based gateway: the goods should still be delivered. However, in case this does not happen again within a reasonable time, we make a second step of escalation: cancel the deal. **Evaluation:** - :thumbsup: This solution explicitly shows how the two steps of this escalation are performed. Timers are modeled separately, followed by their corresponding escalation activities. - :thumbsdown: The usage of separate event-based gateways leads to _duplication_ (for example, of the receiving message events) and makes the model _larger_, even more so in case multiple steps of escalation need to be modeled. - :thumbsdown: During the time we need to remind the dealer, we are strictly speaking not in a position to receive the goods! According to the BPMN specification, a process can handle a message event only if it is ready to receive at exactly the moment it occurs. Fortunately, Camunda 8 introduced [message buffering](/components/concepts/messages.md#message-buffering), allowing to execute this model properly without loosing messages. Using Camunda 7, the message might get lost until we are at the second event-based gateway. :::note You might want to use that pattern when modeling _simple two phase escalations_. You should not execute it on Camunda 7. ::: ### Option 2: Using gateways forming a loop 1 After having ordered the goods, the process passively waits for the success case by means of an event-based gateway: the goods should be delivered. However, in case this does not happen within a reasonable time... 2 We choose by means of an exclusive gateway to make a _first step of escalation_: remind the dealer. We still stay optimistic. Therefore, the process returns to the event-based gateway and again passively waits for the success case: the goods should still be delivered. However, in case this does not happen again within a reasonable time, we choose a _second step of escalation_: cancel the deal. **Evaluation:** - :thumbsup: This model is a more _compact_ and more _generic_ modeling solution to the situation. If it comes to multiple steps of escalation, you will need such an approach to avoid huge diagrams. - :thumbsdown: The solution is _less explicit_. We could not choose to label the timer with explicit durations, as a single timer is used for both durations. The solution is _less readable_ for a less experienced reading public. For a fast understanding of the two step escalation, this method of modeling is less suitable. - :thumbsdown: During the time we need to remind the dealer, we are strictly speaking not in a position to receive the goods! According to the BPMN specification, a process can handle a message event only if it is ready to receive at exactly the moment it occurs. Fortunately, Camunda 8 introduced [message buffering](/components/concepts/messages.md#message-buffering), allowing to execute this model properly without loosing messages. Using Camunda 7, the message might get lost until we are at the second event-based gateway. :::note You might want to use that pattern when modeling _escalations with multiple steps_. You should not execute it on Camunda 7. ::: ### Option 3: Using boundary events 1 After having ordered the goods, the process passively waits for the success case by means of a receive task: the goods should be delivered. However, in case this does not happen within a reasonable time... 2 a non-interrupting boundary timer event triggers a _first step of escalation_: remind the dealer. We still stay optimistic. Therefore, we did not interrupt the receive task, but continued to wait for the success case: the goods should still be delivered. 3 However, in case this does not happen within a reasonable time, we trigger a _second step of escalation_ by means of an interrupting boundary timer event: interrupt the waiting for delivery and cancel the deal. **Evaluation:** - :thumbsup: This model is even more _compact_ and a very _generic_ modeling solution to the situation. If it comes to multiple steps of escalation, the non-interrupting boundary timer event could even trigger multiple times. - :thumbsup: The model complies with BPMN execution semantics. Since we never leave the wait state, the process is always ready to receive incoming messages. - :thumbsdown: The solution is _less readable_ and _less intuitive_ for a less experienced reading public, because the way the interrupting and non-interrupting timers collaborate requires a profound understanding of boundary events and the consequences for token flow semantics. For communication purposes, this method of modeling is therefore typically less suitable. :::note You might want to use that pattern when modeling _escalations with two steps_ as well as _escalations with multiple steps_ for _executable models._ ::: ## Requiring a second set of eyes For a certain task - typically a critical one in terms of your business - you need the opinion, review, or approval of two different people. We sometimes also call that pattern the **four eyes principle**. **Example:** The manager of a small sized bank's lending department has a problem: "Over the last quarter, we lost €100,000 in unrecoverable medium-sized loans. Controlling now tells me that could probably have been easily avoided by more responsible decisions of our lending department staff! I want that every such decision is signed off by two people from now on." Modeling a process dealing with that requirement can be achieved easily, but the better solution also depends on whether you prefer overall speed over total effort. All of the following modeling patterns assume that the two or more tasks needed to ultimately approve the loan must not be completed by one and the same person. When executing such patterns, you must enforce that with the workflow engine. ### Option 1: Using separate tasks 1 A first approver looks at the loan and decides whether they approve. If they decide not to approve, we are done, but if the loan is approved... 2 ...a second approver looks at the loan. If they also decide to approve, the loan is ultimately approved. **Evaluation:** - :thumbsup: This solution _explicitly_ shows how the two steps of this approval are performed. Tasks are modeled separately, followed by gateways visualizing the decision making process. - Note that the approvers work in a _strictly sequential_ mode, which might be exactly what we need in case we want _minimization of effort_ and, for example, display the reasonings of the first approver for the second one. However, we also might prefer _maximization of speed_. If this is the case, observe solution [option 3 (multi-instance)](#option-3-using-a-multi-instance-task) further below. - :thumbsdown: The usage of separate tasks leads to _duplication_ and makes the model _larger_, even more so in case multiple steps of approvals need to be modeled. You might want to use that pattern when modeling the need for a _second set_ of eyes needed in _sequential_ order, therefore _minimizing effort_ needed by the participating approvers. While it is theoretically possible to model separate, explicit approval tasks in parallel, we do not recommend such patterns due to readability concerns. As a better alternative when looking for _maximization of speed_, observe [option 3 (multi-instance)](#option-3-using-a-multi-instance-task) below. ### Option 2: Using a loop 1 A first approver looks at the loan and decides if they approve. If they decide not to approve, we are done, but... 2 ...if the loan is approved, we turn to a second approver to look at the loan. If they also decide to approve, the loan is ultimately approved. **Evaluation:** - :thumbsup: This model is a more _compact_ modeling solution to the situation. If it comes to multiple sets of eyes needed, you will probably prefer such an approach to avoid huge diagrams. - Note that the approvers work in a _strictly sequential_ mode, which might be exactly what we need if we want _minimization of effort_ and, for example, display the reasonings of the first approver for the second one. However, we also might prefer _maximization of speed_. If this is the case, observe [option 3 (multi-instance)](#option-3-using-a-multi-instance-task) below. - :thumbsdown: The solution is _less explicit_. We could not choose to label the tasks with explicit references to a first and a second step of approval, as a single task is used for both approvals. The solution is _less readable_ for a less experienced reading public. For a fast understanding of the two steps needed for ultimate approval, this method of modeling is less suitable. You might want to use that pattern when modeling the need for _multiple sets_ of eyes needed in _sequential_ order, therefore _minimizing effort_ needed by the participating approvers. ### Option 3: Using a multi-instance task 1 All the necessary approvers are immediately asked to look at the loan and decide by means of a multi-instance task. The tasks are completed with a positive approval. Once all positive approvals for all necessary approvers are made, the loan is ultimately approved. 2 If the loan is not approved by one of the approvers, a boundary message event is triggered, interrupting the multi-instance task and therefore removing all the tasks of all approvers who did not yet decide. The loan is then not approved. **Evaluation:** - :thumbsup: This model is a very _compact_ modeling solution to the situation. It can also easily deal with multiple sets of eyes needed. - Note that the approvers work in a _parallel_ mode, which might be exactly what we need in case we want _maximization of speed_ and want the approvers to do their work independent from each other and uninfluenced by each other. However, we also might prefer _minimization of effort_. If this is the case, refer to [option 1 (separate tasks)](#option-1-using-separate-tasks) or [option 2 (loop)](#option-2-using-a-loop) above. - :thumbsdown: The solution is much _less explicit_ and _less readable_ for a less experienced reading public, because the way the boundary event interacts with a multi-instance task requires a profound understanding of BPMN. For communication purposes, this method of modeling is therefore typically less suitable. You might want to use that pattern when modeling the need for _two_ or _multiple sets_ of eyes needed in _parallel_ order, therefore _maximising speed_ for the overall approval process. ## Measuring key performance indicators (KPIs) You want to measure specific aspects of your process execution performance along some indicators. **Example:** A software developer involved in introducing Camunda gets curious about the business: "How many applications do we accept or decline per month, and how many do we need to review manually? How many are later accepted and declined? How much time do we spend for those manual work cases, and how long does the customer have to wait for an answer? I mean...do we focus on the meaningful cases...?" When modeling a process, we should actually always add some information about important key performance indicators (KPIs) implicitly. For example, specifically [naming start and end events](../naming-bpmn-elements/#naming-events) with the process state reached from a business perspective. Additionally, we might explicitly add additional business milestones or phases. While the following section concentrates on the aspects of modeling KPIs, you might want to learn more about using them for [reporting about processes](../../operations/reporting-about-processes/) from a more technical perspective. For example, when being faced with the task to actually retrieve and present Camunda's historical data collected on the way of execution. ### Option 1: Showing milestones 1 First, we assess the application risk based on a set of automatically evaluable rules. 2 We can then determine whether the automated rules already came to a (positive or negative) conclusion or not. If the rules led to an unsure result, a human must assess the application risk. 3 We use explicit intermediate events to make perfectly clear that we are interested in the applications which never see a human... 4 ...and be able to compare that to the applications which needed to be assessed manually, because the automatic assessment failed to determine a clear result. 5 We also use end events, which are meaningful from a business perspective. We must know whether an application was either accepted... 6 ...or rejected. By means of that process model, we can now let Camunda count the applications which were accepted and declined. We know how many and which instances we needed to review manually, and can therefore also narrow down our _accpeted/declined statistics_ to those manual cases. Furthermore, we will be able to measure the _handling time_ needed for the user task; for example, by measuring the time needed from claiming the task to completing it. The customer will need to wait a _cycle time_ from start to end events, and these statistics, for example, could be limited to the manually assessed applications and will then also include any idle periods in the process. *By comparing the economic *value* of manually assessed insurance policies to the *effort\* (handling time) we invest into them, we will also be able to learn whether we focus our manual work on the meaningful cases and eventually improve upon the automatically evaluated assessment rules. ### Option 2: Emphasizing process phases As an alternative or supplement to using events, you might also use subprocesses to emphasize certain phases in your process. 1 By introducing a separate embedded subprocess, we emphasize the _phase_ of manual application assessment, which is the critical one from an economic perspective. Note that this makes even more sense if multiple tasks are contained within one phase. ## Evaluating decisions in processes You need to come to a decision relevant for your next process steps. Your actual decision depends on a number of different factors and rules. We sometimes also call that pattern **business rules** in BPMN. **Example:** The freshly hired business analyst is always as busy as a bee: "Let's see... Category A customers always get their credit card applications approved, whereas Category D gets rejected by default. For B and C it's more complicated. Right, in between 2500 and 5000 Euros, we want a B customer, below 2500 a C customer is OK, too. Mmh. Should be no problem with a couple of gateways!" ### Showing decision logic in the diagram? When modeling business processes, we focus on the flow of work and just use gateways to show that following tasks or results fundamentally differ from each other. However, in the example above, the business analyst used gateways to model the logic underlying a decision, which clearly is considered to be an anti-pattern! It does not make sense to model the rules determining a decision inside the BPMN model. The rules decision tree will grow exponentially for every additional criteria. Furthermore, we typically will want to change such rules much more often than the process (in the sense of tasks needed to be carried out). ### Using a single task for a decision 1 Instead of modeling the rules determining a decision inside the BPMN model, we just show a single task representing the decision. Of course, when preparing for executing such a model in Camunda, we can wire such a task with a DMN decision table or some other programmed piece of decision logic. 2 While it would be possible to hide the evaluation of decision logic behind the exclusive gateway, we recommend always showing an explicit node with which the data is retrieved, which then might be used by subsequent data-based gateways. ## Distinguishing undesired results from fatal problems You model a certain step in a process and wonder about undesired outcomes and other problems hindering you to achieve the result of the step. **Example:** What today is a problem for the business might become part of the happy path in a less successful future: "Before we can issue a credit card, we must ensure that a customer is credit-worthy. Unfortunately sometimes it might also turn out that we cannot even get any information about the customer. Then we typically also reject at the moment. Luckily, we do have enough business with safe customers anyway." ### Option 1: Using gateways to check for undesired results 1 Showing the check for the applicant's creditworthiness as a gateway also informs about the result of the preceding task: the applicant might be creditworthy - or not. Both outcomes are _valid results_ of the task, even though one of the outcomes here might be _undesired_ from a business perspective. ### Option 2: Using boundary error events to check for fatal problems 1 Not to know anything about the creditworthiness (because we cannot even retrieve information about the applicant) is not considered to be a valid result of the step, but a _fatal problem_ hindering us to achieve any valid result. We therefore model it as a boundary error event. The fact that both problems (an unknown applicant number or an applicant which turns out not to be credit-worthy) lead us at the moment to the same reaction in the process (we reject the credit card application) does not influence that we need to model it differently. The decision in favor of a gateway or an error boundary event solely depends on the exact definition of the result of a process step. Refer to the next section. ### Understanding the definition of the result What we want to consider to be a valid result for a process step depends on assumptions and definitions. We might have chosen to model the process above with slightly different execution semantics, while achieving the same business semantics: 1 The only valid result for the step "Ensure credit-worthiness" is knowing that the customer is in fact credit-worthy. Therefore, any other condition must be modeled with an error boundary event. To advance clarity by means of process models, it is absolutely crucial for modelers to have a clear mental definition of the _result_ a specific step produces, and as a consequence, to be able to distinguish _undesired results_ from _fatal problems_ hindering us to achieve any result for the step. While there is not necessarily a right way to decide what to consider as a valid result for your step, the business reader will typically have a mental preference to observe certain business issues, either more as undesired outcomes or more as fatal problems. However, for the executable pools, your discretion to decide about a step's result might also be limited when using, for example, service contracts which are already pre-defined. ## Asking multiple recipients for a single reply You offer something to or request something from multiple communication partners, but you actually just need the first reply. We sometimes also call that pattern **first come, first serve**. **Example:** A well-known personal transportation startup works with a system of relatively independent drivers. "Of course, when the customer requests a tour, speed is everything. Therefore, we need to limit a tour to those of our drivers who are close by. Of course, there might be several drivers within a similar distance. We then just offer the tour to all of them!" ### Using a multi-instance task 1 After determining all drivers currently close enough to serve the customer, we push the information about the tour to all of those drivers. 2 We then wait for the reply of a single driver. Once we have it, the process won't wait any longer, proceeds to the end event, and informs the customer about the approaching driver. According to the process model, it is possible that another driver accepts the tour as well. However, as the process in the tour offering system is not waiting for the message anymore, it will get lost. As our process proceeded to the end event after the first reply, all subsequent messages are intentionally ignored in this process design. ## Processing a batch of objects You need to process many objects at once, which were already created before one by one, or which were updated one by one to reach a certain status. We sometimes also call that pattern simply the **1-to-n problem**. **Example:** A lawyer explains to a new client the way he intends to bill him: "Of course, if you need advice, you can call me whenever you want! We will agree about any work that needs to be done and my assistant will track those services which are subject to a charge. Once a month mostly you will receive a neatly-structured invoice providing you with all the details!" ### Using data stores and multi instance activities 1 The client asks for advice whenever they need it. Note that we create one process instance per request for advice. 2 The lawyer makes sure to record the billable hours needed for the client. 3 As he does not directly inform anybody by doing this, but rather collects data, we show this with a data store representing the time sheet and a data association pointing in its direction - representing the write operation. 4 The assistant starts their invoicing process on a monthly basis. In other words, we create one process instance per monthly billing cycle. 5 As a first step, the assistant determines all the billable clients. This are the clients for which time sheet entries exist in the respective month. Note that we have _many_ legal advice instances who have a relationship to _one_ billing instance and that the connection is implicitly shown by the read operation on the current status of data in the time sheet. 6 Now that the assistant knows the billable clients, they can iterate through them and invoice all of them. We use a sequential multi-instance subprocess to illustrate that we need to do this for every billable client. 7 On the way, the assistant is also in charge of checking and correcting time sheet entries, illustrated with a parallel multi-instance task. Note that these time sheet entries (and hence task instances) relate here 1:1 to the instances of the lawyer's "legal consulting" process. In real life, the lawyer might have created several time sheet entries per legal advice process, but this does not change the logic of the assistant's process. 8 Once the client is invoiced, the assistant starts a "payment processing" instance per invoice, the details of which are not shown in this diagram. We can imagine that the assistant needs to be prepared to follow up with reminders until the client eventually pays the bill. ## Concurring dependent instances You need to process a request, but need to make sure that you don't process several similar requests at the same time. **Example:** A bank worries about the increasing costs for creditworthiness background checks: "Such a request costs real money, and we often have packages of related business being processed at the same time. So we should at least make sure that if one credit check of a customer is already running, we do not want another credit check for the same customer to be performed at the same time." ### Using message events 1 Once an instance passes this event and moves on to the subsequent actual determination of the creditworthiness... 2 ...other instances will determine that there already exists an active instance and wait to be informed by this instance. 3 When the active instance has determined the creditworthiness, it will move on to inform the waiting instances... 4 ...which will receive a message with a creditworthiness payload and be finished themselves with the needed information. The model explicitly shows separate steps (_determine_ and _inform_ waiting instances) which you might want to implement more efficiently within one single step doing both semantic steps at once by means of a small piece of programming code. ### Using a timer event While using timer events can be a feasible approach in case you want to avoid communication between instances, we do not recommend it. For example, one downside is that such solutions cause delays and overhead due to the perdiodical queries and the loop. 1 Once an instance passes this event and moves on to the subsequent actual determination of the creditworthiness... 2 ...all other instances will go into a wait state for some time, but check periodically, if the active instance is finished. 3 When the active instance has determined the creditworthiness and finishes... 4 ...all other instances will also finish after some time. --- ## Naming BPMN elements Name all elements in your BPMN diagrams by focusing on the business perspective. For activities, use a verb to describe what to do. For events, describe in which (business) state the process or domain object is currently in. For (data-based) gateways, pose a question and describe the conditions under which the process moves on along the outgoing flows. ## Essential practices ### Naming activities Name a _task_ using an object and a verb in the infinitive. By doing this, you consistently describe _what you do with an object_. Name a _subprocess_ (or _call activity_) by using an object and a (by convention _nominalized_) verb. Similar to tasks, you should always describe _what you do with an object_. :::note Avoid very broad and general verbs like "Handle invoice" or "Process order." Try to be more specific about what you do in your activity from a business perspective. ::: ### Naming events Wherever possible, name an _event_ using an object and a verb reflecting a state. Always try to describe _which state an object is in_ when the process is about to leave the event. This naming approach does not always work perfectly. In those cases, precisely describe the business semantics when the process is about to leave the event. The following names are also valid: Be specific about the state you reached with your event from a business perspective. Often, you will reach "success" and "failure" like events from a business perspective: 1 "Invoice paid" better qualifies the "successful" business state than "Invoice processed" would... 2 ...because in principle, you can call the failed state "Invoice processed", too, but the reader of the diagram is much better informed by calling it "Invoice rejected". :::note Avoid very broad and general verbs like "Invoice processed" or "Order handled"! ::: ### Naming gateways Label a data-based _exclusive gateway_ with a question. Label the outgoing sequence flows with the conditions they are executed under. Formulate the conditions as answers to the question posed at the gateway. This naming approach does not always work for _inclusive gateways_, because the outgoing flows' conditions can be completely independent from each other. Still, use a question whenever possible. If this is not possible, leave out the question completely but describe the conditions under which the outgoing paths are executed. _Avoid naming event-based gateways_, but ensure you name their subsequent events. Also, avoid naming _parallel gateways_ and all forms of _joining gateways_. You don't need to specify anything about those gateways, as the flow semantics are always the same. ### Naming processes A _pool_ should be given the same name as the process the pool contains using an object and a nominalized verb. Optionally, add the organizational role responsible for the process shown in the pool as a whole. If you have more than one lane in a pool, name each _lane_ using the organizational role or technical system responsible for carrying out the activities shown in the lane. Name a _diagram_ (file) with same name as the process shown in the diagram. In case of a collaboration diagram, use a name reflecting the end-to-end perspective shown in that diagram. ## Recommended practices ### Using sentence case Use [sentence case](https://en.wiktionary.org/wiki/sentence_case) when naming BPMN symbols. This is standard capitalization of an English sentence, with the first letter uppercase and subsequent letters lowercase, with exceptions such as proper nouns or acronyms. ### Avoiding technical terms Avoid using purely _technical terms_ when naming activities or other BPMN symbols, for example. These are not always clear to every reader. Completely avoid using names of coding artifacts like classes, methods, technical services, or purely technical systems. ## Helpful practices ### Avoiding abbreviations Avoid using _abbreviations_ as they are not always clear to every reader. This is especially true for abbreviations which are specific to companies or departments. Try to avoid them completely. If you want to use an abbreviation in your model (to save space or sometimes even to improve understandability) make sure you explain the abbreviation in the model in brackets, by text annotations, or use an accessible glossary. --- ## Naming technically relevant IDs For executable flows, properly name all relevant technical element IDs in your BPMN diagrams. :::note Keep technical IDs and names short enough for the target environment. Most user-defined BPMN and DMN IDs, names, deployed resource names, and form IDs support up to 32,768 characters with Elasticsearch/OpenSearch-backed storage and 256 characters with RDBMS-backed storage. If you use RDBMS, or might migrate to it later, keep these values comfortably within the 256-character limit. ::: Focus on process, activity, message, and error IDs, but also consider events as well as gateways and their sequence flows that carry conditional expressions. Those elements can show up regularly (e.g. in your logs) and it makes things easier if you can interpret their meaning. ## Using naming conventions for BPMN IDs Define developer-friendly and business-relevant IDs for the process itself, as well as all activities, messages, and errors. Also consider events, gateways, and the sequence flows that carry conditional expressions. Even though IDs are just identifiers, keep in mind that they will show up regularly on the technical level. Meaningful IDs will help a lot. Examine the IDs shown in the following example: The following table provides you with a guideline that we would use in a context where developers are comfortable with _Java_ and _PascalCase_ naming style. You may adapt these suggestions to typical naming conventions used in your programming context. | | | XML Attribute | Prefix or Suffix | Resulting ID | | ----- | ----------------- | -------------------- | ---------------- | ----------------------------- | | **1** | Tweet Approval | process/@id | Process | TweetApprovalProcess | | **2** | New tweet written | startEvent/@id | StartEvent\_ | StartEvent_NewTweetWritten | | | | message/@id | Message\_ | Message_NewTweetWritten | | | | message/@name | Msg\_ | Msg_NewTweetWritten | | **3** | Review tweet | userTask/@id | Task\_ | Task_ReviewTweet | | **4** | Tweet approved? | exclusiveGateway/@id | Gateway\_ | Gateway_TweetApproved | | **5** | No | sequenceFlow/@id | SequenceFlow\_ | SequenceFlow_TweetApprovedNo | | **6** | Tweet duplicated | boundaryEvent/@id | BoundaryEvent\_ | BoundaryEvent_TweetDuplicated | | | | error/@id | Error\_ | Error_TweetDuplicated | | | | error/@errorCode | Err\_ | Err_TweetDuplicated | | **7** | Tweet published | EndEvent\_/@id | EndEvent\_ | EndEvent_TweetPublished | ### Editing IDs with Camunda Modeler We recommend using Camunda Modeler's properties panel on the right side of the screen to edit technical identifiers and change them according to your naming conventions, like it is shown here for the process ID: ![Properties Panel](naming-technically-relevant-ids-assets/camunda-modeler-properties-panel.png) We especially do not recommend editing identifiers in the XML directly, as it might accidentally corrupt your BPMN file. You have to keep the identifiers in the section about the graphical layout (so called "DI" for diagram interchange) further down in sync with the execution semantics at the top of the XML. However, we include an XML example of all those identifiers mentioned for illustration: ```xml ... ``` 8 Elements in the diagram interchange section (DI) reference identifiers from above; you have to adjust them accordingly! Camunda Modeler takes care of this automatically. Changing IDs can potentially break your tests or even process logic if done at a late stage of development. Therefore, consider using meaningful IDs right from the beginning and perform the renaming as part of the modeling. ### Aligning the BPMN file name with the process ID It is a good practice to _align_ the _file name_ of your BPMN models with the _process id_ of the executable process that is inside the file. ![BPMN file name](naming-technically-relevant-ids-assets/aligning-the-bpmn-file-names.png) ## Generating ID constants classes If you have lots of process, case, and decision definitions with lots of IDs, consider generating constant classes (e.g. via XSLT) directly from your BPMN or DMN XML files. For example, this can be used for testing. ## Using a Camunda Modeler plugin to generate meaningful ids You can use [this modeler plugin community extension](https://github.com/camunda-community-hub/camunda-modeler-plugin-rename-technical-ids) to automatically convert your IDs to comply with our best practices. Of course, you could also use this as a basis to create your own modeler plugin to generate IDs that follow your custom naming conventions. Or, you could implement a similar plugin to implement checks if all relavant IDs follow your naming conventions. --- ## Reporting about processes The Camunda engine automatically collects audit information about historical process or decision instances. Leverage this data by generating and displaying business relevant reports. Add business relevant phases and milestones to your process models serving as a basis for key performance indicators (KPIs). ## Modeling key performance indicators (KPIs) When modeling a process, you always add information about important key performance indicators implicitly; for example, by introducing **start and end events**. Additionally, you can explicitly add the following: - Meaningful additional business **milestones** by modeling **intermediate events**, for example. This might not have any execution semantics other than leaving a trace in the history of the workflow engine. The milestone is met as soon as the process has passed the event. Its status can therefore be **passed** or **not passed**. - Meaningful business **phases** by modeling things like (embedded) **subprocesses**. In contrast to a milestone, a phase's state can be **not entered**, currently **active**, or **passed**. Consider the following example - a "Tweet Approval Process" shows start and end events as well as **milestones**: " 3 After one business day, the reviewer is reminded to speed up - and such reviews are internally _marked_ by passing the end event 'Review done slowly'. 4 **Approved tweets** will pass the additional **intermediate event**. The **cycle time** up until that point is automatically captured too. 5 Furthermore, when tweets are successfully published, we are interested in the **ratio** of those tweets... 6 ...when compared to tweets that do not get published. Therefore, we model _two different end events_ representing those two business end states of the process. :::note Duplicate tweets will _not be published_ even though they have been _approved_ before. The more precisely we describe and _name_ the business semantics of events, the better our KPI's will reflect the reality we want to measure! ::: When you do not (only) want to concentrate on milestones, but _phases_ in your process, model the phases as subprocesses: 1 The phase _Review_—modeled with a subprocess—will be active, while the human reviewer will need to find time to complete the task... 2 ...whereas the phase _Publication_ will be completed automatically - hence process instances "remaining" there for longer than a few seconds will probably indicate ongoing problems with the uptime and reachability of the used services. ## History architecture It is useful to understand the architecture around history data in Camunda 8. :::caution Camunda 7 Note that the history architecture is very different in Camunda 7.x, refer to [Camunda 7 User Guide](https://docs.camunda.org/manual/latest/user-guide/process-engine/history/). ::: ![History architecture](reporting-about-processes-assets/history-architecture.png) Camunda saves historical data not just when a process instance finishes, but on the go, while a process instance is active. By doing this, Camunda separates runtime data from history data. A growing history will not influence the runtime behavior, and you should never need to access runtime data for reporting purposes. Historical data can be leveraged via three possible mechanisms: - **Camunda tools**: Leverage Camunda Operate or Camunda Optimize. This is a very simple approach that works out-of-the-box and should satisfy many requirements already. Camunda Operate focuses on operational use cases ("Where is my process? Why did this fail?") whereas Camunda Optimize provides business intelligence about your processes. Optimize allows you to build reports and dashboards including setting alerts for thresholds. - **Query API**: Using the public API (currently under development), this has the advantage that you can make use of the history data within your own applications. - Pushing **events**: Pushing Camunda events by using [exporters](/components/zeebe/technical-concepts/architecture.md#exporters). Note that you can only add own exporters in a Self-Managed setting, not in Camunda 8 SaaS. Exporters have the advantage that you can push the data into any infrastructure you have, and possibly even filter or enrich the data in that step. ## Connecting custom business intelligence systems (BI), data warehouses (DWH), or monitoring solutions You might move data from the Camunda History to a decoupled system like a Business Intelligence (BI) solution, a Data Warehouse (DWH), some Data Lake, or an own monitoring solution, for example based on Prometheus. Leveraging typical BI system's **ETL** (extract, transform, and load) features allows you to optimize data structure for your reporting purposes (to _speed up_ report generation) or to combine generic process engine data with business entities (to allow for _more in-depth analysis_). To get the data into the BI system, leverage one of the mechanisms described above. Our recommendation generally is: - In SaaS, leverage the history API to regularly pull data, as custom exporters are not supported there. --- ## Versioning process definitions For real-life applications, it's crucial to understand how Camunda deals with evolving process definitions by means of versioning. As a rule of thumb, we recommend to version just the process and decision models, but not other process solution artifacts (like e.g. code classes or scripts). Often you might not even want to run multiple model versions at the same time, then you have to think about migrate running process instances to new versions. When modeling very long-running processes (> 6 months), consider cutting them into reasonable pieces to ease managing your versioning requirements. ## Understanding versioning By default, deploying a process or decision definition means that the workflow engine will check if the version has changed. If it has, it will register that deployment as a new version of the definition. By default, running instances will continue to run on the basis of the version they started with, new instances will be created based on the latest version of that definition. ![Versions](versioning-process-definitions-assets/database-versions.png) ## Selecting the best versioning approach ### Running versions in parallel You can run several versions of a model in parallel. The big _advantage_ of that default behavior is that you can deploy changed process definitions without caring about running process instances. The process engine is able to manage running instances based on different process definitions in parallel. The _disadvantage_ is that one needs to deal with the operational complexity of different versions of the process running in parallel as well as the additional complexity in case those processes call subprocesses which have different versions of their own. Run versions _in parallel_ for - _Development_ or _test systems_ for which you do not care about old instances - _Phasing out_ existing instances as the existing instances need to finish based on the model they where created with, which often has _legal reasons_. - Situations in which _migration is not advisable_, because it is too complex and too much effort when weighed against its upsides. ### Migrating process instances to a new version _Migrate_ running instances to the newest definition when: - Deploying _patches or bug fixes_ of a process model. - _Avoiding operational complexity_ due to different versions running in production is a priority. Migrating process instances can be achieved either by using the operations tooling or by calling the Zeebe API. You can use the [Orchestration Cluster API (REST)](../../../apis-tools/orchestration-cluster-api-rest/specifications/migrate-process-instance.api.mdx) or the [Zeebe API (gRPC)](../../../apis-tools/zeebe-api/gateway-service.md#migrateprocessinstance-rpc) to migrate a process instance. Learn more about the concepts of [process instance migration](../../../components/concepts/process-instance-migration.md) in the components section. You can also learn [how to migrate process instances in Operate](../../../components/operate/userguide/process-instance-migration.md) in it's dedicated section. It's important to understand that process instance migration _maintains the full 'identity' of the migrated process instances_ including their unique IDs and their full history audit trail. However, as the process definition also might change fundamentally in between versions, this can have effects on the history log of a process instance which might be unexpected from an end user's or operator's perspective. ### Things to consider before migration When planning your migration, here are some factors to consider: - _Do I have a good reason to migrate?_ Technically, you do not have to migrate process instances when using Camunda. Previous process definition instances will simply continue to run as intended (with some important caveats, note other things to consider below). Here are some examples of good reasons to migrate: - Your supporting implementation resources have changed. - Your latest process definition represents a substantial change in your business process. - Your latest process definition fixes a bug. - Your latest process definition enforces some time-sensitive legal obligations or rules. - _How big of a difference is there between process definition versions?_ Not only the definition itself, but the data required to be present at any given time in your instance. - _Did supporting implementation resources change from the previous deployment?_ If a service implementation changes in the new deployment and the reference to the implementation did not change from the previous deployment, then older process instances that are in flight will utilize the newer implementation by default upon deployment of the new resources. If that breaks older instances, then you must migrate. - _Do I have a proper infrastructure to support “real data” testing of my migration?_ This might be the most important aspect. An ideal way to test your process instance migration would be to have prod-like data in some kind of staging environment that represents not only the type and quality of existing production data, but also volume, scale, and size. You run your migration there so that you know what to expect when it comes time to migrate in production. You also need the ability to quickly reset this data via some kind of snapshot, so that you can test over and over again. You can expect many iterations of your migration before you move forward. ## Avoid versioning of dependant artifacts When versioning process or decision definitions, you need to be aware that the process of course communicates with the outside world, e.g. by _calling services_ or by _using forms_ to collect data input from human users. All the additional artifacts needed for that might _depend_ on the details of each other in a subtle way. Whenever possible, we recommend that you _avoid to version other artifacts_ beyond the process and/or decision definitions, in other words, just version '.bpmn' and '.dmn' files by using the default mechanism of the process engine. Embed all other artifacts (like e.g. classes, templates, scripts) into your normal application (for example a Java or Node.js application) and don't version them. Of course, this approach requires that you _manage the subtle differences_ needed by running process instances of old versions. There are various options to do that. And even if some of those options discussed below might not sound 'ideal' from a theoretical point of view, they proved to be _good enough_ for real life purposes and _much easier to understand_ than complex approaches. As understandability by every team member is a very important argument, we recommend going for the approach that is as simple as possible. The following options us a Java example of a process solution, containing not only the process model, but also some Java code and an HTML form: ![Sample Process Application](versioning-process-definitions-assets/process-solution-example.png) ### Option 1: Keep the artifacts backwards compatible _Extend_ the functionality of e.g. a method in `MyClass.java` in a way which can still deal with "old" process instances. ```java public class MyClass { public void doSomething(Long customerId) { if(customerId != null) { // <1> // new code introduced } } } ``` 1 Assume you introduced a customerId in the new version of the process. Your code can still deal with old cases not aware of a customerId. ### Option 2: Introduce a new artifact for different versions _Change_ the artifact and add a new version of it to the application. Now you can reference this new artifact from your new version of the process definition, while the old version will continue to use the first version of it. For example: - Change the file name for the form from `task-form.html` to `task-form-v2.html` - Change the `task type` of a service task from `doSomething` to `doSomethingV2` ![Sample Process Application](versioning-process-definitions-assets/process-solution-v2.png) Sometimes it is preferable to manage different versions by means of folders/packages. Just make sure to have a clear and straightforward convention to keep track of the versions. ## Dealing with long running processes In general, _do not be concerned with deploying long-running processes_ which might run days, weeks or even months. After all, this is exactly what Camunda was built to properly deal with. Having said that, also review the possibilities the workflow engine provides with respect to _cutting process definitions_ (e.g. via _message exchange_ or via _call activities_) and _migrating running process instances_. But even though it's possible to migrate running process instances to a new version (refer below), it's typically a bit of _effort_. Therefore, the information presented in the following sections is meant to enable your conscious decision at which points it might make sense for you to avoid the necessity for migration by cutting processes and which aspects of versioning behavior you can control by doing that. ### Cutting very long running processes into pieces The longer the lifespans of process instances are, the bigger the _risks_ that you might want to exchange important software components like e.g. the workflow engine itself. Typically, _very long-running, end-to-end processes_ (running longer than _six months_) have periods without activity (e.g. waiting for a certain date in the future). Cut the process into several independent process definitions at these points. 1 After the mobile phone was shipped, we finish the first process instance and just keep a reminder for the renewal in 24 months. 2 We periodically check due renewals and start new process instances whenever necessary. We typically don't model such processes in one diagram it's shown here as a way to show the message flow. Typically, we would rather use a separate diagram per executable process and either leave out the other process completely or show it as a collapsed pool. Also try to avoid modeling the complete life-cycle of very long living objects, like a life insurance contract. Only capture the active phases as separate processes (e.g. "Policy Issuing", "Address Change", "Cancellation" or "Death"). Having said this, we want to emphasize that the engine is perfectly fine with handling lots of process instances for a long time. So if you want to have process instances waiting for months or years, you can still do so. Just make sure you think about all resulting implications. ### Using call activities to influence versioning behaviour of pieces When calling separately modeled subprocesses (i.e. _Call Activities_), the default behavior of the process engine is to call the _latest_ deployed version of that subprocess. You can change this default 'binding' behavior to call a _specific_ version or the version which was _deployed_ together with the parent process. Keeping in mind pros and cons of versioning as discussed above, we can therefore _encapsulate parts of a process_, for which we want to be able to change the runtime behavior more often into such call activities. This is an especially useful consideration for _long-running processes_. 1 We could decide that we always want to follow the _latest_ shipping process changes, even if the rules for shipping changed while we are in the order acceptance phase. We for example reason that this acceptance phase could sometimes take a long time, because the procurement for goods currently not shelved happens within that phase. 2 Contrary to that, we could decide that the order billing always happens according to the rules valid at the moment we received the order and instantiated the parent process (_deployment_). We for example reason here that it is critical that the billing follows the rules communicated to the customer together with the offer. --- ## Chatbot Use the Camunda bot in personal chats, group chats, and channels to run commands or interact through buttons on interactive cards. ## Commands You can interact with the bot by typing commands in chat. | Command | What it does | | :------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------- | | **Hi** (or hey, hello, greetings, and similar messages) | The bot greets you. If you have not connected your Camunda account yet, it shows you how to get started. | | **Help** (or ?, support, commands) | Shows a help card with available commands and setup information. | | **Switch context** | Changes your active organization and cluster. | | **Start a process** | Walks you through selecting and starting a Camunda process. If no cluster is selected, it prompts you to choose one. | :::important If you type an unrecognized command, the bot notifies you and suggests using **Help**. ::: Configure notification subscriptions on the [notification rules](./ms-teams-notifications.md) page. ## Interactive cards In addition to commands, the bot sends interactive cards with buttons you can use to perform actions directly in chat. :::note When you trigger actions through the bot, such as starting processes or completing tasks, it automatically adds an [`appContext`](./ms-teams.md#process-variable-appcontext) variable. This variable captures who triggered the action and from which surface (`"message"` or `"channel"`). ::: ### Select an organization and cluster Select your organization from a dropdown, then choose a cluster. The bot remembers your selection for future interactions. ### Start processes Select a process definition and complete the start form or provide variables. The bot confirms when the process starts successfully or shows an error. ### Work with tasks | Action | Description | | :---------------- | :------------------------------------------------------------------------------------------------------------------- | | **Assign to me** | Claim a task. If you do this from a channel, the bot also sends you a personal copy so you can work on it privately. | | **Unassign** | Release a task so others can pick it up. | | **Fill in form** | Open the task completion form directly in the chat card. | | **Complete task** | Submit the form and mark the task as done. | | **Reset form** | Discard your form input and return to the task overview. | ### Manage clusters If your cluster is sleeping, the bot shows a **Wake up cluster** button. ### Use pop-up dialogs The bot opens forms in a pop-up dialog when they: - Contain unsupported element types - Use FEEL expressions (values starting with `=`) in supported properties #### Unsupported element types Adaptive Cards support only a subset of Camunda form elements. Forms that include any of the following elements open in a pop-up dialog: - Group - Table - Dynamic list - iframe - HTML viewer - Button - Expression field - Document preview #### FEEL expressions Forms also open in a pop-up dialog if any field uses a FEEL expression in one of the following properties: - **Label** - **Date label** - **Read only** - **Default value** - **Text** - **Source** - **Alt text** - **Accept** - **Multiple** :::note Static values and process variables are supported in Adaptive Cards. Dynamic FEEL expressions are not. ::: ## Notifications The bot sends notifications based on your [notification rules](./ms-teams-notifications.md). ### Personal notifications Receive a message in your personal chat when a user task matches one of your notification rules. For example, when a task is assigned to you. ### Channel notifications A channel receives notifications for user tasks that match its configured rules. This allows your team to coordinate who picks them up. To configure notifications, see [notification rules](./ms-teams-notifications.md). ### Notification behavior Notification cards are interactive. You can assign, complete, or manage tasks directly from the card. Cards update automatically to reflect the latest task state. For example, when someone else completes or assigns the task. ## Error handling The bot provides clear feedback when something goes wrong: | Error | Behavior | | :---------------------- | :------------------------------------------------------------------- | | **Cluster is sleeping** | Shows a card with a button to wake it up, then retry your action. | | **Task not found** | Indicates the task no longer exists. | | **Access denied** | Informs you that you don't have permission for the requested action. | | **Unexpected errors** | Shows a message suggesting you retry or contact support. | --- ## Install Camunda for Microsoft Teams Install and configure Camunda for Microsoft Teams in a Self-Managed environment using Docker. :::note No installation is needed for SaaS environments. The Camunda app is already available in the Microsoft Teams app store and can be used by all users with a Camunda SaaS subscription. See the [Get started](./ms-teams.md#get-started) section for details. ::: ## About Camunda for Microsoft Teams requires a backend service called **App Integrations** to connect Microsoft Teams to your Camunda Self-Managed distribution. This guide walks you through setting up the backend, registering the Teams app, and configuring the connection. ## Prerequisites Before you begin, ensure the following are available: - A running Camunda Self-Managed distribution (for example, `camunda.your-domain.com`) with Identity (Keycloak or Microsoft Entra). - Docker installed on the system hosting the App Integrations backend. - A PostgreSQL database accessible from the Docker container. - Node.js 20 or later for the Teams app integration CLI. - Microsoft Teams with admin permissions to add apps. - A DNS name for the App Integrations backend (for example, `app-integrations.camunda.your-domain.com`). ## Step 1: Create applications in Camunda Identity Before writing the configuration file, register two OAuth 2.0 applications in your identity provider. 1. Access the Identity management in your Camunda Self-Managed distribution. 2. Create the following two applications. ### App Integrations M2M (Machine-to-Machine) | Field | Value | | :------------- | :--------------------------- | | Type | `m2m` | | Access to APIs | Orchestration API (`read:*`) | Note down the generated `clientId` and `clientSecret`. You will need them for `auth.m2m` in the configuration file. ### App Integrations SPA (Single Page Application) | Field | Value | | :------------ | :---------------------------- | | Type | `Confidential` | | Redirect URIs | `https:///*` | Note down the generated `clientId` and `clientSecret`. You will need them for `auth.spa` in the configuration file. ### Grant offline access role In your Keycloak admin console, ensure that users who will use the Teams integration have the `offline_access` role assigned. This is required so the application can refresh tokens and act on behalf of users when sending proactive notifications. You can assign `offline_access` at the realm level (**Realm Roles** → `offline_access`) or via a group/client scope, depending on your setup. For Entra-based setups, offline/refresh token access is configured via the App Registration's API permissions in the Azure portal. You must ensure the SPA App Registration has the `offline_access` permission granted under **API permissions** → **Microsoft Graph** → **Delegated permissions**. ## Step 2: Set up the Microsoft Teams Camunda App using CLI The `@camunda/teams-app-integration-cli` (command: `c8teams`) automates the creation, build, and deployment of the Teams app package, as well as generating the `teams` section of the backend configuration. ### Install the CLI ```bash npm install -g @camunda/teams-app-integration-cli ``` ### Create a new Teams app project ```bash c8teams create my-teams-app ``` The CLI prompts you for: - A project/package name. - The application display name. - Your App Integrations backend URL. - Whether to create a new Teams app and Entra (Azure AD) app or use existing ones. ### Build and deploy ```bash cd my-teams-app pnpm install c8teams build ``` The `build` command compiles the app package from the template and deploys it. You can also deploy separately as follows: ```bash c8teams deploy ``` This provisions the app in your Microsoft Teams tenant and publishes it to the Teams Admin Portal for approval. After a successful deployment, the CLI automatically prints the `teams` configuration snippet (including `clientId`, `appId`, `appPassword`, `tenantId`, and `tabEndpoint`) ready to paste into your `config.yaml`. Save this output for [Step 4](#step-4-create-the-configuration-file). :::tip If you need to retrieve the configuration snippet again later, run: ```bash c8teams show-config ``` ::: ## Step 3: Configure the App Integrations exporter The App Integrations backend requires a Zeebe exporter to be configured in your Camunda orchestration cluster. This exporter streams process data to the App Integrations backend. Add the following configuration to your orchestration cluster Helm chart values: ```yaml orchestration: exporters: appIntegrations: apiKey: secret: existingSecret: app-integrations-secret existingSecretKey: apiKey extraConfiguration: - file: application.app-int.yaml content: | zeebe: broker: exporters: appIntegrations: className: "io.camunda.exporter.appint.AppIntegrationsExporter" args: url: /api/event/exporter/batch ``` - Replace `` with the base URL of your App Integrations backend. The URL must point to the `/api/event/exporter/batch` endpoint (for example, `https://app-integrations.camunda.your-domain.com/api/event/exporter/batch`). - The `args.url` must include the full path `/api/event/exporter/batch`. This is the batch event ingestion endpoint of the App Integrations backend. The exporter will POST batches of process events to this endpoint. - The `existingSecret` references a Kubernetes Secret containing the API key. Create this secret in your cluster before deploying: ```bash kubectl create secret generic app-integrations-secret --from-literal=apiKey= ``` :::note The same API key must be set in the `exporter.apiKey` field of the App Integrations [configuration file](#step-4-create-the-configuration-file) so that the backend can authenticate with the exporter endpoint. ::: ## Step 4: Create the configuration file Create a `config.yaml` file with your configuration settings. This file will be mounted into the Docker container. ### Auth configuration The `auth` block must be configured to match the identity provider used in your environment. The `auth.kind` field selects the provider variant, which determines how OIDC discovery, token endpoints, and audience/issuer values are resolved. Set `auth.kind` to `"keycloak"` for Keycloak-based Camunda Self-Managed installations. | Field | Required | Description | | :---------------------- | :------- | :----------------------------------------------------------------------------------------------- | | `auth.kind` | Yes | Set to `keycloak`. | | `auth.issuer` | Yes | The Keycloak realm URL. For example, `https:///auth/realms/camunda-platform`. | | `auth.audience` | Yes | Typically `camunda-platform`. | | `auth.m2m.clientId` | Yes | M2M client ID from [Step 1](#step-1-create-applications-in-camunda-identity). | | `auth.m2m.clientSecret` | Yes | M2M client secret from [Step 1](#step-1-create-applications-in-camunda-identity). | | `auth.spa.clientId` | Yes | SPA client ID from [Step 1](#step-1-create-applications-in-camunda-identity). | | `auth.spa.clientSecret` | Yes | SPA client secret from [Step 1](#step-1-create-applications-in-camunda-identity). | Example: ```yaml auth: kind: keycloak m2m: clientId: clientSecret: spa: clientId: clientSecret: issuer: https:///auth/realms/camunda-platform audience: camunda-platform ``` Set `auth.kind` to `"entra"` for Microsoft Entra ID (formerly Azure AD) based setups. | Field | Required | Description | | :------------------------- | :------- | :----------------------------------------------------------------------------------------------------------------------- | | `auth.kind` | Yes | Set to `entra`. | | `auth.issuer` | No | Derived automatically as `https://login.microsoftonline.com//v2.0`. Override only if using a custom authority. | | `auth.audience` | No | Defaults to `auth.spa.clientId`. Override only if using a different audience. | | `auth.m2m.clientId` | Yes | Entra App Registration client ID for the M2M application. | | `auth.m2m.clientSecret` | Yes | Entra App Registration client secret for the M2M application. | | `auth.spa.clientId` | Yes | Entra App Registration client ID for the SPA application. | | `auth.spa.clientSecret` | Yes | Entra App Registration client secret for the SPA application. | | `auth.entra.tenantId` | Yes | The Entra tenant ID. | | `auth.entra.tokenUrl` | No | Defaults to `https://login.microsoftonline.com//oauth2/v2.0/token`. | | `auth.entra.jwksUrl` | No | Defaults to `https://login.microsoftonline.com//discovery/v2.0/keys`. | | `auth.entra.scope` | No | Defaults to `openid profile offline_access /.default`. | | `auth.entra.usernameClaim` | No | The ID token claim used to resolve the user's email. Default: `preferred_username`. | Example: ```yaml auth: kind: entra m2m: clientId: clientSecret: spa: clientId: clientSecret: entra: tenantId: ``` :::note Most parameters in the `auth.entra` block are automatically inferred from the required `tenantId` parameter. You only need to override them if your environment uses non-standard Entra endpoints. ::: ### Secret management The configuration file supports referencing environment variables for any value. :::note This is strongly recommended for sensitive fields such as passwords, client secrets, and encryption keys. ::: Instead of hardcoding a secret in `config.yaml`, use the `${{ ENV_VAR_NAME }}` syntax: ```yaml auth: m2m: clientId: ${{ AUTH_M2M_CLIENT_ID }} clientSecret: ${{ AUTH_M2M_CLIENT_SECRET }} db: password: ${{ DB_PASSWORD }} encryptionKey: ${{ DB_ENCRYPTION_KEY }} teams: appPassword: ${{ TEAMS_APP_PASSWORD }} session: secret: ${{ SESSION_SECRET }} ``` You can also use the shorthand `$ENV_VAR_NAME` without braces. In your deployment, mount the secrets as environment variables on the container (for example, via Kubernetes Secrets, Docker `--env-file`, or your CI/CD pipeline's secret store) rather than storing them in plain text in the config file. ### Example configuration file Replace the placeholder values with your actual settings. - Use the credentials from [Step 1](#step-1-create-applications-in-camunda-identity) and the Teams configuration from [Step 2](#step-2-set-up-the-microsoft-teams-app-cli). - The `exporter.apiKey` must match the API key configured in the Orchestration Cluster Helm chart in [Step 3](#step-3-configure-the-app-integrations-exporter). - For production deployments, replace sensitive values with environment variable references as described in [Secret management](#secret-management). - See [Auth configuration](#auth-configuration) for the full `auth` block reference. ```yaml serverPort: 8080 stage: prod # See "Auth configuration" above for Keycloak and Entra variants. auth: kind: keycloak m2m: clientId: clientSecret: spa: clientId: clientSecret: issuer: https:///auth/realms/camunda-platform audience: camunda-platform db: username: password: database: host: loginType: password encryptionKey: "" # Paste the output of `c8teams show-config` here: teams: clientId: appId: appPassword: tenantId: tabEndpoint: https:///ms-teams-app session: secure: true secret: frontendUrl: https:// backendUrl: https:// flavor: self-managed organisation: name: clusters: - uuid: name: urls: orchestration: https:///orchestration tasklist: https:///tasklist # Extended format (object with base and task): # tasklist: # base: https:///tasklist # task: https:///tasklist/tasks/:userTaskKey/view operate: https:///operate exporter: apiKey: subscriptions: {} ``` :::note The `urls.tasklist` field supports two formats: - **Simple (legacy) format** — a plain URL string (for example, `https:///tasklist`). Deep links to tasks fall back to `{tasklist-url}/tasklist/{userTaskKey}`. - **Extended format** — an object with `base` and `task` fields. The `task` field is a URL template containing a `:userTaskKey` placeholder (for example, `https:///tasklist/tasks/:userTaskKey/view`). When the app generates deep links to tasks (for example, in Teams notification cards), it replaces `:userTaskKey` with the actual task key. This allows customization of the task URL pattern for environments where the default path does not match. ::: ### Stage values The `stage` field controls the environment label. | Value | Description | | :------ | :---------------------------------------------- | | `prod` | Production environment (recommended default). | | `int` | Integration / staging environment. | | `dev` | Development environment. | | `local` | Local development (not for deployed instances). | Use `prod` for production deployment. ## Step 5: Start the application ### Configure your PostgreSQL connection Ensure your `config.yaml` has the correct PostgreSQL database settings. The `host` should be the hostname or IP address of your PostgreSQL server as reachable from within the Docker container. ### Configure your Camunda Self-Managed host Update your `config.yaml` to point to your Camunda Self-Managed distribution: - Set `auth.kind` and the corresponding auth fields for your identity provider (see [Auth configuration](#auth-configuration)). - Set each entry in `clusters[].urls` to the correct Camunda service URLs. - Set `frontendUrl` and `backendUrl` to your public deployment URL. ### Start the backend Run the App Integrations backend container with the configuration file mounted: ```bash docker run -d \ --name app-integrations \ -p 8080:8080 \ -e CONFIG=config/app-integrations.yaml \ -e NODE_ENV=production \ -v ./config.yaml:/app/apps/backend/config/app-integrations.yaml \ --restart unless-stopped \ camunda/app-integrations:SNAPSHOT ``` :::note - **`CONFIG` environment variable**: Must be set to `config/app-integrations.yaml` to point to the mounted configuration file. - **`NODE_ENV` environment variable**: Set to `production` for deployed environments. - **Volume mount**: The `config.yaml` file is mounted to `/app/apps/backend/config/app-integrations.yaml` inside the container. ::: ## Configuration reference Below is a reference of each section in the `config.yaml` file. | Section | Field | Description | | :---------------- | :---------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | | `serverPort` | — | The port the backend server listens on inside the container (default: `8080`). | | `stage` | — | Environment stage: `local`, `dev`, `int`, or `prod`. | | **auth** | | Authentication configuration for connecting to your Camunda platform. See [Auth configuration](#auth-configuration). | | | `kind` | Identity provider variant: `keycloak` (default) or `entra`. Determines how OIDC endpoints are resolved. | | | `m2m.clientId` / `m2m.clientSecret` | Machine-to-machine OAuth2 credentials for backend services. | | | `spa.clientId` / `spa.clientSecret` | Single Page Application OAuth2 credentials for frontend. | | | `issuer` | The OAuth2/OIDC issuer URL. Required for Keycloak; auto-derived for Entra. | | | `audience` | The OAuth2 audience. Required for Keycloak (typically `camunda-platform`); defaults to `spa.clientId` for Entra. | | | `entra.tenantId` | _(Entra only, required)_ The Entra tenant ID. | | | `entra.tokenUrl` | _(Entra only, optional)_ Token endpoint. Defaults to `https://login.microsoftonline.com//oauth2/v2.0/token`. | | | `entra.jwksUrl` | _(Entra only, optional)_ JWKS endpoint. Defaults to `https://login.microsoftonline.com//discovery/v2.0/keys`. | | | `entra.scope` | _(Entra only, optional)_ OAuth2 scope. Defaults to `openid profile offline_access /.default`. | | | `entra.usernameClaim` | _(Entra only, optional)_ ID token claim for the user's email. Default: `preferred_username`. | | **db** | | PostgreSQL database connection settings. | | | `username` | Database username. | | | `password` | Database password. | | | `database` | Database name. | | | `host` | Database hostname (as reachable from the container). | | | `loginType` | Authentication type (`password` for username/password auth). | | | `encryptionKey` | 32-character key used for encrypting sensitive data. | | **teams** | | Microsoft Teams integration settings. | | | `clientId` | Azure AD app client ID. | | | `appId` | Teams app ID. | | | `appPassword` | Azure AD app password (client secret). | | | `tenantId` | Azure AD tenant ID. | | | `tabEndpoint` | Public URL endpoint for the Teams tab. | | | `multitenant` | Enable multi-tenant mode (default: `true`). See [note on multitenant](#notes). | | | `serviceUrl` | Bot Framework service URL (default: `https://smba.trafficmanager.net/teams`). See [note on serviceUrl](#notes). | | **session** | | Session management configuration. | | | `secure` | Set to `true` for HTTPS environments. | | | `secret` | A random secret string for signing session cookies. | | **frontendUrl** | — | Public URL where the frontend is accessible. | | **backendUrl** | — | Public URL where the backend is accessible. | | **flavor** | — | Deployment flavor (must be `self-managed`). | | **organisation** | `name` | Display name for your organization. | | **clusters** | — | Array of Camunda cluster configurations. | | | `uuid` | Unique identifier for the cluster. | | | `name` | Display name for the cluster. | | | `urls.orchestration` | Zeebe/Orchestration API URL. | | | `urls.tasklist` | Tasklist URL (string) or object with `base` and `task`. See note below. | | | `urls.tasklist.base` | _(Object format only)_ Root Tasklist URL. | | | `urls.tasklist.task` | _(Object format only)_ URL template for deep-linking to a specific task. Must contain `:userTaskKey` placeholder. | | | `urls.operate` | Operate URL. | | | `exporter.apiKey` | API key for the exporter. Must match the key configured in the [orchestration cluster Helm chart](#step-3-configure-the-app-integrations-exporter). | | **subscriptions** | — | Subscription configuration (empty object `{}` for Self-Managed). | ### Notes - **`teams.multitenant`:** Multi-tenant support is no longer available for newly created Azure Bot registrations. Only set `multitenant` to `true` if you have an existing Teams application that was already registered with multi-tenant support enabled. For all new installations, leave this at the default (the CLI creates single-tenant apps) or explicitly set it to `false`. - **`teams.serviceUrl`:** The default value (`https://smba.trafficmanager.net/teams`) works for most deployments. Only override it if your environment requires a different Bot Framework service endpoint. If you are unsure, keep the default value. --- ## Notification rules Notification rules let you control which user tasks trigger notifications in Microsoft Teams. Each rule applies to a specific organization and cluster and can filter user task events by process definition, user task elements, candidate users, or candidate groups. ## Channel vs. personal rules Where you create a rule determines who receives notifications. | Context | Who receives notifications | Typical use | | :------------ | :-------------------------------------- | :------------------------------------------------------------------------------------------- | | Channel | Everyone in the channel | Coordinate on new unassigned user tasks so a team can decide who picks them up. | | Personal chat | The user who owns the chat with the bot | Get notified when a task is assigned to you, or when a task you can claim becomes available. | :::note To configure personal notification rules, you must first start a chat with the Camunda bot in Microsoft Teams. Until that chat exists, the **Settings** tab shows a "Personal notifications not set up" message. ::: ## Access the Notification rules page You manage notification rules from the Notification rules page in the Camunda for Microsoft Teams app. ### In a personal chat 1. Open the Camunda app in Microsoft Teams. 2. Select the **Settings** tab. ### In a channel 1. Open the channel where you want to receive notifications. 2. Add the **Camunda** tab (**+** at the top of the channel > select **Camunda**). 3. Open the tab. ## Rule structure A rule triggers a notification only when a user task matches all configured filters. | Field | Required | Description | | :----------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Organization | Yes | The Camunda organization the rule applies to. Auto-selected if you only have access to one. | | Cluster | Yes | The cluster within the organization. Auto-selected if only one cluster is available. | | Process definition | No | Limit the rule to user tasks from a single process. Leave empty (**All processes (no filter)**) to match user tasks from every process in the cluster. | | User tasks | No | One or more specific user task elements within the selected process. Pick them visually on the BPMN diagram. Only available after you select a process. Leave empty to match all user tasks. | | Candidate users | No | Comma-separated list of user identifiers. Matches tasks assigned to any of these users. | | Candidate groups | No | Comma-separated list of group identifiers. Matches tasks assigned to any of these groups. | ### Match semantics - Empty filters match all user tasks in the selected cluster. A rule with no filters matches every user task in the selected cluster — the broadest possible subscription. - Adding filters narrows the match. Filters combine with AND across fields and OR within each list. For example, a rule with `candidateGroups = "finance, hr"` and a selected process matches tasks from that process that have either `finance` or `hr` as a candidate group. - Multiple matching rules deduplicate. If several rules match the same user task event, the recipient still receives only one notification per event. :::tip Start broad and narrow down. If you are not sure which filters you need, create a rule with no filters first, observe the notifications you receive, and then refine. ::: ## Create a rule 1. Open the [notification rules page](#access-the-notification-rules-page). 2. Click **Create rule** in the page header. 3. Select an **organization** and **cluster** if they are not pre-selected. 4. (Optional) Choose a **process definition**. To target specific tasks, select them on the BPMN diagram. 5. (Optional) Provide candidate users or candidate groups as comma-separated lists. 6. Click **Save**. The new rule appears in the list and starts matching new user task events immediately. ## View existing rules The Notification rules page lists all rules configured for the current channel or personal chat. Each row shows: | Column | Description | | :------------ | :------------------------------------------------------------------------------------------------------------------------------------ | | Org / Cluster | The organization and cluster the rule applies to. Hidden when only one organization and cluster is available. | | Process | The process definition ID, or all processes when no process filter is set. | | User task | The selected user task element IDs, or **—** when no specific task is selected. | | Assignments | The combined list of candidate users and groups. Long lists are truncated with **+N more**. Hover over the cell to see the full list. | Click a row to open the rule for editing. ## Edit a rule 1. On the Notification rules page, click the row of the rule you want to edit. 2. Change any of the fields. 3. Click **Save**. ## Delete a rule 1. Open the rule for editing (click its row). 2. Click **Delete rule** in the page header. 3. Confirm the deletion in the dialog. This action cannot be undone. ## What happens when a task matches When a user task matches a rule, the bot posts an interactive notification card to the channel or personal chat. From the card, you can claim, assign, complete, or open the task without leaving Microsoft Teams. Cards update automatically as the task state changes. See [notification behavior](./ms-teams-chatbot.md#notification-behavior) for the full description of how notification cards behave. ## Limits and permissions - There is currently no limit on the number of rules per channel, personal chat, or cluster. - In a channel, any member of the channel can create, edit, and delete notification rules. --- ## Tabs Browse tasks, start processes, and monitor incidents using embedded tabs in Microsoft Teams. ## About Camunda for Microsoft Teams adds visual tabs to your Teams sidebar, providing a rich interface for interacting with your Camunda clusters. Each tab focuses on a specific area of process management. ## Tasks With the **Tasks** tab, you can browse and manage user tasks from your Camunda clusters. - **Filter tasks** by category: My tasks, All open tasks, Unclaimed tasks, or Completed tasks. - **Sort and search** tasks using additional filter controls (assignee, candidate group, process, dates, priority). - **Switch between views**: Card view or list view. - **View task details** including the process it belongs to, priority, due date, and creation time. - **Assign a task** to yourself and **complete it**, either by filling in a Camunda form or by providing data manually. - **Navigate pages** in large task lists or load more items on mobile. :::note Tasks update automatically as you complete work in Camunda or Microsoft Teams. ::: :::note When a task is completed from the tab, an [`appContext`](./ms-teams.md#process-variable-appcontext) variable is automatically included in the task variables with `source` set to `"tab"`. ::: ## Processes With the **Processes** tab, you can view and start Camunda process definitions. - Browse all available process definitions displayed as cards. - Select a process and choose a specific version. - Fill in a start form (if the process has one) or provide variables manually, then start a new process instance. - After starting, get a direct link to view the running instance in [Operate](/components/operate/operate-introduction.md). To start a process: 1. Select the **Processes** tab. 2. Select a process definition. 3. Complete any required fields. 4. Click **Start process**. :::note When a process is started from the tab, an [`appContext`](./ms-teams.md#process-variable-appcontext) variable is automatically included in the process variables with `source` set to `"tab"`. ::: ## Incidents With the **Incidents** tab, you can monitor incidents that occurred during process execution. - See a list of active incidents with error type, description, and timestamp. - Retry a failed incident directly from Teams. - Copy a link to the incident or open it in [Operate](/components/operate/operate-introduction.md) for deeper investigation. ## Onboarding When you first open the app, the **Onboarding** page guides you through connecting your Teams account to your Camunda account via single sign-on. If you don't have a Camunda account yet, you can sign up from this page. ## Cluster status The **Cluster Status** page displays the health of your currently selected cluster. If the cluster is suspended (sleeping), you can wake it up directly from this page. --- ## Troubleshoot Camunda for Microsoft Teams Troubleshoot Camunda for Microsoft Teams to fix common setup and connectivity issues. ### The app does not appear in the Microsoft Teams store - Verify your organization allows third-party app installations. - Search for "Camunda" in the Microsoft Teams app store. - Check with your Teams administrator for app approval policies. - The app is provisioned during [installation](./ms-teams-installation.md) and appears in the **Built for your org** section of Microsoft Teams Apps. - If the app is not visible, check with your IT administrator. ### Unable to connect to Camunda organization - Ensure you have the required permissions in your Camunda organization. - Verify your Camunda SaaS account is active and accessible. - If no tasks or incidents are visible, double-check your Camunda organization, cluster, and tenant settings. - Verify your Camunda Self-Managed distribution is running and accessible. - Check your Identity configuration and ensure the user has the required roles. - If no tasks or incidents are visible, double-check your cluster configuration in the `config.yaml` file. ### Tasks not displayed - Check you are connected to the correct Camunda cluster. - If notifications are not shown, ensure Microsoft Teams notifications are enabled and verify that the relevant [notification rules](./ms-teams-notifications.md) are configured for the channel or personal chat. - This could be due to an expired Camunda session or missing permissions. Sign out and sign in again. ## Get help - Contact [Camunda support](/reference/contact.md) for assistance. - Provide feedback through the [Camunda roadmap portal](https://roadmap.camunda.com). --- ## Camunda for Microsoft Teams Bring Camunda's business process management capabilities directly into Microsoft Teams. :::important The Microsoft Teams integration is released as an [early access](/components/early-access/overview.md) alpha feature to allow you to test and participate in development by sharing feedback before general availability, and is subject to alpha feature limitations. ::: ## About With **Camunda for Microsoft Teams**, you can manage processes, complete tasks, monitor incidents, and receive notifications without leaving your collaboration environment. The app offers two ways to interact with Camunda: | Interaction mode | Description | Capabilities | | :------------------------------- | :------------------------------------------------------------------------------- | :----------------------------------------------------------------------- | | [Chatbot](./ms-teams-chatbot.md) | Conversational assistant available in personal chats, group chats, and channels. | Respond to text commands and interactive card buttons for quick actions. | | [Tabs](./ms-teams-tabs.md) | Visual pages embedded inside Microsoft Teams. | Browse tasks, start processes, view incidents in a rich interface. | You sign in using your organization's Microsoft account, and then link it to your Camunda account during a one-time onboarding step. ### Key features | Feature | Description | | :------------------------------------------- | :-------------------------------------------------------------------------------------------------- | | Task management | View, claim, assign, and complete user tasks directly in Microsoft Teams. | | Start processes | Browse process definitions and start new instances from Microsoft Teams. | | Incident monitoring | View incidents, retry failed jobs, and open them in Operate. | | [Notifications](./ms-teams-notifications.md) | Receive personal and channel notifications for user tasks based on configurable notification rules. | | Cluster management | View cluster health and wake up suspended clusters. | ## Process variable: `appContext` When a process is started or a user task is completed through Microsoft Teams, the integration automatically injects an `appContext` variable into the process variables. This allows downstream BPMN processes to know _where_ and _how_ they were triggered. The `appContext` variable has the following shape: | Field | Type | Description | | :--------------- | :------------------------------------ | :----------------------------------------------------------------------------------------- | | `integration` | `"teams"` | The platform that initiated the action. | | `externalUserId` | `string` | The Microsoft Teams user ID of the person who triggered the action. | | `email` | `string` | The Camunda account email associated with the user. | | `source` | `"tab"` \| `"message"` \| `"channel"` | The UI surface that triggered the action (see below). | | `channel` | `string` (optional) | The Microsoft Teams channel or conversation ID. Present only when `source` is `"channel"`. | ### Source values | Value | Meaning | | :---------- | :------------------------------------------------------------------------------------------------------------ | | `"tab"` | Action was triggered from the Teams tab interface, including forms opened in a pop-up dialog from a bot card. | | `"message"` | Action was triggered from a bot conversation in a personal or group chat. | | `"channel"` | Action was triggered from a channel — either through a bot card posted in the channel or a channel command. | :::tip You can use `appContext` in your BPMN processes to implement conditional logic based on where an action originated. For example, you could route a process differently depending on whether it was started from a tab or a channel command. ::: ## Get started ### Prerequisites | Prerequisite | Description | | :------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Camunda 8 SaaS account | You must have a valid working Camunda 8 SaaS account. | | Microsoft Teams | Microsoft Teams with permissions to add apps from the Microsoft Store. Microsoft Teams administrators can manage app permissions and availability across the organization. | | Camunda organization and cluster | Access to a Camunda organization and cluster. | | Prerequisite | Description | | :---------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Camunda 8 Self-Managed installation | A running and accessible Camunda 8 Self-Managed installation. | | Camunda for Microsoft Teams app | The Camunda for Microsoft Teams app must be [installed](./ms-teams-installation.md) and added to your Microsoft tenant by your organization's administrator. | ### Install the app in Microsoft Teams :::info No separate installation is required for SaaS environments. The Camunda app is available in the Microsoft Teams app store for all users with a Camunda SaaS subscription. ::: 1. Open **Microsoft Teams**. 2. Open **Apps** and search for **Camunda**. 3. Click **Add** to install the app. :::note If your organization manages Teams apps centrally, contact your Teams administrator for access. ::: :::info The Self-Managed setup first requires installation and configuration by your organization's administrator. See [Install](./ms-teams-installation.md) for more details. ::: 1. Open **Microsoft Teams**. 2. Open **Apps** and search for **Camunda** in the **Built for your org** section. :::note Your administrator may have renamed the app. Contact them if you cannot find it. ::: 3. Click **Add** to install the app. ### Sign in and authorize After installing the app, open Camunda for Microsoft Teams in Microsoft Teams. 1. When prompted, sign in using your **Camunda account credentials**. 2. If requested, grant the necessary permissions to allow Microsoft Teams to access your Camunda workspace. ### Switch environment Once you are signed in, select the organization and cluster you want to work in to see the relevant tasks, processes, and incidents. Use the **Switch environment** button at the top of the app to change your active organization and cluster at any time. ## Explore further resources --- ## Overview(Camunda-integrations) Extend the power of your process orchestration by connecting Camunda with your core enterprise systems. Our pre-built integrations simplify communication across platforms and enable true end-to-end automation in your technology landscape. The following integrations are available out of the box: - [ServiceNow integration](#servicenow-integration) - [SAP integration](#sap-integration) ## ServiceNow integration Camunda’s ServiceNow integration bridges your business processes with IT service management (ITSM) to streamline and automate service delivery. With this integration, you can: - **Manage ServiceNow data** Create, read, update, and delete records in any ServiceNow table directly from a Camunda process. - **Trigger ServiceNow flows** Initiate automations built in ServiceNow's Flow Designer as part of an end-to-end process. :::note Use this integration when you need to orchestrate processes that involve ITSM tasks or require seamless collaboration between ServiceNow and other business systems. ::: ## SAP integration Camunda’s SAP integration allows you to include SAP S/4HANA, ECC, Advanced Event Mesh (AEM) and Business Technology Platform (BTP) functionality in your orchestrated processes. With this integration, you can: - **Execute core SAP functions** Call BAPIs and Remote Function Modules (RFCs) in SAP S/4HANA or ECC directly from Camunda. - **Extend event driven architecture** Receive CloudEvents from SAP Advanced Event Mesh (AEM) and publish CloudEvents to AEM. - **Connect to SAP BTP** Integrate with services and applications on the SAP Business Technology Platform to build comprehensive, cross-platform workflows. :::note This integration is ideal for organizations standardizing on SAP systems while extending automation to non-SAP applications and services. ::: --- ## SAP BTP plugin The [Camunda SAP Business Technology Platform (BTP) plugin](/reference/glossary.md#btp) is an artifact run on BTP. It consists of a [UI5 app](https://ui5.sap.com/), a [CAP service layer and backend](https://cap.cloud.sap/) (using PostgreSQL), and an [approuter](https://www.npmjs.com/package/@sap/approuter) for traffic dispatching. The BTP plugin connects to Camunda 8 SaaS to provide: - A generic Fiori app for starting BPMN processes and displaying [Camunda Forms](/components/modeler/forms/camunda-forms-reference.md) in the Fiori design language. - A generic endpoint to start BPMN processes with. ## Prerequisites - **Camunda API Client**: [Create an API client](/components/hub/organization/manage-clusters/manage-api-clients.md) for your Camunda SaaS cluster with the full scope: `Zeebe,Tasklist,Operate,Optimize,Secrets` - Locally, for configuring via `csap` only (see below): [Node.js >= 20 LTS](https://nodejs.org/en/about/previous-releases) - **On SAP BTP**: - [Cloud Foundry CLI](https://github.com/cloudfoundry/cli) with the [multiapps plugin](https://github.com/cloudfoundry/multiapps-cli-plugin) installed on the machine executing the deployment. - SAP BTP subaccount with a [Cloud Foundry environment](https://discovery-center.cloud.sap/serviceCatalog/cloud-foundry-runtime?region=all) enabled and a [created space](https://help.sap.com/docs/btp/sap-business-technology-platform/create-spaces). - A minimum of [4 GB storage quota and 4 GB runtime memory](https://help.sap.com/docs/btp/sap-business-technology-platform/managing-space-quota-plans). - **[Entitlements](https://help.sap.com/docs/btp/sap-business-technology-platform/managing-entitlements-and-quotas-using-cockpit)** for: - BTP PostgreSQL, hyperscaler option. Ensure the [available BTP PostgreSQL, hyperscaler option, and configuration options](https://help.sap.com/docs/postgresql-on-sap-btp/postgresql-on-sap-btp-hyperscaler-option/parameters) match your sizing plan. For example, for multi-region databases or high availability, the BTP plugin defaults to a minimum, only specifying PostgreSQL version 16 (see `/core/pg-options.json`) and a single database instance, with no high availability. - [Destination Service](https://discovery-center.cloud.sap/serviceCatalog/destination?service_plan=lite®ion=all&commercialModel=btpea), `lite` plan - [Connectivity Service](https://discovery-center.cloud.sap/serviceCatalog/connectivity-service?region=all), `lite` plan - [Authorization and Trust Management Service](https://discovery-center.cloud.sap/serviceCatalog/authorization-and-trust-management-service?region=all), `application` plan ## Features - Model user tasks in your BPMN process—they will be automatically detected and rendered by the BTP plugin at runtime. - Design your form in the Form Builder as part of the BPMN process. When you model a user task and link it to the form, the BTP plugin will automatically detect and render the task and its associated form at runtime. ![Camunda Forms in Fiori](./img/forms-fiori.png) - Equip the last user task with a custom header `final-user-task` and the value: - `success` to display the last user task on the "happy path". - `fail` to use that user task to communicate a failed process to the user. ![screenshot of header variable in Modeler](./img/sap-btp-plugin-final-user-task-header.png) - Auto-start a process via URL parameter using `run=`. For example, `https:///index.html?channelId=&run=application-process`. - For debugging purposes, add `debug=true` as the URL parameter. For example, `https:///index.html?channelId=&run=application-process&debug=true`. - `/inbound` endpoint for starting a BPMN process in Camunda ## Camunda Forms in SAP Fiori Layout: Only a single-row layout is supported: ![image-20250219112232376](./img/froms-no-columns.png) :::note Custom properties are not supported: ![image-20250219112156011](./img/forms-no-custom-properties.png) ::: ### Supported Form Features and Properties | | Camunda Forms Feature / Property | Supported in Camunda BTP Plugin? | Comments | | ---------------- | -------------------------------- | :------------------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Input** | | | | | | Text field | :white_check_mark: | | | | Text area | :white_check_mark: | | | | Number | :white_check_mark: | | | | Date time | :white_check_mark: | Only UTC values will be stored- Date format `yyyy-MM-dd`, for example `2025-02-29"12`- Hours format will be stored as the string `10:12:34 pm`- 24 hours format will be stored as the string `22:12:34` | | | Expression | :x: | | | | Filepicker | :x: | | | **Selection** | | | | | | Checkbox | :white_check_mark: | | | | Checkbox group | :x: | | | | Radio group | :white_check_mark: | Only `static` options source is supported. | | | Select | :white_check_mark: | Only `static` options source is supported. | | | Tag list | :x: | | | **Presentation** | | | | | | Text view | :white_check_mark: | | | | Image view | :white_check_mark: | | | | Table | :x: | | | | HTML view | :white_check_mark: | | | | Document preview | :x: | | | | Spacer | :x: | | | | Separator | :x: | | | **Containers** | | | | | | Group | :x: | | | | Dynamic list | :x: | | | | iframe | :x: | | | **Action** | | | | | | Button | :x: | | ## Configuration and deployment Use [`csap`](./csap-cli.md) for setting up the BTP plugin, as a manual configuration is cumbersome and error-prone. Within Camunda, no setup/config work is necessary to use the BTP plugin. ### Configuring the BTP plugin using `csap` Either walk yourself through the prompts or provide all information to the CLI: - `csap setup` will guide you interactively. - Assuming your [Camunda cluster's API credentials](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client) are sourced in your shell environment, this will do the configuration for you: ```shell csap setup --for btp-plugin \ --camunda 8.7 \ --deployment SaaS \ --btpRoute camunda-btp-plugin.cfapps.eu10-004.hana.ondemand.com ``` The host name provided as `btpRoute` will be the URL to the BTP plugin's app; following the example above: `https://camunda-btp-plugin.cfapps.eu10-004.hana.ondemand.com`. ### Deploying to BTP 1. Log into the desired SAP BTP subaccount via the [Cloud Foundry `cli`](https://github.com/cloudfoundry/cli) (cf-cli): ```shell $> cf login API endpoint: https://api.cf. ... ... ``` 2. `cd` to the folder `csap` logs after a successful build, for example, `/tmp/camunda/8.6/sap-btp-plugin` 3. Issue `cf deploy mta_archives/*.mtar` - Add the `-f` switch to force an update, for example, by deploying the same version again (`cf deploy mta_archives/*.mtar -f`). - Consider adding `--delete-services` to recreate eventually failed service creation of previous deployment. For example, `cf deploy mta_archives/*.mtar -f --delete-services`. For advanced deployment configuration, consider working with your SAP practice, starting from the created `mta.yaml` deployment descriptor (in the `$TMP` folder as output by `csap`). ## Working with the BTP plugin The BTP plugin provides a guided, one-user multi-page flow where a single user progresses through a sequence of steps to complete a task or workflow. It renders subsets of Camunda Forms, with each page representing a distinct part of the process. After deployment, the BTP plugin is available at the `btpRoute` provided: `https://`. If called manually (for example, in the browser) it will redirect automatically to `/app/index.html?channelId=` . The `` or "channel ID" links the output device to the BTP plugin, representing a dedicated "output channel". ### Starting a BPMN process in the browser Start any Camunda BPMN process manually via the menu bar. ![BTP Plugin UI to start process](./img/sap-btp-plugin-start-process.png) In the popup, enter the ID of the BPMN to run. ![start process in Fiori app](./img/sap-btp-plugin-process-id.png) Alternatively, the process can be auto-started by directly calling the URL: `https:///index.html?channelId=&run=fiori-bupa-search` ### Starting a BPMN process via API Make a `POST` http call to `https:///backend/inbound` with this defined payload: ```json { "bpmnProcessId": "processId", # ex above: fiori-bupa-search "user": "beck@renegade.org", # unique id of the user "wait": false, # or true to wait for the BPMN run to finish and get the result back "variables": { # optional "some_key": "some_value", "some_other_key": 10 } } ``` The advantage over Orchestration Cluster REST API: use the authentication realm between BTP and S/4 / ECC, there is no need for administrating additional credentials. --- ## SAP integration Use Camunda's [SAP](/reference/glossary.md#sap) integration to bring SAP functionality into your orchestrated processes and connect with SAP BTP services. This integration provides modular components that can be used independently or together to automate business-critical scenarios across SAP and non-SAP systems. ## Purpose The SAP integration enables you to: - Extend end-to-end process orchestration into SAP S/4HANA and ECC systems. - Execute SAP-specific functions (such as BAPIs, RFCs, and OData calls) directly within BPMN workflows. - Surface Camunda Tasklist items directly in SAP Fiori through **SAP Task Center integration**, improving visibility and collaboration for business users. - Enable event-driven automation with **SAP Advanced Event Mesh** (upcoming), allowing real-time responsiveness and better integration with SAP event streams. - Provide IT and business teams with a certified, enterprise-grade solution for SAP automation. :::info The Camunda SAP integration is **SAP Certified**, ensuring compliance with SAP's standards for compatibility, security, and performance. This certification provides added assurance for enterprise deployments. ::: ## Audience This documentation is intended for: - **Developers** who implement process models that call SAP functions, services, or APIs. - **Solution architects** who design system landscapes and need to understand integration options. - **Administrators** who deploy and configure SAP integration modules in SAP BTP or hybrid environments. ## Scope of documentation This section of the documentation covers: - [Prerequisites](./prerequisites.md) for running the integration. - **Integration modules**: What each SAP module does and how they fit into Camunda workflows. - **Setup guidance**: How to configure and deploy integration modules using the [CSAP CLI](./csap-cli.md). - **Module-specific documentation**: - [SAP OData outbound connector](./odata-connector.md) - [SAP RFC outbound connector](./rfc-connector.md) - [SAP BTP plugin](./btp-plugin.md) ## About the integration Camunda's SAP integration consists of several modules that can be used independently: | Module | What it does | | :--------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [SAP OData outbound connector](./odata-connector.md) | Interact with an SAP S/4HANA or ECC system via OData v2 + v4 APIs directly from your BPMN model. | | [SAP RFC outbound connector](./rfc-connector.md) | Query Business Application Programming Interfaces (BAPIs) and remote-enabled function modules (RFCs) on SAP ECC systems. | | [SAP BTP plugin](./btp-plugin.md) | - Use [Tasklist's](/components/tasklist/introduction-to-tasklist.md) forms in the Fiori UI. - Start BPMN process instances via inbound proxy endpoints. | These features run within your [SAP BTP instance](https://www.sap.com/products/technology-platform.html), requiring no proprietary Camunda setup and leveraging existing infrastructure with minimal prerequisites. ![SAP integration overview](./img/sap-integration-overview.svg) --- ## CSAP CLI command line utility The [Camunda SAP Integration CLI](/reference/glossary.md#csap-cli) (`csap`) is a command-line tool designed to simplify the setup of Camunda's SAP integration modules. It provides a streamlined process for configuring and building these modules for deployment. ## Features - Distributed as a standalone binary - no local installation required. - Interactive prompts for configuration. - Command-line switches for automation. - Support for multiple SAP integration modules. - Automatic handling of dependencies and build processes. - Compatibility with Camunda SaaS deployments. ## Supported modules The CLI supports the following SAP integration modules: 1. **SAP OData connector**: Facilitates interaction with SAP S/4HANA or ECC systems from a BPMN model. 2. **SAP RFC connector**: Allows querying BAPIs and Remote Function Modules on SAP ECC systems. 3. **BTP plugin**: Enables rendering task forms in Fiori and provides BTP integration. 4. **All modules**: Configures all available modules. ## Installation To use the CLI, download the binary matching your operating system and architecture from the [releases](https://github.com/camunda/sap-csap-cli/releases) section of its repository: 1. Check your build system meets the following requirements: 1. [Node.js](https://nodejs.org/en) >= 20 (this includes the required `npm`) 1. (Windows) tell `npm` to use `cmd` as the shell script executor: `$> npm config set script-shell cmd` 1. The [transient requirements for SAP's `mbt`](https://sap.github.io/cloud-mta-build-tool/makefile/) (Cloud MTA Build Tool), specifically `make` 1. Navigate to the [releases](https://github.com/camunda/sap-csap-cli/releases) page. 1. Download the binary for your platform: - For Linux: `csap-x86_64-unknown-linux-gnu` - For macOS (Intel): `csap-x86_64-apple-darwin` - For macOS (Apple Silicon): `csap-aarch64-apple-darwin` - For Windows: `csap-x86_64-pc-windows-msvc.exe` 1. Place the binary in a directory included in your system's `PATH` for easy access. ### Example for Linux/macOS ```bash chmod +x csap-x86_64-unknown-linux-gnu mv csap-x86_64-unknown-linux-gnu /usr/local/bin/csap ``` ### Example for Windows 1. Rename the binary to `csap.exe` if necessary. 2. Add the directory containing `csap.exe` to your system's `PATH`. ## Usage The CLI provides a `setup` command to prepare one of Camunda's SAP integration modules for deployment. You can run the command interactively or provide all required options as command-line switches. ### Authentication token Under the hood, `csap` uses the GitHub API to query for releases. The [GitHub API has a rate limit](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api) for unauthenticated requests. It is thus advisable to provide a GitHub access token to the environment `csap` is run in. #### Local use A personal GitHub access token can be obtained in multiple ways. Either [statically by generating one](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token) or dynamically by using the `gh` CLI to log in (`gh auth login`), which in turn produces an access token. Then, inject the token into your (shell) environment as the variable `GH_TOKEN`. `csap` will automatically pick up the token from `GH_TOKEN` and use it for subsequent requests. Windows (Command Prompt) ```shell for /f "delims=" %i in ('gh auth token') do set GH_TOKEN=%i ``` Windows (PowerShell) ```shell $env:GH_TOKEN = (gh auth token) ``` Linux/macOS (bash) ```shell export GH_TOKEN=$(gh auth token) ``` #### CI/CD use If your CI/CD environment isn't GitHub, like [local use](#local-use) an access token from GitHub must be obtained to authenticate requests to the GitHub API from `csap`. For GitHub actions/pipelines, all runs are provided a `GITHUB_TOKEN` automatically. Declare this to the respective run of `csap` via the `env` YAML declaration: ```yaml jobs: your-job: # ... steps: - run: | csap setup --for #... env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} ``` Review csap's [GitHub action for pull requests](https://github.com/camunda/sap-csap-cli/blob/ad10ecf8017ab18e2d4fbd2089f7fb5d1d17fa12/.github/workflows/pr.yml#L46) as an example. ### Interactive mode Run the following command to start the interactive setup: ```bash csap setup ``` The CLI will guide you through prompts to collect all required inputs, including the SAP integration module, Camunda version, deployment method, and credentials. ### Command-line options All prompts are also available as command-line switches, allowing you to automate the setup process. Below is the full list of options: #### Command syntax ```bash csap setup [options] ``` #### Options | Option | Type | Description | Default value | | ---------------- | ------ | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | | `--for` | string | Specifies the SAP integration module to set up. Choices: `btp-plugin`, `odata`, `rfc`, `all`. | `odata` | | `--camunda` | string | Specifies the Camunda version. Choices: `8.7`, `8.6`, `8.5`. | `8.7` | | `--deployment` | string | Specifies the Camunda deployment option. Choices: `SaaS`. (`SM` for self managed currently disabled.) | `SaaS` | | `--btpRoute` | string | (For `btp-plugin` or `all`) Specifies the BTP route to reach the plugin. This is SAP/BTP specific. | `camunda-btp-plugin.cfapps.eu10-004.hana.ondemand.com` | | `--clusterId` | string | Specifies the Camunda cluster ID. | (Prompted if not provided) | | `--region` | string | Specifies the Camunda cluster region. | `bru-2` | | `--clientId` | string | Specifies the Camunda API client OAuth2 client ID. | (Prompted if not provided) | | `--clientSecret` | string | Specifies the Camunda API client OAuth2 client secret. | (Prompted if not provided) | | `--to` | string | Target directory for setup artifacts | os-dependent `tmp` directory | ## Environment variables The CLI can detect Camunda API credentials from environment variables. If these variables are set, the CLI will reuse them without prompting for input. | Environment variable | Description | | ------------------------ | ------------------------- | | `CAMUNDA_CLUSTER_ID` | Camunda cluster ID | | `CAMUNDA_CLIENT_ID` | Camunda API client ID | | `CAMUNDA_CLIENT_SECRET` | Camunda API client secret | | `CAMUNDA_CLUSTER_REGION` | Camunda cluster region | ### Examples #### Example 1: Interactive setup ```bash $> csap setup # ... ? SAP integration module (odata) BTP plugin ❯ OData connector RFC connector All modules ``` This will guide you through the setup process interactively. #### Example 2: Automating setup for the BTP plugin ```bash $> csap setup --for btp-plugin \ --camunda 8.7 \ --deployment SaaS \ --btpRoute my-btp-route.cfapps.eu10-004.hana.ondemand.com \ --clusterId 64ecb347-dd50-49c9-ace2-20c9f6b0798d --region syd-2 --clientId dIsfmFEB47_-Dt2uMlYdw-B_72stz.Yh \ --clientSecret WzIQFJkxd2xopI7lOGArJ0815kC3SvU5ke~lI4did8k0RMG353DiVDPBPEW1-tuD7 # ... ``` This command sets up the BTP plugin for Camunda version 8.7 with all required options provided as command-line arguments. #### Example 3: Setting up all modules, reusing credentials from environment ```bash $> csap setup --for all \ --camunda 8.6 \ --deployment SaaS \ # ... i Camunda API credentials found in environment. Reusing ┌────────────────────────┬──────────┐ │ (idx) │ Values │ ├────────────────────────┼──────────┤ │ CAMUNDA_CLUSTER_ID │ "***5ee" │ │ CAMUNDA_CLIENT_ID │ "***icQ" │ │ CAMUNDA_CLIENT_SECRET │ "***XEq" │ │ CAMUNDA_CLUSTER_REGION │ "***d-1" │ └────────────────────────┴──────────┘ ``` This command sets up all available SAP integration modules for Camunda version 8.6. ## Deploying modules After each Camunda SAP integration module is set up with `csap`, it is ready for deployment. We consider `csap` to be the kitchen of the deployment lifecycle: it brings all the ingredients together, cooks the meal, plates it, and has it ready to serve. Bringing the meal from the kitchen to the table, which translates to how to deploy the module to BTP, should be the responsibility of the SAP practice - along with getting the deployed SAP integration module into the application lifecycle management of the organization. --- ## SAP eventing with SAP Advanced Event Mesh (AEM) Receive [CloudEvents](https://cloudevents.io/) from SAP Advanced Event Mesh (AEM) and send CloudEvents to AEM. ## About SAP eventing SAP eventing uses three connectors that enable bidirectional communication between Camunda and AEM. | Connector | Description | | :----------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [SAP Eventing Outbound Connector](https://marketplace.camunda.com/en-US/apps/632606/sap-eventing-outbound-connector) | Sends CloudEvents from Camunda to an AEM topic or queue endpoint. | | [SAP Eventing Message Start Event Connector](https://marketplace.camunda.com/en-US/apps/632607/sap-eventing-message-start-event-connector) | Translates an incoming CloudEvent from AEM into a [BPMN Message Start Event](/components/modeler/bpmn/message-events/message-events.md#message-start-events) to trigger a new process instance. | | [SAP Eventing Intermediate Event Connector](https://marketplace.camunda.com/en-US/apps/632751/sap-eventing-intermediate-event-connector) | Translates an incoming CloudEvent from AEM into a [BPMN Intermediate Catch Event](/components/modeler/bpmn/message-events/message-events.md#intermediate-message-catch-events) to allow an active process to continue based on the event. | The integration uses **HTTP** as the transport protocol: - Incoming connectors act as webhooks, receiving CloudEvent payloads and delivering them into process instances. - The outbound connector sends `HTTP POST` requests to an AEM [topic](https://docs.solace.com/Messaging/Guaranteed-Msg/Topic-Endpoints.htm) or [queue](https://docs.solace.com/Messaging/Guaranteed-Msg/Queues.htm) endpoint. :::info SAP Advanced Event Mesh uses [Solace Event Broker](https://solace.com/products/event-broker/) as its core event broker. The terms **AEM** and **Solace Event Broker** can be used interchangeably when referring to eventing functionality. ::: ## Prerequisites Because **HTTP** is used as the transport protocol, AEM must be configured to use [REST messaging](https://docs.solace.com/API/REST/REST-get-start.htm). This enables publishers and subscribers to communicate over HTTP. The configuration examples below build on the Solace tutorial [Publish/Subscribe REST Messaging](https://tutorials.solace.dev/rest-messaging/publish-subscribe/), which provides detailed steps for setting up REST-based publish/subscribe messaging. ## Installation Install the SAP Eventing connectors directly from the [Camunda Marketplace](https://marketplace.camunda.com/). ## Configuration overview - [SAP Eventing Message Start Event Connector](#sap-eventing-message-start-event-connector) - [SAP Eventing Intermediate Event Connector](#sap-eventing-intermediate-event-connector) - [SAP Eventing Outbound Connector](#sap-eventing-outbound-connector) ## SAP Eventing Message Start Event Connector Inbound CloudEvents → BPMN message. Applying the **SAP Eventing Message Start Event Connector** generates a unique **webhook URL** that starts a new BPMN process instance when invoked. ![Webhook URL](./img/eventing-webhook.png) :::info The webhook URL is generated **only after the initial deployment** of the process. ::: This URL must be registered as a target in **SAP Advanced Event Mesh (AEM)**. The host portion of the URL (`https://.connectors.camunda.io`) is used when configuring the **REST consumer** in AEM. ![AEM REST consumer](./img/eventing-aem-rest-consumer.png) ### Authentication In the REST consumer configuration, set up authentication from AEM to the Camunda webhook endpoint. The credentials configured in the **Authorization** section of the built-in connector must match the **authentication scheme** used in AEM. ![Camunda and AEM credentials](./img/eventing-authorization.png) ### Queue binding The path component of the Camunda webhook URL must be used as the **POST request target** in the **Queue Binding** of the REST consumer. ![AEM Queue Binding](./img/eventing-aem-queue-binding.png) ### Other configuration options The remaining configuration options are identical to those of the [HTTP Webhook Connector](/components/connectors/protocol/http-webhook.md), except for one key difference: > The default webhook response explicitly returns a `200` status code and an `"OK"` message body, confirming that the CloudEvent was successfully received and acknowledged by Camunda. ![Remaining Incoming Webhook config](./img/eventing-incoming-other-config.png) ### Event flow When a CloudEvent is received from AEM, all **header properties** and the **body payload** are relayed to the target process instance, either: - At process creation (Message Start Event), or - During event correlation (Intermediate Event). This ensures that message attributes and payload data from AEM are preserved end-to-end within the Camunda process. ## SAP Eventing Intermediate Event Connector ### Correlate CloudEvents as BPMN messages The **SAP Eventing Intermediate Event Connector** injects a CloudEvent from AEM into an active Camunda process instance using [message correlation](/components/connectors/protocol/http-webhook.md#correlation). Any CloudEvent property can be used as a **correlation key** to match incoming event data to the correct process instance. ### Correlation via CloudEvent body The configuration options of the **Message Start** and **Intermediate Event** connectors are identical, except that the intermediate event requires an additional **Correlation** section. ![Intermediate Message Correlation](./img/eventing-intermediate-correlation.png) - In the example above, the process variable `ENCOMGridID` must exist within the process instance. - Its value is compared against the CloudEvent payload (`request.body.FlynnLocationID`). - If both values match, the CloudEvent is correlated to that process instance. ### Correlation via CloudEvent metadata Since **HTTP** is used as the transport protocol, the [CloudEvents specification](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/bindings/http-protocol-binding.md) requires all CloudEvent metadata to be passed as **HTTP headers**. AEM prepends all user properties with the prefix `Solace-User-Property-`. For example, the CloudEvent property `ce-id` is represented as the HTTP header `Solace-User-Property-ce-id`. To correlate on metadata, reference the header name including this prefix. #### Example: CloudEvent HTTP message ```json { "request": { "body": { "FlynnLocationID": "34.0522,-118.2437" }, "headers": { "host": ".connectors.camunda.io", "authorization": "Basic CaMUnDakZW1v", "content-type": "application/json", "solace-user-property-ce-specversion": "1.0", "solace-user-property-ce-type": "sap.s4.beh.encom.grid.program.v1", "solace-user-property-ce-source": "/alan/pager/buzz", "solace-user-property-ce-subject": "CLU-2.0", "solace-user-property-ce-id": "Argon-T-01", "solace-user-property-ce-time": "2018-04-05T03:56:24Z", "solace-user-property-ce-datacontenttype": "application/json" } } } ``` To correlate on the `ce-id` property, use `request.headers.solace-user-property-ce-id`. ![Mapping to CloudEvent meta data](./img/eventing-correlation-ce-headers.png) :::tip Use backticks to escape dashes in the header name, for example: `` request.headers.`solace-user-property-ce-id` ``. ::: ## SAP Eventing Outbound Connector The **SAP Eventing Outbound Connector** allows you to send CloudEvents to AEM using its REST messaging capability. It publishes the event using an `HTTP POST` request. ![SAP eventing outbound connector configuration](./img/eventing-outbound-connector.png) ### Endpoint The **Endpoint** field specifies the URL of your AEM event broker. You can find this URL in AEM’s web interface: > **Cluster Manager → Your Cluster → (Service Details) → Connect → Connect with Java → Solace REST Messaging API** The **FQDN** (fully qualified domain name) is shown on the right-hand side. ![FQDN of the AEM REST messaging API](./img/eventing-aem-rest-fqdn.png) ### Topic / queue Specify the target **topic** or **queue** path where the CloudEvent will be published. Do not begin the path with `/` — the connector includes a validation check to prevent this. ### Authorization Supported authorization methods: - `None` - `API Key` - `Basic` - `OAuth 2.0` - `Bearer Token` ### CloudEvent CloudEvent metadata (e.g., `ce-id`, `ce-subject`) is automatically handled by the AEM broker, including the required `Solace-User-Property-` prefix. Configure only the standard CloudEvent attributes in the connector. The CloudEvent data is serialized into a **JSON body**, and both metadata and data fields support [FEEL expressions](/components/modeler/feel/what-is-feel.md) for dynamic values. ![Eventing Outbound CloudEvent](./img/eventing-outbound-cloudevent.png) ### Other configuration options All remaining options are identical to those of the [REST Connector](/components/connectors/protocol/rest.md), including: - [Connection timeout](/components/connectors/protocol/rest.md#network-communication-timeouts) - [Output mapping](/components/connectors/protocol/rest.md#output-mapping) These settings control network behavior and define how response data is mapped to process variables. --- ## SAP OData connector The SAP OData connector is a protocol and outbound [connector](/components/connectors/introduction.md) that runs as a Docker image on the SAP Business Technology Platform (BTP). This connector is designed to run in [hybrid mode](/components/connectors/use-connectors-in-hybrid-mode.md), hosted in the customer's SAP BTP sub-account in the [Cloud Foundry environment](https://discovery-center.cloud.sap/serviceCatalog/cloud-foundry-runtime?region=all). This connector works with Camunda 8 SaaS, and utilizes SAP BTP's [Destination](https://help.sap.com/docs/connectivity/sap-btp-connectivity-cf/destination-service) and [Connectivity](https://help.sap.com/docs/connectivity/sap-btp-connectivity-cf/what-is-sap-btp-connectivity) concepts to query a SAP system via both OData v2 and v4. ## Overview For a standard overview of the steps involved in the SAP OData connector, see the following diagram: ![OData steps](./img/odata-connector-ops.png) ## Prerequisites [Create an API client](/components/hub/organization/manage-clusters/manage-api-clients.md) for your Camunda SaaS cluster with the full scope: `Zeebe,Tasklist,Operate,Optimize,Secrets` To run the SAP OData connector Docker image, the following SAP infrastructure setup is required: - [Cloud Foundry CLI](https://github.com/cloudfoundry/cli) with [multiapps plugin](https://github.com/cloudfoundry/multiapps-cli-plugin) installed on the machine executing the deployment. - SAP BTP subaccount with a [Cloud Foundry environment](https://discovery-center.cloud.sap/serviceCatalog/cloud-foundry-runtime?region=all) enabled and a [created space](https://help.sap.com/docs/btp/sap-business-technology-platform/create-spaces). - Minimum of [1 GB storage quota and 2 GB runtime memory](https://help.sap.com/docs/btp/sap-business-technology-platform/managing-space-quota-plans). - [Entitlements](https://help.sap.com/docs/btp/sap-business-technology-platform/managing-entitlements-and-quotas-using-cockpit) for: - [Connectivity Service](https://discovery-center.cloud.sap/serviceCatalog/connectivity-service?region=all), `lite` plan (to connect to the SAP on-premises). - [Destination Service](https://discovery-center.cloud.sap/serviceCatalog/destination?service_plan=lite®ion=all&commercialModel=btpea), `lite` plan. - One or more instance- or subaccount-level destinations, pointing to the SAP systems to communicate with. ![sample BTP destination configuration](./img/btp-destination.png) - Ensure `Additional Properties` are correctly set on the Destination. For example: ```json HTML5.DynamicDestination: true sap-client: WebIDEEnabled: true WebIDESystem: WebIDEUsage: odata_gen ``` :::info The SAP OData connector currently supports only destination authentication types suitable for straight-through (machine-to-machine) processing — that is, scenarios without user tasks in the execution path. Use `BasicAuthentication` or any `OAuth2`-based mechanism. `PrincipalPropagation` is not supported because it requires a user-bound security context. Camunda user tasks do not expose a JWT (for example, as a transient variable) that the connector can forward at runtime. ::: ## Configuration and deployment A descriptor file is required to deploy the SAP OData connector to a space in a SAP BTP subaccount. An exemplary deployment descriptor `mtad.yaml.example` is provided by Camunda. This is a standard format in SAP BTP's Cloud Foundry environment to describe the application requiring deployment. ### Using `csap` Use CSAP CLI in either: - **Interactive mode**: Follow the on-screen prompts. - **Non-interactive mode**: Provide all required parameters directly to the CLI. Configure the OData connector via [the `csap` cli](./csap-cli.md) (recommended) or manually. Using `csap` simplifies the process by automatically gathering all required files and customizing them for your BTP environment based on the details you provide through prompts or command-line options. Use the command `csap setup` to guide you interactively. - Assuming your [Camunda cluster's API credentials](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client) are sourced in your shell environment, this will do the configuration for you: ```shell csap setup --for odata \ --camunda 8.7 \ --deployment SaaS ``` ### Manual configuration Follow these steps: 1. Find the matching [Docker image](https://hub.docker.com/r/camunda/sap-odata-connector/tags) for the targeted Camunda 8 SaaS version. The version follows the format `..`. Examples: - `8.6.0` is the OData connector in version `0` for Camunda 8 SaaS version `8.6` - `8.5.1` is the OData connector in version `1` for Camunda 8 SaaS version `8.5` 2. Download the matching `mtad.yaml.example` from [the OData connector's GitHub release page](https://github.com/camunda/sap-odata-connector/releases). Adjust the values for the credentials (`client ID`, client secret, etc.) to match those of the API client of the targeted Camunda 8 SaaS environment and rename it to `mtad.yaml`. 3. Customize the names of the SAP BTP Destination and Connectivity instances as needed—both will be automatically created during deployment. If instances with the same names already exist in your subaccount, they will be reused. 4. Download the connector template from the [OData connector's GitHub release page](https://github.com/camunda/sap-odata-connector/releases). ### Deploying to SAP BTP 1. Log into the desired SAP BTP subaccount via the [Cloud Foundry `cf-cli`](https://github.com/cloudfoundry/cli): ```shell $> cf login API endpoint: https://api.cf. ... ... ``` 2. Deploy the SAP OData connector via the `cf-cli`. Note that this requires [the "multiapps" plugin of Cloud Foundry](https://github.com/cloudfoundry/multiapps-cli-plugin) to be installed on the machine the deployment runs on: ```shell $> cf deploy ./ # append the -f flag to shortcircuit ongoing deployments Deploying multi-target app archive /some/path/sap-odata-connector in org / space as you@example.org .. ... Application "sap-odata-connector" started and available at "some.url.hana.ondemand.com" ``` ### Deployment in Camunda 8 SaaS - If you are using Web Modeler, [import the SAP OData connector element](/components/connectors/manage-connector-templates.md#importing-existing-connector-templates) you downloaded in the earlier step to use it in your process design. ![sample BPMN diagram with SAP OData connector](./img/sap-odata-connector-task-in-model.png) - If you are using Desktop Modeler, [follow the standard importing procedure](/components/modeler/desktop-modeler/element-templates/configuring-templates.md). ## Working with the SAP OData connector in Camunda Modeler ### Modeling options To use the **SAP OData connector** in your process, either change the type of existing task by clicking on it and selecting the **Change element** menu icon, or create a new connector task by using the **Append connector** context menu. Follow our [guide to using connectors](/components/connectors/use-connectors/index.md) to learn more. :::note The configuration options will dynamically change with the selected HTTP method and the OData protocol version. For example, a `payload` field is only displayed when the HTTP method is something other than "GET". ::: ![SAP OData connector element template](./img/sap-odata-connector-template.png) Specifying the `BTP destination name` allows you to reuse existing Destinations from the subaccount or instance level. Authentication and authorizations are maintained at this level, which is why it's not necessary to maintain credentials for the connector. ### Advanced capabilities In addition to the basic OData settings such as Service, Entity, EntitySet, Method, and OData version, the **Advanced** section allows you to fine tune `GET` queries to the SAP method with all standard parameters. For example, supplying `$filter` and `$select` parameters helps in reducing data transferred over the wire, while `$expand` helps in retrieving additional entities with a single query. ![Advanced options of the SAP OData connector element template](./img/sap-odata-connector-element-template-advanced.png) ### $batch requests The capabilities are in sync with [https://me.sap.com/notes/1869434](https://me.sap.com/notes/1869434), with the exception of XML over the wire. The OData connector uses `JSON` only. When `Batch Request` is selected as **Request type**, the available query options change and reveal the choice of the OData protocol and the input area for the individual requests. Note that the **Batch Request Payload** field requires the mandatory use of `FEEL`. ![batch connector template](./img/sap-odata-connector-batch-connector-template.png) The **Batch Request** payload is an array of objects that can either be of `"type": "batch"` or `"type": "changeset"` - `batch` (denotes read requests), and `changeset` (denotes write operations, including `DELETE`). ```jsonc [ { "type": "batch", "requests": [...] }, { "type": "changeset", "requests": [...] } ] ``` Both `batch` and `changeset` contain a `requests` node that holds the individual requests that should occur within a `batch` (read) or a `changeset` (write, update, delete). Together, they constitute the entirety of the batch request. The `requests` node is also an array of `objects`. Regardless of if a request is inside a `batch` or a `changeset`, it always has `method` and `resourcePath` as mandatory fields. ```jsonc [ { "type": "batch", "requests": [ { "method": "GET", "resourcePath": "A_BusinessPartner('" + bp1 + "')", "options": { "format": "json", "select": "FirstName" } } ] }, { "type": "changeset", "requests": [ { "method": "PATCH", "resourcePath": "A_BusinessPartner('" + bp1 + "')", "payload": { "FirstName": "Bernd (" + string(now()) + ")" } } ] } ] ``` `batch` request entries can contain an additional `options` block, allowing the same query options as the connector template has in [advanced capabilities](#advanced-capabilities), with the `$` prefix omitted. `changeset` request entries are modifying operations and thus require a `payload` in JSON format. ### Query result structure The result of any query, whether it is reading or writing to the SAP system, is in JSON format in the following structure: ```json { result: , statusCode: , countOrInlineCount: } ``` - `result` contains the result of the query, whether it is content retrieved from a SAP system via `GET` or the result of a write or update operation via `POST`, `PUT`, `PATCH`, or `DELETE`. (Note that with the latter, the `result` is always empty.) - `statusCode` holds the [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) of the operation. - `countOrInlineCount` is only present in the response when the corresponding option `$inlinecount` (for OData v2) or `$count` (for OData v4) was checked in the design time of the BPMN task. It then shows the number of results from the `GET` query to the SAP system. ![the output mapping of the SAP OData element template](./img/sap-odata-connector-element-template-result.png) The query result can either be mapped to a single result variable or worked on [via FEEL with an expression](/components/connectors/use-connectors/index.md#result-expression). The same is applicable to `getResponse`, as a result variable contains the described query JSON in its entirety. The result expression `{getStatusCode: statusCode}` would only hold the HTTP status code in the `getStatusCode` process variable. For `$batch` requests, the query result is an array of the result structure above. This is an example for an OData v2 batch request, consisting of one `Read` and one `Update` operation: ```json { "": [ { "result": { "d": { "__metadata": { "id": "/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner('...')", "uri": "/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner('...')", "type": "API_BUSINESS_PARTNER.A_BusinessPartnerType" }, "FirstName": "..." } }, "statusCode": 200 }, { "result": null, "statusCode": 204 } ] } ``` If one of the operations in the batch request fails, the error is relayed to the `result` node for the request. For example, the second request in this sample result where the business partner with number `0000000` couldn't be found: ```jsonc { "": [ { "result": { "d": { "__count": "4", "results": [ {...} ] } }, "statusCode": 200, "countOrInlineCount": 4 }, { "result": { "target": "/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner('0000000')?$format=json&$select=FirstName%2CLastName", "odata": { "error": { "code": "/IWBEP/CM_MGW_RT/020", "message": { "lang": "en", "value": "Resource not found for segment 'A_BusinessPartnerType'" }, "innererror": { "application": { "component_id": "LO-MD-BP", "service_namespace": "/SAP/", "service_id": "API_BUSINESS_PARTNER", "service_version": "0001" }, "transactionid": "D25C6A8CF4D30020E0067DD0610AFCCA", "timestamp": "", "Error_Resolution": { "SAP_Transaction": "", "SAP_Note": "See SAP Note 1797736 for error analysis (https://service.sap.com/sap/support/notes/1797736)", "Batch_SAP_Note": "See SAP Note 1869434 for details about working with $batch (https://service.sap.com/sap/support/notes/1869434)" }, "longtext_url": "/sap/opu/odata/iwbep/message_text/T100_longtexts(MSGID='%2FIWBEP%2FCM_MGW_RT',MSGNO='020',MESSAGE_V1='A_BusinessPartnerType',MESSAGE_V2='',MESSAGE_V3='',MESSAGE_V4='')/$value", "errordetails": [ { "ContentID": "", "code": "/IWBEP/CX_MGW_BUSI_EXCEPTION", "message": "Resource not found for segment 'A_BusinessPartnerType'", "longtext_url": "/sap/opu/odata/iwbep/message_text/T100_longtexts(MSGID='%2FIWBEP%2FCM_MGW_RT',MSGNO='020',MESSAGE_V1='A_BusinessPartnerType',MESSAGE_V2='',MESSAGE_V3='',MESSAGE_V4='')/$value", "propertyref": "", "severity": "error", "transition": false, "target": "" } ] } } } }, "statusCode": 404 } ] } ``` ### Error handling The SAP OData connector allows handling of query errors directly in the model. This means an OData error is relayed to the process instance in the reserved variables `bpmnError` and `error` and can be processed accordingly. 1. Equip the connector task with an error handling expression such as: ```js if error.code = "400" then bpmnError("400", "client request is bad", { errorMessage: error.message, errorCode: error.code }) else if error.code = "404" then bpmnError("404", "queried resource not found", { errorMessage: error.message, errorCode: error.code }) else if error.code = "500" then bpmnError("500", "server error", { errorMessage: error.message, errorCode: error.code }) else if error.code = "503" then bpmnError("503", "I'm just an proxy teapot", { errorMessage: error.message, errorCode: error.code }) else null ``` ![image-20241010160419616](./img/odata-connector-error-expression.png) 2. Specifically note the third parameter to `bpmnError`: ```js { errorMessage: error.message, errorCode: error.code } ``` This relays the error message and code to the next step in the process flow. 3. Equip the BPMN task with an error boundary event: ![error boundary event on SAP OData connector](./img/sap-odata-connector-task-error-handling2.png) If the SAP OData connector encounters an error, the boundary event will catch the error and continue the process flow. The error boundary event can receive these configuration parameters to contain further error details: ![error output mapping](./img/sap-odata-connector-task-error-handling1.png) - `errorMessage`: Contains a verbose version of the error message and cause and relays it into the process scope as `ov_errorMessage`. - `errorCode`: Holds a predefined value describing the scope of the error, relaying it to the process scope as `errorCode`. It can be one of the following: - `INVALID_PAYLOAD`: The payload of the request was detected as erroneous by the server. - `REQUEST_ERROR`: The request contained an error, for example, a wrong combination of `GET` query parameters. - `GENERIC_ERROR` - `DESTINATION_ERROR`: An error occurred while claiming the Destination from the runtime environment. - `error`: The serialized Error object, available in the example above as `ov_error`. ## Tips - Ensure the connection from the Cloud Foundry environment via the destination to the SAP systems works. Using the [Terminal in Business Application Studio](https://community.sap.com/t5/technology-blogs-by-sap/how-to-check-the-connectivity-to-your-backend-system-in-business/ba-p/13479832) is a quick way to verify this. - Validate requests first in an API client before trying with the SAP OData connector in Modeler. Then, copy over to the element template fields. This saves time and reduces potential error. - Any payload size less than or equal to 2.5 MB can be considered safe. --- ## Prerequisites Before setting up the SAP integration, ensure the following requirements are met. ## Camunda setup - **OData and RFC connectors:** 8.6+ - **BTP Plugin:** 8.6+ - **Advanced Event Mesh integration:** 8.6+ Compatible with both **SaaS** and **Self-Managed** deployments: - **SaaS:** Use [hybrid connectors](/components/connectors/use-connectors-in-hybrid-mode.md) to securely connect to SAP systems. - **Self-Managed:** Ensure outbound network connectivity from your environment to SAP BTP. ## SAP setup You need an **SAP BTP subaccount** with these services enabled: - [**Cloud Foundry Runtime**](https://discovery-center.cloud.sap/serviceCatalog/cloud-foundry-runtime?region=all) - [**Destination Service (Free)**](https://discovery-center.cloud.sap/serviceCatalog/destination?region=all&service_plan=lite&commercialModel=btpea) – for system and service connectivity - [**Connectivity Service (Free)**](https://discovery-center.cloud.sap/serviceCatalog/connectivity-service?region=all) – required for on-premises SAP S/4HANA or ECC - [**SAP Cloud Connector**](https://help.sap.com/docs/connectivity/sap-btp-connectivity-cf/cloud-connector) – bridges on-premises systems with BTP Additional services may be required depending on your use case: - [**SAP Advanced Event Mesh (AEM)**](https://discovery-center.cloud.sap/serviceCatalog/advanced-event-mesh?region=all) – enables event-driven integration between Camunda and SAP. Required for the BTP Plugin: - [**PostgreSQL on SAP BTP (Hyperscaler Option)**](https://discovery-center.cloud.sap/serviceCatalog/postgresql-hyperscaler-option?region=all) ## Authentication and connectivity - An SAP **technical user** with sufficient authorizations for the relevant S/4HANA or ECC systems - [**Authorization and Trust Management Service (Free)**](https://discovery-center.cloud.sap/serviceCatalog/authorization-and-trust-management-service?region=all) – to manage identity and access across integration modules - Secure connectivity between Camunda and SAP, configured either: - Through **SAP Cloud Connector** for on-premises systems - Or directly via **SAP BTP services** for cloud-hosted systems ## Tooling - [**CSAP CLI**](./csap-cli.md): A self-contained binary that runs on any platform without installation. Copy the binary to the target machine and execute it directly. --- ## SAP RFC connector The [SAP RFC](/reference/glossary.md#rfc) [Connector](/components/connectors/introduction.md) is a [protocol and outbound connector](/components/connectors/connector-types.md). This connector is a Java Spring Boot application that runs as a `.war` on the SAP Business Technology Platform (BTP). It connects to Camunda 8 SaaS, and utilizes SAP BTP's [Destination](https://help.sap.com/docs/connectivity/sap-btp-connectivity-cf/destination-service) and [Connectivity](https://help.sap.com/docs/connectivity/sap-btp-connectivity-cf/what-is-sap-btp-connectivity) concepts to query a SAP system via the RFC protocol to interact with remote-enabled Function Modules and BAPIs. ## Overview For a standard overview of the steps involved in the SAP RFC connector, see the following diagram: ![RFC overview](./img/rfc-connector-ops.png) ## Prerequisites - **Camunda API Client** - [Create an API client](/components/hub/organization/manage-clusters/manage-api-clients.md) for your Camunda SaaS cluster with the full scope: `Zeebe,Tasklist,Operate,Optimize,Secrets` - **To run the SAP RFC connector**, the following SAP infrastructure setup is required: - [Cloud Foundry CLI](https://github.com/cloudfoundry/cli) with the [multiapps plugin](https://github.com/cloudfoundry/multiapps-cli-plugin) installed on the machine executing the deployment. - SAP BTP subaccount with a [Cloud Foundry environment](https://discovery-center.cloud.sap/serviceCatalog/cloud-foundry-runtime?region=all) enabled and a [created space](https://help.sap.com/docs/btp/sap-business-technology-platform/create-spaces). - A minimum of [1 GB storage quota and 2 GB runtime memory](https://help.sap.com/docs/btp/sap-business-technology-platform/managing-space-quota-plans). - **[Entitlements](https://help.sap.com/docs/btp/sap-business-technology-platform/managing-entitlements-and-quotas-using-cockpit) for**: - [Connectivity Service](https://discovery-center.cloud.sap/serviceCatalog/connectivity-service?region=all), `lite` plan (to connect to the SAP is on-premises). - [Destination Service](https://discovery-center.cloud.sap/serviceCatalog/destination?service_plan=lite®ion=all&commercialModel=btpea), `lite` plan. - [Authorization and Trust Management Service](https://discovery-center.cloud.sap/serviceCatalog/authorization-and-trust-management-service?region=all), `application` plan. - **One or more instance- or subaccount-level Destinations**, pointing to the SAP systems to communicate with. ![btp-destination-rfc](./img/btp-destination-rfc.png) - **Ensure `Additional Properties` is set** on the Destination are aligned with those of your connector or remote SAP system. ## Configuration and deployment Unlike other built-in connectors, the SAP RFC connector must be deployed as a Java `.war` archive. This is because it uses SAP's [JCo Java library](https://support.sap.com/en/product/connectors/jco.html) to connect via RFC to the configured SAP system. the JCo library's license prohibits redistribution, but it is available at runtime on BTP and auto-discovered by Camunda's RFC connector. A descriptor file is required to deploy the SAP RFC connector to a space in a SAP BTP subaccount. An exemplary deployment descriptor `mtad.yaml.example` is provided by Camunda. This is a standard format in SAP BTP's Cloud Foundry environment to describe the application that needs deployment. ### Configuring the RFC connector Configure the SAP RFC connector via [the `csap` cli](./csap-cli.md) (recommended) or manually. Using `csap` simplifies the process by automatically gathering all required files and customizing them for your BTP environment based on the details you provide through prompts or command-line options. #### Using `csap` Use CSAP CLI in either: - **Interactive mode:** By following the on-screen prompts. - **Non-interactive mode:** By providing all required parameters directly to the CLI. Use the command `csap setup` will guide you interactively. - Assuming your [Camunda cluster's API credentials](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client) are sourced in your shell environment, this will do the configuration for you: ```shell csap setup --for rfc \ --camunda 8.7 \ --deployment SaaS ``` #### Manual configuration 1. Find the matching `.war` archive for the targeted Camunda 8 SaaS version on the [respective GitHub release page](https://github.com/camunda/sap-rfc-connector/releases). The version follows the format `..`. Examples: - `rfc-8.6.0.war` is the RFC connector in version `0` for Camunda 8 SaaS version `8.6` - `rfc-8.5.1.war` is the RFC connector in version `1` for Camunda 8 SaaS version `8.5` 2. Download the matching `mtad.yaml.example` file also from [the GitHub release page](https://github.com/camunda/sap-rfc-connector/releases). Adjust the values for the credentials (such as client ID and client secret) to match those of the API client of the targeted Camunda 8 SaaS environment and rename it to `mtad.yaml`. 3. Donwload the connector template from [the GitHub release page](https://github.com/camunda/sap-rfc-connector/releases). ### Deploying to BTP 1. Log into the desired SAP BTP subaccount via the [Cloud Foundry `cli`](https://github.com/cloudfoundry/cli) (cf-cli): ```shell $> cf login API endpoint: https://api.cf. ... ... ``` 2. Deploy the SAP RFC connector via the `cf-cli`. Note that this requires [the "multiapps" plugin of Cloud Foundry](https://github.com/cloudfoundry/multiapps-cli-plugin) to be installed on the machine the deployment runs on. ```shell $> cf deploy ./ # append the -f flag to shortcircuit ongoing deployments Deploying multi-target app archive /some/path/sap-rfc-connector in org / space as you@example.org .. ... Application "sap-rfc-connector" started and available at "some.url.hana.ondemand.com" ``` ### Deployment in Camunda 8 SaaS - If using Web Modeler, [import the SAP RFC connector's element template](/components/connectors/manage-connector-templates.md#importing-existing-connector-templates) contained in the repository in `element-templates/sap-rfc-connector.json` for use in your process design. ![sap-rfc-connector-task-in-model](./img/sap-rfc-connector-task-in-model.png) - If using Desktop Modeler, [follow the standard importing procedure](/components/modeler/desktop-modeler/element-templates/configuring-templates.md). ## Working with the SAP RFC connector in Camunda Modeler ### Modeling options To use the **SAP RFC connector** in your process, either change the type of existing task by clicking on it and selecting the **Change element** menu icon, or create a new connector task by using the **Append connector** context menu. Follow our [guide to using connectors](/components/connectors/use-connectors/index.md) to learn more. ![sap-rfc-connector-task-in-model](./img/sap-rfc-connector-element-template.png) First, choose whether to call a `BAPI` or a Function Module (`FM`). Then, provide the `exporting`-, `importing-`, and `tables` parameters as lists of objects. All object entries in the list look similar to `[{name:"param", type:"type"}]`, pointing to the parameter name of the BAPI/FM and its type. For example, `[{name:"PERSON_IN_CHARGE_FROM", type:"BAPI0012_GEN-PERS_IN_CHRG"}]`. For those with experience in `ABAP`, the configuration options are similar. ### Sending variables to the RFC target The `exporting parameter` is sent to the RFC target. The object structure generally looks like `[{name: "param", type: "type", value: }]`. Example: ```json [ { "name": "CONTROLLINGAREA", "type": "BAPI0012_GEN-CO_AREA", "value": "1000" } ] ``` This corresponds with the BAPI's/FM's `importing` definition, meaning it imports these variables from the RFC call: ```ABAP *" IMPORTING <-- this is the BAPI/FM - don't be confused! In Camunda, this is "exporting" :) *" VALUE(CONTROLLINGAREA) LIKE BAPI0012_GEN-CO_AREA ``` ### Receiving variables from the RFC target `Importing parameter` is what is expected back from the RFC target. They are configured in the same "list of objects" style pattern in the element template as the other parameters and generally look like `[{name: "param", type: "type"}]`. Example: ```json [ { "name": "DETAIL_DATA", "type": " BAPI1079_DETAIL" } ] ``` This corresponds with the BAPI's/FM's `exporting` definition, meaning it exports these variables to the caller: ```ABAP *" EXPORTING *" VALUE(DETAIL_DATA) LIKE BAPI1079_DETAIL ``` ### Special cases: sending and/or receiving a "table" and a "changing" structure #### tables The `tables parameter` can be both "exporting" and "importing". :::danger Sending tables as tabular data to an RFC target is not yet supported. ::: ```json { "name": "COSTCENTERLIST", "type": "BAPI0012_CCLIST" } ``` The above example is an object parameter in the `tables parameter` section that describes a result table to be received back from the RFC call. In conforms with the BAPI `BAPI_COSTCENTER_GETLIST1` parameter definition on the SAP system: ```ABAP *" TABLES *" COSTCENTERLIST STRUCTURE BAPI0012_CCLIST ``` The same is applicable for the return structure `BAPIRET2` that denotes the result status of the RFC call: ```json { "name": "BAPIRET2", "isReturn": true } ``` This aligns with the BAPI definition: ```ABAP *" TABLES *" .... *" RETURN STRUCTURE BAPIRET2 ``` #### changing A `changing parameter` is a variable received by an RFC target that is processed, changed, and returned. It is only available for `FM`-type RFC targets in the SAP RFC connector. The overall structure is `[{name: "param", type: "type", value: }]`. Example: ```json [ { "name": "CV_RESULT", "type": "I", "value": "100" } ] ``` The value `100` is sent to the Fuction Module and sent back as `CV_RESULT`. ## Query result structure ### BAPI The result of a call to a BAPI holds the following JSON structure: ```json { tables: [ { ... } ], importing: { { ... } } ] ``` `tables` holds a representation of the result tables configured. `importing` is the result of what was sent to the BAPI in the `exporting` section above. ### Function Module The result of a call to a Function Module holds the following JSON structure: ```json { tables: [ { ... } ], importing: [ { ... } ], changing: [ { ... } ] ] ``` - `tables` holds a representation of the result tables configured. - `importing` is the result of what was sent to the Function Module in the `exporting` section above. - `changing` is the result of what was sent to the Function Module in the `changing` section above. ## Error handling The SAP RFC connector allows handling of query errors directly in the model. This means an RFC error is relayed to the process instance in the reserved variables `bpmnError` and `error` and can be processed accordingly: ``` DESTINATION_ERROR, REQUEST_EXECUTION_ERROR, REQUEST_SERIALIZATION_ERROR, JCO_RUNTIME_ERROR, GENERIC_ERROR ``` --- ## Best practices(Servicenow) Use these practices to build secure, reliable, and maintainable Camunda–ServiceNow integrations. ### Manage credentials securely - Store and manage ServiceNow credentials using [Camunda secrets](../../../components/hub/organization/manage-clusters/manage-secrets.md). - Never hardcode usernames or passwords in connector configurations or BPMN models. - Reference secrets with the `{{secrets.}}` syntax in connector fields. - Apply this consistently across all ServiceNow connectors to maintain a unified security model. ### Reuse variables effectively - Map ServiceNow `sys_id` values to **top-scope process variables** in your BPMN process. - Use these variables for: | Action | Purpose | | ----------- | ---------------------------------------------- | | Lookup | Retrieve existing records reliably | | Update | Modify records without additional queries | | Delete | Remove records when needed | | Correlation | Tie records to notifications or process events | **Example:** ```feel = incidentResponse.body.sys_id ``` --- ## Blueprints Use these blueprints to quickly implement common Camunda–ServiceNow integration scenarios. ![Camunda ServiceNow integration blueprint](./img/sn-blueprint-template.png) Download the blueprint from the [Camunda Marketplace](https://marketplace.camunda.com/) and import it directly into Camunda Modeler. It provides **pre-configured processes** demonstrating best practices, reusable patterns, and realistic business use cases. ### Supported integration patterns | Type | Name | Purpose | Example | Built-in connectors | ServiceNow Spoke | | :---------------- | :------------------------------------- | :------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | :---------------------------- | :-------------------------------- | | **Manage** | Manage ServiceNow data entries | Execute CRUD (Create, Read, Update, Delete) operations on any ServiceNow table. | Create or modify a change request in ServiceNow. | ServiceNow Outbound Connector | Not needed | | | Create ServiceNow Incident | Raise business errors from BPMN models that trigger ServiceNow incidents. | Create or modify a change request in ServiceNow. | ServiceNow Incident Handler | Not needed | | **Orchestration** | Start Camunda Process | Start a Camunda orchestration process from a ServiceNow Flow. | Trigger an end-to-end orchestration for employee onboarding. | Not needed | Camunda Spoke → Start Process | | | Orchestrate ServiceNow Flows | Trigger a ServiceNow Flow within a Camunda orchestration and correlate using the ServiceNow `sys_id`. | Interrupt a Camunda orchestration for an approval process executed in ServiceNow. | ServiceNow Flow Starter | Camunda Spoke → Correlate Message | | | Orchestrate ServiceNow Data Management | Trigger a ServiceNow Flow when a dataset changes as part of a Camunda orchestration. | Updating a dataset that triggers a flow for further processing. | ServiceNow Outbound Connector | Camunda Spoke → Correlate Message | | | Orchestrate ServiceNow User Task | Create a ServiceNow User Task from a Camunda orchestration. Users interact with the orchestration through this task. | Create a ServiceNow Catalog item for a user who needs to provide additional information. | ServiceNow Outbound Connector | Camunda Spoke → Correlate Message | --- ## Camunda Spoke for ServiceNow The Camunda Spoke for ServiceNow lets you orchestrate workflows between Camunda and ServiceNow, empowering your organization to automate cross-system workflows end to end. With the Spoke installed in ServiceNow, you can start, signal, correlate, or cancel Camunda process instances directly from Flow Designer. ## Spoke actions ### Start process Start a Camunda process from ServiceNow. ![Start Process action](./img/spoke-action-start.png) Supported inputs **Process ID:** The ID of the deployed BPMN process to start. Example: `handle_incident` **Process Version:** (Optional) The version of the process to start. If empty, the latest deployed version is used. Example: `5` **Variables:** (Optional) Process variables passed to Camunda as key-value pairs in JSON format. Example: `{ "invoiceId": "12345", "amount": 250 }` #### Code example ```javascript const returnObject = { request_item_number: fd_data.trigger.request_item.number.toString() || "", request_sys_id: fd_data.trigger.request_item.sys_id.toString() || "", }; return JSON.stringify(returnObject); ``` :::tip When you add the JSON payload as a code snippet, convert ServiceNow types to a JSON-compatible format. In the example above, `sys_id` is a ServiceNow GUID, but it must be converted to a string for the JSON payload. That is why the example uses `fd_data.trigger.request_item.sys_id.toString()`. ::: **Tenant ID:** (Optional) The tenant identifier for multi-tenant Camunda setups. Leave empty for single-tenant setups. Example: `hr-emea` **Operation Reference:** (Optional) A user-defined reference key available in Camunda for tracking the operation. Example: `camID` **Wait for completion:** (Optional) Whether the flow waits until the Camunda process completes. ### Send signal Broadcast BPMN signals to one or more Camunda process instances ![Send Signal action](./img/spoke-action-send.png) Supported inputs **Signal name:** The name of the BPMN signal to send. Must match the signal name defined in the process model. Example: `sla_limit_exceeded` **Variables:** (Optional) Process variables passed to Camunda as key-value pairs in JSON format. Example: `{ "invoiceId": "12345", "amount": 250 }` **Tenant ID:** (Optional) The tenant identifier for multi-tenant Camunda setups. Leave empty for single-tenant setups. Example: `hr-emea` ### Correlate message Correlate a running Camunda process instance from ServiceNow. ![Correlate Message action](./img/spoke-action-correlate.png) Supported inputs **Message name:** The name of the BPMN message to correlate with. Example: `managerApprovalDone` **Correlation key:** The process variable value used to match the message to a specific process instance. Example: `approvalID` ### Cancel process Cancel a Camunda process instance from ServiceNow when needed. ![Cancel Process Action](./img/spoke-action-cancel.png) Supported inputs **Process Instance Key:** The unique key identifying a running Camunda process instance to cancel. Example: `2251799813685252` **Operation Reference:** (Optional) A user-defined reference key available in Camunda for tracking the operation. Example: `camID` ### Start a ServiceNow process from Camunda Camunda can trigger a ServiceNow flow by calling a REST API as the trigger endpoint in ServiceNow. ![REST API trigger configuration](./img/rest-api-trigger.png) Supported inputs **HTTP Method:** The HTTP method accepted by the flow. Example: `POST` **Path:** A custom URL path suffix for the trigger endpoint used by the ServiceNow Flow Starter Connector. Example: `/api/camunda/my_flow_name` **Requires authentication:** Whether incoming requests must include a valid ServiceNow authentication header. Enable this for production integrations. **Roles:** (Optional) ServiceNow roles authorized to access the trigger endpoint. --- ## ServiceNow Flow Starter Use the ServiceNow Flow Starter connector to trigger ServiceNow flows from Camunda processes and correlate them using the ServiceNow **Sys ID**. The connector calls ServiceNow flows via REST API, enabling you to orchestrate complex business logic such as fulfillment, approvals, or catalog flows as part of an end-to-end Camunda process. It also allows Camunda orchestrations to **pause or wait** for ServiceNow-driven approval steps. :::important The ServiceNow Flow Starter connector requires the **ServiceNow Integration Hub Enterprise Pack** and the **Flow Trigger – REST plugin**. See [prerequisites](../prerequisites.md) for installation requirements. ::: ## Supported operations | Operation | Description | Example use case | | :----------- | :------------------------------------------------------------------- | :---------------------------------------------------------------------- | | Trigger Flow | Start a ServiceNow Flow Designer flow via its REST trigger endpoint. | Initiate catalog requests or approval workflows from Camunda processes. | ## Configure the connector In Camunda Modeler, select **ServiceNow Flow Starter** from the connector templates or download it from the [Camunda Marketplace](https://marketplace.camunda.com/). ### Required fields | Field | Description | | :-------------------- | :----------------------------------------------------------------------------------------------------------------------------------- | | Instance name | Name of your ServiceNow instance (e.g., `your-instance-name`). | | REST API trigger path | REST API endpoint for the ServiceNow flow (e.g., `/api/camun/my_flow_name`). | | Method | HTTP method for the request (`POST`, `GET`, `PUT`, `PATCH`, `DELETE`). | | Headers | Optional HTTP headers to include in the request (e.g., `{"hello":"header"}`). | | Query parameters | Optional URL query parameters (e.g., `{"hello":"query"}`). | | Request body | Payload sent to the ServiceNow flow, typically containing input variables or correlation data (e.g., `{"correlationValue": camId}`). | | Authentication | ServiceNow credentials (username and password). | :::tip Store ServiceNow credentials securely as [Camunda secrets](/components/hub/organization/manage-clusters/manage-secrets.md) and reference them in the connector configuration (e.g., `{{secrets.snUser}}`). ::: ![Configuration of the Flow Starter connector in Camunda Modeler.](../img/flow-starter.png) _Configuration of the Flow Starter connector in Camunda Modeler._ ## Example configuration | Field | Example value | | :-------------------- | :---------------------------- | | Instance name | `your-instance-name` | | REST API trigger path | `/api/camun/your_flow_name` | | Method | `POST` | | Headers | `{"hello": "header"}` | | Query parameters | `{"hello": "query"}` | | Request body | `{"correlationValue": camId}` | | Username | `{{secrets.snUser}}` | | Password | `{{secrets.snPwd}}` | --- ## ServiceNow Incident Handler Use the ServiceNow Incident Handler connector to create, read, update, or delete incidents in ServiceNow directly from Camunda processes. This connector works with the ServiceNow `incident` table, enabling automated IT service management and process-driven incident handling. ## Supported operations | Operation | Description | Example use case | | :-------- | :----------------------------------------------------------- | :---------------------------------------------------------- | | Create | Create a new incident in ServiceNow. | Automatically log an incident when a process task fails. | | Read | Retrieve details of an existing incident using its `sys_id`. | Check the current status of an incident. | | Update | Modify fields on an existing incident. | Change incident priority or assignment group mid-process. | | Delete | Remove an incident by its `sys_id`. | Clean up test or temporary incidents after automation runs. | ## Configure the connector Select **ServiceNow Incident Handler** from Camunda Modeler connector templates or download it from the [Camunda Marketplace](https://marketplace.camunda.com/). ### Required fields | Field | Description | | :------------- | :------------------------------------------------------------------------- | | Instance name | Name of your ServiceNow instance (e.g., `your-instance-name`). | | Operation | One of `Create`, `Read`, `Update`, or `Delete`. | | Payload | JSON data representing incident fields (for Create and Update operations). | | Sys ID | Unique identifier for `Read`, `Update`, or `Delete` operations. | | Authentication | ServiceNow credentials (username and password). | :::tip Store ServiceNow credentials securely as [Camunda secrets](/components/hub/organization/manage-clusters/manage-secrets.md) and reference them in the connector configuration (e.g., `{{secrets.snUser}}` and `{{secrets.snPwd}}`). ::: ![ServiceNow Incident Handler example](../img/incident-handler.png) _Configuration of the Incident Handler connector in Camunda Modeler._ ## Example configurations ### Create a new incident | Field | Example value | | :------------ | :------------------------------------------------------------------- | | Instance name | `your-instance-name` | | Operation | `Create` | | Payload | `{"short_description": "Create ServiceNow Incident (from Camunda)"}` | | Username | `{{secrets.snUser}}` | | Password | `{{secrets.snPwd}}` | ### Update an existing incident's priority | Field | Example value | | :------------ | :------------------- | | Instance name | `your-instance-name` | | Operation | `Update` | | Sys ID | `{{incidentSysId}}` | | Payload | `{"priority": "2"}` | | Username | `{{secrets.snUser}}` | | Password | `{{secrets.snPwd}}` | --- ## ServiceNow outbound connector Use the ServiceNow outbound connector to perform CRUD operations on any ServiceNow table directly from Camunda processes. This connector interacts with ServiceNow tables via REST APIs, enabling powerful integrations without custom scripts. ## Supported operations | Operation | Description | Example use case | | :-------- | :--------------------------------------------------------------------------- | :--------------------------------------------------------------- | | Create | Insert a new record into a ServiceNow table. | Create a new incident or service request from a Camunda process. | | Read | Retrieve records from a ServiceNow table using query parameters or `sys_id`. | Look up user details or check incident status. | | Update | Modify fields of an existing record identified by `sys_id`. | Update ticket status or assignment group. | | Delete | Remove a record from a table by `sys_id`. | Delete temporary or test records after processing. | ## Configure the connector In Camunda Modeler, select **ServiceNow Outbound Connector** from the connector templates or download it from the [Camunda Marketplace](https://marketplace.camunda.com/). ### Required fields | Field | Description | | :--------------- | :------------------------------------------------------------------------------------------------------ | | Instance name | Name of your ServiceNow instance (e.g., `your-instance-name`). | | Operation | One of `Create`, `Read`, `Update`, or `Delete`. | | Target table | The target ServiceNow table (e.g., `incident`, `sc_task`, `sc_req_item`). | | Payload | JSON data sent to ServiceNow for `Create` and `Update` operations. | | Query parameters | For `Read` operations. Use `^` to separate multiple filter conditions (e.g., `active=true^priority=1`). | | Sys ID | Required for `Update` and `Delete` operations to identify the target record. | | Authentication | ServiceNow credentials (username and password). | :::tip Store ServiceNow credentials securely as [Camunda secrets](/components/hub/organization/manage-clusters/manage-secrets.md) and reference them in the connector configuration (e.g., `{{secrets.snUser}}` and `{{secrets.snPwd}}`). ::: ![ServiceNow Outbound Connector example](../img/outbound-connector.png) _Example configuration of the Create operation in Camunda Modeler._ > When using `Read`, `Update`, or `Delete`, the `sys_id` field becomes available in the connector properties to specify the target record. ![Sys ID field example](../img/outbound-sys-id.png) ## Example: Create a requested item | Field | Example value | | :------------ | :--------------------------------------------------------------------------------------------------------------------- | | Instance name | `your-instance-name` | | Operation | `Create` | | Target table | Requested item [sc_req_item] | | Payload | `{"short_description": "Database maintenance scheduled via Camunda process", "category": "Hardware", "priority": "2"}` | | Username | `{{secrets.snUser}}` | | Password | `{{secrets.snPwd}}` | --- ## Glossary Use this glossary to understand key terms and concepts when working with the Camunda–ServiceNow integration. ## Key terms | Term | Definition | Relevance | | :--------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | | Spoke | A scoped ServiceNow application containing Flow Designer content for a specific application or record type. Provides pre-built actions and subflows. | Enables Camunda integration. Custom spokes can handle BPMN process orchestration. | | Application | A self-contained ServiceNow package delivering business capabilities, including tables, UI, logic, data model, and security. | Provides the structure for deploying spokes and plugins used in integrations. | | Plugin | An optional add-on that extends or enhances capabilities of installed applications. | Enables additional integration capabilities, such as Integration Hub packs. | | Connection alias | A named reference in ServiceNow linking credentials and connection details for external system integrations. | Required for secure communication between ServiceNow and Camunda. | | REST API | RESTful web services enabling CRUD operations on ServiceNow tables using standard HTTP methods. | Primary integration method between Camunda and ServiceNow. | | SysID | A unique 32-character GUID identifying each record in a ServiceNow instance. | Essential for tracking and correlating records across Camunda process instances. | | Tables | Database objects in ServiceNow that store data and expose endpoints for CRUD operations via REST APIs. | Critical for data exchange between ServiceNow and Camunda workflows. | | Flows | Automated ServiceNow processes consisting of a trigger and a sequence of reusable actions to orchestrate business logic. | Can be triggered by Camunda processes via REST calls or webhooks. | | Subflows | Reusable sequences of actions that encapsulate common business logic and can be called from multiple flows. | Enable reusable ServiceNow operations that can be called by different Camunda processes. | | Actions | Individual steps within flows that perform operations such as creating records, sending notifications, or calling external APIs. | Can be invoked from Camunda to perform ServiceNow operations. | | Triggers | Event conditions that initiate flow execution, such as record operations, schedules, or external system calls. | Can respond to Camunda process events via REST calls. | --- ## Prerequisites(Servicenow) Ensure you have the following prerequisites before setting up the Camunda–ServiceNow integration. ## Camunda - Camunda 8.6+ - SaaS: Supported out of the box. - Self-managed: May require minor adjustments to the Camunda Spoke in ServiceNow. - User roles: Administrative access to create an API client. ## ServiceNow - ServiceNow version: Yokohama or newer. - Required plugins: - Integration Hub Starter Pack - Integration Hub Action Step – REST - Camunda Spoke: Available on the [ServiceNow Store](https://store.servicenow.com/store/app/aac1b64fc3803290ef46d0af050131d0). - User account: A technical or administrator account with permissions to access target tables and Flow Designer. This account will be used in built-in connectors to authenticate and interact with ServiceNow. - Optional plugins (required only for starting ServiceNow flows from Camunda using the Flow Starter connector): - Integration Hub Enterprise Pack - Integration Hub Flow Trigger – REST --- ## ServiceNow Use Camunda to automate workflows that interact with ServiceNow IT Service Management (ITSM), enabling seamless communication between BPMN processes and ServiceNow workflows. ## Key features The integration enables you to: - Bi-directionally integrate Camunda and ServiceNow to orchestrate flows and processes. - Orchestrate activities inside ServiceNow and across systems end-to-end using BPMN. - Manage records in ServiceNow: create, read, modify, or delete any record directly from Camunda processes. ## Audience This documentation is intended for: - Developers implementing workflows that interact with ServiceNow. - Solution architects designing process automation across Camunda and ServiceNow. - Administrators managing integration configuration and security. ## About the integration Camunda’s ServiceNow integration combines: - Custom actions in the ServiceNow Camunda Spoke to start or correlate Camunda processes from ServiceNow. - Built-in connectors and element templates to interact with ServiceNow tables and flows from Camunda processes. ## Architecture ![Camunda ServiceNow integration architecture](./img/sn-camunda-architecture.png) _This diagram shows how Camunda and ServiceNow interact._ ## Integration features The integration provides bi-directional orchestration using two main components: the Camunda Spoke and built-in connectors. ### Camunda Spoke in ServiceNow | Action | Description | | :---------------- | :--------------------------------------------------------------- | | Start process | Start a Camunda process from ServiceNow. | | Correlate message | Correlate a running Camunda process instance from ServiceNow. | | Send signal | Broadcast BPMN signals to one or many Camunda process instances. | | Cancel process | Cancel a Camunda process instance from ServiceNow when needed. | ### ServiceNow connectors in Camunda | Connector | Description | | :---------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------- | | [ServiceNow Outbound Connector](connectors/outbound-connector.md) | Execute CRUD operations on any ServiceNow table. | | [ServiceNow Flow Starter](connectors/flow-starter.md) | Start a ServiceNow flow from a Camunda process (requires ServiceNow Integration Hub Enterprise Pack). | | [ServiceNow Incident Handler](connectors/incident-handler.md) | Create and manage incidents in ServiceNow directly from a Camunda process. | --- ## Setup & configuration Connect your ServiceNow instance with Camunda to enable end-to-end orchestration and secure data exchange. Install and configure the Camunda Spoke in ServiceNow, set up ServiceNow connectors in Camunda, and configure secure credentials. After setup, you can interact with ServiceNow tables and flows from Camunda and start or control Camunda processes from ServiceNow. ## Configure Camunda 1. Create a [Camunda API credential](../../../components/react-components/create-api-credentials.md) for ServiceNow connectivity. 2. Add and configure the ServiceNow connector templates in your Camunda processes like any other connector. ## Configure ServiceNow ### Install the Camunda Spoke 1. Log in to ServiceNow as an administrator. 2. Navigate to **All → Application Manager**. 3. Search for **Camunda Spoke** and click **Install**. ### Verify installation - **Spoke**: Confirm the "Camunda Spoke" menu appears in the Application Navigator. - **Connection alias**: Go to **Connections & Credentials → Connection & Credential Aliases** and ensure the **Camunda alias** exists and is linked to the Camunda Spoke. ### Create an OAuth profile 1. Navigate to **System OAuth → Application Registry** and click **New**. 2. Select **Connect to a third-party OAuth Provider**. 3. Fill in the form: | Field | Value | | :--------------------- | :------------------------------------------- | | Name | `Camunda OAuth` | | Client ID | Paste Client ID from Camunda | | Client Secret | Paste Client Secret from Camunda | | OAuth API Script | `OAuthCamundaUtil` | | Default Grant Type | `Client Credentials` | | Token URL | `https://login.cloud.camunda.io/oauth/token` | | Refresh Token Lifespan | Default `8,640,000` | 4. Click **Submit**. ### Create a credential record 1. Navigate to **Connections & Credentials → Credentials** and click **New**. 2. Select **OAuth 2.0 Credentials**. 3. Fill in the form: | Field | Value | | :------------------- | :------------------------------ | | Name | `Camunda Connection` | | OAuth Entity Profile | `Camunda OAuth default_profile` | 4. Click **Submit**. ### Create a connection record and link to alias 1. Navigate to **Connections & Credentials → Connection & Credential Aliases** and open the **Camunda alias**. 2. In the **Connections** tab, click **New**. 3. Fill in the form: | Field | Value | | :------------------ | :----------------------------------------------------------------- | | Name | `Camunda API Connection` | | Credential | `Camunda Connection` | | Domain | `Global` | | Connection alias ID | `x_camun_camunda.Camunda` | | URL Builder | Enabled | | Host | Camunda cluster Region ID (e.g., `lhr-1.api.camunda.io`) | | Protocol | `https` | | Base path | Camunda Cluster ID (e.g., `/6b6b3969-a65c-4bdd-905e-c29102eebded`) | 4. Click **Submit**. ### Optional: Configure Flow Starter To start ServiceNow flows from Camunda asynchronously, ensure the following: - ServiceNow Integration Hub Enterprise Pack - ServiceNow Integration Hub Flow Trigger – REST plugin --- ## Troubleshooting Resolve common issues encountered while setting up or using the Camunda–ServiceNow integration and follow recommended actions to fix them. ## ServiceNow | Issue | Possible cause | Recommended action | | ------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Camunda Spoke not visible | Spoke not installed correctly or insufficient permissions. | Confirm the Camunda Spoke is installed from the [ServiceNow Store](https://store.servicenow.com/store/app/aac1b64fc3803290ef46d0af050131d0) and that you are logged in as an administrator. | | Flow doesn’t trigger | Trigger conditions misconfigured or Integration Hub inactive. | Review flow trigger settings and ensure the required Integration Hub plugins are active. | | Authentication failures | OAuth profile misconfigured or invalid Camunda credentials. | Verify the Client ID, Client Secret, and Token URL. Check the OAuth profile settings in ServiceNow. | | Missing required plugins | IntegrationHub packs (e.g., Enterprise Pack) not installed. | Install the required plugins listed in [Prerequisites](./prerequisites.md). | ## Camunda | Issue | Possible cause | Recommended action | | ----------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | | Connector task fails | Invalid credentials or network connectivity issues. | Confirm Camunda API credentials are valid and that the ServiceNow instance is reachable. | | Variables not mapped correctly | Response fields from ServiceNow are not stored or referenced properly. | Map the entire response object and specific fields explicitly in your BPMN model. | | Process not started from ServiceNow | Correlation keys or message names do not match. | Verify that message names and correlation variables are configured correctly in both ServiceNow and Camunda. | | Timeouts or unexpected errors | Large payloads or network latency. | Check logs, increase timeout thresholds, and test the ServiceNow API call separately. | ## General tips - Enable verbose flow logs in ServiceNow Navigate to **Flow Designer → Your flow name → Flow Reporting Settings** and enable full verbose logging to debug flow execution. - Check Camunda Operate for connector errors Inspect failed connector tasks in [Camunda Operate](/components/operate/operate-introduction.md) for detailed error messages and variable mappings. - Validate network connectivity Ensure outbound calls between Camunda and ServiceNow are not blocked by firewalls, VPNs, or proxies. - Check version compatibility Verify that Camunda and ServiceNow versions meet the [prerequisites](./prerequisites.md). ## Frequently asked questions **Do I need IntegrationHub Enterprise Pack for all connectors?** No. It is only required to start ServiceNow flows from Camunda using the Flow Starter connector. **Why do I get 401 Unauthorized errors from ServiceNow?** This usually indicates a misconfigured OAuth profile. Verify the Client ID, Client Secret, and Token URL. --- ## Using Camunda 8 Learn how to use Camunda to orchestrate your processes. Orchestrate and automate complex business processes for people, systems, and devices. Build BPMN processes and DMN decisions using powerful tools offering collaborative modeling, operations, and analytics. Introduction to Camunda 8 :::info Camunda 8.8 - See [what's new in Camunda 8.8](/reference/announcements-release-notes/880/whats-new-in-88.md), [release announcements](/reference/announcements-release-notes/880/880-announcements.md), and [release notes](/reference/announcements-release-notes/880/880-release-notes.md). ::: ## Best Practices Camunda Best Practices distill our experience with BPMN and DMN on the Camunda toolstack, incorporating insights from consulting, community feedback, and various interactions. Camunda Best Practices :::tip Learn about [deciding your stack](/components/best-practices/architecture/deciding-about-your-stack.md), and [sizing your environment](/components/best-practices/architecture/sizing-your-environment.md) to make sure your production deployment is sized appropriately, and can scale without any backpressure issues. ::: ## Features and integrations Get started with selected key features and integrations. ## Using Camunda Explore and learn about Camunda components and BPMN, DMN, and FEEL expressions. ## Camunda 8 SaaS Reference information for Camunda 8 SaaS, including clusters, regions, and encryption at rest. Camunda 8 SaaS ## Glossary Explore the glossary and understand definitions for key Camunda 8 terms and abbreviations. Glossary --- ## Identity and access management in Camunda 8 Use identity access control to provide secure access for authorized users and systems in Camunda 8. ## Identity types in Camunda 8 There are two types of identity in Camunda 8. These identities serve different purposes: one controls access to process execution and runtime APIs, while the other controls access to management and modeling components. Orchestration Cluster Admin (formerly Orchestration Cluster Identity) Used for authenticating and authorizing users and systems that interact with the Orchestration Cluster (such as Zeebe, Operate, Tasklist, and the Orchestration Cluster REST API).Admin governs access to process execution, task management, and related runtime resources. Management Identity Used for managing the components Camunda Hub and Optimize.Management Identity is typically required for platform administrators and developers, and is separate from the identities used for process orchestration. :::tip Understanding which identity is required for a given action helps you apply the correct access control policies. ::: :::note Identity object identifiers and names are limited to **256 characters**. This limit applies independently of the secondary storage backend used by the Orchestration Cluster. ::: ## Identity provider (IdP) integration In production setups, both the Orchestration Cluster Admin and the Management Identity can integrate with an external OIDC IdP (such as Entra ID) for unified user management, single sign-on (SSO), and consistent security policies. | Identity type | Description | Default IdP | External IdP support | | :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------- | :------------------------------------ | | Orchestration Cluster Admin | Built-in user management with support for external IdP integration via OIDC. Connects to enterprise IdPs such as Microsoft Entra ID, Okta, and more. | Built-in user management | OIDC integration with enterprise IdPs | | Management Identity | Uses Keycloak by default, but can be configured with an external IdP via OIDC. | Keycloak | OIDC integration with external IdPs | ## Authentication vs. authorization Authentication and authorization are the two fundamental concepts for access control in Camunda 8. ### Authentication Authentication verifies who a user or client is. For example, you log in with a username and password or through SSO. ### Authorization Authorization determines what an authenticated user or client is allowed to access in Camunda 8 and which actions they can perform on those resources. For example, a user's authorizations allow them to access Operate, view running or completed process instances, start new process instances, or claim and complete user tasks in Tasklist and through the Orchestration Cluster REST API. | Identity type | Authorization model | Description | Management interface | | :-------------------------- | :------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------- | | Orchestration Cluster Admin | Fine-grained permissions | Controls access to applications, APIs, and runtime resources through specific permissions for each resource type and action (for example, `PROCESS_DEFINITION` and `USER_TASK`). | Camunda Admin UI or API | | Management Identity | Role-based access control (RBAC) | Uses predefined roles and permissions for users and groups to manage Camunda Hub and Optimize. | Keycloak admin console or external IdP | ### How authentication and authorization work together 1. Authentication happens first: The system verifies identity. 2. Authorization happens next: The system verifies permissions. A user must be both authenticated and authorized to access protected resources. ## Authentication methods Camunda 8 supports multiple authentication methods depending on the environment: | Environment | Authentication method | Notes | | --------------------------------------------------------------------------------- | ---------------------------------- | ---------------------------------------------------------------------------------------- | | [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) | None / Basic authentication / OIDC | No auth or Basic authentication only for local development. OIDC optional if configured. | | [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md) | None / Basic authentication / OIDC | No auth or Basic authentication only for local development. OIDC optional if configured. | | [Helm Self-Managed](/self-managed/deployment/helm/install/index.md) | Basic authentication / OIDC | Basic authentication default, OIDC optional if configured. | | SaaS | OIDC | OIDC required for all requests. | - No authentication: only for local development (Run, Docker Compose). - Basic authentication: simple to set up; not recommended for production. - OIDC-based authentication: recommended for production Self-Managed and required for SaaS. For API documentation, link to the centralized authentication overview instead of repeating environment defaults. :::warning The Operate, Tasklist, and Zeebe REST APIs are deprecated and should not be used for new development. While they continue to function, new development should use the Orchestration Cluster REST API by referencing the [Orchestration Cluster REST API migration documentation](/apis-tools/migration-manuals/migrate-to-camunda-api.md). Authentication for these APIs works the same way. See [Orchestration Cluster REST API authentication](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md) for details. ::: ### Users and clients Actions in an orchestration cluster can be executed by two kinds of authenticated entities (also known as principals): users and clients. [Users](/components/admin/user.md) typically interact with the cluster through a browser, while [clients](/components/admin/client.md) interact programmatically through the APIs. Although both principal types can use web UIs and APIs, the distinction still matters. Users represent individuals who are granted access to an orchestration cluster, whereas clients represent systems or applications. :::note If you're using Basic authentication to secure your cluster, both users and clients are treated as users. There is no dedicated client concept in this configuration. ::: Distinguishing between users and clients aligns your access management with how identities are modeled in your identity provider. They are usually authenticated differently (for example, username and password for users versus a client certificate for applications), have different authorization requirements (such as administrator access versus deployment permissions). Separating them simplifies auditing and operational clarity. ## How to obtain tokens For environments using OIDC: 1. Generate a JSON Web Token (JWT). 2. Include the token in each API request as: `Authorization: Bearer `. - [Generate a token (SaaS)](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client) - [Generate a token (Self-Managed)](/self-managed/components/orchestration-cluster/admin/connect-external-identity-provider.md) Example request using a token: ```shell curl --header "Authorization: Bearer ${ACCESS_TOKEN}" \ ${BASE_URL}/v1/process-instances/search ``` ### Token expiration Tokens expire according to the `expires_in` field returned by the IdP. After expiration, request a new token. ## Learn more ### Orchestration Cluster authentication and authorization - [Set up OIDC-based authentication](/self-managed/components/orchestration-cluster/admin/connect-external-identity-provider.md) - [Orchestration Cluster authorization](./authorizations.md) ### Task access control - [User task authorization](../../tasklist/user-task-authorization.md) --- ## Orchestration Cluster authorization The Orchestration Cluster in Camunda 8 provides a fine-grained authorization system for controlling access to web components and APIs. ## Orchestration Cluster authorization overview This system only applies to the following Orchestration Cluster components: - [Zeebe](../../zeebe/zeebe-overview.md) - [Admin](../../admin/admin-introduction.md) - [Operate](../../operate/operate-introduction.md) - [Tasklist](../../tasklist/introduction-to-tasklist.md) - [Orchestration Cluster APIs](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) :::note These authorizations do not apply to other Camunda services, such as Web Modeler or Optimize. ::: ## How authorization works The authorization system is built on the principle of least privilege. - When enabled, no access is granted by default, and all permissions must be explicitly assigned. - There are no "deny" rules – if a permission is not explicitly granted, access is denied. This model is enforced across both web components and API requests. ### Owners, resources, and permissions At its core, an authorization grants an owner specific permissions on a resource. For example: - User `john.doe` can be authorized to create new users. - Group `devOps` can be authorized to delete the group `sales`. - Role `processOwner` can be authorized to deploy and run all processes. #### Owners An **owner** is an entity that receives permissions. An authorization can be assigned to any of the following owner types: - User - Group - Role - Client - Mapping rule #### Resources A resource is an object that users interact with and that needs to be secured. Each resource has a unique set of permissions that can be granted. Examples of resources: - Process Definition - Decision Definition - System - User #### Permissions A permission is a specific action that an owner is allowed to perform on a resource. Permissions are unique to each resource type. For example, a `Process Definition` resource has a `CREATE_PROCESS_INSTANCE` permission, while a `User` resource has a `DELETE` permission. ## Available resources The following table lists all resources that support authorization in the Orchestration Cluster (Zeebe, Operate, Tasklist, Orchestration Cluster APIs), as well as the available permissions per resource. | Resource type | Resource key example | Resource key type | Supported permissions | | :--------------------------------- | :------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `AUDIT_LOG` | An operation category (`ADMIN`, `DEPLOYED_RESOURCES`, or `USER_TASKS`) or `*` for all categories. | All audit logs / category name | `READ` | | `AUTHORIZATION` | `*` | All authorizations | `CREATE`, `DELETE`, `READ`, `UPDATE` | | `BATCH` | `*` | All batches | `CREATE`, `CREATE_BATCH_OPERATION_CANCEL_PROCESS_INSTANCE`, `CREATE_BATCH_OPERATION_DELETE_DECISION_DEFINITION`, `CREATE_BATCH_OPERATION_DELETE_DECISION_INSTANCE`, `CREATE_BATCH_OPERATION_DELETE_PROCESS_DEFINITION`, `CREATE_BATCH_OPERATION_DELETE_PROCESS_INSTANCE`, `CREATE_BATCH_OPERATION_MIGRATE_PROCESS_INSTANCE`, `CREATE_BATCH_OPERATION_MODIFY_PROCESS_INSTANCE`, `CREATE_BATCH_OPERATION_RESOLVE_INCIDENT`, `READ`, `UPDATE` | | `CLUSTER_VARIABLE` | `*`, `myVariable` | An exact cluster variable name or `*` for all cluster variables. | `CREATE`, `UPDATE`, `DELETE`, `READ` | | `COMPONENT` | `*`, `operate`, `tasklist`, `identity` | All components, component name | `ACCESS` | | `DECISION_DEFINITION` | `*`, `order_decision` | All decisions / Decision ID | `CREATE_DECISION_INSTANCE`, `DELETE_DECISION_INSTANCE`, `READ_DECISION_DEFINITION`, `READ_DECISION_INSTANCE` | | `DECISION_REQUIREMENTS_DEFINITION` | `*`, `order_decision` | All DRDs / DRD ID | `READ` | | `DOCUMENT` | `*` | All Documents | `CREATE`, `READ`, `DELETE` | | `EXPRESSION` | `*` | All expressions | `EVALUATE` | | `GLOBAL_LISTENER` | `*` | Only wildcard permissions are currently supported | `CREATE_TASK_LISTENER`, `DELETE_TASK_LISTENER`, `READ_TASK_LISTENER`, `UPDATE_TASK_LISTENER` | | `GROUP` | `*`, `accounting` | All groups / Group ID | `CREATE`, `DELETE`, `READ`, `UPDATE` | | `MAPPING_RULE` | `*`, `my_mapping` | All mappings / Mapping ID | `CREATE`, `DELETE`, `READ`, `UPDATE` | | `MESSAGE` | `*`, `my_message_name` | All messages / Message Name | `CREATE`, `READ` | | `PROCESS_DEFINITION` | `*`, `order_process` | All processes / BPMN Process ID | `CANCEL_PROCESS_INSTANCE`, `CLAIM_USER_TASK`, `COMPLETE_USER_TASK`, `CREATE_PROCESS_INSTANCE`, `DELETE_PROCESS_INSTANCE`, `MODIFY_PROCESS_INSTANCE`, `READ_PROCESS_DEFINITION`, `READ_PROCESS_INSTANCE`, `READ_USER_TASK`, `UPDATE_PROCESS_INSTANCE`, `UPDATE_USER_TASK` | | `RESOURCE` | `*`, `my_form`, `order_process` | All resources / Form ID / Process ID | `CREATE`, `READ`, `DELETE_DRD`, `DELETE_FORM`, `DELETE_PROCESS`, `DELETE_RESOURCE` | | `ROLE` | `*`, `myrole` | All roles / Role ID | `CREATE`, `DELETE`, `READ`, `UPDATE` | | `SYSTEM` | `*` | All system operations | `READ`, `READ_JOB_METRIC`, `READ_USAGE_METRIC`, `UPDATE` | | `TENANT` | `*`, `tenantA` | All tenants / Tenant ID | `CREATE`, `DELETE`, `READ`, `UPDATE` | | `USER_TASK` | `assignee` (example) | Task property name used with `PROPERTY` matcher (for example, `assignee`, `candidateUsers`, `candidateGroups`) | `READ`, `UPDATE`, `CLAIM`, `COMPLETE` | | `USER` | `*`, `jane.doe` | All users / Username | `CREATE`, `DELETE`, `READ`, `UPDATE` | ### User task authorizations User task access in the Orchestration Cluster is controlled using a combination of process-level and task-level permissions. - Process-level permissions are granted on the `Process Definition` resource. - Task-level permissions are granted on the `USER_TASK` resource and typically use property-based access control. For Tasklist-specific behavior, permission evaluation, and recommended configuration, see [User task authorization in Tasklist](../../tasklist/user-task-authorization.md). ## Configuration ### SaaS configuration In Camunda 8 SaaS, authorizations can be enabled or disabled per cluster. This setting can be changed by: - Organization admins - Organization owners ### Self-Managed configuration In Self-Managed deployments, you can enable the authorization system using: ```yaml camunda.security.authorizations.enabled: true ``` ```yaml CAMUNDA_SECURITY_AUTHORIZATIONS_ENABLED=true ``` ```yaml orchestration.security.authorizations.enabled=true ``` ## Security considerations Certain permissions grant powerful capabilities and should be assigned with caution. It is critical to ensure that only trusted users and clients are granted these permissions to maintain the security and integrity of your system. ### `CREATE` permission for the Resource (deployment) Granting `CREATE` permission on the **Resource** is equivalent to allowing remote code execution. When a user deploys a BPMN model, it can contain executable code in script tasks, service tasks, or listeners that will be run by the process engine. Only grant this permission to users and clients who are fully trusted to deploy and execute code in your environment. ### `CREATE`/`UPDATE` permissions for the User The `CREATE` and `UPDATE` permissions for the **User** resource are highly sensitive. When a user's password is set or changed via Admin, there are no security controls enforced, such as password complexity policies. This permission should only be assigned to trusted administrators. ### System access permissions Permissions that control system access are particularly security-sensitive. This includes CRUD operations to the following resources: - System - User - Group - Role - Mapping rule - Tenant These permissions should be strictly limited to trusted system administrators who are responsible for managing user access control. ### No validation of owner and resource IDs When you create an authorization, the Orchestration Cluster validates the owner depending on the `ownerType`. For `USER`, `ROLE`, `GROUP`, and `MAPPING_RULE`, the owner must exist in Admin. For `CLIENT`, the owner is not validated. The Orchestration Cluster does not validate whether the resource exists when creating an authorization. - This behavior lets you create authorizations for entities outside of the system (for example OIDC users) or for entities that will be created in the future (for example creating process definition authorizations before the process is deployed). - However, you should keep this in mind when setting up new users, groups, roles, and so on, and verify that the ID of the new entity does not accidentally match an existing authorization. ### Process-level vs. task-level task permissions When configuring access to user tasks, keep the following in mind: - Granting `READ_USER_TASK` or `UPDATE_USER_TASK` on `Process Definition` gives broad access to all user tasks for that process definition. - These process‑level permissions override task‑level checks: if a user has the required process‑level permission, the engine does not evaluate `User Task` authorizations for the same operation. For most scenarios: - Assign process‑level task permissions only to trusted roles such as task managers or administrators. - Use `User Task` property‑based authorizations (and the default Task Worker role) to limit regular task workers to tasks where they are assignee, candidate user, or in a candidate group. ## Default roles Camunda provides predefined roles to simplify access management: | Role ID | Purpose | Typical authorizations | | :------------------- | :-------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **admin** | Full control over all Orchestration Cluster resources and components. | All permissions for all resources: `READ`, `CREATE`, `UPDATE`, `DELETE`, including `ACCESS` to all web components. | | **app-integrations** | Technical role for executing app integration calls. | `READ_PROCESS_DEFINITION` on Process Definition (`*`), `CREATE_PROCESS_INSTANCE`, `READ_PROCESS_INSTANCE`, `UPDATE_PROCESS_INSTANCE` on Process Definition (`*`), `READ_USER_TASK`, `UPDATE_USER_TASK`, `CLAIM_USER_TASK`, `COMPLETE_USER_TASK` on Process Definition (`*`), `CREATE` on Document | | **connectors** | Technical role for executing connector calls. | `READ_PROCESS_DEFINITION` on Process Definition (`*`), `UPDATE_PROCESS_INSTANCE` on Process Definition (`*`), `CREATE` on Message (`*`), `CREATE`, `READ`, and `DELETE` on Document | | **readonly-admin** | Audit-focused users who need read-only access across the cluster. | `READ` for all resources, including `READ_PROCESS_DEFINITION`, `READ_PROCESS_INSTANCE`, `READ_USER_TASK`, etc. | | **rpa** | Role for RPA workers. | `READ` on Resource (`*`), `UPDATE_PROCESS_INSTANCE` on Process Definition (`*`) | | **task-worker** | Default role for task workers to handle their own user tasks. | Property-based `User Task` authorizations on properties `assignee`, `candidateUsers`, `candidateGroups` with permissions `READ`, `CLAIM`, `COMPLETE` (one authorization per property). | ### Role assignment in SaaS - **admin**: Automatically assigned to organization owner and admin. - **connectors**: Automatically assigned to Connector Runtime in cluster deployment. - **app-integrations**: Automatically assigned to app integration clients in cluster deployment. - **readonly-admin**: Automatically assigned to Camunda Support agents for support cases. ## Common authorization use cases ### Web component access Users need specific permissions to access Orchestration Cluster web components: - UI access: Resource type `Component` and a resource key identifying the component: - `operate` for Operate access - `tasklist` for Tasklist access - `admin` for Admin access - `identity` for Admin access (deprecated - please use `admin` instead) - `*` for access to all components - Without these permissions, users cannot access the components. #### Tasklist V1 and Tasklist V2 Tasklist uses different mechanisms to control user task visibility, depending on the API version: - Tasklist V1: Uses user task access restrictions based on BPMN assignee, candidate users, and candidate groups. These restrictions are configured separately and apply only to Tasklist V1. - Tasklist V2 and the Orchestration Cluster REST API: Use the Orchestration Cluster authorization model, including process-level permissions on `Process Definition` and task-level authorizations on `USER_TASK` (with property-based access control). For Tasklist-specific behavior and recommended patterns, see [User task authorization in Tasklist](../../tasklist/user-task-authorization.md). After switching from Tasklist V1 to Tasklist V2, user task access restrictions no longer apply. Instead, configure the appropriate `Process Definition` and `USER_TASK` authorizations to control who can see, claim, and complete tasks. ### Resource-level access This section describes authorization for domain resources (such as process and decision definitions), not access to UI components or APIs. Users need additional permissions to access specific resources within web components: - Process-related: Resource type `Process Definition` - `READ_PROCESS_DEFINITION` to view process models - `CREATE_PROCESS_INSTANCE` to start new processes - `UPDATE_PROCESS_INSTANCE` to update running instances - `MODIFY_PROCESS_INSTANCE` to modify running instances - `CANCEL_PROCESS_INSTANCE` to cancel running instances - `DELETE_PROCESS_INSTANCE` to delete completed instances - Decision-related: Resource type `Decision Definition` - `READ_DECISION_DEFINITION` to view DMN models - `CREATE_DECISION_INSTANCE` to execute decisions ### API access When implementing your own integrations (for example, using a Camunda client), consider the following: - Job workers: Resource type `Process Definition` - `UPDATE_PROCESS_INSTANCE` to activate or complete jobs for the targeted process definitions --- ## Connect to an Identity Provider Integrate with an external identity provider (IdP) for single sign-on (SSO), centralized user management, and secure authentication. ## About IdP integration Connecting Camunda 8 to an external IdP allows you to: - Use enterprise authentication (for example, Microsoft EntraID, Okta, Keycloak, Auth0). - Centrally manage users in your IdP. - Enable SSO for Camunda components. - Enforce organization-wide security policies. ## Self-Managed Self-Managed deployments only support external IdP integration using **OpenID Connect (OIDC)** (for example, Keycloak, Auth0, Okta, EntraID via OIDC). You can integrate an IdP with both Admin (for the Orchestration Cluster) and Management Identity (for Camunda Hub and Optimize). - [Connect Orchestration Cluster Admin to an identity provider](/self-managed/components/orchestration-cluster/admin/connect-external-identity-provider.md) - [Connect Management Identity to an identity provider](../../../../self-managed/components/management-identity/configuration/connect-to-an-oidc-provider/) ## SaaS Camunda 8 SaaS currently only supports external IdP integration using **SAML** or **Azure Active Directory (EntraID)**. - [Connect to an identity provider](../../hub/organization/manage-organization-settings/external-sso.md) --- ## Mapping rules(Access-control) Mapping rules are used to dynamically manage access control by [connecting your Identity Provider](connect-to-identity-provider.md) and mapping claims from a JWT access token to [Admin](/components/admin/admin-introduction.md) entities in Camunda 8. ## Mapping rules in SaaS and Self-Managed In Camunda 8 SaaS, mapping rules are not supported. In Camunda 8 Self-Managed, configure mapping rules in the following components: - Orchestration Cluster Admin: Manage permissions within an [orchestration cluster](../../orchestration-cluster.md). Use mapping rules to assign users to [user groups](../../admin/group.md) and [roles](../../admin/role.md), grant [authorizations](../../admin/authorization.md), and associate them with specific [tenants](../../admin/tenant.md). - Mapping rules are available for Orchestration Cluster Admin only when using [OIDC-based authentication](/self-managed/components/orchestration-cluster/admin/connect-external-identity-provider.md). They do not apply to other authentication methods, such as Basic authentication. - Management Identity: Manage access to components like [Camunda Hub](/self-managed/components/hub/index.md) and [Optimize](/self-managed/components/optimize/overview.md). Mapping rules in [Management Identity](/self-managed/components/management-identity/overview.md) assign users to roles and tenants, granting access to those components. To learn more, see the [guide on managing mapping rules in Management Identity](/self-managed/components/management-identity/mapping-rules.md). ## How to use mapping rules :::info To use mapping rules, you must be familiar with the structure of the JWT access tokens that your OIDC provider issues to clients. ::: A mapping rule has the following properties: - Claim name: The name of a (nested) claim or a [JSONPath expression](https://www.rfc-editor.org/rfc/rfc9535). - Claim value: The expected value of the claim. The mapping rule takes effect only if this value is present in a JWT access token. Using a mapping rule is a two-step process: 1. _Create the mapping rule_ – Define how Camunda identifies a match between a JWT claim and the rule. 2. _Assign the mapping rule_ – Apply it to a group, role, authorization, or tenant. Assume the following payload of an access token issued by your Identity Provider (IdP): ```json { "sub": "1234567890", "name": "John Doe", "isAdmin": true, "orggroups": ["acct", "finance"], "iat": 1516239022 } ``` To make any user a member of the `admin` role when their `isAdmin` claim is set to `true`, first define a mapping rule as follows: - Claim name: `isAdmin` - Claim value: `true` Then, assign the mapping rule to the `admin` role. To make any member of the organizational group `acct` a member of the Orchestration Cluster group `accounting`, define a mapping rule as follows: - Claim name: `orggroups` - Claim value: `acct` Then, assign the mapping rule to the `accounting` group. :::note In this case, the mapping rule matches against an array of objects. Depending on the JWT structure, a claim value is matched using `equals` or `in` semantics. ::: ## References For more details on configuring mapping rules in a Self-Managed environment, see: - [Manage mapping rules in Orchestration Cluster Admin](../../admin/mapping-rules.md) - [Manage mapping rules in Management Identity](/self-managed/components/management-identity/mapping-rules.md) - [Manage mapping rules via API](/apis-tools/orchestration-cluster-api-rest/specifications/create-mapping-rule.api.mdx) --- ## Batch operations A high-level overview of batch operations in Camunda 8. ## About batch operations If a single process instance encounters an incident, or you need to update the instance for any other reason, you can perform an instance operation. However, if you need to perform the same operation on multiple instances, a batch operation: - Optimizes the performance of operations that need to be performed across many process instances. - Provides insights into the progress of the operation across all affected instances. - Gives you control over operation execution, with the ability to suspend, resume, and cancel operations. A **batch operation** is an operation on a selection, or batch, of process instances. Instead of manually operating on each instance, you can specify filter criteria and automatically identify and process matching instances across your cluster in parallel. The individual operation in the batch applied to a process instance is called a [**batch item**](../zeebe/technical-concepts/batch-operations.md#batch-operation-components). Example use cases include: - Many process instances have encountered a critical bug. - You need to skip an activity across multiple instances. - There was in issue when executing a process that corrupted many or all instances of that process. ## Types Here are the types of available batch operations: | Type | Description | | ------------------------- | -------------------------------------------------------------------------------------------------- | | Resolve incidents | Resolves the [incidents](./incidents.md) associated with a batch of process instances. | | Modify process instances | [Moves](./process-instance-modification.md) a batch of process instances from one node to another. | | Migrate process instances | [Migrates](./process-instance-migration.md) a batch of process instances to a new process version. | | Cancel process instances | Cancels a batch of process instances. | | Delete process instances | [Deletes](./process-instance-deletion.md) a batch of process instances. | | Delete decision instances | [Deletes](./decision-instance-deletion.md) a batch of decision instances. | Furthermore, depending on the status of the batch operation, you may be able to suspend, cancel, or resume the operation. :::warning Canceling a batch operation does not rollback any changes that have already been produced. ::: ## States A batch operation can have one of the following statuses: | Type | Description | | ------------------- | --------------------------------------------------------------------------------------------------------------------- | | Created | The batch operation was created, but the filter hasn't been used yet to determine the affected process instances. | | Active | The batch operation is actively being processed. | | Completed | All items in the batch operation were processed, regardless of whether the individual operations succeeded or failed. | | Partially completed | The batch operation successfully processed at least one partition and failed to process at least one partition. | | Suspended | The batch operation was temporarily stopped. Suspended batch operations can be resumed. | | Canceled | The batch operation was permanently stopped. Canceled batch operations can't be resumed. | | Failed | The batch operation failed on all partitions. | :::info Learn more about batch partitions in our [implementation overview](../zeebe/technical-concepts/batch-operations.md). ::: ## Authorization When executing a batch operation, there are two sets of permissions involved: - Batch operation permissions. - Item-level, or process definition, permissions. To create a batch operation, you always need both the permission to create batch operations as well as permissions to read process instances and execute specific operations on each targeted process instance. To suspend, resume, or cancel an operation, you only need the relevant batch operation permissions. The system stores authorization claims with the batch operation and uses them throughout its lifecycle. :::info Read more about [authorizations](/components/concepts/access-control/authorizations.md) and [how to create them in the Admin UI](/components/admin/authorization.md). ::: ## Next steps - [Learn how batch operations work](../zeebe/technical-concepts/batch-operations.md). - [Use batch operations in the Camunda 8 web interface](../operate/userguide/selections-operations.md). - Use the batch operations APIs: - [Search batch operations](/apis-tools/orchestration-cluster-api-rest/specifications/search-batch-operations.api.mdx). - [Cancel process instances (batch)](/apis-tools/orchestration-cluster-api-rest/specifications/cancel-process-instances-batch-operation.api.mdx). - [Resolve related incidents (batch)](/apis-tools/orchestration-cluster-api-rest/specifications/resolve-incidents-batch-operation.api.mdx). - [Migrate process instances (batch)](/apis-tools/orchestration-cluster-api-rest/specifications/migrate-process-instances-batch-operation.api.mdx). - [Modify process instances (batch)](/apis-tools/orchestration-cluster-api-rest/specifications/modify-process-instances-batch-operation.api.mdx). - [Delete process instances (batch)](/apis-tools/orchestration-cluster-api-rest/specifications/delete-process-instances-batch-operation.api.mdx). - [Delete decision instances (batch)](/apis-tools/orchestration-cluster-api-rest/specifications/delete-decision-instances-batch-operation.api.mdx). --- ## BPMN, DMN, and FEEL Use Business Process Model and Notation (BPMN), Decision Model and Notation (DMN), and Friendly Enough Expression Language (FEEL) in your process diagrams. ## BPMN BPMN was developed as a graphical notation to represent complex processes. It is maintained by the non-profit The Object Management Group (OMG) and employed by numerous organizations globally. The visual nature of BPMN enables greater collaboration between different teams, particularly within Modeler. BPMN in Modeler ## DMN DMN is a modeling approach owned by an institution called the Object Management Group (OMG), which also operates worldwide standards for BPMN. In DMN, decisions are modeled and executed using a language both business analysts and developers can understand. Model a set of rules within a table, and this will yield a decision to rapidly execute a process using a decision engine like Camunda. DMN in Modeler ## FEEL expressions FEEL is designed to write expressions in a way that is easily understood by both business professionals and developers. In Camunda, FEEL is used to define expressions in the context of BPMN diagrams, DMN diagrams, and Forms. FEEL is specified in the DMN specification of the Object Management Group (OMG). What is FEEL? --- ## Clusters A [cluster](/components/hub/organization/manage-clusters/create-cluster.md) is a provided group of production-ready nodes that run Camunda 8. When [creating a cluster in SaaS](/components/hub/organization/manage-clusters/create-cluster.md), you can choose the cluster **type** and **size** to meet your organization's availability and scalability needs, and to provide control over cluster performance, uptime, and disaster recovery guarantees. ## Cluster type The cluster type defines the level of availability and uptime for the cluster. You can choose from three different cluster types: - **Basic**: A cluster for non-production use, including experimentation, early development, and basic use cases that do not require a guaranteed high uptime. - **Standard**: A production-ready cluster with guaranteed higher uptime. - **Advanced**: A production-ready cluster with guaranteed minimal disruption and the highest uptime. ### Cluster availability and uptime | Type | Basic | Standard | Advanced | | :-------------------------------------------------------------------------- | :------------------------------------------------------------------------------------- | :-------------------------------------------------------- | :------------------------------------------------------------------------------------ | | Usage | Non-production use, including experimentation, early development, and basic use cases. | Production-ready use cases with guaranteed higher uptime. | Production-ready use cases with guaranteed minimal disruption and the highest uptime. | | Uptime Percentage (Orchestration Cluster\*) | 99% | 99.5% | 99.9% | | RTO/RPO\*\*(Orchestration Cluster\*) | RTO: 8 hoursRPO: 24 hours | RTO: 2 hoursRPO: 4 hours | RTO: < 1 hourRPO: < 1 hour | * Orchestration Cluster means the components critical for automating processes and decisions, such as Zeebe, Operate, Tasklist, Optimize, and connectors. ** RTO (Recovery Time Objective) means the maximum allowable time that a system or application can be down after a failure or disaster before it must be restored. It defines the target time to get the system back up and running. RPO (Recovery Point Objective) means the maximum acceptable amount of data loss measured in time. It indicates the point in time to which data must be restored to resume normal operations after a failure. It defines how much data you can afford to lose. The RTO/RPO figures shown in the table are provided on a best-effort basis and are not guaranteed. :::info See [Camunda Enterprise General Terms](https://legal.camunda.com/licensing-and-other-legal-terms#camunda-enterprise-general-terms) for term definitions for **Monthly Uptime Percentage** and **Downtime**. ::: ## Cluster size The cluster size defines the cluster performance and capacity. After you have chosen your cluster type, choose the cluster size that best meets your cluster environment requirements. To learn more about choosing your cluster size, see [sizing your environment](/components/best-practices/architecture/sizing-your-environment.md#sizing-your-runtime-environment). - You can choose from four cluster sizes: 1x, 2x, 3x, and 4x. - Larger cluster sizes include increased performance and capacity, allowing you to serve more workload. - Increased usage such as higher throughput or longer data retention requires a larger cluster size. - Each size increase uses one of your available cluster reservations. For example, purchasing two HWP advanced reservations for your production cluster allows you to configure two clusters of size 1x, or one cluster of size 2x. - You can change the cluster size at any time. See [resize a cluster](/components/hub/organization/manage-clusters/manage-cluster.md#resize-a-cluster). :::note To increase the cluster size beyond the maximum 4x size, [reach out to Camunda](https://camunda.com/contact-us/). This requires custom sizing and pricing. ::: ## Free Trial clusters Free Trial clusters have the same functionality as a production cluster, but are of a Basic type and 1x size, and only available during your trial period. You cannot convert a Free Trial cluster to a different type of cluster. Once you sign up for a Free Trial, you are able to create one production cluster for the duration of your trial. When your Free Trial plan expires, you are automatically transferred to the Free plan. This plan allows you to model BPMN and DMN collaboratively, but does not support execution of your models. Any cluster created during your trial is deleted, and you cannot create new clusters. ### Auto-pause Free Trial clusters are automatically paused after a period of inactivity. Auto-pause occurs regardless of cluster usage. You can resume a paused cluster at any time, which typically takes five to ten minutes to complete. See [resume a cluster](/components/hub/organization/manage-clusters/manage-cluster.md#resume-a-cluster). - Clusters tagged as `dev` (or untagged) auto-pause eight hours after the cluster is created or resumed from a paused state. - Clusters auto-pause if there is no cluster activity for 48 hours. - Cluster disk space is cleared when a trial cluster is paused. - You will need to redeploy processes to the cluster once it is resumed from a paused state. - Cluster configuration settings (for example, API Clients, connector secrets, and IP allowlists) are saved so you can easily resume a cluster. :::tip To prevent auto-pause, [upgrade your Free Trial plan](https://camunda.com/pricing/) to an Enterprise plan. ::: --- ## Introduction to Camunda 8 Use [Camunda 8](https://camunda.io) to orchestrate and automate complex business processes that include people, AI agents, systems, and devices. ## About Camunda 8 You can deploy Camunda 8 in two ways: - **Camunda 8 SaaS**: A fully managed cloud service for rapid deployment and minimal operational overhead. - **Camunda 8 Self-Managed**: A self-hosted solution for organizations requiring full control over their infrastructure. Camunda 8 combines powerful execution engines for BPMN processes and DMN decisions with tools for collaborative modeling, operations, and analytics. Camunda 8 [components](/components/components-overview.md) work together to form the complete Camunda 8 experience, allowing you to design, automate, and improve your business processes. Camunda 8 separates runtime execution data from analytical and operational data by using distinct storage roles. :::note Storage architecture In the diagram above, storage systems appear in two distinct roles: - **Primary storage** — The authoritative store for runtime execution state used by the [Orchestration Cluster](/reference/glossary.md#orchestration-cluster) to execute, recover, and replicate workflows. This includes partition logs and snapshots and is tightly coupled to process execution. See [primary storage](/reference/glossary.md#primary-storage). - **Secondary storage** — Systems used for indexing, search, analytics, operational views, and long-term retention. Data is populated from primary storage and optimized for querying rather than execution. See [secondary storage](/reference/glossary.md#secondary-storage). ### Secondary storage implementations Camunda 8 supports multiple secondary storage backends, depending on the deployment model and configuration: - **Embedded H2** — A bundled secondary storage option for local development and lightweight setups. See [H2](/reference/glossary.md#h2). - **External RDBMS** — A user-managed relational database used as secondary storage in Self-Managed deployments. See [RDBMS](/reference/glossary.md#rdbms). - **Elasticsearch / OpenSearch** — Search-optimized backends commonly used for analytics and operational visibility. See [Elasticsearch/OpenSearch](/reference/glossary.md#elasticsearchopensearch). For deployment and configuration guidance, see the Self-Managed documentation: - [About Self-Managed](/self-managed/about-self-managed.md) - [Helm quick install](/self-managed/deployment/helm/install/quick-install.md) ::: :::info - Want to migrate your Camunda 7 process solutions to run on Camunda 8? See our [Camunda 7 migration guide](/guides/migrating-from-camunda-7/index.md). - Deployment guides for Camunda 8 components are available in the [Self-Managed section](/self-managed/about-self-managed.md). ::: ## Camunda 8 use cases With Camunda 8, you can model, execute, and operate complex business processes from end to end. Most real-world automation is distributed. A single business outcome (for example, customer onboarding, claims handling, or order fulfillment) often requires many independently deployed services and external systems. That makes it hard to keep the overall process visible, understand where work is waiting, and recover cleanly when something fails. Camunda provides a process orchestration layer that coordinates these endpoints without forcing you into a tightly-coupled architecture. You define process and decision logic using BPMN and DMN, then run it on a stateful, event-driven workflow engine designed for long-running, high-volume execution. This gives teams a consistent way to trigger work, correlate events, handle retries and compensation, and troubleshoot incidents across the full business process, not just within a single service. Many processes also require human input, either as a normal step (for example, review or approval) or as a fallback when automation can't proceed. With Camunda, you can model human tasks alongside automated steps, then assign, track, and escalate work so the process keeps moving. For example, if customer onboarding is blocked waiting for a verification task, the process can route to the right person, enforce due dates, and make the delay visible in operations tooling. Camunda also supports agentic orchestration. You can treat AI agents as process endpoints, just like a microservice or API call, and orchestrate them together with deterministic steps and human checkpoints. You can also build agents in Camunda by modeling agent behavior such as planning loops, tool use, and reflection, including short-term and long-term memory and retrieval-augmented generation (RAG). This makes agent actions observable and auditable, and it helps you combine dynamic agent decisions with the guardrails and policies your process requires, such as role-based access control, compliance boundaries, incident recovery capabilities, and audit trails. Common use cases include orchestrating microservices across complex integrations, modernizing long-running processes that cross legacy systems, coordinating human work with automation, and running AI-assisted steps (for example, document interpretation or decision support) inside governed, end-to-end processes. ## What are the core quality attributes of Camunda 8? Camunda 8 is designed to operate on a very large scale. To achieve this, it provides: - **Horizontal scalability** and no dependence on an external database; [Zeebe](/components/zeebe/zeebe-overview.md) (the workflow engine inside Camunda 8) writes data directly to the file system on the same servers where it is deployed. Zeebe enables distribution processing across a cluster of machines to deliver high throughput. - **High availability and fault tolerance** via a pre-configured replication mechanism, ensuring Camunda 8 can recover from machine or software failure with no data loss and minimal downtime. This ensures the system as a whole remains available without requiring manual action, which is particularly critical for AI-assisted processes, where every agent decision and action must be traceable end-to-end. - **Audit trail** as all process-relevant events are written to an append-only log, providing an audit trail and a history of the state of a process. - **Reactive publish-subscribe interaction model** which enables microservices that connect to Camunda 8 to maintain a high degree of control and autonomy, including control over processing rates. These properties make Camunda 8 resilient, scalable, and reactive. - **Visual processes modeled in ISO-standard BPMN 2.0** so technical and business stakeholders can collaborate on process design in a widely-used modeling language. - **Language-agnostic client model** makes it possible to build a client in nearly any programming language an organization uses to automate work. - **Operational ease of use** because, as a SaaS provider, we take care of all operational details. ## What are the Camunda 8 components? ### Modeler Design fully-executable process and decision models that reduce misalignment and handoff friction while giving engineers the freedom they need to build the right solution. Camunda Modeler gives business users an intuitive way to model processes and decisions using the BPMN and DMN standards so their intent is clear, structured, and directly usable by developers. Developers can take the model as-is and build scalable, flexible solutions without worrying about losing alignment with business intent. Available via [Camunda Hub](/components/hub/workspace/modeler/index.md) and a dedicated [desktop app](/components/modeler/desktop-modeler/index.md). #### Connectors Connectors communicate with any system or technology, reducing the time it takes to automate and orchestrate business processes. [Outbound connectors](/reference/glossary.md#outbound-connector) trigger events outside of Camunda, while [inbound connectors](/reference/glossary.md#inbound-connector) allow processes running on Camunda to receive messages from external systems. Connectors also serve as the tool layer for AI agents, enabling agents to interact with external systems in a governed, reusable way. Browse connectors in [Camunda Marketplace](https://marketplace.camunda.com/). #### AI agents Build [enterprise-grade AI agents](/components/agentic-orchestration/ai-agents.md) with guardrails so they can solve complex problems with autonomy. Camunda implements agentic BPMN that enables teams to model deterministic process logic and dynamic agentic behavior, such as reasoning loops, memory, prompts, RAG, and human‑in‑the‑loop boundaries, in one unified, executable model. #### Forms Some automated processes require human contribution and interaction. [Create and implement custom forms](/components/modeler/forms/utilizing-forms.md) that give work instructions, collect information, and help people make decisions about the tasks they need to complete. ### Tasklist [Tasklist](/components/tasklist/introduction-to-tasklist.md) offers a lightweight, user-friendly interface for human work, tightly integrated with custom forms. It provides an out-of-the-box user interface for tasks so teams can rapidly iterate on process development without having to build a custom frontend application. ### Workflow and decision engine [Zeebe](/components/zeebe/zeebe-overview.md) is a distributed workflow and decision engine that replaces a traditional relational database with an event streaming, message-based architecture. This approach eliminates database bottlenecks, ensures horizontal scalability, and provides built-in resilience. ### Operate With [Operate](/components/operate/operate-introduction.md), teams can monitor running processes, troubleshoot and resolve incidents, and modify and migrate process instances. Trace process flows in real time, investigate failures, modify variables, and resume execution where needed, all within the context of the end-to-end business process. For AI-assisted processes, Operate provides visibility into agent actions and decisions at runtime, enabling teams to detect and resolve unexpected behavior. ### Optimize [Optimize](/components/optimize/what-is-optimize.md) leverages process execution data to continuously [provide actionable insights](/components/optimize/improve-processes-with-optimize.md). Optimize specializes in BPMN-based analysis and can show users exactly what their process model needs for successful execution. ### Camunda Hub With [Camunda Hub](/components/hub/index.md), you'll manage organizational resources, analyze operations and business value, and deliver agentic processes at scale with Camunda Hub. ## How does Camunda 8 compare to other solutions? ### Composability Camunda's integrated yet flexible architecture facilitates best-of-breed technology and reuse. Combine Camunda task automation with other tools and custom code for maximum flexibility. Choose SaaS or Self-Managed, and use your preferred cloud, Kubernetes, and identity provider. ### Open architecture Integrate with a variety of applications, systems, and services to scale your architecture. Camunda works with your preferred source control, CI/CD pipelines, and programming languages, while well-documented APIs, SDKs, and polyglot clients enable easy integration and customization. ### IT and business collaboration IT and business stakeholders build and test processes in a shared modeling environment using the shared languages of BPMN and DMN. Reusable process assets such as connectors and AI agents make it easy to onboard new teams. ### Scalability Event streaming avoids database bottlenecks and can scale process throughput in a highly effective way. Camunda's distributed architecture ensures continuity in the case of hardware or network failure and supports replication across global data centers for high availability. ### Intelligence Camunda enables teams to embed AI agents into governed business processes, combining autonomous agent reasoning with the auditability and compliance controls that enterprise operations require. ## Next steps - To request information about Camunda 8 performance and benchmarking, refer to our [Contact](/reference/contact.md) page. - [Introduction to Camunda 8](/guides/introduction-to-camunda-8.md) - [Create a Camunda 8 account](/components/hub/organization/manage-organization-settings/manage-plan/create-account.md) - [Migrate from Camunda 7 to Camunda 8](/guides/migrating-from-camunda-7/index.md) - [Automate a process using BPMN](/components/modeler/bpmn/automating-a-process-using-bpmn.md) - [Build your first AI agent](/guides/getting-started-agentic-orchestration.md) --- ## Conditionals Conditional events allow a process to react automatically when variables match a condition. Instead of waiting for a message or signal, the engine evaluates a FEEL expression over process variables and triggers the event when the expression becomes `true`. Conditional events are similar to [messages](messages.md) and [signals](signals.md), but they are triggered by variable changes instead of external correlation or broadcast. For details on which BPMN event types support conditionals and how to model them, see the [conditional events modeling guide](../modeler/bpmn/conditional-events/conditional-events.md). ## Conditional event triggers When a conditional event scope is activated, the engine creates a subscription. It evaluates the condition whenever relevant variables change within the event’s visible scope. If the condition becomes `true` while the event is active and in scope, the event triggers and the process follows the behavior defined by the underlying BPMN event. The following sections describe how conditional events are evaluated and triggered, including how variable filters affect re-evaluation. The semantics described below apply to conditional events within running process instances. For process-level conditional start events, see [trigger root-level conditional start events via API](#trigger-root-level-conditional-start-events-via-api). ### On scope activation When a scope is activated, the engine evaluates the condition for conditional events in that scope. For example, when an activity with an attached conditional boundary event is activated, the engine evaluates the condition for that boundary event immediately at activation. Consider the following process definition: An interrupting conditional boundary event with condition `x > 10` is attached to the service task A. Assume the process instance starts with variable `x` initialized to `11`. When the start event completes and service task A is activated, the boundary condition evaluates to `true`, and the boundary event triggers immediately. Service task A will be terminated and the created job will be canceled. The process instance will continue with the flow after the boundary event, skipping the service task A. ### On variable changes In addition to scope activation, conditional events can also be triggered when relevant variables change within the event’s visible scope. When a variable changes (for example, it is created or updated), the engine evaluates the condition for any active conditional events in the variable's scope and all child scopes whose condition depends on that variable (see [top-down evaluation](#top-down-evaluation)). Given the following process definition: An event sub-process with a conditional start event is defined with condition `x > 10`. Assume the process instance starts with variable `x` initialized to `5`. The main process starts and executes the service task, while the event sub-process is not triggered because the condition is not satisfied. If the process instance updates variable `x` to `11` (for example, via the [update element instance variables API](../../apis-tools/orchestration-cluster-api-rest/specifications/create-element-instance-variables.api.mdx)), the engine evaluates the condition for the conditional start event in the event sub-process. Since the condition is now `true`, the conditional start event triggers and the event sub-process starts. #### Top-down evaluation When a variable changes, the engine evaluates conditions in a top-down order based on scopes. Starting from the scope where the variable changed, the engine evaluates conditions for any active conditional events in that scope. It then evaluates conditions in child scopes, continuing down the hierarchy. This continues until a triggered event interrupts one of the scopes or there are no more child scopes to evaluate. Given the following process definition: If a variable is set within the scope of the root process instance or sub-process instance, the engine evaluates the sub-process’s conditional boundary event first. If the condition is satisfied, execution is interrupted; otherwise, the engine evaluates the conditional boundary event on the inner service task A and triggers it if its condition is satisfied. #### Scope isolation A variable change can trigger only the conditional events that can see that variable in the current scope or one of its child scopes. Unrelated scope instances are not affected. Given the following process definition: Service task A and service task B are active in parallel branches of the process. If a variable is set in the sub-process instance, then only the conditional boundary event on service task A is evaluated. The boundary event on service task B cannot trigger because the variable is not visible in its scope. See [variable scopes](variables.md#variable-scopes) for more details on variable visibility rules. #### Expression-based evaluation When a conditional event is activated, the engine analyzes its FEEL condition and derives which variables the expression depends on. The subscription is re-evaluated only when one of those referenced variables changes within the event’s visible scope. The following rules apply to condition expressions: - Condition `x > 1` is re-evaluated only when variable `x` changes. - Condition `x > 1 and y < 5` is re-evaluated when either variable `x` or variable `y` changes. - Condition `x.y.z = true` is re-evaluated when variable `x` changes, because the expression depends on the value of `x` (even if it accesses a property of `x`). :::warning Use plain FEEL expressions instead of nested variable access (for example, `x > 1` instead of `x.value > 1`). This helps the engine derive variable dependencies correctly and re-evaluate the condition when relevant variables change. If you use nested variable access, the engine treats the entire variable as a dependency. For example, if the condition is `x.value > 1`, the engine treats `x` as a dependency. The condition is re-evaluated every time `x` changes, even if the change does not affect `value`. This can lead to unintended triggers for non-interrupting conditional events if variable `x` is updated frequently. ::: #### Variable filter semantics Conditional events can define variable filters to limit when the engine re-evaluates the condition. By default, the engine derives the set of variables that can trigger an event from the FEEL expression. The subscription is re-evaluated only when one of those referenced variables changes within the event’s visible scope. See how filters can be defined in the [conditional events modeling guide](../modeler/bpmn/conditional-events/conditional-events.md#variable-filters). Variable filters restrict evaluation based on specific variable change types (for example, `create` or `update`). Variable change type filters apply only to conditional events within a running process instance. They do not apply to root-level conditional start events, because no process instance exists yet. #### Considerations Conditional events enable reactive process behavior, but incorrect modeling can lead to unintended triggers. Keep the following behavior in mind. ##### Single evaluation per state update If a single command (for example, an update variables request or job completion) updates multiple variables and more than one satisfies the condition, the subscription triggers only once. Using the process definition shown earlier (a service task with an interrupting conditional boundary event), assume the boundary condition is `x > 10 or y > 5`. If both variables `x` and `y` are updated in a single request (for example, via the [update element instance variables API](../../apis-tools/orchestration-cluster-api-rest/specifications/create-element-instance-variables.api.mdx)) and both satisfy the condition, the boundary event triggers only once. ##### Input mappings behavior If an activity defines an input mapping that sets a variable satisfying the boundary condition, the conditional boundary event can trigger when the activity is activated. ##### Output mappings behavior If an activity defines an output mapping that sets a variable satisfying the boundary condition, the conditional boundary event does not trigger when the activity completes. The engine removes the boundary subscription when the activity completes. Variables written during completion are applied after the subscription is removed. Using the same process definition shown earlier (a service task with an interrupting conditional boundary event), if service task A completes and sets variable `x` to `11`, the boundary event with condition `x > 10` does not trigger because the subscription has already been removed. ##### Multi-instance behavior The behavior of conditional boundary events depends on where the variable is updated. - With an interrupting conditional boundary event on the multi-instance body, a child instance that updates a variable and satisfies the condition can interrupt the entire multi-instance activity. - With a non-interrupting conditional boundary event on the multi-instance body, each child instance that satisfies the condition triggers its own boundary event without interrupting the multi-instance body. - A local-scope variable update on a multi-instance child triggers the boundary event only for that child instance. Other child instances continue unaffected. - A process-scope variable update that is visible to all multi-instance children triggers boundary events on all active child instances whose conditions evaluate to `true`. ## Trigger root-level conditional start events via API You can evaluate root-level conditional start events by using: - The Orchestration Cluster REST API. See [evaluate root-level conditional start events](../../apis-tools/orchestration-cluster-api-rest/specifications/evaluate-conditionals.api.mdx). - The [Zeebe Gateway](/reference/glossary.md#zeebe-gateway) gRPC API. See [`EvaluateConditional` RPC](../../apis-tools/zeebe-api/gateway-service.md#evaluateconditional-rpc). - Client SDKs. For example, use `newEvaluateConditionalCommand()` in the Java client. When you evaluate conditional start events: - Multiple conditional start events in the same process definition can trigger if their conditions evaluate to `true`, which can start multiple process instances from the same definition. - If you do not specify a `processDefinitionKey`, the engine evaluates all root-level conditional start events across all deployed process definitions and starts instances for those whose conditions evaluate to `true`. These APIs apply only to root-level conditional start events. Conditional events inside running process instances are evaluated automatically when variables change. ### Migration from Camunda 7 In Camunda 7, conditional events use the `camunda:variableName` and `camunda:variableEvents` attributes. In Camunda 8, the FEEL condition expression is the single source of truth for which variables can trigger the event. The `camunda:variableName` attribute is not supported, so the engine derives variable dependencies directly from the FEEL expression. During migration, `camunda:variableEvents` is converted into a `variableEvents` filter configuration where applicable. Camunda 8 supports only `create` and `update`. If a model uses `delete` in Camunda 7, migration maps it to the closest supported behavior. --- ## Decision instance deletion Use decision instance deletion to permanently remove all data associated with a decision evaluation instance. :::warning Deletion is irreversible. Restore deleted data only by restoring a backup of your cluster. ::: - Delete a single decision instance using the [delete decision instance endpoint](/apis-tools/orchestration-cluster-api-rest/specifications/delete-decision-instance.api.mdx). - Delete multiple decision instances using the [delete decision instances endpoint](/apis-tools/orchestration-cluster-api-rest/specifications/delete-decision-instances-batch-operation.api.mdx). ## Eventual consistency Decision instance deletion runs asynchronously. Depending on how many decision instances are deleted, it may take time for the data to be removed and for the decision instance to disappear from Operate. ## Technical details This section explains how decision instance deletion is handled internally to clarify timing and consistency behavior. Deleting one or more decision instances uses [batch operations](./batch-operations.md). The Zeebe engine queries [secondary storage](/self-managed/concepts/secondary-storage/index.md) for decision instances to delete. For each instance found, the engine writes a delete command to the log, which produces a deleted event. Exporters consume the deleted event and write a record to secondary storage marking the decision instance for deletion. An asynchronous scheduled task then deletes all data associated with each marked decision instance. ```mermaid sequenceDiagram V2 API->>+Engine: Delete decision instances with filter Engine->>Engine: Create batch operation Engine->>-V2 API: Batch operation create response Engine->>+Secondary storage: Query decision instances activate Engine Secondary storage->>-Engine: Return decision instance keys loop for each decision instance key Engine->>Engine: Write DELETED event for each decision instance key Engine->>+Exporter: Export DELETED event deactivate Engine Exporter->>-Secondary storage: Mark decision instance for deletion end note over V2 API,Deletion job: Everything below this note happens asynchronously. Deletion job->>+Secondary storage: Retrieve marked decision instance keys activate Deletion job Secondary storage->>-Deletion job: Return decision instance keys loop for each decision instance key Deletion job->>Deletion job: Delete associated data end deactivate Deletion job ``` --- ## Element templates An **element template** extends the [Modeler](../modeler/about-modeler.md) with domain-specific diagram elements, such as service and user tasks. They allow you to customize how a BPMN element is displayed and how it can be configured by process developers. The example below shows how a generic service task can be transformed into a customized user interface that guides users through its configuration: | Without an element template | With an element template | | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | | ![Service task without an element template](assets/element-templates/service-task-no-template.svg) | ![Service task with an element template](assets/element-templates/service-task-with-template.svg) | | | | :::tip [Connector templates](../connectors/custom-built-connectors/connector-templates.md) are a specific type of element template. ::: ## Next steps Read more about element templates and how to use them: - [Element templates in Modeler](/components/modeler/element-templates/about-templates.md) - [Using element templates in Web Modeler](/components/hub/workspace/modeler/element-templates/using-templates.md) - [Using element templates in Desktop Modeler](/components/modeler/desktop-modeler/element-templates/using-templates.md) --- ## Execution listeners An execution listener (EL) allows users to react to various events in the workflow execution lifecycle by executing custom logic. ## About execution listeners You can use execution listeners to provide flexibility and control over process execution, and handle complex data and external system interactions without cluttering the BPMN model with technical details. ### Use cases Execution listeners are useful in the following typical cases: - Pre- and post-processing actions for activities - External calculations of variables for element expressions - Decoupled processes and data synchronization ### Blocking behavior An execution listener is a blocking operation, meaning that the workflow execution lifecycle only continues once the listener is completed. This ensures that all necessary pre- and post-processing actions defined by the listener are fully executed before the workflow proceeds to the next element. ## Define an execution listener You can configure execution listeners for individual BPMN elements, such as tasks, events, and gateways, as well as for the overall process and subprocesses. There are four types of execution listeners: - **Before all**: Invoked only on the [multi-instance](/components/modeler/bpmn/multi-instance/multi-instance.md) body, _before_ any inner instances are created. Useful for initializing variables such as the `inputCollection`. - **Start**: Invoked _before_ the element is processed. Useful for setting variables or executing preconditions. - **End**: Invoked _after_ the element is processed. Useful for executing cleanup or post-processing tasks. - **Cancel**: Invoked on the process element _when the process instance is terminated_. Useful for cleanup, audit logging, or notifying external systems that the process did not complete. Each listener has three properties: | Property | Description | | :---------- | :----------------------------------------------------------------------------------- | | `eventType` | Specifies when the listener is triggered (`beforeAll`, `start`, `end`, or `cancel`). | | `type` | The name of the job type. | | `retries` | The number of job retries. | :::note If multiple listeners of the same `eventType` (such as multiple start listeners) are defined on the same activity, they are executed sequentially in the order defined in the BPMN model. ::: ## Task headers An execution listener can define an arbitrary number of `taskHeaders`; they are static metadata handed to workers along with the job. The headers can be used as configuration parameters for the worker. The job worker receives the listener headers, as well as the custom headers defined for the BPMN element on which the listener is configured. If the BPMN element and the listener both define the same header key, the listener value is used. ## Implement an execution listener Execution listeners are processed by [job workers](/components/concepts/job-workers.md). - Listeners are based on the same concept of jobs and use the same protocol. - You can implement a handler for an execution listener just as you would for a regular job. See the [job worker documentation](/apis-tools/java-client/job-worker.md) for examples of how to create a job worker and handler that can also process execution listener jobs. :::note [Throwing a BPMN error](/components/best-practices/development/dealing-with-problems-and-exceptions.md#throwing-and-handling-bpmn-errors) for an execution listener's job is not supported. ::: ## Variables in an execution listener Similar to regular job workers, a listener can read variables of the process instance and set new variables by completing the job with variables. The scope of variables and the effect of the job variables depend on the listener event type. ### `beforeAll` listeners `beforeAll` listeners are supported only on the [multi-instance](/components/modeler/bpmn/multi-instance/multi-instance.md) body. They are invoked once per multi-instance body activation, before the `inputCollection` is evaluated and inner instances are created. When a multi-instance activity is entered, the engine processes the body and inner-activity listeners in the following order: 1. Variable input mappings of the multi-instance body are applied, if any. 2. All `beforeAll` body listeners are executed sequentially, in the order defined in the BPMN model. 3. The body's `inputCollection` expression is evaluated. 4. Inner instances are created, sequentially or in parallel, depending on the multi-instance configuration. 5. For each inner instance, the existing [Start listeners](#start-listeners) and [End listeners](#end-listeners) of the inner activity are invoked as usual. You can use variables for the following use cases: | Use case | Description | | :------------------------------- | :-------------------------------------------------------------------------------------------------------------------------- | | Dynamic collection | Compute the `inputCollection` just before multi-instance evaluation, based on current process state or external data. | | Resolving identifiers into items | Resolve IDs into concrete items and write them into the collection variable that the multi-instance activity iterates over. | | Pre-initializing shared context | Set helper variables used by multi-instance expressions and the body's `completionCondition`. | ### `start` listeners Start listeners are invoked after applying the variable input mappings, and before subscribing to events, evaluating the element's expressions, and executing the element's behavior. - A start listener can read the process variables and local variables created by the variable input mappings. - If a start listener completes the job with variables, those variables are set as [local variables](/components/concepts/variables.md#local-variables) for the element. Subsequent listeners can access these variables. You can use variables for the following use cases: | Use case | Description | | :------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Gateways | [Inclusive](/components/modeler/bpmn/inclusive-gateways/inclusive-gateways.md), [exclusive](/components/modeler/bpmn/exclusive-gateways/exclusive-gateways.md), [event-based gateways](/components/modeler/bpmn/event-based-gateways/event-based-gateways.md): Use ELs to calculate and set variables that determine the outgoing path from these gateways. The custom logic executed by ELs can evaluate current data and set the necessary variables to guide the process flow correctly. | | Intermediate catch events | [Message events](/components/modeler/bpmn/message-events/message-events.md#intermediate-message-catch-events): Variables set by ELs can be used to define the message correlation key, ensuring the correct message is matched with the event.[Timer events](/components/modeler/bpmn/timer-events/timer-events.md#intermediate-timer-catch-events): ELs can define timer expressions based on the calculated variables, enabling dynamic timer configurations.[Signal events](/components/modeler/bpmn/signal-events/signal-events.md#signal-intermediate-catch-events): Variables can determine the signal name, allowing for flexible signal handling based on the current process state. | ### `end` listeners End listeners are invoked after applying the variable output mappings and before leaving the element. - An end listener can read the process variables, the local variables of the element, and the resulting variables of the output mappings. - If an end listener completes the job with variables, those variables are propagated to the element's parent scope, like variables from the output mappings. Subsequent listeners can access these variables. ### `cancel` listeners Cancel listeners run when a process instance is terminated. They execute sequentially after all child elements have terminated and before the process reaches its final terminated state. - A cancel listener can read the process variables. If a cancel listener completes the job with variables, the variables are merged into the process scope and visible to subsequent cancel listeners. ## Incident recovery During execution listener processing, issues can arise that lead to [incidents](/components/concepts/incidents.md). For example, these incidents can occur due to job execution failures or problems during expression evaluation. ### Job execution failure If an execution listener job fails (for example, if an external service is unavailable), it is retried according to the `retries` property. If all retries are exhausted and the job still fails, the process halts, and an incident is raised. Once the incident is resolved, only the listener with the failed job is retried, allowing the process to resume from the point of failure without re-executing successfully completed listeners. ### Expression evaluation failure Incidents can also occur during the evaluation of an execution listener's properties (for example, due to incorrect variable mapping or expression syntax). If this happens, all listeners of the same event type (`start`, `end`, or `cancel`) that were processed before the failure are re-executed once the issue is resolved, even if they had previously completed successfully. ## Limitations Execution listeners have the following limitations: - **Unsupported elements**: The following elements do not support `start` or `end` listeners due to their processing nature: - Start events (start ELs): Use `start` listeners of process instances or subprocesses to cover the missing `start` listeners for specific start events. - Boundary events (start ELs): Place the start logic in the `start` ELs of the main activity to which the boundary event is attached. - Gateways (end ELs): Use `start` ELs on the element following the gateway to execute the required logic. This allows handling of any post-execution tasks in a dedicated element. - Error end event (end ELs): Place the ELs on the related error catch event. - Compensation boundary events: Place the ELs on the compensation handler. - **`beforeAll`**: Supported only for multi-instance activities. - Earlier versions do not support the `beforeAll` event type and will reject deployments that use it. - **`cancel`**: Supported only on the process element. - Earlier versions do not support the `cancel` event type and will reject deployments that use it. - **Duplicate listeners**: Execution listeners must have unique combinations of `eventType` and `type`. Defining multiple listeners with the same `eventType` and `type` results in a validation error. However, you can define listeners with the same `type` if they use different `eventType` values. - **Interrupting escalation events**: For intermediate throw and end events with an interrupting escalation event, `end` listeners are not executed. The escalation event terminates the element's processing immediately upon activation, bypassing any defined `end` listeners. - **Throwing a BPMN error**: This operation is not supported for execution listener jobs. ## Related resources - [Variables](/components/concepts/variables.md) - [Expressions](/components/concepts/expressions.md) - [Incidents](/components/concepts/incidents.md) - [Job workers (basics)](/components/concepts/job-workers.md) - [Job workers (Java client)](/apis-tools/java-client/job-worker.md) - [Multi-instance](/components/modeler/bpmn/multi-instance/multi-instance.md) --- ## Expressions Expressions can be used to access variables and calculate values dynamically. This is particularly useful when [automating a process using BPMN](/components/modeler/bpmn/automating-a-process-using-bpmn.md) and [orchestrating human tasks](../../guides/getting-started-orchestrate-human-tasks.md). Some attributes of BPMN elements _require_ an expression, for example, a [sequence flow condition](/components/modeler/bpmn/exclusive-gateways/exclusive-gateways.md#conditions) on an exclusive gateway. Other attributes can define an expression _optionally_ as an alternative to a static value, for example, a [timer definition](/components/modeler/bpmn/timer-events/timer-events.md#timers) of a timer catch event. ## Expressions vs. static values Some attributes of BPMN elements, like the timer definition of a timer catch event, can be defined in one of two ways: - As an expression (e.g. `= remainingTime`) - As a static value (e.g. `PT2H`) Expressions always start with an **equals sign** (**=**). For example, `= order.amount > 100`. The text following the equal sign is the actual expression. For example, `order.amount > 100` checks if the amount of the order is greater than 100. If the element does not start with the prefix, it is used as a static value. A static value is used either as a string (e.g. job type) or as a number (e.g. job retries). A string value must not be enclosed in quotes. :::note An expression can also define a static value by using literals (e.g. `= "foo"`, `= 21`, `= true`, `= [1,2,3]`, `= {x: 22}`, etc.) ::: ## The expression language An expression is written in **Friendly Enough Expression Language (FEEL)**. FEEL is part of the OMG's **Decision Model and Notation (DMN)** specification. It is designed to have the following properties: - Free of side effects - Simple data model with JSON-like object types: numbers, dates, strings, lists, and contexts - Syntax designed for business professionals and developers - Three-valued logic (true, false, null) Camunda 8 integrates the [FEEL Scala](https://github.com/camunda/feel-scala) engine to evaluate FEEL expressions. ## Evaluation timeout Use evaluation timeouts to prevent long-running FEEL expressions from blocking processing. Some FEEL expressions may take a long time to evaluate, especially in the following cases: - Expressions with exponential complexity, such as recursive operations without proper bounds - Expressions that process very large input data sets By default, expression evaluation times out after five seconds. You can [configure this timeout](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#expression). If an expression exceeds the timeout, evaluation is interrupted and an incident is raised for the affected process instance. ## Next steps Read more about FEEL expressions and how to use them on the following pages: - [Data types](/components/modeler/feel/language-guide/feel-data-types.md) - [Expressions and operators](/components/modeler/feel/language-guide/feel-expressions-introduction.md) - [Available built-in functions](/components/modeler/feel/builtin-functions/feel-built-in-functions-introduction.md) - [FEEL](/components/modeler/feel/what-is-feel.md) - [DMN specification](https://www.omg.org/spec/DMN/About-DMN/) --- ## Access control for global user task listeners Global user task listeners are managed through the Orchestration Cluster authorization model. This page lists the permissions required to manage listeners through the Orchestration Cluster REST API and Admin UI. ## When you need to configure permissions Configure permissions for global user task listeners if all of the following apply: - [Authorizations are enabled for the cluster](/components/concepts/access-control/authorizations.md#configuration). - You manage global user task listeners through one of the following: - The [Orchestration Cluster API](./configuration.md#configure-via-orchestration-cluster-api), or - The [Admin UI](./configuration.md#configure-via-admin-ui). You do not need additional Orchestration Cluster authorizations when: - Defining listeners via [Unified Configuration](./configuration.md#configure-through-unified-configuration). - You only execute processes that are already affected by global listeners. Execution-time behavior is not guarded by separate permissions. ## Required permissions Global user task listeners use the `GLOBAL_LISTENER` resource type in the Orchestration Cluster authorization model. Only the wildcard resource ID `*` is supported. Authorizations for specific listener IDs are not evaluated. To allow a user, group, role, or client to manage listeners through the Orchestration Cluster API or the Admin UI, grant authorizations on `GLOBAL_LISTENER` with resource ID `*` and the following permissions: | Operation | Required permission | Related API endpoint | | :------------------------------------------- | :--------------------- | :---------------------------------------------------------------------------------------------------------------------------------- | | List or search global user task listeners | `READ_TASK_LISTENER` | [Search global user task listeners](/apis-tools/orchestration-cluster-api-rest/specifications/search-global-task-listeners.api.mdx) | | View a single global user task listener | `READ_TASK_LISTENER` | [Get global user task listener](/apis-tools/orchestration-cluster-api-rest/specifications/get-global-task-listener.api.mdx) | | Create a new global user task listener | `CREATE_TASK_LISTENER` | [Create global user task listener](/apis-tools/orchestration-cluster-api-rest/specifications/create-global-task-listener.api.mdx) | | Update an existing global user task listener | `UPDATE_TASK_LISTENER` | [Update global user task listener](/apis-tools/orchestration-cluster-api-rest/specifications/update-global-task-listener.api.mdx) | | Delete an existing global user task listener | `DELETE_TASK_LISTENER` | [Delete global user task listener](/apis-tools/orchestration-cluster-api-rest/specifications/delete-global-task-listener.api.mdx) | --- ## Configure global user task listeners You can configure global user task listeners at the cluster level: - [Through the Unified Configuration](#configure-through-unified-configuration). - [Via the Orchestration Cluster API](#configure-via-orchestration-cluster-api). - [Via the Admin UI](#configure-via-admin-ui), which uses the Orchestration Cluster API. Use the Unified Configuration if: - You want to define a static set of global listeners that are always active in the cluster. - You want to use versioning tools to keep track of configuration changes. Use the Orchestration Cluster API if: - You want to dynamically manage listeners without restarting the cluster. - You want to manage permissions for who can create, update, or delete global listeners through API access control. You can use both methods at the same time. After a cluster restart: - Listeners defined through Unified Configuration are recreated, even if they were deleted through the API before restart. - API-defined listeners remain active in addition to configuration-defined listeners. - If an `id` conflict occurs, the listener from Unified Configuration takes precedence and replaces the API-defined listener. ## Configure through Unified Configuration Configure global user task listeners through [Unified Configuration](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#camundaclusterglobal-listeners) under `camunda.cluster.global-listeners.user-task`. Each listener entry supports the properties in [global listener definition](/components/concepts/global-user-task-listeners.md#global-listener-definition), except `source`, which is automatically set to `CONFIGURATION`. Any provided `source` value is ignored. ### How the configuration is validated If configuration entries are invalid, the system rectifies them instead of failing startup. The following validation rules are applied automatically on startup: - If a listener is missing the required `id`, `type`, or `event-types` properties, it is removed. - If a listener defines invalid event types, those event types are removed. If all event types are invalid, the listener is removed. - Valid event types are: `creating`, `assigning`, `updating`, `completing`, `canceling`, or the special value `all`. Event type matching is case-insensitive, so `Creating` and `creating` are both valid, but `create` is not. - If a listener defines duplicate event types, the duplicates are removed and only one instance of each event type is kept. - If a listener defines both the special `all` value and a normal event type for `event-types`, the configuration is corrected to include only `all`. This ensures the listener catches all events as intended. - If a listener defines invalid retry values (non-numeric, negative, or zero), it is removed. Valid retry values are positive integers. - If a listener defines an invalid `priority`, it is removed. Valid priorities are integers from `0` to `100`. In all these cases, the Orchestration Cluster writes a startup warning that identifies the problem location. Invalid listeners are removed while valid listeners remain active. :::note Listeners defined through the API are not subject to this validation. ::: ### Example configuration The following is an example YAML configuration and environment variables: ```yaml camunda: cluster: global-listeners: user-task: - id: "validation-listener" type: "validate-task" event-types: - creating priority: 70 - id: "audit-listener" type: "audit-generic" event-types: all retries: 5 priority: 50 - id: "notification-listener" type: "notify-assignee" event-types: - assigning - updating - canceling after-non-global: true priority: 30 ``` ```bash CAMUNDA_CLUSTER_GLOBAL_LISTENERS_USER_TASK_0_ID=validation-listener CAMUNDA_CLUSTER_GLOBAL_LISTENERS_USER_TASK_0_TYPE=validate-task CAMUNDA_CLUSTER_GLOBAL_LISTENERS_USER_TASK_0_EVENT_TYPES_0=creating CAMUNDA_CLUSTER_GLOBAL_LISTENERS_USER_TASK_0_PRIORITY=70 CAMUNDA_CLUSTER_GLOBAL_LISTENERS_USER_TASK_1_ID=audit-listener CAMUNDA_CLUSTER_GLOBAL_LISTENERS_USER_TASK_1_TYPE=audit-generic CAMUNDA_CLUSTER_GLOBAL_LISTENERS_USER_TASK_1_EVENT_TYPES_0=all CAMUNDA_CLUSTER_GLOBAL_LISTENERS_USER_TASK_1_RETRIES=5 CAMUNDA_CLUSTER_GLOBAL_LISTENERS_USER_TASK_1_PRIORITY=50 CAMUNDA_CLUSTER_GLOBAL_LISTENERS_USER_TASK_2_ID=notification-listener CAMUNDA_CLUSTER_GLOBAL_LISTENERS_USER_TASK_2_TYPE=notify-assignee CAMUNDA_CLUSTER_GLOBAL_LISTENERS_USER_TASK_2_EVENT_TYPES_0=assigning CAMUNDA_CLUSTER_GLOBAL_LISTENERS_USER_TASK_2_EVENT_TYPES_1=updating CAMUNDA_CLUSTER_GLOBAL_LISTENERS_USER_TASK_2_EVENT_TYPES_2=canceling CAMUNDA_CLUSTER_GLOBAL_LISTENERS_USER_TASK_2_AFTER_NON_GLOBAL=true CAMUNDA_CLUSTER_GLOBAL_LISTENERS_USER_TASK_2_PRIORITY=30 ``` ### Apply changes Configuration changes take effect after you restart the cluster. Use rolling restarts to avoid downtime. After the restart, the new configuration applies to new lifecycle events for both running and new instances, without requiring you to redeploy models. ## Configure via Orchestration Cluster API :::note You need [specific authorizations](./access-control.md) to manage global listeners through the API. Read more about [authorizations](/components/concepts/access-control/authorizations.md) and [how to create them in the Admin UI](/components/admin/authorization.md). ::: The [Orchestration Cluster API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) provides CRUD operations to manage global user task listeners at runtime. This allows you to create, update, and delete listeners without restarting the cluster. When you create or update a listener through the API, provide the properties described in [global listener definition](/components/concepts/global-user-task-listeners.md#global-listener-definition). The `source` property is set automatically to `API`. Changes take effect immediately after the API call for new lifecycle events on running and new instances, without requiring model redeployments or a cluster restart. ## Configure via Admin UI :::tip To learn more about the Admin UI for global user task listeners, see the [dedicated page](/components/admin/global-user-task-listeners.md). ::: You can configure global listeners in the Admin UI through the **Global Task Listeners** tab. You can update and delete listeners in the Admin UI without interacting with the API directly or restarting the cluster. The Admin UI uses the Orchestration Cluster API to manage global user task listeners, so the same considerations apply. ## See also - [Global listener configuration properties](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#camundaclusterglobal-listeners). - [Configure properties through Helm charts](/self-managed/deployment/helm/configure/application-configs.md). - [Orchestration Cluster API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). - [Access control for global user task listeners](./access-control.md). --- ## Global user task listeners(Concepts) Global user task listeners are [user task listeners](/components/concepts/user-task-listeners.md) defined once for all processes in a cluster, instead of individually per [user task](/components/modeler/bpmn/user-tasks/user-tasks.md). ## About global user task listeners Use global listeners to react to user task lifecycle events across all processes without modifying BPMN models. Global listeners are configured at the cluster level and behave like model-level user task listeners, using the same lifecycle events, blocking behavior, deny/correction semantics, payload structure, and incident handling. They are particularly useful for: - Replicating user task changes and context to external systems, such as audit, analytics, or custom Tasklist apps. - Centralizing Service Level Agreements and notifications across all processes. - Enforcing governance rules and validations. For example, pre-completion checks. - Consistently applying due date and priority policies. ## Global listener definition Each listener is defined by the following properties: | Property | Required | Description | | :--------------- | :--------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | Yes | User-provided unique identifier for the listener. This identifier is used to interact with the global listener through API | | `eventTypes` | Yes | List of user task event types that trigger the listener.Supported values: `creating`, `assigning`, `updating`, `completing`, `canceling`.The shorthand `all` value is also available if the listener should react to all lifecycle events. | | `type` | Yes | The name of the job type.Used as a reference to specify which job workers request the respective task listener job. For example, `order-items`. | | `retries` | No | Number of retries for the user task listener job. Defaults to `3` if not set. | | `afterNonGlobal` | No | Boolean indicating whether the listener should run after model-level listeners. Defaults to `false` (runs before model-level listeners). | | `priority` | No | The priority of the listener. Higher priority listeners are executed before lower priority ones. It must be an integer between 0 and 100. Defaults to `50` if not set. | | `source` | No (automatically set) | Indicates how the listener was defined, either through configuration or API.Supported values: `CONFIGURATION` and `API`.This property is automatically set by the system and cannot be modified by users. | You have to use a different `id` for each configured listener, since this property is used to uniquely identify the listener and interact with it through the Orchestration Cluster API, for example, to update or delete the listener. You can use the same `type` value for multiple listeners if they should be handled by the same job workers. The `source` property only distinguishes how the listener was defined, but it has no practical effect in how the global listeners are executed. You can configure global user task listeners in multiple ways, as described in [configure global user task listeners](./global-user-task-listeners/configuration.md): - Through the Unified Configuration. - Via the Orchestration Cluster API. - Via the Admin UI. ## Execution order For a given event on a task instance: - Global listeners run in the order defined by the `priority` property of each listener. - Listeners with a higher priority are executed first. - Listeners with the same priority are sorted by their `id` in lexicographical order to ensure a deterministic execution order. - Model-level listeners run next, in the order defined in the BPMN model. - Global listeners marked with `after-non-global: true` (Unified Configuration) or `afterNonGlobal: true` (API) run after model-level listeners. ## Supported features Global listeners support the same features as model-level user task listeners: - [Blocking behavior](/components/concepts/user-task-listeners.md#blocking-behavior). - [Triggering on a specific lifecycle event](/components/concepts/user-task-listeners.md#trigger-a-user-task-listener). - [Accessing user task data in job workers](/components/concepts/user-task-listeners.md#accessing-user-task-data), in particular: - Task-specific data, such as, assignee, due date, or priority. - Attributes changed by the event, either through an `updating` event or because of corrections done by previous listeners. - Headers defined in the user task model. - [Correcting user task data](/components/concepts/user-task-listeners.md#correcting-user-task-data). - [Denying lifecycle transitions](/components/concepts/user-task-listeners.md#denying-the-lifecycle-transition). - [Incident recovery](/components/concepts/user-task-listeners.md#incident-recovery). Additionally, you can configure a single global listener to be triggered by multiple lifecycle events, possibly all of them. ## Limitations The [same limitations as model-level user task listeners](/components/concepts/user-task-listeners.md#limitations) apply. In addition to the above: - **No tenant-specific configuration**: Configuration is cluster-wide, not per tenant. Payloads include `tenantId` for downstream handling. - **Restart required**: Changes made through Unified Configuration take effect only after a cluster restart. This limitation does not apply when you manage listeners through the Orchestration Cluster API. --- ## Incidents In Camunda 8, an [incident](/reference/glossary.md#incident) represents a problem in process execution. This means a [process instance](/reference/glossary.md#process-instance) is stuck at a particular point and requires user interaction to resolve the problem. Incidents are created in different situations, including the following: - A job is failed and it has no retries left. - A condition doesn't return `true` or `false`. - A timer expression doesn't return the expected type. - A decision can't be evaluated. - A BPMN error is thrown and not caught by an error boundary event or error event subprocess. :::note Not all errors necessarily lead to incidents. For example, unexpected errors in Zeebe do not always result in incidents. ::: ## Resolving To resolve an incident, complete the following steps: 1. Identify and resolve the problem. 2. Mark the incident as resolved, triggering retry process execution. 3. If the problem still exists, a new incident is created. ### Resolving a job-related incident If a job fails and has no retries remaining, an incident is created. There are many different reasons why the job may have failed. For example, the variables may not be in the expected format, or a service is not available (e.g. a database). If the variables are causing the incident, complete the following steps: 1. Update the variables of the process instance. 2. Increase the remaining retries of the job. 3. Mark the incident as resolved. :::note It's recommended you complete these operations in [Operate](/components/operate/operate-introduction.md). ::: It is also possible to complete these steps via the [client API](/apis-tools/working-with-apis-tools.md). Using the Java client, this could look like the following: ```java client.newSetVariablesCommand(incident.getElementInstanceKey()) .variables(NEW_PAYLOAD) .send() .join(); client.newUpdateRetriesCommand(incident.getJobKey()) .retries(3) .send() .join(); client.newResolveIncidentCommand(incident.getKey()) .send() .join(); ``` When the incident is resolved, the job can be activated by a worker again. ### Resolving a process instance-related incident If an incident is created during process execution and it's not related to a job, the incident is usually related to the variables of the process instance. For example, a condition expression doesn't return a boolean value. To resolve the incident, update the variables and mark the incident as resolved. :::note It's recommended you complete these operations in [Operate](/components/operate/operate-introduction.md). ::: Using the Java client, this could look like the following: ```java client.newSetVariablesCommand(incident.getElementInstanceKey()) .variables(NEW_VARIABLES) .send() .join(); client.newResolveIncidentCommand(incident.getKey()) .send() .join(); ``` When the incident is resolved, the process instance continues. --- ## Job workers(Concepts) A [job worker](/reference/glossary.md#job-worker) is a service capable of performing a particular task in a process. Each time such a task needs to be performed, this is represented by a [job](/reference/glossary.md#job). A job has the following properties: - **Type**: Describes the work item and is defined in each task in the process. The type is referenced by workers to request the jobs they are able to perform. :::important This is a case-sensitive field, if supported by the underlying operating system. For example, `orderProcess` refers to a different worker than `OrderProcess`. ::: :::note Job worker types are subject to backend-dependent length limits: up to **32,768 characters** with Elasticsearch/OpenSearch-backed secondary storage and up to **256 characters** with RDBMS-backed secondary storage. If you use RDBMS, or might migrate to it later, keep job types within the 256-character limit. ::: - **Custom headers**: Additional static metadata that is defined in the process. Custom headers are used to configure reusable job workers (e.g. a `notify Slack` worker might read out the Slack channel from its header.) - **Key**: Unique key to identify a job. The key is used to hand in the results of a job execution, or to report failures during job execution. - **Variables**: The contextual/business data of the process instance required by the worker to do its work. - **Tags**: Immutable labels copied from the process instance at job creation. This is great for providing additional metadata (e.g., `reference:1234`, `team:accounting`, `trace-id:3004`). See [tags](#tags) and [process instance creation tags](/components/concepts/process-instance-creation.md#tags). ## Requesting jobs Job workers request jobs of a certain type on a regular interval (i.e. polling). This interval and the number of jobs requested are configurable in the [Zeebe client](/apis-tools/working-with-apis-tools.md). If one or more jobs of the requested type are available, [Zeebe](/components/zeebe/zeebe-overview.md) (the workflow engine inside Camunda 8) streams activated jobs to the worker. Upon receiving jobs, a worker performs them and sends back a `complete` or `fail` command for each job, depending on if the job could be completed successfully. For example, the following process might generate three different types of jobs: `process-payment`, `fetch-items`, and `ship-parcel`: ![order-process-model](assets/order-process.png) Three different job workers, one for each job type, could request jobs from Zeebe: ![zeebe-job-workers-requesting-jobs](assets/zeebe-job-workers-graphic.png) Many workers can request the same job type to scale up processing. In this scenario, Zeebe ensures each job is sent to only one of the workers. Such a job is considered activated until the job is completed, failed, or the job activation times out. On requesting jobs, the following properties can be set: - **Worker**: The identifier of the worker used for auditing purposes. - **Timeout**: The time a job is assigned to the worker. If a job is not completed within this time, it can be reassigned by Zeebe to another worker. - **MaxJobsToActivate**: The maximum number of jobs which should be activated by this request. - **FetchVariables**: A list of required variable names. If the list is empty, all variables of the process instance are requested. ### Long polling Ordinarily, a request for jobs can be completed immediately when no jobs are available. To find a job to work on, the worker must poll again for available jobs. This leads to workers repeatedly sending requests until a job is available. This is expensive in terms of resource usage, because both the worker and the server are performing a lot of unproductive work. Zeebe supports **long polling** for available jobs to better utilize resources. With **long polling**, a request is kept open while no jobs are available. The request is completed when at least one job becomes available. **Long polling** is set during [job activation with the parameter `request-timeout`](../../apis-tools/zeebe-api/gateway-service.md#activatejobs-rpc). ### Job queuing Zeebe decouples creation of jobs from performing the work on them. It is always possible to create jobs at the highest possible rate, regardless if there is a job worker available to work on them. This is possible because Zeebe queues jobs until workers request them. This increases the resilience of the overall system. Camunda 8 is highly available so job workers don't have to be highly available. Zeebe queues all jobs during any job worker outages, and progress resumes as soon as workers are available. This also insulates job workers against sudden bursts in traffic. Because workers request jobs, they have full control over the rate at which they take on new jobs. ## Completing or failing jobs After working on an activated job, a job worker informs Camunda 8 that the job has either `completed` or `failed`. - When the job worker completes its work, it sends a `complete job` command along with any variables, which in turn is merged into the process instance. This is how the job worker exposes the results of its work. - If the job worker can not successfully complete its work, it sends a `fail job` command. Fail job commands include the number of remaining retries, which is set by the job worker. - If `remaining retries` is greater than zero, the job is retried and reassigned. - If `remaining retries` is zero or negative, an [incident](/components/concepts/incidents.md) is raised and the job is not retried until the incident is resolved. When failing a job it is possible to specify a `retry back off`. This back off allows waiting for a specified amount of time before retrying the job. This could be useful when a job worker communicates with an external system. If the external system is down, immediately retrying the job will not work. This will result in an incident when the retries run out. Using the `retry back off` delays the retry. This allows the external system some time to recover. If no `retry back off` the job is immediately retried. When completing or failing jobs with [variables](components/concepts/variables.md), the variables are merged into the process at the job's associated task: - When `Completing a job` the variables are propagated from the scope of the task to its higher scopes. - When `Failing a job` the variables are only created in the local scope of the task. :::tip Failing a job with variables There are several advantages when failing a job with variables. Consider the following use cases: - You can fail a job and raise an incident by setting the job `retries` to zero. In this case, it would be useful to provide some additional details through a variable when the incident is analyzed. - If your job worker can split the job into smaller pieces and finish some but not all of these, it can fail the job with variables indicating which parts of the job were successfully finished which weren't. Such a job should be failed with a positive number of retries so another job worker can pick it up again and continue where the other job worker left off. The job can be completed when all parts are finished by a job worker successfully. ::: ### Using job result Job workers can provide a **job result** for [user task listeners](components/concepts/user-task-listeners.md). Job results are used to define: 1. **Corrections**: Updates to specific user task attributes, such as assignee, due date, follow-up date, candidate users, candidate groups, and priority, before the task transition is finalized. For more details, see [correcting user task data](components/concepts/user-task-listeners.md#correcting-user-task-data). 2. **Denial**: Indicates that the lifecycle transition should be explicitly denied. Denying the task lifecycle transition rolls back the user task to the previous state, and discards any corrections made by previous listeners. For more details, see [denying the operation](components/concepts/user-task-listeners.md#denying-the-operation). Below is an example of using job result: ```java final JobHandler userTaskListenerHandler = (jobClient, job) -> { boolean shouldDeny = someValidationLogic(job); jobClient .newCompleteCommand(job) // highlight-start .withResult( r -> r.forUserTask() .correctAssignee("john_doe") .correctPriority(42) .deny(shouldDeny)) // deny based on validation logic // highlight-end .send(); }; ``` If both corrections and denial are provided in the same job result (for example, `.correctAssignee(...)` and `.deny(true)`), the job completion command will be rejected. To avoid this, ensure the job is either completed with corrections (without denial set to `true`) or denied (without corrections). :::info The `corrections` and `deny` features are currently available only for user task listener jobs. ::: ## Timeouts If the job is not completed or failed within the configured job activation timeout, Zeebe reassigns the job to another job worker. This does not affect the number of `remaining retries`. A timeout may lead to two different workers working on the same job, possibly at the same time. If this occurs, only one worker successfully completes the job. The other `complete job` command is rejected with a `NOT FOUND` error. The fact that jobs may be worked on more than once means that Zeebe is an "at least once" system with respect to job delivery and that worker code must be idempotent. In other words, workers **must** deal with jobs in a way that allows the code to be executed more than once for the same job, all while preserving the expected application state. ### Timeout update When a job worker activates a job it can specify a timeout for how long the job should remain activated. For example, this timeout can be updated in the following scenarios: - When there are jobs which have an elastic timespan. They can potentially run for five minutes, but can be also 24+ hours. That can cause a problem when the workers picking up the jobs do not know in advance how long the given process takes, thus they can't accurately estimate a timeout. - In case of a long-running job. The scenario can occur where there is a problem with the job worker, but the task will be unavailable until the timeout is reached. In the scenarios described above, job timeout can be dynamically extended or shortened using `UpdateJobTimeout` gRPC command. This command takes a duration. This is not the duration with which the timeout will be extended or shortened. Instead, this will be the new duration the timeout is set to from the current time. This allows to not only extend the timeout of a job, but also to shorten the timeout. That means the worker does not need to estimate job timeout accurately at the very beginning. It can use some “standard” initial value and then extend or shorten the timeout as necessary. A job worker should not wait until the last second to update a job timeout as some time might be needed to process the update and there is a chance that in between the job could already time out. A buffer should be applied to avoid this issue. Job timeout can be updated [using the `UpdateJobTimeout` command](../../apis-tools/zeebe-api/gateway-service.md#updatejobtimeout-rpc). ## Job streaming It's also possible to use job workers in a streaming fashion, such that jobs are automatically activated and pushed downstream to workers without requiring an extra round of polling, which greatly cuts down on overall latency. ### How it works Job streaming works by having the worker open a long living gRPC unidirectional stream from the client to the gateway. The gateway then aggregates logically equivalent streams and registers each of these aggregated streams to every broker. :::note Two streams are considered logically equivalent if they would both activate the same job in the exact same way. More concretely, this means if they: - Target the same job type - Have the same worker name - Have the same job activation timeout - Have the same fetch variables ::: On the broker side, whenever a job is made activate-able (e.g. a service task is activated, a job times out, a job failed and is retried, etc.), if there is one or more streams for this job type, a random one is picked, the job is activated and pushed to it. As the job makes it way back to the gateway which owns this stream, a random client associated with it is picked, and the job is forwarded to it. :::note The RNG used to randomly pick streams and clients provides a good uniform distribution for the same underlying set, which is a cheap way of evenly distributing the load _as long as the stream set remains stable_. ::: To help visualize the process in general, here is a sequence diagram which shows a single worker opening a job stream for jobs of type "foo" against a cluster consisting of a single gateway and a single broker. It receives some jobs, and when it closes, one job that was pushed asynchronously is returned to the broker: ![Sample Sequence Diagram](assets/job-push-sequence.png) ### Backpressure To avoid workers overloaded with too many jobs, e.g. running out of memory: - The workers rely on the [built-in gRPC flow control mechanism](https://grpc.io/docs/guides/flow-control/). - Or, if lacking for your language of choice, [the built-in HTTP/2 stream flow control](https://httpwg.org/specs/rfc7540.html#FlowControl), e.g. Golang implementation of gRPC. Essentially, as jobs are pushed downstream from the broker to the client, they're first buffered in the gateway where the direct client connection resides. The gateway only sends as much data as the client can consume over a specific connection. If it notices its send buffers fill up, it marks a client as `not-ready`. This can happen, for example, if the client's receive method is blocked/suspended. If a client is not ready to receive a job, the gateway instead tries to re-route the job to another logically equivalent worker. If this fails (e.g. all workers connected to a specific gateway are not ready), the job is returned to the broker. There, it may be retried to another gateway, if and only if it has a logically equivalent worker. #### Implementing backpressure If you're using the raw `StreamActivatedJobs` RPC, or want to add support for this to your client of choice, the criteria to apply backpressure is to stall the underlying HTTP/2 transport. To do so, you may need to block the thread in which the gRPC stream is running (e.g. Java), or suspend the coroutine (e.g. Kotlin, Go). Once the transport stops receiving, this causes the gateway's send buffers to fill up, and effectively apply backpressure. If you wish to test this, you can do so by simulating a very slow worker with your new implementation. Then, start generating many jobs on the server side (e.g. create many process instances with lots of jobs). You should then observe backpressure via server side metrics, or many `Job.YIELD` commands written to the log. Refer to the [client implementations](https://github.com/camunda/camunda/tree/main/clients) for more information. #### Detecting backpressure There are different ways to detect backpressure. On the client side, you can use the job worker metrics to do so. For example, by subtracting the rate of handled jobs (i.e. `camunda.client.worker.job.handled`) from the rate of activated jobs (i.e. `camunda.client.worker.job.activated`), you can estimate the rate of queued jobs. If this is too close to the `maxJobsActive` consistently, this may indicate you need to scale your worker deployment. :::warning Deprecated metrics The following metrics are deprecated and will be removed in version 8.10: - `zeebe.client.worker.job.activated`, replace with `camunda.client.worker.job.activated` - `zeebe.client.worker.job.handled`, replace with `camunda.client.worker.job.handled` Please update your monitoring integrations to use the new metrics before upgrading to version 8.10. ::: :::note If you're using Prometheus, you can use the following query to estimate the queue size of your workers. We recommend adding a job type label to be able to group per workload, e.g. `sum(zeebe_client_worker_job_activated_total - zeebe_client_worker_job_handled_total) by (jobType)` ::: On the server side (e.g. if you're running a self-managed cluster), you can measure the rate of jobs which are not pushed due to clients which are not ready via the metric `zeebe_broker_jobs_push_fail_try_count_total{code="BLOCKED"}`. If the rate of this metric is high for a sustained amount of time, it may be a good indicator that you need to scale your workers. Unfortunately, on the server side we don't differentiate between clients, so this metric doesn't tell you which worker deployment needs to be scaled. We thus recommend using client metrics whenever possible. ### Proxying If you're using a reverse proxy or a load balancer between your worker and your gateway, you may need to configure additional parameters to ensure the job stream is not closed unexpectedly with an error. If you observe regular 504 timeouts, read our guide on [job streaming](../../../self-managed/components/orchestration-cluster/zeebe/zeebe-gateway/job-streaming). ### Troubleshooting Since this feature requires a good amount of coordination between various components over the network, we've built in some tools to help monitor the health of the job streams. #### Metrics We expose several metrics which help check whether the feature is working. - `zeebe_gateway_job_stream_push_total`: Allows you to derive the rate at which a gateway is pushing jobs out to clients. If this is much less than the broker's pushed count, this could indicate an issue between the broker and the gateway: jobs are getting lost, the gateway is overloaded, etc. - `zeebe_gateway_job_stream_clients`: The number of open job stream client calls on the gateway. This can help you figure out if your workers are well load balanced across your gateways. - `zeebe_gateway_job_stream_streams`: The number of aggregated streams per gateway. - `zeebe_gateway_job_stream_servers`: The amount of known brokers (or stream servers) per gateway. This should always be the number of brokers in your cluster. If this is less, this could indicate a clustering issue between your gateways and brokers (e.g. temporary network partition). - `zeebe_gateway_job_stream_push_fail_try_total`: The count of failed push attempts. This includes pushes which eventually succeeded (e.g. tried worker `A`, failed, rerouted to worker `B` which succeeded), and as such may be higher than the total number of pushes. This can be useful to narrow down on which gateways errors are coming from, or if a gateway has a higher number of faulty or blocked clients. - `zeebe_broker_jobs_pushed_count_total`: Allows you to derive the rate at which a broker is pushing jobs out to all streams. This can help you figure out if the broker is the bottleneck when it comes to throughput. - `zeebe_broker_open_job_stream_count`: The count of job streams registered on the broker. This should be the sum of all gateway aggregated streams. - `zeebe_broker_jobs_push_fail_try_count_total`: The count of failed job push attempts registered by a given broker. This includes pushes which eventually succeeded (e.g. tried all workers on gateway A, failed, then rerouted to gateway B where it succeeded), and as such may be higher than the total number of pushes. It's useful to detect if a specific gateway is producing errors which may otherwise be hidden by other gateways picking up the slack. #### Actuator endpoint Each broker and gateway exposes a new actuator endpoint - `/actuator/jobstreams` - accessible via the monitoring port. For example, if you run Zeebe locally with the default monitoring port, it would be accessible under `localhost:9600/actuator/jobstreams`. This returns the current view of the registered job streams, where `client` refers to client streams opened on the gateway, and `remote` refers to the aggregated gateway streams as opened on each broker. For example, if jobs of a given type are not activated, but a worker is opened for this type, you can verify first if it exists in one of the gateways as a client stream. Once you've found it, grab its ID, and verify that you can find it as a consumer of a remote stream on each broker. If it's not present in the gateway as a client stream, restart your worker. If it's not present as a consumer in one of the brokers, this indicates a bug. As a workaround, restart your gateway, which will cause some interruption in your service, but will force all streams for this gateway to be recreated properly. ## Tags Tags provide a powerful way to add lightweight metadata to jobs. ### How tags work with jobs When a BPMN element is activated and creates a job: 1. **Snapshot creation**: The job receives a copy of all tags from the process instance at that exact moment. 2. **Immutability**: Once copied to the job, tags cannot be modified, added, or removed. 3. **Worker access**: Job workers can read these tags to implement custom logic. ### Key characteristics - **Case-sensitive**: Tags `Priority:High` and `priority:high` are different. - **Timing**: Tags are copied exactly once when the job is created from the BPMN element. - **Immutable**: The tag set on a job never changes after creation. - **Inherited**: Jobs inherit the complete tag set from their process instance. For detailed information about tag formats, validation rules, limits, and additional use cases, see [process instance creation tags](/components/concepts/process-instance-creation.md#tags). --- ## Message aggregation Use message aggregation to collect multiple related messages into a single process instance before proceeding to the next step of your workflow. ## What message aggregation is Message aggregation is a message correlation pattern that allows you to receive, store, and process multiple messages belonging to the same business entity. It’s commonly used when: - You receive multiple events about a single entity (for example, shipments for the same order). - You need to wait for _N_ messages before proceeding. - You want to combine or "map-reduce" data across messages before continuing. Instead of creating a new process instance for each message, messages with the same **correlation key** are routed to the same instance. :::note This guide applies to Camunda 8 (Zeebe). The message aggregation pattern requires understanding of [message correlation](/components/concepts/messages.md#message-correlation-overview) and BPMN message events. ::: ## How it works 1. Each message is published with: - A **message name** (for example, `"ItemReceived"`) - A **correlation key** (for example, `"order-123"`) - Optionally, a **time-to-live (TTL)** greater than `0` 2. The first message starts a new process instance. 3. All subsequent messages with the same correlation key are correlated to that instance. 4. The workflow collects the message data (for example, appending to a list). 5. Once all expected messages are received, the process continues. If additional messages with the same correlation key arrive after the process instance has completed, a new instance is created automatically. ## Example use case Imagine a workflow that collects three messages for each order before processing them together. ### BPMN model Below is the key structure of the BPMN process: - **Start event:** Message start event that starts a process for the first message of each `correlation_key`. - **Intermediate catch event:** Waits for additional messages with the same correlation key. - **Gateway:** Checks if the desired number of messages has been received. - **Service task:** Processes all aggregated messages when complete. ```xml =count(messages) < 3 ``` ## Behavior The first message with a unique correlation key starts a process instance. Each subsequent message with that same key is correlated to the same instance and appended to the list. Once three messages are received (`count(messages) == 3`), the process continues to the **Process Aggregated Messages** task. ## Publishing messages Here’s an example in Java using the Zeebe client: ```java final ZeebeClient client = ZeebeClient.newClientBuilder().build(); for (int i = 0; i < 3; i++) { client.newPublishMessageCommand() .messageName("Message_Received") .correlationKey("order-123") .timeToLive(Duration.ofMinutes(5)) .variables(Map.of( "message", "iteration #" + i + " order-123 " + Instant.now(), "correlation_key", "order-123")) .send() .join(); } ``` The first message starts the workflow; the next two correlate to the existing instance. ## Troubleshooting | Problem | Cause | Solution | | ------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------- | | Every message starts a new process instance | Missing or mismatched `correlationKey` in message variables | Ensure all messages use the same correlation key and name | | Process never completes | The `count(messages)` condition is not met | Verify your condition and that messages are successfully correlated | | Messages not correlated | TTL expired or wrong message name | Use a TTL > 0 and match the BPMN message name exactly | | Duplicate aggregation | The instance ended but more messages arrived | This is expected — a new instance is started | ## Best practices - Always include a unique `correlationKey` in message variables and BPMN definition. - Use a **timer boundary event** to avoid waiting indefinitely for missing messages. - Add logging or audit tasks for tracking message count and correlation. - If using multiple sources, validate messages before appending to the collection. - Test with different message arrival orders to ensure correct behavior. ## Related resources - [Messages](/components/concepts/messages.md) --- ## Messages [Process instances](/reference/glossary.md#process-instance) can respond to incoming [messages](/reference/glossary.md#message). Published messages must be mapped onto a process instance. This step is called [message correlation](/components/modeler/bpmn/message-events/message-events.md#message-correlation). ## Message subscriptions A message is not sent to a process instance directly. Instead, the message correlation is based on subscriptions that contain the `message name` and the `correlation key` (also known as the correlation value). ![Message Correlation](assets/message-correlation.png) A subscription is opened when a process instance awaits a message; for example, when entering a message catch event. The message name is defined either statically in the process (e.g. `Money collected`) or dynamically as an expression. The correlation key is defined dynamically as an expression (for example, `= orderId`). The expressions are evaluated on activating the message catch event. The results of the evaluations are used as message name and as correlation key of the subscription (e.g. `"order-123"`). :::note Message names and correlation keys are subject to backend-dependent length limits. They support up to **32,768 characters** with Elasticsearch/OpenSearch-backed secondary storage and up to **256 characters** with RDBMS-backed secondary storage. Length is enforced using Java string length semantics, so the effective visible-character limit can be lower for characters represented as surrogate pairs in Java. ::: When a message is published and the message name and correlation key match to a subscription, the message is correlated to the corresponding process instance. If no proper subscription is opened, the message is discarded. A subscription is closed when the corresponding element (e.g. the message catch event), or its scope is left. After a subscription is opened, it is not updated (for example, when the referenced process variable is changed.)
Publish message via Orchestration Cluster REST API ``` curl -L 'http://localhost:8080/v2/messages/publication' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -d '{ "name": "Money collected", "correlationKey": "order-123" }' ``` See the [API reference for publish message](/apis-tools/orchestration-cluster-api-rest/specifications/publish-message.api.mdx) for more information, including additional request fields and code samples. When you require immediate feedback if the message was correlated to an open subscription, you can use `Correlate message` via Orchestration Cluster REST API. If correlation is successful it will return the first process instance key the message correlated with. ``` curl -L 'http://localhost:8080/v2/messages/correlation' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -d '{ "name": "Money collected", "correlationKey": "order-123" }' ``` See the [API reference for correlate message](/apis-tools/orchestration-cluster-api-rest/specifications/correlate-message.api.mdx) for more information, including additional request fields and code samples.
## Message buffering Messages can be buffered for a given time. Buffering can be useful in a situation when it's not guaranteed the subscription is opened before the message is published. A message has a **time-to-live (TTL)** which specifies for how long it's buffered. Within this time, the message can be correlated to a process instance. When a subscription is opened, it polls the buffer for a proper message. If a proper message exists, it is correlated to the corresponding process instance. In case multiple messages match to the subscription, the first published message is correlated (like a FIFO queue). The buffering of a message is disabled when its TTL is set to zero. If no proper subscription is open, the message is discarded.
Publish message with TTL via Orchestration Cluster REST API ``` curl -L 'http://localhost:8080/v2/messages/publication' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -d '{ "name": "Money collected", "correlationKey": "order-123", "timeToLive": 3600000 }' ``` See the [API reference for publish message](/apis-tools/orchestration-cluster-api-rest/specifications/publish-message.api.mdx) for more information, including additional request fields and code samples.
## Message cardinality A message is correlated only _once_ to a process (based on the BPMN process ID), across all versions of this process. If multiple subscriptions for the same process are opened (by multiple process instances or within one instance), the message is correlated only to one of the subscriptions. When subscriptions are opened for different processes, the message is correlated to _all_ the subscriptions. A message is _not_ correlated to a message start event subscription if an instance of the process is active and was created by a message with the same correlation key. If the message is buffered, it can be correlated after the active instance is ended. Otherwise, it is discarded. ## Message uniqueness A message can have an optional message ID — a unique ID to ensure the message is published and processed only once (i.e. idempotency). The ID can be any string; for example, a request ID, a tracking number, or the offset/position in a message queue. A message is rejected and not correlated if a message with the same name, the same correlation key, and the same ID is already buffered. After the message is discarded from the buffer, a message with the same name, correlation key, and ID can be published again. The uniqueness check is disabled when no message ID is set.
Publish message with message ID via Orchestration Cluster REST API ``` curl -L 'http://localhost:8080/v2/messages/publication' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -d '{ "name": "Money collected", "correlationKey": "order-123", "messageId": "tracking-12345" }' ``` See the [API reference for publish message](/apis-tools/orchestration-cluster-api-rest/specifications/publish-message.api.mdx) for more information, including additional request fields and code samples.
## Message correlation overview By combining the principles of message correlation, message uniqueness, and message buffering, very different behaviors can be achieved. Please note that a message name is mandatory, so it is omitted from the table. | Correlation key | Message ID | Time to live | Receiver type | Behavior | | --------------- | ---------- | ------------ | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | set | not set | set to 0 | Start event | A new instance is started if no instance with the correlation key set at start is active, see [single instance](./#single-instance). | | set | not set | set to 0 | Intermediate event | The message is correlated if a matching subscription is active. | | set | not set | set > 0 | Start event | A new instance is started if no instance with the correlation key set at start is active during the lifetime of the message; new [equal messages](#message-uniqueness) are buffered. | | set | not set | set > 0 | Intermediate event | The message is correlated during the lifetime of the message if a matching subscription is active; new [equal messages](#message-uniqueness) are buffered. | | set | set | set to 0 | Start event | A new instance is started if no instance with the correlation key set at start is active and there is no [equal message](#message-uniqueness) in the buffer. | | set | set | set to 0 | Intermediate event | The message is correlated if a matching subscription is active and there is no [equal message](#message-uniqueness) in the buffer. | | set | set | set > 0 | Start event | A new instance is started if no instance with the correlation key set at start is active during the lifetime of the message and there is no [equal message](#message-uniqueness) in the buffer. | | set | set | set > 0 | Intermediate event | The message is correlated during the lifetime of the message if a matching subscription is active and there is no [equal message](#message-uniqueness) in the buffer. | | empty string | not set | set to 0 | Start event | A new instance is started. | | empty string | not set | set to 0 | Intermediate event | The message is correlated if a matching subscription to the empty string is active. | | empty string | not set | set > 0 | Start event | A new instance is started. | | empty string | not set | set > 0 | Intermediate event | The message is correlated during the lifetime of the message if a matching subscription to the empty string is active; new [equal messages](#message-uniqueness) are buffered. | | empty string | set | set to 0 | Start event | A new instance is started if there is no [equal message](#message-uniqueness) in the buffer. | | empty string | set | set to 0 | Intermediate event | The message is correlated if a matching subscription to the empty string is active and there is no [equal message](#message-uniqueness) in the buffer. | | empty string | set | set > 0 | Start event | A new instance is started if there is no [equal message](#message-uniqueness) in the buffer. | | empty string | set | set > 0 | Intermediate event | The message is correlated during the lifetime of the message if a matching subscription to the empty string is active and there is no [equal message](#message-uniqueness) in the buffer. | ## Message patterns The following patterns describe solutions for common problems that can be solved using message correlation. ### Message aggregator **Problem**: Aggregate/collect multiple messages, map-reduce, batching **Solution**: ![Message Aggregator](assets/message-aggregator.png) The messages are published with a `TTL > 0` and a correlation key that groups the messages per entity. The first message creates a new process instance. The following messages are correlated to the same process instance if they have the same correlation key. When the instance ends and messages with the same correlation key are not correlated yet, a new process instance is created. :::note You may also use TTL to wait for messages that may arrive earlier when combining [start events and intermediate catch events](/components/modeler/bpmn/events.md). ::: Learn more in our [message aggregation guide](./message-aggregation.md). ### Single instance **Problem**: Create exactly one instance of a process **Solution**: ![Message Single Instance](assets/message-single-instance.png) The message is published with a `TTL = 0` and a correlation key that identifies the entity. The first message creates a new process instance. The following messages are discarded and do not create a new instance if they have the same correlation key and the created process instance is still active. ## Message response Publishing a message is a fire-and-forget action. As a user, you do not know if the correlation is a success. To know if a published message was correlated (and to which process instance), use the [message correlation endpoint](../../apis-tools/orchestration-cluster-api-rest/specifications/correlate-message.api.mdx). The message correlation endpoint works similarly to the message publish endpoint. However, the message correlation endpoint does not support [message buffering](#message-buffering). Any message published using this endpoint is either immediately correlated, or not correlated at all. This is due to the synchronous nature of requiring a response. If a message correlated successfully, it returns a process instance key of an instance the message correlated with. This is only one key. It is possible that the message correlated to other process instances. These keys are not part of the response. The response will always prioritize the creation of a new process instance ([message start event](../modeler/bpmn/message-events/message-events.md#message-start-events)) over correlation with an existing process instance ([message catch event](../modeler/bpmn/message-events/message-events.md#intermediate-message-catch-events) or [receive task](../modeler/bpmn/receive-tasks/receive-tasks.md)). --- ## Multi-tenancy :::info Multi-tenancy is only supported in Camunda 8 Self-Managed. It is not available in Camunda 8 SaaS. ::: [Multi-tenancy](/reference/glossary.md#multi-tenancy) in Camunda 8 enables a single installation to serve multiple [tenants](/reference/glossary.md#tenant) such as departments, teams, or external clients, while keeping each tenant's data and processes logically isolated. The following sections explain how multi-tenancy works in Camunda 8. ## Isolation of data and processes Each tenant's data and processes are logically isolated from others. This ensures that one tenant's workflows, data models, and configurations do not interfere with or affect other tenants. Each tenant operates in a secure, independent space within the same Camunda 8 instance. ## Resource sharing Multi-tenancy provides cost efficiency by allowing multiple tenants to share the same Camunda 8 installation and infrastructure. This reduces operational overhead while maintaining logical isolation between tenants. ## Efficient administration Administrators can manage all tenants centrally using [Admin](../admin/tenant.md). This unified management interface simplifies monitoring, configuration, and maintenance tasks across tenant environments. :::note If you are using [Optimize](/components/optimize/what-is-optimize.md), you must keep tenants synchronized with [Management Identity](#management-identity). ::: ## Security Strong access control mechanisms prevent tenants from accessing each other's data or processes. These controls ensure tenant-level security and maintain data integrity across all environments. ## Example: tenant membership in action When a user deploys a process model or starts a process instance, the system validates the user's tenant assignments. For example, assume a user belongs to `Tenant A` but not `Tenant B`: 1. **Deploying a process model** - If the user deploys to `Tenant A`, the Orchestration Cluster verifies the assignment. If valid, the model is deployed and all related process instances belong to `Tenant A`. - If the user deploys to `Tenant B`, the deployment fails because the user lacks access to that tenant. 2. **Running process instances** - When querying process instances, the user only sees instances belonging to `Tenant A`. This mechanism ensures proper isolation and access control across tenants. ## How multi-tenancy works Camunda 8 implements multi-tenancy using tenant identifiers within a single installation. All tenant data is stored in the same database, with isolation enforced by appending a tenant identifier to each data object (e.g., process definitions, process instances, jobs). ### Tenant identifier The tenant identifier is added to all data created in Camunda 8. By default, all data is assigned to the `` tenant identifier. :::note The `` tenant identifier is reserved and cannot be changed by users. ::: Organizations can create additional tenants. Tenant identifiers must meet the following requirements: - Use only alphanumeric characters, dashes (`-`), underscores (`_`), or dots (`.`). - Be no longer than 31 characters. ### Multi-tenancy checks Multi-tenancy checks enforce tenant-based access control. By default, multi-tenancy checks are **disabled**. This means that although tenants can be created and assigned, the system does not restrict access based on those assignments. All data is associated with the `` tenant. When **enabled**, the system verifies that users can only access resources associated with their assigned tenants. ### Inherited tenant ownership Tenant ownership in Camunda 8 is hierarchical. A user can only deploy resources to authorized tenants. Any data created by those resources inherits the same tenant identifier. The following diagram illustrates tenant ownership inheritance: ![Tenant ownership inheritance diagram](img/multi-tenancy.png) ## Management Identity [Management Identity](/self-managed/components/management-identity/overview.md) is a component of Camunda 8 Self-Managed used for identity and access management of components outside the [Orchestration Cluster](/self-managed/components/orchestration-cluster/overview.md). Of those, only [Optimize](/self-managed/components/optimize/overview.md) is tenant aware, and can make use of multi-tenancy. If you wish to use it with the same tenants as an Orchestration Cluster, you will have to manually synchronize the tenants in both the Orchestration Cluster and Management Identity. This means manually creating them, and updating them whenever they change. Two tenants are considered the same if they have the same ID. --- ## Outbound connectors vs. job workers Integrating with external systems can be done with a connector or a [job worker](job-workers.md). You define the domain-specific UI for modeling a connector through a [connector template](/components/connectors/custom-built-connectors/connector-templates.md). A connector template is a type of [element template](/components/modeler/element-templates/about-templates.md). Therefore, you can also build a connector-like system using element templates and job workers. If they both share the same core functionality, how do they differ, and when should you choose what connectors and job workers serve different purposes when it comes to aspects like delivery, reusability, focus, and context. ## Delivery A connector is reusable code, written as an `OutboundConnectorFunction` using the [Connector SDK](/components/connectors/custom-built-connectors/connector-sdk.md#outbound-connector-runtime-logic). It is not a standalone application, you cannot start it and have it work on Camunda 8 jobs. Instead, a connector is delivered as a library and can be used in combination with other connectors in a [Connector runtime environment](/components/connectors/custom-built-connectors/connector-sdk.md#runtime-environments). In contrast, a job worker is usually part of a Zeebe client application that can be directly executed to work on jobs. ## Reusability A job worker usually runs as or inside a standalone Zeebe client. Without effort, you cannot simply run this in Camunda 8 SaaS or any other environment. As a Self-Managed user, you can run it standalone, but often not directly reuse the logic in your existing Zeebe client that you might already have. You can manually extract the job handler from the given job worker, but you also have to ensure that it is still working as expected afterward. In contrast, a connector itself is environment-agnostic. There is a runtime environment for Camunda 8 SaaS that can wrap and call this connector. As the connector developer, you don't have to worry about this as the runtime takes care of it if you developed the connector using the Connector SDK. You can also run the exact same connector (without any modification) in Camunda 8 Self-Managed; either as a standalone job worker, as additional job handler in your existing Zeebe client application, or together with other connectors in one Zeebe client application. This all comes with the Connector SDK, and there is no additional code necessary to get started. However, if you need a custom environment, the Connector SDK provides a guide and default helpers to do that. ## Focus A job worker is often a complete Zeebe client application, dealing with environment tasks like handling variables in and out. The core logic of calling a defined URL is only part of the application. Plus, it handles Camunda 8-specific APIs like the job worker API to handle variables, complete executions, and throw errors. A connector only consists of core business functionality. No environment tasks, no Camunda 8 job worker-related code. You can run this from Camunda 7 as well if you have a runtime that takes care of this. The connector only needs input variables and access to secrets so they can be used in defined input attributes. ## Context Every job worker implementation defines on its own how to handle input data, validating and transforming it. There is no unified way of using secrets in a job worker implementation either, e.g. to replace placeholders in attributes with sensitive information only at runtime. Plus, there is no unified modeling experience for job workers. There can be an element template for the worker, but that template might look completely different for every job worker. In contrast, connectors bring all the above out of the box. The environment brings along secret management baked-in, being flexible in how you provide those secrets. Element templates, called [Connector templates](/components/connectors/custom-built-connectors/connector-templates.md), are a vital part of a connector. There are standardized best practices for developing those. Having used one connector template will make it easy for you to use the next one just the same. ## Which one should you choose? It depends on your use case. - Need access to a low-level API in Camunda 8 to perform a very specific task? You are better off with job workers. - Want to write your worker logic in something other than Java? Job workers are your way to move forward. - Want to create worker logic that is reusable in any environment? Write a connector. - Want to focus on your worker's logic and have no need for using low-level Orchestration Cluster REST API? Write a connector. - Want to provide a standardized modeling experience alongside your runtime behavior? Write a connector. ## Learn more - [Explore connectors](/components/connectors/introduction.md) - [Learn about types of connectors](/components/connectors/connector-types.md) - [Learn about job workers](job-workers.md) --- ## Projects Solutions built with Camunda typically consist of multiple resources that represent the end-to-end use case, such as an entry point process, called supporting processes, DMN decisions, or forms. Bundled together, versioned together, and deployed together, these resources constitute a _project_. For instance, a consumer loan approval project might bundle: - A BPMN process as an entry point (for example, consumer-loan-application.bpmn) to define the workflow. - DMN decision tables (for example, interest-rate-calculation.dmn, credit-score-calculation.dmn) for business rules. - Forms (for example, loan-application-review.form) for user interactions. :::tip We recommend you use a project for all your non-trivial automation solutions. ::: Our [Modeler](../modeler/about-modeler.md) applications support you as you develop a project by different means: - Advanced editor support with contextual assistance - Versioning and review features - Deployment of project files as a unit ## Next steps Read more about how to use projects in Desktop Modeler: - [Projects in Desktop Modeler](/components/modeler/desktop-modeler/process-applications.md) --- ## Process instance creation Depending on the process definition, an instance of it can be created in several ways. Camunda 8 supports the following ways to create a process instance: - [`CreateProcessInstance` commands](#commands) - [Message event](#message-event) - [Timer event](#timer-event) ## Commands A process instance is created by sending a command specifying the BPMN process ID, or the unique key of the process. There are two commands to create a process instance, outlined in the sections below. ### Create and execute asynchronously A process that has a [none start event](/components/modeler/bpmn/none-events/none-events.md#none-start-events) is started explicitly using **[CreateProcessInstance](/apis-tools/zeebe-api/gateway-service.md#createprocessinstance-rpc)**. This command creates a new process instance and immediately responds with the process instance ID. The execution of the process occurs after the response is sent. ![create-process](assets/create-process.png)
Create a process instance via Orchestration Cluster REST API ``` curl -L 'http://localhost:8080/v2/process-instances' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -d '{ "processDefinitionKey": "2251799813685249”, "processDefinitionVersion": 1 }' ``` Response: ``` { "processDefinitionId": "order-process", "processDefinitionVersion": 1, "processDefinitionKey": "2251799813685249", "processInstanceKey": "2251799813686019" } ``` See the [API reference for process instance creation](/apis-tools/orchestration-cluster-api-rest/specifications/create-process-instance.api.mdx) for more information, including additional request fields and code samples.
### Create and await results Typically, process creation and execution are decoupled. However, there are use cases that need to collect the results of a process when its execution is complete. **[CreateProcessInstanceWithResult](/apis-tools/zeebe-api/gateway-service.md#createprocessinstancewithresult-rpc)** allows you to “synchronously” execute processes and receive the results via a set of variables. The response is sent when the process execution is complete. ![create-process](assets/create-process-with-result.png) This command is typically useful for short-running processes and processes that collect information. If the process mutates system state, or further operations rely on the process outcome response to the client, consider designing your system for failure states and retries. :::note When the client resends the command, it creates a new process instance. :::
Create a process instance and await results via Orchestration Cluster REST API ``` curl -L 'http://localhost:8080/v2/process-instances' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -d '{ "processDefinitionId": "order-process”, "processDefinitionVersion": 1, "awaitCompletion": true, "variables": { "orderId": "1234" } }' ``` Response: ``` { "processDefinitionId": "order-process", "processDefinitionVersion": 1, "variables": { "orderId": "1234" } "processDefinitionKey": "2251799813685249", "processInstanceKey": "2251799813686019", } ``` See the [API reference for process instance creation](/apis-tools/orchestration-cluster-api-rest/specifications/create-process-instance.api.mdx) for more information, including additional request fields and code samples.
Failure scenarios applicable to other commands are applicable to this command as well. Clients may not get a response in the following cases even if the process execution is completed successfully: - **Connection timeout**: If the gRPC deadlines are not configured for long request timeout, the connection may be closed before the process is completed. - **Network connection loss**: This can occur at several steps in the communication chain. - **Failover**: When the node processing this process crashes, another node continues the processing. The other node does not send the response because the request is registered on the first one. - **Gateway failure**: If the gateway the client is connected to fails, nodes inside the cluster cannot send the response to the client. ### Run process segment The [`create and execute asynchronously`](#create-and-execute-asynchronously) and [`create and await results`](#create-and-await-results) commands both start the process instance at their default initial element: the single [none start event](/components/modeler/bpmn/none-events/none-events.md#none-start-events). Camunda 8 also provides a way to create a process instance starting or ending at user-defined element(s). :::info This is an advanced feature. Camunda recommends to only use this functionality for testing purposes. The none start event is the defined beginning of your process. Most likely the process is modeled with the intent to start all instances from the beginning. ::: #### Start instructions To start the process instance at a user-defined element, you need to provide start instructions along with the command. Each instruction describes how and where to start a single element. By default, the instruction starts before the given element. This means input mappings of that element are applied as usual. Multiple instructions can be provided to start the process instance at more than one element. You can activate the same element multiple times inside the created process instance by referring to the same element ID in more than one instruction. #### Runtime instructions By default, the process execution continues normally until the end of the process. To change this behavior and end the process instance after a specific element completes or terminates, provide runtime instructions. Each runtime instruction specifies the ID of one element whose completion or termination ends the process instance. You can provide multiple runtime instructions to terminate the process instance after multiple elements—for example, when a process has multiple parallel flows. :::note Start and runtime instructions have the same [limitations as process instance modification](/components/concepts/process-instance-modification.md#limitations), e.g., it is not possible to start or end at a sequence flow. ::: Start and runtime instructions are supported for both `CreateProcessInstance` commands. Both instruction sets can be used separately or together to achieve different scenarios.
Create a process instance with a start and a runtime instruction The example below shows how to create a process instance that starts at a user-defined element and terminates after it, so that only the specified segment of the process is executed. ``` curl -L 'http://localhost:8080/v2/process-instances' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -d '{ "processDefinitionId": "order-process”, "processDefinitionVersion": -1, "startInstructions": [ { "elementId": "ship_parcel" } ], "runtimeInstructions": [ { "type": "TERMINATE_PROCESS_INSTANCE", "afterElementId": "ship_parcel" } ] "variables": { "orderId": "1234" } }' ``` See the [API reference for process instance creation](/apis-tools/orchestration-cluster-api-rest/specifications/create-process-instance.api.mdx) for more information, including additional request fields and code samples.
## Events Process instances are also created implicitly via various start events. Camunda 8 supports message start events and timer start events. ### Message event A process with a [message start event](/components/modeler/bpmn/message-events/message-events.md#message-start-events) can be started by publishing a message with the name that matches the message name of the start event. For each new message a new instance is created. ### Timer event A process can also have one or more [timer start events](/components/modeler/bpmn/timer-events/timer-events.md#timer-start-events). An instance of the process is created when the associated timer is triggered. Timers can also trigger periodically. ## Business ID ### What is a business ID? A business ID is a domain-specific identifier you can assign to a process instance. Unlike the system-generated process instance key, it represents a domain concept such as an order number, case reference, or customer ticket ID. A business ID does **not need to be unique** unless uniqueness control is enabled. See [Uniqueness control](#uniqueness-control) for details. For example, consider a process that ships book orders where each order already has an identifier in your order management system. When you start the process to ship an order, you can use the order ID as the business ID. This lets you easily find all process instances related to a particular order. You set the business ID at process instance creation time via the `businessId` field in the creation request. The business ID is **immutable**; once set, it cannot be changed or removed for the lifetime of the process instance. The maximum length for a business ID is **256 characters**. :::note The business ID feature is currently only available through the API (REST and gRPC) and the official clients. Creating process instances from other tools such as Web Modeler does not support setting a business ID. :::
Create a process instance with a business ID via Orchestration Cluster REST API ``` curl -L 'http://localhost:8080/v2/process-instances' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -d '{ "processDefinitionId": "order-process", "processDefinitionVersion": 1, "businessId": "order-1234" }' ``` See the [API reference for process instance creation](/apis-tools/orchestration-cluster-api-rest/specifications/create-process-instance.api.mdx) for more information, including additional request fields and code samples.
### Propagation to child instances When a process instance with a business ID creates a child process instance via a [call activity](/components/modeler/bpmn/call-activities/call-activities.md), the business ID is automatically propagated to the child. Each child instance inherits the same business ID as its parent. As a result, the entire process hierarchy ultimately shares the root instance's business ID, and child instances cannot override it. This lets you trace an entire process hierarchy using a single domain identifier. ### Retrieving process instances by business ID The business ID is available as a property on the process instance. You can use it to look up or filter process instances through the API: - [Get process instance](/apis-tools/orchestration-cluster-api-rest/specifications/get-process-instance.api.mdx) — retrieve a single process instance and inspect its `businessId` field. - [Search process instances](/apis-tools/orchestration-cluster-api-rest/specifications/search-process-instances.api.mdx) — filter process instances using the `businessId` field to find all instances linked to a specific business case. ### Uniqueness control With uniqueness control, you can ensure that only one active root process instance exists for a given **process definition, tenant, and business ID**. This prevents duplicate processing of the same business case. Uniqueness is checked against **active root process instances**. - A **root process instance** is a process instance that was started directly, not created by a call activity. Child process instances created via call activities don't count toward the uniqueness check, even though they inherit the parent's business ID. - When uniqueness control is enabled, creating a root process instance is rejected if another **root** process instance of the same process definition is already active with the same business ID. The rejection returns an `ALREADY_EXISTS` error (HTTP `409 Conflict`). - Once a process instance is no longer active (completed or terminated), you can use its business ID to create a new process instance. :::note Retroactive enforcement Uniqueness control is **retroactive**. When you enable it, business IDs that were already assigned to active process instances _before_ the feature was turned on are taken into account. This prevents duplicate instances from being created after the feature is enabled, even if duplicates already existed before activation. ::: Uniqueness control is opt-in. Enable it using the configuration property [`camunda.process-instance-creation.business-id-uniqueness-enabled`](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#process-instance-creation). For SaaS, configure this in the cluster configuration via Camunda Hub. For Self-Managed, set it in the application config (for example, `application.yaml` or as an environment variable). :::note When a business ID is specified, the partition for the new process instance is determined deterministically by **hashing the business ID**, rather than using the default round-robin distribution. This ensures that uniqueness checks occur on a single partition. Be aware that this may result in uneven distribution of instances across partitions if business IDs are not well distributed. ::: #### Multi-tenancy scope In a multi-tenant environment, uniqueness is enforced **per tenant and process definition**. Process instances belonging to different tenants can use the same business ID and process definition without conflict. For example, two active root process instances in different tenants may share the same business ID and process definition. ### Process instance migration When a process instance with a business ID is [migrated](/components/concepts/process-instance-migration.md) to a different process definition, the business ID is preserved and carried over to the **target** process definition. The business ID remains immutable; it cannot be changed or removed as part of the migration. After migration, the **source** process definition is no longer associated with the business ID. Migration intentionally **bypasses uniqueness control checks**, because migration operates on existing instances rather than creating new ones. It is a deliberate operator action with accountability provided by the audit log. As a result: - Migration is never rejected due to a business ID conflict at the target process definition. The target definition may end up with more than one active root process instance with the same business ID. - When uniqueness control is enabled, a new process instance with the same business ID can be created for the **source** process definition, since it is no longer associated with the migrated instance. ### Limitations - You cannot currently search related entities (for example, jobs, user tasks, or incidents) **directly by business ID**. - When using [cluster scaling](/self-managed/components/orchestration-cluster/zeebe/operations/cluster-scaling.md) to increase the number of partitions, new process instances created with a business ID are only distributed across the original set of partitions, not to any newly added partitions. ## Tags Process instance tags are lightweight, immutable labels you can attach when creating a process instance via the API or clients. Tags are inherited by all jobs created from that instance. They help downstream workers and external systems make quick routing or decision choices without inspecting full variable payloads. ### Tag format and constraints - A tag is a case-sensitive string. - Format (regex): `^[A-Za-z][A-Za-z0-9_\-:.]{0,99}$` - Must start with a letter (A–Z or a–z). - Remaining characters may be alphanumeric, underscore (`_`), hyphen (`-`), colon (`:`), or dot (`.`). - Length: 1–100 characters. - Maximum of 10 unique tags per process instance (duplicates are ignored). - Order is not guaranteed; treat the set as unordered. - Tags cannot be modified after creation If validation fails during process instance creation (for example, too many tags, invalid pattern, or length), the create request is rejected with a 4xx error. ### Semantics - Tags are included in process instance search responses and in activated job payloads. - Tags are immutable after creation - cannot be added, changed, or removed after process instance has been created. - Search filtering uses AND semantics: an instance must contain all requested tags (it may contain additional tags). Partial or wildcard matching is not supported. - Tags are exported with the process instance and with job entities starting in 8.8 by the default exporters. - Tags are not shown in web applications (such as Operate and Tasklist) — they are API/client-only metadata. ### Use cases - Routing and prioritization (for example, `priority:high`) - Business or domain identifiers from internal or third-party systems (for example, `reference:1234`, `team:accounting`, `origin:crm`) - Cross-system correlation keys without exposing full variable payloads (for example, `trace-id:abcd-1234`, `crm-id:3004`) - Analytics segmentation (for example, `region:emea`, `channel:web`) - Feature rollout or experiment grouping (for example, `experiment:checkout-v2`) ### Guidelines - Do not store secrets or PII; tags propagate with jobs and exports. - Prefer concise `key:value` or `key` patterns for consistency. - Use variables (not tags) for mutable or large data. - Establish internal naming conventions (for example, prefixes like `env:` or `dept:`) for governance. ### Examples Create with tags: ```bash curl -L 'http://localhost:8080/v2/process-instances' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -d '{ "processDefinitionId": "order-process", "processDefinitionVersion": 3, "tags": ["channel:web", "reference:1234", "region:emea"], "variables": { "orderId": "1234" } }' ``` ## Next steps - [About Modeler](/components/modeler/about-modeler.md) - [Automating a process using BPMN](/components/modeler/bpmn/automating-a-process-using-bpmn.md) --- ## Process instance deletion Use process instance deletion to permanently remove all data associated with a completed or terminated process instance. :::warning Deletion is irreversible. Restore deleted data only by restoring a backup of your cluster. ::: Delete a process instance using the [delete process instance endpoint](/apis-tools/orchestration-cluster-api-rest/specifications/delete-process-instance.api.mdx), or multiple using the [delete process instances endpoint](/apis-tools/orchestration-cluster-api-rest/specifications/delete-process-instances-batch-operation.api.mdx). :::note You can also delete a process instance in Operate. See the [Operate user guide](../operate/userguide/delete-finished-instances.md). ::: ## Limitations You can delete only process instances in a completed or terminated state. This preserves consistency and integrity within the system. If a process instance is still active, cancel it first using the [cancel process instance API](/apis-tools/orchestration-cluster-api-rest/specifications/cancel-process-instance.api.mdx). ### Limitations with call activities When process instances are linked through call activities, deletion is scoped to the selected instance only. Related parent or child process instances are not deleted automatically. #### Delete a called process instance If you delete a process instance that was created by a call activity, the parent process instance is not affected. Only the called process instance data is deleted. In Operate, the parent process instance remains visible, but you can’t navigate to the deleted called process instance. #### Delete a parent process instance If you delete a parent process instance that contains a call activity, the called process instance is not affected. Only the parent process instance data is deleted. In Operate, the called process instance remains visible, but navigating to it shows an empty screen. ## Eventual consistency Process instance deletion runs asynchronously. Depending on how many process instances you delete, it can take time for the data to be removed and for the process instance to disappear from Operate. ## Technical details This section explains how process instance deletion is handled internally to help you understand timing and consistency behavior. Deleting one or more process instances uses [batch operations](./batch-operations.md). The Zeebe engine queries [secondary storage](/self-managed/concepts/secondary-storage/index.md) for process instances to delete. For each instance found, the engine writes a delete command to the log, which results in a deleted event. Exporters consume the deleted event and write a record to secondary storage to mark the process instance for deletion. An asynchronous scheduled task then deletes all data associated with each marked process instance. ```mermaid sequenceDiagram V2 API->>+Engine: Delete process instances with filter Engine->>Engine: Create batch operation Engine->>-V2 API: Batch operation create response Engine->>+Secondary storage: Query process instances activate Engine Secondary storage->>-Engine: Return process instance keys loop for each process instance key Engine->>Engine: Write DELETED event for each process instance key Engine->>+Exporter: Export DELETED event deactivate Engine Exporter->>-Secondary storage: Mark process instance for deletion end note over V2 API,Deletion job: Everything below this note happens asynchronously. Deletion job->>+Secondary storage: Retrieve marked process instance keys activate Deletion job Secondary storage->>-Deletion job: Return process instance keys loop for each process instance key Deletion job->>Deletion job: Delete associated data end deactivate Deletion job ``` --- ## Process instance migration Process instance migration fits a running process instance to a different process definition. This can be useful when the process definition of a running process instance needs changes due to bugs or updated requirements. While doing so, we aim to interfere as little as possible with the process instance state during the migration. For example, a migrated active user task remains assigned to the same user if no implementation migration occurs. This principle applies to all parts of the process instance. :::tip To repair a broken process instance without making changes to the process definition, use [process instance modification](./process-instance-modification.md) instead. ::: Use the migration command [RPC](/apis-tools/zeebe-api/gateway-service.md#migrateprocessinstance-rpc) or [REST](/apis-tools/orchestration-cluster-api-rest/specifications/migrate-process-instance.api.mdx) to change the process model of a running process instance. :::note You can also migrate your process instances using Operate's UI by following [the user guide](../operate/userguide/process-instance-migration.md). ::: ## Change the process instance flow for inactive parts Process instance migration allows you to adjust the process model of your process instance to fit to new requirements. Let's consider an example. The process instance below contains a completed start event, an active service task `A`, and an end event. Once you complete the service task `A`, the process will reach the end event. ![The process instance is waiting at the active service task A.](assets/process-instance-migration/migration-basic_before.png) However, the requirements of our process have changed. Instead of completing the process, we want to add a user task `B` after the service task `A` and before the end event. ![In the new process version the service task A flows to a user task B.](assets/process-instance-migration/migration-basic_target.png) We can create new process instances according to this new process model after deploying it, but we also want our active process instances to receive the update and reach user task `B` when service task `A` is completed. To achieve this, we can migrate our process instance from its current process to the newly deployed one. You must provide a migration plan with mapping instructions to the target process definition to clarify your intentions. In our example, we'll provide a mapping instruction from source element ID `A` to target element ID `A`. This means we expect any active element instances of element with ID `A` to be migrated to the element with ID `A` in the target process. Therefore, our process instance will continue to be active at service task `A`. ![Once the service task A is completed, the process instance will flow to the user task B.](assets/process-instance-migration/migration-basic_after.png) After migrating our process instance, we can now complete the service task `A` to reach user task `B`. Process instance migration allows you to change the inactive parts of the process instance. In our example, we placed a user task `B` between the active service task `A` and the inactive end event. We did not change the active service task `A`, just the steps that follow. ## Change the active elements Sometimes your requirements change so much that the currently active element no longer exists in the new process version. Consider the following example: the process contains a completed exclusive gateway, taking the sequence flow to the service task `A` which is currently active. It did not take the sequence flow to service task `B`, so it is inactive. ![The process instance is waiting at the active service task A while service task B is inactive.](assets/process-instance-migration/migration-active_before.png) Due to changed requirements, our process model no longer contains the exclusive gateway, nor the service tasks `A` and `B`. Instead, it only contains a new service task `C`. ![The target process only contains a service task C.](assets/process-instance-migration/migration-active-elements-target.png) We can migrate the active service task `A` to this new service task `C` by providing a mapping instruction from source element ID `A` to target element ID `C`. ![After migrating the process instance, it is waiting at service task C.](assets/process-instance-migration/migration-active_after.png) :::note You cannot map an element to an element of a different type. An active service task of a process instance can only be mapped to a service task in the target process. It cannot be mapped to a user task as this changes the element type. Also note that the [jobs, expressions, and input mappings](#jobs-expressions-and-input-mappings) are not recreated. So, while service task `C` is active in the target process, the associated job still has the job type from when it was associated to service task `A`. ::: :::tip If you need to adjust the job type to its new element, you can use [process instance modification](./process-instance-modification.md) to recreate the service task. Simply cancel the service task instance, and add a new instance of the service task. ![The process instance can be modified to recreate the service task's job.](assets/process-instance-migration/migration-active_after-modification.png) ::: ## Correct mistakes in a process instance Process instance migration can also be used to correct mistakes that led to an [incident](/components/concepts/incidents.md) in a process instance. Let's consider an example. A user deployed a process with a user task `A`. The instance of that process always receives two variables (one boolean and one string). By accident, the string is used in the condition. To fix the problem, the process model should be updated to use the boolean variable instead. A process instance gets stuck at this user task, and an incident is created to inform the user of this problem. ![After creating the process instance, an incident is created for the service task A.](assets/process-instance-migration/migration-process_instance_with_incident.png) To correct the problem in the process instance, the user can take the following steps: 1. Correct the mistake in Modeler by creating a new version of the process definition. 2. Deploy the new process version where the boolean variable is used in the condition. 3. Migrate the process instance to the new process version. 4. Resolve the incident. Afterward, the process instance will continue as expected: ![After migrating the process instance, the input mapping is corrected and the incident is resolved by retry. Afterward, the process instance will continue as expected:](assets/process-instance-migration/migration-process_instance_with_incident_resolved.png) ## Migrate active elements inside subprocesses Active elements located inside subprocesses can be migrated as part of a process instance migration. Let's consider an example where we want to migrate an active element that is located in a subprocess. ![The service task A is inside the subprocess A.](assets/process-instance-migration/migration-subprocess_before.png) After migrating active element `A` to `B` and `Subprocess A` to `Subprocess B`, the process instance will look like this: ![After migrating the process instance, it is waiting at service task B inside the Subprocess B.](assets/process-instance-migration/migration-subprocess_after.png) :::note A mapping instruction must be provided from the process instance's subprocess ID to the target subprocess ID to migrate subprocesses. ::: :::note You cannot migrate an active embedded subprocess to an event subprocess. Additionally, changing the scope of a subprocesses during migration is not possible. ::: ### Call activities and called process instances Active call activities can be migrated like any other element. The called process instance is not changed when migrating the call activity. You can migrate a called process instance in the same way as a regular process instance. ## Migrate active elements inside ad-hoc subprocesses Active elements located inside ad-hoc subprocesses can be migrated as part of a process instance migration. Consider the following example, where you want to migrate an active element that is located in an ad-hoc subprocess: ![The service task A is inside the ad-hoc subprocess A.](assets/process-instance-migration/migration-adhoc-subprocess_before.png) After migrating active element `A` to `B` and `Ad-Hoc Subprocess A` to `Ad-Hoc Subprocess B`, the process instance will look like this: ![After migrating the process instance, it is waiting at service task B inside the Ad-Hoc Subprocess B.](assets/process-instance-migration/migration-adhoc-subprocess_after.png) :::important To migrate ad-hoc subprocesses, you must provide a mapping instruction from the process instance’s ad-hoc subprocess ID to the target ad-hoc subprocess ID. Changing the scope of ad-hoc subprocesses during migration is not possible. ::: ## Migrate job worker user tasks to Camunda user tasks You can migrate user tasks with a job worker implementation to Camunda user tasks by providing mapping instructions between the source and target user tasks. The target Camunda user task preserves `candidate groups`, `candidate users`, `due date`, `follow-up date`, and the `form id` or `form key` from the source task, as well as `customHeaders` set in the job. The target user task uses the [priority](../../tasklist/userguide/defining-task-priorities) defined in the target user task definition, or a default value of 50 if none is defined. :::important When you migrate a job worker user task to a Camunda user task: - Embedded forms are not supported. The form defined in the target user task definition is used. - The current `assignee` is not preserved. The task is assigned to the initial assignee defined in the target user task definition. ::: :::note Incidents on the job worker user task need to be resolved before migrating to a Camunda user task, otherwise the migration will fail. ::: ## Deal with catch events An exception to changing the process instance state is specific to catch events. This is necessary to ensure that the process instance can be executed according to the new process definition. It allows you to add or remove catch events from an active element. At the same time, you can leave an existing catch event unchanged. This section explains how to deal with catch events when migrating a process instance. You decide what happens to the associated event subscription through the mapping instructions for the catch events: - Migrate catch events: if a catch event is mapped, the associated subscription is migrated. - Remove catch events: if a catch event in the source process is not mapped, then the associated subscription is closed during migration. - Add catch events: if a catch event of the target process is not the target of a mapping instruction, then a new subscription is opened during migration. ### Migrate catch events Catch events can be migrated by providing a mapping instruction between the source and the target catch event. Providing a mapping between catch events ensures that the event subscription in the source process is preserved after the migration. In the following section we will discuss situations where mapping a catch event may or may not be useful. Let's explain both mapping and non-mapping scenarios with examples. #### A mapping instruction is provided between the catch events An active user task has been waiting for a timer boundary event. The timer boundary event is defined as a duration of one week. The user task has not been completed and has already spent five days waiting for the timer. ![The process instance is waiting at the active user task A with a timer boundary event attached.](assets/process-instance-migration/migration-catch-event-source.png) Now we want to [change an inactive part of the process](#changing-the-process-instance-flow-for-inactive-parts) by adding a user task after the timer boundary event. Instead of waiting for the full time defined by the target process' timer boundary event, we only want to wait for the remaining two days. To achieve this for the example above, the mapping between active user tasks `A` -> `A` and timer boundary events `Timer1` -> `Timer2` must be provided. This ensures the timer is migrated (_the associated subscription is migrated_) and the duration is preserved. Assuming that the timer boundary event is defined as 2 weeks duration in the target process, the process instance will look as follows after the migration: ![The process instance is waiting at the active user task A with the migrated timer boundary event attached.](assets/process-instance-migration/migration-catch-event-different-target.png) Mapping catch events applies to other event types as well. For example, if you want to keep the message name the same for a message event subprocess, you should map the start event of the event subprocess when migrating the process instance. Another example would be to preserve a signal name for an intermediate signal catch event attached to an event-based gateway. In this case, you should map the signal catch event to the one in the target while migrating the process instance. #### No mapping instruction is provided between the catch events Before moving forward with the example, there are two important scenarios to consider because they affect the output after the migration: - The catch event in the source process is identical to the catch event in the target process. - There are changes between these catch events, for example, the message name is different in the target. Let's consider again the same example above: An active user task has been waiting for a timer boundary event. The timer boundary event is defined as a duration of one week. The user task has not been completed and has already spent five days waiting for the timer. ![The process instance is waiting at the active user task A with a timer boundary event attached.](assets/process-instance-migration/migration-catch-event-source.png) In the first scenario, the catch event in the source process is identical to the catch event in the target process: This time we want to reset the timer and wait for the full week again. To achieve this for the example above, only a mapping between active user tasks `A` -> `A` must be provided. This will cancel the timer (_associated subscription is closed_) and create a new one (_a new subscription is opened_). After the migration the process instance will look like following: ![The process instance is waiting at the active user task A with a new timer boundary event attached.](assets/process-instance-migration/migration-catch-event-identical-target-trigger-updated.png) In the second scenario, there are changes between these catch events: Now, we want to reset the timer and wait for two weeks as in the target process definition. To achieve this for the example above, only a mapping between active user tasks `A` -> `A` must be provided. This will cancel the timer (_associated subscription is closed_) and create a new one (_a new subscription is opened_). After the migration the process instance will look as follows: ![The process instance is waiting at the active user task A with a new timer boundary event attached.](assets/process-instance-migration/migration-catch-event-different-target-trigger-updated.png) Same as above, omitting the mapping instruction for catch events applies to other event types as well. For example, if you want to change the message name for a message event subprocess start event, you must omit mapping the message start event when migrating the process instance. Another example would be to update a signal name for an intermediate signal catch event, you must omit mapping the signal catch event when to the one in the target while migrating the process instance. ### Add or remove catch events You can also add or remove catch events. Let's consider a process instance awaiting at a service task `A`. ![The process instance is waiting at the active service task A without any boundary events attached.](assets/process-instance-migration/migration-boundary-event_before.png) You can migrate it to a process definition where a message event subprocess with message start event `M` is added to the process. To do so, you only have to map element `A` to element `A` in the target process. After migrating active element `A`, the process instance is newly subscribed to the message boundary event `M` (_a new subscription is opened_). ![After migrating, the process instance is subscribed to the newly introduced message event subprocess start event.](assets/process-instance-migration/migration-catch-event-target-added.png) Likewise, you can migrate the process instance back to the previous process definition where no message event subprocess is defined. To do so, you only have to map element `A` to element `A` again. After migrating active element `A`, the process instance is no longer subscribed to the message start event `M` (_associated subscription is closed_). ![After migrating back, the process instance is no longer subscribed to the message boundary event](assets/process-instance-migration/migration-boundary-event_before.png) Likewise, adding and removing catch events applies to all other supported catch event types as well. For instance, an intermediate signal catch event can be added or removed in the same way as the message event subprocess in the example above. :::tip Currently, a mapping instruction must be provided between catch events to migrate message catch events if the target catch event has the same message name. Therefore, it is not possible to re-create message catch events with the same message name in the target process definition. While we're working on resolving this, you can migrate this case by providing a mapping between the boundary events. ::: ### Compensation boundary events Compensation boundary events can be migrated similarly to other catch events, with one exception. If a compensation boundary event is added, a new compensation subscription is only opened for activity instances that are currently active, or haven't been started yet. For activity instances that have already finished, no new compensation subscription will be opened. Consider the following example where the instance is waiting on service task `A`: ![The instance waiting on service task A.](assets/process-instance-migration/migration-compensation_before.png) The target process definition contains a compensation boundary event attached to service task `A`: ![The target process definition contains a compensation boundary event attached to service task A.](assets/process-instance-migration/migration-compensation_after.png) If the process instance is migrated by providing mapping instruction between service tasks `A` -> `A`, the compensation subscription will be created on completion of the element `A`. However, if the process instance is migrated by providing mapping instruction between service tasks `A` -> `B`, then triggering compensation throw event afterward will not compensate `A`. This is because the subscription is only opened when completing a task with a compensation boundary event. ## Deal with gateways Process instance migration allows you to migrate several scenarios for gateways: - An active exclusive gateway with an incident can be migrated like any other active element. - Parallel and inclusive gateways can be involved in [additional scenarios](#migrating-joining-parallel-and-inclusive-gateways). ### Migrate joining parallel and inclusive gateways Joining parallel and inclusive gateways with taken incoming sequence flows, and which are still waiting for more incoming sequence flows, require a mapping instruction similar to active elements. For migrating joining gateways, the following conditions must be true: - The joining gateway in the process instance must be mapped to the target gateway. - The target gateway must have at least the same number of incoming sequence flows as the source gateway. Consider the following example: The process instance is waiting at the joining parallel gateway, with an incoming sequence flow `flow1`, taken after the element `A` completes. Element `B` is still active and waiting at the user task. ![The instance waiting on joining gateway.](assets/process-instance-migration/migration-joining-gateway-before.png) Then, the process definition is updated to include element `C` before the joining gateway. To migrate the process instance, provide the following mapping instructions: - From the active element `B` to the target element `B`. - From the joining parallel gateway instance `gateway1` to the target joining parallel gateway `gateway2`. - From the sequence flow `flow1` to the target sequence flow `flow2`. After the migration, the process instance will look like the following: In the example above, another element `C` is added before the joining gateway in the target process definition. After the migration, element `B` must still be completed for the process instance to continue through the gateway. ## Process definitions and versions So far, we've only discussed migrating a process instance to a new version of its process definition. You are free to migrate your process instance: - From an older version to a newer version of the same process definition. - From a newer version to an older version of the same process definition. - To a different process definition altogether. :::note You do not have to provide a mapping instruction from the process instance's process ID to the target process ID. ::: ## Jobs, expressions, and input mappings We do not recreate jobs, reevaluate expressions, and reapply input mappings of the active elements. We also don't adjust any static values if they differ between the two process definitions. Any existing variables, user tasks, and jobs continue to exist with the same values as previously assigned. Let's consider an active service task that created a job when it was activated with type `send_mail`. In the target process definition, the job type expression is changed as follows: ```feel = order.next_step ``` However, on migrating the process instance this new job type expression is not evaluated. Instead, the job keeps all properties it had before the migration, including the job type. :::tip You can use [process instance modification](./process-instance-modification.md) to terminate and activate the service task if you want to create the job according to the new service task's definitions. This results in new keys for the service task as well as the job. ::: ## Internal Execution In the following example, we will explain the internal execution steps of process instance migration. It is recommended that you read these steps to fully understand the power of process instance migration. Migration of a process instance consists of the following steps: - Validation - Migration of process instance, and global variables - Migration of each active element (including associated jobs, incidents, local variables, and event subscriptions) If any of the steps fail, the migration is rejected and a rejection message that explains the reason is returned. For example, if a mapping is not provided for an active element, the migration is rejected with an error message indicating that the mapping is missing. As a result, the process instance will not be migrated and remains in its current state. The migration runs in a **transactional** manner, meaning that it is migrating all active elements or nothing. ### Validation The migration plan is validated before the migration is executed. The validation starts with validating the mapping instructions provided in the migration plan. For example, the validation checks if the source element ID refers to an existing element in the process instance's process definition. Later, while attempting to migrate each active element, each limitation mentioned in the [limitations section](#limitations) is validated. For example, the flow scope of an active element is validated to ensure that it is not changed during migration. ### Migration of process instance, and global variables After all validations are successful, the migration of the outermost active element which is the process instance itself is started. At this point, the process instance's `processDefinitionKey`, `bpmnProcessId`, and `version` properties are updated to the target process definition. The global variables are also migrated to the target process definition. ### Migration of each active element The execution of the migration is done in a breadth-first manner. The first active child instance of the process instance is migrated followed by the migration of the next active child instance. Later, the migration of the active child instances of each active child instance is executed. In this stage, jobs, incidents, local variables, and event subscriptions contained in each active element is also migrated. While traversing each active element, the migration plan is used to determine the target element for each active element. For each active element, `processDefinitionKey`, `bpmnProcessId`, `elementId`, `version` properties are updated to the target process definition. #### Migration of catch event subscriptions The following operations are performed in respective order for each active element as the execution continues to migrate each active element: - If a catch event exists in the source process instance and is not part of the migration plan, the subscription to the catch event is removed. - If a catch event exists in the target process definition and is not part of the migration plan, a new subscription is created for the catch event. - If a catch event in the source process is mapped to a catch event in the target process, the subscription is migrated. While migrating each catch event subscriptions, the catch event's `processDefinitionKey`, `bpmnProcessId`, `elementId` properties are updated to the target process definition. :::note It is **possible** to change the interrupting status during catch event subscription migration. ::: ## Limitations Not all process instances can be migrated to another process definition. In the following cases, the process instance can't apply the migration plan and rejects the migration command. - Process instance migration can only migrate active process instances, i.e. existing process instances that have not yet been completed, terminated, or banned. - All active elements require a mapping. - The number of active elements cannot be changed. You can use [process instance modification](./process-instance-modification.md) to achieve this instead. - The target process definition must exist in Zeebe, i.e. it must be deployed and not yet deleted. - The migration plan can only map each `sourceElementId` once. - A mapping instruction's `sourceElementId` must refer to an element existing in the process instance's process definition. - A mapping instruction's `targetElementId` must refer to an element existing in the target process definition. - Catch event limitations: - A mapping instruction cannot detach a catch event from an active element. For example, a service task `A` has timer boundary event `T1` and will be migrated to the service task `B` has timer boundary event `T2`. If a mapping instruction between `A` -> `B` is provided, a mapping instruction for `T1` can only refer to `T2`. - Each catch event can only be the target of a mapping instruction once. - Two catch events in the source cannot be mapped to the same catch event in the target. - A catch event in the source cannot be mapped to a different type of catch event in the target. - The message subscription for the message catch event in the source process instance needs to be fully distributed before the migration. - Multi-instance body limitations: - Each child instance of a multi-instance body should be migrated separately because they belong to another process instance. - It is not possible to migrate a parallel multi-instance body to a sequential multi-instance body and vice versa. - Mapping instructions can only change the user task implementation from a job-worker user task to a Camunda user task, but not vice versa. The following limitations exist that may be supported in future versions: - Only [supported BPMN elements](#supported-bpmn-elements) can be migrated. - The following scenarios cannot be migrated: - An element that becomes nested in a newly added subprocess - An element that was nested in a subprocess is no longer nested in that subprocess - Mapping instructions cannot change the element type - The process instance must be in a wait state, i.e. waiting for an event or external input like job completion. It may not be taking a sequence flow or triggering an event while migrating the instance. A full overview of error codes can be found in the migration command [RPC](/apis-tools/zeebe-api/gateway-service.md#migrateprocessinstance-rpc) or [REST](/apis-tools/orchestration-cluster-api-rest/specifications/migrate-process-instance.api.mdx). :::tip If your specific case is not (yet) supported by process instance migration, you can use [cancel process instance](../../apis-tools/zeebe-api/gateway-service.md#cancelprocessinstance-rpc) and [create and start at a user-defined element](./process-instance-creation.md#create-and-start-at-a-user-defined-element) to recreate your process instance in the other process definition. Note that this results in new keys for the process instance and its associated variables, element instances, and other entities. ::: ### Supported BPMN elements The following BPMN elements are supported by the migration tool. #### Subprocesses #### Tasks #### Gateways #### Markers #### Events Type Start Intermediate Event Subprocess Event Subprocess non-interrupting Catch Boundary Boundary non-interrupting Message Timer Error Signal Escalation Compensation Conditional ## Use at your own risk Process instance migration is a powerful tool to change your process instances. However, use it with care. You can migrate the process instance to create situations that are not reachable by the regular execution. Consider the following example: ![The process instance waits on a task after a parallel joining gateway.](assets/process-instance-migration/migration-use-at-your-own-risk.png) The process instance completed the first tasks `A` and `B` and waits on task `C`. We could apply the following migrations, but the process instance may end up in an unintended situation: - If we map task `C` to `A` or `B`, the process instance is stuck on the parallel gateway. - If we map task `C` to `D`, the variables that would be provided by the message are not set, the task `D` could be processed with the wrong input. The process instance doesn't detect these situations. It is up to you to apply suitable migrations. When in doubt, we recommend testing your migration on a non-production cluster or using [Camunda Process Test](../../apis-tools/testing/getting-started.md). :::tip Often it's safer to migrate in multiple smaller steps rather than in one big migration. For example, you can start by migrating the process instance introducing changes to the inactive parts only to keep the current situation unchanged. In some cases it's useful to prepare the process instance before migrating. If your process needs specific variables immediately after migrating, you can set these before migrating the process instance. Both global and local variables are migrated automatically. Before migrating, you can also use process instance modification [activating an element](./process-instance-modification.md#activate-an-element) to avoid the process instance getting stuck on a parallel gateway, or [terminate an element instance](./process-instance-modification.md#terminate-an-element-instance) to get rid of a parallel flow. ::: --- ## Process instance modification Process instance modification is a powerful feature to repair a running process instance. The process instance may be stuck on an element, waiting for an event, or taking an unintended path because an external system is not available or doesn't respond as expected, for example. Use the [modification command](/apis-tools/zeebe-api/gateway-service.md#modifyprocessinstance-rpc) to skip or repeat a step in the process. Consider the following example: ![The process instance is stuck in the message catch event.](assets/process-instance-modification/process-instance-modification-example-1.png) The process contains two service tasks and a message catch event in between. The process instance completed the first task `A` and waits on the message catch event `B`. An external system will publish the message, but the external system is not available and can't continue the process. The process instance is stuck. ![We use the modification to skip the event and continue on the next task.](assets/process-instance-modification/process-instance-modification-example-2.png) We use the modification to repair the process instance. We "move the token" from the catch event `B` to the next task `C`. This operation is presented by two instructions in the modification command: - Terminate the instance of the catch event `B`. - Activate the element `C`. ![After the modification is applied, the message catch event is terminated and the next task is active.](assets/process-instance-modification/process-instance-modification-example-3.png) As a result of the command, the process instances terminated the instance of catch event `B` and activated the task `C`. Now, the process instance is not stuck anymore and can continue in the process. Generally, the process instance modification command can contain multiple instructions: - To activate an element of the process. - To terminate an active instance of an element. Read more about the behavior of the instructions in the following sections. :::note Use the process instance modification only in exceptional cases to repair the process instance. It is not recommended using it as a part of the regular flow of the process; find additional details [here](#use-at-your-own-risk). Instead, model all possible cases explicitly in your process. ::: ## Activate an element We can use the modification command to activate an element of the process. Consider the following example: ![The process instance waits on task. We use the modification command to activate a task from a parallel flow.](assets/process-instance-modification/process-instance-modification-activate-an-element.png) The process instance completed the first task `A` and waits on task `B`. Task `C` is connected to task `A` by a non-interrupting message catch event. An external system will publish the message, but it is not available. To correct the state of the process instance, we modify it and activate task `C`. As a result, task `C` is active, and a job worker can pick it up. The process instance activates the element in the same way as the regular flow; for example, if the incoming sequence flow of the element would be taken. The activation of the element can include the following steps: - Apply the input variable mappings. - Create the event subscriptions; for example, of boundary events. - Apply additional logic depending on the element; for example, create a job for a service task. ## Activate a nested element We can use the modification command to activate an element of the process nested inside an embedded or an event subprocess. This is a special case of [activating an element](#activate-an-element). Consider the following example: ![The process instance waits on a task inside a subprocess. We use the modification command to activate a task from a parallel flow in the same subprocess.](assets/process-instance-modification/process-instance-modification-activate-nested-element.png) The process instance completed the first task `A` in the embedded subprocess. It passed the inclusive gateway and waited on task `B`. Task `C` is also connected to the inclusive gateway, but the condition didn't match. The condition should match, but the job worker for task `A` provided unexpected variables. To correct the state of the process instance, we modify it and activate task `C`. As a result, task `C` is active in the same instance of the subprocess as the other task `B`. The process instance activates the element always in an existing instance of its subprocess. If the subprocess doesn't have an active instance, the process instance creates a new instance of the subprocess first. The creation of the subprocess can include the creation of the event subscriptions; for example, of boundary events. In contrast to a regular activation of the subprocess, the process instance doesn't activate the start event of the subprocess or apply any input variable mappings. If the subprocess itself is nested in another subprocess, the same procedure is applied to this subprocess. :::note The process instance can't activate the element if the subprocess has more than one active instance. It can't decide in which instance of the subprocess to activate the element. As a result, the process instance doesn't apply the activation instruction and rejects the command. ::: ## Activate an interrupting event subprocess We can use the modification command to activate an interrupting event subprocess of the process. Consider the following example: ![The process instance waits on task. We use the modification command to activate an interrupting event subprocess in the same scope.](assets/process-instance-modification/process-instance-modification-activate-interrupting-event-subprocess.png) The process instance completed the first task `A` and waits on task `B`. Task `C` is embedded in an interrupting message event subprocess. An external system will publish the message and interrupt the process, but it is not available. To correct the state of the process instance, we modify it and activate the interrupting event subprocess. As a result, the event subprocess is active and enters the start event. But the activation doesn't interrupt the process instance and terminate task `B`. So, both tasks `B` and `C` are active. If we want to simulate the interrupting behavior of the event subprocess, we need to add a modification instruction to [terminate the instance](#terminate-an-element-instance) of the task `B`. If the start event of the event subprocess has output variable mappings, we may need to [set the variables](#set-variables) with the activation instruction. ## Set variables We can use the modification command to set one or more variables together by activating an element of the process. Consider the following example: ![The process instance waits on task. We use the modification command to activate a task inside a non-interrupting message event subprocess.](assets/process-instance-modification/process-instance-modification-set-variables.png) The process instance completed the first task `A` and waited on task `B`. Task `C` is embedded in a non-interrupting message event subprocess. An external system will publish the message, but it is not available. To correct the state of the process instance, we modify it and activate the task `C` inside the non-interrupting message event subprocess. Additionally, we add variable instructions to the modification to set variables that should be provided by the message. The process instance sets the variables before activating the given element. As a result, the variables are available when applying the input variable mappings and creating the event subscriptions of the element. A variable instruction can define the [scope](variables.md#variable-scopes) of the variables. If a scope is defined, the process instance sets the variables as **local** variables in the given scope. For example, set the message variables as local variables of the event subprocess. The scope must be a flow scope of the activating element. If no scope is defined, the process instance sets the variables **globally** in the root scope of the process instance. ## Terminate an element instance We can use the modification command to terminate an active element instance of the process instance. Consider the following example: ![The process instance waits on a task inside a nested interrupting event subprocess. We use the modification command to terminate the event subprocess.](assets/process-instance-modification/process-instance-modification-terminate-element-instance.png) The process instance completed the first task `A` in the embedded subprocess. It triggered the interrupting timer event subprocess and terminated task `B` inside the embedded subprocess. An external system published a message and triggered the non-interrupting message boundary event on the subprocess. The process instance waits on task `C` inside the event subprocess and on task `D` connected to the boundary event. The job worker for task `C` failed to complete the job successfully. To skip the task and continue the process instance, we modify it and terminate the element instance of the event subprocess. As a result, the process instance terminates the event subprocess and the element instance of the task `C` that is inside the event subprocess. Additionally, the process instance terminates the embedded subprocess because it doesn't contain active element instances anymore. Generally, the modification applies the following rules: - If the terminating element instance is a subprocess, it terminates all active instances in the subprocess. - If the terminating element instance is a call activity, it terminates the child process instance. - If the terminating element instance was the last active instance inside a subprocess, it terminates the subprocess. - If the terminating element instance was the last active instance of the process instance, it terminates the process instance. If a terminating element instance is not active, the process instance doesn't apply the termination instruction and rejects the command. :::note The process instance can't terminate the last active element instance of a child process instance. As a result, the process instance doesn't apply the termination instruction and rejects the command. Instead, we can terminate the call activity that created the child process instance. ::: ## Move an element instance Use the modification command to move an element instance. The move operation combines termination and activation in a single instruction. It terminates the source element instance and activates a target element in one atomic operation. :::note This is the recommended way to relocate execution within a process instance, as it ensures the operation is performed atomically and correctly handles complex scenarios such as multi-instance subprocesses. ::: ### Use the move instruction API When using the [modification API](../../apis-tools/orchestration-cluster-api-rest/specifications/modify-process-instance.api.mdx), you can provide a move instruction with the following parameters: - **Source element**: Specify the element instance to move from, either by element ID or by element instance key. - **Target element**: Specify the element ID of the element to activate. - **Ancestor scope instruction** (for nested elements): Controls how the target element is activated within subprocess hierarchies. See [Move elements inside multi-instance subprocesses](#moving-elements-inside-multi-instance-subprocesses) for details on using this with multi-instance activities. ### Move elements inside multi-instance subprocesses Move modifications are supported for elements inside multi-instance subprocesses. When you move an element instance within a multi-instance subprocess, the process instance terminates only that specific element instance and activates exactly one target element in the same instance of the multi-instance subprocess. With move modifications in multi-instance subprocesses, you can relocate execution within a specific iteration of a multi-instance activity without affecting other instances or creating new instances of the multi-instance subprocess. The **ancestor scope instruction** provides three strategies for controlling how the target element is activated: - `direct`: Specify the key of the ancestor scope in which the element instance should be created. - `inferred`: Let the engine automatically find the appropriate ancestor element scope. - `sourceParent`: Use the source element's parent scope (recommended for moving between sibling elements inside multi-instance subprocesses, as it offers better performance than `inferred`). :::note While move modifications are supported for elements inside multi-instance subprocesses, add (activate) modifications are not. You cannot directly add or activate new tokens for elements inside multi-instance subprocesses. Use move modifications to relocate execution in multi-instance contexts. ::: ## Execute the modification instructions A modification command can contain multiple activation and termination instructions. The process instance applies these instructions in a specific order: 1. Apply all activation instructions. 2. Apply all termination instructions. The order of the instructions matters if the modification terminates the last active instances of the process instance or inside a subprocess, and activates an element in the process instance or the subprocess. Since the process instance applies the activation instructions first, the process instance or the subprocess still has an active instance and is not terminated. If the process instance can't apply one of the modification instructions, it rejects the modification command. For example, if one of the terminating element instances is not active. As a result, the process instance is not modified and is in the same state as before. It applies the instructions of a modification command in a **transactional** way (i.e. apply all or nothing). ## Limitations Currently, we can't modify the process instance in all possible ways. In the following cases, the process instance can't apply the modification instructions and rejects the modification command. - If the activating element is a BPMN element of the type: - A start event of a process or a subprocess - A boundary event - An event that belongs to an event-based gateway - A sequence flow - If the modification terminates all active instances of a child process instance. ## Use at your own risk Process instance modification is a powerful tool to repair a process instance. However, use it with care. You can modify the process instance to create situations that are not reachable by the regular execution. Consider the following example: ![The process instance waits on a task after a parallel joining gateway.](assets/process-instance-modification/process-instance-modification-use-at-your-own-risk.png) The process instance completed the first tasks `A` and `B` and waits on task `C`. We could apply the following modifications, but the process instance may end up in an unintended situation: - If we activate task `A` again, the process instance is stuck on the parallel gateway. - If we activate task `D` and don't set all variables that would be provided by the message, the task `D` could be processed with the wrong input. - If we activate task `E` inside the interrupting event subprocess, the process instance doesn't interrupt task `C` and the processing of the tasks could override variables. The process instance doesn't detect these situations. It is up to you to apply suitable modifications. When in doubt, we recommend testing your modification on a non-production cluster or using [Camunda Process Test](../../apis-tools/testing/getting-started.md). --- ## Processes A [process](/reference/glossary.md#process) is a defined sequence of distinct steps or tasks representing your business logic. Examples of a process could be an e-commerce shopping experience, or onboarding a new employee. At large, process orchestration is a technology that coordinates the various moving parts (or endpoints) of a business process, and sometimes even ties multiple processes together. Process orchestration helps you work with the people, systems, and devices you already have—while achieving goals around end-to-end process automation. For example, with Camunda you can [orchestrate human tasks](../../guides/getting-started-orchestrate-human-tasks.md), [microservices](/guides/getting-started-example.md), and [APIs](/guides/getting-started-orchestrate-apis.md). A **[job worker](./job-workers.md)** implements the business logic required to complete a task. You can choose to write a worker as a microservice, or also as part of a classical 3-tier application, as a \(lambda\) function, via command line tools, etc. Running a process broadly requires three steps: 1. Deploy a process to Camunda 8. 2. Implement and register job workers for tasks in the workflows. 3. Create new instances of the process. However, if you haven't yet, design the process: ## BPMN Camunda uses **[Business Process Model and Notation (BPMN) 2.0](/components/modeler/bpmn/bpmn.md)** to represent processes. The visual nature of BPMN enables greater collaboration between different teams, and is employed by numerous organizations globally. ![process example](./assets/order-process.png) :::note New to BPMN? Visit our step-by-step introductory guide on [automating a process using BPMN](/components/modeler/bpmn/automating-a-process-using-bpmn.md) ::: ## Modeling BPMN Camunda provides [Modeler](/components/modeler/about-modeler.md), a free and open source BPMN modeling tool to create BPMN diagrams and configure their technical properties. Camunda offers two Modeler tools to design and implement your diagrams: - [Camunda Hub](/components/hub/workspace/modeler/index.md): Integrate seamlessly with Camunda 8 SaaS and Self-Managed installations. - [Desktop Modeler](/components/modeler/desktop-modeler/index.md): Design, view, and edit models using this desktop application. Install and use Desktop Modeler locally, all while integrating your local development environment. :::note New to modeling a process? Visit our [getting started guide](/components/hub/workspace/modeler/collaboration/design-your-process.md). ::: ## Process execution The simplest kind of process is an ordered sequence of tasks. Whenever process execution reaches a task, [Zeebe](/components/zeebe/zeebe-overview.md) (the workflow engine inside Camunda 8) creates a job that can be requested and completed by a job worker. ![process-sequence](assets/order-process.png) Process orchestration typically follows the steps below: 1. A process instance reaches a task, and Zeebe creates a job that can be requested by a worker. 2. Zeebe waits for the worker to request a job and complete the work. 3. Once the work is complete, the flow continues to the next step. 4. If the worker fails to complete the work, the process remains at the current step, and the job could be retried until it's successfully completed. As Zeebe progresses from one task to the next in a process, it can move custom data in the form of [variables](/components/concepts/variables.md). Variables are key-value pairs and part of the process instance. ![data-flow](assets/process-data-flow.png) Any job worker can read the variables and modify them when completing a job so data can be shared between different tasks in a process. --- ## Resource deletion Use resource deletion to remove resources from a cluster when they are no longer needed or should no longer be used. Deleting resources: 1. **Frees storage space**, as Zeebe no longer needs to keep the definition in its state. 2. **Prevents new instances from being created**, which can help avoid usage of faulty process definitions. The following resource types can be deleted: 1. [Process definitions](./processes.md) 2. [Decision Requirements Graphs (DRG)](../modeler/dmn/decision-requirements-graph.md) Delete a resource using [Operate](../../components/operate/userguide/delete-resources.md) or by sending the [delete resource command](/apis-tools/zeebe-api/gateway-service.md#deleteresource-rpc) to the Zeebe API. ## Deleting a process definition Delete a process definition by sending a [delete resource command](/apis-tools/zeebe-api/gateway-service.md#deleteresource-rpc) and providing the `process definition key` as the `resource key`. You can delete any version of a process definition. After deletion, the definition no longer exists in Zeebe's state and new process instances cannot be created for it. Attempts to create a new instance result in a `NOT_FOUND` exception. Zeebe **never** reuses a process version. Even after deletion, Zeebe continues tracking version numbers. Deploying a new process with the same ID increments the version as usual. ### Deleting the latest version When deleting the `latest` version of a process definition, the previous version becomes the new `latest`. For example, if three versions exist and `Version 3` is the latest, deleting it results in the following: - No new instances can be created for `Version 3`. - Creating a new process instance using `latest` creates an instance of `Version 2`. - If `Version 2` contains timer start events, they are reactivated and triggered according to their schedule. - If `Version 2` contains message or signal start events, they are reactivated. Publishing a message or broadcasting a signal creates a new process instance of `Version 2`. Deleting `Version 2` before `Version 3` produces the same behavior, except `Version 1` becomes the new `latest`. ### Call activities A [call activity](/components/modeler/bpmn/call-activities/call-activities.md) references a process by ID. If all process definitions for that process ID are deleted, Zeebe creates an [incident](/components/concepts/incidents.md) on the call activity indicating that the referenced process cannot be found. ### Limitations You cannot delete a process definition that has one or more running process instances. Terminate or complete all running instances before deleting the definition. ### Historic data Optionally enable historic data deletion to permanently remove all data related to the process definition from secondary storage. :::warning Deletion is irreversible. Restore deleted data only by restoring a backup of your cluster. ::: Delete historic data for a process definition using the [Orchestration Cluster API](/apis-tools/orchestration-cluster-api-rest/specifications/delete-resource.api.mdx) and set the `deleteHistory` flag to `true`. You can also delete a process definition with historic data using Operate. See the [Operate user guide](../operate/userguide/delete-resources.md#delete-process-definition). If you only want to delete process instance data, see [process instance deletion](./process-instance-deletion.md). #### Eventual consistency Historic data deletion runs asynchronously. Depending on the amount of data, it may take time for the data to be removed and for it to disappear from Operate and Tasklist. ## Deleting a decision requirements graph Delete a decision requirements graph (DRG) by sending a [delete resource command](/apis-tools/zeebe-api/gateway-service.md#deleteresource-rpc) and providing the `decision requirements key` as the `resource key`. Deleting a DRG also deletes the decisions it contains. Attempts to evaluate a deleted decision result in a `NOT_FOUND` exception. Deleting a DRG also deletes historical data. ### Business rule tasks A [business rule task](/components/modeler/bpmn/business-rule-tasks/business-rule-tasks.md) references a decision by ID. If all versions of that decision are deleted, Zeebe creates an incident on the business rule task indicating that no decision with the given ID can be found. ### Historic data Optionally enable historic data deletion to permanently remove all data related to the decision definition from secondary storage. :::warning Deletion is irreversible. Restore deleted data only by restoring a backup of your cluster. ::: Delete historic data for a decision definition using the [Orchestration Cluster API](/apis-tools/orchestration-cluster-api-rest/specifications/delete-resource.api.mdx) and set the `deleteHistory` flag to `true`. You can also delete historic data for a decision definition in Operate. See the [Operate user guide](../operate/userguide/delete-resources.md#delete-decision-definition). If you only want to delete decision instance data, see [decision instance deletion](./decision-instance-deletion.md). #### Eventual consistency Historic data deletion runs asynchronously. Depending on the amount of data, it may take time for the data to be removed and for it to disappear from Operate and Tasklist. --- ## Signals Signals are a similar concept to [messages](messages.md). However, messages are correlated to a specific process instance, whereas signals can trigger _all_ the matching signal events with a single broadcast. Depending on the type of [signal catch events](../modeler/bpmn/signal-events/signal-events.md), the process instance will respond accordingly. ## Broadcasting signals You can broadcast signals in several ways: - Using a [signal throw event or signal end event](../modeler/bpmn/signal-events/signal-events.md#signal-throw-events) - Using the [Orchestration Cluster REST API Broadcast signal](/apis-tools/orchestration-cluster-api-rest/specifications/broadcast-signal.api.mdx) endpoint - Using Zeebe's [`BroadcastSignal` RPC](/apis-tools/zeebe-api/gateway-service.md#broadcastsignal-rpc) ## Signal subscriptions When you broadcast a signal it triggers _all_ signal subscriptions that match the signal name. When a process instance encounters a signal catch event it creates a new signal subscription. This process instance waits until a signal with a matching name is broadcasted. You can define the signal name in the process definition. Deploying a process with a signal start event also creates a new signal subscription. In this case, the triggered subscription starts a new process instance. ## Signal cardinality A broadcasted signal iterates over _all_ available subscriptions. As a result, a single broadcast triggers _all_ the signal catch events that match the signal name, and _all_ [partitions](../zeebe/technical-concepts/partitions.md). :::caution Signals can negatively impact the performance of Camunda 8. Performance is impacted in two ways: - Signals trigger _all_ available subscriptions that match the signal name, potentially resulting in the continued execution of many processes. - Signals are broadcasted to _all_ partitions, resulting in lots of network traffic. This scales linearly with the number of partitions. ::: --- ## Example user task listener Beginner Time estimate: 35 minutes In Camunda 8, **user task listeners** allow you to run custom logic in response to changes to a user task, such as when the `creating`, `assigning`, or `completing` event occurs. User task listeners facilitate the integration of custom logic into your workflows. For more details, refer to the [User Task Listeners concept](/components/concepts/user-task-listeners.md). This guide walks you through: - Defining a task listener using **Camunda Modeler** - Implementing a task listener as a job worker - Verifying the result in **Operate** and **Tasklist** ## Prerequisites You must have access to a Camunda 8 SaaS account.
Have you signed up for Camunda yet?
You must also know how to model a process with a user task. If you haven't done this before, first follow the steps in our guide to [get started with human task orchestration](/guides/getting-started-orchestrate-human-tasks.md). Additionally, you need the following: - Java ≥ 8 - Maven - IDE (IntelliJ, VSCode, or similar) - Download and unzip or clone the [repository](https://github.com/camunda/camunda-platform-tutorials), then navigate to: `camunda-platform-tutorials/quick-start/task-listeners/worker-java` ## Step 1: Create a process with a user task in Modeler 1. Launch **Camunda Modeler**. 2. Create a new BPMN file. 3. Add a user task named `Assigned by creating task listener`. ![Web modeler showing a process with user task](./assets/user-task-listeners-guide/1-process-with-user-task.png) :::note In this guide, you can also use the example BPMN from the repo located in `camunda-platform-tutorials/quick-start/task-listeners/worker-java/src/main/resources/Quick_Start_Task_Listeners.bpmn`. In that case, just explore the BPMN using the steps below, but do not adjust the model in steps 2-4. ::: ## Step 2: Select the user task 1. Click the **user task** (e.g., “Assigned by creating task listener”). 2. In the right-hand **properties panel**, scroll to **Task listeners**. ![Properties panel shows user task details including task listeners](./assets/user-task-listeners-guide/2-user-task-selected-scrolled-to-task-listeners.png) ## Step 3: Define a task listener We'll now add a new task listener to the user task and define its properties. 1. Click the plus sign in the **Task listeners** section to add a new task listener. 2. Under **Event type**, select **creating**. 3. Under **Listener type**, enter `assign_new_task`. ![Creating listener is defined with type assign_new_task](./assets/user-task-listeners-guide/3-creating-listener-defined.png) :::info You've now defined a **creating** task listener for this user task. When a process instance arrives at this user task, the `creating` event is triggered, and a job of type `assign_new_task` is created. A job worker can then activate this job to execute the external logic and complete it, approving the creation of the user task. ::: ## Step 4: Create a cluster ## Step 5: Deploy and run the process 1. Click **Deploy & run**. 2. Select your **Camunda 8 cluster**. 3. Add an `assignee` or `manager` variable as JSON data. For example, `{ "assignee": "john.doe@camunda.com" }`. 4. Click **Deploy & run**. ![Deploy and run process](./assets/user-task-listeners-guide/5-deploy-and-run.png) ## Step 6: Understand what happens to the user task Now, we'll explore what happened to the user task. We'll see that the listener blocks the creation. 1. In the top left corner of the screen, click the square-shaped **Camunda components** button. 2. Navigate to **Tasklist** and notice that there is no task in Tasklist yet. ![No tasks found in Tasklist](./assets/user-task-listeners-guide/6.2-no-tasks-found.png) 3. Navigate to **Operate** to see your process instance with a token waiting at the user task by clicking on the active process instance in the **Dashboard**. ![Active process instances in Operate Dashboard](./assets/user-task-listeners-guide/6.3-active-process-instances.png) 4. Click the **Process instance key** to [inspect the process instance](/components/operate/userguide/basic-operate-navigation.md#inspect-a-process-instance). ![Inspect process instance in Operate](./assets/user-task-listeners-guide/6.4-inspect-process-instance.png) 5. Click the user task and then click the **Listeners** tab to see that the **Creating** listener is **Active**. ![Inspect listeners for user task in Operate](./assets/user-task-listeners-guide/6.5-inspect-listeners.png) 6. Take a moment to understand the properties of the listener, for example verify that the listener type is what you defined in the process model. This listener is a job that can be activated and handled by a job worker. ## Step 7: Implement the listener Next, we'll run the listener application to execute our external logic, and complete the listener job to continue the user task's creation. ### Create credentials for your Camunda client ### Create a job worker to implement the task listener Next, we’ll create a worker that listens to the user task's events by associating it with the **Listener type** we specified on the task listener in the BPMN diagram. 1. Open the downloaded or cloned project ([repo](https://github.com/camunda/camunda-platform-tutorials), then `cd` into `camunda-platform-tutorials/quick-start/task-listeners/worker-java`) in your IDE. 2. Add your credentials to `application.properties`. Your client ID and client secret are available from the previous section in the credential text file you downloaded or copied. Go to the cluster overview page to find your **region Id** and **cluster Id** (in your client credentials under the **API** tab within your cluster). 3. In the `Listener.java` file, change the type to match what you specified in the BPMN diagram. If you followed the previous steps for this guide and entered “assign_new_task”, no action is required. 4. After making these changes, perform a Maven install, then run the Listener.java `main` method via your favorite IDE. If you prefer using a terminal, run `mvn package exec:java`. ## Step 8: Verify the result in Operate and Tasklist Now that the task listener is running, the listener job will have been handled and completed. Let's see what effects this has had. 1. Navigate to Operate and see that the listener that was **Active** previously, has now been **Completed**. ![Listener has been completed in Operate](./assets/user-task-listeners-guide/8.1-verify-listener-completed.png) 2. Navigate to Tasklist and see that the task is available and assigned to the assignee or manager that you provided. ![Task is assigned in Tasklist](./assets/user-task-listeners-guide/8.2-verify-task-assigned.png) ## Suggestions for further exploration To build your understanding of task listeners, you can also: - Stop the listener application. - Start a new instance of the process, and notice that the task does not appear in Tasklist. - Check Operate, and notice that a creating listener is active. - Restart the listener application and notice that the listener failed, and an incident is raised. - Set a variable `assignee` or `manager` in the process instance, and resolve the incident. - Check Tasklist, and notice that the task is assigned to the assignee or manager that you provided. Further, you can try the following: - Add a separate listener for another event type, and [trigger that event](/components/concepts/user-task-listeners.md#trigger-a-user-task-listener). - [Access the user task's data](/components/concepts/user-task-listeners.md#accessing-user-task-data) from the **activated job**. - [Correct the assignee](/components/concepts/user-task-listeners.md#correcting-user-task-data) in the listener by adding a **job result** to the complete job command. - [Deny the assignment](/components/concepts/user-task-listeners.md#denying-the-lifecycle-transition) from the listener to avoid the task's assignment altogether. ## Additional resources and next steps - Learn more about Camunda 8 and what it can do by reading [What is Camunda 8](/components/components-overview.md) or watching our [Overview video](https://bit.ly/3TjNEm7) in Camunda Academy. - Get your local environment ready for development with Camunda 8 by [setting up your first development project](/guides/getting-started-example.md). --- ## User task listeners A [user task listener](/reference/glossary.md#user-task-listener) allows users to react to specific user task lifecycle events. :::tip Try out our [getting started with user task listeners guide](/components/concepts/user-task-listeners-guide.md). ::: ## About user task listeners User task listeners provide flexibility and control over [user task](/components/modeler/bpmn/user-tasks/user-tasks.md) behavior: - They can react to user task lifecycle events, such as assigning and completing. - They can access user task data, such as the assignee, to execute task-specific business logic. - They can dynamically correct user task data during execution, allowing adjustments to key attributes such as the assignee, due date, and priority. - They can deny state transitions, rolling back the task to its previous state, which enables validation of task lifecycle changes. ### Use cases User task listeners are useful in the following scenarios: - Implementing complex user task assignment or reassignment logic. - Validating user task lifecycle changes, e.g. completing with valid variables. - Notifying users of new task assignments with contextual information. - Reacting to task completions with custom logic. ### User task lifecycle A user task has the following lifecycle. A user task listener can react to the events highlighted in orange. ```mermaid stateDiagram-v2 direction LR [*] --> creating creating --> created created --> assigning assigning --> created created --> updating updating --> created created --> completing completing --> created completing --> completed creating --> canceling created --> canceling assigning --> canceling updating --> canceling completing --> canceling canceling --> canceled classDef listenerEvent fill:#fc5d0d,color:white,font-weight:bold class creating listenerEvent class assigning listenerEvent class updating listenerEvent class completing listenerEvent class canceling listenerEvent ``` ### Blocking behavior User task listeners operate in a blocking manner, meaning the user task lifecycle transition is paused until the task listener completes. This ensures that any corrections or validations defined by the task listener are fully applied before the task transition continues. ## Trigger a user task listener The supported user task listener events can be triggered in the following ways. | Event | Triggered | | :----------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `creating` | When a process instance reaches a user task. | | `assigning` | When the [assign user task API](/apis-tools/orchestration-cluster-api-rest/specifications/assign-user-task.api.mdx) is called.When activating a user task that [specifies an `assignee`](/components/modeler/bpmn/user-tasks/user-tasks.md#assignments) in the process.When a user task is assigned using the [Tasklist interface](/components/tasklist/userguide/managing-tasks.md#assign-tasks). | | `updating` | When the [update user task API](/apis-tools/orchestration-cluster-api-rest/specifications/update-user-task.api.mdx) is called. When the [update element instance variables API](/apis-tools/orchestration-cluster-api-rest/specifications/create-element-instance-variables.api.mdx) is called on a user task instance.When the [set variables RPC](/apis-tools/zeebe-api/gateway-service.md#setvariables-rpc) is called on a user task instance.When variables are set at a user task scope using the [Operate interface](/components/operate/userguide/resolve-incidents-update-variables.md). | | `completing` | When a user task is completed using the [Tasklist interface](/components/tasklist/userguide/managing-tasks.md#complete-a-task).When the [complete user task API](/apis-tools/orchestration-cluster-api-rest/specifications/complete-user-task.api.mdx) is called. | | `canceling` | When a canceling process instance terminates a user task.When a [catch event](/components/modeler/bpmn/events.md) interrupts a user task. | Once triggered, the workflow engine creates a job that you can process using a job worker. ## Define a user task listener You can configure user task listeners either: - Per BPMN user task element. - For all user tasks in the cluster. See [Global user task listeners](/components/concepts/global-user-task-listeners.md). ### User task listener properties Each user task listener has the following properties: | Property | Description | | :---------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `eventType` | (Required) Specifies the user task event type that triggers the listener. Supported values are `creating`, `assigning`, `updating`, `completing`, and `canceling`. | | `type` | (Required) The name of the job type, used as a reference to specify which job workers request the respective task listener job. For example, `order-items`. The `type` can be specified as any static value (`myType`), or as a FEEL expression prefixed with `=`, that evaluates to a FEEL string; for example, `= "order-" + priorityGroup`. This is a case-sensitive field, if supported by the underlying operating system. For example, `orderProcess` refers to a different worker than `OrderProcess`. | | `retries` | (Optional) The number of retries for the user task listener job (defaults to 3 if omitted). | :::note If multiple user task listeners of the same `eventType` (such as multiple `assigning` listeners) are defined on the same user task, they are executed sequentially, one after the other, in the order they are defined in the BPMN model. ::: ## Implement a user task listener User task listeners are implemented using [job workers](/components/concepts/job-workers.md), similar to execution listeners and service task jobs. The job worker processes the task listener job, can apply corrections, and may optionally deny the lifecycle transition. See the [job worker documentation](/apis-tools/java-client/job-worker.md) for examples of how to create a job worker and handler that can also process user task listener jobs. ### Accessing user task data User task-specific data, such as `assignee` and `priority`, are accessible through the `userTask` property of the user task listener job. The following user task attributes can be accessed from the activated job's `userTask` property: | Attribute | Description | | :------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `action` | A custom action value provided along with the request that triggered this event. If none was provided, it defaults to one of `assign`, `claim`, `update`, or `complete`. | | `assignee` | The user assigned to the task. If not specified, the task is unassigned. Refer to [assignments](/components/modeler/bpmn/user-tasks/user-tasks.md#assignments) for more details. | | `candidateGroups` | Specifies the groups of users that the task can be assigned to. Refer to [assignments](/components/modeler/bpmn/user-tasks/user-tasks.md#assignments) for more details. | | `candidateUsers` | Specifies the users that the task can be assigned to. Refer to [assignments](/components/modeler/bpmn/user-tasks/user-tasks.md#assignments) for more details. | | `changedAttributes` | Lists the user task attributes that have changed with the event. Refer to the [changed attributes](#changed-attributes) section below for more details. | | `dueDate` | Specifies the due date of the task. Refer to [scheduling](/components/modeler/bpmn/user-tasks/user-tasks.md#scheduling) for more details. | | `followUpDate` | Specifies the follow-up date of the task. Refer to [scheduling](/components/modeler/bpmn/user-tasks/user-tasks.md#scheduling) for more details. | | `formKey` | The form linked to the user task, referenced by its uniquely identifying key. Refer to [user task forms](/components/modeler/bpmn/user-tasks/user-tasks.md#user-task-forms) for more details. | | `priority` | The task’s priority level. Refer to [priority](/components/modeler/bpmn/user-tasks/user-tasks.md#define-user-task-priority) for more details. | | `userTaskKey` | The unique key identifying the user task. | Below is an example of accessing the `assignee` value from the activated job in Java: ```java final JobHandler userTaskListenerHandler = (jobClient, job) -> { // Access the 'assignee' from the job's user task property // highlight-start final String assignee = job.getUserTask().getAssignee(); // highlight-end System.out.println("The assignee for this user task is: " + assignee); // remaining job handler logic }; ``` This user task data can be leveraged to customize the behavior of the user task listener job worker. #### Changed attributes The `changedAttributes` attribute lists which user task attributes have changed with the event. :::note User task data corrections are taken into account. For example, consider a user task with three `assigning` listeners defined. When assigning the user task, the first listener sees the `assignee` attribute in the `changedAttributes`. If it corrects the priority, a subsequent assigning listener sees both the `assignee` and the `priority` attributes as changed attributes. Now, this second listener corrects the priority back to the value it had before assigning. The third listener sees only the `assignee` attribute as changed attribute, because the priority is no longer changed with the event. ::: #### Task headers Configured [task headers](/components/modeler/bpmn/user-tasks/user-tasks.md#task-headers) on the user task are available in the job's custom headers. ### Correcting user task data User task listeners can correct user task data before the lifecycle transition is finalized. Corrections allow user task listeners to update specific attributes of the user task, such as the assignee, due date, follow-up date, candidate users, candidate groups, and priority. These corrections are immediately available to any subsequent task listeners and are applied to the user task when the lifecycle transition is finalized, without triggering the `UPDATING` lifecycle event. If a lifecycle transition is denied by a listener, no corrections are applied to the user task. Below is an example of how to correct the user task data from a job worker while completing the user task listener job in Java: ```java final JobHandler completeTaskListenerJobWithCorrectionsHandler = (jobClient, job) -> jobClient .newCompleteCommand(job) // highlight-start .withResult( r -> r.forUserTask() .correctAssignee("john_doe") // assigns the user task to 'john_doe' .correctDueDate(null) // preserves the current 'dueDate' .correctFollowUpDate("") // clears the 'followUpDate' .correctCandidateUsers(List.of("alice", "bob")) // sets candidate users .correctCandidateGroups(List.of()) // clears the candidate groups .correctPriority(80)) // sets the priority to 80 // highlight-end .send(); client.newWorker() .jobType("user-task-listener-completion") // type of the user task listener job .handler(completeTaskListenerJobWithCorrectionsHandler) .open(); ``` #### On correcting the assignee The assignee can be corrected in the `creating` listener only if the process hasn't specified an assignee for this user task already. For example, if the user task's `assignee` expression evaluates to `null`. :::tip To set an assignee when creating the user task, review [specifying the assignee in the process](components/modeler/bpmn/user-tasks/user-tasks.md#assignments), or verify the assignee is not yet defined by the process by [accessing the `assignee` attribute in the job headers](#accessing-user-task-data). To change the assignee specified by the process, correct it with the `assigning` event. ::: ### Denying the lifecycle transition User task listeners can deny the user task lifecycle transition belonging to the lifecycle event. For example, it can deny the completion of a task in reaction to the completing event, effectively preventing a user request to complete the task. When a lifecycle transition is denied: - **Corrections discarded**: Any corrections made by preceding listeners within the same lifecycle transition are discarded. - **Task state preserved**: The user task retains its state and data as if the lifecycle event never occurred. This capability is particularly useful for implementing validation logic or enforcing business rules before allowing a user task lifecycle transition to proceed. Below is an example of how to deny a user task lifecycle transition from a job worker while completing the user task listener job in Java: ```java final JobHandler denyUserTaskLifecycleTransitionHandler = (jobClient, job) -> jobClient .newCompleteCommand(job) // highlight-start .withResult(r -> r.forUserTask().deny(true)) // highlight-end .send(); ``` Not all events can be denied. For example, it's not possible to deny the creation or cancelation of a user task. Currently, user task listeners can deny the lifecycle transition for the following events: - `assigning` - `updating` - `completing` ## Expression evaluation and incident behavior ### Expression evaluation User task listener properties, such as job `type` or `retries`, are evaluated right before the job creation for the listener. ### Incident recovery If a user task listener job fails or its expression evaluation raises an [incident](/components/concepts/incidents.md), the lifecycle transition is paused until the incident is resolved. There are two types of incidents for task listeners: - **Expression evaluation failure**: Raised when a listener property expression fails to evaluate. After the incident is resolved, the entire lifecycle transition is retried, re-executing all listeners configured for the transition, including those that previously completed successfully. - **Job failure**: If a listener job fails, it is retried according to the `retries` property. If all retries are exhausted and the job still fails, an incident is raised. Once resolved, only the failed listener job is retried, allowing the lifecycle transition to resume without re-executing successfully completed listeners. ## Limitations User task listeners have the following limitations: - **No variable handling**: User task listener jobs cannot be completed if variables are provided. - **No BPMN error throwing**: Throwing BPMN errors from user task listener jobs is not supported. ### Legacy behavior before 8.10 User task listeners are designed for use with the [Orchestration Cluster API](../../apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). Before Camunda 8.10, Tasklist V1 and the deprecated Tasklist API had the following limitations when used with user task listeners: - **Tasklist v1 does not list tasks with pending task listeners**: If a task's lifecycle transition is blocked by a pending task listener, Tasklist v1 does not display the task in the task queue. However, Tasklist v1 can still show the details of such a task. - **Tasklist v1 incorrectly lists creating tasks when filtering for the "all" status (open and completed)**: If a task's creation is blocked by a pending task listener, Tasklist v1 includes it in the task queue when filtering for the "all" status, even though the task has not yet been created. - **Tasklist v1 API cannot filter for tasks with pending task listeners**: The deprecated Tasklist API cannot filter for the following task states when searching tasks: `CREATING`, `ASSIGNING`, `UPDATING`, `COMPLETING`, `CANCELING`. Tasks in these states are included in responses when not filtering by state. - **Tasklist v1 API responses may not reflect corrections applied by task listeners**: Requests made to the deprecated Tasklist API can trigger a listener. Responses to such requests will appear as if the listener did not make any [corrections](#correcting-user-task-data) to the user task data, even when the listener did make corrections. ## Related resources - [Job workers (basics)](/components/concepts/job-workers.md) - [Job workers (Java client)](/apis-tools/java-client/job-worker.md) - [Incidents](/components/concepts/incidents.md) - [Expressions](/components/concepts/expressions.md) - [Execution listeners](/components/concepts/execution-listeners.md) - [User tasks](/components/modeler/bpmn/user-tasks/user-tasks.md) --- ## Variables [Variables](/reference/glossary.md#variable) are part of a [process instance](/reference/glossary.md#process-instance) and represent the data of the instance. A variable has a name and a JSON value. The visibility of a variable is defined by its variable scope. When [automating a process using BPMN](/components/modeler/bpmn/automating-a-process-using-bpmn.md) or [orchestrating human tasks](../../guides/getting-started-orchestrate-human-tasks.md), you can leverage the scope of these variables and customize how variables are merged into the process instance. ## Variable names The name of a variable can be any alphanumeric string including the `_` symbol. For a combination of words, it's recommended to use the `camelCase` or the `snake_case` format. The `kebab-case` format is not allowed because it contains the operator `-`. When accessing a variable in an expression, keep in mind the variable name is case-sensitive. Restrictions of a variable name: - It may not start with a **number** (e.g. `1stChoice` is not allowed; you can use `firstChoice`instead). - It may not contain **whitespaces** (e.g. `order number` is not allowed; you can use `orderNumber` instead). - It may not contain an **operator** (e.g. `+`, `-`, `*`, `/`, `=`, `>`, `?`, `.`). - It may not be a **literal** (e.g. `null`, `true`, `false`) or a **keyword** (e.g. `function`, `if`, `then`, `else`, `for`, `between`, `instance`, `of`, `not`). - It must stay within the length limits of the target backend: up to **32,768 characters** with Elasticsearch/OpenSearch-backed secondary storage and up to **256 characters** with RDBMS-backed secondary storage. :::note Length is enforced using Java string length semantics rather than raw UTF-8 byte counts. Most common characters count as one character, while characters represented as surrogate pairs in Java count as two. Because of that, the effective visible-character limit can be lower for some inputs. ::: ## Variable values The value of a variable is stored as a JSON value. It can have one of the following types: - String (e.g. `"John Doe"`) - Number (e.g. `123`, `0.23`) - Boolean (e.g. `true` or `false`) - Array (e.g. `["item1" , "item2", "item3"]`) - Object (e.g. `{ "orderNumber": "A12BH98", "date": "2020-10-15", "amount": 185.34}`) - Null (`null`) :::note Numbers are subject to the following numeric limits: - Integer numbers are effectively limited to the 64‑bit integer range. - Non‑integer numbers are stored as IEEE‑754 double‑precision values, which provide roughly 15–17 significant decimal digits rather than arbitrary BigDecimal precision. If you need arbitrary-precision or very large numbers, consider storing them as strings or in an external data store instead of process variables. ::: ## Variable size limitation The payload of a process instance is limited to 4 MB. This limit includes both process variables and workflow engine–internal data, so less than 4 MB is available for variables alone. The effective limit depends on the operation. As a rule of thumb, ~1.5 MB is considered safe for commands or events that include variables, such as starting a process instance or completing a job. In these cases, the engine may append follow-up records that temporarily duplicate the variable payload within the same batch. To avoid production issues, leave headroom below the limit—for example, target ≤1 MB—and validate with a production-like test case. If the payload size is uncertain, run a quick test to confirm behavior. :::note Regardless, we don't recommend storing much data in your process context. Refer to our [best practice on handling data in processes](/components/best-practices/development/handling-data-in-processes.md). ::: ## Variable scopes Variable scopes define the _visibility_ of variables. The root scope is the process instance itself. Variables in this scope are visible everywhere in the process. When the process instance enters a subprocess or an activity, a new scope is created. Activities in this scope can observe all variables of this and of higher scopes (i.e. parent scopes). However, activities outside of this scope can not observe the variables which are defined in this scope. If a variable has the same name as a variable from a higher scope, it covers this variable. Activities in this scope observe only the value of this variable and not the one from the higher scope. The scope of a variable is defined when the variable is created. By default, variables are created in the root scope. ![variable-scopes](assets/variable-scopes.png) This process instance has the following variables: - `a` and `b` are defined on the root scope and can be seen by **Task A**, **Task B**, and **Task C**. - `c` is defined in the subprocess scope and can be seen by **Task A** and **Task B**. - `b` is defined again on the activity scope of **Task A** and can be seen only by **Task A**. It covers the variable `b` from the root scope. ### Variable propagation When variables are merged into a process instance (e.g. on job completion, on message correlation, etc.) each variable is propagated from the scope of the activity to its higher scopes. The propagation ends when a scope contains a variable with the same name. In this case, the variable value is updated. If no scope contains this variable, it's created as a new variable in the root scope. This automatic propagation behavior differs depending on the BPMN element: - **Embedded subprocesses**: Local variables created in the subprocess (via input mappings) stay within the subprocess scope unless you explicitly propagate them with output mappings. - **Call activities**: The child process runs in its own variable scope. You can configure which variables are passed to the child and which are returned to the parent using the call activity's variable propagation settings and input/output mappings. ![variable-propagation](assets/variable-propagation.png) The job of **Task B** is completed with the variables `b`, `c`, and `d`. The variables `b` and `c` are already defined in higher scopes and are updated with the new values. Variable `d` doesn't exist before and is created in the root scope. ### Local variables In some cases, variables should be set in a given scope, even if they didn't exist in this scope before. This is possible with **local variables**. Use local variables to create or update variables in a given scope, regardless of whether they existed in this scope before. ### Define local variables To define a local variable in Modeler, add an input mapping on the activity, subprocess, or call activity where you want the variable to exist. For details on input mapping concepts (`source` and `target`) see [input/output variable mappings](#inputoutput-variable-mappings). The `target` of the input mapping becomes a local variable in that element's scope. For example, an input mapping with `source: =customer.name` and `target: reviewerName` creates the local variable `reviewerName` in that scope. ### Scope behavior in common modeling patterns The scope boundary depends on the BPMN element you use: | Pattern | Scope behavior | | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Embedded subprocess | Creates a local scope inside the same process instance. Local variables stay inside the subprocess unless you propagate them with output mappings. Root-scope process variables are still shared, so parallel or multi-instance embedded subprocess instances can overwrite the same process variable. | | Call activity | Starts a new process instance with its own variable scope. Configure the call activity's parent variable propagation settings and input mappings to control which variables the child receives. Use the call activity's child variable propagation settings and output mappings to control which variables are returned to the caller. | | Multi-instance activity | Each instance has its own local scope. Use input mappings to create per-instance local variables, especially in parallel multi-instance activities, to avoid race conditions when multiple instances update the same process variable. | If a form field or task variable should be different for each subprocess or each multi-instance instance, define it as a local variable with an input mapping instead of writing it directly to the root process scope. :::tip When to use local variables Use local variables to isolate data within a specific scope, especially for: - **Per-instance data in multi-instance activities**: Create per-instance copies of variables to avoid race conditions when parallel instances update the same root process variable. - **Subprocess-specific data**: Variables that should not affect sibling subprocess instances or the parent scope. - **Task-specific context**: Variables computed for a single task that shouldn't persist to the process level. Remember: Local variables are removed when a scope is exited unless you explicitly propagate them with output mappings. ::: ## Input/output variable mappings Input/output variable mappings can be used to create new variables or customize how variables are merged into the process instance. Variable mappings are defined in the process as extension elements under `ioMapping`. Every variable mapping has a `source` and a `target` expression. The `source` expression defines the **value** of the mapping. It usually [accesses a variable](/components/modeler/feel/language-guide/feel-variables.md#access-variable) of the process instance that holds the value. If the variable or nested property doesn't exist, the value resolves to `null`. The same applies if you do not provide a `source`. The `target` expression defines **where** the value of the `source` expression is stored. It can reference a variable by its name or a nested property of a variable. If the variable or the nested property doesn't exist, it's created. Variable mappings are evaluated in the defined order. Therefore, a `source` expression can access the target variable of a previous mapping. ![variable-mappings](assets/variable-mappings.png) **Input mappings** | Source | Target | | --------------- | ----------- | | `customer.name` | `sender` | | `customer.iban` | `iban` | | `totalPrice` | `price` | | `orderId` | `reference` | **Output mapping** | Source | Target | | -------- | --------------- | | `status` | `paymentStatus` | ### Input mappings Input mappings can be used to create new variables. They can be defined on [service tasks](/components/modeler/bpmn/service-tasks/service-tasks.md), [script tasks](/components/modeler/bpmn/script-tasks/script-tasks.md), [business rule tasks](/components/modeler/bpmn/business-rule-tasks/business-rule-tasks.md), [call activities](/components/modeler/bpmn/call-activities/call-activities.md), [user tasks](/components/modeler/bpmn/user-tasks/user-tasks.md), [send tasks](/components/modeler/bpmn/send-tasks/send-tasks.md), and [subprocesses](/components/modeler/bpmn/subprocesses.md). When an input mapping is applied, it creates a new [**local variable**](#local-variables) in the scope where the mapping is defined. In Modeler, define these mappings in the element properties. You can use [expressions](./expressions.md) or static values for input mappings. You can leave the `source` empty to map the `target` variable to `null`. For string literals containing escaped characters (e.g., a newline character `\n`), the string is returned in its original form as expected (no double escaping is applied). Examples: | Process variables | Input mappings | New variables | | -------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------- | | `orderId: "order-123"` | **source:** `=orderId` **target:** `reference` | `reference: "order-123"` | | `customer:{"name": "John"}` | **source:** `=customer.name`**target:** `sender` | `sender: "John"` | | `customer: "John"``iban: "DE456"` | **source:** `=customer` **target:** `sender.name`**source:** `=iban`**target:** `sender.iban` | `sender: {"name": "John", "iban": "DE456"}` | | - | **source:** `"Peter"`**target:** `sender` | `sender: "Peter"` | | `customer:{"name": "John"}` | **source:** (not provided)**target:** `customer` | `customer: null` | ### Output mappings Output mappings can be used for several purposes: - To customize how variables are merged into the process instance. - They can be defined on service tasks, receive tasks, message catch events, and subprocesses. - They can be used in script and user tasks. If **one or more** output mappings are defined, the results variables are set as **local variables** in the scope where the mapping is defined. Then, the output mappings are applied to the variables and create new variables in this scope. The new variables are merged into the parent scope. If there is no mapping for a job/message variable, the variable is not merged. :::note This can lead to a case where some variables with an output mapping are merged into the parent scope, and others without an output mapping are not merged. ::: If **no** output mappings are defined, all results variables are merged into the process instance. In the case of a subprocess, the behavior is different. There are no results variables to be merged. However, output mappings can be used to propagate **local variables** of the subprocess to higher scopes. By default, all **local variables** are removed when the scope is left. Examples: | Results variables | Output mappings | Process variables | | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------- | | `status: "Ok"` | **source:** `=status`**target:** `paymentStatus` | `paymentStatus: "OK"` | | `result: {"status": "Ok", "transactionId": "t-789"}` | **source:** `=result.status`**target:** `paymentStatus`**source:** `=result.transactionId`**target:** `transactionId` | `paymentStatus: "Ok"``transactionId: "t-789"` | :::note Avoid using output mappings or result variables that contain a period (for example, `customer.name`). Using a period is discouraged because it updates a property of an existing process variable within the task scope, which can lead to confusing behavior or unexpected results in the process flow. ::: ### Context variable A context variable is a reserved variable that describes the context of a task. It can group variables together to provide a detailed description of the task or offer more descriptive data about it. The reserved variable name for a context variable is `taskContextDisplayName`. This name is reserved exclusively for this purpose and should not be used for other variables. :::warning Context variables were a Tasklist V1 feature and are not supported in current Tasklist releases. See [Tasklist API changes](../tasklist/api-versions.md). ::: Example: | Input variable | Example | | ------------------------ | ------------------------------------ | | `taskContextDisplayName` | `This is a context variable example` | The data from the variable will be shown on the task tile, as shown in the example below: ![context-variables](assets/context-variables.png) ## Next steps - Understand how to [access variables](/components/modeler/feel/language-guide/feel-variables.md). - Explore how to centrally manage cluster configuration with [cluster variables](../../components/modeler/feel/cluster-variable/overview.md). --- ## Workflow patterns For true end-to-end process orchestration, you must be able to accurately express all the things happening in your business processes which will require simple and advanced workflow patterns. This page describes typical patterns and how you can implement them using Camunda and BPMN. ## The power of BPMN Let's discuss the ISO standard [Business Process Model and Notation (BPMN)](https://camunda.com/bpmn/) first, as this is really a great workflow language to express workflow patterns. BPMN was developed as a collaboration of different vendors rooted in real-life industry experience. It happened during a time when the scientific background of workflow patterns was already well researched, for example by the [Workflow Patterns Initiative](http://www.workflowpatterns.com/). In other words, scientists already wrote down all the patterns that are important to express any problem you might get in a workflow, and BPMN used this knowledge to design a language that implemented all the relevant patterns (refer to this [evaluation](http://www.workflowpatterns.com/evaluations/standard/bpmn.php), for example). Essentially, BPMN is feature complete and will always be able to express what you need to orchestrate your processes. Additionally, BPMN has expressed all real-life problems rather easily when reflecting on our more than 15 years of hands-on experience with the language. If you now try to rely on workflow languages that promise to be simpler than BPMN, what it really means is that they lack important workflow patterns. You might want to look in the blog post on [why process orchestration needs advanced workflow patterns](https://camunda.com/blog/2022/07/why-process-orchestration-needs-advanced-workflow-patterns/), showing exemplary workarounds that are necessary if the language cannot express certain patterns. Typically, this involves emulating advanced patterns with basic constructs plus programming code so that your development takes longer, your solution becomes more brittle, and the resulting process model can't serve as a communication vehicle for business and IT as the model will be contaminated with technical details. ## Routing The most basic workflow patterns are (excuse the play on words) around the basic flow of work. ### Sequence Refer to [Workflow Pattern 1: Sequence](http://www.workflowpatterns.com/patterns/control/basic/wcp1.php): "A task in a process is enabled after the completion of a preceding task in the same process." This is implemented by a [sequence flow](/components/modeler/bpmn/bpmn-primer.md#sequence-flow-controlling-the-flow-of-execution) connecting two activities: The first sequence flow (1) connects the start event with **Task A**. Then, **Task B** is connected using another sequence flow, meaning that it can only happen if **Task A** was completed. If **Task B** completes, the sequence flow routes to the end event so that the process instance can complete. You can read more about it in [our BPMN primer: sequence flows - controlling the flow of execution](/components/modeler/bpmn/bpmn-primer.md#sequence-flow-controlling-the-flow-of-execution). ### Conditions (if/then) Refer to [Workflow Pattern 4: Exclusive Choice](http://www.workflowpatterns.com/patterns/control/basic/wcp4.php): "The thread of control is immediately passed to precisely one of the outgoing branches." This is implemented by an [exclusive gateway (XOR)](/components/modeler/bpmn/exclusive-gateways/exclusive-gateways.md): All outgoing sequence flows of the XOR gateway (1) have a [condition](/components/modeler/feel/language-guide/feel-boolean-expressions.md) configured, which decides if the process continues in **Task B** (2 if `x>42`) or **Task C** (3if `not(x>42)`). You can read more about it in [our BPMN primer: gateways - steering flow](/components/modeler/bpmn/bpmn-primer.md#gateways-steering-flow). ### Invoke subworkflows You need to invoke another process as part of your process. This is implemented by a [call activity](/components/modeler/bpmn/call-activities/call-activities.md): 1 When the call activity is entered, a new process instance of the referenced process is created. Only when the created process instance is completed is the call activity left and the outgoing sequence flow taken. You can reference any other BPMN process, for example: ### Loop Refer to [Workflow Pattern 21: Structured Loop](http://www.workflowpatterns.com/patterns/control/basic/wcp21.php): "The ability to execute a task or subprocess repeatedly. The loop has either a pre-test or post-test condition associated with it." In BPMN, you can simply model a loop: 1 This exclusive gateway contains the expression to decide if to continue or exit the loop. The gateway can be before or after the loop. ### Static parallel branches Imagine you want some tasks known during design time to be carried out in parallel. Refer to [Workflow Pattern 2: Parallel Split](http://www.workflowpatterns.com/patterns/control/new/wcp2.php) and [Workflow Pattern 33: Generalized AND-Join](http://www.workflowpatterns.com/patterns/control/new/wcp33.php): "The divergence of a branch into two or more parallel branches each of which execute concurrently" plus "the convergence of two or more branches into a single subsequent branch." In BPMN, this is implemented using [parallel gateways (AND)](/components/modeler/bpmn/parallel-gateways/parallel-gateways.md): 1 This AND-gateway splits the flow into concurrent paths so that Task A, B, and C are executed in parallel. 2 This AND-gateway waits for Task A, B, and C to complete before the flow can move on. You can read more about it in [our BPMN primer: gateways - steering flow](/components/modeler/bpmn/bpmn-primer.md#gateways-steering-flow). ### Dynamic parallel branches You might want to execute some tasks for every element of a list, like the `for each` construct in programming languages. Refer to [Workflow Pattern 14: Multiple Instances with a priori Run-Time Knowledge](http://www.workflowpatterns.com/patterns/control/new/wcp14.php): "Multiple instances of a task can be created. The required number of instances may depend on a number of runtime factors, but is known before the task instances must be created. Once initiated, these instances are independent of each other and run concurrently. It is necessary to synchronize the instances at completion before any subsequent tasks can be triggered." In BPMN, this is implemented using [multiple instance activities](/components/modeler/bpmn/multi-instance/multi-instance.md): 1 The parallel multiple instance marker defines that this subprocess is executed multiple times - once for each element of a given collection (like a `for each` loop in a programming language). ### Wait A typical situation is that a process needs to wait for some event to happen, e.g. some time to pass or some external message to arrive. This is related to [Workflow Pattern 23: Transient Trigger](http://www.workflowpatterns.com/patterns/control/new/wcp23.php). In BPMN, this is implemented using [events](/components/modeler/bpmn/events.md) (or [receive tasks](/components/modeler/bpmn/receive-tasks/receive-tasks.md)): 1 The timer event causes the process to wait, in this case until a specific point in time is due or some duration has elapsed. Refer to [timer events](/components/modeler/bpmn/timer-events/timer-events.md) for more details. 2 The process will wait for a message to arrive. The message is an external trigger provided by API and can technically be anything, from a callback (e.g. via REST), over real messaging (like AMQP), or to notifications within your system. Refer to [message events](/components/modeler/bpmn/message-events/message-events.md) for more details. You can read more about events in [our BPMN primer: events - waiting for something to happen](/components/modeler/bpmn/bpmn-primer.md#events-waiting-for-something-to-happen). ## Reacting to events The waiting mentioned above is a special case where you react to events while not doing anything else. Oftentimes, you want to react to events even if the process is doing something else at the moment. This is described in this section. Typical examples are customer cancelation requests coming in for running order fulfillment processes, or timeouts if parts of the process take too long. ### Time based You want to react if a certain point in time is due or a specific time duration has passed. This is related to [Workflow Pattern 23: Transient Trigger](http://www.workflowpatterns.com/patterns/control/new/wcp23.php). In BPMN, you can leverage [boundary events](/components/modeler/bpmn/events.md#boundary-events) or [event subprocesses](/components/modeler/bpmn/event-subprocesses/event-subprocesses.md). Those events can be interrupting or non-interrupting, meaning you will either interrupt the current activity, or start something in parallel. 1 This timer is non-interrupting (dashed line), so the **Escalate request approval** task is started in parallel, additionally to the **Approve request** task. The idea is that the escalation task might make a manager double-checking the original task does not slip. Non-interrupting events can also be recurring, so you could also escalate "every two hours". 2 This timer is interrupting (solid line). Once it fires, the **Approve request** task is canceled and the process continues on the alternative path, in this case to automatically reject the request. Note that both timers so far can only happen if the task **Approve request** is active. 3 This is an event subprocess (dotted line). This can be activated from everywhere in the current scope. In this example, the scope is the whole process. 4 So if the process is not completed within the defined SLA, the timer fires and the event subprocess is started. As the timer is non-interrupting (dashed line again), it does not intervene with the typical flow of operations, but starts something additionally in parallel. :::note The above process is not necessarily modeled following all of our [modeling best practices](/components/best-practices/modeling/creating-readable-process-models.md), but intentionally shows different ways to use BPMN to implement certain workflow patterns. ::: ### External messages/events You might also want to react to certain incoming messages or events in an existing process. A good example is a customer canceling the current order fulfillment process. This might be possible only in a certain process phase and lead to different actions. This is related to [Workflow Pattern 23: Transient Trigger](http://www.workflowpatterns.com/patterns/control/new/wcp23.php) and [Workflow Pattern 24: Persistent Trigger](http://www.workflowpatterns.com/patterns/control/new/wcp24.php). As with timers, you can leverage [boundary events](/components/modeler/bpmn/events.md#boundary-events) or [event subprocesses](/components/modeler/bpmn/event-subprocesses/event-subprocesses.md). Assume that an order cancelation message comes in for the current process instance using [message correlation](/components/concepts/messages.md). 1 Subprocesses can be easily used to define phases of a process, as the cancelation is treated differently depending on the current process phase. 2 For example, a cancelation during the clearing phase has no consequences and can simply be executed. 3 But when the process is already in the preparation phase it might need to clean up certain things properly. 4 During delivery, it does not even allow cancelations anymore. This is also why this event is non-interrupting (dashed line), so we keep doing **Delivery**. ### Correlation mechanisms Mapping external messages to an existing process instance is called [message correlation](/components/concepts/messages.md). This is a crucial functionality to ensure you can communicate with process instances from the outside. There are two main problems to solve: 1. How to find the right process instance? In Camunda, this is solved by a `message name` and a `correlation key` (e.g. `orderCanceled` and `order-42`). 2. How to persist messages if a process instance is not yet ready to receive that message yet? In Camunda, this is solved by having an internal message store and a `time to live` attached to messages. This is related to [Workflow Pattern 24: Persistent Trigger](http://www.workflowpatterns.com/patterns/control/new/wcp24.php) You can find more information in [our documentation about messages](/components/concepts/messages.md). ### Events from subprocesses Sometimes, a subprocess needs to communicate with its parent process without ending the subprocess yet. BPMN allows this by an [escalation event](/components/modeler/bpmn/bpmn-coverage.md). 1 An escalation event can be thrown from any of the called subprocesses and is picked up by its parent to start something in parallel, as this is a non-interrupting event (dashed line). The subprocess can raise the escalation any time: ### Broadcasts and engine-wide events While messages are always targeted at one specific process instance, you might also want to inform many processes about an event at once. For example, you might regularly adjust certain customer scoring rules that always should be taken into account immediately. This can be implemented using the [signal event](/components/modeler/bpmn/bpmn-coverage.md). 1 The signal event is caught and in this case interrupts the onboarding to go back to score the customer again. ## Handling errors Handling exceptions well is one of the most important capabilities of a workflow engine, and it needs built-in support from the modeling language. You might also want to look into our [best practice: modeling beyond the happy path](/components/best-practices/modeling/modeling-beyond-the-happy-path.md) to understand possibilities. ### Error scopes The reaction to errors might need to be different depending on the current state of the process. This can be achieved by using [subprocesses](/components/modeler/bpmn/embedded-subprocesses/embedded-subprocesses.md) in combination with either [boundary events](/components/modeler/bpmn/events.md#boundary-events) or [event subprocesses](/components/modeler/bpmn/event-subprocesses/event-subprocesses.md). 1 This boundary error event is attached to the subprocess "clearing" and only catches errors within that subprocess. The idea here would be that in case of any clearing service not being available, the order is assumed cleared. Note that this example is mainly built for illustration, and does not necessarily mean this is the best way to solve this business requirement. 2 Alternatively, this error event subprocess is triggered whenever there is a fraud detected, independent of whether the error occurs in any of the subprocesses or the main process. ### Catch errors per type You might need to react to different event types differently, which is possible by using the [error type](/components/modeler/bpmn/error-events/error-events.md#defining-the-error) known to BPMN: Now there is a different reaction if fraud was detected (1) or the address was found to contain an error (2). ## Business transactions Modern systems are highly distributed across the network. In such systems, you cannot rely on technical ACID transactions for consistency, but need to elevate decisions around consistency or regaining consistency to the business level. Refer to [Achieving consistency without transaction managers](https://blog.bernd-ruecker.com/achieving-consistency-without-transaction-managers-7cb480bd08c) for additional background on this. ### Compensation An important problem to solve is how to roll back a business transaction in case of problems. In other words, how to restore business consistency. One strategy is to leverage compensating activities to undo the original actions whenever the problem occurs. This is also known as the [Saga Pattern](https://blog.bernd-ruecker.com/saga-how-to-implement-complex-business-transactions-without-two-phase-commit-e00aa41a1b1b). In BPMN, you can use [compensation events](/components/modeler/bpmn/bpmn-coverage.md) to easily implement compensations in your processes. 1 For every task in a process model, you can define a compensation task. This can be any valid BPMN task, like a service task, a human task (2), or a subprocess, for example. 3 This compensation task is connected to the original task by a dedicated compensation event. 4 Within your process model, you can define when it is time to compensate. Whenever you trigger the compensation event, all tasks of the current scope that were executed are automatically compensated. This means that their configured compensation task is executed. The big advantage is that you don't have to remodel the routing logic to compensate correctly, like checking again if the customer balance was used. The workflow engine will take care automatically, also in more complicated situations like multiple instance activities. --- ## Inbound connector deduplication Inbound In the simplest case, each inbound connector element in a BPMN diagram corresponds to a unique endpoint, event consumer, or a polling task. However, the connector runtime can combine multiple compatible inbound connector elements. The runtime can do this automatically, or you can request it. This page explains the concept of deduplication in the connector runtime. ## Purpose of deduplication Consider the following BPMN diagram: ![Connector deduplication use-case example](../img/deduplication-example.png) In this diagram, two connector events are listening to the same message queue that can contain messages of two types: `PAYMENT_COMPLETED` and `PAYMENT_CANCELLED`. When the process execution arrives at the event gateway, the type of the message determines which path the process will take. If each connector event listened to the message queue using a separate subscription, this might lead to race conditions if the message is received by a different consumer (for example, the `PAYMENT_COMPLETED` event being consumed by the consumer that expects `PAYMENT_CANCELLED`). Eventually, this might lead to message loss or delayed processing, while also increasing the load on the message broker as the message is returned to the queue. To avoid this, both events can be assigned to the same subscription by assigning the same deduplication ID to both events. Then, all messages will be consumed by the same subscription, and the connector runtime will evaluate the activation condition of each event to determine which one should be triggered. :::note When using this pattern, ensure each event’s activation condition is mutually exclusive, so only one event is triggered per message. Attempting to trigger multiple events for the same message will result in an error. ::: ## Automatic deduplication By default, the connector runtime assigns the same deduplication ID to connector events with matching properties, and different IDs to events with different properties. In this context, equal properties means the properties that define the business logic of the connector are exactly the same, including whitespace characters. The automatic deduplication only takes into account the properties that are related to the business logic of the connector itself (for example, **Server URL** or **Authentication properties**). It does not take into account the properties that define output mapping (**Result variable**, **Result expression**, **Response expression**), correlation (**Correlation key (process)**, **Correlation key (payload)**, **Activation condition**), or other properties that are handled by the connector runtime and not by the connector itself. This way, two connectors of the same type that are identical in terms of business logic and are defined in the same process definition will be deduplicated automatically. ### Cross-version deduplication In Camunda 8.9+, deduplication is performed across all versions of the same process definition. This means that if you deploy version 1 and version 2 of a process with the same compatible inbound connector, there will only be one executable connector instance serving both versions. This behavior is a natural extension of support for [inbound connectors across multiple process versions](./inbound-lifecycle.md). When connector properties are identical across versions, the connector runtime automatically consolidates them into a single subscription or endpoint. To learn more about how inbound connectors behave across process versions, see [Inbound connector lifecycle](./inbound-lifecycle.md). :::warning Webhook connectors and multiple versions When using webhook connectors, you cannot reuse the same webhook endpoint for multiple process versions if you want to keep them both active simultaneously. The default behavior is to keep the old version running when you deploy a new version that uses the same endpoint. The new version's connector will not become active until no process instances are running on the older version. To keep both versions active: - Complete all process instances on the older version before you deploy the new version. - Or, use a different webhook endpoint path in the new version. For more details, see [Inbound connector lifecycle](./inbound-lifecycle.md). ::: ## Manual deduplication You can manually assign a deduplication ID to each connector event. This allows you to group connectors in a more flexible way based on your requirements. If needed, you can have multiple connectors with the same properties that have different deduplication IDs. This way, you can still have multiple instances of the same connector listening to the same event source, but each instance will have its own deduplication ID and will be treated as a separate entity by the connector runtime. To assign a deduplication ID, take the following steps: 1. Enable the **Manual mode** checkbox in the **Deduplication** section of the connector properties. 2. In **Deduplication ID**, enter the deduplication ID. 3. Repeat the process for all connectors that should share the same deduplication ID. 4. Deploy the BPMN diagram for the changes to take effect. ![Deduplication input example](../img/deduplication-input-example.png) :::note When manual deduplication is used, connectors that have the same deduplication ID must also have the same properties. Attempting to assign the same deduplication ID to connectors with different properties will result in a runtime error. ::: ## Should I use automatic or manual deduplication? Use automatic deduplication if: - You don't need to group connectors in a specific way and don't have any special requirements for deduplication. - Deduplication configuration is not important for your use case (you don't use multiple connectors in the same process that listen to the same event source). Use manual deduplication if: - You need to group connectors in a specific way that is not supported by automatic deduplication. - You are unsure which properties of the connector are used for automatic deduplication. - You want to have more control over the deduplication process. ## How to choose a deduplication ID A deduplication ID can contain alphanumeric characters, dashes, and underscores. We recommend using a descriptive ID that reflects the deduplication group’s purpose, for example, `payment-outcome-event-consumer`. ## Limitations of deduplication While deduplication is a powerful tool that can optimize the execution of your BPMN process, it has some limitations. It is important to understand them to avoid unexpected behavior. 1. **Deduplication ID scope** – The deduplication ID is scoped to a single process definition across all its versions. You can’t deduplicate connectors across different process definitions. 2. **Connector type** – Connectors of different types can’t share the same deduplication ID (for example, a Webhook connector and a Message Queue connector). 3. **Connector properties** – Connectors that share the same deduplication ID must use the same business-logic properties. This means they must have the same **Webhook ID**, **Server URL**, **Authentication properties**, and so on, depending on the connector type. 4. **Activation condition** – Connectors with the same deduplication ID must have mutually exclusive activation conditions. If multiple connectors can evaluate to true for the same message, the connector runtime can’t determine which connector to trigger and returns an error. --- ## Inbound connector lifecycle Inbound Inbound connectors in Camunda have a lifecycle that depends on process definition deployments. It is important to understand this lifecycle to work with inbound connectors effectively. This page explains when inbound connectors are activated and deactivated, what can affect their execution, and how to monitor their status. ## Inbound connector executables An executable is an instance of an inbound connector that is managed by the connector runtime. An executable is mapped to one or more process definitions deployed to the engine. In the simplest case, deploying a new process definition will create a new executable in the connector runtime. However, in general, one executable can be reused for multiple inbound connector elements in the diagram. To learn how the runtime reuses executables across connectors, see [Inbound connector deduplication](./deduplication.md). ## Reacting to process definition deployments ![Inbound connector data sources](../img/inbound-data-source.png) The connector runtime maintains an internal state that tracks: - Latest versions of process definitions. - Previous process-definition versions with active message subscriptions. The following endpoints of the Orchestration Cluster API are used as data sources: - [Search process definitions](/apis-tools/orchestration-cluster-api-rest/specifications/search-process-definitions.api.mdx) - [Search message subscriptions](/apis-tools/orchestration-cluster-api-rest/specifications/search-message-subscriptions.api.mdx) If a process definition matches at least one of these two criteria (i.e. it is either the latest version of that process definition, or it has active instances waiting on message subscriptions), the connector runtime will create an executable for every inbound connector element in that process and keep it active as long as these criteria are met. If a process definition is deleted from the engine; or it is replaced with a newer version of the same process, and has no active message subscriptions, the connector runtime will deactivate the related executables. ## Lifecycle overview ![Inbound connector lifecycle](../img/inbound-lifecycle.png) The lifecycle of an executable starts when the connector runtime detects a change in the engine state and determines that it needs a new executable to reflect that change. - `INVALID_DEFINITION` – The runtime detects an invalid deduplication configuration and stores the executable in this state. For details about supported configurations and common causes, see [Inbound connector deduplication](./deduplication.md). - `FAILED_TO_ACTIVATE` – The runtime attempts to activate the executable, but the activation fails. The executable remains inactive in this state. - `ACTIVE` – The runtime successfully activates the executable. In this state, the executable is ready to receive and process external events from the third-party system. - `CANCELED` – An active executable encounters an unrecoverable error and transitions to this state. This is rare, as most connectors include retry logic to handle transient failures such as temporary network issues. When you deploy a new process version, the connector runtime checks whether it can deduplicate inbound connectors in the new version with any currently active executables. If deduplication is possible, the runtime updates the existing executable. If the executable is inactive (for example, in the `CANCELED` or `FAILED_TO_ACTIVATE` state), the runtime also attempts to restart it. When the executable is no longer needed—for example, when its originating process version is no longer the latest and no active instances are waiting on message subscriptions—the runtime deactivates it. --- ## Intrinsic functions Outbound Intrinsic functions are transformations you can use to preprocess connector input data before invoking a connector. - Intrinsic functions are JSON structures you can define in element template input fields. - Intrinsic functions are executed in the connector runtime. :::note You can only use intrinsic functions with [outbound connectors](/components/connectors/connector-types.md#outbound-connectors) as they transform data obtained from process variables. ::: ## Use cases A common use case is to transform a [Camunda document](/components/document-handling/getting-started.md) to a specific format when using a REST connector. For example: - You have a Camunda document containing some data in JSON format. - You want to send this data in the HTTP request body to a REST API using a connector. The [REST connector](/components/connectors/protocol/rest.md) is capable of handling documents, but the data format in which the document is sent by default may not match the format expected by the REST API. In this example, the document in the Camunda document store is structured as follows, with the document reference stored in the `document` process variable: ```json { "name": "John Doe", "age": 30 } ``` The REST API expects the data in the following format: ```json { "user": { "name": "John Doe", "age": 30 } } ``` In this example, you can use the `getText` intrinsic function to extract the text content from the document and insert it into the request body. The resulting JSON structure of your connector's input would be as follows: ```json { "user": { "camunda.function.type": "getText", "params": [ document ] } } ``` - `document` is the process variable that contains the document reference. - The [`getText`](#gettext) function extracts the text content from the document and inserts it into the request body as a string. :::note - You can use intrinsic functions with any outbound connector to execute an operation with a connector input. - Intrinsic functions are executed in the connector runtime. ::: ## Available functions The following prebuilt functions are available: ### `base64` The `base64` function accepts a document or a string. It encodes the document content or string to Base64 format. ```json { "camunda.function.type": "base64", "params": [ myDocument ] } ``` ### `createLink` The `createLink` function accepts a document and an optional TTL (time-to-live) value. It creates a temporary pre-signed link to the document in the Camunda document storage. The link is returned as a string. - Pre-signed links can only be created for documents that are stored in cloud storage (for example, AWS S3 or Google Cloud Storage), but not local or in-memory storage. - The optional TTL parameter must be a valid [ISO 8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations) string. - If not provided, the default TTL is 1 hour. ```json { "camunda.function.type": "createLink", "params": [ myDocument, "PT1H" ] } ``` ### `getText` The `getText` function accepts a document and an optional encoding parameter. It extracts the text content from the document and returns it as a string. - The optional encoding parameter specifies the character encoding to be used when extracting the text. - If not provided, the default encoding is UTF-8. ```json { "camunda.function.type": "getText", "params": [ myDocument, "UTF-8" ] } ``` ### `getJson` The `getJson` function accepts a document and an optional FEEL expression parameter. It extracts the text content from the JSON document and returns it as an object. - The optional FEEL expression parameter specifies the part that will be extracted from the JSON document content. - If not provided, the whole document is returned as a JSON object. ```json { "camunda.function.type": "getJson", "params": [ myDocument, "field1.field2" ] } ``` ### `createGithubAppInstallationToken` The `createGithubAppInstallationToken` function generates a GitHub App installation access token. This is useful when you need to authenticate as a GitHub App installation to access GitHub APIs. The function accepts three required parameters: - `privateKey`: The RSA private key of your GitHub App (PEM format). Store this value as a secret. - `appId`: The app ID of your GitHub App. - `installationId`: The installation ID of your GitHub App. See [GitHub's documentation](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app-installation#generating-an-installation-access-token) for instructions on how to find your installation ID. ```json { "camunda.function.type": "createGithubAppInstallationToken", "params": ["{{secrets.GITHUB_APP_PRIVATE_KEY}}", "12345", "67890"] } ``` The function returns the installation access token as a string. You can use this token to authenticate GitHub API requests. ## Create a custom function In **Self-Managed** deployments, you can create custom intrinsic functions by implementing the `IntrinsicFunctionProvider` interface included with the [Connector SDK](/components/connectors/custom-built-connectors/connector-sdk.md), and registering it in the connector runtime. - The custom function is implemented as a Java method inside the `IntrinsicFunctionProvider` implementation class. - The method must be annotated with the `@IntrinsicFunction` annotation. The method arguments are transformed into a list of intrinsic function parameters. ```java public class MyFunctionProvider implements IntrinsicFunctionProvider { @IntrinsicFunction(name = "concat") public String execute(String s1, String s2) { return s1 + s2; } } ``` --- ## Connector types Connectors are categorized by the direction data flows into or out of Camunda 8. ## Outbound connectors Outbound connectors allow workflows to trigger external systems or services, making it possible to integrate workflows with other parts of a business process or system architecture. The Java code to connect to the external system is executed when the workflow reaches the service task. ![Outbound connectors](img/outbound-connectors.png) Use outbound connectors if something needs to happen in the third-party system if a process reaches a service task. For example, calling a REST endpoint or publishing a message to Slack. ## Inbound connectors Inbound connectors enable workflows to receive data or messages from external systems or services, making it possible to integrate workflows into a wider business process or system architecture. Inbound connectors can be used to create a new process instance, or to send a message to a running process instance. The Java code of the inbound connector has a lifecycle suitable for long-running operations, such as listening for messages on a queue or waiting for a webhook to be called. The connector code is **activated** as soon as the connector Runtime detects an element in a process definition that references an inbound connector. It gets `deactivated` in case of an updated or deleted process definition. Inbound connector instances are linked to process definitions and not to specific process instances. If a process definition contains an element referencing an inbound connector, the connector code will be first executed when the process definition is deployed and the deployment has been detected by the connector Runtime. The connector object created during deployment will be kept active as long as the process is deployed, and it is reused to serve all instances of the process. When the process definition is deleted or replaced with a newer version, the connector object will be removed or updated as well. :::note The connector Runtime currently fetches only the **latest version** of each process definition. Deploying a new process definition version deactivates inbound connectors from older versions. [Migrate older versions](../../concepts/process-instance-migration) if the new inbound connector configuration won't correlate messages for existing instances. ::: ![Inbound connectors](img/inbound-connectors.png) Use inbound connectors if something needs to happen within the workflow engine because of an external event in the third-party system. For example, because a Slack message was published, or a REST endpoint is called. There are three types of inbound connectors: 1. **Webhook connector**: An inbound connector which creates a webhook for a Camunda workflow. 2. **Subscription connector**: An inbound connector that subscribes to a message queue. 3. **Polling connector**: An inbound connector that periodically polls an external system or service for new data using HTTP polling. ## Protocol connectors Protocol connectors can serve as either inbound or outbound connectors, supporting a variety of technical protocols. These connectors are highly generic, designed to provide a flexible and customizable means of integrating with external systems and services. Protocol connectors can be customized to meet the needs of specific use cases using configurable [Connector Templates](manage-connector-templates.md), with no additional coding or deployment required. Examples of protocol connectors include HTTP REST, GraphQL, as well as message queue connectors. ## Next steps Review the current list of [available connectors](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md). --- ## Build a custom connector Can't find the connector you need? Build your own custom connector using connector templates or the SDK. Camunda supports two main ways to build custom connectors: - [Connector templates](/components/connectors/custom-built-connectors/connector-templates.md) for creating custom connectors based on existing built-in connectors, such as REST or gRPC. - [Connector SDK](/components/connectors/custom-built-connectors/connector-sdk.md) for building your own Java-based connectors from scratch. Choose the approach that best fits your requirements and technical expertise. This guide provides an overview of both methods to help you get started. ## Understand the connector architecture A connector consists of the Java backend and an element template that defines the Modeler user interface. - The Java code defines the connector’s functionality and how it interacts with an external system. For example, the [Connector function](/components/connectors/custom-built-connectors/connector-sdk.md#outbound-connector-runtime-logic) used for outbound connectors. - The user interface allows you to configure and use the connector in Modeler. This is defined in a [Connector template](../manage-connector-templates.md) that controls how the BPMN element is shown in Modeler and which configuration options are available for the connector. This separation enables a layered approach to building connectors. You can customize the user interface and configuration options in Modeler using connector templates, without modifying the underlying Java code. ## Choose your approach Decide which of the following scenarios best describes your use case. ### Protocol-based API call You need to call an API that uses a common protocol such as REST, SOAP, or GraphQL. There is no existing connector on Camunda marketplace that meets your needs. Camunda recommends using a custom connector template based on the [REST connector](/components/connectors/protocol/rest.md), [SOAP connector](/components/connectors/protocol/soap.md), or [GraphQL connector](/components/connectors/protocol/graphql.md). This approach allows you to leverage the existing functionality while customizing it to fit your API requirements. See [Create a custom REST connector](create-connector-from-rest.md) for more information. ### Complex integration logic You need to implement integration logic that goes beyond issuing an API call. Build a custom connector using the [Connector SDK](connector-sdk.md). This approach gives you full control over the connector's behavior. ## Connector templates Connectors use [connector templates](/components/connectors/custom-built-connectors/connector-templates.md) to customize how a BPMN element is shown, and how it can be configured by process developers. Connector templates are a specific type of [element template](/components/modeler/element-templates/about-templates.md). Connector templates Create a connector task and start using connector secrets. Generate a connector template Automatically generate a custom connector template in Web Modeler. Manage connector templates Create and manage connector templates in a workspace. --- ## Connector SDK The **Connector SDK** allows you to [develop custom connectors](#creating-a-custom-connector) using Java code. You can focus on the logic of the connector, test it locally, and reuse its runtime logic in multiple [runtime environments](#runtime-environments). The SDK achieves this by abstracting from Camunda 8 internals that usually come with [job workers](/components/concepts/job-workers.md). You can find the latest **Connector SDK** version source code [here](https://github.com/camunda/connectors). The SDK provides APIs for common connector operations, such as: - Fetching and deserializing input data - Validating input data - Replacing secrets in input data Additionally, the SDK allows for convenient [testing](#testing) of your connector behavior and [executing it in the environments](#runtime-environments) that suit your use cases best. ## Creating a custom connector Using the Connector SDK, you can create environment-agnostic and reusable connector runtime behavior. This section outlines how to set up a connector project, test it, and run it locally. ### Setup When developing a new **Connector**, we recommend using one of our custom connector templates for custom [outbound](https://github.com/camunda/connector-template-outbound) and [inbound](https://github.com/camunda/connector-template-inbound) connectors. These templates are [Maven](https://maven.apache.org/)-based Java projects, and can be used in various ways such as: - **Create your own GitHub repository**: Click **Use this template** and follow the prompted steps. You can manage code changes in your new repository afterward. - **Experiment locally**: Check out the source code to your local machine using [Git](https://git-scm.com/). You won't be able to check in code changes to the repository due to restricted write access. - **Fetch the source**: Download the source code as a ZIP archive using **Code > Download ZIP**. You can adjust and manage the code the way you like afterward using your chosen source code management tools. To manually set up your connector project, include the following dependency to use the SDK. Ensure you adhere to the project outline detailed in the next section. ```xml io.camunda.connector connector-core ${version.connectors} ``` ```yml implementation "io.camunda.connector:connector-core:${version.connectors}" ``` ### Outbound connector project outline There are multiple parts of a connector that enable it for reuse, modeling, and the runtime behavior. For example, the following parts make up an outbound connector: ``` my-connector ├── element-templates/ │ └── connector.json (1) ├── src/main │ ├── java/io/camunda/example (2) │ │ ├── MyConnector.java (3) │ └── resources/META-INF/services │ └── io.camunda.connector.api.outbound.OutboundConnectorProvider (4) └── pom.xml (5) ``` For the modeling building blocks, the connector provides [Connector templates](/components/connectors/custom-built-connectors/connector-templates.md) with **(1)**. You provide the runtime logic as Java source code under a package like **(2)** including an implementation of your connector, in this case `MyConnector` with **(3)**. For a detectable connector, you are required to expose your function class name in the [`OutboundConnectorProvider` SPI implementation](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/ServiceLoader.html) with **(4)**. A configuration file like **(5)** manages the project setup, including dependencies. In this example, we include a Maven project's `POM` file. Other build tools like [Gradle](https://gradle.org/) can also be used. ### Outbound connector element template [Connector templates](/components/connectors/custom-built-connectors/connector-templates.md) act as the modeling interface for the users of your connector. The template can be written manually but we recommend using the [`element-template-generator`](https://github.com/camunda/connectors/tree/main/element-template-generator) which generates the element template for your connector as part of your build process. Element templates define the data and configuration bindings to your connector on the BPMN element via properties. Properties have different types that define their visual representation. They can also be hidden in the modeling UI but still applied to the BPMN: ```json { ... "properties" : [ { "type": "Hidden", "value": "io.camunda:template:1", "binding": { "type": "zeebe:taskDefinition", "property": "type" } } } ``` This type definition `io.camunda:template:1` is the connection configuring which version of your connector runtime behavior to use. In technical terms, this defines the **Type** of jobs created for tasks in your process model that use this template. Consult the [job worker](/components/concepts/job-workers.md) guide to learn more. Besides the type binding, connector templates also define the input variables of your connector as `zeebe:input` objects. For example, you can create the input variable `message` of your connector in the element template as follows: ```json { "label": "Message", "type": "Text", "feel": "optional", "binding": { "type": "zeebe:input", "name": "message" } } ``` You can also define nested data structures to reflect domain objects that group attributes. For example, you can create the domain object `authentication` that contains the properties `user` and `token` as follows: ```json { "label": "Username", "description": "The username for authentication.", "type": "String", "binding": { "type": "zeebe:input", "name": "authentication.user" } }, { "label": "Token", "description": "The token for authentication.", "type": "String", "binding": { "type": "zeebe:input", "name": "authentication.token" } } ``` You can deserialize these authentication properties into a domain object using the SDK. Visit the [input data](#outbound-connector-input-data) section for further details. Connectors that offer any kind of result from their invocation should allow users to configure how to map the result into their processes. Therefore, connector templates can reuse the two recommended objects, **Result Variable** and **Result Expression**: ```json { "label": "Result Variable", "description": "Name of variable to store the response in", "type": "String", "binding": { "type": "zeebe:taskHeader", "key": "resultVariable" } }, { "label": "Result Expression", "description": "Expression to map the response into process variables", "type": "Text", "feel": "required", "binding": { "type": "zeebe:taskHeader", "key": "resultExpression" } } ``` These objects create custom headers for the jobs created for the tasks that use this template. The connector runtime environments pick up those two custom headers and translate them into process variables accordingly. You can find an example of how to use this in the [out-of-the-box REST connector](/components/connectors/protocol/rest.md#response). All connectors are recommended to offer exception handling to allow users to configure how to map results and technical errors into BPMN errors. To provide this, connector templates can provide an **Error Expression**: ```json { "label": "Error Expression", "description": "Expression to define BPMN Errors to throw", "group": "errors", "type": "Text", "feel": "required", "binding": { "type": "zeebe:taskHeader", "key": "errorExpression" } } ``` This object creates a custom header for the jobs created for the tasks that use this template. The connector runtime environments pick up this custom header and translate it into BPMN errors accordingly. You can observe an example of how to use this in the [BPMN errors in connectors guide](/components/connectors/use-connectors/index.md#bpmn-errors-and-failing-jobs). ### Outbound connector runtime logic The connector implements the `OutboundConnectorProvider` interface of the SDK. This allows the Connector runtime to discover and invoke your Connector. It introspects the `@OutboundConnector` annotation and uses the `type` to register the Connector as a job worker to fetch jobs. A Connector implementation can now declare ```java package io.camunda.example; @OutboundConnector( // (1) name = "MyConnector", type = "io.camunda:my-connector:1" ) @ElementTemplate( // (2) id = "my-connector-template:1", name = "My Connector Template" ) public class MyConnector implements OutboundConnectorProvider { @Operation(id = "operation1") // (3) public String operation1(@Variable(name = "input") String input) { // (4) System.out.println("Received input: " + input); return "Test operation executed successfully!"; } public record MyInput(@NotNull Integer a, @NotNull int b) {} public record MyOutput(int result) {} @Operation(id = "operation2") public MyOutput operation2(@Variable MyInput input) { // (5) return new MyOutput(input.a() + input.b()); } @Operation(id = "operation3") public String operation3(@Header(name = "name") String name) { // (6) System.out.println("Received name: " + name); return name; } @Operation(id = "operation4") public String operation4() { throw new ConnectorException(("MY_ERROR"), "This is a test exception"); // (7) } } ``` A single `@OutboundConnector` annotated connector (**(1)**) can declare one or multiple operations. The element template generation can be configured using the `@ElementTemplate` annotation (**(2)**) Every declared operation (**(3)**) can accept one or multiple inputs as parameters. Using the `@Variable` annotation, a primitive type has to specify the variable name for example `input` as shown in (**(4)**). Binding to a complex type will use the property names (`a`, `b`) of the type for variable mapping (**(5)**). Types can use [Jakarta Validation](https://beanvalidation.org/) annotations. Validation will be applied during binding. It's also possible to bind job headers using the `@Header` annotation (**(6)**) but this is only recommended for static config data defined at modeling time. If the connector handles exceptional cases, it can use any exception to express technical errors. If a technical error should be associated with a specific error code, the connector can throw a `ConnectorException` and define a `code` as shown in **(7)**. We recommend documenting the list of error codes as part of the connector's API. Users can build on those codes by creating [BPMN errors](/components/connectors/use-connectors/index.md#bpmn-errors-and-failing-jobs) in their connector configurations. ### Inbound connector project outline There are multiple parts of a connector that enable it for reuse, as a reusable building block, for modeling, and for the runtime behavior. For example, the following parts make up an inbound connector: ``` my-connector ├── element-templates │ └── inbound-template-connector.json (1) ├── pom.xml (6) ├── src │ ├── main │ │ ├── java/io/camunda/connector │ │ │ └── inbound │ │ │ ├── MyConnectorExecutable.java (2) │ │ │ ├── MyConnectorEvent.java (3) │ │ │ ├── MyConnectorProperties.java (4) │ │ │ └── subscription │ │ │ ├── MockSubscription.java │ │ │ └── MockSubscriptionEvent.java │ │ └── resources/META-INF/services │ │ └── io.camunda.connector.api.inbound.InboundConnectorExecutable (5) ``` For the modeling building blocks, the connector provides [Connector element templates](./connector-templates.md) with **(1)**. You provide the runtime logic as Java source code. Typically, a connector runtime logic consists of exactly one implementation of a `InboundConnectorExecutable` with **(2)** and at least one input object like **(3)**, and connector's properties like **(4)**. For a detectable connector function, you are required to expose your function class name in the [`InboundConnectorExecutable` SPI implementation](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/ServiceLoader.html) with **(5)**. A configuration file like **(6)** manages the project setup, including dependencies. In this example, we include a Maven project's `POM` file. Other build tools like [Gradle](https://gradle.org/) can also be used. ### Inbound connector element template To create reusable building blocks for modeling, you are required to provide a domain-specific [Connector element template](./connector-templates.md). A connector template defines the binding to your connector runtime behavior via the following object: ```json { "type": "Hidden", "value": "io.camunda:mytestinbound:1", "binding": { "type": "zeebe:property", "name": "inbound.type" } } ``` This type definition `io.camunda:mytestinbound:1` is the connection configuring which version of your connector runtime behavior to use. In technical terms, this defines the **Type** of jobs created for tasks in your process model that use this template. Consult the [job worker](../../concepts/job-workers.md) guide to learn more. Besides the type binding, connector templates also define the properties of your connector as `zeebe:property` objects. For example, you can create the input variable `sender` of your connector in the element template as follows: ```json { "type": "String", "label": "Sender", "description": "Message sender name", "value": "Alice", "binding": { "type": "zeebe:property", "name": "sender" } } ``` ### Inbound connector runtime logic To create a reusable runtime behavior for your connector, you are required to implement and expose an implementation of the `InboundConnectorExecutable` interface of the SDK. The connector runtime environments will call this function; it handles input data, executes the connector's business logic. Exception handling is optional since the connector runtime environments handle this as a fallback. The `InboundConnectorExecutable` interface consists of two methods: `activate` and `deactivate`. A minimal recommended outline of a connector function implementation looks as follows: ```java package io.camunda.connector.inbound; @InboundConnector(name = "MYINBOUNDCONNECTOR", type = "io.camunda:mytestinbound:1") public class MyConnectorExecutable implements InboundConnectorExecutable { private MockSubscription subscription; private InboundConnectorContext connectorContext; @Override public void activate(InboundConnectorContext connectorContext) { MyConnectorProperties props = connectorContext.bindProperties(MyConnectorProperties.class); this.connectorContext = connectorContext; subscription = new MockSubscription( props.getSender(), props.getMessagesPerMinute(), this::onEvent); } @Override public void deactivate() { subscription.stop(); } private void onEvent(MockSubscriptionEvent rawEvent) { MyConnectorEvent connectorEvent = new MyConnectorEvent(rawEvent); var result = connectorContext.correlateWithResult(connectorEvent); handleResult(result); } private void handleResult(CorrelationResult result) { switch (result) { case Success ignored -> LOG.debug("Message correlated successfully"); case Failure failure -> { switch (failure.handlingStrategy()) { case ForwardErrorToUpstream ignored -> { LOG.error("Correlation failed, reason: {}", failure.message()); // forward error to upstream } case Ignore ignored -> { LOG.debug("Correlation failed but no action required, reason: {}", failure.message()); // ignore } } } } } } ``` The `activate` method is a trigger function to start listening to inbound events. The implementation of this method has to be asynchronous. Once activated, the inbound connector execution is considered active and running. From this point, it should use the respective methods of `InboundConnectorContext` to communicate with the connector runtime (e.g. to correlate the inbound event or signal the interrupt). The `deactivate` method is just a graceful shutdown hook for inbound connectors. The implementation must release all resources used by the subscription. The `onEvent` method is a callback function that is triggered by the subscription whenever a new event is received. This method is responsible for passing the event to the connector runtime environment for correlation. The `handleResult` method is a helper method to handle the result of the correlation. The `CorrelationResult` object contains the result of the correlation and the handling strategy. The handling strategy defines how the connector implementation should handle the result. Depending on the strategy, the connector implementation should either forward the error to the upstream system or ignore it. The handling strategy is derived by the connector runtime based on user configuration. #### Validation Validating input data is a common task in connectors. The SDK provides an out-of-the-box solution for input validation. A default implementation of the SDK's core validation API is provided in a separate, optional artifact `connector-validation`. If you want to use validation in your Connector, add the following dependency to your project: ```xml io.camunda.connector connector-validation ${version.connectors} ``` ```yml implementation "io.camunda.connector:connector-validation:${version.connectors}" ``` Validation is performed automatically when binding variables to parameters. This instructs the context to prepare a validator that is provided by an implementation of the `ValidationProvider` interface. The `connector-validation` artifact brings along such an implementation. It uses the [Jakarta Bean Validation API](https://beanvalidation.org/) together with [Hibernate Validator](https://hibernate.org/validator/). For your input object to be validated, you need to annotate the input's attributes to define your requirements: ```java package io.camunda.connector; public class MyConnectorRequest { @NotEmpty private String message; @NotNull @Valid private Authentication authentication; } ``` The Jakarta Bean Validation API comes with a long list of [supported constraints](https://jakarta.ee/specifications/bean-validation/2.0/bean-validation_2.0.html#builtinconstraints). It also allows to [validate entire object graphs](https://jakarta.ee/specifications/bean-validation/2.0/bean-validation_2.0.html#constraintdeclarationvalidationprocess-validationroutine-graphvalidation) using the `@Valid` annotation. Thus, the `authentication` object will also be validated. ```java package io.camunda.connector; public class Authentication { @NotEmpty private String user; @NotEmpty @Pattern(regexp = "^xobx") private String token; } ``` Using this approach, you can validate your whole input data structure with one initial call from the central connector function. #### Secrets Connectors that require confidential information to connect to external systems need to be able to manage those securely. As described in the [guide for creating secrets](/components/hub/organization/manage-clusters/manage-secrets.md), secrets can be controlled in a secure location and referenced in a connector's properties using a placeholder pattern `{{secrets.*}}`. To make this mechanism as robust as possible, secret handling comes with the connector SDK out of the box. That way, all connectors can use the same standard way of handling secrets in input data. The SDK allows replacing secrets in input data as late as possible to avoid passing them around in the environments that handle connector invocation. We do not pass secrets into the Connector function in clear text but only as placeholders that you can replace from within the connector function. Secrets are replaced automatically in the connector input when you use the variable binding or properties access methods of the `InboundConnectorContext`. You will always receive inputs with secrets replaced. The Runtime automatically replaces secrets in String fields or in container types. Using the placeholder pattern `{{secrets.*}}` in a String field will replace the placeholder with the secret value. Using the placeholder pattern in a container type will replace the placeholder in all String fields of the container type. ## Runtime environments To integrate connectors with your business use case, you need a runtime environment to act as the intermediary between your business and connectors space. The Connector SDK enables you to write environment-agnostic runtime behavior for connectors. This makes the connector logic reusable in different setups without modifying your connector code. To invoke this logic, you need a runtime environment that knows the connector function and how to call it. In Camunda 8 SaaS, every cluster runs a component that knows the [available out-of-the-box connectors](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md) and how to invoke them. This component is the runtime environment specific to Camunda's SaaS use case. Regarding Self-Managed environments, you are responsible for providing the runtime environment that can invoke the connectors. There are several runtime options provided by Camunda: - [Spring Boot Starter runtime](#spring-boot-starter-runtime) - [Docker runtime image](#docker-runtime-image) ### Spring Boot Starter runtime This option is applicable for Spring Boot users. All you need to do is to include respective starter: ```xml io.camunda.connector spring-boot-starter-camunda-connectors ${version.connectors} org.myorg connector-my-awesome ${version.connector-my-awesome} ``` Upon starting your Spring Boot application, you will have a job worker connected to Zeebe, waiting to receive jobs for your connectors. ### Docker runtime image This option is applicable for those users who prefer Docker. Make sure to have an orchestration cluster running. A good start is the [Camunda Distributions](https://github.com/camunda/camunda-distributions/tree/main/docker-compose) docker compose repository. Clone the repository. Switch into the version folder you would like to run and start the cluster: ```shell docker compose -f docker-compose.yaml up ``` The latest Connectors Docker images can be found at the [Docker Hub](https://hub.docker.com/r/camunda/connectors). You can start the runtime including your Connector jar by running: ```shell docker run --rm -i \ -v $PWD/your-connector.jar:/opt/app/connector.jar \ # Add a connector jar to the classpath --network=camunda \ # Optional: Attach to the orchestration cluster Docker network -e CAMUNDA_CLIENT_GRPC_ADDRESS=http://orchestration:26500 \ # Specify cluster GRPC API address -e CAMUNDA_CLIENT_REST_ADDRESS=http://orchestration:8080 \ # Specify cluster REST API address camunda/connectors:X.Y.Z # Connector docker image version ``` If you would like to disable inbound connectors, you can do so by setting `CAMUNDA_CONNECTOR_POLLING_ENABLED=false`. --- ## Connector templates Connectors are available [out-of-the-box (OOTB)](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md) and come with [connector templates](/components/connectors/manage-connector-templates.md) that customize how a BPMN element is shown and how it can be configured by process developers. Connector templates are a specific type of [element template](/components/modeler/element-templates/about-templates.md), which can also be used when creating custom connectors via the [Connector SDK](/components/connectors/custom-built-connectors/connector-sdk.md). Additionally, the [Camunda Marketplace](/components/hub/workspace/modeler/modeling/camunda-marketplace.md) provides connectors by Camunda partners and community contributors. Before developing one, you'll need to decide what you would like to achieve with your connector. Currently, the options are: - Starting a BPMN process, triggered by external service - Use [inbound start event connector template](#inbound-start-event-connector-templates). - Continue process with an intermediate catch event emitted by external service call - Use [inbound intermediate catch event connector templates](#inbound-intermediate-catch-event-connector-templates). - Trigger an external service - Use [outbound connector template](#outbound-connector-templates). :::note Do not confuse **Connector templates** with the **[Connector template](https://github.com/camunda/connector-template-outbound)**, which is used to supply boilerplate code and configuration when developing a new custom connector. ::: ## Inbound start event connector templates You can, for example, allow the user to model and configure the following **HTTP Webhook connector** by providing a simple JSON configuration: ![Webhook Inbound connector Example](./img/custom-connector-template-inbound-start.png) ```json { "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", "name": "Webhook connector", "id": "io.camunda.connectors.webhook.WebhookConnector.v1", "version": 1, "description": "Configure webhook to receive callbacks", "documentationRef": "https://docs.camunda.io/docs/components/connectors/out-of-the-box-connectors/http-webhook/", "category": { "id": "connectors", "name": "Connectors" }, "appliesTo": ["bpmn:StartEvent"], "elementType": { "value": "bpmn:StartEvent" }, "groups": [ { "id": "endpoint", "label": "Webhook Configuration" }, { "id": "activation", "label": "Activation" }, { "id": "variable-mapping", "label": "Variable Mapping" } ], "properties": [ { "type": "Hidden", "value": "io.camunda:webhook:1", "binding": { "type": "zeebe:property", "name": "inbound.type" } }, { "type": "Hidden", "value": "ConfigurableInboundWebhook", "binding": { "type": "zeebe:property", "name": "inbound.subtype" } }, { "label": "Webhook ID", "type": "String", "group": "endpoint", "binding": { "type": "zeebe:property", "name": "inbound.context" }, "description": "The webhook ID is a part of the URL" }, { "id": "shouldValidateHmac", "label": "HMAC authentication", "group": "endpoint", "description": "Choose whether HMAC verification is enabled. See documentation and example that explains how to use HMAC-related fields", "value": "disabled", "type": "Dropdown", "choices": [ { "name": "Enabled", "value": "enabled" }, { "name": "Disabled", "value": "disabled" } ], "binding": { "type": "zeebe:property", "name": "inbound.shouldValidateHmac" } }, { "label": "HMAC secret key", "description": "Shared secret key", "type": "String", "group": "endpoint", "optional": true, "binding": { "type": "zeebe:property", "name": "inbound.hmacSecret" }, "condition": { "property": "shouldValidateHmac", "equals": "enabled" } }, { "label": "HMAC header", "description": "Name of header attribute that will contain the HMAC value", "type": "String", "group": "endpoint", "optional": true, "binding": { "type": "zeebe:property", "name": "inbound.hmacHeader" }, "condition": { "property": "shouldValidateHmac", "equals": "enabled" } }, { "label": "HMAC algorithm", "group": "endpoint", "description": "Choose HMAC algorithm", "value": "sha_256", "type": "Dropdown", "choices": [ { "name": "SHA-1", "value": "sha_1" }, { "name": "SHA-256", "value": "sha_256" }, { "name": "SHA-512", "value": "sha_512" } ], "binding": { "type": "zeebe:property", "name": "inbound.hmacAlgorithm" }, "condition": { "property": "shouldValidateHmac", "equals": "enabled" } }, { "label": "Condition", "type": "String", "group": "activation", "feel": "required", "optional": true, "binding": { "type": "zeebe:property", "name": "inbound.activationCondition" }, "description": "Condition under which the connector triggers. Leave empty to catch all events. See documentation" }, { "label": "Variables", "type": "String", "group": "variable-mapping", "feel": "required", "binding": { "type": "zeebe:property", "name": "inbound.variableMapping" }, "description": "Map variables from the webhook payload (request) to start the process with. When blank, entire payload is copied over. See documentation" } ], "icon": { "contents": "data:image/svg+xml,%3Csvg id='icon' xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 32 32'%3E%3Cdefs%3E%3Cstyle%3E .cls-1 %7B fill: none; %7D %3C/style%3E%3C/defs%3E%3Cpath d='M24,26a3,3,0,1,0-2.8164-4H13v1a5,5,0,1,1-5-5V16a7,7,0,1,0,6.9287,8h6.2549A2.9914,2.9914,0,0,0,24,26Z'/%3E%3Cpath d='M24,16a7.024,7.024,0,0,0-2.57.4873l-3.1656-5.5395a3.0469,3.0469,0,1,0-1.7326.9985l4.1189,7.2085.8686-.4976a5.0006,5.0006,0,1,1-1.851,6.8418L17.937,26.501A7.0005,7.0005,0,1,0,24,16Z'/%3E%3Cpath d='M8.532,20.0537a3.03,3.03,0,1,0,1.7326.9985C11.74,18.47,13.86,14.7607,13.89,14.708l.4976-.8682-.8677-.497a5,5,0,1,1,6.812-1.8438l1.7315,1.002a7.0008,7.0008,0,1,0-10.3462,2.0356c-.457.7427-1.1021,1.8716-2.0737,3.5728Z'/%3E%3Crect id='_Transparent_Rectangle_' data-name='<Transparent Rectangle>' class='cls-1' width='32' height='32'/%3E%3C/svg%3E" } } ``` ## Inbound intermediate catch event connector templates You can, for example, allow the user to model and configure the following **HTTP Webhook connector** by providing a simple JSON configuration: ![Webhook Inbound intermediate connector Example](./img/custom-connector-template-inbound-intermediate.png) ```json { "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", "name": "Webhook connector", "id": "io.camunda.connectors.webhook.WebhookConnectorIntermediate.v1", "version": 1, "description": "Configure webhook to receive callbacks", "documentationRef": "https://docs.camunda.io/docs/components/connectors/out-of-the-box-connectors/http-webhook/", "category": { "id": "connectors", "name": "Connectors" }, "appliesTo": ["bpmn:IntermediateCatchEvent", "bpmn:IntermediateThrowEvent"], "elementType": { "value": "bpmn:IntermediateCatchEvent", "eventDefinition": "bpmn:MessageEventDefinition" }, "groups": [ { "id": "endpoint", "label": "Webhook Configuration" }, { "id": "activation", "label": "Activation" }, { "id": "variable-mapping", "label": "Variable Mapping" } ], "properties": [ { "type": "Hidden", "value": "io.camunda:webhook:1", "binding": { "type": "zeebe:property", "name": "inbound.type" } }, { "type": "Hidden", "generatedValue": { "type": "uuid" }, "binding": { "type": "bpmn:Message#property", "name": "name" } }, { "type": "Hidden", "value": "ConfigurableInboundWebhook", "binding": { "type": "zeebe:property", "name": "inbound.subtype" } }, { "label": "Webhook ID", "type": "String", "group": "endpoint", "binding": { "type": "zeebe:property", "name": "inbound.context" }, "description": "The webhook ID is a part of the URL" }, { "id": "shouldValidateHmac", "label": "HMAC authentication", "group": "endpoint", "description": "Choose whether HMAC verification is enabled. See documentation and example that explains how to use HMAC-related fields", "value": "disabled", "type": "Dropdown", "choices": [ { "name": "Enabled", "value": "enabled" }, { "name": "Disabled", "value": "disabled" } ], "binding": { "type": "zeebe:property", "name": "inbound.shouldValidateHmac" } }, { "label": "HMAC secret key", "description": "Shared secret key", "type": "String", "group": "endpoint", "optional": true, "binding": { "type": "zeebe:property", "name": "inbound.hmacSecret" }, "condition": { "property": "shouldValidateHmac", "equals": "enabled" } }, { "label": "HMAC header", "description": "Name of header attribute that will contain the HMAC value", "type": "String", "group": "endpoint", "optional": true, "binding": { "type": "zeebe:property", "name": "inbound.hmacHeader" }, "condition": { "property": "shouldValidateHmac", "equals": "enabled" } }, { "label": "HMAC algorithm", "group": "endpoint", "description": "Choose HMAC algorithm", "value": "sha_256", "type": "Dropdown", "choices": [ { "name": "SHA-1", "value": "sha_1" }, { "name": "SHA-256", "value": "sha_256" }, { "name": "SHA-512", "value": "sha_512" } ], "binding": { "type": "zeebe:property", "name": "inbound.hmacAlgorithm" }, "condition": { "property": "shouldValidateHmac", "equals": "enabled" } }, { "label": "Correlation key (process)", "type": "String", "group": "activation", "feel": "required", "description": "Sets up the correlation key from process variables", "binding": { "type": "bpmn:Message#zeebe:subscription#property", "name": "correlationKey" }, "constraints": { "notEmpty": true } }, { "label": "Correlation key (payload)", "type": "String", "group": "activation", "feel": "required", "binding": { "type": "zeebe:property", "name": "correlationKeyExpression" }, "description": "Extracts the correlation key from the incoming message payload", "constraints": { "notEmpty": true } }, { "label": "Condition", "type": "String", "group": "activation", "feel": "required", "optional": true, "binding": { "type": "zeebe:property", "name": "inbound.activationCondition" }, "description": "Condition under which the connector triggers. Leave empty to catch all events. See documentation" }, { "label": "Variables", "type": "String", "group": "variable-mapping", "feel": "required", "binding": { "type": "zeebe:property", "name": "inbound.variableMapping" }, "description": "Map variables from the webhook payload (request) to start the process with. When blank, entire payload is copied over. See documentation" } ], "icon": { "contents": "data:image/svg+xml,%3Csvg id='icon' xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 32 32'%3E%3Cdefs%3E%3Cstyle%3E .cls-1 %7B fill: none; %7D %3C/style%3E%3C/defs%3E%3Cpath d='M24,26a3,3,0,1,0-2.8164-4H13v1a5,5,0,1,1-5-5V16a7,7,0,1,0,6.9287,8h6.2549A2.9914,2.9914,0,0,0,24,26Z'/%3E%3Cpath d='M24,16a7.024,7.024,0,0,0-2.57.4873l-3.1656-5.5395a3.0469,3.0469,0,1,0-1.7326.9985l4.1189,7.2085.8686-.4976a5.0006,5.0006,0,1,1-1.851,6.8418L17.937,26.501A7.0005,7.0005,0,1,0,24,16Z'/%3E%3Cpath d='M8.532,20.0537a3.03,3.03,0,1,0,1.7326.9985C11.74,18.47,13.86,14.7607,13.89,14.708l.4976-.8682-.8677-.497a5,5,0,1,1,6.812-1.8438l1.7315,1.002a7.0008,7.0008,0,1,0-10.3462,2.0356c-.457.7427-1.1021,1.8716-2.0737,3.5728Z'/%3E%3Crect id='_Transparent_Rectangle_' data-name='<Transparent Rectangle>' class='cls-1' width='32' height='32'/%3E%3C/svg%3E" } } ``` ## Inbound boundary event connector templates You can, for example, allow the user to model and configure the following **HTTP webhook connector** for boundary events by providing a simple JSON configuration: ![Webhook Inbound boundary connector Example.png](img/custom-connector-template-inbound-boundary.png) ```json { "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", "name": "Webhook Boundary Event connector", "id": "io.camunda.connectors.webhook.WebhookConnectorBoundary.v1", "description": "Configure webhook to receive callbacks", "documentationRef": "https://docs.camunda.io/docs/components/connectors/out-of-the-box-connectors/http-webhook/", "version": 11, "category": { "id": "connectors", "name": "Connectors" }, "appliesTo": ["bpmn:BoundaryEvent"], "elementType": { "value": "bpmn:BoundaryEvent", "eventDefinition": "bpmn:MessageEventDefinition" }, "groups": [ { "id": "endpoint", "label": "Webhook configuration" }, { "id": "authentication", "label": "Authentication" }, { "id": "authorization", "label": "Authorization" }, { "id": "webhookResponse", "label": "Webhook response" }, { "id": "activation", "label": "Activation" }, { "id": "correlation", "label": "Correlation", "tooltip": "Learn more about message correlation in the documentation." }, { "id": "output", "label": "Output mapping" } ], "properties": [ { "value": "io.camunda:webhook:1", "binding": { "name": "inbound.type", "type": "zeebe:property" }, "type": "Hidden" }, { "id": "inbound.method", "label": "Webhook method", "description": "Select HTTP method", "optional": false, "value": "any", "group": "endpoint", "binding": { "name": "inbound.method", "type": "zeebe:property" }, "type": "Dropdown", "choices": [ { "name": "Any", "value": "any" }, { "name": "GET", "value": "get" }, { "name": "POST", "value": "post" }, { "name": "PUT", "value": "put" }, { "name": "DELETE", "value": "delete" } ] }, { "id": "inbound.context", "label": "Webhook ID", "description": "The webhook ID is a part of the URL", "optional": false, "constraints": { "notEmpty": true, "pattern": { "value": "^[a-zA-Z0-9]+([-_][a-zA-Z0-9]+)*$", "message": "can only contain letters, numbers, or single underscores/hyphens and cannot begin or end with an underscore/hyphen" } }, "group": "endpoint", "binding": { "name": "inbound.context", "type": "zeebe:property" }, "type": "String" }, { "id": "inbound.shouldValidateHmac", "label": "HMAC authentication", "description": "Choose whether HMAC verification is enabled. See documentation and example that explains how to use HMAC-related fields", "optional": false, "value": "disabled", "group": "authentication", "binding": { "name": "inbound.shouldValidateHmac", "type": "zeebe:property" }, "type": "Dropdown", "choices": [ { "name": "Enabled", "value": "enabled" }, { "name": "Disabled", "value": "disabled" } ] }, { "id": "inbound.hmacSecret", "label": "HMAC secret key", "description": "Shared secret key", "optional": true, "feel": "optional", "group": "authentication", "binding": { "name": "inbound.hmacSecret", "type": "zeebe:property" }, "condition": { "property": "inbound.shouldValidateHmac", "equals": "enabled", "type": "simple" }, "type": "String" }, { "id": "inbound.hmacHeader", "label": "HMAC header", "description": "Name of header attribute that will contain the HMAC value", "optional": true, "feel": "optional", "group": "authentication", "binding": { "name": "inbound.hmacHeader", "type": "zeebe:property" }, "condition": { "property": "inbound.shouldValidateHmac", "equals": "enabled", "type": "simple" }, "type": "String" }, { "id": "inbound.hmacAlgorithm", "label": "HMAC algorithm", "description": "Choose HMAC algorithm", "optional": false, "value": "sha_256", "group": "authentication", "binding": { "name": "inbound.hmacAlgorithm", "type": "zeebe:property" }, "condition": { "property": "inbound.shouldValidateHmac", "equals": "enabled", "type": "simple" }, "type": "Dropdown", "choices": [ { "name": "SHA-1", "value": "sha_1" }, { "name": "SHA-256", "value": "sha_256" }, { "name": "SHA-512", "value": "sha_512" } ] }, { "id": "inbound.hmacScopes", "label": "HMAC scopes", "description": "Set HMAC scopes for calculating signature data. See documentation", "optional": true, "feel": "required", "group": "authentication", "binding": { "name": "inbound.hmacScopes", "type": "zeebe:property" }, "condition": { "property": "inbound.shouldValidateHmac", "equals": "enabled", "type": "simple" }, "type": "String" }, { "id": "inbound.auth.type", "label": "Authorization type", "description": "Choose the authorization type", "value": "NONE", "group": "authorization", "binding": { "name": "inbound.auth.type", "type": "zeebe:property" }, "type": "Dropdown", "choices": [ { "name": "None", "value": "NONE" }, { "name": "Basic", "value": "BASIC" }, { "name": "API key", "value": "APIKEY" }, { "name": "JWT", "value": "JWT" } ] }, { "id": "inbound.auth.username", "label": "Username", "description": "Username for basic authentication", "optional": false, "feel": "optional", "group": "authorization", "binding": { "name": "inbound.auth.username", "type": "zeebe:property" }, "condition": { "property": "inbound.auth.type", "equals": "BASIC", "type": "simple" }, "type": "String" }, { "id": "inbound.auth.password", "label": "Password", "description": "Password for basic authentication", "optional": false, "feel": "optional", "group": "authorization", "binding": { "name": "inbound.auth.password", "type": "zeebe:property" }, "condition": { "property": "inbound.auth.type", "equals": "BASIC", "type": "simple" }, "type": "String" }, { "id": "inbound.auth.apiKey", "label": "API key", "description": "Expected API key", "optional": false, "feel": "optional", "group": "authorization", "binding": { "name": "inbound.auth.apiKey", "type": "zeebe:property" }, "condition": { "property": "inbound.auth.type", "equals": "APIKEY", "type": "simple" }, "type": "String" }, { "id": "inbound.auth.apiKeyLocator", "label": "API key locator", "description": "A FEEL expression that extracts API key from the request. See documentation", "optional": false, "value": "=split(request.headers.authorization, \" \")[2]", "constraints": { "notEmpty": true }, "feel": "required", "group": "authorization", "binding": { "name": "inbound.auth.apiKeyLocator", "type": "zeebe:property" }, "condition": { "property": "inbound.auth.type", "equals": "APIKEY", "type": "simple" }, "type": "String" }, { "id": "inbound.auth.jwt.jwkUrl", "label": "JWK URL", "description": "Well-known URL of JWKs", "optional": false, "feel": "optional", "group": "authorization", "binding": { "name": "inbound.auth.jwt.jwkUrl", "type": "zeebe:property" }, "condition": { "property": "inbound.auth.type", "equals": "JWT", "type": "simple" }, "type": "String" }, { "id": "inbound.auth.jwt.permissionsExpression", "label": "JWT role property expression", "description": "Expression to extract the roles from the JWT token. See documentation", "optional": false, "feel": "required", "group": "authorization", "binding": { "name": "inbound.auth.jwt.permissionsExpression", "type": "zeebe:property" }, "condition": { "property": "inbound.auth.type", "equals": "JWT", "type": "simple" }, "type": "String" }, { "id": "inbound.auth.jwt.requiredPermissions", "label": "Required roles", "description": "List of roles to test JWT roles against", "optional": false, "feel": "required", "group": "authorization", "binding": { "name": "inbound.auth.jwt.requiredPermissions", "type": "zeebe:property" }, "condition": { "property": "inbound.auth.type", "equals": "JWT", "type": "simple" }, "type": "String" }, { "id": "inbound.responseExpression", "label": "Response expression", "description": "Expression used to generate the HTTP response", "optional": true, "feel": "required", "group": "webhookResponse", "binding": { "name": "inbound.responseExpression", "type": "zeebe:property" }, "type": "Text" }, { "id": "inbound.verificationExpression", "label": "One time verification response expression", "description": "Specify condition and response. Learn more in the documentation", "optional": true, "feel": "required", "group": "webhookResponse", "binding": { "name": "inbound.verificationExpression", "type": "zeebe:property" }, "type": "Text" }, { "id": "activationCondition", "label": "Activation condition", "description": "Condition under which the connector triggers. Leave empty to catch all events", "optional": true, "feel": "required", "group": "activation", "binding": { "name": "activationCondition", "type": "zeebe:property" }, "type": "String" }, { "id": "consumeUnmatchedEvents", "label": "Consume unmatched events", "value": true, "group": "activation", "binding": { "name": "consumeUnmatchedEvents", "type": "zeebe:property" }, "tooltip": "Unmatched events are rejected by default, allowing the upstream service to handle the error. Check this box to consume unmatched events and return a success response", "type": "Boolean" }, { "id": "correlationKeyProcess", "label": "Correlation key (process)", "description": "Sets up the correlation key from process variables", "constraints": { "notEmpty": true }, "feel": "required", "group": "correlation", "binding": { "name": "correlationKey", "type": "bpmn:Message#zeebe:subscription#property" }, "type": "String" }, { "id": "correlationKeyPayload", "label": "Correlation key (payload)", "description": "Extracts the correlation key from the incoming message payload", "constraints": { "notEmpty": true }, "feel": "required", "group": "correlation", "binding": { "name": "correlationKeyExpression", "type": "zeebe:property" }, "type": "String" }, { "id": "messageIdExpression", "label": "Message ID expression", "description": "Expression to extract unique identifier of a message", "optional": true, "feel": "required", "group": "correlation", "binding": { "name": "messageIdExpression", "type": "zeebe:property" }, "type": "String" }, { "id": "messageTtl", "label": "Message TTL", "description": "Time-to-live for the message in the broker (ISO-8601 duration)", "optional": true, "constraints": { "notEmpty": false, "pattern": { "value": "^(PT.*|)$", "message": "must be an ISO-8601 duration" } }, "feel": "optional", "group": "correlation", "binding": { "name": "messageTtl", "type": "zeebe:property" }, "type": "String" }, { "id": "messageNameUuid", "generatedValue": { "type": "uuid" }, "group": "correlation", "binding": { "name": "name", "type": "bpmn:Message#property" }, "type": "Hidden" }, { "id": "resultVariable", "label": "Result variable", "description": "Name of variable to store the response in", "group": "output", "binding": { "name": "resultVariable", "type": "zeebe:property" }, "type": "String" }, { "id": "resultExpression", "label": "Result expression", "description": "Expression to map the response into process variables", "feel": "required", "group": "output", "binding": { "name": "resultExpression", "type": "zeebe:property" }, "type": "Text" } ], "icon": { "contents": "data:image/svg+xml;base64,PHN2ZyBpZD0naWNvbicgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJyB3aWR0aD0nMTgnIGhlaWdodD0nMTgnIHZpZXdCb3g9JzAgMCAzMiAzMic+CiAgPGRlZnM+CiAgICA8c3R5bGU+LmNscy0xIHsgZmlsbDogbm9uZTsgfTwvc3R5bGU+CiAgPC9kZWZzPgogIDxwYXRoCiAgICBkPSdNMjQsMjZhMywzLDAsMSwwLTIuODE2NC00SDEzdjFhNSw1LDAsMSwxLTUtNVYxNmE3LDcsMCwxLDAsNi45Mjg3LDhoNi4yNTQ5QTIuOTkxNCwyLjk5MTQsMCwwLDAsMjQsMjZaJy8+CiAgPHBhdGgKICAgIGQ9J00yNCwxNmE3LjAyNCw3LjAyNCwwLDAsMC0yLjU3LjQ4NzNsLTMuMTY1Ni01LjUzOTVhMy4wNDY5LDMuMDQ2OSwwLDEsMC0xLjczMjYuOTk4NWw0LjExODksNy4yMDg1Ljg2ODYtLjQ5NzZhNS4wMDA2LDUuMDAwNiwwLDEsMS0xLjg1MSw2Ljg0MThMMTcuOTM3LDI2LjUwMUE3LjAwMDUsNy4wMDA1LDAsMSwwLDI0LDE2WicvPgogIDxwYXRoCiAgICBkPSdNOC41MzIsMjAuMDUzN2EzLjAzLDMuMDMsMCwxLDAsMS43MzI2Ljk5ODVDMTEuNzQsMTguNDcsMTMuODYsMTQuNzYwNywxMy44OSwxNC43MDhsLjQ5NzYtLjg2ODItLjg2NzctLjQ5N2E1LDUsMCwxLDEsNi44MTItMS44NDM4bDEuNzMxNSwxLjAwMmE3LjAwMDgsNy4wMDA4LDAsMSwwLTEwLjM0NjIsMi4wMzU2Yy0uNDU3Ljc0MjctMS4xMDIxLDEuODcxNi0yLjA3MzcsMy41NzI4WicvPgogIDxyZWN0IGlkPSdfVHJhbnNwYXJlbnRfUmVjdGFuZ2xlXycgZGF0YS1uYW1lPScmbHQ7VHJhbnNwYXJlbnQgUmVjdGFuZ2xlJmd0OycgY2xhc3M9J2Nscy0xJwogICAgd2lkdGg9JzMyJyBoZWlnaHQ9JzMyJy8+Cjwvc3ZnPg==" } } ``` ## Outbound connector templates You can, for example, allow the user to model and configure the following **REST connector** by providing a JSON configuration for a service task: ![REST Outbound connector Example](./img/custom-connector-template.png) ```json { "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", "name": "(Conditional) REST connector", "id": "io.camunda.examples.ConditionalRestConnector", "description": "A REST API invocation task.", "appliesTo": ["bpmn:ServiceTask"], "icon": { "contents": "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' viewBox='0 0 22 22' fill='none'%3E%3Ccircle cx='11' cy='11' r='9' fill='black'/%3E%3Ctext x='6.9' y='14.9' fill='white' style='font-family: Arial; font-size: 10px;'%3EM%3C/text%3E%3C/svg%3E" }, "properties": [ { "type": "Hidden", "value": "http", "binding": { "type": "zeebe:taskDefinition", "property": "type" } }, { "label": "REST Endpoint URL", "description": "Specify the url of the REST API to talk to.", "type": "String", "binding": { "type": "zeebe:taskHeader", "key": "url" }, "constraints": { "notEmpty": true, "pattern": { "value": "^https?://.*", "message": "Must be http(s) URL." } } }, { "id": "httpMethod", "label": "REST Method", "description": "Specify the HTTP method to use.", "type": "Dropdown", "value": "get", "choices": [ { "name": "GET", "value": "get" }, { "name": "POST", "value": "post" }, { "name": "PATCH", "value": "patch" }, { "name": "DELETE", "value": "delete" } ], "binding": { "type": "zeebe:taskHeader", "key": "method" } }, { "label": "Request Body", "description": "Data to send to the endpoint.", "value": "", "type": "String", "optional": true, "binding": { "type": "zeebe:input", "name": "body" }, "condition": { "property": "httpMethod", "oneOf": ["patch", "post", "delete"] } }, { "id": "authenticationType", "label": "Authentication Type", "description": "Specify the authentication type to use.", "type": "Dropdown", "value": "", "optional": true, "choices": [ { "name": "None", "value": "" }, { "name": "Basic", "value": "basic" }, { "name": "Bearer", "value": "bearer" } ], "binding": { "type": "zeebe:input", "name": "authentication.type" } }, { "label": "Username", "type": "String", "feel": "optional", "binding": { "type": "zeebe:input", "name": "authentication.username" }, "constraints": { "notEmpty": true }, "condition": { "property": "authenticationType", "equals": "basic" } }, { "label": "Password", "type": "String", "feel": "optional", "binding": { "type": "zeebe:input", "name": "authentication.password" }, "constraints": { "notEmpty": true }, "condition": { "property": "authenticationType", "equals": "basic" } }, { "label": "Bearer Token", "type": "String", "feel": "optional", "binding": { "type": "zeebe:input", "name": "authentication.token" }, "constraints": { "notEmpty": true }, "condition": { "property": "authenticationType", "equals": "bearer" } } ] } ``` ## Develop connector templates You can develop connector templates using the [`element template` feature](/components/modeler/element-templates/defining-templates.md). You can also look at existing [examples](https://github.com/camunda/camunda-modeler/blob/master/resources/element-templates/cloud-samples.json). ## Providing and using connector templates When using [Web Modeler](/components/hub/workspace/modeler/launch-modeler.md), you can create **Connector templates** [directly within the application](/components/connectors/manage-connector-templates.md) and share them with your respective organization. When using [Desktop Modeler](/components/modeler/desktop-modeler/index.md), you must place the **Connector templates** [within the file system](/components/modeler/desktop-modeler/element-templates/configuring-templates.md) so Modeler will pick them up. Once available, process developers can directly [use the **Connector templates** from within the modeling canvas](/components/connectors/use-connectors/index.md). --- ## Create a custom REST connector Create a custom REST connector based on and using the [Camunda REST connector](/components/connectors/protocol/rest.md) as a starting point. ## Create a custom connector based on the Camunda REST connector 1. In Web Modeler, add a [task](/components/modeler/bpmn/tasks.md) element to a new or existing BPMN diagram. 1. Change the task type to [REST connector](/components/connectors/protocol/rest.md). 1. In the Properties panel, configure the connector as required. For example, define the Authentication URL, HTTP method, and any headers or payload needed for the API request. 1. Click **Save as Template**. 1. Enter details for the new connector template, such as a name and description. Save and create the new connector template. 1. Open the new connector template in the template editor and customize it as required. For example, add or remove fields, adjust default values and input parameters, and update the description and other metadata. 1. Save your changes to the new connector template, and use it as required in your processes. :::caution When creating a new template based on the REST connector, you must ensure that any field(s) used to set variables are placed **before** any field(s) that uses these variables. For example, in the following code, as the `url` requires the variables defined by `swid` and `swresource`, it is placed after them. Incorrectly placed variables will be considered as `null`. ```json { "id": "swid", "label": "id", "description": "Index of the resource", "feel": "optional", "group": "swapi", "binding": { "name": "index", "type": "zeebe:input" }, "type": "String" }, { "id": "swresource", "label": "Type", "description": "Choose the resource type", "value": "Planets", "group": "swapi", "binding": { "name": "resource", "type": "zeebe:input" }, "type": "Dropdown", "choices": [...] }, { "id": "url", "label": "URL", "optional": false, "constraints": { "notEmpty": true, "pattern": { "value": "^(=|(http://|https://|secrets|\\{\\{).*$)", "message": "Must be a http(s) URL" } }, "group": "endpoint", "binding": { "name": "url", "type": "zeebe:input" }, "type": "Hidden", "value": "=\"https://swapi.dev/api/\" + resource + \"/\" + index" } ``` ::: ## Example: Custom Star Wars API connector The following example shows how you can create a custom connector based on the Camunda REST connector that retrieves data from the [Star Wars API (SWAPI)](https://swapi.dev/). ### Step 1: Create connector template In this first step, create a new connector template, using the REST connector as a starting point. 1. Create a REST Outbound connector task. 1. Define the URL as a FEEL (Friendly Enough Expression Language) expression, using the `resource` and `index` variables. 1. Click **Save as** to create a template from your configured connector. 1. Enter the **Template Name** and **Template Description** and click **Save**. These fields are essential for identifying and understanding the purpose of the template. ### Step 2: Edit the new connector template Once the new connector template is created, edit and configure it to retrieve data from the Star Wars API. 1. Open the new connector template in the Template Editor. 1. Hide Unwanted Properties: For properties that are not required in your connector, set the `type` to `Hidden`. For example, since authentication is not required in this example, it is set to `Hidden`. ```json { "id": "authentication.type", "label": "Type", "description": "Choose the authentication type. Select 'None' if no authentication is necessary", "value": "noAuth", "group": "authentication", "binding": { "name": "authentication.type", "type": "zeebe:input" }, "type": "Hidden", "choices": [ { "name": "API key", "value": "apiKey" }, { "name": "Basic", "value": "basic" }, { "name": "Bearer token", "value": "bearer" }, { "name": "None", "value": "noAuth" }, { "name": "OAuth 2.0", "value": "oauth-client-credentials-flow" } ] } ``` Similarly, hide other fields such as `url`, `method`, `headers`, and `queryParameters`. If the `hidden` type does not apply, ensure that you remove the `feel` property. ```json { "id": "url", "label": "URL", "optional": false, "constraints": { "notEmpty": true, "pattern": { "value": "^(=|(http://|https://|secrets|\\{\\{).*$)", "message": "Must be a http(s) URL" } }, "group": "endpoint", "binding": { "name": "url", "type": "zeebe:input" }, "type": "Hidden", "value": "=\"https://swapi.dev/api/\" + resource + \"/\" + index" } ``` 1. Create a Custom Group for the Star Wars API: Add a customized group named `swapi` for organizing your Star Wars-related properties. ```json { "id": "swapi", "label": "Star Wars Payload" } ``` 1. Define the Properties in the SWAPI Group: Map the properties within the new group to the previously defined `resource` and `index` variables. - Set `resource` as a `Dropdown`. - Set `index` as a `String`. ```json [ { "id": "swid", "label": "id", "description": "Index of the resource", "feel": "optional", "group": "swapi", "binding": { "name": "index", "type": "zeebe:input" }, "type": "String" }, { "id": "swresource", "label": "Type", "description": "Choose the resource type", "value": "Planets", "group": "swapi", "binding": { "name": "resource", "type": "zeebe:input" }, "type": "Dropdown", "choices": [ { "name": "Planets", "value": "planets" }, { "name": "Spaceships", "value": "spaceships" }, { "name": "Vehicles", "value": "vehicles" }, { "name": "People", "value": "people" }, { "name": "Films", "value": "films" }, { "name": "species", "value": "Species" } ] } ] ``` 1. Add an appropriate icon if required to enhance your connector's visual appeal. 1. Once configuration is complete, click **Publish** to publish the connector template and make it available for use. 1. Use your newly published SWAPI connector in your BPMN workflows. --- ## Host custom connectors This guide explains how to host your own **Connectors** developed with [Connector SDK](/components/connectors/custom-built-connectors/connector-sdk.md). ## Prerequisites - Ensure you have to have a working Camunda cluster in SaaS or Self-Managed. - Ensure you have a distribution version of your connector in the form of "fat" `jar` file. For the purpose of this guide, we will be using a generic [Connector template](https://github.com/camunda/connector-template-outbound) as a reference. Clone the repository, and execute `mvn clean verify package`. This will produce a file called `target/connector-template-0.1.0-SNAPSHOT-with-dependencies.jar`. In this guide, we will refer this file as `connector.jar`. ## Wiring your connector with a Camunda cluster This approach is equivalent to the [hybrid mode](/components/connectors/use-connectors-in-hybrid-mode.md), except you don't need to override existing connectors and instead add a new one. You need to have a running Camunda cluster, and a pair of `Client ID`/`Client Secret` with `Zeebe` and `Operate` scopes. Learn more about [how to obtain required credentials](/components/hub/organization/manage-clusters/manage-api-clients.md). Run the following command: ```shell docker run --rm --name=CustomConnectorInSaaS \ -v $PWD/connector.jar:/opt/app/connector.jar \ -e CAMUNDA_CLIENT_SECURITY_PLAINTEXT=false \ -e CAMUNDA_CLIENT_CLOUD_CLUSTER-ID='' \ -e CAMUNDA_CLIENT_CLOUD_CLIENT-ID='' \ -e CAMUNDA_CLIENT_CLOUD_CLIENT-SECRET='' \ -e CAMUNDA_CLIENT_CLOUD_REGION='' \ -e CAMUNDA_OPERATE_CLIENT_URL='https://.api.camunda.io/' \ camunda/connectors-bundle: ``` The line `-v $PWD/connector.jar:/opt/app/connector.jar` binds a volume with your connector at the path `$PWD/connector.jar` of you local machine. ## Wiring your connector with Camunda Docker instance (without Keycloak) This option is applicable if you launch your cluster in a Self-Managed version with [Camunda Docker Compose variant without Keycloak](https://github.com/camunda/camunda-distributions/tree/main/docker-compose). Run the following command: ```shell docker run --rm --name=CustomConnectorInSMCore \ -v $PWD/connector.jar:/opt/app/connector.jar \ --network=camunda-platform_camunda-platform \ -e CAMUNDA_CLIENT_BROKER_GATEWAY-ADDRESS=zeebe:26500 \ -e CAMUNDA_CLIENT_SECURITY_PLAINTEXT=true \ -e CAMUNDA_OPERATE_CLIENT_URL=http://operate:8080 \ -e CAMUNDA_OPERATE_CLIENT_USERNAME=demo \ -e CAMUNDA_OPERATE_CLIENT_PASSWORD=demo \ camunda/connectors-bundle: ``` :::note Exact values of the environment variables related to Zeebe, Operate, or network may depend on your own configuration. ::: ## Wiring your connector with Camunda Docker instance (with Keycloak) This option is applicable if you launch your cluster in a Self-Managed version with [Camunda Platform Docker Compose variant with Keycloak](https://github.com/camunda/camunda-distributions/tree/main/docker-compose). Run the following command: ```shell docker run --rm --name=CustomConnectorInSMWithKeyCloak \ -v $PWD/connector.jar:/opt/app/connector.jar \ --network=camunda-platform_camunda-platform \ -e CAMUNDA_CLIENT_BROKER_GATEWAY-ADDRESS=zeebe:26500 \ -e CAMUNDA_CLIENT_SECURITY_PLAINTEXT=true \ -e CAMUNDA_CLIENT_ID= \ -e CAMUNDA_CLIENT_SECRET= \ -e CAMUNDA_CLIENT_CONFIG_PATH=/tmp/zeebe_auth_cache \ -e ZEEBE_TOKEN_AUDIENCE=zeebe-api \ -e ZEEBE_AUTHORIZATION_SERVER_URL=http://keycloak-service:18080/auth/realms/camunda-platform/protocol/openid-connect/token \ -e CAMUNDA_IDENTITY_TYPE=KEYCLOAK \ -e CAMUNDA_IDENTITY_AUDIENCE=operate-api \ -e CAMUNDA_IDENTITY_ISSUER_BACKEND_URL=http://keycloak:18080/auth/realms/camunda-platform \ -e CAMUNDA_IDENTITY_CLIENT_ID=connectors \ -e CAMUNDA_IDENTITY_CLIENT_SECRET= \ -e CAMUNDA_OPERATE_CLIENT_URL=http://operate:8080 \ camunda/connectors-bundle: ``` :::note Exact values of the environment variables related to Zeebe, Operate, Keycloak, or network may depend on your own configuration. ::: ## Wiring your connector with Camunda Helm charts There are multiple ways to configure a Helm/Kubernetes Self-Managed cluster. Refer to the [official guide](/self-managed/setup/overview.md) to learn more. For the purpose of this section, imagine you installed Helm charts with `helm install camunda camunda/camunda-platform --version $HELM_CHART_VERSION`, and forwarded the Zeebe and Operate ports. If you use [Keycloak deployed via the Keycloak Operator](/self-managed/deployment/helm/configure/operator-based-infrastructure.md), also forward the Keycloak port: ```bash kubectl port-forward svc/camunda-zeebe-gateway 26500:26500 kubectl port-forward svc/camunda-zeebe-gateway 8080:8080 ``` ```bash # Only if using Keycloak kubectl port-forward svc/keycloak-service 18080:18080 ``` Now, you need to obtain both Zeebe and connectors' Operate OAuth clients. You can do it with `kubectl get secret camunda-zeebe-identity-secret -o jsonpath="{.data.*}" | base64 --decode` and `kubectl get secret camunda-connectors-identity-secret -o jsonpath="{.data.*}" | base64 --decode` respectively. Run the following command: ```shell docker run --rm --name=CustomConnectorInSMWithHelm \ -v $PWD/connector.jar:/opt/app/connector.jar \ -e CAMUNDA_CLIENT_BROKER_GATEWAY-ADDRESS=host.docker.internal:26500 \ -e CAMUNDA_CLIENT_SECURITY_PLAINTEXT=true \ -e CAMUNDA_CLIENT_ID=zeebe \ -e CAMUNDA_CLIENT_SECRET= \ -e CAMUNDA_CLIENT_CONFIG_PATH=/tmp/zeebe_auth_cache \ -e ZEEBE_TOKEN_AUDIENCE=zeebe-api \ -e ZEEBE_AUTHORIZATION_SERVER_URL=http://host.docker.internal:18080/auth/realms/camunda-platform/protocol/openid-connect/token \ -e CAMUNDA_IDENTITY_TYPE=KEYCLOAK \ -e CAMUNDA_IDENTITY_AUDIENCE=operate-api \ -e CAMUNDA_IDENTITY_ISSUER_BACKEND_URL=http://host.docker.internal:18080/auth/realms/camunda-platform \ -e CAMUNDA_IDENTITY_CLIENT_ID=connectors \ -e CAMUNDA_IDENTITY_CLIENT_SECRET= \ -e CAMUNDA_OPERATE_CLIENT_URL=http://host.docker.internal:8081 \ camunda/connectors-bundle: ``` :::note Exact values of the environment variables related to Zeebe, Operate, Keycloak, or network may depend on your own configuration. ::: Interested in creating a custom connector? Review the related Camunda Academy courses on [creating a custom inbound connector](https://academy.camunda.com/c8-custom-inbound-connectors) or [creating a custom outbound connector](https://academy.camunda.com/c8-custom-outbound-connectors). --- ## Connectors(Connectors) [Built-in connectors](/reference/glossary.md#connector) are reusable building blocks you can use to easily connect processes to external systems, applications, and data. - Use a [Slack](/components/connectors/out-of-the-box-connectors/slack.md) or [Microsoft Teams](/components/connectors/out-of-the-box-connectors/microsoft-teams.md) connector to notify people of pending or completed processes in a human task orchestration onboarding flow. - In microservices orchestration, use a [Kafka](/components/connectors/out-of-the-box-connectors/kafka.md) connector to add real-time messaging to your automated processes. Connectors are often configured as a [BPMN process](/components/concepts/processes.md) task, with the required integration parameters already set up for easy configuration. This helps remove the need for you to write custom integration programming code. Find a built-in connector ## Get started New to connectors? Learn about connector types and how to start using connectors in your processes. :::info Get started by learning how to [integrate a Camunda 8 connector](/components/connectors/use-connectors/configuring-out-of-the-box-connector.md). ::: ## Custom connectors Build and deploy your own [custom connectors](../custom-built-connectors/build-connector) using [connector templates](/reference/glossary.md#connector-template) and the connector SDK. ## Featured connectors Get started with our latest and most popular connectors. --- ## Manage connector templates Connector templates are a specific type of [element template](/components/concepts/element-templates.md). To manage them, see the [Manage element templates](/components/hub/workspace/modeler/element-templates/manage-element-templates.md) documentation. --- ## Ad-Hoc Tools Schema Resolver connector The **Ad-Hoc Tools Schema connector** is an outbound connector that implements the tool resolution part of the [**AI Agent connector**](./agentic-ai-aiagent.md). ## About this connector Although its function is also embedded in the AI Agent connector, the Ad-Hoc Tools Schema connector can be used independently in combination with other AI connectors. This can be useful for: - **Direct LLM interaction**: If you don't want to use the AI Agent connector but still want to resolve tools for an ad-hoc sub-process, use the `fromAi` function to define the input schema in combination with custom LLM integrations. - **Debugging tool definitions**: If you want to test the tool resolution logic without having to set up a full AI Agent connector, you can use this connector to get the tool definitions and see how they are generated. ## Prerequisites The following prerequisites are required to use this connector: | Prerequisite | Description | | :----------------- | :-------------------------------------------------------------------------- | | Ad-hoc sub-process | Your process must contain an ad-hoc sub-process whose ID you can reference. | ## Create an Ad-Hoc Tools Schema connector task 1. Create a service task. 2. [Apply](../use-connectors/outbound.md) the **Ad-Hoc Tools Schema** element template. 3. Configure the **Ad-hoc sub-process ID** to reference the element ID of the ad-hoc sub-process. 4. Configure [a result variable or a result expression](../use-connectors/index.md#variableresponse-mapping) to map the connector results to process variables. ## Tool Definitions For more information on how to define tools, see the [tool definitions](agentic-ai-aiagent-tool-definitions.md#tool-definitions) documentation. ## Response Structure This connector returns a list of tool definitions in the following format. Individual tool definitions are modeled after the [list tools response](https://modelcontextprotocol.io/specification/2025-03-26/server/tools#listing-tools) defined in the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) specification, so you should be able to directly use the definitions with different LLMs or transform them into the required format for your AI integration. ```json { "toolDefinitions": [ { "name": "GetDateAndTime", "description": "Returns the current date and time including the timezone.", "inputSchema": { "type": "object", "properties": {}, "required": [] } }, { "name": "Download_A_File", "description": "Download a file from the provided URL.", "inputSchema": { "type": "object", "properties": { "url": { "type": "string", "description": "The URL to download the file from" } }, "required": ["url"] } }, { "name": "SuperfluxProduct", "description": "Calculates the superflux product (a very complicated calculation) given two input numbers.", "inputSchema": { "type": "object", "properties": { "a": { "type": "number", "description": "The first number to be superflux calculated." }, "b": { "type": "number", "description": "The second number to be superflux calculated." } }, "required": ["a", "b"] } } ] } ``` You can either configure a result variable to contain the whole response or use a result expression to map parts of the response into your process. --- ## AI Agent connector customization In a self-managed or [hybrid](../../../reference/glossary.md#hybrid-mode) environment, you can customize and extend the [AI Agent connector](./agentic-ai-aiagent.md) to suit your specific needs. For example, you can: - Implement custom storage backends for conversation history - Add support for additional AI models - Inject additional logic into the agent execution flow ## HTTP proxy configuration In Self-Managed environments, the AI Agent connector supports routing HTTP requests to LLM providers through an HTTP proxy. This applies to the AI Agent, [MCP Client](./agentic-ai-mcp-client.md), and [A2A Client](/components/early-access/alpha/a2a-client/a2a-client.md) connectors. These connectors support [plain proxy variables](/self-managed/components/connectors/http-proxy-configuration.md#plain-proxy-variables) in addition to the standard connector proxy variables. Refer to the [HTTP proxy configuration](/self-managed/components/connectors/http-proxy-configuration.md) page for the full list of environment variables and configuration options. The following LLM providers do not support connector proxy variables, but respect standard [JVM proxy properties](/self-managed/components/connectors/http-proxy-configuration.md#jvm-properties): - Google Vertex AI. To disable proxy support entirely (for example, if only an HTTPS-based proxy is available): - **Spring Boot property:** `camunda.connector.agenticai.http.proxy-support.enabled=false`. - **Environment variable:** `CAMUNDA_CONNECTOR_AGENTICAI_HTTP_PROXYSUPPORT_ENABLED=false`. ## Extending the AI Agent connector ### Prerequisites This guide assumes you are starting from a fresh Spring Boot project and intend to run a customized AI Agent connector in a self-managed or hybrid environment. 1. Create a new Spring Boot project. 2. Add the [Camunda Connector Spring Boot Starter](../custom-built-connectors/connector-sdk.md#spring-boot-starter-runtime) and the Agentic AI dependencies to your `pom.xml`: ```xml 8.10.0 io.camunda.connector spring-boot-starter-camunda-connectors ${version.connectors} io.camunda.connector connector-agentic-ai ${version.connectors} ``` 3. Configure the SDK to connect to your cluster according to [the Camunda SDK documentation](../../../apis-tools/camunda-spring-boot-starter/getting-started.md#configuring-the-camunda-8-connection). 4. To only run the AI Agent Client connector, disable the other agentic AI connectors provided by the `connector-agentic-ai` dependency in your `application.yml`: ```yaml camunda: connector: agenticai: ad-hoc-tools-schema-resolver: enabled: false mcp: remote-client: enabled: false a2a: client: outbound: enabled: false polling: enabled: false webhook: enabled: false ``` 5. If the default AI Agent connector is already connected to your engine (for example, if you are connecting to SaaS), you can override the registered AI Agent connector job worker type by setting one of the following type environment variables to a custom value (such as `my-ai-agent`) when starting your application. This allows you to use your custom connector in combination with an [element template configured](../use-connectors-in-hybrid-mode.md) for the `my-ai-agent` job worker type. | Variable | Description | | :----------------------------------- | :----------------------------------------------------------------------------------------------------- | | `CONNECTOR_AI_AGENT_JOB_WORKER_TYPE` | Overrides the type of the [AI Agent Sub-process](./agentic-ai-aiagent-subprocess.md) job worker. | | `CONNECTOR_AI_AGENT_TYPE` | Overrides the type of the [AI Agent Task](./agentic-ai-aiagent-task.md) outbound connector job worker. | ### Customize individual components :::tip Instead of the example below, you can also use other Spring mechanisms to customize the AI Agent connector, such as using Aspect Oriented Programming (AOP) to intercept and modify method calls. ::: Each component of the AI Agent connector is registered as a Spring bean and annotated with the `@ConditionalOnMissingBean` annotation. This means you can override any component by defining your own bean of the same type in your custom project. For example, to customize the agent initialization logic, you can create a new bean that implements the `AgentInitializer` interface and register it in your Spring context. In the example below, this is done using the `@Component` annotation, but other Spring Boot mechanisms—like `@Bean` producer methods—work as well. The following example wraps the default initialization implementation with additional logging, but you can insert any custom logic as needed: ```java @Component public class MyCustomAgentInitializer implements AgentInitializer { private static final Logger LOGGER = LoggerFactory.getLogger(MyCustomAgentInitializer.class); private final AgentInitializer delegate; public MyCustomAgentInitializer( AgentToolsResolver agentToolsResolver, GatewayToolHandlerRegistry gatewayToolHandlers) { this.delegate = new AgentInitializerImpl(agentToolsResolver, gatewayToolHandlers); } @Override public AgentInitializationResult initializeAgent(AgentExecutionContext executionContext) { LOGGER.info(">>> Initializing agent"); final var result = delegate.initializeAgent(executionContext); LOGGER.info("<<< Agent initialized. Result: {}", result); return result; } } ``` ### Custom conversation storage The AI Agent connector includes a set of default storage backends for conversation history, but you can also implement your own to meet specific needs. Similar to the agent initialization example above, you can register a bean that implements the `ConversationStore` interface to provide your own storage implementation. A custom store needs three pieces: - A `ConversationStore` bean: The entry point. Its `type()` value is referenced from the element template. - A `ConversationSession` returned by `createSession(...)`: Performs the actual load and store for a single agent turn. The caller manages its lifecycle via `try-with-resources`, so override `close()` if your session holds external resources (connections, clients). - A `ConversationContext` implementation: The storage cursor persisted as part of the `agentContext` process variable. It must be annotated with `@JsonTypeName` and registered with the runtime `ObjectMapper`. The following example shows how to implement a custom store using a Spring Data JPA repository. The value returned by the `type()` method is used to identify the store type in the AI Agent connector configuration. ```java @Component public class MyConversationStore implements ConversationStore { public static final String TYPE = "my-conversation"; private final MyConversationRepository repository; public MyConversationStore(MyConversationRepository repository) { this.repository = repository; } @Override public String type() { return TYPE; } @Override public ConversationSession createSession( AgentExecutionContext executionContext, AgentContext agentContext) { return new MyConversationSession(repository, executionContext); } @Override public void onJobCompleted( AgentExecutionContext executionContext, AgentContext committedContext) { // Best-effort hook fired after Zeebe accepted the job completion. Optional: use // this to update a projection, archive the previous record, emit an event, etc. } @Override public void onJobCompletionFailed( AgentExecutionContext executionContext, AgentContext failedContext, JobCompletionFailure failure) { // Best-effort hook fired after Zeebe rejected the job completion (or the // connector itself raised an error). The record written by storeMessages during // this job is now an orphan — optional: delete it here so orphans do not // accumulate. } } ``` The session reads and writes the conversation messages for a single agent turn. `loadMessages` returns the message history that the incoming `ConversationContext` references; `storeMessages` persists the updated message list and returns a new `ConversationContext` pointing to the newly written record. The caller assembles the full `AgentContext` from the returned context. ```java public class MyConversationSession implements ConversationSession { @Override public ConversationLoadResult loadMessages(AgentContext agentContext) { // Load the messages referenced by the ConversationContext in agentContext. } @Override public ConversationContext storeMessages( AgentContext agentContext, ConversationStoreRequest request) { // Persist request.messages() to a new record and return a ConversationContext // pointing at it. Never mutate the record the previous context points to — // see the storage contract note below. } } ``` The `ConversationContext` is the storage cursor. It is serialized as part of the `agentContext` process variable, so it must be annotated with `@JsonTypeName` and contain everything needed to locate the stored data on the next turn: ```java @JsonTypeName("my-conversation") public record MyConversationContext(String conversationId, UUID recordId) implements ConversationContext {} ``` Register the subtype with the runtime `ObjectMapper` so the connector can deserialize the context back from the process variable. For example via a `Jackson2ObjectMapperBuilderCustomizer` bean calling `registerSubtypes(MyConversationContext.class)`. :::note Storage contract `storeMessages` must always write to a **new** record (or document, or branch) and return a `ConversationContext` pointing to it. Never mutate or overwrite the data the previous context points at. If job completion fails, Zeebe retries with the old `AgentContext` (and therefore the old cursor); the old pointer must still resolve to the old data. The newly written record becomes an orphan, which the `onJobCompletionFailed` hook can clean up. See the [storage contract reference](https://github.com/camunda/connectors/blob/main/connectors/agentic-ai/docs/reference/ai-agent.md#storage-contract) for the full rules every implementation must follow. ::: After implementing the custom store, you can reference the store type in your AI Agent connector configuration (see [memory configuration](./agentic-ai-aiagent.md#memory)): 1. In the **Memory** group of the AI Agent connector properties, set the **Memory storage type** to **Custom implementation**. 2. In the **Implementation type** field, enter the type value of your custom store implementation (`my-conversation` in the example above). 3. Run your process model. It should now use your custom conversation store for storing the conversation history. :::info An incident is raised if the AI Agent connector is not able to find a conversation store implementation for the specified type. ::: --- ## AI Agent document support The AI Agent connector can pass [Camunda documents](/self-managed/concepts/document-handling/overview.md) to the LLM from two sources: - The [user prompt](./agentic-ai-aiagent-subprocess.md#user-prompt) **Documents** field. - [Tool call results](./agentic-ai-aiagent-tool-definitions.md#tool-call-responses) and event payloads from event sub-processes. In both cases, supported documents are resolved and passed to the LLM as native content blocks so the model can interpret them directly. ## Supported document types Because file type support varies by LLM provider and model, you must test your document use case with the provider you are using. | File type | Supported | Description | | :---------------- | :-------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Text | Yes | Text files (MIME types matching `text/*`, `application/xml`, `application/json`, or `application/yaml`) are passed as plain text content blocks. | | PDF | Yes | PDF files (MIME types matching `application/pdf`) are passed as base64 encoded content blocks. | | Image | Yes | Image files (MIME types matching `image/jpeg`, `image/png`, `image/gif`, or `image/webp`) are passed as base64 encoded content blocks. | | Audio/video/other | No | Audio and video files are not currently supported, and will result in an error if passed. All other unsupported file types not listed here will also result in an error if passed. | :::info To learn more about storing, tracking, and managing documents in Camunda 8, see [document handling](/components/document-handling/getting-started.md). ::: ## Documents in the user prompt Use the [user prompt](./agentic-ai-aiagent-subprocess.md#user-prompt) **Documents** field to add a list of document references the agent can interact with. The list is internally resolved and passed to the LLM if the document type is supported. LLM APIs allow the user prompt to be specified as a list of content blocks. Each supported document reference is resolved to a corresponding content block and passed as part of the user message. For examples of how LLM providers accept document content blocks, refer to the [Anthropic](https://docs.anthropic.com/en/docs/build-with-claude/vision#base64-encoded-image-example) and [OpenAI](https://platform.openai.com/docs/guides/images-vision#giving-a-model-images-as-input) documentation. ## Documents in tool call results [Tool call responses](./agentic-ai-aiagent-tool-definitions.md#tool-call-responses) can contain document references nested anywhere within the result structure. The agent extracts these documents from the tool call result and passes them to the LLM as native content blocks (plain text for text files, base64 encoded content for PDFs and images). This is the same mechanism used for user prompt documents. In the conversation, the tool call result itself retains a lightweight document _reference_ (for example, the document ID and store, or an external URL). The resolved document content is delivered in a separate follow-up user message immediately after the tool result, allowing the model to correlate each reference with its content. For example, a tool can return a document for the LLM to analyze: - A [REST connector](/components/connectors/protocol/rest.md) tool with the **Store response** option enabled downloads a PDF document. - A user task tool with a [Filepicker](/components/modeler/forms/form-element-library/forms-element-library-filepicker.md) form lets a person upload a document as part of a human-in-the-loop workflow. --- ## Example AI Agent Sub-process connector integration This worked example demonstrates how to use the [AI Agent Sub-process connector](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-subprocess.md) applied to an [ad-hoc sub-process](/components/modeler/bpmn/ad-hoc-subprocesses/ad-hoc-subprocesses.md) to model AI Agent [tools and response interaction feedback loops](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md#feedback-loop-use-cases). ## Create an AI Agent element As the **AI Agent Sub-process** implementation implicitly creates a tools feedback loop, you only need to add an ad-hoc sub-process with an applied AI Agent connector template to the process. :::info For more information on how to model the tools available to the AI agent, see [tool definitions](./agentic-ai-aiagent-tool-definitions.md). ::: After adding the element, open the properties panel to configure the connection to your model provider, and modify the system and user prompts as required. ## Example response interaction feedback loop {#response-loop} Similar to the tools feedback loop, another feedback loop acting on the agent response can be added by re-entering the AI Agent connector with new information. You must model your user prompt so that it adds the follow-up data instead of the initial request. For example, your **User Prompt** field could contain the following FEEL expression to make sure it acts upon follow-up input: ```feel =if (is defined(followUpInput)) then followUpInput else initialUserInput ``` With the **AI Agent Sub-process** implementation, the user feedback needs to be modeled to loop back to the AI Agent ad-hoc sub-process: ![AI Agent Sub-process with user feedback loop](../img/ai-agent-subprocess-user-feedback-loop.png) :::note How you model this type of feedback loop greatly depends on your specific use case. - The example feedback loop expects a simple feedback action based on a user task, but this could also interact with other process flows or another agent process. - Instead of the user task, you could also use another LLM connector to verify the response of the AI Agent. For an example of this pattern, see the [fraud detection example](https://github.com/camunda/connectors/tree/main/connectors/agentic-ai/examples/ai-agent/ad-hoc-sub-process/fraud-detection). ::: ## Additional resources - The connectors repository contains a set of [ready-made examples](https://github.com/camunda/connectors/tree/main/connectors/agentic-ai/examples/ai-agent/ad-hoc-sub-process) using the AI Agent Sub-process connector. --- ## AI Agent Sub-process connector Implement an AI agent using an ad-hoc sub-process with an applied AI Agent connector template. ## About this implementation This connector implementation creates an implicit feedback loop for tool calls and agent responses, using the [job worker implementation type](../../../components/modeler/bpmn/ad-hoc-subprocesses/ad-hoc-subprocesses.md#job-worker-implementation) of an [ad-hoc sub-process](../../../components/modeler/bpmn/ad-hoc-subprocesses/ad-hoc-subprocesses.md). :::info - For more information and usage examples, see [AI Agent Sub-process](./agentic-ai-aiagent.md#ai-agent-sub-process). - The [example integration](agentic-ai-aiagent-subprocess-example.md) outlines how to model an agentic AI process using the AI Agent Sub-process implementation. ::: ## Configuration --- ## Example AI Agent Task connector integration This worked example demonstrates how to use the [AI Agent Task connector](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-task.md) and an [ad-hoc sub-process](/components/modeler/bpmn/ad-hoc-subprocesses/ad-hoc-subprocesses.md) to model AI Agent [tools and response interaction feedback loops](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md#feedback-loop-use-cases). ## Create an AI Agent element First, an AI Agent connector is added and configured in the process diagram. Next, an ad-hoc sub-process is added in a feedback loop to connect the agent to the tools it needs. :::info For more information on how to model the tools available to the AI agent, see [tool definitions](./agentic-ai-aiagent-tool-definitions.md). ::: After adding the element, open the properties panel to configure the connection to your model provider and adapt the system and user prompts as needed. It is important to align the **Agent context** field and the result variable. The following defaults should be set to ensure re-entering the process will pick up the previous context value: - **Agent context**: `agent.context` - **Result variable**: `agent` ## Example tools feedback loop {#tools-loop} ### Configure ad-hoc sub-process and loop 1. The ad-hoc sub-process is marked as a [parallel multi-instance](../../modeler/bpmn/multi-instance/multi-instance.md). This allows the process to execute the tools in parallel, and wait for all tool calls to complete before continuing with the process. 1. A descriptive ID is configured for the ad-hoc sub-process. This can then be configured in the **Ad-hoc sub-process ID** field in the AI Agent connector [tools](agentic-ai-aiagent.md#tools) section. 1. A loop is modeled into the sub-process and back to the AI Agent connector. - The `no` flow of the `Contains tool calls?` gateway is marked as the default flow. - The `yes` flow condition is configured to activate when the AI Agent response contains a list of tool calls. For example, if the suggested default values for the [result variable/expression](#result-variableexpression) are used, this condition could be configured as follows: ```feel not(agent.toolCalls = null) and count(agent.toolCalls) > 0 ``` The process execution routes through the ad-hoc sub-process if the LLM response requests one or more tools to be called. ### Configure multi-instance execution The ad-hoc sub-process in this example is configured as a [parallel multi-instance](../../modeler/bpmn/multi-instance/multi-instance.md) sub-process (instead of sequential multi-instance). This allows: - Tools to be called **independently of each other**, each with its own set of input parameters. This also implies that the same tool can be called **multiple times with different parameters** within the same ad-hoc sub-process execution. For example, a _Lookup user_ tool could be called multiple times with different user IDs. - The process to **wait until all requested tools have been executed** before passing the results back to the AI Agent/LLM. After all tools have been executed, results are passed back to the AI Agent connector. #### Configure properties The following properties for the ad-hoc sub-process must be configured. You can use the following suggested values as a starting point and change as required or if dealing with multiple agents within the same process. - **Input collection**: Set this to the list of tool calls your AI Agent connector returns, for example `agent.toolCalls`. - **Input element**: Contains the individual tool call, including LLM-generated input parameters based on the [tool definition](#tool-definitions). Must aways be set to `toolCall`. - **Output collection**: Collects the results of all the requested tool calls. Suggested value: `toolCallResults`. Make sure you pass this value as [Tool Call Results](agentic-ai-aiagent.md#tools) in the AI Agent configuration. - **Output element**: Collects the individual tool call result as returned by an individual tool (see [Tool Call Responses](#tool-call-responses)). When changing this `toolCallResult` to a different value, make sure you also change your tools to write to the updated variable name. ```feel { id: toolCall._meta.id, name: toolCall._meta.name, content: toolCallResult } ``` As a final step, the element must be configured to activate the ad-hoc sub-process. - When using a multi-instance configuration, this is always the single task ID of the tool being executed in the individual instance. - Configure **Active elements collection** to contain the exact `[toolCall._meta.name]`. For example, the completed ad-hoc sub-process configuration would look as follows: ![AI Agent ad-hoc sub-process multi-instance configuration](../img/ai-agent-ad-hoc-sub-process-multi-instance.png) #### Configure an input mapping for the tool call result variable To prevent interference between tool calls, create an [input mapping](../../concepts/variables.md#input-mappings) for the `toolCallResult` variable. This ensures the variable is created as a local variable within the ad-hoc sub-process. 1. In the **Inputs** section of the ad-hoc sub-process properties panel, add a new entry. 2. In the **Local variable name** field, enter `toolCallResult` (or use your custom variable name if you changed it earlier). 3. Leave the **Variable assignment value** field blank. ## Example response interaction feedback loop {#response-loop} Similar to the tools feedback loop, another feedback loop acting on the agent response can be added by re-entering the AI Agent connector with new information. You must model your user prompt so that it adds the follow-up data instead of the initial request. For example, your **User Prompt** field could contain the following FEEL expression to make sure it acts upon follow-up input: ```feel =if (is defined(followUpInput)) then followUpInput else initialUserInput ``` With the **AI Agent Task** implementation, the user feedback needs to be modeled to loop back to the AI Agent task: ![AI Agent Task with user feedback loop](../img/ai-agent-task-user-feedback-loop.png) :::note How you model this type of feedback loop greatly depends on your specific use case. - The example feedback loop expects a simple feedback action based on a user task, but this could also interact with other process flows or another agent process. - Instead of the user task, you could also use another LLM connector to verify the response of the AI Agent. For an example of this pattern, see the [fraud detection example](https://github.com/camunda/connectors/tree/main/connectors/agentic-ai/examples/ai-agent/service-task/fraud-detection). ::: ## Additional resources - The connectors repository contains a set of [ready-made examples](https://github.com/camunda/connectors/tree/main/connectors/agentic-ai/examples/ai-agent/service-task) using the AI Agent Task connector. --- ## AI Agent Task connector Implement an AI agent using an AI Agent connector applied to a service task, paired with an optional ad-hoc sub-process to provide tools usable by the AI. :::info - For more information and usage examples, see [AI Agent Task](./agentic-ai-aiagent.md#ai-agent-task). - The [example integration](agentic-ai-aiagent-task-example.md) page outlines how to model an agentic AI process using the AI Agent Task implementation. ::: ## Configuration ## Limitations ### No event handling support Unlike the AI Agent Sub-process implementation, the AI Agent Task implementation does not support event handling as part of an [event subprocess](../../../components/modeler/bpmn/event-subprocesses/event-subprocesses.md). If you want to handle events while the AI agent is working on a task, use the [AI Agent Sub-process](./agentic-ai-aiagent-subprocess.md) implementation instead. ### Process definition not found errors when running the AI Agent for the first time The AI Agent Task implementation relies on the eventually consistent [Get process definition XML API](../../../apis-tools/orchestration-cluster-api-rest/specifications/get-process-definition-xml.api.mdx) to fetch the BPMN XML source when resolving available tool definitions. - If you deploy a new or changed process and directly run it after (for example using **Deploy & Run**), the process definition might not be available when the AI Agent attempts to fetch the process definition XML. - It will retry to fetch the definition several times, but if the definition is still not available after the retries are exhausted, the connector will fail with a "Process definition not found" error and raise an incident. To avoid this error, wait a few seconds before running a newly deployed new or changed process, to allow the exporter to make the process definition available via the API. --- ## AI Agent Tool Definitions When resolving the available tools within an ad-hoc sub-process, the AI Agent will take all activities into account which **have no incoming flows** (root nodes within the ad-hoc sub-process) and **are not boundary events**. For example, in the following image the activities marked in green are the ones that will be considered as tools: ![AI Agent tool resolution](../img/ai-agent-tool-resolution.png) You can use any BPMN elements and connectors as tools and to model sub-flows within the ad-hoc sub-process. ## Tool resolution To resolve available tools, the AI Agent connector either resolves the tools by reling on data provided by the Zeebe engine or reads the BPMN model directly. The approach depends on the chosen AI Agent implementation: When using the **AI Agent Sub-process** implementation, the connector relies on data provided by the [ad-hoc sub-process](/components/modeler/bpmn/ad-hoc-subprocesses/ad-hoc-subprocesses.md#special-ad-hoc-sub-process-variables) implementation to resolve the tools. When using the **AI Agent Task** implementation, the connector reads the BPMN model directly to resolve the tools: 1. It reads the BPMN model and looks up the ad-hoc sub-process using the configured ID. If not found, the connector throws an error. 2. Iterates over all activities within the ad-hoc sub-process and checks that they are root nodes (no incoming flows) and not boundary events. 3. For each activity found, analyzes the input mappings and looks for the [`fromAi`](../../modeler/feel/builtin-functions/feel-built-in-functions-miscellaneous.md#fromaivalue) function calls that define the parameters that need to be provided by the LLM. 4. Creates a tool definition for each activity found, and passes these tool definitions to the LLM as part of the prompt. :::note Refer to the [Anthropic](https://docs.anthropic.com/en/docs/build-with-claude/tool-use/overview) and [OpenAI](https://platform.openai.com/docs/guides/function-calling) documentation for examples of how tool/function calling works in combination with an LLM. ::: ## Tool definitions :::important The AI Agent connector only considers the **root node** of the sub-flow when resolving a tool definition. ::: A tool definition consists of the following properties which will be passed to the LLM. The tool definition is closely modeled after the [list tools response](https://modelcontextprotocol.io/specification/2025-03-26/server/tools#listing-tools) as defined in the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/). | Property | Description | | :---------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | name | The name of the tool. This is the **ID of the activity** in the ad-hoc sub-process. | | description | The description of the tool, used to inform the LLM of the tool purpose. If the **documentation** of the activity is set, this is used as the description, otherwise the **name** of the activity is used. Make sure you provide a meaningful description to help the LLM understand the purpose of the tool. | | inputSchema | The input schema of the tool, describing the input parameters of the tool. The connector will analyze all input mappings of the activity and create a [JSON Schema](https://json-schema.org/) based on the [`fromAi`](../../modeler/feel/builtin-functions/feel-built-in-functions-miscellaneous.md#fromaivalue) function calls defined in these mappings. If no `fromAi` function calls are found, an empty JSON Schema object is returned. | :::note Provide as much context and guidance in tool definitions and input parameter definitions as you can to ensure the LLM selects the right tool and generates proper input values. Refer to the [Anthropic documentation](https://docs.anthropic.com/en/docs/build-with-claude/tool-use/implement-tool-use#example-of-a-good-tool-description) for tool definition best practices. ::: ### AI-generated parameters via `fromAi` Within an activity, you can define parameters which should be AI-generated by tagging them with the [`fromAi`](../../modeler/feel/builtin-functions/feel-built-in-functions-miscellaneous.md#fromaivalue) FEEL function in input mappings. The function itself does not implement any logic (it simply returns the first argument it receives), but provides a way to configure all the necessary metadata (for example, description, type) to generate an input schema definition. The tools schema resolution will collect all `fromAi` definitions within an activity and combine them into an input schema for the activity. :::important The first argument passed to the `fromAi` function must be a reference to a field within the `toolCall` context which will automatically be populated by the AI Agent connector. Example: `toolCall.myParameter`. ::: By using the `fromAi` tool call as a wrapper function around the actual value, the connector can both **describe the parameter** for the LLM by generating a JSON Schema from the function calls and at the same time **utilize the LLM-generated value** as it can do with any other process variable. You can use the `fromAi` function in: - Input mappings (for example, service task, script task, user task). - Custom input fields provided by an element template if an element template is applied to the activity as technically these are handled as input mappings. For example, the following image shows an example of `fromAi` function usage on a [REST outbound connector](../protocol/rest.md): ![AI Agent fromAi tool resolution](../img/ai-agent-tool-resolution-fromAi.png) #### `fromAi` examples The [`fromAi`](../../modeler/feel/builtin-functions/feel-built-in-functions-miscellaneous.md#fromaivalue) FEEL function can be called with a varying number of parameters to define simple or complex inputs. The simplest form is to just pass a value. ```feel fromAi(toolCall.url) ``` This makes the LLM aware that it needs to provide a value for the `url` parameter. As the first value to `fromAi` needs to be a variable reference, the last segment of the reference is used as parameter name (`url` in this case). To make an LLM understand the purpose of the input, you can add a description: ```feel fromAi(toolCall.url, "Fetches the contents of a given URL. Only accepts valid RFC 3986/RFC 7230 HTTP(s) URLs.") ``` To define the type of the input, you can add a type (if no type is given, it will default to `string`): ```feel fromAi(toolCall.firstNumber, "The first number.", "number") fromAi(toolCall.shouldCalculate, "Defines if the calculation should be executed.", "boolean") ``` For more complex type definitions, the fourth parameter of the function allows you to specify a JSON Schema from a FEEL context. Note that support for complex JSON Schema features may be limited by the selected provider/model. For a list of examples, refer to the [JSON Schema documentation](https://json-schema.org/learn/miscellaneous-examples). ```feel fromAi( toolCall.myComplexObject, "A complex object", "string", { enum: ["first", "second"] } ) ``` To mark a parameter as optional, you can use the `options` parameter: ```feel fromAi( toolCall.optionalParameter, "An optional parameter", "string", null, { required: false } ) ``` Or using named parameters: ```feel fromAi( value: toolCall.optionalParameter, description: "An optional parameter", options: { required: false } ) ``` You can combine multiple parameters within the same FEEL expression, for example: ```feel fromAi(toolCall.firstNumber, "The first number.", "number") + fromAi(toolCall.secondNumber, "The second number.", "number") ``` For more examples, refer to the [`fromAi`](../../modeler/feel/builtin-functions/feel-built-in-functions-miscellaneous.md#fromaivalue) documentation. ## Tool call responses To collect the output of the called tool and pass it back to the agent, the task within the ad-hoc sub-process needs to set its output to a predefined variable name. For the **AI Agent Sub-process** implementation, this variable is predefined as `toolCallResult`. For the **AI Agent Task** implementation, the variable depends on the configuration of the [multi-instance execution](#tools-loop), but is also typically named `toolCallResult`. Depending on the used task, setting the variable content can be achieved in multiple ways: - A [result variable](../use-connectors/index.md#result-variable) or a [result expression](../use-connectors/index.md#result-expression) containing a `toolCallResult` key - An [output mapping](../../concepts/variables.md#output-mappings) creating the `toolCallResult` variable or adding to a part of the `toolCallResult` variable (for example, an output mapping could be set to `toolCallResult.statusCode`) - A [script task](../../modeler/bpmn/script-tasks/script-tasks.md) that sets the `toolCallResult` variable Tool call results can be either primitive values (for example, a string) or complex ones, such as a [FEEL context](../../modeler/feel/language-guide/feel-context-expressions.md) that is serialized to a JSON string before passing it to the LLM. As most LLMs expect _some_ form of response to a tool call, the AI Agent will return a constant string indicating that the tool was executed successfully without returning a result to the LLM if the `toolCallResult` variable is not set or empty after executing the tool. ### Document support Tool call responses can contain [Camunda document references](/self-managed/concepts/document-handling/overview.md) nested anywhere within the result structure. The agent extracts these documents and passes them to the LLM as native content blocks. For supported file types and details on how documents are resolved, see [document support](./agentic-ai-aiagent-documents.md). ## Gateway tool definitions Gateway tools are activities that expose multiple tools from an external source, such as an MCP server or an A2A agent. Unlike static tool definitions, gateway tools discover their available tools dynamically during agent initialization by calling the external source. To configure an activity as a gateway tool, set the [extension property](../../modeler/element-templates/defining-templates.md#zeebeproperty) `io.camunda.agenticai.gateway.type` on the activity. The property value specifies which gateway implementation to use (for example, `mcpClient`). The agent must also have access to a handler for the specified gateway type. Custom implementations can be made available to the agent in self-managed or hybrid setups. For more details, see the available gateway tool implementations: - [MCP Client connectors](./agentic-ai-mcp-client.md) --- ## AI Agent connector Use the **AI Agent** connector to integrate Large Language Models (LLMs) with AI agents to build solutions using [agentic orchestration](../../agentic-orchestration/agentic-orchestration-overview.md). ## About this connector The AI Agent connector enables AI agents to integrate with an LLM to provide interaction/reasoning capabilities. This connector is designed for use with an ad-hoc sub-process in a feedback loop, providing automated user interaction and tool selection. For example, use this connector to enable an AI agent to autonomously select and execute tasks within ad-hoc sub-processes by evaluating the current process context and determining the relevant tasks and tools to use in response. You can also use the AI Agent connector independently, although it is designed to be used with an ad-hoc sub-process to define the tools an AI agent can use. Core features include: | Feature | Description | | :------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | LLM provider support | Supports a range of LLM providers, such as Anthropic, Amazon Bedrock, Google Gemini, and OpenAI. | | Memory | Provides conversational/short-term memory handling to enable feedback loops. For example, this allows a user to ask follow-up questions to an AI agent response. | | Tool calling | Support for an AI agent to interact with tasks within an ad-hoc sub-process, allowing use of all Camunda features such as connectors and user tasks (human-in-the-loop). Automatic **tool resolution** allows an AI agent to identify the tools available in an ad-hoc sub-process. | :::tip New to agentic orchestration? - The [Build your first AI Agent](../../../guides/getting-started-agentic-orchestration.md) guide provides a quick introduction to agentic orchestration and how to use the AI Agent Sub-process connector using a blueprint. - See the [example AI Agent connector integration](agentic-ai-aiagent-subprocess-example.md) for a worked example of a simple Agent AI feedback loop model. - See [additional resources](#additional-resources) for examples of how you can use the AI Agent connector. ::: ## Prerequisites The following prerequisites are required to use this connector: | Prerequisite | Description | | :------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Set up your LLM model provider and authentication | Prior to using this connector, you must have previously set up an account with access and authentication details for the supported LLM model provider you want to use.For example:To use an LLM model provided by Amazon Bedrock, you must have an AWS account with an access key and secret key to execute `Converse` actions.For OpenAI, you must configure the [OpenAI model](https://platform.openai.com/docs/models) and obtain an OpenAI API key to use for authentication. | ## Choose an implementation The AI Agent connector is available in two variants, each with different capabilities, suited for different use cases, and available with a dedicated element template: - [AI Agent Sub-process](#ai-agent-sub-process). - [AI Agent Task](#ai-agent-task). The right implementation depends on your use case. | Use case | Implementation | Description | | ------------------------------------------------------------------------------------------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Agentic workflow with automatic tool calling (most use cases) | AI Agent Sub-process | It handles tool resolution and the feedback loop automatically. No explicit loop modeling is needed. You must include at least one activity/tool inside the sub-process. | | Inference tasks that don't require tool calling | AI Agent Task without tools | A single, one-shot LLM call with no ad-hoc sub-process. | | Intercepting tool calls, for example, to add a human approval step or run PII detection before tool execution | AI Agent Task with tools | Tools are provided by an external ad-hoc sub-process that you model explicitly, giving you full control over the feedback loop. This is the most complex configuration. | :::info The **recommended approach** for most use cases is to use the **AI Agent Sub-process** implementation due to the simplified configuration and support for event sub-processes. ::: ### Configuration comparison The following table summarizes the key configuration differences between the two implementations. | Configuration field | AI Agent Sub-process | AI Agent Task | | :--------------------- | :------------------------------------------------------------------------------ | :---------------------------------------------------------------- | | Model provider | Yes | Yes | | Model | Yes | Yes | | System prompt | Yes | Yes | | User prompt | Yes | Yes | | Tools | Automatic (resolved from activities inside the sub-process) | Optional. Requires ad-hoc sub-process ID and tool call results | | Agent context (memory) | Optional. Only needed when re-entering the agent from an external feedback loop | Required. Must be aligned with the output mapping result variable | | Limits | Yes | Yes | | Event handling | Yes | No | | Response | Yes | Yes | | `toolCalls` in output | No | Yes. Returned for routing to the ad-hoc sub-process | | Error handling | Yes | Yes | | Retries | Yes | Yes | | Execution listeners | Yes | Yes | :::note Execution listeners behave differently between the two implementations. On the AI Agent Sub-process, they only run when entering and exiting the ad-hoc sub-process, not on every loop iteration. On the AI Agent Task, they are triggered on every job execution. ::: ### AI Agent Sub-process The [AI Agent Sub-process](./agentic-ai-aiagent-subprocess.md) implementation uses the [job worker implementation type](../../../components/modeler/bpmn/ad-hoc-subprocesses/ad-hoc-subprocesses.md#job-worker-implementation) of an [ad-hoc sub-process](../../../components/modeler/bpmn/ad-hoc-subprocesses/ad-hoc-subprocesses.md) to provide an integrated solution to handle tool resolution and a feedback loop. This is the recommended implementation type for most use cases, and offers: - Simplified configuration as the tool feedback loop is handled internally - Support for handling of event sub-processes within the ad-hoc sub-process #### Restrictions - Because of BPMN semantics, the ad-hoc sub-process must contain at least one activity. This means you cannot create an AI Agent Sub-process without any tools. - As the tool calling feedback loop is implicitly handled within the AI Agent execution, you have less control over the tool calls. #### Example A basic AI Agent Sub-process might look similar to the following example. - The connector is configured so the AI Agent resolves available tools and activates them as needed to complete it's goal. - Handling of event sub-processes within the ad-hoc sub-process is supported (See [Event Handling](./agentic-ai-aiagent-subprocess.md#event-handling)). The AI Agent Task implementation does not support this. This pattern can also be combined with a user feedback loop for verification or follow-up interactions. For example, instead of the showcased user task, this could also be another LLM acting as a judge, or any other task that validates the agent's response. ![AI Agent Sub-process with user feedback loop](../img/ai-agent-subprocess-user-feedback-loop.png) ### AI Agent Task The [AI Agent Task](./agentic-ai-aiagent-task.md) implementation is the original variant that relies on a BPMN [service task](../../../components/modeler/bpmn/service-tasks/service-tasks.md) in combination with a multi-instance ad-hoc sub-process. Unlike the AI Agent Sub-process implementation, you must model the feedback loop explicitly in the BPMN diagram, leading to a more complex configuration. This implementation is best suited for: - Simple, one-shot tasks using the AI Agent connector as a generic LLM connector without any tool calling. - Advanced use cases where you want to model the feedback loop explicitly, for example to pre-/post-process tool calls for approval or auditing. #### Example A very simple example of using the AI Agent Task connector for a non-agentic task is as follows: - The connector can be made agentic by adding a multi-instance ad-hoc sub-process and gateways to create a tool feedback loop. - The connector will be able to call tools until it reaches its goal or a configured limit. The multi-instance ad-hoc sub-process acts as a toolbox: The process can also be further enhanced to add a user feedback loop outside the tool calling loop. When the AI Agent completes its task and does not request any tool calls, its response can be verified with a task (such as a user task or another LLM as a judge), with the process set up to loop back to the AI Agent if required. This allows you to create a user-in-the-loop process for example, such as a chat where the user can ask follow-up questions: If you need more control over the feedback loop, you can model pre-/post-processing of tool calls with additional tasks, such as approval or tool call auditing. ## Concepts ### System prompt, user prompt, and tool descriptions Reliable agent behavior depends on three inputs working together: - **System prompt**: Defines the agent's role, boundaries, priorities, and success criteria. - **User prompt**: Carries the current request and immediate context. - **Tool/task descriptions**: Define which actions are available in the ad-hoc sub-process and how each should be used. At runtime, the connector passes this combined context to the LLM. The model then selects which tools to call (if any), along with parameters. #### How task descriptions are used for tools When using an ad-hoc sub-process, each activity can be exposed as a tool. For best results, document each tool with: - A clear task name that describes intent. - A behavior-oriented description that includes when to use it, when not to use it, and the expected outcome. This makes tool selection more predictable and reduces repeated or incorrect calls. ### Execution responsibility split The decision and execution loop is shared between the LLM and Camunda: - **LLM decides**: Which tool to call next, in what order, and with which parameters. - **Camunda orchestrates**: Executes the selected BPMN activity, stores variables, applies retries and incident handling, and routes human tasks and events. This means tools can be called in different orders, repeated, run in parallel, or skipped entirely, while execution remains constrained by the modeled process boundaries. ### Feedback loop This connector is typically used in a feedback loop, with the connector implementation repeatedly being executed based on tool call results or user feedback until it is able to reach its goal. For example, the following diagram shows a tool calling loop modeled with the [AI Agent Task](#ai-agent-task) implementation type. - The process loops back to the AI Agent connector task from the ad-hoc sub-process until the agent decides no further tool calls are needed. - With the [AI Agent Sub-process](#ai-agent-sub-process) implementation type, the tool calling loop is handled internally and so is not explicitly modeled in the BPMN diagram. ![AI Agent feedback loop](../img/ai-agent-loop-overview.png) 1. A request is made to the AI agent connector task, and the LLM determines what action to take. 1. If the AI agent decides that further action is needed, the process enters the ad-hoc sub-process and calls any tools deemed necessary to satisfactorily resolve the request. 1. The process loops back and re-enters the AI agent connector task, where the LLM decides (with contextual memory) if more action is needed before the process can continue. The process loops repeatedly in this manner until the AI agent decides it is complete, and passes the AI agent response to the next step in the process. #### Feedback loop use cases Typical feedback loop use cases for this connector include the following: | Use case | Description | | :------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Tool calling | In combination with an ad-hoc sub-process, the AI Agent connector will resolve available tools and their input parameters, and pass these tool definitions to the LLM.The LLM generates a response, that might include tool calls (a request to call a tool paired with the input parameters).If tool calls are requested, the tools can be executed by activating the respective activities within the ad-hoc sub-process.With the [AI Agent Sub-process](#ai-agent-sub-process) implementation, activating the activities will be implicitely handled by the agent implementation.With the [AI Agent Task](#ai-agent-task) implementation, it is necessary to model the process to pass these tool calls to the ad-hoc sub-process and to return the tool call results to the AI Agent task. | | Response interaction | After returning a response (and without calling any tools), model the process to act upon the response. For example, present the response to a user who can then ask follow-up questions back to the AI Agent connector. | As the agent preserves the context of the conversation, follow-up questions/tasks and handling of tool call results can relate to the previous interaction with the LLM, allowing the LLM to provide more relevant responses. ### Agent context An important concept to understand is the use of the **Agent context** process variable to store information required for allowing re-entry to the AI Agent connector task with the same context as before. Depending on which implementation type is used, the Agent context must be configured differently in the model: - [**AI Agent Sub-process**](#ai-agent-sub-process): The agent context is kept within the sub-process scope. This means you only need to configure the agent context when the agent should pick up an existing conversation, for example to model a user feedback loop as in the [quickstart example](../../../guides/getting-started-agentic-orchestration.md). In this case, you must align the configured agent context variable with the used result variable/expression so that the context update is correctly passed to the next execution of the AI Agent connector task. - [**AI Agent Task**](#ai-agent-task): You must align the agent context input variable and the response variable/expression so the context update is correctly passed to the next execution of the AI Agent connector task. ### Response format {#response} The AI Agent connector can be configured to instruct the LLM to respond in a structured JSON format, and to parse the JSON structure into a FEEL context accordingly. This allows the LLM to return structured data that can be easily processed in the BPMN process. ### Example conversation The following is a high-level example conversation with the AI Agent connector, including both user and tool feedback loops. The conversational awareness provided by the agent context allows use cases such as the user only responding with `Yes, please proceed`, with the agent understanding what it should do next. ``` # Initial input/user prompt User: Is John Doe eligible for a credit card? # Tool feedback loop AI Agent: Call the `Check_Credit_Card_Eligibility` tool with the following parameters: {"name": "John Doe"} Tool Call Result: {"Check_Credit_Card_Eligibility": {"eligible": true}} # User feedback loop AI Agent: John Doe is eligible for a credit card. Would you like to proceed? User: Yes, please proceed. AI Agent: Call the `Create_Credit_Card` tool with the following parameters: {"name": "John Doe"} Tool Call Result: {"Create_Credit_Card": {"success": true}} AI Agent: John Doe's credit card has been created successfully. ``` ## Process instance migration :::warning Process instance migration is a powerful feature that should be used with caution. [Use at your own risk](../../concepts/process-instance-migration.md#use-at-your-own-risk). ::: Because AI agent implementations are closely tied to the underlying process definition that determines which tools are available, carefully consider the impact of applying [process instance migrations](../../concepts/process-instance-migration.md) to instances using the AI Agent connector while the agent is mid‑conversation. As a result, some migration scenarios are not supported. ### Supported migration scenarios The following migration scenarios are supported for running AI Agent process instances: | Migration scenario | Description | | :-------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Adding a new tool | Adding a new activity to the ad-hoc sub-process. The new tool is picked up on the next AI Agent execution and added to the agent context. | | Changing an existing tool without affecting the tool definition | For example, updating a form linked to a user task, or changing a script task implementation. No agent changes are necessary because the tool definition remains unchanged. | | Changing an existing tool definition | Updating a tool's description or `fromAi()` parameters is supported, but proceed carefully. See [Considerations when changing tool definitions](#considerations-when-changing-tool-definitions) for details. | | Changing AI Agent configuration (AI Agent Task only) | Updating the system prompt or model parameters on an [AI Agent Task](#ai-agent-task). These changes are picked up on the next execution as input mappings are re-evaluated for each loop iteration. This is **not supported** for the [AI Agent Sub-process](#ai-agent-sub-process) implementation because the parameters are applied via input mappings to the ad-hoc sub-process which are evaluated only once when entering the sub-process. | ### Unsupported migration scenarios The following migration scenarios are **not supported** and will result in an error: | Migration scenario | Description | | :-------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Removing or renaming tools | Removing an existing tool or changing its element ID can lead to stuck executions or validation errors if the agent references tools that no longer exist. The agent throws a `MIGRATION_MISSING_TOOLS` error when it detects a removed tool. | | Adding or removing [gateway tool definitions](./agentic-ai-aiagent-tool-definitions.md#gateway-tool-definitions) (such as MCP or A2A clients) | Gateway tools require a tool discovery flow during initialization. Adding or removing gateway tool definitions to a running agent is not supported as it would require re-executing tool discovery. The agent throws a `MIGRATION_GATEWAY_TOOL_DEFINITIONS_CHANGED` error when it detects such changes. | ### Considerations when changing tool definitions :::info When you change an existing tool definition (such as updating a tool's description or adding/modifying `fromAi()` parameters), the AI Agent detects the change and updates its tool definitions on the next execution. ::: Because a migration can occur between an AI Agent execution and the actual tool call, tools may receive parameters based on the previous definition. Ensure your tool implementations handle this scenario gracefully. - **Description changes**: Updates to tool or parameter descriptions take effect on the next AI Agent execution. The LLM uses the updated descriptions when deciding which tools to call. - **Parameter changes**: Adding, removing, or modifying `fromAi()` parameters is supported. However, the tool implementation must handle potentially missing or changed parameters. For example: - A script task should implement a null-check to return an error message if a newly required parameter is missing from an in-flight tool call. - When changing an input parameter from a numeric type to a complex type (such as an object), the implementation should handle cases where the parameter is still provided using the numeric type. When a tool receives a parameter in a different format than expected, it can either handle the situation gracefully (for example, by using a default value or converting to a suitable format), or return an actionable error message that can instruct the LLM to provide the correct parameters. ## Additional resources - [Intelligent by design: A step-by-step guide to AI task agents in Camunda](https://camunda.com/blog/2025/05/building-your-first-ai-agent-with-camunda-s-new-agentic-ai/). - [AI Agent Chat Quick Start blueprint](https://marketplace.camunda.com/en-US/apps/587865/ai-agent-chat-quick-start) on the Camunda Marketplace. - Agentic AI examples GitHub repository [working examples](https://github.com/camunda/connectors/tree/main/connectors/agentic-ai/examples). - The [MCP Client connector](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-client.md) can be used in combination with the AI agent connector to connect to tools exposed by Model Context Protocol (MCP) servers. --- ## Configure MCP Client connectors Learn how to configure MCP Client connectors, including connector mode, tool access, and availability. ## Connector mode Choose how the connector operates based on your use case. ### AI Agent tool mode Use when the connector is invoked as a tool from within an AI Agent ad-hoc sub-process. This is the default mode. The **Method** and **Parameters** fields accept FEEL expressions and default to `toolCall.method` and `toolCall.params`, which are automatically populated by the AI Agent connector during tool discovery and tool calling. #### Operation Configure the operation to execute on the MCP server. You typically only need to change the default value if the ad-hoc sub-process multi-instance uses an input element other than `toolCall`. | Field | Required | Description | | :--------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | | Method | Yes | The [MCP method](https://modelcontextprotocol.io/specification/2025-06-18/server/tools#protocol-messages) to call. Defaults to `toolCall.method`. | | Parameters | Yes | The parameters to pass with the MCP client execution. Defaults to `toolCall.params`. | ### Standalone mode Use when invoking MCP operations directly from a BPMN process without an AI Agent. This mode allows you to call MCP servers independently of the agentic orchestration flow. Select the operation to perform: | Operation | Description | Configuration | | :---------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------ | | List tools | Retrieves a list of tools available on the MCP server. | No additional configuration required. | | Call tool | Invokes a specific tool on the MCP server. | **Tool name**: The name of the tool to invoke.**Tool arguments**: Tool arguments as a FEEL context expression. | | List resources | Retrieves a list of available resources on the MCP server. | No additional configuration required. | | List resource templates | Retrieves a list of available resource templates on the MCP server.These are similar to resources, but include a set of parameters for dynamic resource access. | No additional configuration required. | | Read resource | Retrieves the content of a specific resource defined by its URI. | **Resource URI**: The URI of the resource to read. | | List prompts | Retrieves a list of available prompts on the MCP server. | No additional configuration required. | | Get prompt | Retrieves a specific prompt and its messages, potentially customized based on the provided input arguments. | No additional configuration required. | ## Filters Filter the list of available data provided by the MCP server, such as tools, resources, or prompts. If not configured, all tools provided by the MCP server will be available to the AI agent. Filters can also prevent operations from being executed when applied to retrieval or execution operations, such as tool calls. Below is an example of tool filtering. The same approach applies to resources and prompts: | Field | Required | Description | Example | | :------------- | :------- | :------------------------------------------------------ | :------------------------------------- | | Included tools | No | List of allowed tools provided by the MCP server. | `["read_file", "read_multiple_files"]` | | Excluded tools | No | List of tools to exclude. Overrides any included tools. | `["write_file"]` | For example, an MCP client connected to a [file system MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem) could be configured with `["read_file", "read_multiple_files"]` as included tools to only allow read-only operations to the file system. Alternatively, it could be configured with `["write_file"]` as a list of excluded tools to prevent write operations. ## Output mapping Specify the process variables to map and export the tool calling response into. | Field | Required | Description | | :---------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Result variable | Yes | Defaults to `toolCallResult`. Change only if using the AI Agent Task connector and the output mapping of the ad-hoc sub-process multi-instance is configured to use a different variable for the [content mapping](./agentic-ai-aiagent-task-example.md#configure-multi-instance-execution). | | Result expression | No | Optionally unpack the response content into multiple process variables using the **Result expression** field as a [FEEL context expression](../../concepts/expressions.md). | ## HTTP proxy support In Self-Managed environments, the MCP Client connector supports HTTP proxy configuration, including [plain proxy variables](/self-managed/components/connectors/http-proxy-configuration.md#plain-proxy-variables). See the [AI Agent connector proxy configuration](./agentic-ai-aiagent-customization.md#http-proxy-configuration) for details, including how to disable proxy support. --- ## MCP Client connector :::note This connector is not directly available on Camunda 8 SaaS. Instead, you can connect a custom connector runtime configured to run the MCP Client connector to your Camunda 8 SaaS instance. ::: The MCP Client connector integration allows configuring MCP clients to be started as part of the connector runtime. As the runtime manages MCP client connections (unlike the job worker in the [MCP Remote Client connector](./agentic-ai-mcp-remote-client-connector.md#limitations)), this approach enables using both STDIO and remote MCP servers without the overhead of repeatedly opening and closing connections for each interaction. ## Runtime configuration :::note Configuration properties can be defined as environment variables using [Spring Boot conventions](https://docs.spring.io/spring-boot/reference/features/external-config.html#features.external-config.typesafe-configuration-properties.relaxed-binding.environment-variables). To define an environment variable, convert the configuration property to uppercase, remove any dashes `-`, and replace any delimiters `.` with underscores `_`. For example, the property `camunda.connector.agenticai.mcp.remote-client.client.cache.expire-after` is represented by the environment variable `CAMUNDA_CONNECTOR_AGENTICAI_MCP_REMOTECLIENT_CLIENT_CACHE_EXPIREAFTER`. ::: To use the MCP Client connector, it must be enabled in the connector runtime. Any clients that should be available to the connector must also be defined in the runtime configuration. STDIO servers can use any programming language or execution runtime available on the machine running the connector runtime. The example below starts MCP servers using both Node.js and Docker, and therefore requires a Node.js and Docker environment to be available. :::warning Configuring STDIO servers results in the connector runtime starting and managing the lifecycle of the configured processes. When configuring third-party MCP servers, ensure that the configured commands are trusted and secure. ::: ```yaml camunda: connector: agenticai: mcp: client: enabled: true # <-- disabled by default clients: # STDIO server started as Node.js process (requires Node.js runtime) filesystem: # <-- client ID, needed to reference the client in the MCP Client connector configuration type: stdio stdio: command: npx args: - "-y" - "@modelcontextprotocol/server-filesystem" - "" env: MY_ENV_VAR: "my-value" # <-- optional environment variables # STDIO server started as docker container (requires Docker being available) time: type: stdio stdio: command: docker args: - "run" - "-i" - "--rm" - "mcp/time" # Remote Streamable HTTP MCP server (recommended for remote servers) # fetch: # enabled: true # type: http # http: # url: https://remote.mcpservers.org/fetch/mcp # headers: # X-Dummy: dummy-value # # authentication examples # authentication: # type: basic # or bearer or oauth # basic: # username: my-username # password: my-password # bearer: # token: my-token # oauth: # oauth-token-endpoint: http://example.com/oauth/token # client-id: my-client-id # client-secret: my-client-secret # scopes: my-scope # audience: my-audience # client-authentication: basic-auth-header # or credentials-body # Connection to a remote HTTP/SSE MCP server # some-remote-sse-server: # enabled: false # type: sse # sse: # url: https://example.com/mcp/sse # headers: # X-Dummy: dummy-value # authentication: # ... ``` The YAML structure above describes the overall configuration structure of the MCP Client connector. How to configure this for your specific use case varies on the connector runtime you are using. ### Camunda 8 Run 1. Download and extract the latest [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) release (at least version 8.8.0-alpha6). 2. Before starting Camunda 8 Run, create a config file (for example `mcp-clients.yml`) in the same directory as `connectors-application.properties` and add the MCP Client configuration as shown above. Adapt the configuration as needed. 3. Edit `connectors-application.properties` and add the following line to include your custom config file: ```properties spring.config.import=file:./mcp-client.yml ``` 4. [Start Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run/install-start.md#install-and-start-camunda-8-run). 5. While starting up, you can follow `logs/connectors.log`. If configured correctly, you should see log messages for the initialization of the configured MCP clients and the registration of the MCP Client connector: ```log [...] Creating MCP client with ID 'filesystem' [...] Creating MCP client with ID 'time' [...] Starting job worker: JobWorkerValue{type='io.camunda.agenticai:mcpclient:xxx', name='MCP Client', ...} ``` ### Custom project using the Spring Boot starter runtime 1. Create a new Spring Boot project. 2. Add the [Camunda Connector Spring Boot starter](../custom-built-connectors/connector-sdk.md#spring-boot-starter-runtime) and the Agentic AI dependencies to your `pom.xml`: ```xml 8.8.0 io.camunda.connector spring-boot-starter-camunda-connectors ${version.connectors} io.camunda.connector connector-agentic-ai ${version.connectors} ``` 3. Configure the SDK to connect to your cluster, according to [the Camunda SDK documentation](../../../apis-tools/camunda-spring-boot-starter/getting-started.md#configuring-the-camunda-8-connection). 4. In your application configuration file (e.g., `application.yml`), add the MCP client configuration as shown above. 5. If you only want to run the MCP Client connector (for example, because you're connecting the runtime to SaaS), disable the other Agentic AI connectors provided by the `connector-agentic-ai` dependency: ```yaml camunda: connector: agenticai: aiagent: enabled: false ad-hoc-tools-schema-resolver: enabled: false mcp: remote-client: enabled: false ``` ## Modeling 1. Configure an AI agent ad-hoc sub-process as described in the [example integration](./agentic-ai-aiagent-subprocess-example.md). Do not configure any tools within the ad-hoc sub-process yet. 2. In a Self-Managed environment, install the [MCP Client element template](https://raw.githubusercontent.com/camunda/connectors/refs/heads/main/connectors/agentic-ai/element-templates/agenticai-mcp-client-outbound-connector.json). 3. Create a service task within the ad-hoc sub-process and apply the **MCP Client** element template. 4. In the **MCP Client** section of the properties panel, configure the **Client ID** to match the value of the MCP client you used in the runtime configuration (example: `filesystem`). 5. Execute your process. You should see tool discovery calls being routed to the MCP Client service task, and tool definitions provided by the MCP server listed in the agent context variable. As a result, the agent should be able to call the tools provided by the MCP server. --- ## Human in the loop Learn how to build human-in-the-loop AI workflows in Camunda by combining MCP Client tool discovery with BPMN elements like gateways, intermediate events, and user tasks to control and approve tool execution. ## About Using [tool discovery](./agentic-ai-mcp-client-tool-discovery.md), you can combine the MCP Client connector with other BPMN elements, such as user tasks or intermediate events, to create a human-in-the-loop interaction. Instead of directly exposing the MCP Client connector as a tool, an intermediate event marked as an MCP client gateway can serve as the root activity of a tool flow within the ad-hoc sub-process. ## Configure a filesystem MCP server The following is an example of how you can configure a filesystem MCP server: 1. Add a service task to the ad-hoc sub-process and configure one of the [MCP Client connectors](./agentic-ai-mcp-client.md#mcp-client-connectors). 2. Add an intermediate throw event to the ad-hoc sub-process and add an extension property named `io.camunda.agenticai.gateway.type` with the value `mcpClient`. 3. Create an exclusive gateway after the event to decide whether the MCP client tool call should be executed directly or require confirmation. 4. Create a flow from the exclusive gateway to the MCP Client service task for direct execution. - In the condition expression, you can use a FEEL expression like the following to allow tool listing and selected operations directly. This differs from [filtering](#tools), because all tools remain available, but you can decide which tools need user confirmation: ```feel if toolCall.method = "tools/list" then true else toolCall.method = "tools/call" and list contains([ "read_file", "read_multiple_files" ], toolCall.params.name) ``` 5. Create a default flow to a user task for the confirmation. Set up a form for the user task to enable a decision on whether the tool call should be executed. - Add a checkbox to the form so the user can confirm or deny the tool call. - Add a text view to present the tool call with a template such as the following: ``` # MCP Tool Call Confirmation The model requested to call the following MCP tool: {{toolCall.params}} ``` 6. Configure a second exclusive gateway after the user task to decide whether the tool call should be executed depending on the value of the checkbox. - If tool execution is allowed, connect the exclusive gateway to the MCP Client service task. - If tool execution is not allowed, end the tool flow in an intermediate throw event. Configure an output variable `toolCallResult`, to return a denied tool call to the model. Use the following FEEL expression as a variable assignment value: ```feel {"isError": true, "content": [{"type": "text", "text": "Tool call was not allowed by the user"}]} ``` See the example diagram below: ![MCP Client connector human-in-the-loop example](agentic-ai/img/mcp-client-hitl.png) --- ## MCP Client tool discovery Learn how AI agents use gateway tool definitions to automatically discover and invoke MCP client tools, enabling distributed, advanced BPMN workflows. ## About AI agents can detect and use tools provided by MCP clients via activities within an ad-hoc sub-process. This is made possible by so-called [_gateway tool definitions_](./agentic-ai-aiagent-tool-definitions.md#gateway-tool-definitions), which provide access to a list of tools instead of a single tool definition. Tool discovery and calling are performed within the ad-hoc sub-process, rather than directly in the AI agent. This enables: - Running the AI agent and individual MCP clients in different deployments. For example, a custom MCP client interacting with a local filesystem could be connected to a Camunda 8 SaaS instance. - More advanced modeling use cases involving other BPMN elements. For example, user tasks can be used to combine MCP clients with an approval flow for individual tool calls. See [Human in the loop](./agentic-ai-mcp-client-human-in-the-loop.md) for more details. The following sequence diagram illustrates the process of tool discovery and calling with MCP clients. Each actor in the diagram can potentially run in a different deployment, making the architecture truly distributed: ```mermaid sequenceDiagram autonumber actor User as User participant AAC as AI Agent Connector participant LLM as LLM participant MCPC as MCP Client Connector participant MCPS as MCP Server User ->>+ AAC: new request %% MCP tool lookup AAC ->>+ MCPC: list tools deactivate AAC MCPC ->>+ MCPS: list tools note left of MCPC: no LLM interaction(process flow only) MCPS -->>- MCPC: MCP tools MCPC -->>- AAC: MCP tools %% LLM call with tool definitions activate AAC AAC ->>+ LLM: request including MCP tools LLM -->>- AAC: tool call requests %% MCP tool call AAC ->>+ MCPC: call tool A deactivate AAC MCPC ->>+ MCPS: call tool A MCPS -->>- MCPC: tool A response MCPC -->>- AAC: tool A response activate AAC %% LLM feedback based on tool call AAC ->>+ LLM: tool call response LLM -->>- AAC: AI response derived from tool call AAC -->>- User: final response ``` ## Tool discovery To mark an activity tool as a gateway tool definition, the agent expects an [extension property](../../modeler/element-templates/defining-templates.md#zeebeproperty) named `io.camunda.agenticai.gateway.type` with the value `mcpClient`. This is automatically applied by the provided [MCP connectors](./agentic-ai-mcp-client-connector.md#mcp-connectors), but it also allows for more advanced use cases, such as tool calling with human-in-the-loop interaction, when added to other activities, like an intermediate event. When the AI agent connector [resolves its available tools](./agentic-ai-aiagent-tool-definitions.md#tool-resolution), it also resolves gateway tool definitions. If required by the gateway tool type, it initiates a tool discovery feedback loop through the ad-hoc sub-process. The implementation of tool discovery depends on the gateway tool type. For MCP clients (gateway type `mcpClient`), it triggers the [`tools/list`](https://modelcontextprotocol.io/specification/2025-06-18/server/tools#listing-tools) method on each MCP Client connector configured within the ad-hoc sub-process. It is the responsibility of the MCP client implementation to fetch tool definitions from the connected MCP server and return them to the AI agent as part of this discovery call. ## Tool definitions Because the AI agent must present unique tool names to the LLM while also being able to map tool calls to specific activities in the ad-hoc sub-process, it applies a naming convention to uniquely identify MCP tool names: ``` MCP____> ``` For example, the `get_current_time` tool provided by a [time MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/time) would resolve to the following tool definition when accessed through an MCP Client activity with the ID `Time`: ```json { "name": "MCP_Time___get_current_time", "description": "Get current time in a specific timezones", "inputSchema": { "properties": { "timezone": { "type": "string", "description": "IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use 'UTC' as local timezone if no timezone provided by the user." } }, "required": ["timezone"], "type": "object" } } ``` When handling LLM tool call requests, the MCP Client integration of the AI agent connector transparently maps the unique tool names back to the matching activity. The tool name and arguments are then passed to the MCP Client connector for the actual tool call. ### Name restrictions Because the `___` sequence is used as a separator in the tool naming convention, **MCP client activity IDs must not contain `___`**. If an activity ID contains this reserved separator, the AI agent connector throws an error with the error code `MCP_GATEWAY_INVALID_TOOL_DEFINITIONS` when attempting tool discovery. To resolve this error, rename the affected MCP Client activities in your BPMN model so that their IDs do not contain `___`. --- ## MCP Client Integrate [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) clients with [agentic orchestration](../../agentic-orchestration/agentic-orchestration-overview.md). :::tip Camunda as an MCP server The Orchestration Cluster includes two built-in MCP servers that can be used as remote MCP server targets with the MCP Client connectors: - The [Orchestration Cluster MCP Server](/apis-tools/orchestration-cluster-api-mcp/orchestration-cluster-api-mcp-overview.md) exposes Camunda's operational capabilities (incidents, user tasks, process instances…). See [Enable and connect](/apis-tools/orchestration-cluster-api-mcp/orchestration-cluster-api-mcp-setup.md#use-with-the-mcp-client-connectors) for configuration details. - The [Processes MCP Server](/apis-tools/processes-mcp/processes-mcp-overview.md) exposes your deployed BPMN processes as callable tools (served at `/mcp/processes`). See [Enable and connect](/apis-tools/processes-mcp/processes-mcp-setup.md#use-with-the-mcp-client-connectors) for configuration details. ::: ## About Camunda's MCP Client enables you to use the [AI Agent connector](./agentic-ai-aiagent.md) together with MCP clients to access tools provided by MCP servers. :::info The MCP Client supports only tool-related functionality. Other MCP features, such as resources or prompts, are not currently supported. ::: This includes: - Locally started standard input/output servers [STDIO](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#stdio). These are operating system processes launched and managed by the connector runtime. - Remote MCP servers using [Streamable HTTP](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#streamable-http) and [HTTP with SSE](https://modelcontextprotocol.io/specification/2024-11-05/basic/transports#http-with-sse) (deprecated). See the MCP Client architecture below: ![MCP Client architecture](agentic-ai/img/mcp-clients-architecture.png) ## MCP Client connectors Camunda provides two MCP connectors with distinct purposes. | Connector | STDIO | Remote/HTTP | Configuration | Availability | Description | | :------------------------------------------------------------------------- | :---------- | :---------- | :----------------------------------- | :---------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------- | | [MCP Remote Client connector](./agentic-ai-mcp-remote-client-connector.md) | Unsupported | Supported | Properties panel | Available on SaaS | Suitable for prototyping with remote MCP servers. Uses on-demand HTTP connections instead of persistent ones. | | [MCP Client connector](./agentic-ai-mcp-client-connector.md) | Supported | Supported | Connector runtime + properties panel | Not directly available on SaaS, but a custom runtime running the client connector can be connected to SaaS. | Flexible MCP integration based on persistent connections managed by the connector runtime. Supports STDIO MCP servers. | :::info The two connectors are not mutually exclusive and can be used together as long as your environment is configured accordingly. ::: ## Learn the fundamentals Understand the fundamental concepts of the MCP Client. ## Explore further resources Dive into practical resources to see the MCP Client in action. Try out this example Watch this webinar on using MCP with Camunda --- ## MCP Remote Client connector The MCP Remote Client connector allows connecting an AI agent to a remote MCP server by configuring a connection to a [Streamable HTTP](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#streamable-http) (recommended) or [HTTP with SSE](https://modelcontextprotocol.io/specification/2024-11-05/basic/transports#http-with-sse) (legacy) endpoint. ## Limitations Since the MCP client functionality is handled by a stateless [job worker](../../concepts/job-workers.md), each activation of an activity using the MCP Remote Client connector requires opening a dedicated HTTP connection/SSE subscription to the MCP server. For example, each of the following actions in an agentic AI feedback loop opens and closes a dedicated MCP client connection to the remote server: 1. Tool discovery 2. Tool call 3. Every subsequent tool call Due to this overhead, the MCP Remote Client connector is primarily intended for prototyping and testing purposes. For production or more efficient usage, consider using the [MCP Client](./agentic-ai-mcp-client-connector.md) connector instead. ## Modeling 1. Configure an AI agent ad-hoc sub-process as described in the [example integration](./agentic-ai-aiagent-subprocess-example.md). Do not configure any tools within the ad-hoc sub-process yet. 2. In a Self-Managed environment, install the [MCP Remote Client element template](https://raw.githubusercontent.com/camunda/connectors/refs/heads/main/connectors/agentic-ai/element-templates/agenticai-mcp-remote-client-outbound-connector.json). 3. Create a service task within the ad-hoc sub-process and apply the **MCP Remote Client** element template. 4. Configure the transport type and connection settings as described in [Transport type](#transport-type). 5. Execute your process. You should see tool discovery calls routed to the MCP Client service task, and tool definitions provided by the MCP server listed in the agent context variable. As a result, the agent should be able to call the tools provided by the MCP server. ## Transport type Select the transport protocol for connecting to the remote MCP server. ### Streamable HTTP Use for MCP servers exposing a Streamable HTTP endpoint. :::info This is the recommended transport for new implementations. ::: | Field | Required | Description | | :------ | :------- | :--------------------------------------------------------------------------------- | | URL | Yes | The Streamable HTTP endpoint URL. Typically ends in `/mcp`. | | Headers | No | Custom HTTP headers as a FEEL map. For example, `={ "X-Custom-Header": "value" }`. | | Timeout | No | Connection timeout as an ISO 8601 duration. For example, `PT60S`. | ### Server-Sent Events (SSE) Use for MCP servers exposing a Server-Sent Events (SSE) endpoint. :::info This transport type is considered legacy; use Streamable HTTP for new implementations where possible. ::: | Field | Required | Description | | :------ | :------- | :--------------------------------------------------------------------------------- | | URL | Yes | The SSE endpoint URL. Typically ends in `/sse`. | | Headers | No | Custom HTTP headers as a FEEL map. For example, `={ "X-Custom-Header": "value" }`. | | Timeout | No | Connection timeout as an ISO 8601 duration. For example, `PT60S`. | ## Authentication The MCP Remote Client connector supports multiple authentication methods for connecting to secured MCP servers. Authentication is configured per transport type. ### None Select **None** in the **Authentication** dropdown. No authentication headers are added to requests. You can still configure custom headers for API keys or other header-based authentication mechanisms. ### Basic Sends an `Authorization: Basic ` header with each request. | Field | Required | Description | | :------- | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Username | Yes | The username for authentication. | | Password | Yes | The password for authentication. Camunda recommends using [secrets](/components/hub/organization/manage-clusters/manage-secrets.md). For example, `{{secrets.MCP_PASSWORD}}`. | ### Bearer token Sends an `Authorization: Bearer ` header with each request. | Field | Required | Description | | :----------- | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Bearer token | Yes | The bearer token value. Camunda recommends using [secrets](/components/hub/organization/manage-clusters/manage-secrets.md). For example, `{{secrets.MCP_BEARER_TOKEN}}`. | ### OAuth 2.0 client credentials Automatically retrieves and manages access tokens using the OAuth 2.0 client credentials flow (machine-to-machine authentication). Tokens are cached in memory and automatically refreshed when expired. | Field | Required | Description | | :-------------------- | :------- | :---------------------------------------------------------------------------------------------------------------------------- | | OAuth token endpoint | Yes | The URL to obtain access tokens. | | Client ID | Yes | Your OAuth client identifier. | | Client secret | Yes | Your OAuth client secret. Camunda recommends using [secrets](/components/hub/organization/manage-clusters/manage-secrets.md). | | Audience | No | Target API identifier (required by some OAuth providers). | | Scopes | No | Space-separated list of scopes to request. | | Client authentication | Yes | **Send credentials in header** (Basic authentication) or **send credentials in body**. | For more details on OAuth 2.0 client credentials flow, see [REST connector OAuth](/components/connectors/protocol/rest.md#rest-connector-oauth-token). ## Client caching To reduce the connection overhead described in [Limitations](#limitations), the MCP Remote Client connector can cache MCP clients and reuse connections for calls to the same MCP server. ### Per-client caching options Enable the **Client cache** checkbox in the connector properties to reuse the MCP client connection for as long as configured on the runtime. :::warning Enable client caching **only** when authentication credentials do not depend on process-specific data. Cached clients reuse the authentication context from the first connection, which may conflict with process-specific credentials in subsequent calls. ::: ### Runtime cache configuration In a Camunda 8 Self-Managed setup or a [custom connector runtime](../custom-built-connectors/connector-sdk.md#runtime-environments), configure overall client-caching behavior using the following properties: :::note Configuration properties can be defined as environment variables using [Spring Boot conventions](https://docs.spring.io/spring-boot/reference/features/external-config.html#features.external-config.typesafe-configuration-properties.relaxed-binding.environment-variables). To define an environment variable, convert the configuration property to uppercase, remove any dashes `-`, and replace any delimiters `.` with underscores `_`. For example, the property `camunda.connector.agenticai.mcp.remote-client.client.cache.expire-after` is represented by the environment variable `CAMUNDA_CONNECTOR_AGENTICAI_MCP_REMOTECLIENT_CLIENT_CACHE_EXPIREAFTER`. ::: ```properties camunda.connector.agenticai.mcp.remote-client.client.cache.enabled=true camunda.connector.agenticai.mcp.remote-client.client.cache.expire-after=PT10M camunda.connector.agenticai.mcp.remote-client.client.cache.maximum-size=15 ``` --- ## MCP start event The **MCP start event** is an [element template](/reference/glossary.md#element-template) applied to a BPMN message start event. When deployed, it registers the process as an MCP tool in the [Processes MCP Server](/apis-tools/processes-mcp/processes-mcp-overview.md). :::note The MCP start event is not handled by the Connector Runtime like other Connectors. The element template configures the start event metadata that the Processes MCP Server uses to expose the process as a tool to MCP clients. ::: ## Apply the template 1. Open your BPMN process in Modeler or Modeler. 2. Select a start event (or add a new one). 3. In the properties panel, click the element template picker. 4. Select **MCP start event** from the **AI Tools** category. ## Properties | Property | Required | Description | | :------------------------ | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Name** | Yes | The MCP tool identifier. Alphanumeric characters, hyphens (`-`), underscores (`_`), and dots (`.`) **only**. Maximum 100 characters. Used by MCP clients to call this tool. | | **What it does** | Yes | A plain-language description of the process function. Shown to LLMs as tool metadata. | | **Which inputs it needs** | Yes | A plain-language description of required and optional input parameters, their types, and any constraints. Shown to LLMs as tool metadata. | | **When to use** | No | Specific situations or user intents that should trigger this tool. | | **When not to use** | No | Conditions or situations where this tool should not be invoked. | | **What the tool returns** | No | The outcomes, results, and variable names the process produces on completion. | A hidden `messageNameUuid` property is auto-generated and binds the start event to its BPMN message name. No user action is required for this property. ## How it works When the process is deployed: 1. The Processes MCP Server reads the element template metadata and registers the process as an MCP tool. 2. MCP clients can discover the tool by name and call it. 3. Each tool invocation starts a new process instance with the tool call arguments mapped as process variables. 4. The MCP Server returns the key of the started process instance to the client, which can use it to track execution status or retrieve results. :::important Only the latest deployed version of a process is exposed. See [version binding](/apis-tools/processes-mcp/processes-mcp-version-binding.md) for more details. ::: --- ## Amazon Bedrock AgentCore Long-Term Memory connector With the **Amazon Bedrock AgentCore Long-Term Memory** outbound connector, you can retrieve persistent knowledge, such as facts, preferences, and summaries, from [AWS Bedrock AgentCore Long-Term Memory](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/long-term-memory-long-term.html) in your BPMN process. ## Prerequisites To use the **Amazon Bedrock AgentCore Long-Term Memory connector**, you need the following: - An AWS account with an access key and secret key, or a configured default credentials chain. - An [AgentCore Memory resource](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/long-term-memory-create.html) created in your AWS account. - IAM permissions to execute the `RetrieveMemoryRecords` and `ListMemoryRecords` actions. Learn more about Amazon Bedrock AgentCore Long-Term Memory in the [official documentation](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/long-term-memory-long-term.html). :::note Use Camunda secrets to store credentials and avoid exposing sensitive information directly from the process. Refer to [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ::: ## Create an Amazon Bedrock AgentCore Long-Term Memory connector task ## Authentication To authenticate, choose one of the methods from the **Authentication** dropdown. The supported options are: - **Credentials**: Select this option if you have a valid pair of access and secret keys provided by your AWS account administrator. :::note This option is applicable for both SaaS and Self-Managed users. ::: - **Default Credentials Chain**: Select this option if your system is configured as an implicit authentication mechanism, such as role-based authentication, credentials supplied via environment variables, or files on target host. This approach uses the [Default Credential Provider Chain](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html) to resolve required credentials. :::note This option is applicable only for Self-Managed or hybrid distributions. ::: ## Configuration 1. In the **Region** field, enter the AWS region where your AgentCore Memory resource is deployed. For example, `us-east-1`. 1. In the **Memory ID** field, enter the identifier of the AgentCore Memory resource you want to query. 1. In the **Namespace** field, enter a namespace prefix to scope memory records. For example, `customer/12345`. This is required by AWS to organize and isolate memory records. ## Operations The **Amazon Bedrock AgentCore Long-Term Memory connector** supports the following operations. ### Retrieve memory records Perform a semantic search to find relevant memory records based on a natural language query. #### Parameters | Parameter | Required | Description | | :--------------------- | :------- | :------------------------------------------------------------------------------- | | **Search query** | Yes | Semantic search query to find relevant memory records (up to 10,000 characters). | | **Memory strategy ID** | No | Limits the search to memories created by a specific extraction strategy. | | **Max results** | No | Maximum number of results to return (1–100). Defaults to 10. | | **Next token** | No | Pagination token from a previous response to fetch the next page. | ### List memory records List all memory records within the configured namespace. #### Parameters | Parameter | Required | Description | | :--------------------- | :------- | :---------------------------------------------------------------- | | **Memory strategy ID** | No | Filter memory records by a specific extraction strategy. | | **Max results** | No | Maximum number of results to return (1–100). Defaults to 20. | | **Next token** | No | Pagination token from a previous response to fetch the next page. | ## Response The connector operations use the following response structure: | Field | Description | | :------------ | :------------------------------------------------------------------------------------------- | | `records` | A list of memory record entries. Each entry contains the fields described below. | | `resultCount` | The number of records returned. | | `nextToken` | A token for retrieving additional results in subsequent requests. `null` if no more results. | Each record in the `records` list contains: | Field | Description | | :----------------- | :------------------------------------------------------ | | `memoryRecordId` | Unique identifier for the memory record. | | `content` | The text content of the memory record. | | `memoryStrategyId` | The extraction strategy that created this record. | | `namespaces` | List of namespaces associated with the record. | | `createdAt` | Timestamp when the record was created. | | `score` | Relevance score (only present for retrieve operations). | | `metadata` | Key-value metadata associated with the record. | ## Output mapping 1. Use **Result Variable** to store the response in a process variable. For example, `memoryResult`. 2. Use **Result Expression** to map specific fields from the response into process variables. For example, to extract the content from all records: ```feel = { memories: records.content, count: resultCount } ``` ## Example response The following is an example of the connector response: ```json { "records": [ { "memoryRecordId": "mem-abc123", "content": "Customer prefers email communication over phone calls.", "memoryStrategyId": "preference-extraction", "namespaces": ["customer/12345"], "createdAt": "2024-01-15T10:30:00Z", "score": 0.95, "metadata": { "source": "support-ticket", "confidence": "high" } }, { "memoryRecordId": "mem-def456", "content": "Customer is located in the Pacific timezone.", "memoryStrategyId": "fact-extraction", "namespaces": ["customer/12345"], "createdAt": "2024-01-10T14:20:00Z", "score": 0.87, "metadata": { "source": "profile-update" } } ], "resultCount": 2, "nextToken": null } ``` --- ## Amazon Bedrock AgentCore Runtime connector With the **Amazon Bedrock AgentCore Runtime** outbound connector, you can invoke external agents deployed on [Amazon Bedrock AgentCore Runtime](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agents-tools-runtime.html) from your BPMN process. Use this connector to delegate complex reasoning tasks to specialized AI agents hosted on AWS, such as fraud detection, risk analysis, or document processing agents built with frameworks like Strands, LangGraph, or CrewAI. ## Prerequisites To use the **Amazon Bedrock AgentCore Runtime connector**, you need the following: - An AWS account with an access key and secret key, or a configured default credentials chain. - An agent deployed to [AgentCore Runtime](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-getting-started.html) in an `ACTIVE` state. - IAM permissions to execute the `InvokeAgentRuntime` action on the agent's ARN. Learn more about AgentCore Runtime in the [official documentation](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agents-tools-runtime.html). :::note Use Camunda secrets to store credentials and avoid exposing sensitive information directly from the process. Refer to [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ::: ## Create an Amazon Bedrock AgentCore Runtime connector task ## Authentication To authenticate, choose one of the methods from the **Authentication** dropdown. The supported options are: - Use **Credentials** if you have a valid pair of access and secret keys provided by your AWS account administrator. :::note This option is applicable for both SaaS and Self-Managed users. ::: - Use **Default Credentials Chain** if your system is configured as an implicit authentication mechanism, such as role-based authentication, credentials supplied via environment variables, or files on target host. This approach uses the [Default Credential Provider Chain](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html) to resolve required credentials. :::note This option is applicable only for Self-Managed or hybrid distributions. ::: For more information on authentication and security in Amazon Bedrock, see [Amazon Bedrock security and privacy](https://aws.amazon.com/bedrock/security-compliance/). ## Configuration In the **Region** field, enter the AWS region where your AgentCore Runtime agent is deployed. For example, `us-east-1`. ## Action The **Amazon Bedrock AgentCore Runtime connector** supports the following action. ### Invoke agent runtime #### Parameters | Parameter | Required | Description | | :-------------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | | **Agent Runtime ARN** | Yes | The ARN of the deployed AgentCore Runtime agent. You can find it in the AgentCore console or in the output of `agentcore deploy`. | | **Prompt** | Yes | The message or task to send to the agent. Supports [FEEL](/components/modeler/feel/what-is-feel.md) expressions. | | **Session ID** | No | An optional session ID for multi-turn conversations. When provided, the agent retains context from previous interactions within the same session. | #### Response The connector returns the following fields: | Field | Description | | :----------- | :--------------------------------------------------------------------------------------- | | `response` | The agent's response text or payload. | | `sessionId` | The runtime session ID. Use this value in subsequent calls to continue the conversation. | | `statusCode` | The HTTP status code of the response. | #### Output mapping 1. Use **Result Variable** to store the response in a process variable. For example, `agentResult`. 2. Use **Result Expression** to map specific fields from the response into process variables. For example, to extract the agent's response: ```feel = { answer: response, session: sessionId } ``` #### Example: Invoke a fraud detection agent **Configuration:** - Agent Runtime ARN: `arn:aws:bedrock-agentcore:us-east-1:123456789012:runtime/fraud_agent-1GRoTlCtHi` - Prompt: `Claim: Customer reports stolen vehicle worth $45,000. Filed 2 days after policy activation. No police report.` **Response:** ```json { "response": "{\"result\": {\"role\": \"assistant\", \"content\": [{\"text\": \"{\\\"riskScore\\\": 85, \\\"riskLevel\\\": \\\"HIGH\\\", \\\"flags\\\": [\\\"Policy activated only 2 days before claim\\\", \\\"No police report filed\\\", \\\"High-value claim\\\"], \\\"recommendation\\\": \\\"REJECT\\\"}\"}]}}", "sessionId": "76f079e6-f30b-4416-9fbc-5d09d2ad31de", "statusCode": 200 } ``` #### Multi-turn conversations To maintain context across multiple interactions with the same agent, pass the `sessionId` from the previous response into the **Session ID** field of the next call. This allows the agent to remember prior messages and maintain state. :::note When using the connector as a tool in an [AI Agent Sub-process](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-subprocess.md), use the `fromAi()` function for the **Prompt** field to let the orchestrating agent compose the message dynamically based on the user's request. ::: --- ## Amazon Bedrock Code Interpreter connector With the **Amazon Bedrock Code Interpreter** outbound connector, you can execute Python code in a secure [Amazon Bedrock AgentCore Code Interpreter](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/code-interpreter-tool.html) sandbox from your BPMN process. ## Prerequisites To use the **Amazon Bedrock Code Interpreter connector**, you need the following: - An AWS account with an access key and secret key, or a configured default credentials chain. - IAM permissions to execute the following actions on the `bedrock-agentcore` service: - `StartCodeInterpreterSession` - `InvokeCodeInterpreter` - `StopCodeInterpreterSession` - The AgentCore Code Interpreter service must be available in your selected AWS region. Learn more about the AgentCore Code Interpreter in the [official documentation](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/code-interpreter-tool.html). :::note Use Camunda secrets to store credentials and avoid exposing sensitive information directly from the process. Refer to [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ::: ## Create an Amazon Bedrock Code Interpreter connector task ## Authentication To authenticate, choose one of the methods from the **Authentication** dropdown. The supported options are: - Use **Credentials** if you have a valid pair of access and secret keys provided by your AWS account administrator. :::note This option is applicable for both SaaS and Self-Managed users. ::: - Use **Default Credentials Chain** if your system is configured as an implicit authentication mechanism, such as role-based authentication, credentials supplied via environment variables, or files on target host. This approach uses the [Default Credential Provider Chain](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html) to resolve required credentials. :::note This option is applicable only for Self-Managed or hybrid distributions. ::: For more information on authentication and security in Amazon Bedrock, see [Amazon Bedrock security and privacy](https://aws.amazon.com/bedrock/security-compliance/). ## Configuration In the **Region** field, enter the AWS region where Amazon Bedrock AgentCore Code Interpreter is available. For example, `us-east-1`. ## Action The **Amazon Bedrock Code Interpreter connector** supports the following action. ### Code execution #### Parameters | Parameter | Required | Description | | :---------------------------- | :------- | :---------------------------------------------------------------------------------------------------------------- | | **Code** | Yes | The Python code to execute in the sandbox. Supports [FEEL](/components/modeler/feel/what-is-feel.md) expressions. | | **Session timeout (seconds)** | No | How long the sandbox session stays alive, in seconds (60–28,800). Defaults to 300. | :::note The session timeout controls how long the sandbox environment remains available, not how long the code takes to execute. Code execution typically completes in seconds. ::: The sandbox environment includes common Python libraries such as `matplotlib`, `pandas`, `numpy`, and `csv`. Network access is not available in the sandbox. #### Response The connector returns the following fields: | Field | Description | | :---------------- | :------------------------------------------------------------------------------------------------------------------- | | `stdout` | The standard output printed by the code. | | `stderr` | Any error output produced during execution. | | `exitCode` | The process exit code. A value of `0` indicates successful execution. | | `executionTimeMs` | The execution duration in milliseconds. | | `files` | A list of [Camunda documents](/components/document-handling/getting-started.md) for any files generated by the code. | #### File handling Any files created by the code during execution are automatically detected and returned as Camunda documents in the `files` field. This includes images (for example, charts saved with `plt.savefig()`), CSV files, text files, and other output files. :::note A maximum of 10 files are retrieved per execution, with a total size limit of 10 MB. Files exceeding these limits are skipped, and a warning is logged. ::: #### Output mapping 1. Use **Result Variable** to store the response in a process variable. For example, `codeResult`. 2. Use **Result Expression** to map specific fields from the response into process variables. For example, to extract the output and generated files: ```feel = { output: stdout, files: files } ``` #### Example: Run a calculation **Code:** ```python total = sum([12000, 8500, 15000, 9200]) print(f"Total claims: ${total:,}") ``` **Response:** ```json { "stdout": "Total claims: $44,700\n", "stderr": "", "exitCode": 0, "executionTimeMs": 0.05, "files": [] } ``` #### Example: Generate a chart **Code:** ```python plt.bar(["Q1", "Q2", "Q3", "Q4"], [12000, 8500, 15000, 9200]) plt.title("Claims by Quarter") plt.ylabel("Amount ($)") plt.savefig("chart.png") print("Chart saved") ``` **Response:** ```json { "stdout": "Chart saved\n", "stderr": "", "exitCode": 0, "executionTimeMs": 1.67, "files": [ { "storeId": "in-memory", "documentId": "851505b2-61bd-4717-9c86-07678b67f033", "metadata": { "contentType": "image/png", "size": 14734, "fileName": "chart.png" }, "camunda.document.type": "camunda" } ] } ``` #### Example: Generate a CSV report **Code:** ```python with open("claims.csv", "w", newline="") as f: w = csv.writer(f) w.writerow(["ClaimID", "Amount", "Status"]) w.writerows([["C001", 45000, "Rejected"], ["C002", 12000, "Approved"]]) print("Report created") ``` **Response:** ```json { "stdout": "Report created\n", "stderr": "", "exitCode": 0, "executionTimeMs": 0.05, "files": [ { "storeId": "in-memory", "documentId": "c209e3a4-cefa-4622-adc1-88c92bf06bb2", "metadata": { "contentType": "text/csv", "size": 80, "fileName": "claims.csv" }, "camunda.document.type": "camunda" } ] } ``` --- ## Amazon Bedrock Knowledge Base connector With the **Amazon Bedrock Knowledge Base** outbound connector, you can perform semantic search over documents indexed in an [Amazon Bedrock Knowledge Base](https://docs.aws.amazon.com/bedrock/latest/userguide/knowledge-base.html) from your BPMN process. ## Prerequisites To use the **Amazon Bedrock Knowledge Base connector**, you need the following: - An AWS account with an access key and secret key, or a configured default credentials chain. - A [Bedrock Knowledge Base](https://docs.aws.amazon.com/bedrock/latest/userguide/knowledge-base-create.html) created and configured with at least one data source. - IAM permissions to execute the [`Retrieve`](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_agent-runtime_Retrieve.html) action on the knowledge base. Learn more about Amazon Bedrock Knowledge Bases in the [official documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/knowledge-base.html). :::note Use Camunda secrets to store credentials and avoid exposing sensitive information directly from the process. Refer to [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ::: ## Create an Amazon Bedrock Knowledge Base connector task ## Authentication To authenticate, choose one of the methods from the **Authentication** dropdown. The supported options are: - Use **Credentials** if you have a valid pair of access and secret keys provided by your AWS account administrator. The access key must have permissions to the Bedrock Knowledge Base `Retrieve` action. :::note This option is applicable for both SaaS and Self-Managed users. ::: - Use **Default Credentials Chain** if your system is configured as an implicit authentication mechanism, such as role-based authentication, credentials supplied via environment variables, or files on target host. This approach uses the [Default Credential Provider Chain](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html) to resolve required credentials. :::note This option is applicable only for Self-Managed or hybrid distributions. ::: For more information on authentication and security in Amazon Bedrock, see [Amazon Bedrock security and privacy](https://aws.amazon.com/bedrock/security-compliance/). ## Configuration In the **Region** field, enter the AWS region where your knowledge base is deployed. For example, `us-east-1`. In the **Knowledge Base ID** field, enter the ID of the Bedrock Knowledge Base you want to query. You can find this ID in the [Amazon Bedrock console](https://console.aws.amazon.com/bedrock/). ## Action The **Amazon Bedrock Knowledge Base connector** supports the following action. ### Retrieve from Knowledge Base Perform a semantic search over the documents indexed in your knowledge base. The connector returns the most relevant passages that match your query. #### Parameters | Parameter | Required | Description | | :-------------------- | :------- | :---------------------------------------------------------------------------------------------------------------------------- | | **Query** | Yes | A natural language query to search the knowledge base. Supports [FEEL](/components/modeler/feel/what-is-feel.md) expressions. | | **Number of results** | No | The maximum number of results to return (1-100). Defaults to five. | #### Response The connector returns the following fields: | Field | Description | | :---------------- | :------------------------------------------------------------------------------------------------------------------- | | `results` | A list of retrieval results. Each result contains the fields described below. | | `resultCount` | The number of results returned. | | `paginationToken` | A token for retrieving additional results in subsequent requests. This value is `null` if there are no more results. | Each result in the `results` list contains the following fields: | Field | Description | | :------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `documentReference` | A [Camunda document](/components/document-handling/getting-started.md) reference containing the chunk text, for use with downstream connectors or processing. | | `content` | The matched text passage from the knowledge base. | | `score` | A relevance score between 0 and 1 indicating how well the result matches the query. | | `sourceUri` | The S3 URI of the source document. | | `metadata` | Key-value metadata associated with the source document. | #### Output mapping 1. Use **Result Variable** to store the response in a process variable. For example, `kbResult`. 2. Use **Result Expression** to map specific fields from the response into process variables. For example, to extract just the text content from all results: ```feel = { texts: results.content, count: resultCount } ``` #### Query guidelines For best results, use specific, descriptive queries rather than short keywords: | Query | Quality | Why | | :--------------------------------------------------------------- | :------ | :------------------------------------------------ | | "What does the auto insurance policy cover for stolen vehicles?" | Good | Specific topic with clear intent. | | "What are the exclusions for water damage in home insurance?" | Good | Targets a specific section and policy type. | | "Coverage" | Poor | Too broad. It may return loosely related results. | | "Tell me everything about insurance" | Poor | Too vague. No specific topic to match against. | :::tip When using the connector as a tool in an [AI Agent Sub-process](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-subprocess.md), the agent composes the query automatically based on the user's request. Use the `fromAi()` function to let the agent generate targeted queries. ::: #### Example response The following is an example of the connector response: ```json { "results": [ { "documentReference": { "storeId": "in-memory", "documentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" }, "content": "Comprehensive auto insurance covers damage to your vehicle from events other than collisions, including theft, vandalism, natural disasters, and falling objects.", "score": 0.92, "sourceUri": "s3://my-bucket/policies/auto-insurance.pdf", "metadata": { "category": "auto", "policyType": "comprehensive" } }, { "documentReference": { "storeId": "in-memory", "documentId": "b2c3d4e5-f6a7-8901-bcde-f12345678901" }, "content": "Liability coverage pays for bodily injury and property damage that you cause to others in an auto accident.", "score": 0.85, "sourceUri": "s3://my-bucket/policies/auto-insurance.pdf", "metadata": { "category": "auto", "policyType": "liability" } } ], "resultCount": 2, "paginationToken": null } ``` --- ## Amazon Bedrock connector The **Amazon Bedrock connector** is an outbound connector that allows you to interact with [Amazon Bedrock](https://aws.amazon.com/bedrock/) from your BPMN process. ## Prerequisites To use the **Amazon Bedrock connector**, you need to have an AWS account with an access key and secret key to execute [`InvokeModel`](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_InvokeModel.html) or [`Converse`](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_Converse.html) actions. The necessary models must be enabled beforehand on the region you are operating from. See more about this [in the Amazon Bedrock user guide](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html). Learn more about Amazon bedrock in the [official Bedrock documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html). :::note Use Camunda secrets to store credentials and avoid exposing sensitive information directly from the process. Refer to [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ::: ## Create an Amazon Bedrock connector task ## Authentication To authenticate, choose one of the methods from the **Authentication** dropdown. The supported options are: - Use **Credentials** if you have a valid pair of access and secret keys provided by your AWS account administrator. The access key provides permissions to the Amazon Bedrock `InvokeModel` and/or `Converse` actions, as mentioned in the [AWS documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/security_iam_id-based-policy-examples.html#security_iam_id-based-policy-examples-perform-actions-pt). :::note This option is applicable for both SaaS and Self-Managed users. ::: - Use **Default Credentials Chain** if your system is configured as an implicit authentication mechanism, such as role-based authentication, credentials supplied via environment variables, or files on target host. This approach uses the [Default Credential Provider Chain](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html) to resolve required credentials. :::note This option is applicable only for Self-Managed or hybrid distributions. ::: For more information on authentication and security in Amazon Bedrock, see [Amazon Bedrock security and privacy](https://aws.amazon.com/bedrock/security-compliance/). ## Region In the **Region** field write the region of the deployed endpoint. ## Action There are two possible actions with the Amazon Bedrock connector: `InvokeModel` and `Converse`. ### InvokeModel This action is meant to invoke a model with a raw payload. A model ID must be specified. Find all the available options for Amazon Bedrock [in the model ID documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids.html). :::note Ensure the model is available in your region, that your model can invoke the `Invoke Model` action, and you are a user with adequate rights. ::: The payload is dependent on the model used, and you can find the different payloads [in the model parameters documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters.html). 1. Use **Result Variable** to store the response in a process variable. For example, `myResultVariable`. 2. Use **Result Expression** to map fields from the response into process variables. The response is dependent on the model used, and you can find the different responses [in the model parameters documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters.html). #### Example If using the model `Jamba-instruct` with model ID `ai21.jamba-instruct-v1:0`, and looking at the [model parameters Jamba documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-jamba.html), the payload could be as follows: ```json { "messages": [{ "role": "user", "content": "Hello" }], "max_tokens": 256, "top_p": 0.8, "temperature": 0.7 } ``` The FEEL mapping could be as follows: ``` { response : body.choices.message.content[1] } ``` ### Converse This action is meant to start or continue a conversation with a model. A model ID must be specified. Find all available model IDs for Amazon Bedrock [in the model ID documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids.html). :::note Ensure the model is available in your region, that your model can invoke the `Converse` action, and you are a user with adequate rights. ::: - `New Message` is either the first message (to start a conversation) or is the next message from an already started conversation. - `Documents` is a list of documents to include as part of your **new message**. - To work with documents you must upload them first, [using the Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/specifications/create-document.api.mdx) for example. - See [Amazon Bedrock supported document formats](https://docs.aws.amazon.com/bedrock/latest/userguide/knowledge-base-ds.html) for currently supported file formats. - The result of the endpoint must then be assigned to a variable in **Start Process Instance** so you can use the list of these variables in the **Documents** field. - `Message History` is the history of the conversation that should always be passed. If not set, this will be a new conversation. 1. Use **Result Variable** to store the response in a process variable. For example, `myResultVariable`. 2. Use **Result Expression** to map fields from the response into process variables. The **Response** is a list of consecutive messages of the user and the assistant. :::info important The current implementation supports the assistant's responses only in text format. ::: Ideally, the message's history must transit within the process and be the input of this `Converse` task with the new message. :::note Starting from version 8.7.0, the Amazon Bedrock connector supports consuming documents as inputs for conversations. Review the **Document** field in the properties panel where the document reference can be provided. See additional details and limitations in [document handling](/components/document-handling/getting-started.md). ::: --- ## Amazon Comprehend connector :::info The **Amazon Comprehend connector** is available for `8.7.0` or later. ::: The **Amazon Comprehend connector** allows you to integrate your BPMN service with [Amazon Comprehend](https://docs.aws.amazon.com/comprehend/latest/dg/what-is.html), a service which extracts insights about the content of documents, such as personal identifiable information and key phrases. ## Prerequisites To use the **Amazon Comprehend connector**, you must have an **AWS IAM Access Key** and **Secret Key** with the appropriate Comprehend permissions. Refer to the [AWS Comprehend set up instructions](https://docs.aws.amazon.com/comprehend/latest/dg/setting-up.html). :::note Use **Camunda secrets** to avoid exposing your AWS IAM credentials as plain text. Refer to [managing secrets](components/hub/organization/manage-clusters/manage-secrets.md) for more details. ::: ## Create an Amazon Comprehend connector task ## Make your Amazon Comprehend connector executable To execute the **Amazon Comprehend connector**, ensure all mandatory fields are correctly filled. ## 1. Authentication Choose an authentication type from the **Authentication** dropdown. For details on the different authentication types, refer to the [appendix](#aws-authentication-types). If you select **Credentials**, the following fields must be provided: - **Access Key**: The AWS access key for a user with Comprehend permissions. - **Secret Key**: The corresponding AWS secret key. Both **Access Key** and **Secret Key** are required to use the connector. ## 2. **Configuration (AWS Region)** After authentication, set the AWS **Region** where the Textract service is hosted: - **Region**: Specify the region (for example, `us-east-1`, `eu-west-1`). :::note Ensure the region matches the location of your Comprehend service and S3 buckets to reduce latency and meet compliance requirements. For a full list of AWS regions, refer to the [AWS regional data](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/). ::: ## 3. Configure input ### Execution types Select the desired execution type from the **Execution Type** dropdown. The following options are available: - **Sync** Use **Sync** execution to create a classification request and analyze a single document in real-time. For more details, refer to [sync execution](https://docs.aws.amazon.com/comprehend/latest/APIReference/API_ClassifyDocument.html). - **Async** Use **Async** execution to start an asynchronous document classification job using a custom classification model. This method allows you to submit a document for analysis and receive results at a later time, making it ideal for background processing or batch operations. **Async** execution enables you to process documents without waiting for immediate responses. This is particularly useful for larger files or when handling multiple documents simultaneously. For more details on the fields that can be configured during asynchronous execution, refer to [async execution](https://docs.aws.amazon.com/comprehend/latest/APIReference/API_StartDocumentClassificationJob.html). ### Sync execution fields - **Text (mandatory)**: The document text to be analyzed. - **Endpoint Arn (mandatory)**: The Amazon Resource Number (ARN) of the endpoint. For more details, refer to [Classify Document](https://docs.aws.amazon.com/comprehend/latest/APIReference/API_ClassifyDocument.html#API_ClassifyDocument_RequestSyntax). ### Async execution fields - **Document read mode**: Determines the text extraction actions for PDF files. For more details, refer to [document read mode](https://docs.aws.amazon.com/comprehend/latest/APIReference/API_DocumentReaderConfig.html#comprehend-Type-DocumentReaderConfig-DocumentReadMode). - **Document read action**: This field defines the Amazon Textract API operation that Amazon Comprehend uses to extract text from PDF files and image files. For more details, refer to [document read action](https://docs.aws.amazon.com/comprehend/latest/APIReference/API_DocumentReaderConfig.html#comprehend-Type-DocumentReaderConfig-DocumentReadAction). - **Analyze tables**: Returns additional information about any tables that are detected in the input document. For more details, refer to [feature types](https://docs.aws.amazon.com/comprehend/latest/APIReference/API_DocumentReaderConfig.html#comprehend-Type-DocumentReaderConfig-FeatureTypes). - **Analyze forms**: Returns additional information about any forms that are detected in the input document. For more details, refer to [feature types](https://docs.aws.amazon.com/comprehend/latest/APIReference/API_DocumentReaderConfig.html#comprehend-Type-DocumentReaderConfig-FeatureTypes). - **Inputs' S3 URI (mandatory)**: The Amazon S3 URI for the input data. For more details, refer to [S3 URI](https://docs.aws.amazon.com/comprehend/latest/APIReference/API_InputDataConfig.html#comprehend-Type-InputDataConfig-S3Uri). - **Input file processing mode**: Specifies how the text in an input file should be processed. For more details, refer to [InputFormat](https://docs.aws.amazon.com/comprehend/latest/APIReference/API_InputDataConfig.html#comprehend-Type-InputDataConfig-InputFormat). - **Client request token**: A unique identifier for the request. If you do not set the client request token, Amazon Comprehend generates one. - **Data Access Role's ARN (mandatory)**: The Amazon Resource Name (ARN) of the IAM role that grants Amazon Comprehend read access to your input data. - **Document Classifier's ARN**: The Amazon Resource Name (ARN) of the document classifier to use to process the job. - **Flywheel's ARN**: The Amazon Resource Number (ARN) of the flywheel associated with the model to use. - **Job name**: The identifier of the job. - **Output's S3 URI (mandatory)**: The Amazon S3 location where you want to write the output data. For more details, refer to [output data config](https://docs.aws.amazon.com/comprehend/latest/APIReference/API_OutputDataConfig.html). - **Outputs KMS Key Id**: The ID for the AWS Key Management Service (KMS) key that Amazon Comprehend uses to encrypt the output results from an analysis job. For more details, refer to [output data config](https://docs.aws.amazon.com/comprehend/latest/APIReference/API_OutputDataConfig.html). - **Tags**: Tags to associate with the document classification job. A tag is a key-value pair that adds metadata to a resource used by Amazon Comprehend. **Example:** ```feel = {"status": "active"} ``` - **VolumeKmsKeyId**: ID for the AWS Key Management Service (KMS) key that Amazon Comprehend uses to encrypt data on the storage volume attached to the ML compute instance(s) that process the analysis job. - **Security group Ids**: The ID number for a security group on an instance of your private VPC. For more details, refer to [security group](https://docs.aws.amazon.com/comprehend/latest/APIReference/API_VpcConfig.html#comprehend-Type-VpcConfig-SecurityGroupIds). **Example:** ```feel = ["sg-07a2cc6d96e4ec178"] ``` - **Subnets**: The ID for each subnet being used in your private VPC. For more details, refer to [Subnets](https://docs.aws.amazon.com/comprehend/latest/APIReference/API_VpcConfig.html#comprehend-Type-VpcConfig-Subnets). **Example:** ```feel = ["subnet-013eac53274e1d93f"] ``` :::note To use **VPC** you need at last one VPC endpoint For more details, refer to [create a VPC endpoint](https://docs.aws.amazon.com/vpc/latest/privatelink/create-interface-endpoint.html#create-interface-endpoint-aws). ::: ## Amazon Comprehend connector response The response from the **Amazon Comprehend connector** will mirror the AWS Comprehend service’s response. The type of response you receive depends on the execution mode selected: - **[Sync Response](https://docs.aws.amazon.com/comprehend/latest/APIReference/API_ClassifyDocument.html#API_ClassifyDocument_ResponseSyntax)**: Provides immediate analysis for provided text. - **[Asynchronous Response](https://docs.aws.amazon.com/comprehend/latest/APIReference/API_StartDocumentClassificationJob.html#API_StartDocumentClassificationJob_ResponseSyntax)**: Used for batch processing where results are returned later through job completion. ### Using the Comprehend connector response in your process The **Amazon Comprehend connector** provides the same response structure as the AWS Comprehend API. You can map fields from the response to process variables, depending on your needs. Here's an example of how to extract specific fields using **Result Expression** and **Result Variable**: ### Example Comprehend response (real-time execution) Utilize output mapping to align this response with process variables: 1. Use **Result Variable** to store the response in a process variable. For example, `myResultVariable`. This approach stores the entire Comprehend message as a process variable named `myResultVariable`. 2. Use **Result Expression** to map fields from the response into process variables. This approach allows for more granularity. Instead of storing the entire response in one variable, you can extract specific fields from the **Comprehend connector** message and assign them to different process variables. This is particularly useful when you are only interested in certain parts of the message, or when different parts of the message need to be used separately in your process. Example: ```json { "classes": [ { "name": "CHECKING_AC", "score": 0.5423, "page": null }, { "name": "SAVINGS_AC", "score": 0.4577, "page": null } ], "labels": null, "documentMetadata": null, "documentType": null, "errors": null, "warnings": null } ``` #### Mapping example To store only first **Classes** element information, use the following result **expression**: ```feel = {classInfo: classes[1]} ``` Mapped values **result**: ```json { "name": "CHECKING_AC", "score": 0.5422999858856201, "page": null } ``` ## Appendix & FAQ ### How do I securely store AWS IAM credentials for my Comprehend connector? Store your AWS IAM credentials as **Camunda secrets** to avoid exposing sensitive information. Follow our [managing secrets guide](components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ### AWS authentication types You can authenticate the **Amazon Comprehend connector** in two ways: - **Credentials**: Select this option if you have an AWS **Access Key** and **Secret Key**. This method is applicable for both SaaS and Self-Managed users. - **Default Credentials Chain (Hybrid/Self-Managed only)**: Select this option if your system uses implicit authentication methods like role-based access, environment variables, or files on the target host. This method is applicable only for Self-Managed or Hybrid environments. It uses the [Default Credential Provider Chain](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html) to resolve credentials. --- ## Amazon DynamoDB connector The **Amazon DynamoDB connector** allows you to connect your BPMN service with Amazon Web Service's [DynamoDB Service](https://aws.amazon.com/dynamodb/). This can be useful for performing CRUD operations on Amazon DynamoDB tables from within a BPMN process. ## Prerequisites To use the **Amazon DynamoDB connector**, you need to have an AWS account with an access key and secret key to access DynamoDB, as well as a region where your DynamoDB instance is located. You can create an account and obtain the access and secret keys from the [AWS Console](https://aws.amazon.com/console/). :::note Use Camunda secrets to store credentials so you don't expose sensitive information directly from the process. Refer to [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ::: ## Create an Amazon DynamoDB connector task ## Make your Amazon DynamoDB connector executable To work with **Amazon DynamoDB connector**, choose the required operation type in the **Operation** section and complete the mandatory fields highlighted in red in the connector properties panel on the right side of the screen. :::note All the mandatory and non-mandatory fields depending on the authentication selection you choose are covered in the upcoming sections. ::: ## Operation Choose an operation type of either **Table** or **Item** in the **Operation** section: - **Table**: Perform operations on a DynamoDB table. - **Item**: Perform operations on a specific item in a DynamoDB table. ### Method Choose one of the following methods: #### [Table](#table-operations) - [Create table](#create-table): Creates a new DynamoDB table. - [Delete table](#delete-table): Deletes an existing DynamoDB table. - [Describe table](#describe-table): Returns information about a DynamoDB table. - [Scan table](#scan-table): Returns one or more items and their attributes by accessing every item in a table. You can use filter expressions to selectively scan for items that meet certain criteria. #### [Item](#item-operations) - [Add item](#add-item): Creates a new item or replaces an existing item with a new item. - [Delete item](#delete-item): Deletes a single item in a table by primary key. - [Get item](#get-item): Returns a set of attributes for the item with the given primary key. - [Update item](#update-item): Modifies an existing item's attributes or adds a new item to the table if it does not already exist. ## Authentication Choose an applicable authentication type from the **Authentication** dropdown. Learn more about authentication types in the related [appendix entry](#aws-authentication-types). If you select **credentials** to access the **Amazon DynamoDB service**, the connector requires the appropriate credentials. The following authentication options are available: - **Access key**: Provide an access key of a user with permissions to the Amazon DynamoDB service. - **Secret key**: Provide the secret key of the user with the access key provided above. The Access Key and Secret Key are required properties and must be provided to use the connector. If these properties are not set, the connector will not be able to authenticate with the [DynamoDB Service](https://aws.amazon.com/dynamodb/). For more information on authentication and security in Amazon DynamoDB, refer to the [AWS documentation](https://docs.aws.amazon.com/dynamodb/index.html). ## Configuration The **Region** property in the **Configuration** section specifies the AWS region in which the DynamoDB table exists or will be created. This property is required and must be set to use the connector. For more information on AWS regions, refer to the [AWS documentation](https://docs.aws.amazon.com/general/latest/gr/rande.html). ## Input The **Input** section of the **Amazon DynamoDB connector** specifies the input data for the [selected operation](#operation). The input data varies depending on the [operation type](#operation) and [method](#method) selected. ### Table operations For the **Table** operation type, the following input data is required: #### Create table **Request** | Property name | Data type | Required | Description | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------: | :------: | :------------------------------------------------------------------------------------: | | [Table name](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_CreateTable.html#DDB-CreateTable-request-TableName) | string | Yes | The name of the DynamoDB table to be created. | | [Partition key](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_CreateTable.html#DDB-CreateTable-request-KeySchema) | string | Yes | The attribute name of the partition key for the table. | | [Partition key role](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_CreateTable.html#DDB-CreateTable-request-KeySchema) | dropdown | Yes | The role of the partition key. Can be set to "HASH" or "RANGE". | | [Partition key attribute data type](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_AttributeDefinition.html#DDB-Type-AttributeDefinition-AttributeType) | dropdown | Yes | The data type of the partition key attribute. | | [Sort key](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_CreateTable.html#DDB-CreateTable-request-KeySchema) | string | No | The attribute name of the sort key for the table (if applicable). | | [Sort key role](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_CreateTable.html#DDB-CreateTable-request-KeySchema) | dropdown | No | The role of the sort key. Can be set to "HASH" or "RANGE". | | [Sort key attribute data type](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_AttributeDefinition.html#DDB-Type-AttributeDefinition-AttributeType) | dropdown | No | The data type of the sort key attribute. | | [Read capacity units](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_CreateTable.html#DDB-CreateTable-request-ProvisionedThroughput) | number | Yes | The maximum number of strongly consistent reads per second that the table can support. | | [Write capacity units](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_CreateTable.html#DDB-CreateTable-request-ProvisionedThroughput) | number | Yes | The maximum number of writes per second that the table can support. | | [Billing mode](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_CreateTable.html#DDB-CreateTable-request-BillingMode) | dropdown | No | The billing mode of the table. Can be set to "PROVISIONED" or "PAY_PER_REQUEST". | | [Deletion protection](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_CreateTable.html#DDB-CreateTable-request-GlobalSecondaryIndexUpdates) | dropdown | No | Indicates whether to enable or disable deletion protection for the table. | **Response** | Property | Data type | Description | | :-----------------------------------------------------------------------------------------------------------: | :-------: | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | [Table description](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TableDescription.html) | Object | Information about the created table, including the table name, attribute definitions, primary key schema, provisioned throughput settings, and more. | #### Delete table **Request** | Property name | Data type | Required | Description | | :------------------------------------------------------------------------------------------------ | :-------: | :------: | :-------------------------------------------: | | [Table name](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DeleteTable.html) | string | Yes | The name of the DynamoDB table to be deleted. | **Response** | Property | Data type | Description | | :------- | :-------: | :-----------------------------------------------------------------------------------------------------------------------------------: | | action | string | The action performed. In this case, it will always be "delete Table [tableName]", where `tableName` is the name of the deleted table. | | status | string | The status of the operation. In this case, it will always be "OK" to indicate that the table was successfully deleted. | #### Describe table **Request** | Property name | Data type | Required | Description | | :-------------------------------------------------------------------------------------------------- | :-------: | :------: | :---------------------------------------------: | | [Table name](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DescribeTable.html) | string | Yes | The name of the DynamoDB table to be described. | **Response** | Property | Data type | Description | | :-----------------------------------------------------------------------------------------------------------: | :-------: | -------------------------------------------------------------------------------------------------------------------------------------------- | | [Table description](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TableDescription.html) | Object | Information about the table, including the table name, attribute definitions, primary key schema, provisioned throughput settings, and more. | #### Scan table **Request** | Property name | Data type | Required | Description | | ----------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [Table name](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html#DDB-Scan-request-TableName) | string | Yes | The name of the DynamoDB table to be scanned. | | [Filter expression](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/QueryAndScan.html#FilterExpression) | string | No | The filter expression to apply to the scan results. For more information, refer to the [Expression Attribute Names and Values](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ExpressionAttributeNames.html) section of the Amazon DynamoDB Developer Guide. | | [Projection expression](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/QueryAndScan.html#ProjectionExpression) | string | No | A string that identifies one or more attributes to retrieve from the specified table. | | [Expression attribute names](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ExpressionPlaceholders.html#ExpressionAttributeNames) | map | No | A map of attribute names to their replacements in the filter expression or projection expression. For more information, refer to the [Expression Attribute Names and Values](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ExpressionAttributeNames.html) section of the Amazon DynamoDB Developer Guide. | | [Expression attribute values](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ExpressionPlaceholders.html#ExpressionAttributeValues) | map | No | A map of attribute values to their replacements in the filter expression or projection expression. For more information, refer to the [Expression Attribute Names and Values](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ExpressionAttributeValues.html) section of the Amazon DynamoDB Developer Guide. | **Response** | Property | Data type | Description | | :------- | :-------: | :---------------------------------------------------------------------------------------------------------------------------------------: | | action | string | The action performed. In this case, it will always be `scanTable`. | | status | string | The status of the operation. If successful, it will always be "OK". Otherwise, it will be an error message. | | items | list | The list of items returned by the scan operation, in case the operation was successful. If there are no items, this field will be `null`. | ### Item operations :::note The **Amazon DynamoDB connector** does not currently support binary data types. If binary data is input during the creation or update of items, it will be saved as a string. When updating items, if an attribute of type SET is updated, it will be overwritten and saved as a list type. Consider these limitations to prevent unintended data structure modifications in your DynamoDB tables. ::: #### Add item **Request** | Property name | Data type | Required | Description | | --------------------------------------------------------------------------------------------------------------------------- | --------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [Table name](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html#DDB-PutItem-request-TableName) | string | Yes | The name of the DynamoDB table to add the item to. | | [Item](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html#DDB-PutItem-request-Item) | object | Yes | The item to add to the table is represented in JSON format. For example: `{"Name": "Example Item", "ID": "123", "Description": "This is an example item"}`.This JSON object succinctly represents the item's attributes through straightforward key-value pairs, without the need to explicitly mention data types. | **Response** | Property | Data type | Description | | ---------------------------------------------------------------------------------------------------------------------- | --------- | ----------- | ----------------------------- | | [Result](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html#API_PutItem_ResponseElements) | object | Yes | The item to add to the table. | #### Delete item **Request** | Property name | Data type | Required | Description | | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -------- | ------------------------------------------------------------ | | [Table name](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DeleteItem.html#DDB-DeleteItem-request-TableName) | string | Yes | The name of the DynamoDB table to delete the item from. | | [Primary Key Components](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.PrimaryKey) | object | Yes | The primary key components that identify the item to delete. | **Response** | Property | Data type | Description | | ---------------------------------------------------------------------------------------------------------------------------------- | --------- | ----------------------------------------------------------------------------- | | [Deleted Item](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DeleteItem.html#API_DeleteItem_ResponseElements) | object | The item that was deleted. This field will be null if the item was not found. | #### Get item **Request** | Property Name | Data type | Required | Description | | --------------------------------------------------------------------------------------------------------------------------------- | --------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [Table name](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_GetItem.html#DDB-GetItem-request-TableName) | string | Yes | The name of the table containing the requested item. | | [Primary key components](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_GetItem.html#DDB-GetItem-request-Key) | object | Yes | A map of attribute names to `AttributeValue` objects, representing the primary key of the item to retrieve. For the primary key, you must provide all the attributes. For example, with a simple primary key, you only need to provide a value for the partition key. For a composite primary key, you must provide values for both the partition key and the sort key. | **Response** | Property | Data type | Description | | ----------------------------------------------------------------------------------------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [Attributes](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_GetItem.html#DDB-GetItem-response-Item) | object | A map of attribute names to `AttributeValue` objects, representing the item retrieved. If there is no matching item, the response will contain only the consumed capacity, and a null attributes field. The keys of the attributes map correspond to the column names of the table | #### Update item **Request** | Property name | Data type | Required | Description | | ---------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------- | | [Table name](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateItem.html#DDB-UpdateItem-request-TableName) | string | Yes | The name of the table to update the item in. | | [Primary key components](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateItem.html#DDB-UpdateItem-request-Key) | map | Yes | A map of attribute names to `AttributeValue` objects, representing the primary key of the item to update. | | [Key attributes](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateItem.html#DDB-UpdateItem-request-AttributeUpdates) | map | Yes | A map of attribute names to `AttributeValue` objects, representing the attributes to update. | | [Attribute action](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateItem.html#DDB-UpdateItem-request-AttributeUpdates) | dropdown | No | Dropdown option for each attribute to be updated, allowing selection between "PUT" (add or replace) and "DELETE" (remove) | **Response** | Property | Data type | Description | | --------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [UpdateItemOutcome](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBMapper.Methods.html#DynamoDBMapper.Methods.updateItem) | object | An object representing the outcome of the `UpdateItem` operation. The `UpdateItemOutcome` object contains the updated attributes of the item, as well as other metadata about the operation, such as the consumed capacity. | ## Request example | Section | Field | Description | Example value | | -------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | | Operation | Category | Choose the category of the operation to be performed. | Item | | | Action | Select the specific action to update an item in the DynamoDB table. | Update item | | Authentication | Authentication type | The method of AWS authentication; credentials are used here. | Credentials | | | Access key | An example of an AWS access key. | `AKIAU3GOTH...JBYX` | | | Secret key | An example of an AWS secret key. | `bZ/LPpqaw...0igikS` | | | Region | The AWS region where the DynamoDB table is located. | `us-east-1` | | Input | Table name | The name of the DynamoDB table to be updated. | `test` | | | Primary key components | The primary key component(s) of the item to be updated. | `{"id": "5"}` | | | Key attributes | JSON object representing the new values for the item attributes. | `{ "stringValue": "StringValue", "numberValue": 42, "booleanValue": true }` | | | Attribute action | The action to be performed on the attributes. Here it's set to PUT, which means the specified attributes will be added or updated. | PUT | | Output mapping | Result variable | The name of the variable that will store the response from DynamoDB. | `result` | | | Result expression | The FEEL expression used to map the DynamoDB response to process variables. Not provided in the screenshots. | - | ## Response Mapping When using the DynamoDB connector, the response from the DynamoDB connector will be available in a temporary local `response` variable. This variable can be mapped to the process by specifying the **Result Variable**. For example, if you use the **Update Item** method in the DynamoDB connector, the response may look like this: ```json { "action": "updateItem", "status": "OK", "response": { "Attributes": { "ID": { "N": "3" }, "price": { "N": "10" } } } } ``` In this example, the `response` variable contains an `Attributes` object with the updated values for the specified item. The following fields are available in the `response` variable: - `action`: The action that was performed by the DynamoDB connector. - `status`: The status of the response, which will be "OK" if the operation was successful. - `response`: The response from the DynamoDB service, which will contain the updated attributes of the specified item. You can choose to unpack the content of your `response` into multiple process variables using the **Result Expression**, which is a [FEEL Context Expression](/components/modeler/feel/language-guide/feel-context-expressions.md). The **Result Expression** allows you to access specific attributes from the response and assign them to process variables that can be used in subsequent steps of your process. ```feel = { id: response.response.Attributes.ID.N, price: response.response.Attributes.price.N } ``` In this example, we are using the **Result Expression** to extract the **ID** and **price** attributes from the response variable and assign them to the ID and price process variables, respectively. You can then use these variables in subsequent steps of your process. :::note The syntax for accessing attributes in the **Result Expression** may vary depending on the structure of your response object. You can refer to the [FEEL Context Expression](/components/modeler/feel/language-guide/feel-context-expressions.md) documentation for more information on how to use the **Result Expression**. ::: ## Error handling The **Amazon DynamoDB connector** may throw the following exceptions: - AwsDynamoDbConnectionException: Thrown if there is an error connecting to DynamoDB. - AwsDynamoDbExecutionException: Thrown if there is an error executing a DynamoDB operation. - AwsDynamoDbConfigurationException: Thrown if the connector is not properly configured. All of these checked exceptions are wrapped in a `RuntimeException`, so be prepared to handle this type of exception as well. ## Troubleshooting If you are having issues with the **Amazon DynamoDB connector**, try the following: - Ensure your AWS credentials are correct. - Ensure your DynamoDB table exists and is located in the specified region. - Ensure your configuration properties are set correctly. - Check the logs for any error messages. - Contact (Camunda support)[https://camunda.com/services/support/] if you need further assistance. For more information on Amazon DynamoDB, visit the [official documentation](https://docs.aws.amazon.com/dynamodb/). ## Using DynamoDB connector best practice When using the DynamoDB connector in a BPMN process, it is important to keep in mind that there is no guarantee that a requested item will be retrieved or updated immediately. In this case, it is recommended to build your BPMN diagram to periodically retry polling until the item is available. :::note To avoid performance issues, it is recommended to limit the number of retries. ::: To learn more about implementing retry logic in your BPMN diagram, you can refer to the [Camunda BPMN examples](https://camunda.com/bpmn/examples/) page, which includes examples of BPMN diagrams with timer and loop configurations. ## Appendix ### AWS authentication types There are two options to authenticate the connector with AWS: - Choose **Credentials** in the **Authentication** dropdown if you have a valid pair of access and secret keys provided by your AWS account administrator. This option is applicable for both SaaS and Self-Managed users. - Choose **Default Credentials Chain (Hybrid/Self-Managed only)** in the **Authentication** dropdown if your system is configured as an implicit authentication mechanism, such as role-based authentication, credentials supplied via environment variables, or files on target host. This option is applicable only for Self-Managed or hybrid distribution. This approach uses the [Default Credential Provider Chain](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html) to resolve required credentials. --- ## Amazon EventBridge connector The **Amazon EventBridge connector** integrates your BPMN service with [Amazon EventBridge](https://aws.amazon.com/eventbridge/), enabling the sending of events from your workflows for further processing or routing to other AWS services. It provides seamless event-driven integration within your business processes. For more information, refer to the [Amazon EventBridge documentation](https://docs.aws.amazon.com/eventbridge/index.html). ## Prerequisites Before using the **Amazon EventBridge connector**, ensure you have the necessary permissions in your AWS account to send events to EventBridge. You will need an access key and secret key of a user with the appropriate permissions. Refer to the [AWS documentation](https://docs.aws.amazon.com/eventbridge/latest/userguide/auth-and-access-control-eventbridge.html) for more information. :::note Use Camunda secrets to avoid exposing your AWS IAM credentials as plain text. Refer to our documentation on [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ::: ## Create an Amazon EventBridge connector task ## Configure the Amazon EventBridge connector Follow these steps to configure the Amazon EventBridge connector: 1. Choose an applicable authentication type from the **Authentication** dropdown. Learn more about authentication types in the related [appendix entry](#aws-authentication-types). 2. In the **Authentication** section, enter the relevant IAM key and secret pair of the user with permissions to send events to [Amazon EventBridge](https://aws.amazon.com/eventbridge). 3. In the **Configuration** section, specify the AWS region where your EventBridge resides. 4. In the **Event Details** section, provide the following information: - **Event bus name**: Enter the name of the destination event bus. Refer to the [Amazon EventBridge documentation](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-create-event-bus.html) for more details on event buses. - **Source**: Enter the value that identifies the service that generated the event. - **Detail type**: Enter the type of event being sent. Refer to the [Amazon documentation](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-events-structure.html) for more information on these properties. 5. In the **Event Payload** section, enter a JSON object that contains information about the event. 6. (Optional) In the **Output Mapping** section, you can set a **Result variable** or **Result expression**. Refer to the [response mapping documentation](/components/connectors/use-connectors/index.md#response-mapping) to learn more. 7. (Optional) In the **Error Handling** section, define the **Error expression** to handle errors that may occur during the event sending process. Refer to the [response mapping documentation](/components/connectors/use-connectors/index.md#bpmn-errors-and-failing-jobs) to learn more. ## Amazon EventBridge connector response The **Amazon EventBridge connector** returns the [original response](https://docs.aws.amazon.com/eventbridge/latest/APIReference/API_PutEvents.html) from the Amazon EventBridge service, including the **sdkResponseMetadata** and **sdkHttpMetadata**. Here is an example of the response: ```json { "sdkResponseMetadata": { "requestId": "766647a2-835a-418b-9161-94245d0c93a3" }, "sdkHttpMetadata": { "httpHeaders": { "Content-Length": "85", "Content-Type": "application/x-amz-json-1.1", "Date": "Fri, 23 Jun 2023 08:39:22 GMT", "x-amzn-RequestId": "766647a2-835a-418b-9161-94245d0c93a3" }, "httpStatusCode": 200, "allHttpHeaders": { "x-amzn-RequestId": ["766647a2-835a-418b-9161-94245d0c93a3"], "Content-Length": ["85"], "Date": ["Fri, 23 Jun 2023 08:39:22 GMT"], "Content-Type": ["application/x-amz-json-1.1"] } }, "failedEntryCount": 0, "entries": [ { "eventId": "bb86b1af-9abb-0f8e-28c2-c69c24c35e05", "errorCode": null, "errorMessage": null } ] } ``` ## Appendix ### AWS authentication types There are two options to authenticate the connector with AWS: - Choose **Credentials** in the **Authentication** dropdown if you have a valid pair of access and secret keys provided by your AWS account administrator. This option is applicable for both SaaS and Self-Managed users. - Choose **Default Credentials Chain (Hybrid/Self-Managed only)** in the **Authentication** dropdown if your system is configured as an implicit authentication mechanism, such as role-based authentication, credentials supplied via environment variables, or files on target host. This option is applicable only for Self-Managed or hybrid distribution. This approach uses the [Default Credential Provider Chain](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html) to resolve required credentials. ## Next steps - [Amazon EventBridge documentation](https://docs.aws.amazon.com/eventbridge/) - Learn about [other connectors available](./available-connectors-overview.md) in Camunda to integrate with different systems and services. - Learn more about [using connectors](../use-connectors/index.md). The **Amazon EventBridge Webhook connector** is an inbound connector enabling you to start a BPMN process instance triggered by an event from [Amazon EventBridge](https://aws.amazon.com/eventbridge/). ## Create an Amazon EventBridge Webhook connector task 1. Start building your BPMN diagram. You can use the **Amazon EventBridge Webhook connector** with either a **Start Event** or an **Intermediate Catch Event** building block. 2. Select the applicable element and change its template to an **Amazon EventBridge connector**. 3. Fill in all required properties. 4. Complete your BPMN diagram. 5. Deploy the diagram to activate the event trigger. ## Configure the Amazon EventBridge Webhook connector ### Fill properties in the Webhook Configuration section 1. Choose one of the required methods in the **Webhook method** property. For example, if you know the webhook will be triggered by the **POST** method, choose **POST**. Alternatively, if it is not essential to specify a specific method for the webhook trigger, select **ANY**. 2. Configure the **Webhook ID**. By default, the **Webhook ID** is pre-filled with a random value. This value will be part of the Webhook URL. For more details about Webhook URLs, refer to the section below on [activating the Amazon EventBridge Webhook connector by deploying your diagram](#activate-the-amazon-eventbridge-connector-by-deploying-your-diagram). 3. (Optional) Fill in the **Event Bus Name** property if you want to specify a specific event bus to subscribe to. If left empty, the default event bus will be used. ### Fill properties in the Authorization section The Amazon EventBridge Webhook connector supports four types of authorization: - **None (without authorization)**: No authentication is required for the webhook. Anyone can trigger the webhook without any credentials. - **JWT (JSON Web Token)**: This authorization type requires the following properties to be filled: - **JWK URL**: A link to the JSON Web Key (JWK) Set containing the public keys used to verify the JWT signature. [Learn more about JWK](https://datatracker.ietf.org/doc/html/rfc7517). - **JWT Role Property Expression** (optional): An expression to extract the roles from the JWT token. These roles will be used to check against the **Required Roles** property. For example, the expression could be: ``` =if admin = true then ["admin"] else roles ``` - **Required Roles** (optional): A list of roles to test JWT roles against. If provided, the webhook will only be triggered if the JWT token contains at least one of the required roles. For example, if the required role is "admin", the property could be: ``` ["admin"] ``` - **Basic**: This authorization type requires the following properties to be filled: - **Username**: The username to authenticate the webhook. - **Password**: The password associated with the provided username. - **API Key**: This authorization type requires the following properties to be filled: - **API Key**: The API key that needs to be provided in the request to authenticate the webhook. - **API Key Locator**: A FEEL expression that extracts the API key from the request. This expression is evaluated in the connector Runtime to retrieve the API key from the incoming request. For example, the API Key Locator could be: ``` =split(request.headers.authorization, " ")[2] ``` or ``` request.headers.mycustomapikey ``` Select the appropriate authorization type based on your security requirements and fill in the corresponding properties accordingly. ### Fill out the properties in the **Activation** and **Correlation** sections 1. (Optional) Configure the **Activation Condition**. This condition will be used to filter the events from the specified event source. For example, if an incoming Amazon EventBridge event has the following body: ``` { "version": "0", "id": "6d3d35b7-5bf2-43ec-9e55-5cfb27ad31b4", "detail-type": "MyEvent", "source": "custom.application", "account": "123456789012", "time": "2023-07-25T12:34:56Z", "region": "us-west-2", "resources": [], "detail": { "shipment": "123456789", "status": "received" } } ``` the Activation Condition value might look like this: ``` =(get value(request.body, "detail-type")="MyEvent" and request.body.detail.status="received") ``` This condition will trigger the Amazon EventBridge Webhook connector only when the detail-type is "MyEvent" and the status is "received". 2. When using the **Amazon EventBridge Webhook connector** with an **Intermediate Catch Event**, fill in the **Correlation key (process)** and **Correlation key (payload)**. - **Correlation key (process)** is a FEEL expression that defines the correlation key for the subscription. This corresponds to the **Correlation key** property of a regular **Message Intermediate Catch Event**. - **Correlation key (payload)** is a FEEL expression used to extract the correlation key from the incoming message. This expression is evaluated in the connector Runtime, and the result is used to correlate the message. - **Message ID expression** and **Message TTL** are optional properties that can be used to set the message ID and time-to-live for the incoming message. Refer to the [message ID expression](#message-id-expression) and [message TTL](#message-ttl) sections for more information. For example, if your correlation key is defined with a process variable named `myCorrelationKey`, and you want to correlate by the `shipment` property in the request detail, which contains: ```json { "version": "0", "id": "6d3d35b7-5bf2-43ec-9e55-5cfb27ad31b4", "detail-type": "MyEvent", "source": "custom.application", "account": "123456789012", "time": "2023-07-25T12:34:56Z", "region": "us-west-2", "resources": [], "detail": { "shipment": "123456789", "status": "received" } } ``` your correlation key settings will look like this: - **Correlation key (process)**: `=myCorrelationKey` - **Correlation key (payload)**: `=request.body.detail.shipment` #### Message ID expression The **Message ID expression** is an optional field that allows you to extract the message ID from the incoming message. The message ID serves as a unique identifier for the message and is used for message correlation. This expression is evaluated in the connector Runtime and the result is used to correlate the message. In most cases, it is not necessary to configure the **Message ID expression**. However, it is useful if you want to ensure message deduplication or achieve a certain message correlation behavior. Learn more about how message IDs influence message correlation in the [messages guide](../../../concepts/messages#message-correlation-overview). For example, if you want to set the message ID to the value of the `detail.transactionId` field in the incoming event, configure the **Message ID expression** as follows: ``` = request.body.detail.transactionId ``` #### Message TTL The **Message TTL** is an optional field that allows you to set the time-to-live (TTL) for the correlated messages. TTL defines the time for which the message is buffered in Zeebe before being correlated to the process instance (if it can't be correlated immediately). The value is specified as an ISO 8601 duration. For example, `PT1H` sets the TTL to one hour. Learn more about the TTL concept in Zeebe in the [message correlation guide](../../../concepts/messages#message-buffering). ## Activate the Amazon EventBridge connector by deploying your diagram Once you click **Deploy**, your Amazon EventBridge Webhook connector will be activated and ready to receive events. The URLs of the exposed Amazon EventBridge Webhooks adhere to the following pattern: `http(s):///webhooks/` - `` is the URL of the connectors component deployment. When using the Camunda 8 SaaS offering, this will typically contain your **region Id** and **cluster Id**, found in your client credentials under the **API** tab within your cluster. - `` is the ID (path) you configured in the properties of your Amazon EventBridge Webhook connector. :::note If you make changes to your Amazon EventBridge Webhook connector configuration, redeploy the BPMN diagram for the changes to take effect. ::: When you click on the event with the Amazon EventBridge Webhook connector applied to it, a new **Webhooks** tab will appear in the properties panel. This tab displays the URL of the Amazon EventBridge Webhook connector for every cluster where you have deployed your BPMN diagram. :::note The **Webhooks** tab is only supported in Web Modeler as part of the Camunda 8 SaaS offering. You can still use the Amazon EventBridge Webhook connector in Desktop Modeler or with Camunda 8 Self-Managed. In that case, Amazon EventBridge Webhook connector deployments and URLs will not be displayed in Modeler. ::: ## Output mapping The **Output mapping** section allows you to configure the mapping of the event payload to the process variables. - Use the **Result variable** to store the event data in a process variable. For example, `myEventPayload`. - Use the **Result expression** to map specific fields from the event payload into process variables using [FEEL](/components/modeler/feel/what-is-feel.md). For example, given the Amazon EventBridge connector is triggered with an event payload like: ``` { "id": "6d3d35b7-5bf2-43ec-9e55-5cfb27ad31b4", "detail-type": "MyEvent", "source": "custom.application", "region": "us-west-2", "resources": [], "detail": { "event": "order_created", "customer_id": "12345", "order_total": 100.50 } } ``` and you would like to extract the `customer_id` and `order_total` as process variables `customerId` and `orderTotal`, the **Result Expression** might look like this: ``` = { customerId: request.body.detail.customer_id, orderTotal: request.body.detail.order_total } ``` ## Example of configuring Amazon EventBridge To configure Amazon EventBridge, follow the steps below: 1. Go to the [AWS Management Console](https://aws.amazon.com/console/). 2. Set the required permissions for EventBridge by navigating to: https://aws.permissions.cloud/iam/events. 3. Access Amazon EventBridge service by going to [Amazon EventBridge](https://aws.amazon.com/eventbridge/). 4. Click **Integration > API Destination**. 5. Switch to the **Connection** tab. 6. Create a new connection with the required authorization type (basic, API key, OAuth). 7. Now, create a new API destination with the following information: - Select the previously created **connection**. - Choose the appropriate **HTTP method**. - Specify the **API destination endpoint**, which should be the webhook URL generated after deploying the BPMN diagram. 8. Create a new event bus by following the documentation [here](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-create-event-bus.html). 9. Lastly, create a rule using the **API destination** that you already created. Refer to the [documentation](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-get-started.html) for guidance. ## Next steps - Learn more about [Amazon EventBridge](https://aws.amazon.com/eventbridge/) and its capabilities. - Explore other [Connectors available](./available-connectors-overview.md) in Camunda to integrate with different systems and services. - Learn more about using connectors [here](../use-connectors/index.md). - Learn more about inbound connectors [here](../use-connectors/inbound.md). --- ## Amazon S3 connector The **Amazon S3 connector** is an outbound connector that allows you to interact with [Amazon Simple Storage Service (Amazon S3)](https://aws.amazon.com/S3/) from your BPMN process. ## Prerequisites To use the **Amazon S3 connector**, you will need an AWS account with an access key and secret key. The key will need the following permissions: - `GetObject` - `DeleteObject` - `PutObject` Learn more about Amazon S3 in the [Amazon Simple Storage Service Documentation](https://docs.aws.amazon.com/s3/). :::note Use Camunda secrets to store credentials and avoid exposing sensitive information from the process. See [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md). ::: ## Create an Amazon S3 connector task ## Authentication Select an authentication type from the **Authentication** dropdown. - **Credentials** (SaaS/Self-Managed): Select this option if you have a valid pair of access and secret keys provided by your AWS account administrator. This option is supported for both SaaS and Self-Managed users. - **Default Credentials Chain** (Hybrid/Self-Managed only): Select this option if your system is configured as an implicit authentication mechanism, such as role-based authentication, credentials supplied via environment variables, or files on target host. This option is only supported for Self-Managed or hybrid distributions. This approach uses the [Default Credential Provider Chain](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html) to resolve required credentials. If you select **Credentials**, you must supply the appropriate credentials: - **Access key**: Provide an access key of a user with permissions to the Amazon S3 actions. - **Secret key**: Provide the secret key of the user with the access key provided above. :::note The **Access key** and the **Secret key** are required properties and must be provided to use the connector. ::: ## Region In the **Region** field, enter the region of the deployed endpoint. ## Action The Amazon S3 connector supports the following actions: - `Upload Document` - `Download Document` - `Delete Document` ### Upload Document Upload a document. The incoming document must be a reference from the previous process. #### Parameters | Parameter | Description | | :----------- | :----------------------------------------------------------------------------------------------------------------------------------------------- | | `AWS bucket` | The targeted AWS S3 bucket where the document should be uploaded. | | `AWS key` | (Optional) The key of the document that uniquely identifies the object in an Amazon S3 bucket. Will fallback to the document filename if not set | | `Document` | The document that should be uploaded to S3, provided as a FEEL expression with the document reference. | :::info To learn more about Friendly Enough Expression Language (FEEL) expressions, see [what is FEEL?](/components/modeler/feel/what-is-feel.md). ::: #### Response Structure The following JSON response is returned after a successful document upload operation: - `bucket`: Echoes back the bucket of the uploaded document. - `key`: Echoes back the unique key of the uploaded document. - `link`: The document link. :::note Starting from version 8.7.0, the Amazon S3 connector supports uploading documents from (or downloading documents to) the Camunda document store. Review the **Document** field in the properties panel where the document reference can be provided. See additional details and limitations in [document handling](/components/document-handling/getting-started.md). ::: #### Example Response The following example shows a successful send upload operation response: ```json { "bucket": "Example Subject", "key": true, "link": "https://mybucket.s3.amazonaws.com/test" } ``` ### Download Document Download the document from AWS S3. #### Parameters | Parameter | Description | | :---------------- | :-------------------------------------------------------------------------------------------------------------------------- | | `AWS bucket` | The targeted AWS S3 bucket that the document should be downloaded from. | | `AWS key` | The key of the document that uniquely identifies the object in an Amazon S3 bucket. | | `Create document` | Either `false` or `true`. If `false`, no document is created and the output is inserted as JSON, text, or base64 raw bytes. | :::info To learn more about Friendly Enough Expression Language (FEEL) expressions, see [what is FEEL?](/components/modeler/feel/what-is-feel.md). ::: #### Response Structure The following JSON response is returned after a successful document download operation: - `bucket`: Echoes back the bucket of the uploaded document. - `key`: Echoes back the unique key of the uploaded document. - `element`: Represents the document in the workflow. The behavior changes based on the `Create document` setting: - If `Create document` is set to `false`: - For `String` content type: `element` will contain the string content of the document. - For `Json` content type: `element` will contain the JSON content of the document. - For other content types: `element` will contain the raw bytes of the document, encoded in base64. - If `Create document` is set to `true`: - `element` will contain a document reference, rather than the document content itself. #### Example Response The following examples show a successful download operation response: ```json { "bucket": "Example Subject", "key": true, "document": { "storeId": "in-memory", "documentId": "20f1fd6a-d8ea-403b-813c-e281c1193495", "metadata": { "contentType": "image/webp; name=305a4816-b3df-4724-acd3-010478a54add.webp", "size": 311032, "fileName": "305a4816-b3df-4724-acd3-010478a54add.webp" }, "camunda.document.type": "camunda" }, "content": null } ``` or ```json { "bucket": "Example Subject", "key": true, "document": null, "content": { "json": { "testKey": "testValue" } } } ``` ### Delete Document Delete the document from AWS S3. #### Parameters | Parameter | Description | | :----------- | :---------------------------------------------------------------------------------- | | `AWS bucket` | The targeted AWS S3 bucket that the document should be deleted from. | | `AWS key` | The key of the document that uniquely identifies the object in an Amazon S3 bucket. | :::info To learn more about Friendly Enough Expression Language (FEEL) expressions, see [what is FEEL?](/components/modeler/feel/what-is-feel.md). ::: #### Response Structure The following JSON response is returned after a successful document deletion operation: - `bucket`: Echoes back the bucket of the uploaded document. - `key`: Echoes back the unique key of the uploaded document. #### Example Response The following example shows a successful deletion operation response: ```json { "bucket": "Example Subject", "key": "document.txt" } ``` --- ## Amazon SageMaker connector :::info The **Amazon SageMaker connector** is available for `8.6.0-alpha2` or later. ::: The **Amazon SageMaker connector** is an outbound connector that allows you to interact with [Amazon SageMaker](https://aws.amazon.com/sagemaker/) from your BPMN process. ## Prerequisites To use the **Amazon SageMaker connector**, you need to have an AWS account with an access key and secret key to execute [`InvokeEndpoint`](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_runtime_InvokeEndpoint.html) or [`InvokeEndpointAsync`](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_runtime_InvokeEndpointAsync.html) actions. The necessary endpoints must be deployed beforehand and point to the operational model. Learn more on how to deploy real-time and asynchronous models in the [official SageMaker documentation](https://docs.aws.amazon.com/sagemaker/latest/dg/how-it-works-deployment.html). :::note Use Camunda secrets to store credentials and avoid exposing sensitive information directly from the process. Refer to [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ::: ## Create an Amazon SageMaker connector task ## Make your Amazon SageMaker connector executable To work with the **Amazon SageMaker connector**, fill all mandatory fields. ## Authentication Choose an applicable authentication type from the **Authentication** dropdown. Learn more about authentication types in the related [appendix entry](#aws-authentication-types). If you select **Credentials** to access the **Amazon SageMaker connector**, the connector requires the appropriate credentials. The following authentication options are available: - **Access key**: Provide an access key of a user with permissions to the Amazon SageMaker `InvokeEndpoint` and/or `InvokeEndpointAsync` actions. - **Secret key**: Provide the secret key of the user with the access key provided above. The **Access Key** and the **Secret Key** are required properties and must be provided to use the connector. For more information on authentication and security in Amazon SageMaker, refer to the [AWS Knowledge Center post](https://repost.aws/knowledge-center/sagemaker-minimum-permissions). ## Region In the **Region** field write the region of the deployed endpoint. ## Inference type Learn more about inferences at the [official Amazon SageMaker documentation page](https://docs.aws.amazon.com/sagemaker/latest/dg/deploy-model.html). ### Real-time Ensure you deployed your model with a real-time endpoint. - In the **Inference type** field, select **Real-time**. - In the **Endpoint name** field, enter your deployed real-time endpoint name. - In the **Payload** field, enter data that is required by your deployed model. - In the **Content type** field, enter the content type of the input. Be aware that the **Amazon SageMaker connector** currently supports only JSON-like content types. - In the **Accept** field, enter the content type of the return value. - Fill in the other fields as described in the [SageMaker documentation](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_runtime_InvokeEndpoint.html). ### Asynchronous - In the **Inference type** field, select **Asynchronous**. - In the **Endpoint name** field, enter your deployed asynchronous endpoint name. - In the **Input location** field, enter an S3 URL where the inference payload is stored. - In the **Content type** field, enter the content type of the input. Unlike the real-time, this inference supports any kind of content type. - In the **Accept** field, enter the content type of the return value. - Fill in the other fields as described in the [SageMaker documentation](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_runtime_InvokeEndpointAsync.html). ## Handle connector response ## Amazon SageMaker connector response The response of the **Amazon SageMaker connector** depends on the model deployed and an inference type. You can use an output mapping to map the response: 1. Use **Result Variable** to store the response in a process variable. For example, `myResultVariable`. 2. Use **Result Expression** to map fields from the response into process variables. ### Real-time inference response The response of the real-time inference depends on your model deployed. Refer to the [Amazon SageMaker documentation](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_runtime_InvokeEndpoint.html#API_runtime_InvokeEndpoint_ResponseElements) to learn more. ### Asynchronous inference response The response of the real-time inference depends on your model deployed. Refer to the [AWS SageMaker documentation](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_runtime_InvokeEndpointAsync.html#API_runtime_InvokeEndpointAsync_ResponseElements) to learn more. ## AWS authentication types There are two options to authenticate the connector with AWS: - Choose **Credentials** in the **Authentication** dropdown if you have a valid pair of access and secret keys provided by your AWS account administrator. This option is applicable for both SaaS and Self-Managed users. - Choose **Default Credentials Chain (Hybrid/Self-Managed only)** in the **Authentication** dropdown if your system is configured as an implicit authentication mechanism, such as role-based authentication, credentials supplied via environment variables, or files on target host. This option is applicable only for Self-Managed or hybrid distribution. This approach uses the [Default Credential Provider Chain](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html) to resolve required credentials. --- ## Amazon Simple Notification Service connector The **Amazon Simple Notification Service (SNS)** connector is an outbound connector that allows you to connect your BPMN service with [Amazon Simple Notification Service (SNS)](https://aws.amazon.com/sns/) to send messages. ## Prerequisites To use the **Amazon SNS connector**, you need to have an SNS Topic, IAM key, and secret pair with the `sns:Publish` policy relative to your SNS. :::note Use Camunda secrets to avoid exposing your AWS IAM credentials as plain text. Refer to our [appendix entry](#how-do-i-store-aws-iam-secrets-for-my-sns-connector) and the [SNS Developer Guide](https://docs.aws.amazon.com/sns/latest/dg/sns-using-identity-based-policies.html#sns-policy-keys) to learn more. ::: ## Create an Amazon SNS connector task ## Make your Amazon SNS connector for sending messages executable To make your Amazon SNS connector for sending messages executable, take the following steps: 1. Choose an applicable authentication type from the **Authentication** dropdown. Learn more about authentication types in the related [appendix entry](#aws-authentication-types). 2. Set the relevant IAM key and secret pair in the **Authentication** section. For example, `{{secrets.MY_AWS_ACCESS_KEY}}`. The value can be plain text, but this is not recommended due to security concerns. 3. In the **Topic Properties** section, set the topic ARN of your SNS topic as well as its region. 4. In the **Input message data** section, fill out the field **Message** with the data you would like to publish to the topic. The field requires FEEL input. 5. (Optional) In the **Input message data** section, fill out the field **Message attributes** to set optional message metadata. This field requires FEEL input. Refer to the relevant [appendix](#what-are-the-message-attributes-and-how-can-i-set-them) section to find out more about this field. 6. (Optional) In the **Input message data** section, fill out the field **Subject** to set optional message subject. FEEL input of the field is optional. Length must be less than 100 characters. 7. (FIFO only) For a FIFO type topic in Amazon SNS, a **Message Group ID** is required. This ID ensures that messages within the same group are delivered in sequence. The [Amazon SNS documentation on FIFO topics](https://docs.aws.amazon.com/sns/latest/dg/sns-fifo-topics.html) provides more details on Message Group ID usage. Additionally, an optional **Message Deduplication ID** can be provided. This is useful for message deduplication in FIFO topics and its necessity depends on the [deduplication settings of your SNS FIFO topic](https://docs.aws.amazon.com/sns/latest/dg/sns-message-deduplication.html). The Message Deduplication ID helps ensure Amazon SNS does not resend the same message within the deduplication interval. ## Amazon SNS connector response The **Amazon SNS connector** returns the SNS message identifier of a newly created message. The response contains a `messageId` variable. You can use an output mapping to map the response: 1. Use **Result Variable** to store the response in a process variable. For example, `myResultVariable`. 2. Use **Result Expression** to map fields from the response into process variables. For example: ``` = { "createdMessageID": response.messageId } ``` ## Appendix & FAQ ### What are the message attributes and how can I set them? Amazon SNS lets you include structured metadata (such as timestamps, geospatial data, signatures, and identifiers) with messages using message attributes. The **Amazon SNS connector** allows you to include non-binary message attributes in the **Input message data** section. The message attribute value must be composed to be compliant with Amazon SNS [message attribute data format](https://docs.aws.amazon.com/sns/latest/dg/sns-message-attributes.html). Example of a valid message attribute as a FEEL value: ``` = { "timestamp":{ "StringValue":today(), "DataType":"String" }, "messageSubmittedBy":{ "StringValue":"user12345", "DataType":"String" } } ``` ### How do I store AWS IAM secrets for my SNS connector? Use Camunda secrets to avoid exposing your AWS IAM credentials. Follow our documentation on [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ### AWS authentication types There are two options to authenticate the connector with AWS: - Choose **Credentials** in the **Authentication** dropdown if you have a valid pair of access and secret keys provided by your AWS account administrator. This option is applicable for both SaaS and Self-Managed users. - Choose **Default Credentials Chain (Hybrid/Self-Managed only)** in the **Authentication** dropdown if your system is configured as an implicit authentication mechanism, such as role-based authentication, credentials supplied via environment variables, or files on target host. This option is applicable only for Self-Managed or hybrid distribution. This approach uses the [Default Credential Provider Chain](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html) to resolve required credentials. The **Amazon Simple Notification Service (SNS) inbound connector** is a connector that allows you to start or continue a BPMN process triggered by an [Amazon SNS](https://console.aws.amazon.com/sns/home) notification. ## Create an Amazon SNS inbound connector task 1. Start building your BPMN diagram. You can use the **Amazon SNS inbound connector** with either a **Start Event** or **Intermediate Catch Event**. 2. Select the applicable element and change its template to an **Amazon SNS connector**. 3. Fill in all required properties. 4. Complete your BPMN diagram. 5. Deploy the diagram to activate the webhook. 6. Navigate to the **Webhooks** tab in the properties panel on the right side of the screen to observe the webhook URL. ## Make your Amazon SNS inbound connector for receiving notifications executable 1. In the **Subscription Configuration** section, configure the **Subscription ID**. By default, **Subscription ID** is pre-filled with a random value. This value will be a part of the topic subscription URL. 2. Set the **Allow to receive messages from topic(s)** value to **any** if the process may be triggered by any topic, or **Specific topic(s)** if you wish to allow-list only certain topics to start a new BPMN process. 3. If you have chosen the **Specific topic(s)**, you have to list comma-separated topics in the field **Topic ARN(s)** as well. In that case, the **Amazon SNS inbound connector** will auto-approve each qualified subscription request. 4. In the section **Activation**, configure **Condition** when the Amazon SNS topic can trigger a new BPMN process. The following example will trigger a new BPMN process for every notification with a subject _Start BPMN_: `=(request.body.Subject = "Start BPMN")`. 5. In the **Output mapping** section, fill in the **Result variable** field to store the response in a process variable. For example, `myResultVariable`. Alternatively, fill in the **Result expression** field to map specific fields from the response into process variables using [FEEL](/components/modeler/feel/what-is-feel.md). The following example will extract both the message and subject from an Amazon SNS message: `={message: request.body.Message, subject: request.body.Subject}`. ### Correlation The **Correlation** section allows you to configure the message correlation parameters. :::note The **Correlation** section is not applicable for the plain **start event** element template of the Amazon SNS connector. Plain **start events** are triggered by process instance creation and do not rely on message correlation. ::: #### Correlation key - **Correlation key (process)** is a FEEL expression that defines the correlation key for the subscription. This corresponds to the **Correlation key** property of a regular **Message Intermediate Catch Event**. - **Correlation key (payload)** is a FEEL expression used to extract the correlation key from the incoming message. This expression is evaluated in the connector Runtime and the result is used to correlate the message. For example, given that your correlation key is defined with `myCorrelationKey` process variable, and the request body contains `"MessageAttributes": {"attrName1" : {"Type":"String","Value":"attrVal"}}`, your correlation key settings will look like this: - **Correlation key (process)**: `=myCorrelationKey` - **Correlation key (payload)**: `=request.body.MessageAttributes.attrName1.Value` Learn more about correlation keys in the [messages guide](../../../concepts/messages). #### Message ID expression The **Message ID expression** is an optional field that allows you to extract the message ID from the incoming message. Message ID serves as a unique identifier for the message and is used for message correlation. This expression is evaluated in the connector Runtime and the result is used to correlate the message. In most cases, it is not necessary to configure the **Message ID expression**. However, it is useful if you want to ensure message deduplication or achieve certain message correlation behavior. Learn more about how message IDs influence message correlation in the [messages guide](../../../concepts/messages#message-correlation-overview). For example, if you want to set the message ID to the value of the `attrName1` attribute in the incoming event, configure the **Message ID expression** as follows: ``` = request.body.MessageAttributes.attrName1.Value ``` #### Message TTL The **Message TTL** is an optional field that allows you to set the time-to-live (TTL) for the correlated messages. TTL defines the time for which the message is buffered in Zeebe before being correlated to the process instance (if it can't be correlated immediately). The value is specified as an ISO 8601 duration. For example, `PT1H` sets the TTL to one hour. Learn more about the TTL concept in Zeebe in the [message correlation guide](../../../concepts/messages#message-buffering). ## Activate the Amazon SNS inbound connector by deploying your diagram Once you click the **Deploy** button, your **Amazon SNS inbound connector** will be activated and publicly available. URLs of the exposed **Amazon SNS Inbound connector** adhere to the following pattern: `https:///inbound/` - `` is the URL of connectors component deployment. When using the Camunda 8 SaaS offering, this will typically contain your **region Id** and **cluster Id**, found in your client credentials under the **API** tab within your cluster. - `` is the ID (path) you configured in the properties of your **Amazon SNS inbound connector**. If you make changes to your **Amazon SNS inbound connector** configuration, you need to redeploy the BPMN diagram for the changes to take effect. When you click on the event with **Amazon SNS inbound connector** applied to it, a new **Webhooks** tab will appear in the properties panel. This tab displays the URL of the **Amazon SNS inbound connector** for every cluster where you have deployed your BPMN diagram. :::note The **Webhooks** tab is only supported in Web Modeler as part of the Camunda 8 SaaS offering. You can still use Amazon SNS inbound connectors in Desktop Modeler, or with Camunda 8 Self-Managed. In that case, Amazon SNS inbound connector deployments and URLs will not be displayed in Modeler. ::: ## Wiring with Amazon SNS 1. Sign in to the [Amazon SNS console](https://console.aws.amazon.com/sns/home). 2. On the navigation panel, choose **Topics**. 3. Choose the **Create** subscription. 4. In the **Protocol** drop-down list, select **HTTPS**. 5. In the **Endpoint** box, paste in the URL of the subscription found in at the **Webhooks** tab of your BPMN diagram that you want the topic to send messages. Then, choose **Create subscription**. 6. The confirmation message is displayed. Choose **Close**. Your new subscription's **Subscription ID** displays **PendingConfirmation**. Shortly after it will be confirmed by the BPMN process assuming **Allow to receive messages from topic(s)** contains the SNS topic ARN. ## Security considerations ### Access control The field **Allow to receive messages from topic(s)** and related **Topic ARN(s)** allows you to control which Amazon SNS topics can trigger a BPMN process. You can also achieve the same outcome by specifying **Condition** in the **Activation** section. For example, given **Topic ARN(s)** equals `arn:aws:sns:eu-central-1:1234567890:SNSWebhook`, is the same as **Condition** equals `=(request.body.TopicArn = "arn:aws:sns:eu-central-1:1234567890:SNSWebhook")`. ### Integrity Each Amazon SNS message is digitally signed with an AWS private key. The body of a message contains a digital signature of the entire content. The **Amazon Simple Notification Service (SNS) Inbound connector** verifies every message against the Amazon SNS public certificate to ensure the message is of known origin and has not been tampered with. ## Appendix ### Amazon SNS Subscription message example ``` POST https:///inbound/ connection: close accept-encoding: gzip,deflate user-agent: Amazon Simple Notification Service Agent host: content-length: 9999 content-type: text/plain; charset=UTF-8 x-amz-sns-topic-arn: arn:aws:sns:eu-central-1:1234567890:SNSWebhook x-amz-sns-message-id: b9b4574f-b4ab-4c03-ac14-a3145896747f x-amz-sns-message-type: SubscriptionConfirmation { "Type": "SubscriptionConfirmation", "MessageId": "b9b4574f-b4ab-4c03-ac14-a3145896747f", "Token": "233...18b", "TopicArn": "arn:aws:sns:eu-central-1:1234567890:SNSWebhook", "Message": "You have chosen to subscribe to the topic arn:aws:sns:eu-central-1:1234567890:SNSWebhook.\nTo confirm the subscription, visit the SubscribeURL included in this message.", "SubscribeURL": "https://sns.eu-central-1.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:eu-central-1:1234567890:SNSWebhook&Token=233...18b", "Timestamp": "2023-04-26T15:04:47.883Z", "SignatureVersion": "1", "Signature": "u+0i/F/+qew...zw==", "SigningCertURL": "https://sns.eu-central-1.amazonaws.com/SimpleNotificationService-56e67fcb41f6fec09b0196692625d385.pem" } ``` ### Amazon SNS Notification message example ``` POST https:///inbound/ connection: close accept-encoding: gzip,deflate user-agent: Amazon Simple Notification Service Agent host: webhook.site content-length: 1046 x-amzn-trace-id: Root=1-64493ecd-dcfadf2f053429acb884eee3;Sampled=1 content-type: text/plain; charset=UTF-8 x-amz-sns-subscription-arn: arn:aws:sns:eu-central-1:1234567890:SNSWebhook:4aa14ec3-a492-4a8e-8247-ea658d1aad96 x-amz-sns-topic-arn: arn:aws:sns:eu-central-1:1234567890:SNSWebhook x-amz-sns-message-id: 2e062e6b-a527-5e68-b69b-72a8e42add60 x-amz-sns-message-type: Notification { "Type" : "Notification", "MessageId" : "2e062e6b-a527-5e68-b69b-72a8e42add60", "TopicArn" : "arn:aws:sns:eu-central-1:1234567890:SNSWebhook", "Subject" : "Subject - test", "Message" : "Hello, world", "Timestamp" : "2023-04-26T15:10:05.479Z", "SignatureVersion" : "1", "Signature" : "a2w...A==", "SigningCertURL" : "https://sns.eu-central-1.amazonaws.com/SimpleNotificationService-56e67fcb41f6fec09b0196692625d385.pem", "UnsubscribeURL" : "https://sns.eu-central-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:eu-central-1:1234567890:SNSWebhook:4aa14ec3-a492-4a8e-8247-ea658d1aad96", "MessageAttributes" : { "attrName1" : {"Type":"String","Value":"attrVal"} } } ``` --- ## Amazon Simple Queue Service connector The **Amazon SQS connector** is an outbound connector that allows you to connect your BPMN service with [Amazon Simple Queue Service (SQS)](https://aws.amazon.com/sqs/) to send messages. ## Prerequisites To use the **Amazon SQS connector**, you need to have an SQS Queue, IAM key, and secret pair with the `sqs:SendMessage` policy relative to your SQS. :::note Use Camunda secrets to avoid exposing your AWS IAM credentials as plain text. Refer to an [appendix entry](#how-do-i-store-aws-iam-secrets-for-my-sqs-connector) and the [SQS Developer Guide](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-basic-examples-of-iam-policies.html) to learn more. ::: ## Create an Amazon SQS connector task ## Make your Amazon SQS connector for sending messages executable To make your Amazon SQS connector for sending messages executable, take the following steps: 1. Choose an applicable authentication type from the **Authentication** dropdown. Learn more about authentication types in the related [appendix entry](#aws-authentication-types). 2. Set the relevant IAM key and secret pair in the **Authentication** section. For example, `{{secrets.MY_AWS_ACCESS_KEY}}`. The value can be plain text, but this is not recommended due to security concerns. 3. In the **Queue Properties** section, set the URL of your SQS queue, its region, and its type. 4. In the **Input message data** section, fill the **Message body** with the data you would like to submit to the queue. The field requires FEEL input. 5. (Optional) In the **Input message data** section, fill out the field **Message attributes** to set optional message metadata. This field requires FEEL input. Refer to the relevant [appendix](#what-are-the-message-attributes-and-how-can-i-set-them) section to find out more about this field. 6. (FIFO only) If you are using a queue of type **FIFO**, a [**Message Group ID** must be provided](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/using-messagegroupid-property.html). An optional **Message Deduplication ID** can be provided as well, depending on how you [configured](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/using-messagededuplicationid-property.html) the message deduplication of the queue. ## Amazon SQS connector response The **Amazon SQS connector** returns the SQS message identifier of a newly created message. The response contains a `messageId` variable. You can use an output mapping to map the response: 1. Use **Result Variable** to store the response in a process variable. For example, `myResultVariable`. 2. Use **Result Expression** to map fields from the response into process variables. For example: ``` = { "createdMessageID": response.messageId } ``` ## Appendix & FAQ ### What are the message attributes and how can I set them? Amazon SQS lets you include structured metadata (such as timestamps, geospatial data, signatures, and identifiers) with messages using message attributes. The **Amazon SQS connector** allows you to include non-binary message attributes in the section **Input message data**. The message attribute value must be composed to be compliant with Amazon SQS [message attribute data format](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-message-metadata.html#sqs-message-attributes). Example of a valid message attribute as a FEEL value: ``` = { "timestamp":{ "StringValue":today(), "DataType":"String" }, "messageSubmittedBy":{ "StringValue":"user12345", "DataType":"String" } } ``` ### How do I store AWS IAM Secrets for my SQS connector? Store your AWS IAM credentials as Camunda secrets to avoid exposing sensitive information. Follow our documentation on [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ### AWS authentication types There are two options to authenticate the connector with AWS: - Choose **Credentials** in the **Authentication** dropdown if you have a valid pair of access and secret keys provided by your AWS account administrator. This option is applicable for both SaaS and Self-Managed users. - Choose **Default Credentials Chain (Hybrid/Self-Managed only)** in the **Authentication** dropdown if your system is configured as an implicit authentication mechanism, such as role-based authentication, credentials supplied via environment variables, or files on target host. This option is applicable only for Self-Managed or hybrid distribution. This approach uses the [Default Credential Provider Chain](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html) to resolve required credentials. The **Amazon SQS Inbound connector** is an inbound connector that allows you to start or continue a BPMN process triggered by [Amazon Simple Queue Service (SQS)](https://aws.amazon.com/sqs/). ## Prerequisites Before using the Amazon SQS inbound connector, ensure you have the following: 1. An active SQS Queue in your AWS account. 2. IAM credentials with the necessary permissions to receive messages from the SQS Queue. Use Camunda secrets to store your AWS IAM credentials securely. Refer to the [Camunda secrets documentation](/components/hub/organization/manage-clusters/manage-secrets.md) for more details. ## Create an SQS inbound connector task To receive messages from Amazon SQS in your process, follow these steps: 1. Start building your BPMN diagram. You can use the **Amazon SNS Inbound connector** with either **Start Event** or **Intermediate Catch Event** building blocks. 2. Select the appropriate element and change its template to an SQS inbound connector. 3. Fill in all the required properties for the connector, such as the AWS region, SQS Queue URL, and the visibility timeout. 4. Complete your BPMN diagram by adding other necessary elements and connectors. 5. Deploy the diagram to activate the SQS Inbound connector. ## Configure the SQS inbound connector To configure the SQS inbound connector and receive messages from your SQS Queue, follow these steps: 1. Choose an applicable authentication type from the **Authentication** dropdown. Learn more about authentication types in the related [appendix entry](#aws-authentication-types-1). 2. Set the relevant IAM key and secret pair in the **Authentication** section. For example, `{{secrets.MY_AWS_ACCESS_KEY}}`. The value can be plain text, but this is not recommended due to security concerns. 3. In the **Queue Properties** section, set the URL of your SQS Queue and its region. 4. In the **Message polling properties** section, set the polling wait time. This is the duration (in seconds) for which the call waits for a message to arrive in the queue before returning. Refer to the [Amazon documentation](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-short-and-long-polling.html) for more details. Be aware that setting the value to 0 will not take effect, as it will automatically be overridden with a value of 1 during runtime. 5. (Optional) In the **Use next attribute names for activation condition** section, set an array of **Attribute names** or **Message attribute name** (e.g., `["attributeName1", "attributeName2"]`) to receive messages from the queue with specific metadata. Alternatively, you can leave it empty to get results with all available attributes. Learn more about message metadata [here](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-message-metadata.html). 6. (Optional) Configure the **Activation Condition**. For example, if an external message has the body `{"messageId": 1, "body": "Hi team", "messageAttributes":{"key":{"stringValue":"value"}}...}`, the **Activation Condition** value might look like `=(messageAttributes.key.stringValue="value")`. Leave this field empty to receive all messages every time. 7. Set the **Output mapping**. For example, to get only the message body, you can set `{resultBody: body}` in the **Result expression** field. Learn more about **Output mapping** [here](../use-connectors/index.md). ### Activation condition **Activation condition** is an optional FEEL expression field that allows for fine-tuning of the connector activation. For example, if an external message has the body `{"messageId": 1, "body": "Hi team", "messageAttributes":{"key":{"stringValue":"value"}}...}`, the **Activation Condition** value might look like `=(messageAttributes.key.stringValue="value")`. Leave this field empty to receive all messages every time. By default, messages with unmatched activation conditions are not deleted from the queue. They become available for consumers again after the visibility timeout expires. You can set up a dead-letter queue where messages are forwarded after a certain number of delivery attempts. You can also configure the Amazon SQS inbound connector to delete messages from the queue if they don't match the activation condition. In this case, the message will not end up in the dead-letter queue. To delete messages that don't match the activation condition, check the **Consume unmatched events** box. | **Consume unmatched events** box | Activation condition | Outcome | | -------------------------------- | -------------------- | ------------------------------------------------------------------------------------------------ | | Checked | Matched | Message is removed from the queue | | Unchecked | Matched | Message is removed from the queue | | Checked | Unmatched | Message is removed from the queue | | Unchecked | Unmatched | Message is not removed from the queue and will be redelivered or placed in the dead-letter queue | ### Correlation The **Correlation** section allows you to configure the message correlation parameters. :::note The **Correlation** section is not applicable for the plain **start event** element template of the Amazon SQS connector. Plain **start events** are triggered by process instance creation and do not rely on message correlation. ::: #### Correlation key - **Correlation key (process)** is a FEEL expression that defines the correlation key for the subscription. This corresponds to the **Correlation key** property of a regular **Message Intermediate Catch Event**. - **Correlation key (payload)** is a FEEL expression used to extract the correlation key from the incoming message. This expression is evaluated in the connector Runtime and the result is used to correlate the message. Example for correlation and activation condition properties (correlation by ID in the body and activation condition by message attribute): SQS message: ```json { "messageId": "12345", "receiptHandle": "ABCDE", "mD5OfBody": "1c6bb59997376e5182a88a6f582cd92a", "body": { "id": 4567, "value": "Hello world" }, "attributes": { "ApproximateReceiveCount": "1", "SentTimestamp": "1703062074171", "SenderId": "333293239507", "ApproximateFirstReceiveTimestamp": "1703062074185" }, "messageAttributes": { "messageName": { "stringValue": "myProcess", "binaryValue": null, "stringListValues": [], "binaryListValues": [], "dataType": "String" } }, "md5OfMessageAttributes": "9de691a346c79e4fda4af06248aa9dfc" } ``` - **Correlation key (process)**: `=4567` - **Correlation key (payload)**: `=body.id` - **Activation condition**: `=messageAttributes.key.stringValue="myProcess"` Learn more about correlation keys in the [messages guide](../../../concepts/messages). #### Message ID expression The **Message ID expression** is an optional field that allows you to extract the message ID from the incoming message. The message ID serves as a unique identifier for the message and is used for message correlation. This expression is evaluated in the connector Runtime and the result is used to correlate the message. In most cases, it is not necessary to configure the **Message ID expression**. However, it is useful if you want to ensure message deduplication or achieve certain message correlation behavior. Learn more about how message IDs influence message correlation in the [messages guide](../../../concepts/messages#message-correlation-overview). For example, if you want to set the message ID to the value of the `transactionId` field in the incoming message, you can configure the **Message ID expression** as follows: ``` = body.transactionId ``` #### Message TTL The **Message TTL** is an optional field that allows you to set the time-to-live (TTL) for the correlated messages. TTL defines the time for which the message is buffered in Zeebe before being correlated to the process instance (if it can't be correlated immediately). The value is specified as an ISO 8601 duration. For example, `PT1H` sets the TTL to one hour. Learn more about the TTL concept in Zeebe in the [message correlation guide](../../../concepts/messages#message-buffering). ### Deduplication The **Deduplication** section allows you to configure the connector deduplication parameters. Not to be confused with **message deduplication**, **Connector deduplication** is a mechanism in the connector Runtime that determines how many SQS subscriptions are created if there are multiple occurrences of the **Amazon SQS Consumer connector** in the BPMN diagram. By default, the connector runtime deduplicates connectors based on properties, so elements with the same subscription properties only result in one subscription. For details, see [Inbound connector deduplication](../advanced-topics/deduplication.md). To customize the deduplication behavior, check the **Manual mode** checkbox and configure the custom deduplication ID. ## Activate the SQS inbound connector Once you click the **Deploy** button, your SQS inbound connector will be activated and publicly available. Whenever the SQS inbound connector receives a new message, a new BPMN process will be created. ## Amazon SQS connector response The **Amazon SQS connector** provides the SQS message as a response. Utilize output mapping to align this response with process variables: 1. Use **Result Variable** to store the response in a process variable. For example, `myResultVariable`. This approach stores the entire SQS message as a process variable named `myResultVariable`. 2. Use **Result Expression** to map fields from the response into process variables. This approach allows for more granularity. Instead of storing the entire response in one variable, you can extract specific fields from the SQS message and assign them to different process variables. This is particularly useful when you are only interested in certain parts of the message, or when different parts of the message need to be used separately in your process. Example: SQS message : ```json { "messageId": "12345", "receiptHandle": "ABCDE", "mD5OfBody": "1c6bb59997376e5182a88a6f582cd92a", "body": { "id": 4567, "value": "Hello world" }, "attributes": { "ApproximateReceiveCount": "1", "SentTimestamp": "1703062074171", "SenderId": "33333333333", "ApproximateFirstReceiveTimestamp": "1703062074185" }, "messageAttributes": { "messageName": { "stringValue": "myProcess", "binaryValue": null, "stringListValues": [], "binaryListValues": [], "dataType": "String" } }, "md5OfMessageAttributes": "9de691a346c79e4fda4af06248aa9dfc" } ``` To store the entire body in a process variable `resultBody`, ID from body to `bodyId`, and messageId to `messageId`, use: ``` = `{resultBody:body, bodyId:body.id, messageId: messageId}` ``` Learn more about **Variable mapping** [here](../use-connectors/index.md). ## Appendix ### AWS authentication types There are two options to authenticate the connector with AWS: - Choose **Credentials** in the **Authentication** dropdown if you have a valid pair of access and secret keys provided by your AWS account administrator. This option is applicable for both SaaS and Self-Managed users. - Choose **Default Credentials Chain (Hybrid/Self-Managed only)** in the **Authentication** dropdown if your system is configured as an implicit authentication mechanism, such as role-based authentication, credentials supplied via environment variables, or files on target host. This option is applicable only for Self-Managed or hybrid distribution. This approach uses the [Default Credential Provider Chain](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html) to resolve required credentials. ## Next Steps - Explore more about [Amazon Simple Queue Service (SQS)](https://aws.amazon.com/sqs/) and its capabilities. - Learn about [other connectors available](./available-connectors-overview.md) in Camunda to integrate with different systems and services. - Learn more about [using connectors](../use-connectors/index.md). - Learn more about [inbound connectors](../use-connectors/inbound.md). --- ## Amazon Textract connector Integrate Amazon Textract to automatically extract document text and data in your BPMN service. ## About this connector Use this connector to orchestrate Amazon Textract-powered extraction as part of business processes that rely on documents. Using machine learning allows you to read and process any type of document, reducing manual work and increasing accuracy in document-centric processes. The [Amazon Textract](https://aws.amazon.com/textract/) machine learning (ML) service can automatically extract text, handwriting, layout elements, and data from scanned documents. :::tip Camunda marketplace The [Amazon Textract connector](https://marketplace.camunda.com/en-US/apps/473161/amazon-textract-connector) is available in the Camunda marketplace. ::: ## Prerequisites The following prerequisites are required to use this connector: | Prerequisite | Description | | :------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [Amazon Web Services (AWS) IAM user and permissions](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users.html) | A valid AWS Identity and Access Management (IAM) user with permissions configured to allow access to Amazon Textract (and Amazon S3 if used), such as:`AmazonTextractFullAccess`: Required`AmazonS3ReadOnlyAccess`: Required if using Amazon S3 as the document source`AmazonS3FullAccess`: Optional if using Amazon S3 as the output location for asynchronous executionThe [access key pair](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html) (_access key_ and _secret access key_) for this IAM user. This is required for connector authentication. | :::info For Amazon Textract setup instructions, refer to the [Amazon Textract Developer Guide](https://docs.aws.amazon.com/textract/latest/dg/getting-started.html). ::: ## Authentication Select an authentication type from the **Authentication** dropdown. ### Credentials Use AWS authentication. | Property | Type | Required | Description | Example | | :--------- | :----- | :------- | :--------------------------- | :------------------------------- | | Access Key | String | Yes | AWS access key for Textract. | `AKIAIOSFODNN37` | | Secret Key | String | Yes | AWS secret key for Textract. | `wJalrXUtnFEgfMIK7MDENGbPxRfiCY` | :::note Requires your AWS access key and secret access key (see [prerequisites](#prerequisites)). ::: ### Default Credentials Chain (hybrid/Self-Managed only) Use this authentication type if your system relies on implicit authentication (for example, IAM roles, environment variables, or credentials files). Uses the [Default Credential Provider Chain](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html) to resolve credentials. ## Configuration ### Region Configure the AWS region for this connector. | Property | Type | Required | Description | Example | | :------- | :----- | :------- | :-------------------------------------------------------------------------------- | :---------- | | Region | String | Yes | Specify the AWS region where the Textract service and your S3 buckets are hosted. | `us-east-1` | ## Operations ### Analyze Document Analyze documents using Textract. Different input parameters are available depending on the **Execution type** you select. #### Input parameters | Property | Type | Required | Description | Example | | :---------------- | :------- | :--------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------- | | Execution type | Dropdown | Yes | Specify the inference endpoint type:**Real-time**: For small files requiring immediate text extraction. Only single-page PDFs are supported when using S3. For multi-page PDFs, use **Polling** or **Asynchronous**.**Polling**: Starts analysis and polls every five seconds until the result is available. Best for larger documents where blocking execution is acceptable.**Asynchronous**: For large or complex documents processed in the background. | document | | Document location | Dropdown | Yes | Where the document that should be analyzed is stored. S3 is best for most use-cases | S3 | | Document bucket | String | Yes for S3 source | Name of the S3 bucket containing the document. Ensure proper permissions for Textract access. | automation-test | | Document name | String | Yes for S3 source | Full path from the bucket root to the document. | my-document.pdf | | Document version | String | No | Specify if you need to process a specific document version. If not set, the latest version is used. | 5 | | Camunda document | String | Yes for Camunda source | Select the document from the Camunda document store. Only PNG and JPEG formats are supported. Real-time execution only. | document | | Output S3 Bucket | String | Yes for Asynchronous | Output S3 Bucket | automation-output | You must select at least one feature type. Combining multiple options can produce richer extraction results. | Property | Type | Required | Description | Example | | :----------------- | :------ | :------------------------------ | :------------------------------------------------------------------ | :------------------------------- | | Analyze form | Boolean | No | Select this to return information detected form data. | | | Analyze signatures | Boolean | No | Select this to return the locations of detected signatures. | | | Analyze layout | Boolean | No | Select this to return information about the layout of the document. | | | Analyze queries | Boolean | No | Select this to return an answer to a query. | | | Query | String | Yes, if analyze queries is true | The query to be applied to the document. | What is the IBAN in the invoice? | Additional optional parameters for advanced configuration: | Property | Type | Required | Description | Example | |:--------------------|:----------|:---------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------| | Client Request Token | String | No | The idempotent token that you use to identify the start request. | | | Job Tag | String | No | An identifier that you specify that's included in the completion notification published to the Amazon SNS topic. | | | KMS Key ID | String | No | The KMS key used to encrypt the inference results. | | | Notification Channel Role ARN | String | No | The Amazon SNS topic role ARN that you want Amazon Textract to publish the completion status of the operation to. | | | Notification Channel SNS Topic ARN | String | No | The Amazon SNS topic ARN that you want Amazon Textract to publish the completion status of the operation to. | | #### Output The connector response mirrors the [AWS Textract API](https://docs.aws.amazon.com/textract/latest/dg/API_Reference.html), depending on the execution type: - [Real-time Execution Response](https://docs.aws.amazon.com/textract/latest/dg/API_AnalyzeDocument.html#API_AnalyzeDocument_ResponseSyntax) - [Polling Execution Response](https://docs.aws.amazon.com/textract/latest/dg/API_GetDocumentAnalysis.html#API_GetDocumentAnalysis_ResponseSyntax) - [Asynchronous Execution Response](https://docs.aws.amazon.com/textract/latest/dg/API_StartDocumentAnalysis.html#API_StartDocumentAnalysis_ResponseSyntax) To get the answer of the query when using the Analyze queries feature: ```feel = {"answer": response.blocks[item.blockType = "QUERY_RESULT"][1].text} ``` For example, to get the response, when using asynchronous execution, use a timer event for example and retrieve the result with the S3 connector. ## Further Resources - [Amazon Textract connector](https://marketplace.camunda.com/en-US/apps/473161/amazon-textract-connector) in the Camunda marketplace - [Amazon Textract Documentation](https://docs.aws.amazon.com/textract/latest/dg/what-is.html) --- ## Asana connector The Asana connector is an outbound protocol connector that allows you to connect your BPMN service with [Asana](https://asana.com/) to get and create Asana tasks and projects. ## Prerequisites To use the **Asana connector**, you must have an Asana [personal access token](https://developers.asana.com/docs/personal-access-token). :::note Use Camunda secrets to avoid exposing your Asana personal access token as plain text. Refer to our documentation on [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ::: ## Create an Asana connector task ## Authentication In the **Authentication** section, provide a **Personal access token**. [Read more on how to obtain it](https://developers.asana.com/docs/personal-access-token). ## Select operation to execute ### Tasks #### Get tasks from a project - **Asana API:** [Get tasks from a project](https://developers.asana.com/reference/gettasksforproject). - **Project ID:** Globally unique identifier for the project. #### Get a task by ID - **Asana API:** [Get a task](https://developers.asana.com/reference/gettask). - **Task ID:** The task to operate on. #### Create a task - **Asana API:** [Create a task](https://developers.asana.com/reference/createtask). - **Task name:** The name of the task. - **Project ID:** Globally unique identifier for the project. - **Parent task ID:** Globally unique identifier for the parent task. - **Notes:** Free-form textual information associated with the task (i.e. its description). #### Delete a task - **Asana API:** [Delete a task](https://developers.asana.com/reference/deletetask). - **Task ID:** The task to operate on. ### Projects #### Get projects - **Asana API:** [Get multiple projects](https://developers.asana.com/reference/getprojects). - **Workspace ID:** The workspace or organization to filter projects on. - **Team ID:** The team to filter projects on. #### Get a project by ID - **Asana API:** [Get a project](https://developers.asana.com/reference/getproject). - **Project ID:** Globally unique identifier for the project. #### Create a project in a workspace - **Asana API:** [Create a project in a workspace](https://developers.asana.com/reference/createprojectforworkspace). - **Workspace ID:** Globally unique identifier for the workspace or organization. - **Project name:** Name of the project. This is generally a short sentence fragment that fits on a line in the UI for maximum readability. However, it can be longer. - **Project note:** Free-form textual information associated with the project (ie., its description). #### Delete a project - **Asana API:** [Delete a project](https://developers.asana.com/reference/deleteproject). - **Project ID:** Globally unique identifier for the project. ## Handle connector response The **Asana connector** is a protocol connector, meaning it is built on top of the **HTTP REST connector**. Therefore, handling response is still applicable [as described](/components/connectors/protocol/rest.md#response). --- ## Automation Anywhere connector The **Automation Anywhere connector** allows you to orchestrate an Automation Anywhere queue from your BPMN process with [Automation Anywhere RPA](https://www.automationanywhere.com/) to add work items to the queue and obtain work item results. To start using the connector, you need an instance with a [license](https://docs.automationanywhere.com/bundle/enterprise-v2019/page/enterprise-cloud/topics/control-room/dashboards/cloud-administration-licenses.html) we configured via an API service. Refer to the [official documentation page](https://docs.automationanywhere.com/bundle/enterprise-v2019/page/enterprise-cloud/topics/aae-client/bot-creator/using-the-workbench/cloud-install.html) to learn more about installing and configuring the Automation Anywhere API service. You also need a user account with the `AAE_Queue Admin` role to query and manage workload queues and work items in a Control Room. Read more about roles in the [official documentation](https://docs.automationanywhere.com/bundle/enterprise-v2019/page/enterprise-cloud/topics/control-room/administration/roles/cloud-system-created-roles.html). ## Create an Automation Anywhere connector task ## Make your Automation Anywhere connector executable To work with Automation Anywhere, choose the required operation type in the **Operation** section and authentication type in the **Authentication** section and complete the mandatory fields highlighted in red in the connector properties panel on the right side of the screen. :::note All the mandatory and non-mandatory fields depending on the authentication and operation selections you choose are covered in the upcoming sections. ::: ## Authentication You can choose among the available **Automation Anywhere connector** authentication types according to your authentication requirements. :::note We advise you to keep your authentications and secrets data safe and avoid exposing it in the BPMN XML file by creating a secret: 1. Follow our [guide for creating secrets](/components/hub/organization/manage-clusters/manage-secrets.md). 2. Name your secret (i.e `AUTOMATION_ANYWHERE_PASSWORD`) so you can reference it later in the connector. ::: ### _Authenticate (username and password)_ authentication Select the **Automation Anywhere connector** and fill out the following properties under the **Authentication** section: 1. Select **Authenticate (username and password)** in the **Authentication** section. 2. Set **Password** to `Password` to the secret you created (i.e. `{{secrets.AUTOMATION_ANYWHERE_PASSWORD}}`). 3. Set **Username** to `Username` to the secret you created (i.e. `{{secrets.AUTOMATION_ANYWHERE_UESRNAME}}`). 4. Select needed **Multiple login** type. If this value is set to `true`, you will be allowed multiple API sessions. For more information on multi-login, refer to [multi-login user](https://docs.automationanywhere.com/bundle/enterprise-v2019/page/enterprise-cloud/topics/control-room/administration/users/cloud-multi-login-user.html). ### _Authenticate (username and API key)_ authentication Select the **Automation Anywhere connector** and fill out the following properties under the **Authentication** section: 1. Select **Authenticate (username and API key)** in the **Authentication** section. 2. Set **Password** to `Password` to the secret you created (i.e. `{{secrets.AUTOMATION_ANYWHERE_PASSWORD}}`). 3. Set **API key** as `API key` to the secret you created (i.e. `{{secrets.AUTOMATION_ANYWHERE_API_KEY}}`). The API-Key is a 40-character string generated in the Control Room. Refer to [create and assign API key generation role documentation](https://docs.automationanywhere.com/bundle/enterprise-v2019/page/enterprise-cloud/topics/control-room/administration/roles/cloud-control-room-apikey-role.html) to learn more. ### _Authentication (refresh) token_ authentication Select the **Automation Anywhere connector** and fill out the following properties under the **Authentication** section: 1. Select **Authentication (refresh) token** in the **Authentication** section. 2. Set **Token** to `Token` to the secret you created (i.e. `{{secrets.AUTOMATION_ANYWHERE_TOKEN}}`). It can be an authentication or refresh token. Refer to [authentication API documentation](https://docs.automationanywhere.com/bundle/enterprise-v11.3/page/enterprise/topics/control-room/control-room-api/api-authentication.html) to learn how to generate an authentication token or observe the [refresh token API documentation](https://docs.automationanywhere.com/bundle/enterprise-v11.3/page/enterprise/topics/control-room/control-room-api/refresh-authentication-token.html) to learn how to generate a refresh token. ## Configuration ### Control Room URL 1. Set **Control Room URL** to `Control Room URL`. The Control Room URL is the URL you use to access the Automation Anywhere Control Room. The Control Room URL is typically provided by the Automation Anywhere administrator and is specific to the organization's instance of the platform (i.e. `https://domainname.myautomationanywhere.digital`). ## Operation types The **Automation Anywhere connector** currently supports two operation types in the **Operation type** dropdown list: _Add work item to the queue_ and _Get work item result from queue by ID_. ### Add work item to the queue This operation provides the ability to add a work queue item in the specified queue. It corresponds directly to the respective Automation Anywhere API - [`Add Work Items to the queue API`](https://docs.automationanywhere.com/bundle/enterprise-v11.3/page/enterprise/topics/control-room/control-room-api/add-work-item-data-to-queue-api.html). #### Usage 1. Select **Add work item to the queue** from the **Operation type** dropdown in the **Operation** section. 2. Populate **Authentication section** as described in the [respective section](#authentication). 3. In the **Configuration** section, set the **Control Room URL** field as described in the [respective section](#control-room-url). 4. In the **Input** section, set **Work queue ID**. This is the identifier of a queue, where an item will be fetched from. 5. In the **Input** section, set **Work Item json Data** that you want to pass together with the item. The **Data** has to comply with the Automation Anywhere API, and must contain the following semantics: ```json { "coll_name": "your value", "last_name": "Doe", "email": "jane.doe@example.com" } ``` #### Add work item to the queue response The operation **Add work item to the queue** returns information about the newly created item in the queue. You can use an output mapping to map the response: 1. Use **Result Variable** to store the response in a process variable. For example, `myResultVariable`. 2. Use **Result Expression** to map fields from the response into process variables. It comes with a pre-filled value of `={itemId:response.body.list[1].id}`. To use operation _Get work item result from queue by ID_, you need an `itemId`. This expression will add it to the context for you. Learn more in [get work item result from queue by ID](#get-work-item-result-from-queue-by-id). Response example: ```json { "list": [ { "id": "40957", "createdBy": "25", "createdOn": "2021-11-24T01:53:10.175335900Z", "updatedBy": "25", "updatedOn": "2021-11-24T01:53:10.175335900Z", "version": "0", "json": { "TRN_ID": "A11", "DATA": "mydata" }, "result": "", "deviceId": "0", "status": "NEW", "col1": "A11", "col2": "", "deviceUserId": "0", "queueId": "0", "comment": "", "automationId": "0", "totalPausedTime": "0", "error": "", "col6": "", "jobExecutionId": "" } ] } ``` ### Get work item result from queue by ID This operation provides the ability to return the details of the specified work item from the work queue. It corresponds directly to the respective Automation Anywhere API - [`List Work Items in queue with filter by work item ID`](https://docs.automationanywhere.com/bundle/enterprise-v11.3/page/enterprise/topics/control-room/control-room-api/get-all-work-items-in-queues-api.html). #### Usage 1. Select **Get work item result from queue by ID** from the **Operation type** dropdown in the **Operation** section. 2. Populate **Authentication section** as described in the [respective section](#authentication). 3. In the **Configuration** section, set the **Control Room URL** field as described in the [respective section](#control-room-url). 4. In the **Input** section, set **Work queue ID**. This is the identifier of a queue, where an item will be fetched from. 5. In the **Input** section, set **Work item ID**. This is the identifier of the item to be fetched. #### Get work item result from queue by ID response Given you have a queue work item ID previously added to a queue, the operation **Get work item result from queue by ID** returns information about a certain work item. You can use an output mapping to map the response: 1. Use **Result Variable** to store the response in a process variable. For example, `myResultVariable`. 2. Use **Result Expression** to map fields from the response into process variables. It comes with a pre-filled value of `={itemState:response.body.list[1].status}`. You will observe the `itemState` in the process variables. Its value will let you know if the item was processed or not. Response example: ```json { "page": { "offset": 0, "total": 5, "totalFilter": 1 }, "list": [ { "id": "11804", "createdBy": "24", "createdOn": "2020-05-26T10:19:34.786711300Z", "updatedBy": "24", "updatedOn": "2020-05-26T10:19:34.786711300Z", "version": "1", "json": {}, "result": "", "deviceId": "0", "status": "NEW", "col1": "Brian", "col2": "Matthews", "col3": "bmatthews0@example.com", "deviceUserId": "0", "queueId": "20", "comment": "", "automationId": "0", "totalPausedTime": "0", "error": "", "col6": "", "col10": "" } ] } ``` ## Using Automation Anywhere connector best practice There is no guarantee a queue item will be processed right away. In that case, we suggest building your BPMN diagram to periodically retry polling. To learn more, refer to an entry _Solution with Timer and Loop_ on the [Camunda BPMN examples](https://camunda.com/bpmn/examples/) page. :::note To avoid performance issues, it is recommended to limit the number of loop retries. ::: --- ## Built-in connectors Find technical documentation for our library of built-in connectors. :::tip Don't see the connector you need? Build your own [custom connector](/components/connectors/custom-built-connectors/build-connector.md), or explore the [Camunda Marketplace](https://marketplace.camunda.com/) for more connectors developed by Camunda, Partners, and the Community. ::: --- ## AWS Lambda connector The **AWS Lambda connector** is an outbound connector that allows you to connect your BPMN service with Amazon Web Service's [AWS Lambda Service](https://aws.amazon.com/lambda/) to invoke [AWS Lambda functions](https://aws.amazon.com/lambda/). ## Prerequisites To use an **AWS Lambda connector**, you need to have an [AWS Lambda Function](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html), IAM key, and secret pair with permissions for execute function. Refer to the [AWS Lambda developer guide](https://docs.aws.amazon.com/lambda/latest/dg/lambda-permissions.html) to learn more. :::note Use Camunda secrets to avoid exposing your AWS IAM credentials as plain text. Refer to [managing secrets](components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ::: ## Create an AWS Lambda connector task ## Invoking your AWS Lambda function To make the **AWS Lambda connector** executable, fill out the mandatory fields highlighted in red in the properties panel on the right side of the screen: 1. Choose an applicable authentication type from the **Authentication** dropdown. Learn more about authentication types in the related [appendix entry](#aws-authentication-types). 2. Set the relevant IAM key and secret pair in the **Authentication** section. For example, `{{secrets.MY_AWS_ACCESS_KEY}}`. The value can be plain text, but this is not recommended due to security concerns. 3. Set the relevant AWS region in the **Authentication** section. Refer to the [Regions and Zones](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html) to learn more. 4. In the **Select Operation** section, the default option is set to synchronous invocation; an asynchronous invocation option is currently not available. Refer to [event-driven invocation](https://docs.aws.amazon.com/lambda/latest/dg/lambda-services.html#event-driven-invocation) to learn more. 5. In the **Operation Details** section, fill out the field **Function name**. This field can be a [function URL](https://docs.aws.amazon.com/lambda/latest/dg/lambda-urls.html?icmpid=docs_lambda_help), [function ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html), function name, or alias. 6. (Optional) The **Payload** field in the **Operation Details** section is optional. This field requires FEEL input. Payload must be in JSON format as this is the data that will be processed by your Lambda function. ## AWS Lambda connector response The **AWS Lambda connector** returns the HTTP status code, executed version, and payload (the response from the function, or an error object). The following fields are available in the response variable: - `statusCode` - HTTP status code returned by the AWS Lambda Invoke API. This shows whether AWS Lambda accepted and handled the invocation request. It does not indicate whether the Lambda function itself returned a successful result. - `executedVersion` - Executed version of the Lambda function. - `payload` - The response returned by the Lambda function, or an error object. If your function returns its own `statusCode` in the payload, that value describes the function result and is separate from the top-level connector `statusCode`. If your Lambda function includes a `statusCode` in its response body, access it with `response.payload.statusCode` rather than `response.statusCode`. You can use an output mapping to map the response: 1. Use **Result Variable** to store the response in a process variable. For example, `myResultVariable`. 2. Use **Result Expression** to map fields from the response into process variables. For example: ``` = { "myNewReportStatusCode": response.statusCode, "myNewReportExecutedVersion": response.executedVersion, "myNewReportPayload": response.payload } ``` ## Appendix ### AWS authentication types There are two options to authenticate the connector with AWS: - Choose **Credentials** in the **Authentication** dropdown if you have a valid pair of access and secret keys provided by your AWS account administrator. This option is applicable for both SaaS and Self-Managed users. - Choose **Default Credentials Chain (Hybrid/Self-Managed only)** in the **Authentication** dropdown if your system is configured as an implicit authentication mechanism, such as role-based authentication, credentials supplied via environment variables, or files on target host. This option is applicable only for Self-Managed or hybrid distribution. This approach uses the [Default Credential Provider Chain](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html) to resolve required credentials. --- ## Azure blob storage connector The **Azure Blob Storage connector** is an outbound connector that allows you to interact with [Azure Blob Storage](https://azure.microsoft.com/en-us/products/storage/blobs) resources from your BPMN processes. The **Azure Blob Storage connector** currently supports uploading and downloading documents. ## Prerequisites To begin using the **Azure Blob Storage connector**, ensure you have created the following [resources](https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction#blob-storage-resources): - An Azure [storage account](https://portal.azure.com/#browse/Microsoft.Storage%2FStorageAccounts) - An Azure [container](https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction#containers) ## Authentication ### Create a new connector secret Keep your secrets secure by avoiding direct inclusion in the BPMN `xml` file. Instead, create a secret: 1. Follow our [guide for creating secrets](/components/hub/organization/manage-clusters/manage-secrets.md). 2. Name your secrets (for example, `AZURE_SAS_TOKEN`) so you can reference them later in the connector configuration. ### OAuth 2.0 1. In the [Azure Portal](https://portal.azure.com), open **Microsoft Entra ID** and select **+ Add > App registration** to create a new app registration. - Note the **client ID** and **tenant ID** from the app registration’s **Overview** tab. 2. Go to **Manage > Certificates & secrets**, select **+ New client secret**, and copy the **Value** (this is your client secret). 3. In **Blob Storage**, open your container, select **Access control (IAM) > Add > Add role assignment**, and assign the app registration one of the following roles: - `Storage Blob Data Contributor` - `Storage Blob Data Reader` 4. In the connector configuration, enter the following details: - **Client ID** – Client ID of the app registration - **Tenant ID** – Tenant ID of the app registration - **Client Secret** – Client secret value of the app registration - **Account URL** – URL of the storage account, for example: `https://.blob.core.windows.net/` Replace `` with your actual storage account name. ### SAS token 1. In the **Azure Portal**, open your **Storage account > Containers**, and select your container. 2. From the container’s **toolbar**, click **Generate SAS**. 3. In the connector configuration, enter the **SAS token** and **SAS URL**. ## Operation Select the desired operation from the **Operation** dropdown. ### Upload document 1. Enter the **Container name** — it must match the container the **SAS token** was created for. 2. (Optional) Enter the **File name**. If left blank, the filename from the document metadata will be used. 3. Reference the **Document to upload**. :::note If an uploaded document already exists in the container with the same name, it will be overwritten. Depending on the settings made on the Azure Storage Account, the previous version of the document may still be accessible in the file version history. ::: ### Download document 1. Enter the **Container name** — it must match the container the **SAS token** was created for. 2. Enter the **File name** to download. 3. Select the **Create document** checkbox, as preferred: - If selected, a document reference will be created. - If not selected, the content will be extracted and included in the response. --- ## Azure OpenAI connector The **Azure OpenAI connector** is an outbound connector that allows you to interact with [Azure OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service) models from your BPMN processes. The **Azure OpenAI connector** currently supports only prompt operations: [`completions`](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#completions), [`chat completions`](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#chat-completions), and [`completions extensions`](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#completions-extensions). Refer the [official models documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models) to find out if a desired model supports the operations mentioned. ## Prerequisites To begin using the **Azure OpenAI connector**, ensure you have created and deployed an Azure OpenAI resource. A valid Azure OpenAI API key is also required. Learn more at the [official Azure OpenAI portal entry](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/create-resource). ## Create an Azure OpenAI connector task ## Make your Azure OpenAI connector executable To work with the **Azure OpenAI connector**, fill all mandatory fields. ## Authentication Fill the **API key** field with a valid Azure OpenAI API key. [Learn more](https://learn.microsoft.com/en-us/azure/ai-services/openai/quickstart?tabs=command-line%2Cpython-new&pivots=rest-api#retrieve-key-and-endpoint) about obtaining a key. ### Create a new connector secret Keep your **API key** safe and avoid exposing it in the BPMN `xml` file by creating a secret: 1. Follow our [guide for creating secrets](/components/hub/organization/manage-clusters/manage-secrets.md). 2. Name your secret (for example, `AZURE_OAI_SECRET`) so you can reference it later in the connector. ## Operation Select the desired operation from the **Operation** dropdown. Fill in the **Resource name**, the **Deployment ID**, and the **API version** related to your operation. Ensure the deployed [model](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models) supports the selected operation. ### Completion, chat completion, and completion extension - For **completion** details, refer to the related [Microsoft reference documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#completions). - For **chat completion** details, refer to the related [Microsoft reference documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#chat-completions). - For **completion extension** details, refer to the related [Microsoft reference documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#completions-extensions). ## Handle connector response The **Azure OpenAI connector** is a protocol connector, meaning it is built on top of the **HTTP REST connector**. Therefore, handling response is still applicable [as described](/components/connectors/protocol/rest.md#response). ## Usage example ### Chat completions Assume you have deployed a `gpt-35-turbo` model with the following URL: `https://myresource.openai.azure.com/openai/deployments/mydeployment/completions?api-version=2024-02-01`, and created a Connector secret with the name `AZURE_OAI_SECRET`. Consider the following input: - **API key**: `{{secrets.AZURE_OAI_SECRET}}` - **Operation**: `Chat completion` - **Resource name**: `myresource` - **Deployment ID**: `mydeployment` - **API version**: `2024-02-01` - **Message role**: `User` - **Message content**: `What is the age of the Universe?` - **Message context**: `=[{"role": "system", "content": "You are helpful assistant."}]` - Leave the rest of the params blank or default - **Result variable**: `myOpenAIResponse` In the `myOpenAIResponse` you will find the following result: ```json { "status": 200, "headers": { ... }, "body": { "choices": [ { "content_filter_results": { "hate": { "filtered": false, "severity": "safe" }, "self_harm": { "filtered": false, "severity": "safe" }, "sexual": { "filtered": false, "severity": "safe" }, "violence": { "filtered": false, "severity": "safe" } }, "finish_reason": "stop", "index": 0, "message": { "content": "The age of the universe is estimated to be around 13.8 billion years. This age is determined through various scientific methods, such as measuring the cosmic microwave background radiation and studying the expansion rate of the universe.", "role": "assistant" } } ], "created": "...", "id": "...", "model": "gpt-35-turbo", "object": "chat.completion", "prompt_filter_results": [ { "prompt_index": 0, "content_filter_results": { "hate": { "filtered": false, "severity": "safe" }, "self_harm": { "filtered": false, "severity": "safe" }, "sexual": { "filtered": false, "severity": "safe" }, "violence": { "filtered": false, "severity": "safe" } } } ], "usage": { "completion_tokens": 43, "prompt_tokens": 24, "total_tokens": 67 } } } ``` --- ## Blue Prism connector The **Blue Prism connector** allows you to orchestrate a Blue Prism queue from your BPMN process with [Blue Prism RPA](https://www.blueprism.com/). To start using the connector, you must have a running Blue Prism instance configured API service. Refer to the [official documentation page](https://documentation.blueprism.com/bp-7-5/en-us/Guides/bp-api/api-introduction.htm?tocpath=Installation%7CInstall%20the%20Blue%20Prism%20API%7C_____0) to learn more about how to install and configure Blue Prism API service. ## Create a Blue Prism connector task ## Authentication You can choose among the available **Blue Prism connector** authentication types according to your authentication requirements. ### Bearer token authentication #### Create a new connector secret We advise you to keep your **Bearer Token** safe and avoid exposing it in the BPMN `xml` file by creating a secret: 1. Follow our [guide for creating secrets](../../hub/organization/manage-clusters/manage-secrets.md). 2. Name your secret (i.e `BLUE_PRISM_BEARER_TOKEN`) so you can reference it later in the connector. #### Configure the bearer token Select the **Blue Prism connector** and fill out the following properties under the **Authentication** section: 1. Click **Bearer Token** in the **Authentication** section. 2. Set **Bearer** to the secret you created (i.e. `{{secrets.BLUE_PRISM_BEARER_TOKEN}}`). ### OAuth Client Credentials Flow #### Create a new connector secret We advise you to keep your **Client ID** and **Client secret** safe and avoid exposing it in the BPMN `xml` file by creating a secret: 1. Follow our [guide for creating secrets](../../hub/organization/manage-clusters/manage-secrets.md). 2. Name your secret (i.e `BLUE_PRISM_CLIENT_ID`) so you can reference it later in the connector. #### Configure the OAuth Token Select the **Blue Prism connector** and fill out the following properties under the **Authentication** section: 1. Select **OAuth 2.0 client credentials** in the **Authentication** section. 2. Set **Identity token provider URL** to identity provider configured for your Blue Prism instance. 3. Set **Client ID** to the secret you created (i.e. `{{secrets.BLUE_PRISM_CLIENT_ID}}`). 4. Set **Client secret** to the secret you created (i.e. `{{secrets.BLUE_PRISM_CLIENT_SECRET}}`). Find more information about the OAuth client credentials flow in the [RFC reference](https://www.rfc-editor.org/rfc/rfc6749#section-4.4). ## Operation types The **Blue Prism connector** currently supports two operation types in the **Operation type** dropdown list: _Get item from a queue by ID_ and _Create work queue item_. ### Get item from a queue by ID This operation allows you to return details of a specified item from a work queue. It matches directly to respective Blue Prism API endpoint - [`Return details of a specified item from a work queue`](https://documentation.blueprism.com/bp-7-5/en-us/bp-api/bpe-7-5-0-api-spec.html#tag/Work-Queues/operation/getWorkQueueItemFromWorkQueue). #### Usage 1. Select **Get item from a queue by ID** from the **Operation** dropdown. 2. Populate **Authentication section** as described in the [respective section](#authentication). 3. In the **Configuration** section, set **Blue Prism API base URL** field. E.g., `http://my.bp.host.com:9876`. 4. In the **Input** section, set **Work queue ID**. This is the identifier of a queue, where the item is fetched from. 5. In the **Input** section, set **Queue item ID**. This is the identifier of the item to be fetched. #### Get item from a queue by ID response Given you have a queue item ID previously added to a queue, the operation **Get item from a queue by ID response** returns information about a certain item. You can use an output mapping to map the response: 1. Use **Result Variable** to store the response in a process variable. For example, `myResultVariable`. 2. Use **Result Expression** to map fields from the response into process variables. It comes with a pre-filled value of `={itemState:response.body.state}`. You will observe the `itemState` in the process variables. Its value will let you know if the item was processed or not. Response example: ```json { "id": "01234567-89ab-cdef-0123-456789abcdef", "priority": 3, "ident": 123, "state": "Completed", "keyValue": "Example value", "status": "Example status", "tags": ["Example tag 1", "Example tag 2"], "attemptNumber": 1, "loadedDate": "2020-10-02T12:34:56+01:00", "deferredDate": "2020-10-02T12:34:56+01:00", "lockedDate": "0001-01-01T00:00:00Z", "completedDate": "2020-10-02T13:00:00+01:00", "exceptionedDate": "0001-01-01T00:00:00Z", "exceptionReason": "Example reason", "lastUpdated": "2020-10-02T13:00:00+01:00", "workTimeInSeconds": 123, "attemptWorkTimeInSeconds": 123, "resource": "Example resource", "data": { "rows": [] }, "sla": 7200, "sladatetime": "0001-01-01T00:00:00Z", "processname": "Example process name", "issuggested": false } ``` ### Create work queue item This operation allows you to create work queue items in the specified queue. It matches directly to respective Blue Prism API endpoint - [`Create work queue items`](https://documentation.blueprism.com/bp-7-5/en-us/bp-api/bpe-7-5-0-api-spec.html#tag/Work-Queues/operation/createWorkQueueItems). #### Usage 1. Select **Create work queue item** from the **Operation** dropdown. 2. Populate the **Authentication section** as described in the [respective section](#authentication). 3. In the **Configuration** section, set **Blue Prism API base URL** field. E.g., `http://my.bp.host.com:9876`. 4. In the **Input** section, set **Work queue ID**. This is the identifier of a queue, where item will be fetched from. 5. In the **Input** section, set **Item type** of the data entry you wish to submit to the queue. 6. In the **Input** section, set **Item value** of the data entry you wish to submit to the queue. 7. In the **Input** section, set **Defer date**. This field is the earliest time and date that this item is deferred until. 8. In the **Input** section, set **Priority**. This field is the priority value assigned to the item. 9. In the **Input** section, set **Status**. This is the user-supplied status value. _Note: Do not confuse this with queue item 'state' property._ #### Create work queue item response The operation **Create work queue item** returns information about the newly created item in the queue. You can use an output mapping to map the response: 1. Use **Result Variable** to store the response in a process variable. For example, `myResultVariable`. 2. Use **Result Expression** to map fields from the response into process variables. It comes with a pre-filled value of `={itemId:response.body.ids[1]}`. To use operation _Get queue item result by ID_, you need an `itemId`. This expression will add it in the context for you. Learn more in [get queue item result by ID](#get-item-from-a-queue-by-id). Response example: ```json { "ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"] } ``` ### Using Blue Prism connector best practice There is no guarantee a queue item will be processed right away. In that case, we suggest building your BPMN diagram to periodically retry polling. To learn more, refer to an entry _Solution with Timer and Loop_ at [Camunda BPMN examples](https://camunda.com/bpmn/examples/) page. :::note To avoid performance issues, it is recommended to limit the number of loop retries. ::: --- ## Box connector The **Box connector** is an outbound connector that allows you to interact with the [Box](https://box.com/) storage API from your BPMN process. ## Prerequisites To use the **Box connector**, you must have a Box account. You can use an [enterprise account](https://www.box.com/) or create a [developer account](https://developer.box.com/). The **Box connector** supports different Box API authentication methods, all of which require a [Custom App](https://developer.box.com/guides/applications/app-types/custom-apps/) in your Box account. - Camunda recommends enabling the **Generate user access tokens** feature in **Advanced Features** in your app configuration. This allows the connector to login as an [app user](https://github.com/box/box-java-sdk/blob/v4.13.1/doc/authentication.md#obtaining-user-token). You can use the **User ID** shown on the overview page in your Box app console. - Ensure the app is **Authorized** in the **Platform Apps Manager** section of your Box account. :::note A [Custom App](https://developer.box.com/guides/applications/app-types/custom-apps/) is required to interact with the Box API without any manual user interaction during credentials creation when authenticating with the connector. ::: ## Create a Box connector task ## Authentication To authenticate the connector with Box, select an authentication type from the **Authentication** dropdown: - **Client Credentials Enterprise**: Authenticate using an **Enterprise ID**. You must have an enterprise account with Box. - **Client Credentials User**: Authenticate using a **User ID**. You must have a [Custom App](https://developer.box.com/guides/applications/app-types/custom-apps/) set up in Box. - **Devoloper Token** : Authenticate using a developer token. These tokens are usually only valid for 60 minutes, and should be used for testing purposes only. - **JWT JSON Config**: Authenticate using a JSON configuration file created and downloaded from your Box App Configuration page. ## Operations The **Box connector** supports the following operations that allow you to interact with the items stored in your Box account. :::note The Box connector supports two methods for locating items stored in your Box account: - File or folder path properties allow you to either specify the **Item ID** (found in the Box URL when browsing your items) or using the item names separated by slashes (`/`) for items in folders. - A path consisting of only a single `/` denotes the root of your Box folder. For example, `/my-folder` would point to the `my-folder` folder located in the root directory. You can use the path notation for files (`/my/image.png`) and folders (`/my/sub/folder`). ::: :::note Starting from version 8.7.0, the Box connector supports uploading documents from (or downloading documents to) the Camunda document store. Review the **Document** field in the properties panel where the document reference can be provided. See additional details and limitations in [document handling](/components/document-handling/getting-started.md). ::: ### Create Folder Creates a new folder in your store. | Property | Type | Required | Example | | :---------- | :---------- | :------- | :------------------------------- | | Folder name | String | Yes | "my-folder" | | Parent path | Folder path | No | `/`, `123`, `/my/parent/folders` | :::note This operation fails if a folder already exists with the same name, as Box only supports unique names. ::: ### Delete Folder Deletes an existing folder. Optionally include all items and subfolders. | Property | Type | Required | Example | | :---------- | :------ | :------- | :----------- | | Folder path | String | Yes | "/my-folder" | | Recursive | Boolean | No | `true` | ### Delete File Deletes a single file item. | Property | Type | Required | Example | | :-------- | :----- | :------- | :------------- | | File path | String | Yes | "/my-file.png" | ### Move File Moves a file item into a folder. | Property | Type | Required | Example | | :----------------- | :----- | :------- | :---------------- | | File path | String | Yes | "/my-file.png" | | Target folder path | String | Yes | "/another/folder" | ### Download File Downloads a file item by creating a document in the process engine, and returning a reference to the document in the response. | Property | Type | Required | Example | | --------- | ------ | -------- | -------------- | | File path | String | Yes | "/my-file.png" | For example, you can reference the document representing the downloaded file using the example response expression: ```json {"download": document} ``` ### Upload File Upload a file using an existing document reference. The Box connector resolves the document reference and creates a new file item in your Box store. | Property | Type | Required | Example | | ------------------ | ------------------------- | -------- | ---------------- | | File path | String | Yes | "/my-file.png" | | Folder path | String | Yes | "/upload/folder" | | Document reference | Document reference object | Yes | `{...}` | The result of the upload can be accessed via the `item` property of the result. Example value of a successful file upload: ```json { "item": { "id": "1734104173434", "name": "my_new_file.png", "type": "file" } } ``` ### Search Searches the items stored in the Box account. | Property | Type | Required | Example | | --------------------- | ------ | -------- | ------------- | | Search query | String | Yes | "inci" | | Seach sort column | String | No | `modified_at` | | Search sort direction | String | No | `DESC` | | Search offset | String | No | `0`, `10` | | Search limit | String | No | `50` | Example search result with a single item: ```json { "items": [ { "id": "1733978444906", "name": "incident-wrapped.png", "type": "file" } ] } ``` --- ## CSV connector The **CSV connector** reads CSV files and converts JSON data to CSV format for document or text use. ## Create a CSV connector task ## Operations The **CSV connector** supports operations to [read](#read-csv) and [write](#write-csv) CSVs. ### Read CSV Reads a CSV from a text or a document and converts it into an array of JSON records. | Property | Type | Description | Required | Example | | ------------------ | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ---------------------------------------------------- | | Data | Document or String | The CSV data as a document or text. | Yes | [Example CSV](#example-csv-input) | | Delimiter | String | The delimiter used to separate each column. | No | Defaults to `,` | | Skip header record | Boolean | Whether to skip the first row (header) in the records. | No | Defaults to `true` | | Headers | Array of strings | Used when no header is present or to override column names. | No | Defaults to `[]`. Example: `["name","cost","count"]` | | Row type | String | Determines the structure of the result records. | No | Defaults to `Object`. Either `Object` or `Array`. | | Record mapping | [FEEL](../../../modeler/feel/what-is-feel) script | The FEEL script will be evaluated against every record. All data returned by this mapping script will be part of the results. This can be useful for parsing string-based CSV data into different types like numbers. Returning `null` excludes the record from the final results. | No | [Example script](#example-with-record-mapping) | #### Example CSV `Data` input: ```csv product,quantity,price Wireless Mouse,25,29.99 Office Chair,8,149.50 USB Cable,100,12.99 Monitor Stand,15,45.00 Desk Lamp,32,24.95 ``` :::info To pass the CSV `Data` as a FEEL string, end lines with `\r\n`. This is the default line separator when reading CSV files. For example: ``` ="product,quantity,price\r\nWireless Mouse,25,29.99\r\nOffice Chair,8,149.50\r\nUSB Cable,100,12.99\r\nMonitor Stand,15,45.00\r\nDesk Lamp,32,24.95" ``` ::: #### Example output for row type `Object` ```json { "records": [ { "product": "Wireless Mouse", "quantity": "25", "price": "29.99" }, { "product": "Office Chair", "quantity": "8", "price": "149.50" }, { "product": "USB Cable", "quantity": "100", "price": "12.99" }, { "product": "Monitor Stand", "quantity": "15", "price": "45.00" }, { "product": "Desk Lamp", "quantity": "32", "price": "24.95" } ] } ``` Based on the `Object` [example](#example-output-for-row-type-object) above, you can access the CSV data in your result expression for further processing: ``` = { sum: sum(for r in records return number(r.quantity)) } ``` #### Example output for row type `Array` ```json { "records": [ ["Wireless Mouse", "25", "29.99"], ["Office Chair", "8", "149.50"], ["USB Cable", "100", "12.99"], ["Monitor Stand", "15", "45.00"], ["Desk Lamp", "32", "24.95"] ] } ``` Based on the `Array` [example](#example-output-for-row-type-array) above, you can access the CSV data in your result expression for further processing: ``` = { sum: sum(for r in records return number(r[2])) } ``` #### Example with record mapping Based on the data of the `Object` [example](#example-output-for-row-type-object) above, we can use the following [FEEL](../../../modeler/feel/what-is-feel) script to extract only the `product` and the `price` converted to a number per record: ``` = { product: record.product, price: number(record.price) } ``` Leading to the following output: ``` [ {"product":"Wireless Mouse","price":29.99}, {"product":"Office Chair","price":149.5}, {"product":"USB Cable","price":12.99}, {"product":"Monitor Stand","price":45}, {"product":"Desk Lamp","price":24.95} ] ``` We can also use the record mapping as a filter to only include certain records in the final results: ``` = if number(record.price) >= 30 then {product: record.product, price: number(record.price)} else null ``` This will exclude all products with a price lower than 30 from the final results: ``` [ {"product":"Office Chair","price":149.5}, {"product":"Monitor Stand","price":45} ] ``` ### Write CSV Takes an array of JSON objects and creates a CSV from it. The result can either be stored as a document for further processing (e.g., uploading) or returned as a string. | Property | Type | Description | Required | Example | | :----------------- | :--------------- | :--------------------------------------------------------------------------------------------------------------------------------------- | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Data | Array | The CSV data as an array of objects or arrays | Yes | [Object](#example-for-an-object-based-data-input) and [Array](#example-for-an-array-based-data-input) example. | | Create document | Boolean | If `true` the connector will store the CSV document in Camunda and returns a reference. If `false` the CSV will be returned as a string. | No | Defaults to `false` and leads to the creation of a [string](#example-output-for-a-csv-returned-as-a-string) with the data in the CSV format. If set to `true` a [document](#example-output-for-a-csv-stored-in-a-document) will created and returned by the Connector. | | Delimiter | String | The delimiter used to separate each column. | No | Defaults to `,` | | Skip header record | Boolean | Whether to include the first row in the records or not. | No | Defaults to `true` | | Headers | Array of strings | Can be used when there is no header record present in the record or to change the column names if there is a header record. | No | Defaults to `[]`. Example: `["name","cost","count"]`. Needs to be specified when using object-based arrays as the `Data` input. | #### Example for an object-based `Data` input Every record for an object-based `Data` input contains all column names as their property (key) names. The values of the properties will be written into the CSV. ```json { "records": [ { "product": "Wireless Mouse", "quantity": "25", "price": "29.99" }, { "product": "Office Chair", "quantity": "8", "price": "149.50" }, { "product": "USB Cable", "quantity": "100", "price": "12.99" }, { "product": "Monitor Stand", "quantity": "15", "price": "45.00" }, { "product": "Desk Lamp", "quantity": "32", "price": "24.95" } ] } ``` :::info `Headers` must be specified when using object-based arrays as the `Data` input when writing a CSV. The `Headers` must match the property names of the objects. For the example above, one would provide the following value for `Headers`: ```json =["product", "quantity", "price"] ``` ::: #### Example for an array-based `Data` input Every record for an array-based `Data` input contains all values in a single array per row. ```json [ ["Wireless Mouse", "25", "29.99"], ["Office Chair", "8", "149.50"], ["USB Cable", "100", "12.99"], ["Monitor Stand", "15", "45.00"], ["Desk Lamp", "32", "24.95"] ] ``` #### Example output for a CSV returned as a string ```json { "content": "Wireless Mouse,25,29.99\r\nOffice Chair,8,149.50\r\nUSB Cable,100,12.99\r\nMonitor Stand,15,45.00\r\nDesk Lamp,32,24.95\r\n" } ``` #### Example output for a CSV stored in a document ```json { "document": { "storeId": "in-memory", "documentId": "8b54b413-b847-4650-b445-de963d5c506d", "contentHash": "ed0f7ad835669698a108a32b2a99e89e4f5aea84127fde68df4248b11197b0e5", "metadata": { "contentType": "text/csv", "size": 114, "fileName": "8b54b413-b847-4650-b445-de963d5c506d" }, "camunda.document.type": "camunda" } } ``` --- ## EasyPost connector The **EasyPost connector** is an outbound connector that allows you to create addresses, parcels, and shipments, as well as purchase and verify shipments with [EasyPost Service](https://www.easypost.com/) from your BPMN process. ## Prerequisites To use the **EasyPost connector**, sign up for an EasyPost account, enter your carrier-specific credentials on the [Carrier Account Dashboard](https://www.easypost.com/account/carriers), and get your [API key](https://www.easypost.com/account/api-keys). :::note Use Camunda secrets to avoid exposing your EasyPost API key as plain text. Refer to our documentation on [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ::: ## Create an EasyPost connector task ## Authentication In the **Authentication** section, provide an **API key**, which you can find in the [API Key dashboard](https://www.easypost.com/account/api-keys). ## Select operation to execute The **EasyPost connector** currently supports the following operations: ### Create address Allows you to create an address, save it, and get an address ID to use in follow-up operations. Set address information in the **Input** section. Refer to [address object documentation](https://docs.easypost.com/docs/addresses) to learn more about address object and to observe response examples. ### Verify a created address Allows you to verify an address by **Address ID** and return verified address object. ### Create a parcel Allows you to create a parcel, save it, and get a parcel ID to use in follow-up operations. Set required properties in the **Input** section. Refer to the [parcel object documentation](https://docs.easypost.com/docs/parcels) to learn more about the parcel object and to observe response examples. ### Create a shipment Allows you to create a shipment, save it, and get the shipment ID for use in follow-up operations. Set required properties in the **Input** section: ID of destination address, ID of origin address, and ID of parcel. Refer to the [shipment object documentation](https://docs.easypost.com/docs/shipments) to learn more about the shipment object and to observe response examples. In the **Output** section, the pre-filled **Result Expression** returns the ID of the shipment and the ID of [rate](https://docs.easypost.com/docs/shipments/rates). FEEL expression: ``` {priorityRate: response.body.rates[item.service = "Priority"], shipmentId: priorityRate[1].shipment_id, rateId: priorityRate[1].id} ``` Response: ``` { "shipment_id": "shp...", "rateId": "rate...." } ``` ### Buy a shipment Allows you to buy a shipment. Set required properties in the **Input** section: IDs of rate and shipment. Refer to the [shipment object documentation](https://docs.easypost.com/docs/shipments#buy-a-shipment) to learn more about the shipment object and to observe response examples. In the **Output** section the pre-filled **Result Expression** returns the ID of a tracker, tracking code, and status of the shipment. FEEL expression: ``` {trackerId: response.body.tracker.id, trackingCode: response.body.tracking_code, shipmentstatus:response.body.status} ``` Response: ``` { "shipmentstatus": "shp...", "trackerId": "trk....", "trackingCode: :"track...." } ``` ### Retrieve a tracker by ID Allows you to retrieve a tracker by ID and get information about the status of the tracker. Set required properties in the **Input** section: Tracker ID fetched after buying shipment). Refer to the [tracker object documentation](https://docs.easypost.com/docs/trackers#tracker-object) to learn more about the tracker object and to observe response examples. In the **Output** section, the pre-filled **Result Expression** returns the ID of the tracker, tracking code, and status of the shipment. FEEL expression: ``` {trackerStatus: response.body.status} ``` ## Handle connector response The **EasyPost connector** is a protocol connector, meaning it is built on top of the **HTTP REST connector**, therefore handling response is still applicable [as described](/components/connectors/protocol/rest.md#response). --- ## Email connector The Email inbound connector allows your BPMN service to receive emails via IMAP. You can start a new process instance when an email arrives or catch email events in an ongoing process. The message start event inbound connector triggers a new process for each incoming email. ## Prerequisites To use the **Email Inbound connector**, you must have an IMAP server available to connect to. :::note Use Camunda secrets to avoid exposing your sensitive data as plain text. See [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md). ::: ## Authentication You can authenticate to a mail server as follows. ### Simple Authentication This method allows the user to connect to any IMAP server using an email address and password. #### Parameters | Parameter | Description | | :--------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `username` | Enter your full email address (for example, user@example.com) or the username provided by your email service. This is used to authenticate your access to the mail server. | | `password` | Enter the password for your email account. Keep your password secure and do not share it with others. | ## Listener information This inbound connector creates a new process each time a new email is received. | Parameter | Description | | :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Folder` | (Optional) Define the folder the inbound connector will monitor.If not specified, the default folder is set to `INBOX`.For subfolders, use `.` or `/` separated path (for example, `inside/folder` or `inside.folder`) | | `Polling Wait Time` | Set the interval between each polling operation. See [timer events](/components/modeler/bpmn/timer-events/timer-events.md#time-duration) for more information on time duration and correct format. | | `Polling Configuration` | This section contains settings related to the polling behavior of the connector.Poll All Emails: Poll every email found in the specified folder.Move to Another Folder After Processing: Move processed emails to a specific folder.Folder: Specify the target folder to move processed emails to. To specify a new folder or a nested hierarchy, use a `.` or `/` separated path (for example, Archive/test or Projects.2023.January). Non-existent folders in the path are automatically created.Delete After Processing: Permanently delete each email after processing.`Poll Unseen Emails`: Poll only emails not marked as read in the specified folder.`Move to Another Folder After Processing`: Move processed unseen emails to a specific folder.`Folder`: Specify the target folder to move processed unseen emails to. To specify a new folder or a nested hierarchy, use a `.` or `/` separated path (for example, Archive/test or Projects.2023.January). Non-existent folders in the path are automatically created.`Delete After Processing`: Permanently delete unseen emails from the folder after processing.`Mark as Read After Processing`: Mark each unseen email as read after it is processed. | ## Response Structure The task returns a JSON object containing detailed information about the email: - `messageId`: The unique identifier of the email message. - `fromAddress`: The email addresses of the sender. - `headers` : A list of the email headers. - `subject`: The subject line of the email. - `size`: The size of the email (in bytes). - `plainTextBody`: The plain text version of the email content. - `htmlBody`: The HTML version of the email content, if it exists. - `attachments` A list of document reference - `receivedDateTime`: The date and time the email was received. :::note As of the 8.8 release, angle brackets (`<` and `>`) are no longer removed from the `messageId`. ::: #### Example Response The following example JSON response shows the data structure produced when an email triggers the creation of a process instance: ```json { "messageId": "messageId", "fromAddress": "example@camunda.com", "subject": "Urgent Test", "size": 65646, "plainTextBody": "Hey how are you?\r\n", "htmlBody": "Hello", "headers": [ { "key": "header1", "value": "example" }, { "key": "header2", "value": "test" } ], "attachments": [ { "storeId": "in-memory", "documentId": "20f1fd6a-d8ea-403b-813c-e281c1193495", "metadata": { "contentType": "image/webp; name=305a4816-b3df-4724-acd3-010478a54add.webp", "size": 311032, "fileName": "305a4816-b3df-4724-acd3-010478a54add.webp" }, "documentType": "camunda" } ], "receivedDateTime": "2024-08-19T06:54:28Z" } ``` This response includes essential email details such as the `messageId`, sender addresses, subject, size, and the content of the email both in plain text and HTML format. This information can be used by the process for various workflows, such as prioritizing tasks, content analysis, and automated responses. --- ## Email connector(Out-of-the-box-connectors) The **Email outbound connector** allows your BPMN service to send and receive emails via an email server. It supports multiple email protocols, including SMTP for sending emails, and POP3 and IMAP for receiving and managing emails. ## Prerequisites To use the **Email connector**, you must have an SMTP, POP3 or IMAP server available to connect to. :::note Use Camunda secrets to avoid exposing your sensitive data as plain text. See [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md). ::: ## Authentication You can authenticate to a mail server as follows. ### Simple authentication This method allows the user to connect to any SMTP, POP3 or IMAP server using an email address and password. | Parameter | Description | | :--------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `username` | Enter your full email address (for example, user@example.com) or the username provided by your email service. This is used to authenticate your access to the mail server. | | `password` | Enter the password for your email account. Keep your password secure and do not share it with others. | ### No authentication For SMTP servers that do not require authentication, select this option to connect without providing credentials. ## POP3 The Post Office Protocol version 3 (POP3) is an Internet standard protocol used by local email clients to retrieve emails from a remote server over a TCP/IP connection. POP3 allows users to download messages from their email server to their local computer, where they can be read, managed, or archived even without an internet connection. It operates on a simple download-and-delete model, meaning emails are typically removed from the server once they are retrieved. | Field | Description | | :----------------------- | :----------------------------------------------------------------------------------------------------- | | `POP3 host` | The host URL of the POP3 server. | | `POP3 port` | The host port of the POP3 server. | | `Cryptographic protocol` | Defines how the connection to the server is secured, `TLS`, `SSL` or `None`. Default is typically TLS. | ### List Emails Allow users to fetch a list of emails from the `INBOX` folder, with customizable sorting and limitation options. #### Parameters | Parameter | Description | | :------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `Max Emails to read` | Specify the maximum number of emails to retrieve. This parameter determines the cap on the number of emails the task will return. | | `Sort emails by` | Choose the field by which to sort the emails. Supported sorting fields are:`Sent date`: Sorts emails by the date and time they were sent.`Size`: Sorts emails by the size of the email. | | `Sort order` | Define the sort order:`ASC`: Ascending order, from the oldest or smallest value to the most recent or largest.`DESC`: Descending order, from the most recent or largest value to the oldest or smallest. | #### Sorting and Limiting Behavior Emails are initially sorted based on the specified sorting field and order. The list is then limited to the number of emails as defined by the Max Emails to read parameter. For example, if you sort by Sent date in descending order (DESC) with a limit of one email, the task will return the most recently sent email. #### Response Structure The task returns a list of emails in JSON format. Each email object contains the following information: - `messageId`: A unique identifier for the email message. - `fromAddress`: The email addresses of the sender. - `subject`: The subject line of the email. - `size`: The size of the email (in bytes). :::note As of the 8.8 release, angle brackets (`<` and `>`) are no longer removed from the `messageId`. ::: #### Example Response Example of a returned JSON array: ```json [ { "messageId": "RandomId", "fromAddress": "msa@communication.microsoft.com", "subject": "Example", "size": 99865 }, { "messageId": "RandomId2", "fromAddress": "example@camunda.com", "subject": "Example", "size": 48547 } ] ``` ### Read Email Retrieve the contents of an email, using the unique `messageId` associated with the email message. :::danger Reading an email using POP3 protocol will delete the email ::: #### Parameters | Parameter | Description | | :---------- | :---------------------------------------------------------------------------------------------------------- | | `MessageId` | The identifier of the email message you wish to read. Provide this to locate and return the specific email. | #### Response Structure The task returns a JSON object containing detailed information about the email: - `messageId`: The unique identifier corresponding to the email message. - `fromAddress`: The email addresses of the sender. - `headers` : A list containing the email's headers - `subject`: The subject line of the email. - `size`: The size of the email in bytes. - `plainTextBody`: The plain text version of the email's content. - `htmlBody`: The HTML version of the email's content (if content exists). - `attachments`: A list of all the email's attachments, provided as a document reference. - `receivedDateTime`: The email's reception datetime :::note As of the 8.8 release, angle brackets (`<` and `>`) are no longer removed from the `messageId`. ::: :::note Starting from version 8.7.0, the outbound email connector supports sending Camunda documents as attachments. For example, the **Attachment** field in the properties panel may look as `=[ document1, document2]`. See additional details and limitations in [document handling](/components/document-handling/getting-started.md). ::: #### Example Response Below is an example of the JSON response returned when a specific email is read: ```json { "messageId": "MessageId", "fromAddress": "example@camunda.com", "subject": "Example Subject", "size": 99865, "plainTextBody": "Any text content", "htmlBody": "Any Html Content", "headers": [ { "key": "header1", "value": "example" }, { "key": "header2", "value": "test" } ], "attachments": [ { "storeId": "in-memory", "documentId": "20f1fd6a-d8ea-403b-813c-e281c1193495", "metadata": { "contentType": "image/webp; name=305a4816-b3df-4724-acd3-010478a54add.webp", "size": 311032, "fileName": "305a4816-b3df-4724-acd3-010478a54add.webp" }, "documentType": "camunda" } ], "receivedDateTime": "2024-08-19T06:54:28Z" } ``` ### Delete Email Delete (remove) an email from the server, using the specific `messageId` assigned to the email message. #### Parameters | Parameter | Description | | :---------- | :--------------------------------------------- | | `MessageId` | The identifier of the email message to delete. | #### Response Structure After the deletion task is performed, a JSON object is returned to confirm the action: - `deleted`: A boolean value that indicates whether the deletion was successful (true) or not (false). - `messageId`: The identifier of the email message that was attempted to be deleted. :::note As of the 8.8 release, angle brackets (`<` and `>`) are no longer removed from the `messageId`. ::: #### Example Response The following JSON response shows the result of a successful deletion request: ```json { "deleted": true, "messageId": "MessageId" } ``` ### Search Emails Enable users to perform advanced searches within an email inbox, by constructing a criteria-based query using a JSON object. Supports complex queries that can combine multiple conditions using logical operators. #### Parameters A search query is represented as a JSON object. The following is an example of a JSON object that represents a search criteria using an AND and OR operator to combine multiple conditions: ```json { "operator": "AND", "criteria": [ { "field": "FROM", "value": "example@camunda.com" }, { "operator": "OR", "criteria": [ { "field": "SUBJECT", "value": "urgent" }, { "field": "SUBJECT", "value": "important" } ] } ] } ``` This example query returns emails from "example@camunda.com" with a subject containing either "urgent" or "important". A simpler query without logical operators might look like the following example: ```json { "field": "FROM", "value": "example@camunda.com" } ``` Search supports the following logical operators: - **AND**: Returns emails that match **all** the specified criteria. - **OR**: Returns emails that match **any of** the specified criteria. The following email fields can be used to set search criteria: - **BODY**: The content of the email body. - **SUBJECT**: The subject line of the email. - **FROM**: The email address of the sender. :::note When using an operator such as AND or OR, you must also define a criteria array. This array contains the individual conditions that the search will evaluate against the emails. Each condition within the criteria array is itself a JSON object with a field and a value. - If an operator is set, the criteria array must also be defined. - Each criterion within the criteria array is applied to the specified field based on the value associated with it. ::: #### Response Structure - `subject`: The email subject line. - `messageId`: The identifier of the email message that was attempted to be deleted. :::note As of the 8.8 release, angle brackets (`<` and `>`) are no longer removed from the `messageId`. ::: #### Example Response The following is an example of a returned response: ```json [ { "messageId": "MessageId", "subject": "Important" }, { "messageId": "MessageId2", "subject": "Urgent" } ] ``` ## SMTP Simple Mail Transfer Protocol (SMTP) is the standard communication protocol for sending emails across the Internet. It facilitates mail transfer from a client's email application to the outgoing mail server and between servers for relaying email messages to their final destination. SMTP operates on a push model, where the sending server pushes the message to the receiving server for delivery to the appropriate mailbox. | Field | Description | | :----------------------- | :----------------------------------------------------------------------------------------------------- | | `SMTP host` | The host URL of the SMTP server. | | `SMTP port` | The host port of the SMTP server. | | `Cryptographic protocol` | Defines how the connection to the server is secured, `TLS`, `SSL` or `None`. Default is typically TLS. | ### Send Email Allow users to send an email from the connected email account. #### Parameters | Parameter | Description | | :------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `From` | Specify the sender's email address(es). This can be a single email address (for example, 'example@camunda.com'), a comma-separated list of addresses, or a Friendly Enough Expression Language (FEEL) expression returning a list of email addresses (for example, =["example@camunda.com"]). | | `To` | Defines the email recipient(s). Similar to the `From` parameter, this can be a single email address, a comma-separated list, or a FEEL expression (for example, =["example@camunda.com"]). | | `Cc` | (Optional) Specify the email address(es) to include in the **Carbon Copy (CC)** field. The format is the same as the **From** and **To** fields, and can include a single address, a list, or a FEEL expression. | | `Bcc` | (Optional) Specify the email address(es) to include in the **Blind Carbon Copy (BCC)** field. It follows the same format as the **CC** field and ensures that BCC recipients are not visible to other recipients. | | `Headers` | Feel expression containing all the desired headers to be added to the email's headers. cf. `{ "customHeaders" : "new header value" }` | | `Subject` | The email subject line. | | `Content Type` | The content type of the email. | | `Email Text Content` | The text content of the email. This must only be provided if the `Content Type` is `PLAIN` or `HTML & PlainText`. | | `Html Text Content` | The HTML content of the email. This must only be provided if the `Content Type` is `HTML` or `HTML & PlainText`. | | `Attachment` | The document reference, either for a single document or as a list for multiple documents. | :::info To learn more about Friendly Enough Expression Language (FEEL) expression, see [what is FEEL?](/components/modeler/feel/what-is-feel.md). ::: #### Response Structure Upon successfully sending the email, the following JSON response is returned: - `subject`: Echoes back the subject of the sent email. - `sent`: A boolean value indicating the success status of the email being sent (true for success, false for failure). - `messageId`: A unique identifier for the email message. :::note As of the 8.8 release, angle brackets (`<` and `>`) are no longer removed from the `messageId`. ::: #### Example Response The following is an example of a successful send email operation: ```json { "subject": "Example Subject", "sent": true, "messageId": "" } ``` In this response: - `sent: true` confirms that the email with the specified subject "Example Subject" was successfully sent. - `sent: false` indicates the email failed to send. ## IMAP The Internet Message Access Protocol (IMAP) is a protocol used by email clients to access messages stored on a mail server, allowing users to view and manage their emails from multiple devices. Unlike POP3, IMAP supports both online and offline modes, synchronizes email across devices, and allows manipulation of mailboxes (create, delete, and rename) as well as messages (read, delete, or flag) directly on the server. | Field | Description | | :----------------------- | :----------------------------------------------------------------------------------------------------- | | `IMAP host` | The host URL of the IMAP server. | | `IMAP port` | The host port of the IMAP server. | | `Cryptographic protocol` | Defines how the connection to the server is secured, `TLS`, `SSL` or `None`. Default is typically TLS. | ### List Emails Allow users to fetch a list of emails from a specified folder, with customizable sorting and limitation options. #### Parameters | Parameter | Description | | :------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `Max Emails to read` | Specify the maximum number of emails to retrieve. This parameter determines the cap on the number of emails the task will return. | | `Sort emails by` | Choose the field by which to sort the emails. Supported sorting fields are:`Sent date`: Sorts emails by the date and time they were sent.`Size`: Sorts emails by the size of the email. | | `Sort order` | Define the sort order:`ASC`: Ascending order, from the oldest or smallest value to the most recent or largest.`DESC`: Descending order, from the most recent or largest value to the oldest or smallest. | | `Folder` | (Optional) the folder to list emails from, default is `INBOX`. For subfolders, use `.` or `/` separated path (ex: `inside/folder` or `inside.folder`) | #### Sorting and Limiting Behavior - Emails are initially sorted based on the specified sorting field and order. - The list is then limited to the number of emails as defined by the Max Emails to read parameter. For example, sort by Sent date in descending order (DESC) with a limit of one email, to return the most recently sent email. #### Response Structure The task returns a list of emails in JSON format. Each email object contains the following information: - `messageId`: A unique identifier for the email message. - `fromAddress`: the email addresses of the sender. - `subject`: The subject line of the email. - `size`: The size of the email in bytes. :::note As of the 8.8 release, angle brackets (`<` and `>`) are no longer removed from the `messageId`. ::: #### Example Response Example of a returned JSON array: ```json [ { "messageId": "RandomId", "fromAddress": "msa@communication.microsoft.com", "subject": "Example", "size": 99865 }, { "messageId": "RandomId2", "fromAddress": "example@camunda.com", "subject": "Example", "size": 48547 } ] ``` ### Read Email Retrieve an email's details based on the specified `messageId`. #### Parameters | Parameter | Description | | :---------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `MessageId` | The unique identifier of the email that must be read. | | `Folder` | (Optional) Specifies the folder from which the email should be retrieved. If not provided, the default folder is `INBOX`. For subfolders, use `.` or `/` separated path (ex: `inside/folder` or `inside.folder`) | #### Response Structure The task returns a JSON object containing detailed information about the email: - `messageId`: The unique identifier of the email message. - `fromAddress`: The email addresses of the sender. - `headers` : A list of the email headers. - `subject`: The subject line of the email. - `size`: The size of the email (in bytes). - `plainTextBody`: The plain text version of the email content. - `htmlBody`: The HTML version of the email content, if it exists. - `attachments`: A list of all the email's attachments, provided as a document reference. - `receivedDateTime`: The date and time the email was received. :::note As of the 8.8 release, angle brackets (`<` and `>`) are no longer removed from the `messageId`. ::: #### Example Response The following JSON structure shows an expected response after a successful email retrieval: ```json { "messageId": "MessageId", "fromAddress": "example@camunda.com", "subject": "Example Subject", "size": 99865, "plainTextBody": "Any text content", "htmlBody": "Any Html Content", "headers": [ { "key": "header1", "value": "example" }, { "key": "header2", "value": "test" } ], "attachments": [ { "storeId": "in-memory", "documentId": "20f1fd6a-d8ea-403b-813c-e281c1193495", "metadata": { "contentType": "image/webp; name=305a4816-b3df-4724-acd3-010478a54add.webp", "size": 311032, "fileName": "305a4816-b3df-4724-acd3-010478a54add.webp" }, "documentType": "camunda" } ], "receivedDateTime": "2024-08-19T06:54:28Z" } ``` ### Delete Email Delete an email from a specified folder, using the email's unique `messageId`. #### Parameters | Parameter | Description | | :---------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `MessageId` | The identifier of the email message to delete. | | `Folder` | (Optional) Specifies the folder from which the email should be deleted. If this parameter is not supplied, the default folder is assumed to be `INBOX`. For subfolders, use `.` or `/` separated path (ex: `inside/folder` or `inside.folder`) | #### Response Structure The task provides a JSON object in the response, indicating the outcome of the deletion request: - `deleted`: A boolean value that signifies whether the email was successfully deleted (true) or not (false). - `messageId`: Reiterates the `messageId` of the email that was targeted for deletion. :::note As of the 8.8 release, angle brackets (`<` and `>`) are no longer removed from the `messageId`. ::: #### Example Response The following is an example of the JSON response confirming successful email deletion: ```json { "deleted": true, "messageId": "MessageId" } ``` ### Search Emails Enable users to perform advanced searches within an email inbox by constructing a criteria-based query using a JSON object. Search supports complex queries that can combine multiple conditions using logical operators. #### Parameters A search query is represented as a JSON object. Below is an example of a JSON object that represents a search criteria using an AND and OR operator to combine multiple conditions: - `Folder`: (Optional) Specifies the folder from which the email should be deleted. If this parameter is not supplied, the default folder is assumed to be `INBOX`. For subfolders, use `.` or `/` separated path (ex: `inside/folder` or `inside.folder`) - `Criteria`: _See below_ ```json { "operator": "AND", "criteria": [ { "field": "FROM", "value": "example@camunda.com" }, { "operator": "OR", "criteria": [ { "field": "SUBJECT", "value": "urgent" }, { "field": "SUBJECT", "value": "important" } ] } ] } ``` This example query returns emails from "example@camunda.com" with a subject containing either "urgent" or "important". A simpler query without logical operators might look like the following example: ```json { "field": "FROM", "value": "example@camunda.com" } ``` Search supports the following logical operators: - **AND**: Returns emails that match all the specified criteria. - **OR**: Returns emails that match any of the specified criteria. The following email fields can be used to set search criteria: - **BODY**: The content of the email body. - **SUBJECT**: The subject line of the email. - **FROM**: The email address of the sender. :::note When using an operator such as AND or OR, you must also define a criteria array. This array contains the individual conditions that the search will evaluate against the emails. Each condition within the criteria array is itself a JSON object with a field and a value. - If an operator is set, the criteria array must also be defined. - Each criterion within the criteria array is applied to the specified field based on the value associated with it. ::: #### Response Structure - `subject`: The email subject line. - `messageId`: The identifier of the email message that was attempted to be deleted. :::note As of the 8.8 release, angle brackets (`<` and `>`) are no longer removed from the `messageId`. ::: #### Example Response The following is an example returned response: ```json [ { "messageId": "MessageId", "subject": "Important" }, { "messageId": "MessageId2", "subject": "Urgent" } ] ``` ### Move Email Enable users to transfer an email from one folder to another, streamlining inbox organization. #### Parameters | Parameter | Description | | :-------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `MessageId` | The identifier of the email that needs to be moved. | | `Source folder` | (Optional) The folder from which the email will be moved. If not specified, the default is INBOX. For subfolders, use `.` or `/` separated path (ex: `inside/folder` or `inside.folder`) | | `Target folder` | The destination folder where the email is placed. To specify a new folder or a nested hierarchy, use `.` or `/` separated path (for example, 'Archive/test' or 'Projects.2023.January'). The system automatically creates any non-existent folders in the path. | #### Response Structure Upon successful completion of the move operation, the response contains a JSON object with the following details: - `messageId`: The `messageId` of the email that was moved. - `from`: The source folder from which the email was moved. - `to`: The target folder to which the email has been moved. :::note As of the 8.8 release, angle brackets (`<` and `>`) are no longer removed from the `messageId`. ::: #### Example Response The example below shows the expected JSON response after an email has been successfully moved: ```json { "messageId": "", "from": "INBOX", "to": "TEST" } ``` --- ## Vector Database connector The **Vector Database connector** allows embedding, storing, and retrieving Large Language Model (LLM) embeddings. This enables building AI-based solutions such as context document search, long-term LLM memory, and agentic AI interaction. For an example, see how to [add long-term memory to your AI agents](/components/agentic-orchestration/rag-ai-agents.md). :::note The **Vector Database connector** uses the [LangChain4j](https://docs.langchain4j.dev/) library. Data models and possible implementations are limited to the latest stable released LangChain4j library. ::: ## Prerequisites Before using the **Vector Database connector**, ensure you understand the [concept of LLM embeddings](https://huggingface.co/spaces/hesamation/primer-llm-embedding). To start using the **Vector Database connector**, ensure you have access to a supported LLM embeddings API to convert document content into vectorized embedding form. You will also need to have write access to a supported database. ## Create a connector task ## Operations The **embed document** operation performs the following steps: 1. [Consume a document](#embedding-document-configuration). 2. Parse the document depending on a file format (optionally [split into text chunks](#splitting)). 3. Convert chunks into a vector form with LLM help. 4. Store produced vectors in a vector database. To perform this operation, enter the following: - **Operation** dropdown: **Embed document**. - **Embedding model**: Refer to the [relevant section](#embedding-models). - **Vector store**: Refer to the [relevant section](#vector-stores). - **Document**: Refer to the [relevant section](#embedding-document-configuration). As a result of this operation, you will get an array of created embedding chunk IDs, for example `["d599ec62-fe51-4a91-bbf0-26e1241f9079", "a1fad021-5148-42b4-aa02-7de9d590e69c"]`. ### Updating embedded documents Each time you embed a document, the connector generates a new set of chunks and stores them in the vector database. If the document was previously embedded, this creates duplicate chunks. To prevent duplicates: 1. Delete the existing chunks before re-embedding the document. 2. Use the chunk IDs returned by the previous embedding operation. 3. If the embedded document is from Camunda, use the `filename` metadata field to find the chunk IDs. 4. Follow your vector store’s documentation for deleting chunks. The **retrieve document** operation performs the following steps: 1. Consumes a query. 2. Convert the query into a vector form with LLM help. 3. Perform a vector similarity search on previously-stored LLM embeddings. 4. Store results in Camunda document storage. To perform this operation, enter the following: - **Operation** dropdown: **Retrieve document**. - **Search query**: Enter your search query. - **Max results**: Enter maximum amount of returned results. - **Min score**: Enter the lowest score threshold similarity value; the value should be between 0 and 1, for example, 0.81. - **Embedding model**: Refer to the [relevant section](#embedding-models). - **Vector store**: Refer to the [relevant section](#vector-stores). As a result of this operation, you will get an array of relevant chunks, where each includes a [chunk ID](#splitting), Camunda document reference metadata, similarity score, and the actual text content. ```json { "chunks": [ { "chunkId": "e30d570b-2a3a-4f4a-9a0c-78f0f1acd383", "documentReference": { "storeId": "local", "documentId": "2b36ec67-a78f-4f99-9371-8c6e5b332838", "contentHash": "1c232bc1e553c10d00c3327dcca9012b6b4b0758a1c2afaad8b77c80fa1bd36e", "metadata": { "size": 116, "fileName": null, "processDefinitionId": null, "processInstanceKey": null, "customProperties": {}, "expiresAt": null, "contentType": "text/plain" }, "camunda.document.type": "camunda" }, "score": 0.6721556, "content": "Camunda is a platform for orchestrating and automating business processes. It helps organizations design, execute, and manage workflows, enabling them to optimize processes and improve efficiency." } ] } ``` ## Embedding models The **Vector Database connector** supports [Amazon Titan V1 and V2 models](https://docs.aws.amazon.com/bedrock/latest/userguide/titan-embedding-models.html). You can also specify any custom model that supports text embedding and is available in your Amazon Bedrock account. To use Amazon Bedrock as an embedding model, provide: - **Access key** – Access key for a user with permissions for the Amazon Bedrock `InvokeModel` action. - **Secret key** – Secret key for the user associated with the provided access key. - **Region** – AWS region where the model is hosted (for example, `us-east-1`). See [AWS model region support](https://docs.aws.amazon.com/bedrock/latest/userguide/models-regions.html) for details. - **Model name** – One of: - **Amazon Titan V1** – `amazon.titan-embed-text-v1` - **Amazon Titan V2** – `amazon.titan-embed-text-v2:0` - **Custom model** – Name of your custom Amazon Bedrock embedding model. When using Amazon Titan V2, you can also specify: - **Embedding dimensions** – Number of dimensions for the embedding vector. - **Normalize** – Whether to normalize the embedding vector. See [AWS blog](https://aws.amazon.com/blogs/aws/amazon-titan-text-v2-now-available-in-amazon-bedrock-optimized-for-improving-rag/) for more details. For all models, the following parameter is optional: - **Max retries** – Maximum number of retries for the embedding request in case of failure. To use OpenAI as an embedding model, provide: - **API key** – Your OpenAI account API key for authorization. - **Model name** – The OpenAI model to use for embeddings. See the [OpenAI documentation](https://platform.openai.com/docs/guides/embeddings) for available models. Optional parameters include: - **Organization ID** – For projects accessed through a legacy user API key, specify the organization ID for API requests with this connector. - **Project ID** – For projects accessed through a legacy user API key, specify the project ID for API requests with this connector. - **Embedding dimensions** – Number of dimensions for the embedding vector. If not specified, the default value for the selected model is used. - **Custom headers** – Additional headers to include in the request. - **Custom base URL** – Base URL for API requests when using a custom OpenAI endpoint. - **Max retries** – Maximum number of retries for the embedding request in case of failure. To use Azure OpenAI as an embedding model, provide: - **Endpoint** – The Azure OpenAI endpoint URL, for example `https://.openai.azure.com/`. - **Authentication** – Select the authentication type to use with Azure OpenAI. Optional parameters include: - **Embedding dimensions** – Number of dimensions for the embedding vector. If not specified, the default value for the selected model is used. - **Custom headers** – Additional headers to include in the request. - **Max retries** – Maximum number of retries for the embedding request in case of failure. Two authentication methods are supported: - **API key** – Authenticate using an Azure OpenAI API key from the [Azure AI Foundry portal](https://ai.azure.com/). - **Client credentials** – Authenticate using a client ID and secret. This requires registering an application in [Microsoft Entra ID](https://go.microsoft.com/fwlink/?linkid=2083908). Provide: - **Client ID** – The Microsoft Entra application ID. - **Client secret** – The application’s client secret. - **Tenant ID** – The Microsoft Entra tenant ID. - **Authority host** – _(Optional)_ The authority host URL. Defaults to `https://login.microsoftonline.com/`. Can also be an OAuth 2.0 token endpoint. To use Google Vertex AI as an embedding model, provide: - **Project ID** – The Google Cloud project ID. - **Region** – The [region](https://cloud.google.com/vertex-ai/docs/general/locations#feature-availability) where AI inference should take place. - **Authentication** – Select the authentication type for connecting to Google Cloud. - **Model name** – The Vertex AI model to use for embeddings. Refer to the [Vertex AI documentation](https://cloud.google.com/vertex-ai/docs/generative-ai/embeddings) for available models. - **Embedding dimensions** – Number of dimensions for the embedding vector. Consult the documentation for the selected model for valid ranges. Optional parameters include: - **Publisher** – The publisher of the Vertex AI model. Defaults to `google` if not specified. - **Max retries** – Maximum number of retries for the embedding request in case of failure. Two authentication methods are supported: - **Service Account Credentials** – Authenticate using a [service account](https://cloud.google.com/iam/docs/service-account-overview) key in JSON format. - **Application Default Credentials (ADC)** – Authenticate using the default credentials available in your environment. This method is only supported in Self-Managed or hybrid environments. To set up ADC in a local development environment, follow the instructions [here](https://cloud.google.com/docs/authentication/set-up-adc-local-dev-environment). ## Vector stores Enter the following parameters: - **Base URL** – The Elasticsearch base URL, including protocol, for example `https://host:port`. - **Username** – For the Elasticsearch user that has read/write access. - **Password** – For the Elasticsearch user that has read/write access. - **Index name** – Name of the index where you wish to store embeddings. - When embedding: If the index is not present, the connector will create a new one. - When retrieving: If the index is absent, the connector will raise an error. :::important The Elasticsearch version must be 8+. ::: Enter the following parameters: - **Base URL** – The OpenSearch base URL, including protocol, for example `https://host:port`. - **Username** – For the OpenSearch user that has read/write access. - **Password** – For the OpenSearch user that has read/write access. - **Index name** – Name of the index where you wish to store embeddings. - When embedding: If the index is not present, the connector will create a new one. - When retrieving: If the index is absent, the connector will raise an error. Enter the following parameters: - **Access key** and **Secret key** – Enter AWS IAM credentials for the user that has read/write access. - **Server URL** – An Amazon OpenSearch URL _without_ protocol, for example `my-opensearch.aws.com:port`. - **Region** – Region of the Amazon OpenSearch instance. - **Index name** – Name of the index where you wish to store embeddings. - When embedding: If the index is not present, the connector will create a new one. - When retrieving: If the index is absent, the connector will raise an error. Enter the following parameters: - **Endpoint** – The Azure AI Search endpoint URL, for example `https://.search.windows.net/`. - **Authentication** – Select the authentication type for connecting to Azure AI Search. - **Index name** – Name of the index where embeddings will be stored. - When embedding: If the index is not present, the connector will create it. - When retrieving: If the index is absent, the connector will raise an error. Two authentication methods are supported: - **API key** – Authenticate using an Azure AI Search key. - **Client credentials** – Authenticate using a client ID and secret. This requires registering an application in [Microsoft Entra ID](https://go.microsoft.com/fwlink/?linkid=2083908) and assigning the [required roles](https://learn.microsoft.com/en-us/azure/search/search-security-rbac). Role-based access control must be explicitly enabled for the Azure AI Search resource. Provide the following fields: - **Client ID** – The Microsoft Entra application ID. - **Client secret** – The application’s client secret. - **Tenant ID** – The Microsoft Entra tenant ID. - **Authority host** – _(Optional)_ The authority host URL. Defaults to `https://login.microsoftonline.com/`. This can also be an OAuth 2.0 token endpoint. Enter the following parameters: - **Endpoint** – The Azure Cosmos DB NoSQL endpoint URL, for example `https://.documents.azure.com/`. - **Authentication** – Select the authentication type for connecting to Azure Cosmos DB NoSQL. - **Database name** – The name of the Azure Cosmos DB NoSQL database. - **Container name** – The name of the Azure Cosmos DB NoSQL container. _Note:_ The container must already exist and have an `/id` partition key. - **Consistency level** – The consistency level for the container. Defaults to `Eventual`. - **Distance function** – The distance function to use for vector similarity search. Defaults to `Cosine`. - **Vector index type** – The vector index type to use. Defaults to `Flat`. :::info For more information about Azure Cosmos DB NoSQL vector search, refer to the [official documentation](https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/vector-search). Pay special attention to the vector dimensions limitations as stated in the documentation. ::: Two authentication methods are supported: - **API key** – Authenticate using an Azure Cosmos DB key. - **Client credentials** – Authenticate using a client ID and secret. This requires registering an application in [Microsoft Entra ID](https://go.microsoft.com/fwlink/?linkid=2083908). Provide the following fields: - **Client ID** – The Microsoft Entra application ID. - **Client secret** – The application’s client secret. - **Tenant ID** – The Microsoft Entra tenant ID. - **Authority host** – _(Optional)_ The authority host URL. Defaults to `https://login.microsoftonline.com/`. Can also be an OAuth 2.0 token endpoint. ## Embedding document configuration ### Document source The **Document source** can be either **Plain text** or a **Camunda document**. **Plain text** can be useful when you deal with small size data that can fit into a text field or a process variable. Input will be handled as a regular UTF-8 text. :::note A FEEL [string conversion function](/components/modeler/feel/builtin-functions/feel-built-in-functions-conversion.md#stringfrom) might be useful if you have JSON input. ::: The **Camunda document** might be useful when you deal with larger document pipelines that come from [webhook or user tasks](/components/document-handling/getting-started.md). Input documents will be parsed with [Apache Tika](https://tika.apache.org/), so files can be of any Apache Tika-supported formats. ### Splitting **Splitting** is an action of breaking large documents into smaller pieces. It can be either recursive or no splitting at all. Seek guidance from your local data scientist to determine if you require splitting. Learn more about splitting in the [LangChain4j documentation](https://docs.langchain4j.dev/tutorials/rag#document-splitter). ## HTTP proxy configuration In Self-Managed environments, the Vector Database connector supports routing HTTP requests through an HTTP proxy. This applies to both embedding model API calls and vector store connections. The Vector Database connector supports [plain proxy variables](/self-managed/components/connectors/http-proxy-configuration.md#plain-proxy-variables) in addition to the standard connector proxy variables. Refer to the [HTTP proxy configuration](/self-managed/components/connectors/http-proxy-configuration.md) page for the full list of environment variables and configuration options. The following providers do not support connector proxy variables, but respect standard [JVM proxy properties](/self-managed/components/connectors/http-proxy-configuration.md#jvm-properties): - Google VertexAI (embedding model). - Azure AI Search (vector store). The following providers do not support proxy configuration: - Azure Cosmos DB NoSQL (vector store). To disable proxy support entirely (for example, if only an HTTPS-based proxy is available), set the following environment variable: ```bash CAMUNDA_CONNECTOR_VECTORDB_HTTP_PROXYSUPPORT_ENABLED=false ``` --- ## GitHub connector The **GitHub connector** is an outbound connector that allows you to connect your BPMN service with [GitHub](https://github.com/) to manage [GitHub](https://github.com/) issues and releases. ## Prerequisites To use the **GitHub connector**, you must have a GitHub instance and an [access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) of a user or a service account on whose behalf a BPMN process will be executed. :::note Use Camunda secrets to avoid exposing your GitHub access token credentials as plain text. Refer to our documentation on [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ::: ## Create a GitHub connector task ## Authentication In the **Authentication** section, select an **Authentication type** from the dropdown: - **Personal Access Token**: Use a personal access token for authentication. - **GitHub App**: Use a GitHub App for authentication. ### PAT If you select **PAT**, provide the following: - **GitHub Personal token**: Your personal access token. ### GitHub App If you select **GitHub App**, provide the following: - **Private key**: The private key of your GitHub App. It is recommended to use [secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to store this value securely. - **App ID**: The ID of your GitHub App. - **Installation ID**: The installation ID of your GitHub App. Refer to the [GitHub documentation](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app-installation#generating-an-installation-access-token) for more information on how to obtain the installation ID. ## Select operation to execute The **GitHub connector** currently supports the following operations. ### Issues #### Create an issue - **GitHub API:** [Create an issue](https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#create-an-issue). - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repo:** The name of the repository. The name is not case-sensitive. - **Title:** The title of the issue. - **Body:** The contents of the issue. - **Assignees:** Logins for users to assign to this issue. Only users with push access can set assignees for new issues. Assignees are silently dropped otherwise. - **Labels:** Labels to associate with this issue. Only users with push access can set labels for new issues. Labels are silently dropped otherwise. - **Milestone:** The number of the milestone to associate this issue with or use null to remove the current milestone. Only users with push access can set the milestone for issues. Without push access to the repository, milestone changes are silently dropped. #### Get an issue - **GitHub API:** [Get an issue](https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#get-an-issue). - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repo:** The name of the repository. The name is not case-sensitive. - **Issue number:** The number that identifies the issue. #### Update an issue - **GitHub API:** [Update an issue](https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#update-an-issue). - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repo:** The name of the repository. The name is not case-sensitive. - **Issue number:** The number that identifies the issue. - **Title:** The title of the issue. - **Body:** The contents of the issue. - **Assignees:** Logins for users to assign to this issue. Only users with push access can set assignees for new issues. Assignees are silently dropped otherwise. - **Labels:** Labels to associate with this issue. Only users with push access can set labels for new issues. Labels are silently dropped otherwise. - **Milestone:** The number of the milestone to associate this issue with or use null to remove the current milestone. Only users with push access can set the milestone for issues. Without push access to the repository, milestone changes are silently dropped. - **Issue state:** The open or closed state of the issue. Can be open or closed. - **State reason:** The reason for the state change. Ignored unless state is changed. Can be one of: completed, not_planned, reopened, null. #### Create an issue comment - **GitHub API:** [Create an issue comment](https://docs.github.com/en/rest/issues/comments?apiVersion=2022-11-28#create-an-issue-comment). - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repo:** The name of the repository. The name is not case-sensitive. - **Issue number:** The number that identifies the issue. - **Body:** The contents of the comment. #### Search issue - **GitHub API:** [Search issue](https://docs.github.com/en/rest/search?apiVersion=2022-11-28#search-issues-and-pull-requests). - **Query:** The query contains one or more search keywords and qualifiers. Qualifiers allow you to limit your search to specific areas of GitHub. The GitHub REST API supports the same qualifiers as the web interface for GitHub. To learn more about the format of the query, refer to [constructing a search query](https://docs.github.com/en/rest/search?apiVersion=2022-11-28#constructing-a-search-query). Refer to [searching issues and pull requests](https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests) for a detailed list of qualifiers. #### List commits - **GitHub API:** [List commits](https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28#list-commits). - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repo:** The name of the repository. The name is not case-sensitive. ### Branches #### List branches - **GitHub API:** [List branches](https://docs.github.com/en/rest/branches/branches?apiVersion=2022-11-28#list-branches). - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repo:** The name of the repository. The name is not case-sensitive. #### Get a branch - **GitHub API:** [Get a branch](https://docs.github.com/en/rest/branches/branches?apiVersion=2022-11-28#get-a-branch). - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repo:** The name of the repository. The name is not case-sensitive. - **Branch:** The name of the branch. Cannot contain wildcard characters. To use wildcard characters in branch names, use the GraphQL API. #### Merge a branch - **GitHub API:** [Merge a branch](https://docs.github.com/en/rest/branches/branches?apiVersion=2022-11-28#merge-a-branch). - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repo:** The name of the repository. The name is not case-sensitive. - **Base:** The name of the base branch that the head will be merged into. - **Head:** The head to merge. This can be a branch name or a commit SHA1. ### Code scanning #### List code scanning alerts for an organization - **GitHub API:** [List code scanning alerts for an organization](https://docs.github.com/en/rest/code-scanning?apiVersion=2022-11-28#list-code-scanning-alerts-for-an-organization). - **Organization name:** The organization name. The name is not case-sensitive. #### List code scanning alerts for a repository - **GitHub API:** [List code scanning alerts for a repository](https://docs.github.com/en/rest/code-scanning?apiVersion=2022-11-28#list-code-scanning-alerts-for-a-repository). - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repo:** The name of the repository. The name is not case-sensitive. ### Organization #### Create an organization invitation - **GitHub API:** [Create an organization invitation](https://docs.github.com/en/rest/orgs/members?apiVersion=2022-11-28#create-an-organization-invitation). - **Organization name:** The organization name. The name is not case-sensitive. ### Release #### Create a release - **GitHub API:** [Create a release](https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#create-a-release). - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repo:** The name of the repository. The name is not case-sensitive. - **Body:** Text describing the contents of the tag. - **Tag name:** The name of the tag. - **Release name:** The name of the release. - **Make latest:** Specifies whether this release should be set as the latest release for the repository. Drafts and pre-releases cannot be set as latest. Defaults to true for newly published releases. Legacy specifies that the latest release should be determined based on the release creation date and higher semantic version. Default: true. Can be one of: true, false, legacy. #### Update a release - **GitHub API:** [Update a release](https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#update-a-release). - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repository:** The name of the repository. The name is not case-sensitive. - **Release ID:** The unique identifier of the release. - **Body:** Text describing the contents of the tag. - **Tag name:** The name of the tag. - **Release name:** The name of the release - **Make latest:** Specifies whether this release should be set as the latest release for the repository. Drafts and pre-releases cannot be set as latest. Defaults to true for newly published releases. Legacy specifies that the latest release should be determined based on the release creation date and higher semantic version. Default: true. Can be one of: true, false, legacy. #### Delete a release - **GitHub API:** [Delete a release](https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#delete-a-release). - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repository:** The name of the repository. The name is not case-sensitive. - **Release ID:** The unique identifier of the release. #### List releases - **GitHub API:** [List releases](https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#list-releases). - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repo:** The name of the repository. The name is not case-sensitive. ### Repository #### List organization repositories - **GitHub API:** [List organization repositories](https://docs.github.com/en/rest/repos/repos?apiVersion=2022-11-28#list-organization-repositories). - **Organization name:** The organization name. The name is not case-sensitive. #### Create an organization repository - **GitHub API:** [Create an organization repository](https://docs.github.com/en/rest/repos/repos?apiVersion=2022-11-28#create-an-organization-repository). - **Organization name:** The organization name. The name is not case-sensitive. - **Repository name:** The organization name. The name is not case-sensitive. - **Description:** A short description of the repository. - **Home page:** A URL with more information about the repository. - **Visibility:** The visibility of the repository. Can be one of: public, private. #### Get a repository - **GitHub API:** [Get a repository](https://docs.github.com/en/rest/repos/repos?apiVersion=2022-11-28#get-a-repository). - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repo:** The name of the repository. The name is not case-sensitive. #### Update a repository - **GitHub API:** [Update a repository](https://docs.github.com/en/rest/repos/repos?apiVersion=2022-11-28#update-a-repository). - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repo:** The name of the repository. The name is not case-sensitive. - **Repository name:** The organization name. The name is not case-sensitive. - **Description:** A short description of the repository. - **Home page:** A URL with more information about the repository. - **Visibility:** The visibility of the repository. Can be one of: public, private. #### Delete a repository - **GitHub API:** [Delete a repository](https://docs.github.com/en/rest/repos/repos?apiVersion=2022-11-28#delete-a-repository). - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repo:** The name of the repository. The name is not case-sensitive. #### List repository contributors - **GitHub API:** [List repository contributors](https://docs.github.com/en/rest/repos/repos?apiVersion=2022-11-28#list-repository-contributors). - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repo:** The name of the repository. The name is not case-sensitive. #### Get repository content - **GitHub API:** [Get repository content](https://docs.github.com/en/rest/repos/contents?apiVersion=2022-11-28#get-repository-content). - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repo:** The name of the repository. The name is not case-sensitive. - **Path:** The path of the content within the repository. - **Ref:** The name of the commit/branch/tag. Defaults to the repository’s default branch. #### Create or update file contents - **GitHub API:** [Create or update file contents](https://docs.github.com/en/rest/repos/contents?apiVersion=2022-11-28#create-or-update-file-contents). - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repo:** The name of the repository. The name is not case-sensitive. - **Path:** The path of the content within the repository. - **Branch:** The name of the target branch for the new commit. Defaults to the repository’s default branch. - **Commit message**: The commit message for the file change. - **Content**: A string or [document reference](components/document-handling/getting-started.md). If your content is already base64 encoded, please check the **Content is already base64 encoded** checkbox. - **SHA**: The blob SHA of the file being replaced. If you are creating a new file, this parameter is not required. Use [Get repository content](#get-repository-content) to retrieve the SHA of an existing file. - **Use custom author/committer**: If checked, you can specify custom author and committer information. Otherwise, the author and committer will be the owner of the PAT. ### Actions #### Create workflow dispatch event - **GitHub API:** [Create workflow dispatch event](https://docs.github.com/en/rest/actions/workflows?apiVersion=2022-11-28#create-a-workflow-dispatch-event) - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repo:** The name of the repository. The name is not case-sensitive - **Workflow ID:** The ID of the workflow. You can also pass the workflow file name as a string. - **Git reference:** The branch or tag name for the workflow. - **Workflow inputs:** An object containing up to 10 key-value pairs. Inputs are configured in the workflow file. ### References #### Create a reference - **GitHub API:** [Create a reference](https://docs.github.com/en/rest/git/refs?apiVersion=2022-11-28#create-a-reference) - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repo:** The name of the repository. The name is not case-sensitive - **Git reference:** The name of the fully qualified reference (i.e: `refs/heads/master`). - **SHA1:** The SHA1 value to create this reference from. ### Pulls #### Create a pull request - **GitHub API:** [Create a pull request](https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#create-a-pull-request). - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repo:** The name of the repository. The name is not case-sensitive - **Title:** The title of the new pull request. - **Head:** The name of the branch where your changes are implemented. - **Base:** The name of the branch you want the changes pulled into. - **Body:** The contents of the pull request. - **Draft:** Indicates whether the pull request is a draft. ### Collaborators #### List repository collaborators - **GitHub API:** [List repository collaborators](https://docs.github.com/en/rest/collaborators/collaborators?apiVersion=2022-11-28#list-repository-collaborators). - **Owner:** The account owner of the repository. The name is not case-sensitive. - **Repo:** The name of the repository. The name is not case-sensitive - **Page:** The page number of the results to fetch. - **Results per page:** The number of results per page. ## Handle connector response The **GitHub connector** is a protocol connector, meaning it is built on top of the **HTTP REST connector**, therefore handling response is still applicable [as described](/components/connectors/protocol/rest.md#response). The **GitHub Webhook connector** is an inbound connector that allows you to start a BPMN process instance triggered by a [GitHub event](https://docs.github.com/en/developers/webhooks-and-events/webhooks/about-webhooks). ## Create a GitHub Webhook connector task 1. Start building your BPMN diagram. You can use GitHub Webhook connector with either **Start Event** or **Intermediate Catch Event** building blocks. 2. Select the applicable element and change its template to a GitHub Webhook. 3. Fill in all required properties. 4. Complete your BPMN diagram. 5. Deploy the diagram to activate the webhook. 6. Navigate to the **Webhooks** tab in the properties panel on the right side of the screen to observe the webhook URL. ## Make your GitHub Webhook connector for receiving messages executable 1. In the **Webhook Configuration** section, configure the **Webhook ID**. By default, **Webhook ID** is pre-filled with a random value. This value will be part of the Webhook URL. You will find more details about GitHub Webhook URLs [below](#activate-the-github-webhook-connector-by-deploying-your-diagram). 2. Set the **GitHub secret**. This is a shared secret key that has to be defined in both your BPMN and GitHub webhook configuration page. The value is used to calculate HMAC authentication signature. 3. Configure **Activation Condition**. For example, given GitHub triggers a webhook endpoint with a new PR payload `{"action": "opened", "pull_request": ...}`, the **Activation Condition** value might look like as `=(request.body.action = "opened")`. Leave this field empty to trigger your webhook every time. 4. Use **Result Variable** to store the response in a process variable. For example, `myResultVariable`. 5. Use **Result Expression** to map specific fields from the response into process variables using [FEEL](/components/modeler/feel/what-is-feel.md). For example, given that the GitHub webhook is triggered with the body `{"pull_request": {"id": 123}}` and you would like to extract the pull request `id` as a process variable `pullRequestId`, the **Result Expression** might look like this: ``` = { pullRequestId: request.body.pull_request.id } ``` ### Correlation The **Correlation** section allows you to configure the message correlation parameters. :::note The **Correlation** section is not applicable for the plain **start event** element template of the GitHub Webhook connector. Plain **start events** are triggered by process instance creation and do not rely on message correlation. ::: ### Correlation keys - **Correlation key (process)** is a FEEL expression that defines the correlation key for the subscription. This corresponds to the **Correlation key** property of a regular **message intermediate catch event**. - **Correlation key (payload)** is a FEEL expression used to extract the correlation key from the incoming message. This expression is evaluated in the connector Runtime and the result is used to correlate the message. For example, given that your correlation key is defined with `pullRequestId` process variable, and the request body contains `{"pull_request": {"id": 123}}`, your correlation key settings will look like this: - **Correlation key (process)**: `=pullRequestId` - **Correlation key (payload)**: `=request.body.pull_request.id` Learn more about correlation keys in the [messages guide](../../../concepts/messages). #### Message ID expression The **Message ID expression** is an optional field that allows you to extract the message ID from the incoming message. The message ID serves as a unique identifier for the message and is used for message correlation. This expression is evaluated in the connector Runtime and the result is used to correlate the message. In most cases, it is not necessary to configure the **Message ID expression**. However, it is useful if you want to ensure message deduplication or achieve certain message correlation behavior. Learn more about how message IDs influence message correlation in the [messages guide](../../../concepts/messages#message-correlation-overview). For example, if you want to set the message ID to the value of the `pull_request.id` field in the incoming request, you can configure the **Message ID expression** as follows: ``` = request.body.pull_request.id ``` #### Message TTL The **Message TTL** is an optional field that allows you to set the time-to-live (TTL) for the correlated messages. TTL defines the time for which the message is buffered in Zeebe before being correlated to the process instance (if it can't be correlated immediately). The value is specified as an ISO 8601 duration. For example, `PT1H` sets the TTL to one hour. Learn more about the TTL concept in Zeebe in the [message correlation guide](../../../concepts/messages#message-buffering). ## Activate the GitHub Webhook connector by deploying your diagram Once you click the **Deploy** button, your GitHub Webhook will be activated and publicly available. URLs of the exposed GitHub Webhooks adhere to the following pattern: `http(s):///inbound/>` - `` is the URL of connectors component deployment. When using the Camunda 8 SaaS offering, this will typically contain your **region Id** and **cluster Id**, found in your client credentials under the **API** tab within your cluster. - `` is the ID (path) you configured in the properties of your GitHub Webhook connector. If you make changes to your GitHub Webhook connector configuration, you need to redeploy the BPMN diagram for the changes to take effect. When you click on the event with GitHub Webhook connector applied to it, a new **Webhooks** tab will appear in the properties panel. This tab displays the URL of the GitHub Webhook connector for every cluster where you have deployed your BPMN diagram. :::note The **Webhooks** tab is only supported in Web Modeler as part of the Camunda 8 SaaS offering. You can still use GitHub Webhook connector in Desktop Modeler, or with your Camunda 8 Self-Managed. In that case, GitHub Webhook connector deployments and URLs will not be displayed in Modeler. ::: ## Configure GitHub 1. Ensure you have administrator rights for the repository where you wish to enable a webhook. 2. Open a repository in your web browser and navigate to the **Settings** page. 3. Click **Webhooks > Add webhook**. 4. Fill out the required fields: 1. **Payload URL** - URL of your webhook. 2. **Content type** - Select `application/json`. 3. **Secret** - Shared secret between GitHub and your BPMN diagram. 5. Confirm by clicking **Add webhook**. Refer to the [GitHub documentation](https://docs.github.com/en/rest/webhooks) for more details. ## Next steps - Learn more about [GitHub webhooks](https://docs.github.com/en/developers/webhooks-and-events/webhooks/about-webhooks). --- ## GitLab connector The **GitLab connector** is an outbound connector that allows you to connect your BPMN service with [GitLab](https://about.gitlab.com/) to manage GitLab issues and releases. ## Prerequisites To use the **GitLab connector**, you must have a GitLab instance and an [access token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html) of a user or a service account on whose behalf a BPMN process will be executed. :::note Use Camunda secrets to avoid exposing your GitLab access token credentials as plain text. Refer to our documentation on [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ::: ## Create a GitLab connector task ## Add endpoint and authentication In the **HTTP Endpoint** section, provide a **GitLab base URL**, i.e. `https://gitlab.mycorp.com`, and a **GitLab access token**. ## Select operation to execute The **GitLab connector** currently supports the following operations. ### Issues #### Get an issue by ID - **GitLab API:** [Single project issue](https://docs.gitlab.com/ee/api/issues.html#single-project-issue). - **Project ID:** The global ID or URL-encoded path of the project owned by the authenticated user. - **Issue ID:** The internal ID of a project’s issue. #### Create an issue - **GitLab API:** [New issue](https://docs.gitlab.com/ee/api/issues.html#new-issue). - **Project ID:** The global ID or URL-encoded path of the project owned by the authenticated user. - **Title:** The title of an issue. - **Description:** The description of an issue. #### Delete an issue - **GitLab API:** [Delete an issue](https://docs.gitlab.com/ee/api/issues.html#delete-an-issue). - **Project ID:** The global ID or URL-encoded path of the project owned by the authenticated user. - **Issue ID:** The internal ID of a project’s issue. #### Comment to an issue - **GitLab API:** [Create a new issue note](https://docs.gitlab.com/ee/api/notes.html#create-new-issue-note). - **Project ID:** The global ID or URL-encoded path of the project owned by the authenticated user. - **Issue ID:** The internal ID of a project’s issue. - **Note text:** The content of a note. - **Level of confidentiality:** Indicates if an issue has to be marked as **internal** or not. #### Search issues - **GitLab API:** [List issues](https://docs.gitlab.com/ee/api/issues.html#list-issues). - **Scope:** Return issues for the given scope: **Created by me**, **Assigned to me**, or **all**. - **State:** Return all issues or just those that are **opened** or **closed**. - **Assignee ID:** Return issues assigned to the given user ID. Mutually exclusive with **Assignee username**. **None** returns unassigned issues. **Any** returns issues with an assignee. - **Assignee username:** Return issues assigned to the given username. Similar to **Assignee ID** and mutually exclusive with **Assignee ID**. - **Author ID:** Return issues created by the given user ID. - **Contains text:** Search issues against their **Title** and **Description**. ### Releases #### List all releases by a project ID - **GitLab API:** [List releases](https://docs.gitlab.com/ee/api/releases/#list-releases). - **Project ID:** The global ID or URL-encoded path of the project owned by the authenticated user. #### Get release by a tag name - **GitLab API:** [Get a release by a tag name](https://docs.gitlab.com/ee/api/releases/#get-a-release-by-a-tag-name). - **Project ID:** The global ID or URL-encoded path of the project owned by the authenticated user. - **Tag name:** The Git tag the release is associated with. #### Create a release - **GitLab API:** [Create a release](https://docs.gitlab.com/ee/api/releases/#create-a-release). - **Project ID:** The global ID or URL-encoded path of the project owned by the authenticated user. - **Tag name:** The tag where the release is created from. - **Ref:** A commit SHA, another tag name, or a branch name. - **Release name:** The release name. - **Description:** The description of the release. ### Branches #### List repository branches - **GitLab API:** [List repository branches](https://docs.gitlab.com/ee/api/branches.html#list-repository-branches). - **Project ID:** The global ID or URL-encoded path of the project owned by the authenticated user. - **Search String:** Return list of branches containing the search string. - **Regular Expression:** Return list of branches with names matching a [re2](https://github.com/google/re2/wiki/Syntax) regular expression. ### Create repository branch - **GitLab API:** [Create repository branch](https://docs.gitlab.com/ee/api/branches.html#create-repository-branch). - **Project ID:** The global ID or URL-encoded path of the project owned by the authenticated user. - **Name:** The name of the new branch. - **Reference:** The branch name or commit SHA to create the branch from. ### Repository files #### Create new file in repository - **GitLab API:** [Create new file in repository](https://docs.gitlab.com/ee/api/repository_files.html#create-new-file-in-repository). - **Project ID:** The global ID or URL-encoded path of the project owned by the authenticated user. - **Branch name:** Name of the new branch to create the file in. The commit is added to this branch. - **Commit message:** Message of the commit that adds the new file. - **Content:** The content of the new file. - **File path:** URL-encoded full path to new file. For example, `lib%2Fclass%2Erb`. - **Author email:** The commit author's email address. - **Author name:** The commit author's name. - **Encoding:** The encoding of the content. GitLab's default is `text`. - **Execute file mode:** Enables or disables the `execute` flag on the file. - **Start branch:** The branch to start the new branch from. - **Allow collaboration:** Allow commits from members who can merge to the target branch. ## Merge requests ### Create merge request - **GitLab API:** [Create merge request](https://docs.gitlab.com/ee/api/merge_requests.html#create-mr). - **Project ID:** The global ID or URL-encoded path of the project owned by the authenticated user. - **Source branch:** Name of the source branch. - **Target branch:** Name of the target branch. - **Title:** Title of the merge request. - **Assignee IDs:** The IDs of the users to assign the merge request to as an array of numbers. - **Description:** Description of the merge request. - **Labels:** Comma-separated list of label names for the merge request. - **Milestone ID:** The ID of a milestone to assign the merge request to. - **Remove source branch:** Flag indicating if a merge request should remove the source branch when merging. - **Reviewer IDs:** The ID of the users to set as reviewers of the merge request as an array of numbers. - **Squash:** Flag indicating if commits should be squashed into a single commit when merging. - **Target project ID:** Numeric ID of the target project. ## Handle connector response The **GitLab connector** is a protocol connector, meaning it is built on top of the **HTTP REST connector**, therefore handling response is still applicable [as described](/components/connectors/protocol/rest.md#response). --- ## Google Cloud storage connector The **Google Cloud Storage connector** is an outbound connector that allows you to interact with [Google Cloud Storage](https://cloud.google.com/storage?hl=en) resources from your BPMN processes. :::note The connector currently supports uploading and downloading documents. ::: ## Prerequisites To begin using the **Google Cloud Storage connector**, ensure the following are set up: - A Google [service account](https://cloud.google.com/iam/docs/service-account-overview) - A [JSON key for the service account](https://cloud.google.com/iam/docs/keys-create-delete) - A Google Cloud Storage [bucket](https://cloud.google.com/storage/docs/creating-buckets) The service account must be granted access to the bucket: - To **upload and download** objects, assign the `Storage Object Admin` role. - To **download only**, assign the `Storage Object Viewer` role. ## Authentication ### Create a new connector secret To keep your **JSON key** secure, avoid including it directly in the BPMN XML. Instead, create a secret: 1. Follow our [guide for creating secrets](/components/hub/organization/manage-clusters/manage-secrets.md). 2. Name your secret (e.g., `GOOGLE_SERVICE_ACCOUNT_KEY`) for reference in the connector configuration. ## Operation Select the desired operation from the **Operation** dropdown. ### Upload document 1. Enter the **project ID**. 2. Enter the **bucket name**. 3. (Optional) Enter the **File name**. If left blank, the filename from the document metadata will be used. 4. Reference the **Document to upload**. :::note If a document with the same name already exists in the bucket, it will be overwritten. If object versioning is enabled, previous versions can be restored. ::: ### Download document 1. Enter the **project ID**. 2. Enter the **bucket name**. 3. Enter the **File name** to download. 4. Choose whether to check the **Create document** checkbox: - If selected, a document reference will be created. - If not selected, the content will be extracted and returned in the response. :::note If the specified file is not found, the operation will fail and return an error message. ::: ## Limitations For detailed information on quotas and limits, including file size constraints, refer to the official [Google Cloud Storage quotas and limits](https://cloud.google.com/storage/quotas). --- ## Google Gemini connector :::info The **Google Gemini connector** is available for `8.7.0` or later. ::: The **Google Gemini connector** is an outbound connector that allows you to access Gemini multimodal models from Google. It is capable of understanding virtually any input, and can combine different types of information in a BPMN process. ## Create a Google Gemini connector task ## Make your Google Gemini connector executable To execute this connector, ensure all mandatory fields are correctly filled. :::note All the mandatory and non-mandatory fields and required settings depending on the operation selection you choose are covered in the upcoming sections. ::: ## Authentication Choose an authentication type from the **Type** dropdown. For details on authentication types, see [Google authentication types](#google-authentication-types). ## Project ID Enter your google cloud project identifier. ## Region Enter the region where your project is located. For example, `us-central1 (lowa)`, `us-west1 (Oregon)`. ## Model Select a model from the dropdown. The following models are currently supported: - gemini-1.5-flash-001 - gemini-1.5-flash-002 - gemini-1.5-pro-001 - gemini-1.5-pro-002 - gemini-1.0-pro-001 - gemini-1.0-pro-002 - gemini-1.0-pro-vision-001 ## Prompt Enter a prompt as a FEEL expression, providing text and media. - To provide text to Gemini, your expression should contain key _"text"_ and text data. For example, _"text"_ : _"your text"_ - To provide media to Gemini, your expression should contain key _"mime"_ and mime type text, and key _"uri"_ and media URI. For example, _"mime"_: _"mime type"_, _"uri"_: _"your URI"_. For example: ```feel = [{"text": "who is this video about"}, {"mime": "video/*", "uri": "https://youtu.be/..."}] ``` ## System instructions Enter system instructions as a string, to determine how the model should respond. To learn more about system instructions, refer to [Google system instructions](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/prompts/system-instructions?hl=en). ## Grounding Grounding connects the model output to the verifiable sources of information. - This is useful in situations where accuracy and reliability are important. - To use grounding, select the _Grounding_ checkbox and input the path to the data store. To learn more about grounding, refer to [Google grounding overview](https://cloud.google.com/vertex-ai/generative-ai/docs/grounding/overview?hl=en). ## Safety Filter Settings You can adjust the likelihood of receiving a model response which might contain harmful content. - Content is blocked based on the probability that it is harmful. - To use safety filter settings, select the _Safety Filter Settings_ checkbox and select the desired level from dropdown. - By default, all filters are set to OFF. To learn more about safety filters, refer to [Google responsible AI safety filters and settings](https://cloud.google.com/vertex-ai/docs/generative-ai/learn/responsible-ai?hl=en#safety_filters_and_attributes). ## Add stop sequence A stop sequence is a series of characters (including spaces) that stops response generation if encountered by the model. The stop sequence should be inserted as a string list. For example: ```feel = ["text 1", "text 2"] ``` ## Temperature The **Temperature** controls the randomness in token selection. - A lower temperature is good when you expect a true or correct response. A temperature of `0` means the highest probability token is usually selected. - A higher temperature can lead to diverse or unexpected results. Some models have a higher temperature max to encourage more random responses. ## Output token limit The **Output token limit** Determines the maximum amount of text output from a single prompt. A token is approximately four characters. ## Seed Setting a **Seed** value is useful if you make repeated requests and want the same model response. Deterministic outcome isn’t guaranteed. Changing the model or other settings can cause variations in the response even when you use the same seed value. ## Top-K The **Top-K** specifies the number of candidate tokens when the model is selecting an output token. - Use a lower value for less random responses and a higher value for more random responses. - Only the _gemini-1.0-pro-vision-001_ model supports Top-K. ## Top-P The **Top-P** changes how the model selects tokens for output. - Tokens are selected from the most probable to the least probable, until the sum of their probabilities equals the top-p value. - For example, if tokens A, B, and C have a probability of .3, .2, and .1 and the top-p value is .5, then the model will select either A or B as the next token (using temperature). - For the least variable results, set top-P to 0. ## Functional call description **Function calling** is a feature of Gemini models that makes it easier to get structured data outputs from generative models. - The **Functional call description** must be provided in fell format. - It is important that all types must be registered with capslock. To learn more about function calling, refer to [Google function calling](https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling). For example: ```fell [ { "name": "get_exchange_rate", "description":"Get the exchange rate for currencies between countries", "parameters": { "type": "OBJECT", "properties": { "currency_date": { "type": "STRING", "description": "A date that must always be in YYYY-MM-DD format or the value 'latest' if a time period is not specified" }, "currency_from": { "type": "STRING", "description": "The currency to convert from in ISO 4217 format" }, "currency_to": { "type": "STRING", "description": "The currency to convert to in ISO 4217 format" } }, "required":[ "currency_date", "currency_from", "currency_to" ] } } ] ``` ### Google authentication types The **Google Gemini connector** currently supports two methods for authentication and authorization: - Based on a short-lived JWT bearer token. - Based on a refresh token. Google supports multiple ways to obtain both types of token. Refer to the [official Google OAuth documentation](https://developers.google.com/identity/protocols/oauth2) for current instructions, or see the examples below. #### Example 1: Obtain JWT bearer token with a service account :::danger The following code snippet is for demonstration purposes only and must not be used for real production systems due to security concerns. For production usage, follow the [official Google guidelines](https://developers.google.com/identity/protocols/oauth2/service-account). ::: Assuming you have created a service account and downloaded a JSON file with keys, run the following Python 3 snippet to print the JWT token in the terminal: ```python from google.oauth2 import service_account # Scopes required to execute 'create' endpoind with Google Drive API SCOPES = ['https://www.googleapis.com/auth/cloud-platform', 'https://www.googleapis.com/auth/generative-language.retriever'] # File with keys SERVICE_ACCOUNT_FILE = 'google-service-account-creds.json' credentials = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES) auth_req = google.auth.transport.requests.Request() credentials.refresh(auth_req) # Print token print(credentials.token) ``` #### Example 2: Obtain bearer and refresh token with OAuth client :::danger The following code snippet is for demonstration purposes only and must not be used for real production systems due to security concerns. For production usage, follow the [official Google guidelines](https://developers.google.com/identity/protocols/oauth2/web-server). ::: Assuming you have created an OAuth client, you can download key files from the Google [Console](https://console.cloud.google.com/apis/credentials). Run the following Python 3 snippet to print the refresh token in the terminal: ```python from google_auth_oauthlib.flow import InstalledAppFlow SCOPES = ['https://www.googleapis.com/auth/cloud-platform', 'https://www.googleapis.com/auth/generative-language.retriever'] OAUTH_KEYS = './oauth-keys.json' # path to your file with OAuth credentials def main(): flow = InstalledAppFlow.from_client_secrets_file(OAUTH_KEYS, SCOPES) creds = flow.run_local_server(port=54948) pprint.pprint(vars(creds)) if __name__ == "__main__": main() ``` --- ## Google Maps Platform connector The **Google Maps Platform connector** in an inbound connector that allows you to validate addresses, retrieve postal addresses, and calculate distances with [Google Maps Platform Service](https://mapsplatform.google.com/) in BPMN process. ## Create a Google Maps Platform connector task ## Make your Google Maps Platform connector executable To work with the Google Maps Platform connector, choose the required operation type in the **Operation** section and enable the required Google Service API (which depends on the operation). Set the API key in the **Authentication** section and complete the mandatory fields highlighted in red in the connector on the right side of the screen under the **Deploy** button. :::note All the mandatory and non-mandatory fields and required settings depending on the operation selection you choose are covered in the upcoming sections. ::: ## Authentication In the **Authentication** section, set the relevant API key. Refer to the [official documentation](https://cloud.google.com/docs/authentication/api-keys#create) for more information on creating an API key. :::note We advise you to keep your authentications and secrets data safe and avoid exposing it in the BPMN XML file by creating a secret: 1. Follow our [guide for creating secrets](/components/hub/organization/manage-clusters/manage-secrets.md). 2. Name your secret (i.e `GOOGLE_MAPS_PLATFORM_API_KEY`) so you can reference it later in the connector. ::: ## Operation types ### Validate address This operation allows you to validate an address and its components, standardize the address for mailing, and determine the best known geocode for it. To use this operation, enable the [Google Address Validation API](https://developers.google.com/maps/documentation/address-validation/overview). Refer to the [official documentation](https://developers.google.com/maps/documentation/address-validation/cloud-setup) for more information on enabling the Google API. ### Get place ID This operation allows you to get the Google Maps place ID by address. To use this operation, enable the [Google Places API](https://developers.google.com/maps/documentation/places/web-service). Refer to the [official documentation](https://developers.google.com/maps/documentation/places/web-service/get-api-key) for more information on enabling the Google API. ### Calculate distance This operation allows you to calculate a distance between two place IDs. To use this operation, enable the [Google Directions API](https://developers.google.com/maps/documentation/directions). Refer to the [official documentation](https://developers.google.com/maps/documentation/directions/get-api-key) for more information on enabling the Google API. ## Usage ### Address validation, formatting, getting postal address 1. Select **Validate Address** from the **Operation type** dropdown in the **Operation** section. 2. Populate the **Authentication** section as described in the [respective section](#authentication). 3. (Optional) In the **Input** section, set **Region Code** (i.e `US`). You can find supported region codes [here](https://developers.google.com/maps/documentation/address-validation/coverage). 4. (Optional) In the **Input** section, set **Locality**, an incorporated city or town political entity (i.e `Mountain View`). 5. In the **Input** section, set **Address**, an incorporated city or town political entity (i.e `1600 Amphitheatre Pkwy`). 6. In the **Output** section set **Result Variable** or **Result Expression**. Refer to the [response mapping documentation](/components/connectors/use-connectors/index.md#response-mapping) to learn more. 7. Find a full example of the **Google Maps Platform connector** response [here](https://developers.google.com/maps/documentation/address-validation/requests-validate-address#address_validation_response). To get postal address and formatted address, set to **Result Expression** in the FEEL expression: ``` { formattedAddress: response.body.result.address.formattedAddress, postalAddress: response.body.result.address.postalAddress } ``` ### Get place ID 1. Select **Get Place ID** from the **Operation type** dropdown in the **Operation** section. 2. Populate the **Authentication** section as described in the [respective section](#authentication). 3. In the **Input** section, set **Address**. This address can be `formatedAddress`, which you can get using [this example](#address-validation-formatting-getting-postal-address). 4. In the **Output** section in the **Result Expression** property, the following expression is preset: ``` { placeId: response.body.candidates[1].place_id } ``` In this way, the response of this method will contain a mapping from the variable 'placeId' and the ID of the place: ```json { "placeId": "place....." } ``` ### Calculate distance 1. Select **Calculate Distance** from the **Operation type** dropdown in the **Operation** section. 2. Populate the **Authentication** section as described in the [respective section](#authentication). 3. In the **Input** section, set **Destination**, the place ID value that you want to use as the destination for calculating distance. 4. In the **Input** section, set **Origin**, the place ID value that you want to use as the starting point for calculating distance. 5. Select the unit system to use when displaying results from the **Units** dropdown in the **Input** section. 6. Select the transportation mode to use when calculating distances and directions from the **Mode** dropdown in the **Input** section. 7. In the **Output** section, set **Result Variable** or **Result Expression**. Refer to the [response mapping documentation](/components/connectors/use-connectors/index.md#response-mapping) to learn more. 8. Find a full example of the **Google Maps Platform connector** response [here](https://developers.google.com/maps/documentation/directions/start#getting-directions). To get a distance, set **Result Expression** in the FEEL expression: ``` { distance: response.body.routes[1].legs[1].distance.text } ``` ## Using Google Maps Platform connector best practice There is no guarantee a queue item will be processed right away. In that case, we suggest building your BPMN diagram to periodically retry polling. To learn more, refer to the entry titled _Solution with Timer and Loop_ on the [Camunda BPMN examples](https://camunda.com/bpmn/examples/) page. :::note To avoid performance issues, it is recommended to limit the number of loop retries. ::: --- ## Google Sheets connector The **Google Sheets connector** is an outbound connector that allows you to work with an existing or new spreadsheet on [Google Drive](https://drive.google.com/) from your BPMN process. ## Prerequisites To start working with the **Google Sheets connector**, a relevant OAuth token must be configured and stored as a secret in your cluster. The token must have permission to read/write and create a file from a desired Google Drive instance. Follow the steps from the [appendix](#appendix--faq) to find out more about creating an OAuth token and assigning relevant permissions. ## Create a Google Sheets connector task Currently, the Google Sheets connector supports next operations: - [Add values to spreadsheet](#add-values-to-spreadsheet) - [Create empty column or row](#create-empty-column-or-row) - [Create row](#create-row) - [Create spreadsheet](#create-spreadsheet) - [Create worksheet](#create-worksheet) - [Delete column](#delete-column) - [Delete worksheet](#delete-worksheet) - [Get row by index](#get-row-by-index) - [Get spreadsheet details](#get-spreadsheet-details) - [Get worksheet data](#get-worksheet-data) ## Make your Google Sheets connector executable To make the **Google Sheets connector** executable, fill out the mandatory fields highlighted in red in the properties panel. ### Add values to spreadsheet To add values to a spreadsheet, take the following steps: 1. Set the required credentials in the **Authentication** section. Refer to the [relevant appendix entry](#how-can-i-authenticate-my-connector) to find out more. 2. In the **Select operation** section, select **Add values to spreadsheet**. 3. In the **Operation details** section, set the field **Spreadsheet ID** to the desired spreadsheet, in which a new value will be added. 4. _(optional)_ In the **Operation details** section, set the field **Worksheet name** to the desired worksheet, in which a new value will be added. Keep in mind that if not specified, a new value will be added to the first available worksheet in the desired spreadsheet. 5. In the **Operation details** section, set the field **Cell ID** to the desired cell, in which a new value a new value will be added. Use format ColumnRow (for example A1). 6. In the **Operation details** section, set the field **Value** to the desired value, which will be added in the desired cell. #### Operation response The following fields are available in the response variable: - `action` - The action performed. In this case, it will always be **Add values to Spreadsheet**. - `status` - The status of the operation. If successful, it will always be "OK". Otherwise, there will be an error message. - `response` - The response of the operation. In this case, it will always be **null**. ### Create empty column or row To create empty column or row, take the following steps: 1. Set the required credentials in the **Authentication** section. Refer to the [relevant appendix entry](#how-can-i-authenticate-my-connector) to find out more. 2. In the **Select operation** section, select **Create empty column or row**. 3. In the **Operation details** section, set the field **Spreadsheet ID** to the desired spreadsheet, in which new columns/rows will be added. 4. In the **Operation details** section, set the field **Worksheet ID** to the desired worksheet, in which new columns/rows will be added. 5. In the **Operation details** section, select the **Dimension**, which will be added. 6. _(optional)_ In the **Operation details** section, set the both of the **Start index** and **End index** fields, in which new columns/rows will be added. Keep in mind that **count starts from 0**. It's possible to leave these fields empty. In this case, a new column/row will be added in the end of the desired worksheet. #### Operation response The following fields are available in the response variable: - `action` - The action performed. In this case, it will always be **Create empty column or row**. - `status` - The status of the operation. If successful, it will always be "OK". Otherwise, it will be an error message. - `response` - The response of the operation. In this case, it will always be **null**. ### Create row To create a row, take the following steps: 1. Set the required credentials in the **Authentication** section. Refer to the [relevant appendix entry](#how-can-i-authenticate-my-connector) to find out more. 2. In the **Select operation** section, select **Create row**. 3. In the **Operation details** section, set the field **Spreadsheet ID** to the desired spreadsheet, in which a new row will be added. 4. _(optional)_ In the **Operation details** section, set the field **Worksheet name** to the desired worksheet, in which a new row will be added. Keep in mind that if not specified, a new row will be added to the first available worksheet in the desired spreadsheet. 5. _(optional)_ In the **Operation details** section, set the field **Row index** to the desired row index, where a new row will be added. Refer to the [relevant appendix entry](#what-is-a-row-index) to find out more. If not set, a new row will be appended to the last row. 6. In the **Operation details** section, set the field **Enter values** to the desired values, which will be added. This property requires [FEEL input](../../../components/modeler/feel/language-guide/feel-expressions-introduction.md). #### Operation response The following fields are available in the response variable: - `action` - The action performed. In this case, it will always be **Create row**. - `status` - The status of the operation. If successful, it will always be "OK". Otherwise, it will be an error message. - `response` - The response of the operation. In this case, it will always be **null**. ### Create spreadsheet To create a spreadsheet, take the following steps: 1. Set the required credentials in the **Authentication** section. Refer to the [relevant appendix entry](#how-can-i-authenticate-my-connector) to find out more. 2. In the **Select operation** section, select **Create spreadsheet**. 3. _(optional)_ In the **Operation details** section, set the field **Parent folder ID** to the desired parent, inside which a new spreadsheet will be created. Keep in mind that if not specified, a new spreadsheet will be created in the Google Drive root folder of a user who owns the OAuth token. 4. In the **Operation details** section, set the field **Spreadsheet name** to the desired spreadsheet name. #### Operation response The following fields are available in the response variable: - `spreadsheetId` - ID of the newly created spreadsheet. - `spreadsheetUrl` - Human-readable URL of the newly created spreadsheet. ### Create worksheet To create a worksheet, take the following steps: 1. Set the required credentials in the **Authentication** section. Refer to the [relevant appendix entry](#how-can-i-authenticate-my-connector) to find out more. 2. In the **Select operation** section, select **Create worksheet**. 3. In the **Operation details** section, set the field **Spreadsheet ID** to the desired spreadsheet, in which a new worksheet will be created. 4. In the **Operation details** section, set the field **Worksheet name** to the desired worksheet name. 5. _(optional)_ In the **Operation details** section, set the field **Worksheet index** to the desired index, in which a new worksheet will be created. Refer to the [relevant appendix entry](#what-is-a-worksheet-index) to find out more. #### Operation response The following fields are available in the response variable: - `action` - The action performed. In this case, it will always be **Create worksheet**. - `status` - The status of the operation. If successful, it will always be "OK". Otherwise, it will be an error message. - `response` - The response of the operation. In this case, it will always be **null**. ### Delete column To delete a column, take the following steps: 1. Set the required credentials in the **Authentication** section. Refer to the [relevant appendix entry](#how-can-i-authenticate-my-connector) to find out more. 2. In the **Select operation** section, select **Delete column**. 3. In the **Operation details** section, set the field **Spreadsheet ID** to the desired spreadsheet, in which a column will be deleted. 4. In the **Operation details** section, set the field **Worksheet ID** to the desired worksheet ID, in which a column will be deleted. 5. In the **Operation details** section, select **Index format** of desired index of column to be deleted. Refer to the [relevant appendix entry](#how-can-i-define-which-column-will-be-deleted) to find out more. 6. In the **Operation details** section, set the **Column letter index** to the desired column index. #### Operation response The following fields are available in the response variable: - `action` - The action performed. In this case, it will always be **Delete column**. - `status` - The status of the operation. If successful, it will always be "OK". Otherwise, it will be an error message. - `response` - The response of the operation. In this case, it will always be **null**. ### Delete worksheet To delete a worksheet, take the following steps: 1. Set the required credentials in the **Authentication** section. Refer to the [relevant appendix entry](#how-can-i-authenticate-my-connector) to find out more. 2. In the **Select operation** section, select **Delete worksheet**. 3. In the **Operation details** section, set the field **Spreadsheet ID** to the desired spreadsheet, in which a worksheet will be deleted. 4. In the **Operation details** section, set the field **Worksheet ID** to the desired worksheet ID, which will be deleted. #### Operation response The following fields are available in the response variable: - `action` - The action performed. In this case, it will always be **Delete worksheet**. - `status` - The status of the operation. If successful, it will always be "OK". Otherwise, it will be an error message. - `response` - The response of the operation. In this case, it will always be **null**. ### Get row by index To get row by index, take the following steps: 1. Set the required credentials in the **Authentication** section. Refer to the [relevant appendix entry](#how-can-i-authenticate-my-connector) to find out more. 2. In the **Select operation** section, select **Get row by index**. 3. In the **Operation details** section, set the field **Spreadsheet ID** to the desired spreadsheet, from which a row will be retrieved. 4. _(optional)_ In the **Operation details** section, set the field **Worksheet name** to the desired worksheet, from which a row will be retrieved. Keep in mind that if not specified, a row will be retrieved from the first available worksheet in the desired spreadsheet. 5. In the **Operation details** section, set the field **Row index** to the desired row index. Refer to the [relevant appendix entry](#what-is-a-row-index) to find out more. #### Operation response The following fields are available in the response variable: - `action` - The action performed. In this case, it will always be **Get row by index**. - `status` - The status of the operation. If successful, it will always be "OK". Otherwise, it will be an error message. - `response` - The response of the operation. If row is empty, the response is **null**, else **array**. ### Get spreadsheet details To get spreadsheet details, take the following steps: 1. Set the required credentials in the **Authentication** section. Refer to the [relevant appendix entry](#how-can-i-authenticate-my-connector) to find out more. 2. In the **Select operation** section, select **Get spreadsheet details**. 3. In the **Operation details** section, set the field **Spreadsheet ID** to the desired spreadsheet, which details will be returned. #### Operation response The response contains spreadsheet properties. For details, read the [official Google documentation](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets#spreadsheetproperties). ### Get worksheet data To get worksheet data, take the following steps: 1. Set the required credentials in the **Authentication** section. Refer to the [relevant appendix entry](#how-can-i-authenticate-my-connector) to find out more. 2. In the **Select operation** section, select **Get worksheet data**. 3. In the **Operation details** section, set the field **Spreadsheet ID** to the desired spreadsheet, from which data will be retrieved. 4. In the **Operation details** section, set the field **Worksheet name** to the desired worksheet, from which data will be retrieved. #### Operation response The following fields are available in the response variable: - `action` - The action performed. In this case, it will always be **Get worksheet data** - `status` - The status of the operation. If successful, it will always be "OK". Otherwise, it will be an error message. - `response` - The response of the operation. If the worksheet is empty, the response is **null**, else **array of rows (also array)**. ## Appendix & FAQ ### How can I authenticate my connector? The **Google Sheets connector** currently supports two methods for authentication and authorization: based on short-lived JWT bearer token, and based on refresh token. Google supports multiple ways to obtain both. Refer to the [official Google OAuth documentation](https://developers.google.com/identity/protocols/oauth2) to get up-to-date instructions or refer to the examples below. You also enable _Google Sheets API_ and _Google Drive API_ for every client intended to use. You can do this from the [Google Cloud API Library](https://console.cloud.google.com/apis/library). #### Example 1: Obtaining JWT bearer token with a service account :::danger The following code snippet is for demonstration purposes only and must not be used for real production systems due to security concerns. For production usage, follow the [official Google guidelines](https://developers.google.com/identity/protocols/oauth2/service-account). ::: Assuming you have created a service account and downloaded a JSON file with keys, run the following Python 3 snippet that prints the JWT token in the terminal: ```python from google.oauth2 import service_account # Scopes required to execute 'create' endpoind with Google Sheets API SCOPES = ['https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/drive.file', 'https://www.googleapis.com/auth/drive.appdata'] # File with keys SERVICE_ACCOUNT_FILE = 'google-service-account-creds.json' credentials = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES) auth_req = google.auth.transport.requests.Request() credentials.refresh(auth_req) # Print token print(credentials.token) ``` #### Example 2: Obtaining bearer and refresh tokens with OAuth client :::danger The following code snippet is for demonstration purposes only and must not be used for real production systems due to security concerns. For production usage, follow the [official Google guidelines](https://developers.google.com/identity/protocols/oauth2/web-server). ::: Assuming you have created an OAuth client, you can download key files from the Google [Console](https://console.cloud.google.com/apis/credentials). Run the following Python 3 snippet that prints the refresh token in the terminal: ```python from google_auth_oauthlib.flow import InstalledAppFlow SCOPES = ['https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/spreadsheets'] OAUTH_KEYS = './oauth-keys.json' # path to your file with OAuth credentials def main(): flow = InstalledAppFlow.from_client_secrets_file(OAUTH_KEYS, SCOPES) creds = flow.run_local_server(port=54948) pprint.pprint(vars(creds)) if __name__ == "__main__": main() ``` ### Where do I get a spreadsheet ID? The spreadsheet ID is located within the URL of the Google Sheets document. Here's how to find it: For example, if the URL looks like this: ``` https://docs.google.com/spreadsheets/d/1xhNL0a6WjZtYRHF2522FrCYUYxHve9ju-DHNkaTm9Sk/edit#gid=0 ``` The spreadsheet ID is the alphanumeric string between `d/` and `/edit`, which in this case is `1xhNL0a6WjZtYRHF2522FrCYUYxHve9ju-DHNkaTm9Sk`. ### Where do I get a worksheet ID? The Worksheet ID (also known as the gid) can be found in the same URL. Here's how to find it: For example, if the URL looks like this: ``` https://docs.google.com/spreadsheets/d/1xhNL0a6WjZtYRHF2522FrCYUYxHve9ju-DHNkaTm9Sk/edit#gid=0 ``` The worksheet ID is the number after `#gid=`, which in this case is `0`. ### What is a worksheet index? You can define the place where a new worksheet will be created. By default, the new worksheet will be created at the end of the worksheet. Keep in mind, **count starts from 0**. For instance, to create a new worksheet on the second place, worksheet index should be set as **1**. ### What is a row index? Row index is the unique identifier for each row in some worksheet, which is used both for reading and writing operations with row. This index is defined to the left of the row. ### How can I define which column will be deleted? There are two ways to define which column will be deleted: by letter index and numeric one. Numeric index can be found at the top of the column. The other option is to use numeric index. Keep in mind **count starts from 0**. To delete column A, numeric index should be 0, B -> 1... --- ## Google Drive connector The **Google Drive connector** is an outbound connector that allows you to create empty folders or files on [Google Drive](https://drive.google.com/) from templates from your BPMN process. ## Prerequisites To start working with the **Google Drive connector**, a relevant OAuth token must be configured and stored as a secret in your cluster. The token must have permission to read and create a folder and/or files from a desired Google Drive instance. Follow the steps from the [appendix](#appendix--faq) to find out more about creating an OAuth token and giving relevant permissions. ## Create a Google Drive connector task Currently, the Google Drive connector supports two types of operations: create a folder and create a file from a template. ## Make your Google Drive connector executable To make the **Google Drive connector** executable, fill out the mandatory fields highlighted in red in the properties panel on the right side of the screen. ### Create a new folder To create a new folder, take the following steps: 1. Set the required credentials in the **Authentication** section. Refer to the [relevant appendix entry](#how-can-i-authenticate-my-connector) to find out more. 2. In the **Select Operation** section, set the field value **Operation Type** as **Create Folder**. 3. In the **Operation Details** section, set the field **Folder name** as the desired name of a folder you wish to create. For example, `MyNewFolder`. Alternatively, you could use a FEEL expression. 4. _(optional)_ In the **Operation Details** section, set the field **Parent folder ID** to the desired parent, inside which a new folder will be created. Keep in mind that if not specified, a new folder will be created in the Google Drive root folder of a user who owns the OAuth token. 5. _(optional)_ In the **Operation Details** section, you can set the **Additional properties or metadata** field to Google Drive compatible properties. For example, _description_ of the folder. This property requires FEEL input. Check [the appendix](#what-are-the-limitations-of-the-additional-properties-or-metadata) for known values and limitations. ### Create a new file from a template To create a new file from a template, take the following steps: 1. Set the required credentials in the **Authentication** section. Refer to the [relevant appendix entry](#how-can-i-authenticate-my-connector) to find out more. 2. In the **Select Operation** section, set the field value **Operation Type** as **Create File from template**. 3. In the **Operation Details** section, set the field **File name** as the desired name of a folder you wish to create. You can use FEEL expressions here. 4. In the **Operation Details** section, set the field **Template ID** of the desired template. 5. _(optional)_ In the **Operation Details** section, set the field **Parent folder ID** to the desired parent, inside which a new file will be created. Keep in mind that if not specified, a new folder will be created in the Google Drive root folder of a user who owns the OAuth token. 6. In the **Operation Details** section, set the field **Template variables** as desired variables that will be applied to the template. The template variables are compatible with the Google Docs [Requests API](https://developers.google.com/docs/api/reference/rest/v1/documents/request). This property requires FEEL input. 7. _(optional)_ In the **Operation Details** section, you can set the **Additional properties or metadata** field to Google Drive compatible properties. This property requires FEEL input. Check [the appendix](#what-are-the-limitations-of-the-additional-properties-or-metadata) for known values and limitations. :::note Starting from version 8.7.0, the Google Drive connector supports uploading documents from (or downloading documents to) the Camunda document store. Review the **Document** field in the properties panel where the document reference can be provided. See additional details and limitations in [document handling](/components/document-handling/getting-started.md). ::: ### Upload file To upload a file, take the following steps: 1. Set the required credentials in the **Authentication** section. Refer to the [relevant appendix entry](#how-can-i-authenticate-my-connector) to find out more. 2. In the **Select Operation** section, set the field value **Operation Type** to **Upload File**. 3. _(optional)_ In the **Operation Details** section, set the field **Parent folder ID** to the desired parent, inside which a new file will be created. If not specified, a new folder is created in the Google Drive root folder of the user who owns the OAuth token. 4. In the **Document** section, input the variable name to which the document is assigned. :::note To work with documents you must upload them first, [using the Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/specifications/create-document.api.mdx) for example. The result of the endpoint must then be assigned to a variable in **Start Process Instance** so you can use the variable in the **Document** field. ::: ### Download file To download a file, take the following steps: 1. Set the required credentials in the **Authentication** section. Refer to the [relevant appendix entry](#how-can-i-authenticate-my-connector) to find out more. 2. In the **Select Operation** section, set the field value **Operation Type** to **Download File**. 3. In the **Operation Details** section, set the field _File ID_ to the google drive file that will be downloaded. For more information, refer to the [file id appendix](#Where-do-I-get-File-ID). ## Google Drive connector response The following response types can be returned by the _Google Drive connector response_, depending on the **Operation Type** selected. ### Response type for list of operations - **Create Folder** - **Create File from template** - **Upload File** The **Google Drive connector response** exposes the Google Drive API response as a local variable named "response". The following fields are available in the response variable: - `googleDriveResourceId`: The ID of the newly created resource. - `googleDriveResourceUrl`: Human-readable URL of the newly created resource. You can use an output mapping to map the response: 1. Use **Result Variable** to store the response in a process variable. For example, `myResultVariable`. 2. Use **Result Expression** to map fields from the response into process variables. For example: ``` = { "myNewReportFolderId": response.googleDriveResourceId, "myNewReportFolderUrl": response.googleDriveResourceUrl } ``` ### Response type for _Download file_ operation only The **Google Drive connector** response will be identical to the [REST API](/apis-tools/orchestration-cluster-api-rest/specifications/create-document.api.mdx). For example: ``` { "camunda.document.type": "camunda", "storeId": "in-memory", "documentId": "c3c8e499-321d-421c-afa2-4632d2f5ce48", "metadata": { "contentType": "image/png", "fileName": "file name", "size": 66497, "customProperties": {} } } ``` ## Appendix & FAQ ### What Google API does the Google Drive connector use to create a folder? The **Google Drive connector** uses the Google Drive [`Files:Create`](https://developers.google.com/drive/api/v3/reference/files/create) API endpoint. ### What Google API does the Google Drive connector use to create a file from template? The **Google Drive connector** uses the Google Drive [`Files:Copy`](https://developers.google.com/drive/api/v3/reference/files/copy) API endpoint to copy an original template. Afterwards, the **Google Drive connector** utilizes Google Docs [Merge](https://developers.google.com/docs/api/how-tos/merge) approach via [`Documents:BatchUpdate`](https://developers.google.com/docs/api/reference/rest/v1/documents/batchUpdate) Google Docs API method. ### How can I authenticate my connector? The **Google Drive connector** currently supports two methods for authentication and authorization: based on short-lived JWT bearer token, and based on refresh token. Google supports multiple ways to obtain both. Refer to the [official Google OAuth documentation](https://developers.google.com/identity/protocols/oauth2) to get up-to-date instructions or refer to the examples below. You also enable _Google Docs API_ and _Google Drive API_ for every client intended to use. You can do this at the [Google Cloud API Library](https://console.cloud.google.com/apis/library) page. #### Example 1: Obtaining JWT bearer token with a service account :::danger The following code snippet is for demonstration purposes only and must not be used for real production systems due to security concerns. For production usage, follow the [official Google guidelines](https://developers.google.com/identity/protocols/oauth2/service-account). ::: Assuming you have created a service account and downloaded a JSON file with keys, run the following Python 3 snippet that prints the JWT token in the terminal: ```python from google.oauth2 import service_account # Scopes required to execute 'create' endpoind with Google Drive API SCOPES = ['https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/drive.file', 'https://www.googleapis.com/auth/drive.appdata'] # File with keys SERVICE_ACCOUNT_FILE = 'google-service-account-creds.json' credentials = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES) auth_req = google.auth.transport.requests.Request() credentials.refresh(auth_req) # Print token print(credentials.token) ``` When using shared drives, you must use an Oauth client instead of a service account. The service account does not have access to shared drives. #### Example 2: Obtaining bearer and refresh tokens with OAuth client :::danger The following code snippet is for demonstration purposes only and must not be used for real production systems due to security concerns. For production usage, follow the [official Google guidelines](https://developers.google.com/identity/protocols/oauth2/web-server). ::: Assuming you have created an OAuth client, you can download key files from the Google [Console](https://console.cloud.google.com/apis/credentials). Run the following Python 3 snippet that prints the refresh token in the terminal: ```python from google_auth_oauthlib.flow import InstalledAppFlow SCOPES = ['https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/documents'] OAUTH_KEYS = './oauth-keys.json' # path to your file with OAuth credentials def main(): flow = InstalledAppFlow.from_client_secrets_file(OAUTH_KEYS, SCOPES) creds = flow.run_local_server(port=54948) pprint.pprint(vars(creds)) if __name__ == "__main__": main() ``` ### Where do I get a parent folder ID? To find the Parent Folder ID for a Google Drive folder, follow these steps: 1. Open the Google Drive folder in your web browser. 2. Look at the URL in the address bar, which will look something like this: ``` https://docs.google.com/drive/folder/1whNL0a6WjZtYRHF2522FrCYUYxHve9ju-DHNkaTm9Sk ``` 3. The Parent Folder ID is the alphanumeric string after `/folders/`, which in this case is `1whNL0a6WjZtYRHF2522FrCYUYxHve9ju-DHNkaTm9Sk`. ### How do I set additional properties or metadata? You can set any property from the Google Drive [Create API](https://developers.google.com/drive/api/v3/reference/files/create). For example: ``` = { "description":"myDescription" } ``` The unknown or mistyped properties will be ignored. ### What are the limitations of the additional properties or metadata? Some properties are applicable only for the token owners, like `folderColorRgb` and `starred`. ### Where do I get Template ID? To find the Template ID for a Google Docs template, follow these steps: 1. Open the link to the Google Docs template in your web browser. The URL will look something like this: ``` https://docs.google.com/document/d/1whNL0a6WjZtYRHF2522FrCYUYxHve9ju-DHNkaTm9Sk ``` 2. The Template ID is the alphanumeric string after `/d/`, which in this case is `1whNL0a6WjZtYRHF2522FrCYUYxHve9ju-DHNkaTm9Sk`. ### Can you show me an example of a valid template? Certainly! Here is an example of a valid template: ```text {{CompanyName}} confidential. {{DocumentDate}} {{RecipientFullName}} {{RecipientAddress}} Dear {{RecipientShortName}}! We are pleased to inform you that your application {{ApplicationNumber}} has been approved. Sincerely, {{SigneeName}}, Executive Director ``` Now, in the **Template variables** field we can apply the following FEEL JSON object which must be compatible with the Google Docs Requests API: ``` = { "requests":[ { "replaceAllText":{ "containsText":{ "text":"{{DocumentDate}}", "matchCase":"true" }, "replaceText":today() } }, { "replaceAllText":{ "containsText":{ "text":"{{RecipientFullName}}", "matchCase":"true" }, "replaceText":"John W. Doe" } }, { "replaceAllText":{ "containsText":{ "text":"{{RecipientAddress}}", "matchCase":"true" }, "replaceText":"Zweibrückenstraße 1845, 80000 Munich" } }, { "replaceAllText":{ "containsText":{ "text":"{{RecipientShortName}}", "matchCase":"true" }, "replaceText":"Mr. Doe" } }, { "replaceAllText":{ "containsText":{ "text":"{{ApplicationNumber}}", "matchCase":"true" }, "replaceText":"0123456789" } }, { "replaceAllText":{ "containsText":{ "text":"{{SigneeName}}", "matchCase":"true" }, "replaceText":"Jane T. Doe" } }, { "replaceAllText":{ "containsText":{ "text":"{{CompanyName}}", "matchCase":"true" }, "replaceText":"Good Company Inc." } } ] } ``` The result should be as follows: ```text Good Company inc. confidential. 2022-08-10 John W. Doe Zweibrückenstraße 1845, 80000 Munich Dear Mr. Doe! We are pleased to inform you that your application 0123456789 has been approved. Sincerely, Jane T. Doe, Executive Director ``` ### Where do I get File ID? To find the File ID for a Google file, follow these steps: 1. Select the desired file on your Google Drive, click on the three horizontal dots to the right of the file name. 2. Click on the share section. 3. Click on copy link. The URL will look something like the following: ``` https://drive.google.com/file/d/1y1td3iIKWOh88gK4hVevGM1WnX7tibCW/view ``` 4. The File ID is the alphanumeric string after `/d/`. In this example, this would be `1y1td3iIKWOh88gK4hVevGM1WnX7tibCW`. ### What kind of templates are currently supported? The **Google Drive connector** currently supports only Google Doc files (MIME type `application/vnd.google-apps.document`). --- ## HubSpot connector The **Hubspot connector** is an outbound connector that allows you to connect your BPMN service with [HubSpot](https://hubspot.com/) to manage HubsSpot contacts, companies, and deals. ## Prerequisites To use the **HubSpot connector**, you must have a HubSpot account and a [Bearer token](https://knowledge.hubspot.com/integrations/how-do-i-get-my-hubspot-api-key) to authenticate requests. When creating a private app, you must grant the permissions required to access the HubSpot API. Different operations require different permissions. To use all HubSpot connector operations, add the following permissions to your app: - `crm.objects.contacts.read` - `crm.objects.contacts.write` - `crm.objects.companies.read` - `crm.objects.companies.write` - `crm.objects.deals.read` - `crm.objects.deals.write` - `crm.lists.read` - `crm.lists.write` - `automation` :::note Use Camunda secrets to avoid exposing your token credentials as plain text. Refer to our documentation on [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ::: ## Create a HubSpot connector task ## Select operation to execute The **HubSpot connector** currently supports the following operations. ### Contacts #### Get all contacts - **Next page after object id:** HubSpot limits the number of results to 100 and provides pagination. To retrieve the first page keep this field empty. To retrieve the following page, set it to `response.body.paging.next.after` from the previous page. #### Get contact by id - **Contact ID:** The ID of the contact. - **Properties:** The properties of the contact to receive. Learn more about [properties](https://developers.hubspot.com/docs/reference/api/crm/properties/v1-contacts#get-all-contact-properties) and [default properties of contacts](https://knowledge.hubspot.com/properties/hubspots-default-contact-properties). If you do not set this field, standard properties (firstname,lastname, and email) are returned.
To get all default properties, set properties to the following list: ``` address, annualrevenue, associatedcompanyid, associatedcompanylastupdated, city, closedate, company, company_size, country, createdate, currentlyinworkflow, date_of_birth, days_to_close, degree, email, engagements_last_meeting_booked, engagements_last_meeting_booked_campaign, engagements_last_meeting_booked_medium, engagements_last_meeting_booked_source, favorite_content_topics, fax, field_of_study, first_conversion_date, first_conversion_event_name, first_deal_created_date, firstname, gender, graduation_date, hs_additional_emails, hs_all_accessible_team_ids, hs_all_contact_vids, hs_all_owner_ids, hs_all_team_ids, hs_analytics_average_page_views, hs_analytics_first_referrer, hs_analytics_first_timestamp, hs_analytics_first_touch_converting_campaign, hs_analytics_first_url, hs_analytics_first_visit_timestamp, hs_analytics_last_referrer, hs_analytics_last_timestamp, hs_analytics_last_touch_converting_campaign, hs_analytics_last_url, hs_analytics_last_visit_timestamp, hs_analytics_num_event_completions, hs_analytics_num_page_views, hs_analytics_num_visits, hs_analytics_revenue, hs_analytics_source, hs_analytics_source_data_1, hs_analytics_source_data_2, hs_associated_target_accounts, hs_avatar_filemanager_key, hs_buying_role, hs_calculated_form_submissions, hs_calculated_merged_vids, hs_calculated_mobile_number, hs_calculated_phone_number, hs_calculated_phone_number_area_code, hs_calculated_phone_number_country_code, hs_calculated_phone_number_region_code, hs_clicked_linkedin_ad, hs_contact_enrichment_opt_out, hs_contact_enrichment_opt_out_timestamp, hs_content_membership_email, hs_content_membership_email_confirmed, hs_content_membership_follow_up_enqueued_at, hs_content_membership_notes, hs_content_membership_registered_at, hs_content_membership_registration_domain_sent_to, hs_content_membership_registration_email_sent_at, hs_content_membership_status, hs_conversations_visitor_email, hs_count_is_unworked, hs_count_is_worked, hs_country_region_code, hs_created_by_conversations, hs_created_by_user_id, hs_createdate, hs_document_last_revisited, hs_email_bad_address, hs_email_bounce, hs_email_click, hs_email_customer_quarantined_reason, hs_email_delivered, hs_email_domain, hs_email_first_click_date, hs_email_first_open_date, hs_email_first_reply_date, hs_email_first_send_date, hs_email_hard_bounce_reason, hs_email_hard_bounce_reason_enum, hs_email_is_ineligible, hs_email_last_click_date, hs_email_last_email_name, hs_email_last_open_date, hs_email_last_reply_date, hs_email_last_send_date, hs_email_open, hs_email_optout, hs_email_optout_697354363, hs_email_optout_697354364, hs_email_quarantined, hs_email_quarantined_reason, hs_email_replied, hs_email_sends_since_last_engagement, hs_emailconfirmationstatus, hs_enriched_email_bounce_detected, hs_facebook_ad_clicked, hs_facebook_click_id, hs_first_closed_order_id, hs_first_engagement_object_id, hs_first_order_closed_date, hs_first_outreach_date, hs_first_subscription_create_date, hs_full_name_or_email, hs_google_click_id, hs_has_active_subscription, hs_ip_timezone, hs_is_contact, hs_is_enriched, hs_is_unworked, hs_language, hs_last_metered_enrichment_timestamp, hs_last_sales_activity_date, hs_last_sales_activity_timestamp, hs_last_sales_activity_type, hs_lastmodifieddate, hs_latest_disqualified_lead_date, hs_latest_meeting_activity, hs_latest_open_lead_date, hs_latest_qualified_lead_date, hs_latest_sequence_ended_date, hs_latest_sequence_enrolled, hs_latest_sequence_enrolled_date, hs_latest_sequence_finished_date, hs_latest_sequence_unenrolled_date, hs_latest_source, hs_latest_source_data_1, hs_latest_source_data_2, hs_latest_source_timestamp, hs_latest_subscription_create_date, hs_lead_status, hs_legal_basis, hs_linkedin_ad_clicked, hs_linkedin_url, hs_live_enrichment_deadline, hs_membership_has_accessed_private_content, hs_membership_last_private_content_access_date, hs_merged_object_ids, hs_mobile_sdk_push_tokens, hs_notes_last_activity, hs_notes_next_activity, hs_notes_next_activity_type, hs_object_id, hs_object_source, hs_object_source_detail_1, hs_object_source_detail_2, hs_object_source_detail_3, hs_object_source_id, hs_object_source_label, hs_object_source_user_id, hs_persona, hs_pinned_engagement_id, hs_pipeline, hs_prospecting_agent_actively_enrolled_count, hs_quarantined_emails, hs_read_only, hs_recent_closed_order_date, hs_registered_member, hs_registration_method, hs_role, hs_sa_first_engagement_date, hs_sa_first_engagement_descr, hs_sa_first_engagement_object_type, hs_sales_email_last_clicked, hs_sales_email_last_opened, hs_sales_email_last_replied, hs_searchable_calculated_international_mobile_number, hs_searchable_calculated_international_phone_number, hs_searchable_calculated_mobile_number, hs_searchable_calculated_phone_number, hs_seniority, hs_sequences_actively_enrolled_count, hs_sequences_enrolled_count, hs_sequences_is_enrolled, hs_state_code, hs_sub_role, hs_testpurge, hs_testrollback, hs_time_to_first_engagement, hs_timezone, hs_unique_creation_key, hs_updated_by_user_id, hs_user_ids_of_all_notification_followers, hs_user_ids_of_all_notification_unfollowers, hs_user_ids_of_all_owners, hs_v2_date_entered_customer, hs_v2_date_entered_evangelist, hs_v2_date_entered_lead, hs_v2_date_entered_marketingqualifiedlead, hs_v2_date_entered_opportunity, hs_v2_date_entered_other, hs_v2_date_entered_salesqualifiedlead, hs_v2_date_entered_subscriber, hs_v2_date_exited_customer, hs_v2_date_exited_evangelist, hs_v2_date_exited_lead, hs_v2_date_exited_marketingqualifiedlead, hs_v2_date_exited_opportunity, hs_v2_date_exited_other, hs_v2_date_exited_salesqualifiedlead, hs_v2_date_exited_subscriber, hs_was_imported, hs_whatsapp_phone_number, hubspot_owner_assigneddate, hubspot_owner_id, hubspot_team_id, hubspotscore, industry, ip_city, ip_country, ip_country_code, ip_latlon, ip_state, ip_state_code, ip_zipcode, job_function, jobtitle, lastmodifieddate, lastname, lifecyclestage, marital_status, message, military_status, mobilephone, notes_last_contacted, notes_last_updated, notes_next_activity_date, num_associated_deals, num_contacted_notes, num_conversion_events, num_notes, num_unique_conversion_events, numemployees, phone, preferred_channels, recent_conversion_date, recent_conversion_event_name, recent_deal_amount, recent_deal_close_date, relationship_status, salutation, school, seniority, start_date, state, surveymonkeyeventlastupdated, total_revenue, twitterhandle, webinareventlastupdated, website, work_email, zip ```
#### Get multiple contacts by id - **Contact ids:** The IDs of the contacts to retrieve. This is limited to 100 contacts. #### Search contact - **Search field:** The field to search for. For example, "lastname". - **Search value:** The value to search for. For example, "Smith". :::note All contacts matching the search criteria are returned. ::: #### Create contact - **Properties:** The properties of the contact to create. Learn more about [properties](https://developers.hubspot.com/docs/guides/api/crm/properties) and [default properties of contacts](https://knowledge.hubspot.com/properties/hubspots-default-contact-properties). - **Company ID:** The ID of the company the contact is associated with. HubSpot automatically adds the contact to the company if the mail address domain matches the company domain. For example, "jane.doe@camunda.com" is automatically added to the company with the domain "camunda.com". If you set the company ID manually, the contact is added to the company with the given ID **AND** the company with the matching domain. #### Update contact - **Properties:** The properties of the contact to update. Learn more about [properties](https://developers.hubspot.com/docs/guides/api/crm/properties) and [default properties of contacts](https://knowledge.hubspot.com/properties/hubspots-default-contact-properties). Only add properties you want to adjust to the properties field. - **Contact ID:** The ID of the contact to update. :::note It is not possible to update associations to companies with this operation. To do so use the `add contact to` or `remove contact from company` operations under `Companies`. ::: #### Delete contact - **Contact ID:** The ID of the contact to delete. ### Companies #### Get all companies - **Next page after object id:** HubSpot limits the number of results to 100 and provides pagination. To retrieve the first page keep this field empty. To retrieve the following page, set it to `response.body.paging.next.after` from the previous page. #### Get company by id - **Company ID:** The ID of the company. #### Search company - **Search field:** The field to search for. For example, "name". - **Search value:** The value to search for. For example, "Camunda". :::note All companies matching the search criteria are returned. ::: #### Get all contacts of a company - **Company ID:** The ID of the company. #### Add contact to company - **Contact ID:** The ID of the contact. - **Company ID:** The ID of the company. #### Remove contact from company - **Contact ID:** The ID of the contact. - **Company ID:** The ID of the company. #### Create company - **Properties:** The properties of the company to create. Learn more about [properties](https://developers.hubspot.com/docs/guides/api/crm/properties) and [default properties of companies](https://knowledge.hubspot.com/properties/hubspot-crm-default-company-properties). #### Delete company - **Company ID:** The ID of the company to delete. ### Deals #### Get all deals - **Next page after object id:** HubSpot limits the number of results to 100 and provides pagination. To retrieve the first page keep this field empty. To retrieve the following page, set it to `response.body.paging.next.after` from the previous page. #### Get deal by id - **Deal ID:** The ID of the deal. #### Search deal - **Search field:** The field to search for. For example, "dealname". - **Search value:** The value to search for. For example, "Inital Deal for Camunda". :::note All deals matching the search criteria are returned. ::: #### Delete deal - **Deal ID:** The ID of the deal to delete. ### Miscellaneous #### Submit form - **Portal ID:** The HubSpot account that the form belongs to. [Learn more](https://knowledge.hubspot.com/account-management/manage-multiple-hubspot-accounts#check-your-current-account) - **Form ID:** The unique ID of the form you are sending data to. [Learn more](https://knowledge.hubspot.com/forms/find-your-form-guid) - **Form fields:** The value of the input fields of the form. [Learn more](https://developers.hubspot.com/docs/reference/api/marketing/forms/v3-legacy) #### Add element to list - **List ID:** The ID of the list. - **Object IDs:** The IDs of the objects to add to the list. :::note Adding elements to a list may take a few seconds to be reflected in the HubSpot UI. ::: #### Enroll contact to workflow - **Workflow ID:** The workflow ID. You can retrieve the ID by sending a request to the [get all workflows endpoint](https://developers.hubspot.com/docs/reference/api/automation/create-manage-workflows/v3#get-all-workflows). - **Contact email:** The email of the contact to be enrolled to the workflow. ## Handle connector response As the **HubSpot connector** is a protocol connector (built on top of the **HTTP REST connector**) the handling response is still applicable [as described](/components/connectors/protocol/rest.md#response). --- ## Hugging Face connector The **Hugging Face connector** is an outbound connector that allows you to interact with [Hugging Face](https://huggingface.co/) models from your BPMN processes. ## Prerequisites To begin using the **Hugging Face connector**, you need to have a valid [API key](https://huggingface.co/docs/api-inference/quicktour#get-your-api-token), and a model deployed with [Inference API](https://huggingface.co/docs/api-inference/index). ## Create a Hugging Face connector task ## Make your Hugging Face connector executable To work with the **Hugging Face connector**, fill all mandatory fields. ## Authentication Fill the **Hugging Face API key** field with a valid Hugging Face API key. ### Create a new connector secret Keep your **API key** safe and avoid exposing it in the BPMN `xml` file by creating a secret: 1. Follow our [guide for creating secrets](/components/hub/organization/manage-clusters/manage-secrets.md). 2. Name your secret (for example, `HUGGING_FACE_SECRET`) so you can reference it later in the connector. ## Payload In the **Model** field, enter a model name you wish to use in your BPMN process, for example, `gpt2` if you wish to use the [GPT2 model](https://huggingface.co/openai-community/gpt2). In the **Input** field, enter input parameters for your model, for example, `{"inputs":"What is the Capital of Germany?"}`. ## Handle connector response The **Hugging Face connector** is a protocol connector, meaning it is built on top of the **HTTP REST connector**. Therefore, handling response is still applicable [as described](/components/connectors/protocol/rest.md#response). ## Usage example Let's assume you want to use the [BART (large-sized model), fine-tuned on CNN Daily Mail](https://huggingface.co/facebook/bart-large-cnn) model, and created the `HUGGING_FACE_SECRET` secret containing your Hugging Face API key. Consider the following input: - **Hugging Face API key**: `{{secrets.HUGGING_FACE_SECRET}}` - **Model**: `facebook/bart-large-cnn` - **Input**: ```json { "inputs": "Java is a high-level, class-based, object-oriented programming language that is designed to have as few implementation dependencies as possible. It is a general-purpose programming language intended to let programmers write once, run anywhere (WORA), meaning that compiled Java code can run on all platforms that support Java without the need to recompile. Java applications are typically compiled to bytecode that can run on any Java virtual machine (JVM) regardless of the underlying computer architecture. The syntax of Java is similar to C and C++, but has fewer low-level facilities than either of them. The Java runtime provides dynamic capabilities (such as reflection and runtime code modification) that are typically not available in traditional compiled languages. As of March 2024, Java 22 is the latest version. Java 8, 11, 17, and 21 are previous LTS versions still officially supported.", "parameters": { "max_length": 75, "temperature": 10 }, "options": { "use_cache": "false" } } ``` - **Result variable**: `myHuggingFaceResponse`. In the `myHuggingFaceResponse` you will find the following result: ```json { "status":200, "headers":{ ... }, "body":[ { "summary_text":" Java is a high-level, class-based, object-oriented programming language. It is intended to let programmers write once, run anywhere. Java applications are typically compiled to bytecode that can run on any Java virtual machine (JVM) regardless of the underlying computer architecture. As of March 2024, Java 22 is the latest version." } ] } ``` --- ## Kafka connector The **Kafka Producer connector** is an outbound connector that allows you to connect your BPMN service with [Apache Kafka](https://kafka.apache.org/) to produce messages. ## Prerequisites To use the **Kafka Producer connector**, you must have a Kafka instance with a configured bootstrap server. :::note Use Camunda secrets to avoid exposing your sensitive data as plain text. To learn more, see [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md). ::: ## Create a Kafka Producer connector task ## Make your Kafka Producer connector for publishing messages executable To make your **Kafka Producer connector** for publishing messages executable, complete the following sections. ### Authentication (Optional) Set the relevant credentials in the **Authentication** section. For example, `{{secrets.MY_KAFKA_USERNAME}}`. ### Schema In the **Kafka** section: 1. Select the schema strategy for your messages. - Select **No schema**, **Inline schema** for Avro serialization. - Select **Schema registry** if you have a Confluent Schema Registry. 2. Set the URL of the bootstrap server(s). If more than one server is required, use comma-separated values. 3. Set the topic name. 4. (Optional) Set producer configuration values in the **Headers** field. Only `UTF-8` strings are supported as header values. 5. (Optional) Set producer configuration values in the **Additional properties** field. :::info The [appendix](#appendix-and-faq) provides more information about: - [Kafka secure authentication](#what-mechanism-is-used-to-authenticate-against-kafka). - [Inline schema](#inline-schema) and [Schema registry](#schema-registry). - [Pre-configured producer configuration values](#what-are-default-kafka-producer-client-properties) for this connector. Additionally, to learn more about supported producer configurations, see the [official Kafka documentation](https://kafka.apache.org/41/configuration/producer-configs/). ::: ### Message In the **Message** section, set the **Key** and the **Value** that will be sent to Kafka topic. ## Schema strategies :::caution Use Schema strategies with caution, as this is an [alpha feature](/components/early-access/alpha/alpha-features.md). Functionality may not be comprehensive and could change. ::: This connector supports different schema strategies, offering a compact, fast, and binary data exchange format for Kafka messages. When using a schema strategy, each message is serialized according to a specific schema written in JSON format. This schema defines the Kafka message structure, ensuring the data conforms to a predefined format, and enables schema evolution strategies. :::info To learn more about Schema strategies, refer to the official documentation: - [Inline Avro serialization](https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/serdes-avro.html) and [official Apache Avro documentation](https://avro.apache.org/docs/). - [Confluent Schema Registry](https://docs.confluent.io/platform/current/schema-registry/index.html) (Avro, and JSON schemas). ::: ### No schema Select **No schema** to send messages without a schema. This option is suitable for simple messages that do not require a schema. ### Inline schema Select **Inline schema** to send messages with an **Avro schema**. - This option is suitable for messages that require a schema and that are not (or do not need to be) registered in a schema registry. - Enter the Avro schema that defines the message structure into the **Schema** field that appears in the **Message** section. ### Schema registry Select **Schema registry** to send messages with a schema registered in a schema registry. - This option is suitable for messages that require a schema and that are registered in a [schema registry](https://docs.confluent.io/platform/current/schema-registry/index.html). - You must provide: - The **schema registry URL** in the **Kafka** section. - The **schema** itself (that defines the message structure) in the **Message** section. - The **credentials** for the schema registry (if required). Refer to the [Schema Registry documentation](https://docs.confluent.io/platform/current/schema-registry/sr-client-configs.html#basic-auth-credentials-source) for more information. :::info Currently, the Kafka connector supports only [Confluent Schema Registry](https://docs.confluent.io/platform/current/schema-registry/index.html). Other schema registry implementations are not supported at this time. ::: ### Example Avro schema and data The following is an example Avro schema and data: #### Avro schema: ```json { "doc": "Sample schema to help you get started.", "fields": [ { "name": "name", "type": "string" }, { "name": "age", "type": "int" }, { "name": "emails", "type": { "items": "string", "type": "array" } } ], "name": "sampleRecord", "namespace": "com.mycorp.mynamespace", "type": "record" } ``` #### Kafka message - **Key**: `employee1` - **Value**: ```json { "name": "John Doe", "age": 29, "emails": ["johndoe@example.com"] } ``` ## Kafka Producer connector response The **Kafka Producer connector** returns metadata for a record that has been acknowledged by the Kafka instance. The following fields are available in the `response` variable: - `timestamp`: The timestamp of the message. - `offset`: The message offset. - `partition`: The message partition. - `topic`: The topic name. :::info For more information on these fields, refer to the [official Kafka documentation](https://kafka.apache.org/41/getting-started/introduction/). ::: You can use an output mapping to map the response: 1. Use **Result Variable** to store the response in a process variable. For example, `myResultVariable`. 2. Use **Result Expression** to map fields from the response into process variables. For example: ``` = { "messageAcknowledgedAt": response.timestamp } ``` ## Appendix and FAQ ### What mechanism is used to authenticate against Kafka? If the fields **Username** and **Password** are not empty, by default the **Kafka Producer connector** enables the credentials-based SASL SSL authentication and the following properties are set: ``` sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required username='' password=''; security.protocol=SASL_SSL sasl.mechanism=PLAIN ``` If any of the fields are not populated, you must configure your security method for your Kafka configuration. You can do this using the **Additional properties** field. ### What are default Kafka Producer client properties? - Authentication properties (only if both **Username** and **Password** are not empty): ``` sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required username='' password=''; security.protocol=SASL_SSL sasl.mechanism=PLAIN ``` - Bootstrap server property: ``` bootstrap.servers= ``` - Message properties: ``` key.serializer=org.apache.kafka.common.serialization.StringSerializer value.serializer=org.apache.kafka.common.serialization.StringSerializer ``` - Miscellaneous properties: ``` session.timeout.ms=45000 client.dns.lookup=use_all_dns_ips acks=all delivery.timeout.ms=45000 ``` ### What is the precedence of client properties loading? Properties loading consists of three steps: 1. Construct client properties from the BPMN diagram: authentication, bootstrap server, message properties. 2. Load miscellaneous properties. 3. Load and **override** properties from the field **Additional properties**. ### How do I set or override additional client properties? The following example sets a new client property `client.id` and overrides the SASL mechanism to `SCRAM SHA-256` instead of plain text: ``` = { "client.id":"MyDemoClientId", "sasl.mechanism":"SCRAM-SHA-256" } ``` The **Kafka Consumer connector** allows you to consume messages by subscribing to [Kafka](https://kafka.apache.org/) topics and mapping them to your BPMN processes as start or intermediate events. ## Prerequisites To use the **Kafka Consumer connector**, you must have a Kafka instance with a configured bootstrap server. :::note Use Camunda secrets to avoid exposing your sensitive data as plain text. To learn more, see [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md). ::: ## Create a Kafka Consumer connector event 1. Add a **Start Event** or an **Intermediate Event** to your BPMN diagram to get started. 2. Change its template to a Kafka Consumer. 3. Fill in all required properties. 4. Complete your BPMN diagram. 5. Deploy the diagram to activate the Kafka consumer. ## Configure your Kafka Consumer connector To make your **Kafka Consumer connector** executable, fill in the required properties. ### Authentication In the **Authentication** section, select the **Authentication type**. If you selected **Credentials** as the **Authentication type**, set the username and password. :::note - Use Camunda secrets to avoid exposing your sensitive data as plain text. To learn more, see [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md). - To learn more about Kafka authentication, see [Kafka secure authentication](#what-mechanism-is-used-to-authenticate-against-kafka-1). ::: ### Kafka properties In the **Kafka** section, you can configure the following properties: - **Consumer Group ID**: Set the consumer group ID for this connector. Always provide an explicit, stable value that identifies the logical consumer group (for example, `my-app-order-processor`). If you leave this field empty, the connector auto-generates an ID from its internal deduplication key. That generated ID can change across connector upgrades, including from 8.8 to 8.9, causing Kafka to treat the connector as a new consumer group and potentially replay already processed messages. - **Schema strategy**: Select the schema strategy for your messages. - Select **No schema**, **Inline schema** for Avro serialization. - Select **Schema registry** If you have a Confluent Schema Registry. - **Bootstrap servers**: Set the URL of the bootstrap server(s). If more than one server is required, use comma-separated values. - **Topic**: Set the topic name. - **Additional properties**: Set consumer configuration values. - **Offsets**: Set the offsets for the partition. The number of offsets specified should match the number of partitions on the current topic. - **Auto offset reset**: Set the strategy to use when there is no initial offset in Kafka or if the specified offsets do not exist on the server. :::info The [appendix](#appendix-and-faq-1) provides more information about [pre-configured consumer configuration values](#what-are-default-kafka-consumer-client-properties) for this connector. Additionally, to learn more about supported consumer configurations, see the [official Kafka documentation](https://kafka.apache.org/41/configuration/consumer-configs/). ::: ### Schema strategy #### No schema Select **No schema** to send messages without a schema. This option is suitable for simple messages that don’t require a schema. #### Inline schema Select **Inline schema** to send messages with an **Avro schema**. - This option is appropriate for messages that require a schema but are not (or do not need to be) registered in a schema registry. - Enter the Avro schema that defines the message structure into the **Schema** field in the **Message** section. #### Schema registry Select **Schema registry** to send messages using a schema registered in a schema registry. - This option is appropriate for messages that require a schema and are registered in a [schema registry](https://docs.confluent.io/platform/current/schema-registry/index.html). - You must provide: - The **schema registry URL** in the **Kafka** section. - The **schema** itself (defining the message structure) in the **Message** section. - The **credentials** for the schema registry, if required. See the [Schema Registry documentation](https://docs.confluent.io/platform/current/schema-registry/sr-client-configs.html#basic-auth-credentials-source) for more details. :::info Currently, the Kafka connector supports only the [Confluent Schema Registry](https://docs.confluent.io/platform/current/schema-registry/index.html). Other schema registry implementations are not supported at this time. Schema configuration is required only for the outbound connector. It is not required when using Inbound Connectors. ::: ### Example Avro schema and data If the expected Kafka message looks like this: - **Key**: `employee1` - **Value**: ```json { "name": "John Doe", "age": 29, "emails": ["johndoe@example.com"] } ``` The corresponding Avro schema to describe this message's structure would be: ```json { "doc": "Sample schema to help you get started.", "fields": [ { "name": "name", "type": "string" }, { "name": "age", "type": "int" }, { "name": "emails", "type": { "items": "string", "type": "array" } } ], "name": "sampleRecord", "namespace": "com.mycorp.mynamespace", "type": "record" } ``` This schema defines a structure for a record that includes a name (string), an age (integer), and emails (an array of strings), aligning with the given Kafka message's value format. ### Activation condition **Activation condition** is an optional FEEL expression field that allows for the fine-tuning of the connector activation. This condition filters if the process step triggers when a Kafka message is consumed. For example, `=(value.itemId = "a4f6j2")` only triggers the start event or continues the catch event if the Kafka message has a matching itemId in the incoming message payload. Leave this field empty to trigger your process every time. :::danger By default, this connector does not commit the offset if the message cannot be processed. This includes cases where the activation condition is not met. This means that if there is a message in the topic that cannot be processed due to an activation condition mismatch, the Kafka subscription will be stopped. Follow the steps below to configure this behavior. ::: To ignore messages that do not meet the activation condition and commit the offset, select the **Consume unmatched events** checkbox. | **Consume unmatched events** checkbox | Activation condition | Outcome | | ------------------------------------- | -------------------- | ---------------------------------------------------- | | Checked | Matched | connector is triggered, offsets are commited | | Unchecked | Matched | connector is triggered, offsets are commited | | Checked | Unmatched | connector is not triggered, offsets are commited | | Unchecked | Unmatched | connector is not triggered, offsets are not commited | ### Correlation The **Correlation** section allows you to configure the message correlation parameters. :::note The **Correlation** section is not applicable for the plain **start event** element template of the Kafka connector. Plain **start events** are triggered by process instance creation and do not rely on message correlation. ::: #### Correlation key - **Correlation key (process)** is a FEEL expression that defines the correlation key for the subscription. This corresponds to the **Correlation key** property of a regular **message intermediate catch event**. - **Correlation key (payload)** is a FEEL expression used to extract the correlation key from the incoming message. This expression is evaluated in the connector Runtime and the result is used to correlate the message. For example, given that your correlation key is defined with `myCorrelationKey` process variable, and the incoming Kafka message contains `value:{correlationKey:myValue}`, your correlation key settings would be as follows: - **Correlation key (process)**: `=myCorrelationKey` - **Correlation key (payload)**: `=value.correlationKey` You can also use the key of the message to accomplish this in the **Correlation key (payload)** field with `=key`. :::info To learn more about correlation keys, see [messages](../../../concepts/messages). ::: #### Message ID expression The **Message ID expression** is an optional field that allows you to extract the message ID from the incoming message. The message ID serves as a unique identifier for the message and is used for message correlation. This expression is evaluated in the connector Runtime and the result is used to correlate the message. In most cases, it is not necessary to configure the **Message ID expression**. However, it is useful if you want to ensure message deduplication or achieve a certain message correlation behavior. :::info To learn more about how message IDs influence message correlation, see [messages](../../../concepts/messages#message-correlation-overview). ::: For example, if you want to set the message ID to the value of the `transactionId` field in the incoming message, you can configure the **Message ID expression** as follows: ``` = value.transactionId ``` #### Message TTL The **Message TTL** is an optional field that allows you to set the time-to-live (TTL) for the correlated messages. TTL defines the time for which the message is buffered in Zeebe before being correlated to the process instance (if it can't be correlated immediately). The value is specified as an ISO 8601 duration. For example, `PT1H` sets the TTL to one hour. Learn more about the TTL concept in Zeebe in the [message correlation guide](../../../concepts/messages#message-buffering). ### Deduplication The **Deduplication** section allows you to configure the connector deduplication parameters. **Connector deduplication** is a mechanism in the connector Runtime that determines how many Kafka subscriptions are created if there are multiple occurrences of the **Kafka Consumer connector** in the BPMN diagram. This is not to be confused with **message deduplication**. By default, the connector runtime deduplicates connectors based on properties, so that elements with the same subscription properties only result in one subscription. :::info To learn more about deduplication, see [deduplication](../advanced-topics/deduplication.md). ::: To customize the deduplication behavior, select the **Manual mode** checkbox, and configure the custom deduplication ID. ### Output mapping The **Kafka Consumer connector** returns the consumed message. The following fields are available in the `response` variable: - `key`: The key of the message. - `value`: The value of the message. - `rawValue`: The value of the message as a JSON string. You can use an output mapping to map the response: 1. Use **Result variable** to store the response in a process variable. For example, `myResultVariable`. 2. Use **Result expression** to map fields from the response into process variables. For example: ``` = { "itemId": value.itemId } ``` ## Activate the Kafka Consumer connector by deploying your diagram When you click the **Deploy** button, your Kafka Consumer is activated and starts consuming messages from the specified topic. ## Appendix and FAQ ### What mechanism is used to authenticate against Kafka? If you selected _Credentials_ as the **Authentication type** and the fields **Username** and **Password** are not empty, by default the **Kafka Consumer connector** enables the credentials-based SASL SSL authentication, and sets the following properties: ``` sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required username='' password=''; security.protocol=SASL_SSL sasl.mechanism=PLAIN ``` If any of the field is not populated, you must configure your security method for your Kafka configuration. You can do this using the **Additional properties** field. ### What are default Kafka Consumer client properties? - Authentication properties (only if both **Username** and **Password** are not empty): ``` sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required username='' password=''; security.protocol=SASL_SSL sasl.mechanism=PLAIN ``` - Bootstrap server property: ``` bootstrap.servers= ``` - Message properties: ``` key.deserializer=org.apache.kafka.common.serialization.StringDeserializer value.deserializer=org.apache.kafka.common.serialization.StringDeserializer ``` - Miscellaneous properties: ``` session.timeout.ms=45000 client.dns.lookup=use_all_dns_ips acks=all group.id=kafka-inbound-connector-{{bpmnProcessId}} enable.auto.commit=false ``` :::caution The `group.id` value above is auto-generated when no explicit **Consumer Group ID** is configured in the connector. This generated ID is derived from the connector's internal deduplication key and can change across connector upgrades, including from 8.8 to 8.9. When the group ID changes, Kafka treats the connector as a new consumer group, which means committed offsets are not reused and messages may be replayed. To avoid this, always set an explicit **Consumer Group ID**. You can [look up existing consumer groups](https://docs.confluent.io/kafka/operations-tools/manage-consumer-groups.html#list-groups-and-view-offsets) to find the current group ID in use. ::: ### What is the precedence of client properties loading? Properties loading consists of three steps: 1. Construct client properties from the BPMN diagram: authentication, bootstrap server, message properties. 2. Load miscellaneous properties. 3. Load and **override** properties from the field **Additional properties**. ### How is the message payload deserialized? As Kafka messages usually use JSON format, we first try to deserialize it as a `JsonElement`. If this fails (for example, because of a wrong format) we use the `String` representation of the original raw value. For convenience, we always store the original raw value as `String` in a different attribute. The deserialized object structure: ``` { key: "String" rawValue: "String" value: {} } ``` ### When is the offset committed? What happens if the connector execution fails? The following outcomes are possible: - If the connector execution is successful and the **Activation condition** was met, the offset is committed. - If the **Activation condition** was not met, the offset is also committed to prevent consuming the same message twice. - If the connector execution fails due to an unexpected error (for example, Zeebe is unavailable), the offset is not committed. ### What lifecycle does the Kafka Consumer connector have? The Kafka Consumer connector is a long-running connector that is activated when the process is deployed, and deactivated when the process is undeployed or overwritten by a new version. --- ## Message Send connector The Message Send connector can **publish** or **correlate** BPMN messages. It either calls the [PublishMessage RPC](/apis-tools/zeebe-api/gateway-service.md#publishmessage-rpc) of the Zeebe API to send messages buffered by Zeebe, or the [Correlate a message REST API](/apis-tools/orchestration-cluster-api-rest/specifications/correlate-message.api.mdx) to get the ID of the process instance that received the message without buffering. The element template allows you to select the mode: - `publish message (with buffer)` - `correlate message (with result)` and fill all parameters directly in the modeler. ## Publish message (buffered) parameters | Parameter | Type | Description | Default / Optional | | --------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------ | | Message name | string | Name of the message. Must match a receiving message event. | Required | | Correlation key | string | Value for correlating with a process instance. Can be empty to start a new instance. | Optional | | Payload | JSON | Variables to transfer to the receiving process instance. Example: `{"customerName": cust_name}`. Multiple variables can be defined. | Required | | Time to live | int | Time in milliseconds to buffer the message. | No buffer | | Message ID | string | Unique message ID for idempotency. Ensures the message is delivered only once until correlated. | Optional | | Tenant ID | string | Tenant ID of the receiving instance. | `` | | Request timeout | int | Timeout for the publish message command. | Connector default | ### Response | Field | Type | Description | | ---------- | ------ | ---------------------------------- | | messageKey | int64 | Unique ID of the published message | | tenantId | string | Tenant ID of the message | ## Correlate message (with result) parameters | Parameter | Type | Description | Default / Optional | | --------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------ | | Message name | string | Name of the message. Must match a receiving message event. | Required | | Correlation key | string | Value for correlating with a process instance. Can be empty to start a new instance. | Optional | | Payload | JSON | Variables to transfer to the receiving process instance. Example: `{"customerName": cust_name}`. Multiple variables can be defined. | Required | | Tenant ID | string | Tenant ID of the receiving instance. | `` | | Request timeout | int | Timeout for the correlate message command. | Connector default | ### Response | Field | Type | Description | | ------------------ | ------ | ------------------------------------------------------------- | | messageKey | int64 | Unique ID of the correlated message | | processInstanceKey | int64 | Key of the first process instance the message correlated with | | tenantId | string | Tenant ID of the message | > The connector raises an incident with a detailed error message containing the 404 status `Not found` if the message could not be correlated. --- ## Microsoft 365 email inbound connector The **Microsoft 365 Email Inbound connector** allows you to consume emails by monitoring [Microsoft 365](https://outlook.office.com/mail/) mailboxes and mapping them to your BPMN processes as start or intermediate events. ## Prerequisites - A [Microsoft 365](https://outlook.office.com/mail/) mailbox to monitor. - Sufficient access rights at [Microsoft Entra](https://entra.microsoft.com) to create a new app and set [Microsoft Graph](https://developer.microsoft.com/en-us/graph) permissions. - Required Microsoft Graph API permissions for your application: - `Mail.Read` - Read emails from mailboxes. - `Mail.ReadWrite` - Read and modify emails (required for operations like mark as read, move, delete). Learn more about [creating, configuring, and authorizing Microsoft Apps](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app). :::note Use Camunda secrets to avoid exposing your Microsoft credentials as plain text. Refer to our documentation on [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ::: ## Security considerations ### Authentication security The connector supports multiple authentication methods. For server-to-server scenarios without user interaction, **Client credentials** is recommended. For delegated (user-context) scenarios, use **Refresh token**. **Bearer token** is available but not recommended for polling connectors due to short token lifetimes. All authentication methods provide: - **Scoped permissions**: API permissions are explicitly granted to the application. - **Auditable access**: All API calls are associated with the registered application. ### Restricting mailbox access with RBAC :::warning By default, an Azure AD application with `Mail.Read` or `Mail.ReadWrite` permissions can access _all mailboxes_ in your organization. In the OAuth 2.0 client credentials flow, the application gets access to all mailboxes it has been granted permissions for. To restrict access to specific mailboxes, use _Role-Based Access Control (RBAC) for Applications_. Learn more about [scoping application permissions to specific Exchange Online mailboxes](https://learn.microsoft.com/en-us/graph/auth-limit-mailbox-access). ::: ## Configure the Microsoft 365 Outlook connector To use the Microsoft 365 Email Inbound connector, you must register an application in Microsoft Entra (formerly Azure AD) and configure the required permissions. :::note This is a simplified guide to help you get started. For the full guide, refer to the [official Microsoft documentation](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app). ::: ### Register an application in Microsoft Entra 1. Sign in to the [Microsoft Entra admin center](https://entra.microsoft.com). 2. Navigate to **Identity** > **Applications** > **App registrations**. 3. Click **New registration**. 4. Enter a name for your application (for example, `Camunda Email Connector`). 5. Select **Accounts in this organizational directory only** for supported account types. 6. Click **Register**. ### Configure API permissions 1. In your registered application, navigate to **API permissions**. 2. Click **Add a permission**. 3. Select **Microsoft Graph** > **Application permissions**. 4. Search for and add the following permissions: - `Mail.Read` - Required for reading emails. - `Mail.ReadWrite` - Required for operations like mark as read, move, or delete emails. 5. Click **Add permissions**. 6. Click **Grant admin consent** for your organization. This requires administrator privileges. :::warning The **Grant admin consent** step is critical. Without admin consent, the application cannot access mailbox data. ::: ## Authentication Choose an authentication type in the **Authentication** section of the connector properties panel. The connector uses the [Microsoft Graph API](https://learn.microsoft.com/en-us/graph/overview) and supports the following authentication methods. Visit the [Microsoft Graph auth overview](https://learn.microsoft.com/en-us/graph/auth/) for more information. :::note Choosing an auth type for email polling - **Client credentials** (recommended): Uses Azure SDK's automatic token renewal. Best suited for long-running polling connectors. - **Refresh token**: The connector re-exchanges the refresh token for a fresh access token on every poll cycle, ensuring continuous access. Suitable for delegated (user-context) scenarios. - **Bearer token**: The client is created once with the provided token. Bearer tokens are short-lived (typically 1 hour) and **not recommended for polling connectors** — the token will expire and the connector will stop working. ::: ## Create a Microsoft 365 email inbound connector event 1. Add a **Start Event** or an **Intermediate Catch Event** to your BPMN diagram. 2. Change its template to **Microsoft 365 Email Inbound**. 3. Fill in all required properties. 4. Complete your BPMN diagram. 5. Deploy the diagram to activate the email monitoring. ## Configure your Microsoft 365 email inbound connector To make your **Microsoft 365 Email Inbound connector** executable, fill in the required properties. ### Mailbox configuration In the **Mailbox** section, configure which mailbox and folder to monitor: - **User ID/User Principal Name**: The email address or user principal name of the mailbox to monitor (for example, `user@company.com`). - **Folder Identifier Type**: Select how to identify the folder to monitor: - **Folder ID** (default): Use a [well-known folder ID](https://learn.microsoft.com/en-us/graph/api/resources/mailfolder?view=graph-rest-1.0#properties) such as `inbox`, `drafts`, or `sentitems`, or a custom folder ID. Prefer this option when using well-known folders. - **Folder Name**: Use the display name of the folder. The name must be unique within the mailbox. Use this option for custom folders where you don't know the folder ID. ### Polling configuration Configure how frequently the connector checks for new emails using the **Polling Interval** field. Specify the time between polls in ISO 8601 duration format (for example, `PT30S` for 30 seconds, `PT5M` for 5 minutes). Review [how to configure a time duration](/components/modeler/bpmn/timer-events/timer-events.md#time-duration) for details on the format. :::tip Choose an appropriate polling interval based on your use case. More frequent polling increases API usage but provides faster response times. ::: ### Email filtering Optionally filter which emails trigger the process: #### Simple filter The simple filter provides the following options: - **Only Unread**: When enabled (default), only unread emails (`isRead eq false`) will trigger the process. When disabled, all emails (both read and unread) will trigger the process. - **Subject Contains** (optional): Only fetch emails where the subject contains this text (case-sensitive) - **From Email Address** (optional): Only fetch emails from this sender address (exact match, for example, `invoice@vendor.com`) #### Advanced filter For more complex filtering, use the **Advanced Configuration** section to provide an [OData filter query](https://learn.microsoft.com/en-us/graph/query-parameters#filter-parameter). :::note OData filter queries are evaluated at deployment time and can only use static values or [Camunda secrets](/components/hub/organization/manage-clusters/manage-secrets.md). They cannot reference process variables. If you need dynamic filtering based on runtime data, use the [Activation Condition](#activation-condition) section instead. ::: Examples of filters not supported by the simple filter: Filter emails from a specific domain: ``` endswith(from/emailAddress/address, '@example.com') and isRead eq false ``` Filter emails whose body contains specific text: ``` contains(body/content, 'password') and isRead eq false ``` ### Email processing operations Configure what happens to emails after they trigger a process. **At least one operation must be configured.** - **Mark as read**: Mark the email as read after processing - **Delete**: Delete the email after processing - **Force delete**: Bypass the deleted items folder and permanently delete - **Move to folder**: Move the email to a different folder after processing. Select a **Folder Identifier Type** (folder ID or folder name) and specify the destination folder. :::warning Avoid Infinite Reprocessing Loops Ensure your processing operation removes emails from matching your filter criteria. If emails continue to match the filter after processing, they will be reprocessed on every polling cycle, potentially creating multiple process instances for the same email. **Examples of dangerous configurations:** - Disabling **Only Unread** filter (fetching all emails) and only using **Mark as read** (emails remain in the monitored folder and continue matching the filter) - Moving emails to a different folder and then having another process or rule move them back to the monitored folder (they will match the filter again) ::: ### Activation condition **Activation condition** is an optional FEEL expression field that allows for fine-tuning of connector activation. This condition filters whether the process step triggers when an email is consumed. For example, `=(subject = "Order Confirmation")` only triggers the start event or continues the catch event if the email subject matches exactly. Leave this field empty to trigger your process for every email that matches the filtering criteria. :::note The activation condition is evaluated after the email filter. Use email filters for simple conditions and activation conditions for complex FEEL-based logic. ::: When an email matches the filter but does not meet the activation condition, the connector does not trigger and the email is not processed (not marked as read, deleted, or moved). This means the email will be fetched again on the next polling cycle. To change this behavior, enable the **Consume unmatched events** checkbox in the **Activation** section. Learn more about [consuming unmatched events](../use-connectors/inbound.md#consume-unmatched-events). ### Correlation The **Correlation** section allows you to configure message correlation parameters. :::note The **Correlation** section is not applicable for the plain **start event** element template of the Microsoft 365 Email Inbound connector. Plain **start events** are triggered by process instance creation and do not rely on message correlation. ::: #### Correlation key - **Correlation key (process)** is a FEEL expression that defines the correlation key for the subscription. This corresponds to the **Correlation key** property of a regular **message intermediate catch event**. - **Correlation key (payload)** is a FEEL expression used to extract the correlation key from the incoming email. This expression is evaluated in the connector runtime and the result is used to correlate the message. For example, if your correlation key is defined with the `orderId` process variable, and the incoming email subject contains `Order #12345`, you could extract the order ID from the subject: - **Correlation key (process)**: `=orderId` - **Correlation key (payload)**: `=substring(subject, 7)` (extracts "12345" from "Order #12345") :::note To learn more about correlation keys, see [messages](../../../concepts/messages). ::: #### Message ID expression The **Message ID expression** is an optional field that allows you to extract the message ID from the incoming email. The message ID serves as a unique identifier and is used for message deduplication. By default, the connector uses the email's unique identifier from Microsoft Graph. However, you can customize this if needed: ```feel = id ``` :::note To learn more about how message IDs influence message correlation, see [messages](../../../concepts/messages#message-correlation-overview). ::: #### Message TTL The **Message TTL** is an optional field that allows you to set the time-to-live (TTL) for correlated messages. TTL defines the time for which the message is buffered in Zeebe before being correlated to the process instance. The value is specified as an ISO 8601 duration. For example, `PT1H` sets the TTL to one hour. Learn more about the TTL concept in Zeebe in the [message correlation guide](../../../concepts/messages#message-buffering). ### Deduplication The **Deduplication** section allows you to configure connector deduplication parameters. **Connector deduplication** is a mechanism in the connector runtime that determines how many email subscriptions are created if there are multiple occurrences of the **Microsoft 365 Email Inbound connector** in the BPMN diagram. By default, the connector runtime deduplicates connectors based on properties, so elements with the same subscription properties only result in one subscription. :::note To learn more about deduplication, see [deduplication](../use-connectors/inbound.md#connector-deduplication). ::: To customize the deduplication behavior, select the **Manual mode** checkbox and configure a custom deduplication ID. ### Output mapping The **Microsoft 365 Email Inbound connector** returns the consumed email message with the following structure: ```json { "id": "AAMkAGVmMDEzM...", "conversationId": "AAQkAGVmMDEzM...", "subject": "Invoice #12345", "body": "Please find attached...", "bodyContentType": "text/plain", "sender": { "name": "John Doe", "address": "john.doe@example.com" }, "recipients": [ { "name": "Jane Smith", "address": "jane.smith@company.com" } ], "cc": [], "bcc": [], "receivedDateTime": "2024-01-15T14:30:00Z", "attachments": [ { "id": "AAMkAGVmMDEzM...", "name": "invoice.pdf", "contentType": "application/pdf", "size": 125000 } ] } ``` You can use an output mapping to map the response: 1. Use **Result variable** to store the response in a process variable. For example, `emailMessage`. 2. Use **Result expression** to map fields from the response into process variables. For example: ```feel = { "emailSubject": response.subject, "senderEmail": response.sender.address, "receivedAt": response.receivedDateTime, "hasAttachments": count(response.attachments) > 0 } ``` ## Activate the Microsoft 365 email inbound connector by deploying your diagram When you click the **Deploy** button, your Microsoft 365 Email Inbound connector is activated and starts monitoring the specified mailbox for new emails. The connector is a long-running connector that remains active until the process is undeployed or overwritten by a new version. ## Next steps - Learn more about [Microsoft Graph Mail API](https://learn.microsoft.com/en-us/graph/api/resources/mail-api-overview) and its capabilities. - Explore [configuring application permissions to specific mailboxes](https://learn.microsoft.com/en-us/graph/auth-limit-mailbox-access) for enhanced security. - Learn about [other connectors available](./available-connectors-overview.md) in Camunda to integrate with different systems and services. - Learn more about [using connectors](../use-connectors/index.md). - Learn more about [inbound connectors](../use-connectors/inbound.md). ## Appendix and FAQ ### What happens if email processing fails? If the connector fails to process an email (for example, due to Zeebe being unavailable), the email remains unprocessed and the connector will attempt to process it again on the next polling cycle. If you configured a processing operation (mark as read, delete, move), the operation will only be executed after successful process correlation. ### Can I monitor multiple folders? To monitor multiple folders, create separate connector instances with different folder configurations. You can reuse the same authentication credentials across multiple instances. ### How are email attachments handled? Email attachments are automatically fetched and stored using [Camunda document handling](/components/document-handling/getting-started.md). The attachment metadata is included in the connector output (see [Output Mapping](#output-mapping)), and each attachment is available as a document reference that you can use in subsequent process steps. For example, to pass an attachment to another connector or download it, use the document reference from the `attachments` array: ```feel = { "firstAttachment": response.attachments[1], "allAttachments": response.attachments } ``` Learn more about working with documents in [document handling](/components/document-handling/getting-started.md). ### What lifecycle does the Microsoft 365 Email Inbound connector have? The Microsoft 365 Email Inbound connector is a long-running connector that is activated when the process is deployed, and deactivated when the process is undeployed or overwritten by a new version. --- ## Microsoft 365 connector The Microsoft 365 Connector is an outbound connector that allows you to connect your BPMN service with [Microsoft 365](https://outlook.office.com/mail/) mail to send emails, read emails, and manage folders. ## Prerequisites - A [Microsoft 365](https://outlook.office.com/mail/) mail instance. - Sufficient permissions in [Microsoft Entra](https://entra.microsoft.com) to: - Create a new app registration - Configure [Microsoft Graph](https://developer.microsoft.com/en-us/graph) permissions - Assign the app to a user Learn more about [creating, configuring, and authorizing a Microsoft app](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app). :::note Use Camunda secrets to avoid exposing your Microsoft credentials as plain text. Refer to our documentation on [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ::: ## Create a Microsoft 365 connector task ## Access control Each operation requires permissions to be assigned by a system administrator. Learn more about [Microsoft permissions](https://learn.microsoft.com/en-us/entra/identity-platform/permissions-consent-overview). :::warning By default, an application with Mail API permissions can access _all mailboxes_ in your organization. To restrict access to specific mailboxes, use _Role-Based Access Control (RBAC) for Applications_. Learn more about [scoping application permissions to specific Exchange Online mailboxes](https://learn.microsoft.com/en-us/graph/auth-limit-mailbox-access). ::: ### Bearer token authentication If you own a bearer token, in the **Authentication** section select **Bearer token** in the **Type** field. Enter a bearer token in the field **Bearer token**. Use [Camunda secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to avoid exposing sensitive credentials. :::note The default time-to-live (TTL) for bearer tokens is 3600 seconds. Therefore, this approach might not work for long-living and/or repetitive processes. ::: ### OAuth2 client credentials flow authentication :::note In the client credential flow, an application gets access to all accounts associated with the organization. For example, if an app has permissions `Mail.Read`, it will be able to read emails of all users. ::: To proceed with this step, you'll need the following data: - OAuth 2.0 token endpoint - Client ID (Application ID) - Client secret; can be created on your application page The app must be assigned to a user. If you own a bearer token, in the **Authentication** section select **OAuth 2.0** in the **Type** field. Enter the above data into the respective fields. Learn more about [creating, configuring, and authorizing Microsoft App](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app). ## Select operation to execute Select the desired operation from the **Operations** section. ### Get user folders Related Microsoft Graph API: [user: list mailFolders](https://learn.microsoft.com/en-us/graph/api/user-list-mailfolders) 1. Enter user's email or system UUID to fetch all their folders in the **User ID** field. 2. You can also pass [OData parameters](https://learn.microsoft.com/en-us/graph/query-parameters?tabs=http) in the **Query parameters** field. For example, if you wish to pass an OData `$top` URL parameter, execute the following: ```json { "$top": 10 } ``` ### Create mail folder for a user Related Microsoft Graph API: [user: create mailFolder](https://learn.microsoft.com/en-us/graph/api/user-post-mailfolders) 1. Enter user's email or system UUID to fetch all their folders in the **User ID** field. 2. In the **Request** section, enter a **Folder display name** string value. ### Get user messages Related Microsoft Graph API: [user: list messages](https://learn.microsoft.com/en-us/graph/api/user-list-messages) 1. Enter user's email or system UUID to fetch all their folders in the **User ID** field. 2. You can also pass [OData parameters](https://learn.microsoft.com/en-us/graph/query-parameters?tabs=http) in the **Query parameters** field. For example, if you wish to pass an OData `$top` URL parameter, execute the following: ```json { "$top": 10 } ``` ### Send mail on behalf of a user Related Microsoft Graph API: [user: sendMail](https://learn.microsoft.com/en-us/graph/api/user-sendmail) 1. Enter user's email or system UUID to fetch all their folders in the **User ID** field. 2. In the **Request** section, enter a **Subject** string value. 3. Select **Body content type** from the dropdown. 4. Enter desired content in the field **Body content**. 5. Pass an array of emails into the **To recipients** field, for example `["myuser1@mycompany.com", "myuser2@mycompany.com"]`. 6. (Optional) Pass an array of emails into the **CC recipients** field, for example `["myuser3@mycompany.com", "myuser4@mycompany.com"]`. ## Handle connector response The Microsoft 365 Connector is a protocol connector, meaning it is built on top of the **HTTP REST connector**, therefore [handling response is still applicable](/components/connectors/protocol/rest.md#response). --- ## Microsoft Teams connector The **Microsoft Teams connector** is an outbound connector that allows you to connect your BPMN process with [Microsoft Teams](https://www.microsoft.com/microsoft-teams/) to manage interactions. ## Prerequisites To use the **Microsoft Teams connector**, you need to have a [Microsoft Teams](https://www.microsoft.com/microsoft-teams/) account and relevant [permissions](https://support.microsoft.com/en-us/office/manage-team-settings-92d238e6-0ae2-447e-af90-40b1052c4547) or the registered application in the [Azure Active Directory](https://aad.portal.azure.com/) (visit [how to register the app](https://learn.microsoft.com/en-us/graph/auth-register-app-v2) for more information) alongside the relevant [Microsoft Graph API permissions](https://learn.microsoft.com/en-us/graph/permissions-reference). :::note Use Camunda secrets to store credentials so you don't expose sensitive information directly from the process. See [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ::: ## Create a Microsoft Teams connector task ## Make your Microsoft Teams connector executable To work with Microsoft Teams, choose the required connection type in the **Authentication** section and complete the mandatory fields highlighted in red in the connector properties panel on the right side of the screen. :::note All the mandatory and non-mandatory fields depending on the authentication selection you choose are covered in the upcoming sections. ::: ## Authentication Choose an authentication type according to your requirements. The Microsoft Teams connector uses the [Microsoft Graph API](https://learn.microsoft.com/en-us/graph/overview). Visit the [Microsoft Graph auth overview](https://learn.microsoft.com/en-us/graph/auth/) for more information on the Microsoft Graph API authentication. You must have a user account with Microsoft Teams with the necessary permissions. See more at [Microsoft Teams overview](https://learn.microsoft.com/microsoftteams/teams-overview). If you don't have administration roles and permissions, ask your Microsoft Teams administrator to add required permissions to work with the **Microsoft Teams connector**. :::note With **Client credentials** type authentication, some methods of the **Microsoft Teams connector** may not be available. Find more details in the [chat methods table](#chat-methods) and [channel methods table](#channel-methods). ::: ## Conversation type and method In the **Operation** section, choose a conversation type of either **Chat** or **Channel**. Then, choose one of the suggested methods. For example, if you want to send a message in a Microsoft Teams channel, choose the conversation type **Channel** and method **Send message in channel**. ## Data section ### Chat conversation type #### Properties | Property | Methods | Required | Type | Description | | :-------------: | :---------------------------------------------------------------------------------------------------------------------------------------------: | :------: | :-------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | | Chat ID | Get chat by ID List chat members Send message in chat List messages in chat Get message in chat List chat members | Yes | string | Microsoft Teams chat ID | | Content | Send message in chat | Yes | text | Content that will be sent to chat | | Content Type | Send message in chat | Yes | dropdown | Content type of body message | | Chat type | Create a new chat | Yes | dropdown | Click **one on one** to create a one-on-one chat or **group** to create a group chat. | | Topic | Create a new chat | No | string | Topic of chat | | Members | Create a new chat | Yes | FEEL expression | See [members property](#members-property) to learn more. | | Top | List messages in chat | No | numbers | Controls the number of items per response; maximum allowed top value is 50. | | Order by | List messages in chat | Yes | dropdown | Can order by 'lastModifiedDateTime' and 'createdDateTime'. | | Expand response | Get chat by ID | Yes | dropdown | Choose | | Filter | List messages in chat | No | string | Sets the date range filter for the lastModifiedDateTime and createdDateTime properties. [Learn more about filtering](https://learn.microsoft.com/en-us/graph/filter-query-parameter). | | Message ID | Get message in chat | Yes | string | Microsoft Teams chat message ID | ##### Expand response For method **Get chat by ID**, you can get more information in the response by using the dropdown property **Expand response**. You can choose one of the following values: - select **With chat members**, to get information about chat members. - select **With last message preview**, to get last message in chat. **Note:** This function doesn't work with [client credentials type authentication](#client-credentials-type-authentication), make sure that you use another authentication type. - select **Without expand**, to get main information about chat. ##### Members property The **members** property must contain a list of members: | Property | Type | Required | | :---------------: | :----------: | -------------------------------------- | | userId | string | Yes, if 'userPrincipalName' is not set | | userPrincipalName | string | Yes, if 'userId' is not set | | roles | string array | Yes | ```json [ { "userId": "abc01234-0c7f-012c-9876-&812dsfw2", "roles": ["owner"] }, { "principalName": "john.dou@mail.com", "roles": ["owner"] } ] ``` #### Chat methods | Method | Use [protected APIs](https://learn.microsoft.com/en-us/graph/api/resources/teams-api-overview#teams-apis-that-require-rscs-permissions) | Available for [client credentials type authentication](#client-credentials-type-authentication) | Link to method documentation with required permissions and return value | | :-------------------: | :-------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------: | | Create a new chat | false | true | [https://learn.microsoft.com/en-us/graph/api/chat-post](https://learn.microsoft.com/en-us/graph/api/chat-post?view=graph-rest-1.0&tabs=http) | | Get chat by ID | false | true | [https://learn.microsoft.com/en-us/graph/api/chat-get](https://learn.microsoft.com/en-us/graph/api/chat-get?view=graph-rest-1.0&tabs=http) | | List chats | false | true | [https://learn.microsoft.com/en-us/graph/api/chat-list](https://learn.microsoft.com/en-us/graph/api/chat-list?view=graph-rest-1.0&tabs=http) | | List chat members | false | false | [https://learn.microsoft.com/en-us/graph/api/chat-list-members](https://learn.microsoft.com/en-us/graph/api/chat-list-members?view=graph-rest-1.0&tabs=http) | | Send message in chat | false | false | [https://learn.microsoft.com/en-us/graph/api/chat-post-messages](https://learn.microsoft.com/en-us/graph/api/chat-post-messages?view=graph-rest-1.0&tabs=http) | | Get message in chat | false | true | [https://learn.microsoft.com/en-us/graph/api/chatmessage-get](https://learn.microsoft.com/en-us/graph/api/chatmessage-get?view=graph-rest-1.0&tabs=http) | | List messages in chat | true | true | [https://learn.microsoft.com/en-us/graph/api/chat-list-messages](https://learn.microsoft.com/en-us/graph/api/chat-list-messages?view=graph-rest-1.0&tabs=http) | ### Channel conversation type #### Properties | Property | Methods | Required | Type | Description | | :---------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------: | :---------------: | :--------------------------------------------------------------------------------------------------------------------: | | Group ID | Create channel Get channel List channels Send message to channel Get channel message List channel messages List message replies List members | Yes | string | Microsoft Teams group ID | | Channel ID | Get channel List channels Send message to channel Get channel message List channel messages List message replies List members | Yes | string | Microsoft Teams channel ID | | Display name | Create channel | No | string | Displayed name of new Microsoft Teams channel | | Description | Create channel | No | text | Description of new Microsoft Teams channel | | Channel membership type | Create channel | Yes | dropdown | See [teams-channels-overview](https://learn.microsoft.com/microsoftteams/teams-channels-overview) for more information | | Owner | Create channel (if Channel membership type != STANDARD) | Yes | string | Channel owner; Microsoft Teams user ID or Microsoft Teams principal name | | Filter | List channels | No | string | The search filter. [Learn more about filtering](https://learn.microsoft.com/en-us/graph/filter-query-parameter) | | Content | Send message to channel | Yes | text | Content that will be sent to chat | | Content Type | Send message to channel | Yes | dropdown | Content type of body message | | Message ID | Get channel message | Yes | string | Message ID of Microsoft Teams in channel | | Top | List channel messages | No | numbers | Controls the number of items per response | | With replies | List channel messages | Yes | boolean | Choose **FALSE** for get messages without repliesChoose **FALSE** for get messages without replies | | Message ID | List message replies | Yes | string | Microsoft Teams channel message ID | | Documents | List of documents to attach to a message | No | List of documents | Microsoft Teams channel message ID | #### Channel methods | Method | Use [protected APIs](https://learn.microsoft.com/en-us/graph/api/resources/teams-api-overview#teams-apis-that-require-rscs-permissions) | Available for [client credentials type authentication](#client-credentials-type-authentication) | Link to method documentation with required permissions and return value | | :---------------------: | :-------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Create channel | false | true | [https://learn.microsoft.com/en-us/graph/api/channel-post](https://learn.microsoft.com/en-us/graph/api/channel-post?view=graph-rest-1.0&tabs=http) | | Get channel | false | true | [https://learn.microsoft.com/en-us/graph/api/channel-get](https://learn.microsoft.com/en-us/graph/api/channel-get?view=graph-rest-1.0&tabs=http) | | List channels | false | true | [https://learn.microsoft.com/en-us/graph/api/channel-list](https://learn.microsoft.com/en-us/graph/api/channel-list?view=graph-rest-1.0&tabs=http) | | Send message to channel | false | false | [https://learn.microsoft.com/en-us/graph/api/channel-post-messages](https://learn.microsoft.com/en-us/graph/api/channel-post-messages?view=graph-rest-1.0&tabs=http) | | Get channel message | true | true | [https://learn.microsoft.com/en-us/graph/api/chatmessage-get](https://learn.microsoft.com/en-us/graph/api/chatmessage-get?view=graph-rest-1.0&tabs=http) | | List channel messages | true | true | [https://learn.microsoft.com/en-us/graph/api/channel-list-messages](https://learn.microsoft.com/en-us/graph/api/channel-list-messages?view=graph-rest-1.0&tabs=http) | | List message replies | true | true | [https://learn.microsoft.com/en-us/graph/api/chatmessage-list-replies](https://learn.microsoft.com/en-us/graph/api/chatmessage-list-replies?view=graph-rest-1.0&tabs=http) | | List members | false | true | [https://learn.microsoft.com/en-us/graph/api/channel-list-members](https://learn.microsoft.com/en-us/graph/api/channel-list-members?view=graph-rest-1.0&tabs=http) | ## Attachments The Microsoft Teams connector supports sending attachments, such as Adaptive Cards, when sending messages to channels or chats. With this feature, you can create rich, interactive messages with structured content. :::note Attachments are only available when the **Content Type** is set to **HTML**. They are not supported with the **Text** body type because Microsoft Teams cannot render attachment tags in plain text messages. When the body type is set to **Text**, the attachments field is hidden and unavailable. ::: ### Attachment fields Each attachment requires the following fields: - `id` (`string`): A unique identifier for the attachment, for example a UUID like `16677edaada34773b62bca2e77ba059b`. This ID is used to reference the card in the message body via `` HTML tags. - `contentType` (`string`): The media type of the attachment. The most common value is `application/vnd.microsoft.card.adaptive` for Adaptive Cards. Other supported Microsoft Bot Framework card content types include `application/vnd.microsoft.card.hero`, `application/vnd.microsoft.card.thumbnail`, and `application/vnd.microsoft.card.receipt`. - `content` (`string`): The JSON payload of the card, provided as a stringified JSON string. For Adaptive Cards, you can [design your card visually](https://adaptivecards.microsoft.com/designer), then copy the resulting JSON and stringify it by escaping quotes and removing newlines before pasting it into this field. If using a FEEL expression, the JSON object can be passed directly. ### Auto-appended attachment tags If you do not manually include `` tags in your HTML message body, the connector automatically appends them for each attachment. If the tags are already present in the body, the connector does not modify anything. ### Example The following example shows how to send a message with an Adaptive Card attachment: **Content (HTML body):** ```html Hello! Here is an important update: ``` **Attachments (FEEL expression):** ```feel = [ { "id": "16677edaada34773b62bca2e77ba059b", "contentType": "application/vnd.microsoft.card.adaptive", "content": "{\"type\":\"AdaptiveCard\",\"$schema\":\"http://adaptivecards.io/schemas/adaptive-card.json\",\"version\":\"1.5\",\"body\":[{\"type\":\"TextBlock\",\"text\":\"Hello from Camunda!\",\"weight\":\"Bolder\",\"size\":\"Large\"},{\"type\":\"TextBlock\",\"text\":\"This is an Adaptive Card sent via the Microsoft Teams connector.\",\"wrap\":true}],\"actions\":[{\"type\":\"Action.OpenUrl\",\"title\":\"Learn More\",\"url\":\"https://camunda.com\"}]}" } ] ``` **Attachments (stringified JSON for direct input):** ```json [ { "id": "16677edaada34773b62bca2e77ba059b", "contentType": "application/vnd.microsoft.card.adaptive", "content": "{\"type\":\"AdaptiveCard\",\"$schema\":\"http://adaptivecards.io/schemas/adaptive-card.json\",\"version\":\"1.5\",\"body\":[{\"type\":\"TextBlock\",\"text\":\"Hello from Camunda!\",\"weight\":\"Bolder\",\"size\":\"Large\"},{\"type\":\"TextBlock\",\"text\":\"This is an Adaptive Card sent via the Microsoft Teams connector.\",\"wrap\":true}],\"actions\":[{\"type\":\"Action.OpenUrl\",\"title\":\"Learn More\",\"url\":\"https://camunda.com\"}]}" } ] ``` :::tip To create an Adaptive Card, use the [Adaptive Cards Designer](https://adaptivecards.microsoft.com/designer) to visually build your card. Once complete, copy the JSON from the **Card Payload Editor** panel, then stringify it by escaping quotes and removing newlines before using it in the `content` field. ::: ## Microsoft Teams connector response The **Microsoft Teams connector** returns the Microsoft Graph API response in `result` wrapper: ```json { "result": { "chatType": "ONE_ON_ONE", "createdDateTime": { "dateTime": { "date": { "year": 2022, "month": 11, "day": 29 }, "time": { "hour": 18, "minute": 10, "second": 33, "nano": 361000000 } }, "offset": { "totalSeconds": 0 } }, "lastUpdatedDateTime": { "dateTime": { "date": { "year": 2022, "month": 11, "day": 29 }, "time": { "hour": 18, "minute": 10, "second": 33, "nano": 361000000 } }, "offset": { "totalSeconds": 0 } }, "tenantId": "0000000-0000-0000-0000-000000000", "webUrl": "https://teams.microsoft.com/l/chat/19%3Aefb08ac3-0000f-0000-0000-example-chat-id_fe35bf61-0000-0000-0000-ddc97d8903d4%40unq.gbl.spaces/0?tenantId=00000-0000-0000-0000-00000000", "id": "19%3Aefb08ac3-0000f-0000-0000-example-chat-id_fe35bf61-0000-0000-0000-ddc97d8903d4%40unq.gbl.spaces" } } ``` See [channel resource type](https://learn.microsoft.com/graph/api/resources/channel?view=graph-rest-1.0) to find the response for the required method for a channel conversation type, or see [chat resource type](https://learn.microsoft.com/graph/api/resources/chat?view=graph-rest-1.0) to find the response for the required method for a chat conversation type. You can use an output mapping to map the response: 1. Use **Result Variable** to store the response in a process variable. For example, `myResultVariable`. 2. Use **Result Expression** to map fields from the response into process variables. For example: ``` = { "chatId": result.id, "tenantId": result.tenantId } ``` --- ## OpenAI connector The **OpenAI connector** is an outbound connector that allows you to use [ChatGPT](https://platform.openai.com/docs/guides/chat/chat-completions-beta) or [Moderation API](https://platform.openai.com/docs/guides/moderation/moderation) in your BPMN process. ## Prerequisites To use the **OpenAI connector**, create an OpenAI account and create an API key. Refer to the [OpenAI Platform](https://platform.openai.com/docs/quickstart) documentation for a detailed setup guide. :::note Use Camunda secrets to avoid exposing your sensitive data, such as your OpenAI API key, as plain text. Follow our documentation on [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ::: ## Create an OpenAI connector task ## Make your OpenAI connector executable To work with the **OpenAI connector**, fill all mandatory fields. ## Authentication To use the **OpenAI connector**, obtain an API key from OpenAI. To create an OpenAI account and learn more about API keys, visit the [OpenAI Platform](https://platform.openai.com/) documentation. ### Create a new connector secret Keep your **API key** safe and avoid exposing it in the BPMN `xml` file by creating a secret: 1. Follow our [guide for creating secrets](/components/hub/organization/manage-clusters/manage-secrets.md). 2. Name your secret (i.e `OPENAI_API_KEY`) so you can reference it later in the connector. ### Configure the API key Select the **OpenAI API key** field in the **Authentication** section and set it to the secret you created (e.g. `{{secrets.OPENAI_API_TOKEN}}`). ## Operations The **OpenAI connector** currently supports two operation types in the **Operation** dropdown list: **Chat** and **Moderation**. ## Chat With the **Chat** operation, you can interact with OpenAI chat-based language models. ### Model The **Model** dropdown list allows you to select the model. Refer to the [Models](https://platform.openai.com/docs/models) section of OpenAI documentation for detailed information about models. #### Custom model version The **Model** dropdown list does not contain all available models. To use a model that is not listed, use the **Custom** option and provide the model name in the **Custom model version** field that appears. :::note Selection of models is user-specific and depends on your account privileges. For this reason, GPT-4 may appear as non-existing when you attempt to use it, although it is defined in the element template. ::: ### Temperature The **Temperature** field controls the randomness of the model's output. Lower temperatures make the model more deterministic and less random, while higher temperatures make the model more random. Accepted values are between `0` and `2` (add a leading `0` for values less than `1`), and you can use two digits after the decimal point. ### System message The **System message** field allows you to provide initial instructions for the model, and helps set the behavior of the assistant. For example, if you want ChatGPT to translate the prompt into a different language instead of interpreting the questions contained in the prompt, you can set the **System message** to `You are a translator bot. You provide literal translation of inputs from English into German. You do not interpret the input.` ### Chat history OpenAI API doesn't store message history for ChatGPT. Therefore, it is up to you as a process developer to decide if and how you should retain the chat history. The **Chat history** input field may contain the history of previous messages or examples of the desired behavior. Following the translation example above, you can provide some translation examples to make the expectations clearer. Chat history consumed by this connector follows the chat format described in the corresponding part of [OpenAI documentation](https://platform.openai.com/docs/guides/chat/introduction). ### Prompt While **System message** and **Chat history** fields are optional and provide the model with additional context, **Prompt** is the actual input. This is the query that is used to trigger the model output. To use the **System message**, **Chat history**, and **Prompt** together, you would follow this format: The example below illustrates how you can use **System message**, **Chat history**, and **Prompt** together. **System message** ```text You are a helpful assistant. ``` **Chat history** ``` = [ {"role": "user", "content": "Who won the world series in 2020?"}, {"role": "assistant", "content": "The Los Angeles Dodgers the World series in 2020."} ] ``` **Prompt** ```text Where was it played? ``` In this example, the chat history provides the context of a user asking who won the World Series in 2020, and the assistant providing the correct answer that the Los Angeles Dodgers won. The prompt, "Where was it played?" is the follow-up question that seeks additional information about the location where the World Series took place in 2020. :::note Find more complex examples of prompt engineering and sample real-life use cases of ChatGPT on the OpenAI [examples](https://platform.openai.com/examples) page. ::: ### Choices to generate The numeric **Choices to generate** field determines how many alternative answers the model returns in the API response. ### Sample chat output You can use an output mapping to map the response: 1. Use **Result Variable** to store the response in a process variable. For example, `myResultVariable`. 2. Use **Result Expression** to map fields from the response into process variables. ``` { "status": 200, "headers": { # response headers }, "body": { "id": "chatcmpl-6ws27w7nADFLWp7KD3dhjiUmP0kfu", "object": "chat.completion", "created": 1679488747, "model": "gpt-3.5-turbo-0301", "usage": { "prompt_tokens": 16, "completion_tokens": 79, "total_tokens": 95 }, "choices": [ { "message": { "role": "assistant", "content": "ChatGPT has gained significant attention in recent years, especially with the development and advancement of Natural Language Processing (NLP) tools used in chatbots and virtual assistants." }, "finish_reason": "stop", "index": 0 } ] } } ``` ## Moderation It is recommended to use the Moderation API to sanitize inputs and outputs of the language model. You will be able to prevent violation of OpenAI policies and displaying the potentially unsafe content in your system. ### Evaluation input ### Sample moderation output Output contains the evaluation result broken down by violation categories. To learn more about Moderation output, visit the [OpenAI documentation](https://platform.openai.com/docs/guides/moderation/moderation). ``` { "status": 200, "headers": { # response headers }, "body": { "id": "modr-6wtH8E1f2W533qdQAzq8dUpmKRVCV", "model": "text-moderation-004", "results": [ { "flagged": false, "categories": { "sexual": false, "hate": false, "violence": false, "self-harm": false, "sexual/minors": false, "hate/threatening": false, "violence/graphic": false }, "category_scores": { "sexual": 1.0084246241603978E-5, "hate": 5.5422344303224236E-5, "violence": 8.184280159184709E-5, "self-harm": 1.3117542607687938E-7, "sexual/minors": 4.457491709075612E-9, "hate/threatening": 9.144552337581047E-10, "violence/graphic": 1.770446012017146E-8 } } ] } } ``` --- ## RabbitMQ connector The **RabbitMQ connector** is an outbound connector that allows you to connect your BPMN process with [RabbitMQ](https://www.rabbitmq.com/) to send messages to RabbitMQ. ## Prerequisites To use the **RabbitMQ connector**, you need to have installed a RabbitMQ server and create the relevant [credentials](https://www.rabbitmq.com/passwords.html). Use Camunda secrets to store credentials, so that you don't expose sensitive information directly from the process. See [this appendix entry](#how-do-i-store-secrets-for-my-connector) to learn more. :::note Ensure you enter the correct exchange name and routing key, as the **RabbitMQ connector** can't throw an exception if they are incorrect. ::: ## Create a RabbitMQ connector task ## Connecting to RabbitMQ and sending messages To connect to RabbitMQ, choose the required connection type in the **Authentication** section and complete the mandatory fields highlighted in red in the connector properties panel on the right side of the screen. :::note All the mandatory and non-mandatory fields depending on the authentication selection you choose are covered in the upcoming sections. ::: ## Authentication You can choose among the available RabbitMQ connectors according to your authentication requirements. First, you must have a user in your RabbitMQ instance with the necessary permissions. See more at the [RabbitMQ access control specification](https://www.rabbitmq.com/access-control.html). Next, we will choose the type of connection. ### URI type connection For a URI connection, take the following steps: 1. Click the **URI** connection type in the **Authentication** section 2. Set **URI** to `URI`. It must contain RabbitMQ username, password, host name, port number, and virtual host. For example, `amqp://userName:password@serverHost:port/virtualHost`; follow the [RabbitMQ URI specification](https://www.rabbitmq.com/uri-spec.html) to learn more. ### Credentials type connection To connect with credentials, take the following steps: 1. Click the **Username/Password** connection type in the **Authentication** section 2. Set the **Password** to `Password`. ## Routing data In the **Routing** section, you must set the routing data attributes: - For a **URI** type connection, the required fields are `exchange` and `routingKey`. - For a **Credentials** type connection, the required fields are `exchange`, `routingKey`, `virtualHost`, `hostName`, and `port`. Refer to the RabbitMQ documentation to learn about routing attributes: - [Exchanges, routing keys, and bindings](https://www.cloudamqp.com/blog/part4-rabbitmq-for-beginners-exchanges-routing-keys-bindings.html) - [Virtual hosts](https://www.rabbitmq.com/vhosts.html) - [Networking, host, and port configuration](https://www.rabbitmq.com/networking.html) ## Message 1. In the **Message** section, insert the message payload. The message can be Text or JSON format. 2. (Optional) In the **Properties** section, insert the message properties in JSON or as a [FEEL](/components/modeler/feel/what-is-feel.md) expression. Go to [RabbitMQ documentation](https://www.rabbitmq.com/publishers.html#message-properties) for learn more about RabbitMQ message properties. example of message : ``` = {"myMessageKey":"Hello Camunda Team"} ``` example of properties: ``` = { "contentEncoding":"UTF-8", "contentType":"text/plain" } ``` ## RabbitMQ connector response The **RabbitMQ connector** returns the `Success` result. The response contains a `messageId` variable. You can use an output mapping to map the response: 1. Use **Result Variable** to store the response in a process variable. For example, `myResultVariable`. 2. Use **Result Expression** to map specific fields from the response into process variables using [FEEL](/components/modeler/feel/what-is-feel.md). For example: ``` = { "myResultVariable": response.statusResult } ``` ## Appendix & FAQ ### How do I store secrets for my connector? Use Camunda secrets to avoid exposing your credentials. Follow our documentation on [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. The **RabbitMQ connector** is an inbound connector that allows you to connect your BPMN process with [RabbitMQ](https://www.rabbitmq.com/) to receive messages from RabbitMQ. ## Prerequisites To use the **RabbitMQ connector**, you need to have installed a RabbitMQ server and create the relevant [credentials](https://www.rabbitmq.com/passwords.html). Using Camunda secrets to store credentials is recommended so you do not expose sensitive information directly from the process. See [this appendix entry](#how-do-i-store-secrets-for-my-connector) to learn more. ## Create a RabbitMQ connector event See [create a RabbitMQ connector task](#create-a-rabbitmq-connector-task) for additional details. 1. Add a **Start Event** or an **Intermediate Event** to your BPMN diagram to get started. 2. Change its template to a RabbitMQ connector. 3. Fill in all required properties. 4. Complete your BPMN diagram. 5. Deploy the diagram to activate the RabbitMQ consumer. ## Connecting to RabbitMQ and receiving messages ### Authentication You can choose among the available authentication types according to your requirements. First, you must have a user in your RabbitMQ instance with the necessary permissions. See more at the [RabbitMQ access control specification](https://www.rabbitmq.com/access-control.html). Next, we will choose the type of connection. #### URI type connection For a URI connection, take the following steps: 1. Click the **URI** connection type in the **Authentication** section. 2. Set **URI** to `URI`. It must contain RabbitMQ username, password, host name, port number, and virtual host. For example, `amqp://userName:password@serverHost:port/virtualHost`; follow the [RabbitMQ URI specification](https://www.rabbitmq.com/uri-spec.html) to learn more. #### Credentials type connection To connect with credentials, take the following steps: 1. Click the **Username/Password** connection type in the **Authentication** section 2. Set the **Password** to `Password`. ### Routing data - For a **Credentials** type connection, you are required to fill in the `virtualHost`, `hostName`, and `port` fields. - For a **URI** type connection, these values are already included in the URI, so you don't need to fill them in. ### Subscription properties The **Subscription** section allows you to configure the subscription to the RabbitMQ queue. - **Queue name** is a mandatory field that specifies the name of the queue to subscribe to. - **Consumer tag** is an optional field that specifies the consumer tag to use for the consumer. If not set, the server will generate one. - **Exclusive consumer** is an optional dropdown field that specifies whether the consumer is exclusive. Exclusivity allows you to ensure only one consumer at a time consumes from the queue. - **Arguments** is an optional FEEL expression field that specifies the arguments for the queue. The expression must be a [FEEL context expression](/components/modeler/feel/language-guide/feel-context-expressions.md). For example, `={x-message-ttl: 60000}`. See more at the [RabbitMQ queue arguments specification](https://www.rabbitmq.com/queues.html#optional-arguments). :::note When configuring the **Arguments** field, remember that inbound connectors are executed outside the BPMN process context and are not tied to a specific process instance. Therefore, you cannot use process variables in the **Arguments** context expression. However, you can refer to connector secrets using placeholder syntax. For example, `= {x-consumer-timeout: "{{secrets.CONSUMER_TIMEOUT}}"}`. ::: ### Activation condition **Activation condition** is an optional FEEL expression field that allows for fine-tuning of the connector activation. For example, given a RabbitMQ message contains the payload `{"role": "USER", "action": "LOGIN""}`, the **Activation Condition** value might look like `=(message.body.role="USER")`. This way, the connector is triggered only if the message body contains the `role` field with the value `USER`. Leave this field empty to trigger your connector for every incoming message. By default, messages with unmatched activation conditions are rejected without re-queuing. You can set up a dead-letter queue in RabbitMQ to handle these messages. Learn more about dead-letter queues in the [RabbitMQ documentation](https://www.rabbitmq.com/dlx.html). You can also configure the RabbitMQ inbound connector to acknowledge messages that don't match the activation condition. In this case, the message will not end up in the dead-letter queue, but will be acknowledged and removed from the queue. To acknowledge messages that don't match the activation condition, check the **Consume unmatched events** checkbox. | **Consume unmatched events** checkbox | Activation condition | Outcome | | ------------------------------------- | -------------------- | -------------------------------------------------- | | Checked | Matched | Message is acknowledged and removed from the queue | | Unchecked | Matched | Message is acknowledged and removed from the queue | | Checked | Unmatched | Message is acknowledged and removed from the queue | | Unchecked | Unmatched | Message is rejected and re-queued | ### Correlation The **Correlation** section allows you to configure the message correlation parameters. :::note The **Correlation** section is not applicable for the plain **start event** element template of the RabbitMQ connector. Plain **start events** are triggered by process instance creation and do not rely on message correlation. ::: #### Correlation key - **Correlation key (process)** is a FEEL expression that defines the correlation key for the subscription. This corresponds to the **Correlation key** property of a regular **Message Intermediate Catch Event**. - **Correlation key (payload)** is a FEEL expression used to extract the correlation key from the incoming message. This expression is evaluated in the connector Runtime and the result is used to correlate the message. For example, given that your correlation key is defined with `myCorrelationKey` process variable, and the incoming RabbitMQ message contains `message:{body:{correlationKey:myValue}}`, your correlation key settings will look like this: - **Correlation key (process)**: `=myCorrelationKey` - **Correlation key (payload)**: `=message.body.correlationKey` Learn more about correlation keys in the [messages guide](../../../concepts/messages). #### Message ID expression The **Message ID expression** is an optional field that allows you to extract the message ID from the incoming message. The message ID serves as a unique identifier for the message and is used for message correlation. This expression is evaluated in the connector Runtime and the result is used to correlate the message. In most cases, it is not necessary to configure the **Message ID expression**. However, it is useful if you want to ensure message deduplication or achieve a certain message correlation behavior. Learn more about how message IDs influence message correlation in the [messages guide](../../../concepts/messages#message-correlation-overview). For example, to set the message ID to the value of the `transactionId` field in the incoming message, configure the **Message ID expression** as follows: ``` = message.body.transactionId ``` #### Message TTL The **Message TTL** is an optional field that allows you to set the time-to-live (TTL) for the correlated messages. TTL defines the time for which the message is buffered in Zeebe before being correlated to the process instance (if it can't be correlated immediately). The value is specified as an ISO 8601 duration. For example, `PT1H` sets the TTL to one hour. Learn more about the TTL concept in Zeebe in the [message correlation guide](../../../concepts/messages#message-buffering). ### Deduplication The **Deduplication** section allows you to configure the connector deduplication parameters. Not to be confused with **message deduplication**, **Connector deduplication** is a mechanism in the connector Runtime that determines how many RabbitMQ subscriptions are created if there are multiple occurrences of the **RabbitMQ Consumer connector** in the BPMN diagram. By default, the connector runtime deduplicates connectors based on properties, so elements with the same subscription properties only result in one subscription. For details, see [Inbound connector deduplication](../advanced-topics/deduplication.md). To customize the deduplication behavior, check the **Manual mode** checkbox and configure the custom deduplication ID. ### Output mapping The **Output mapping** section allows you to configure the mapping of the RabbitMQ message to the process variables. - Use **Result variable** to store the response in a process variable. For example, `myResultVariable`. - Use **Result expression** to map specific fields from the response into process variables using [FEEL](/components/modeler/feel/what-is-feel.md). For example, given the RabbitMQ connector is triggered with the message body `{"role": "USER", "action": "LOGIN""}` and you would like to extract the pull request `role` as a process variable `messageRole`, the **Result Expression** might look like this: ``` = { "messageRole": message.body.role } ``` ## Appendix & FAQ ### How do I store secrets for my connector? Use Camunda secrets to avoid exposing your credentials. Follow our documentation on [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ### What is the output format of the RabbitMQ connector? The RabbitMQ connector returns the following output that can be used in the next steps of your process, including result expressions: ``` { "message": { "consumerTag": "myConsumerTag", "body": { {{ the message body }} }, "properties": { "contentType": "application/json", "contentEncoding": "UTF-8", "headers": { "x-first": "1", "x-second": "2" }, "deliveryMode": 2, "priority": 0, "correlationId": "myCorrelationId", "replyTo": "myReplyTo", "expiration": "myExpiration", "messageId": "myMessageId", "timestamp": "myTimestamp", "type": "myType", "userId": "myUserId", "appId": "myAppId", "clusterId": "myClusterId" } } } ``` :::note The output payload contains a top-level `message` object that contains `consumerTag`, `body`, and `properties` fields. ::: ### How is message body deserialized? The RabbitMQ Consumer connector always tries to deserialize the message body as JSON. If the deserialization fails, the connector will return the message body as a string. However, if the body only contains a primitive value, such as a string, a number, or a boolean, the connector will return the primitive value itself. ### When is the message acknowledged? What happens if the connector execution fails? The following outcomes are possible: - If connector execution is successful and **Activation condition** was met, the message is acknowledged and removed from the queue. - If **Activation condition** was not met, the message is rejected and removed from the queue. - If connector execution fails due to an unexpected error (e.g. Zeebe is unavailable), the message is rejected and re-queued. ### What lifecycle does the RabbitMQ Consumer connector have? The RabbitMQ Subscription connector is a long-running connector that is activated when the process is deployed and deactivated when the process is un-deployed or overwritten by a new version. --- ## Salesforce connector The **Salesforce connector** is an outbound protocol connector that allows you to connect your BPMN service with [Salesforce](https://salesforce.com/) to interact with the [Salesforce APIs](https://developer.salesforce.com/docs/apis). ## Prerequisites To use the **Salesforce connector**, you must have a [Salesforce Connected App with OAuth 2.0 Client Credentials Flow](https://help.salesforce.com/s/articleView?id=sf.connected_app_client_credentials_setup.htm&type=5). :::note Use Camunda secrets to avoid exposing your _Salesforce Connected App_ client ID and client secret as plain text. Learn more in our documentation on [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md). ::: ## Create a Salesforce connector task ## Instance Each operation requires information about the **Salesforce Base URL**. Example: `https://MyDomainName.my.salesforce.com` The Salesforce API version should be the one you want to use. You can search for this information [in your Salesforce API](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_versions.htm). ## Authentication In the **Authentication** section, select **Bearer Token** to provide a static access token or **OAuth 2.0** to configure client credentials. :::note While the static access token is useful for getting started, it is recommended to provide the **OAuth 2.0** client credentials. ::: ## Operation ### Operation types Currently, this connector supports two types of operation: - [SOQL Query](https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql.htm) - [sObject records](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_list.htm) ### SOQL Query The **SOQL Query** only requires the query itself as input. A query is useful for receiving data based on a structured query language. Take a closer look at some available [examples](https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select_examples.htm). The response body looks like the following: ```json { "totalSize": 1, "done": true, "records": [ { "attributes": { "type": "", "url": "/services/data//sobjects//" }, "": "", "...": "..." } ] } ``` ### sObject records **sObject records** support **Create record**, **Get record**, **Update record**, and **Delete record**. :::note Every operation explanation contains a link to the Salesforce API docs which will explain the request and provide an example. ::: #### Create record - **Salesforce object:** The Salesforce object to create, e.g. _Account_. - **Record fields:** Field values for the Salesforce object to create, e.g. `{ Name: "Express Logistics and Transport" }`. Review an example including the response body format in the [Salesforce documentation](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_sobject_create.htm). #### Get record - **Salesforce object:** The Salesforce object to create, e.g. _Account_. - **Salesforce object ID:** Identifier of the Salesforce object, e.g. _001R0000005hDFYIA2_. - **Relationship field name _(optional)_:** Name of the field that contains the relationship, e.g. _Opportunities_. - **Query Parameters _(optional)_:** Additional query parameters that can be provided along with the request, e.g. `{ fields: "AccountNumber,BillingPostalCode" }`. When omitting the **Relationship field name**, a [get request for a record](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_retrieve_get.htm) is performed. Otherwise, a [get request for records using sObject relationships](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_relationships_get.htm) is performed. In the documentation linked above, you can find the possible use case for **Query parameters**; for example, [filtering fields](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_get_field_values.htm). The response body will contain the requested object as the root object: ```json { "attributes": { "type": "", "url": "/services/data//sobjects//" }, "": "", "...": "..." } ``` Find another example [here](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_get_field_values.htm). #### Update object - **Salesforce object:** The Salesforce object to create, e.g. _Account_. - **Salesforce object ID:** Identifier of the Salesforce object, e.g. _001R0000005hDFYIA2_. - **Record fields:** Field values for the Salesforce object to update, e.g. `{ BillingCity : "San Francisco" }`. [These update the record](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_retrieve_patch.htm) using the given fields. As an update does not return a body, you will not be able to map any data from the response back to the process. #### Delete object - **Salesforce object:** The Salesforce object to create, e.g. _Account_. - **Salesforce object ID:** Identifier of the Salesforce object, e.g. _001R0000005hDFYIA2_. [These delete the record](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_retrieve_delete.htm). As a delete does not return a body, you will not be able to map any data from the response back to the process. ## Handle connector response The **Salesforce connector** is a protocol connector, meaning it is built on top of the **HTTP REST connector**. Therefore, handling response is still applicable [as described](/components/connectors/protocol/rest.md#response). --- ## SendGrid connector The **SendGrid connector** is an outbound connector that allows you to quickly send emails from your BPMN processes. ## Prerequisites To use the SendGrid connector, a SendGrid API key is needed. Follow [these steps](#appendix) if you do not have a SendGrid account or API key [secret configured](#create-a-new-connector-secret) in your cluster. ## SendGrid connector The SendGrid connector comes with two options: 1. **SendGrid Email connector** allows sending simple emails (i.e. text/plain, text/html). 2. **SendGrid Email Template connector** supports [SendGrid Dynamic Templates](https://sendgrid.com/solutions/email-api/dynamic-email-templates/). ### SendGrid Email connector #### Create a SendGrid Email connector Task #### Make your SendGrid Email connector executable To make the **SendGrid Email connector** executable, you need to fill out all the mandatory fields highlighted in red in the properties panel on the right side of the screen: 1. Set **SendGrid API Key** to `{{secrets.SEND_GRID_API_KEY}}`. 2. Set **Sender Name** to `Jane Doe` (or the [sender identity](#create-a-sender-identity) you configured above). 3. Set **Sender Email** to `jane-doe@camunda.com` (or the [sender identity](#create-a-sender-identity) you configured above). 4. Set **Receiver Name** to `Your Name`. 5. Set **Receiver Email** to `Your email address`. 6. Set **Email Content Subject**. 7. Leave **Content Type** to **text/plain** (or alternatively to **text/html** if you intend to provide an HTML body to your email). 8. Provide a text (or HTML) **Body** for your email. 9. **Attachments** is a list of camunda documents to include as part of your **new email**. - To work with documents you must upload them first, [using the Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/specifications/create-document.api.mdx) for example. - The result of the endpoint must then be assigned to a variable in **Start Process Instance** so you can use the list of these variables in the **Attachments** field. :::note Starting from version 8.7.0, the SendGrid connector provides attachment support. See additional details and limitations in [document handling](/components/document-handling/getting-started.md). ::: ### SendGrid Email Template connector Send an email via SendGrid Dynamic Template and use the [Handlebars templating language](https://handlebarsjs.com/) to pass dynamic values to your connector. #### Configure a Dynamic Template 1. Open the [Dynamic Transactional Templates page](https://sendgrid.com/dynamic_templates) and click **Create Template**. 2. Add a unique template name and click **Save**. 3. To begin editing your new template, click **Add Version**. 4. Select an editor and click **Continue**. 5. Design your template. Find more information on using Handlebars [here](https://docs.sendgrid.com/for-developers/sending-email/using-handlebars). In our example template, we will use the following subject and body: ```text Subject: Your Camunda Weather Report for {{location}} ``` ```text Body: Hi {{name}}, Thanks for using Camunda connectors to check your current weather report. Your current weather in Berlin is {{weather}} with {{actual-temp}}°C and feels like {{feel-temp}}°C The Camunda Team ``` In our example template, we will use the following Handlebars: `{{name}}` - The name of the user requesting the weather report `{{location}}` - The location used for the weather report `{{weather}}` - The current weather condition `{{actual-temp}}` - The measured temperature `{{feel-temp}}` - How the temperature feels like in reality While you are editing your template, you can test how your email would look by switching to **Preview** mode, choosing **{} Show Test Data**, and then providing the necessary data. #### Create a SendGrid Email template connector task See [create a SendGrid email connector task](#create-a-sendgrid-email-connector-task) for additional details. #### Make your SendGrid Email Template connector executable To make the **SendGrid Email Template connector** executable, fill out all the mandatory fields highlighted in red in the properties panel: 1. Set **SendGrid API Key** to `{{secrets.SEND_GRID_API_KEY}}`. 2. Set **Sender Name** to `Jane Doe` (or the [sender identity](#create-a-sender-identity) you configured above). 3. Set **Sender Email** to `jane-doe@camunda.com` (or the [sender identity](#create-a-sender-identity) you configured above). 4. Set **Receiver Name** to `Your Name`. 5. Set **Receiver Email** to `Your email address`. 6. Log in to your SendGrid account and navigate to [the Dynamic Template you created](#configure-a-dynamic-template). 7. Copy the ID of the template and paste it in the **Template ID field**. 8. Provide the test data in the **Template Data** field as a [FEEL context expression](/components/modeler/feel/language-guide/feel-context-expressions.md): 9. **Attachments** is a list of camunda documents to include as part of your **new email**. - To work with documents you must upload them first, [using the Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/specifications/create-document.api.mdx) for example. - The result of the endpoint must then be assigned to a variable in **Start Process Instance** so you can use the list of these variables in the **Attachments** field. ```text = { name: "Jane", location: "Berlin", weather: "Clear", actual-temp: 30, feel-temp: 3 } ``` If you want to provide dynamic content in the email via process variables, you can set them in the **Template Data** field as well: ```text = { name: nameVariable, location: locationVariable, weather: weatherVariable, actual-temp: temerature, feel-temp: windChill } ``` ## Appendix ### Create a SendGrid account To use the **SendGrid connector**, create a free account in SendGrid if you do not have one yet: 1. Go to [https://signup.sendgrid.com/](https://signup.sendgrid.com/). 2. Set up the account with your email and choose a password. 3. Click **Create Account**. 4. Provide further information required by SendGrid. 5. Click **Get Started**. ### Create a sender identity Before sending your first email, you'll need to create a sender identity and verify it. 1. Click **Settings > Sender Authentication** or click [here](https://app.sendgrid.com/settings/sender_auth). 2. Choose **Verify a Single Sender** for demo purposes (or alternatively **Authenticate Your Domain** for a production setup.) 3. Provide the details requested by SendGrid in the form and click **Create**. 4. Go to your email inbox and open the email sent to you by SendGrid. 5. Click **Verify Single Sender**. ### Create an API key To create an API key in SendGrid, take the following steps: 1. Log in to your new account. 2. Go to **Settings**. 3. Click **API Keys > Create API Key**. 4. Give your key a name (i.e. `my-camunda-connector-key`). 5. Click **Create Key**. 6. Copy the **API Key** and move on to the next step for creating a connector secret. ### Create a new connector secret We advise you to keep your API key safe and avoid exposing it in the BPMN `xml` file by creating a secret: 1. Follow our [guide for creating secrets](/components/hub/organization/manage-clusters/manage-secrets.md). 2. Name your secret `SEND_GRID_API_KEY` so you can reference it later in the connector. --- ## Slack connector The **Slack connector** is an outbound connector that allows you to send messages to channels or users in your [Slack](https://slack.com) workspace from your BPMN process. ## Prerequisites To use the Slack connector, a Slack app must be registered with the Slack workspace you would like to send messages to. A respective OAuth token needs to be configured as a secret in your cluster. Follow [these steps in the appendix](#appendix) to learn how to set this up. ## Create a Slack connector task ## Make your Slack connector executable To make the **Slack connector** executable, fill out the mandatory fields highlighted in red in the properties panel on the right side of the screen. ### Authentication Set **Authentication** to your Slack OAuth token, which is stored as a secret. For example, `{{secrets.SLACK_OAUTH_TOKEN}}`. ### Create channel :::info This API uses the Slack [`conversations.create`](https://api.slack.com/methods/conversations.create) method. You need to ensure that your Slack application has related permissions enabled. ::: To create a channel, take the following steps: 1. Set **Method** to `Create Channel`. 2. Set the **New Channel Name**: - The channel name can be up to 80 characters, and can contain lowercase letters, digits, and symbols `-` and `_`. - This can be provided as a FEEL expression. 3. Set channel **Visibility** as required: - **Public** channels are visible to every workspace member. - **Private** channels are visible to explicitly invited people only. ### Invite user to channel :::info This API uses the Slack [`conversations.invite`](https://api.slack.com/methods/conversations.invite) method. You need to ensure that your Slack application has related permissions enabled. ::: To invite users to a channel, take the following steps: 1. Set **Method** to `Invite to Channel`. 2. Set the `Invite by` method: - Invite by **Channel Name**: - The channel name can be up to 80 characters, and can contain lowercase letters, digits, and symbols `-` and `_`. - This can be provided as a FEEL expression. - Invite by **Channel ID**: - The channel ID must be a valid Slack Channel ID. - This can be provided as a FEEL expression. 3. Set the **Users** as required: 1. One single username or email or ID (for example: `@myUser` or `my.user@company.com` or `ABCDEF12345`). 2. A comma separated list of users (for example: `@myUser, my.user@company.com, ABCDEF12345`). 3. FEEL expression. In this case you can provide a valid list of strings (for example: `["@myUser", "my.user@company.com", "ABCDEF12345"]`). - Formats: - If a username starts with an `@` symbol, it will be handled as user name. - If a username is in an email format, it will be handled as an email. - If a username doesn't start with an `@`, and isn't an email, it will be handled as a user ID. - If a null input or an input which is not a type of String or a Collection provided, you will get an Exception. - If all username is provided as any other type than a String, you will get an Exception. - If one of the usernames is provided as any other type than a String, it will be omitted. - If you provide a channel name it will be omitted since it is not possible to invite a channel to another channel. ### Post message :::info This API uses the Slack [`chat.postMessage`](https://api.slack.com/methods/chat.postMessage) method. You need to ensure that your Slack application has related permissions enabled. ::: To post a message, take the following steps: 1. Set **Method** to `Post Message`. 2. Set **Channel/User Name** to either the **channel** or **user** you want to send the message to. 1. A **channel** is specified by a unique identifier starting with a `#` (for example, `#myChannel`) or by using the channel id. 2. A **user** is specified by a username starting with an `@` symbol (for example, `@myUser`). 3. (Optional) A **thread** can be specified to start a thread from a specific message. For example, `ts` in the response can be used (see [here](#post-message)). If the message has been posted by a user, we currently have no way to retrieve the `ts` value. Visit the [Slack documentation](https://api.slack.com/methods/chat.postMessage) for additional details. 4. Select a **Message type**. 1. When **Plain text** is selected, set **Message** to the message string you would like to send (for example, `Hello World!`). 2. When **Message block** is selected, set **Message block** to a formatted rich text block format. Learn more about rich text message block format in the [official Slack documentation](https://api.slack.com/reference/surfaces/formatting#stack_of_blocks). 5. (Optional) **Attachments** are an array of variables to which **Camunda documents** are assigned. To work with attachments you must add `files:read` and `files:write` permissions for your **Bot Token Scopes** in your Slack app. :::note To work with documents you must upload them first, using the [REST API](/apis-tools/orchestration-cluster-api-rest/specifications/create-document.api.mdx) for example. The result of the endpoint must then be assigned to a variable in **Start Process Instance** so you can use the variable in the **Attachments** field. ::: :::note Starting from version 8.7.0, the Slack connector works with document handling to support adding attachments and increasing template versions. See additional details and limitations in [document handling](/components/document-handling/getting-started.md). ::: The **Channel/User Name** and **Message** can either be given [static values](/components/concepts/expressions.md#expressions-vs-static-values), or FEEL expressions. FEEL expressions can be used to [access process variables or dynamically create values](/components/concepts/expressions.md). This can be handy if a process variable is used to store the relevant channel or if the message needs to be composed dynamically, for example: `Channel/User Name` property might look like: ``` #slack-connectors ``` `Message` property: ``` = "Order-" + orderId + " was dispatched" ``` In the above example, the Channel/User Name is set to the [static value](/components/concepts/expressions.md#expressions-vs-static-values) "#slack-connectors," which will post the message to the specified Slack channel. The **Message** property uses a FEEL expression to dynamically create the message content. It concatenates the string "Order-" with the value stored in the process variable orderId and adds "was dispatched" to complete the message. This way, the message will vary based on the specific orderId stored during the process execution. :::note Slack's [guidance on formatting](https://api.slack.com/reference/surfaces/formatting#basics) can assist in formatting messages. ::: ## Slack API response The **Slack connector** exposes the Slack API response as a [local variable](/components/concepts/variables.md#variable-scopes) called `response`. Response contents are method-specific. ### Create channel The following fields are available in the `response` variable after executing **Create Channel** method: - **channel**: - **id**: channel ID - **name**: channel name Notice that the **name** field can be subsequently used as an argument of **Post Message** method. ### Post message The following fields are available in the `response` variable after executing the **Post Message** method. Notice that all fields describe state in the Slack workspace: - **ts**: timestamp ID - **channel**: channel ID - **message**: - **type**: message type - **team**: team ID - **user**: user ID - **text**: message text - **ts**: timestamp ID - **appID**: Slack App ID - **botID**: Slack Bot ID ### Output mapping You can use an Output Mapping to map the response: 1. Use **Result Variable** to store the response in a process variable. For example, `myResultVariable`. 2. Use **Result Expression** to map fields from the response into process variables. For example: ``` = { messageText: response.message.text } ``` ## Appendix To use the **Slack connector**, the following prerequisites need to be set up. 1. [Slack workspace](#use-a-well-known-slack-workspace) - The workspace the **Slack connector** will communicate with. 2. [Slack basic app with bot token configured](#configure-a-basic-slack-app) - The **Slack connector** will communicate through this Slack app with the workspace. You can consider the Slack app as _Slack bot representing the Camunda platform_. 3. [Slack bot token stored as secret](#store-slack-bot-token-as-secret) - The secret will store the Slack bot token and can be used to reference it from BPMN processes without revealing it in the BPMN `xml`. ### Use a well-known Slack workspace A Slack workspace consists of channels in which workspace members can communicate and collaborate. A workspace is identified by a unique name, for example `https://myWorkspace.slack.com/`. In most cases you will know which workspace you want to connect with already. If you want to set up a new workspace, refer to the [official Slack documentation](https://slack.com/help/articles/115001344007-Create-a-workspace-on-Enterprise-Grid). ### Configure a basic Slack app :::caution You can only install a Slack app to a workspace in which you are a member or that you own. It is not possible if you have guest-only permissions. See the [guide to apps in Slack](https://slack.com/help/articles/360001537467-Guide-to-apps-in-Slack) for more details. ::: The **Slack connector** communicates through a Slack app with a concrete Slack workspace. For example, when sending a Slack message, the message will be posted by the Slack app. For the **Slack connector** to work, you need to perform the following steps: 1. [Create a Slack app](https://api.slack.com/apps). 2. [Request required scopes](https://api.slack.com/scopes) - The scopes represent what your app can and cannot do (for example, posting messages). 1. For the **Create Channel** method to work, you need to grant at least the [`channels:manage`](https://api.slack.com/scopes/channels:manage) scope. 2. For the **Post Message** method to work, you need to grant at least the [`chat:write`](https://api.slack.com/scopes/chat:write) scope. 3. [Install the Slack app to your workspace](https://api.slack.com/authentication/basics#installing). 4. [Invite the Slack app to your workspace via /invite](https://slack.com/help/articles/201259356-Slash-commands-in-Slack#h_01EPZ2Z81EJ67RA2BGDKZ9M1AN). Once the app is set up, copy the [bot token](https://api.slack.com/authentication/token-types) of the app. It is represented as a string and begins with `xoxb-`. This is the OAuth Bearer token, which the **Slack connector** will use to authenticate with the Slack API. ### Store Slack bot token as secret The **Slack connector** uses an OAuth bearer token (for example, the Slack app bot token) to authenticate with the Slack API. We advise you to keep your Slack bot token safe and avoid exposing it in the BPMN `xml` file by creating a secret: 1. Follow our [guide for creating secrets](/components/hub/organization/manage-clusters/manage-secrets.md). 2. Name your secret `SLACK_OAUTH_TOKEN` so you can reference it later in the connector. The **Slack inbound connector** is a connector that allows you to start or continue a BPMN process triggered by a [Slack](https://slack.com/) message. ## Create a Slack inbound connector task 1. Start building your BPMN diagram. You can use the **Slack inbound connector** with either a **Start Event** or **Intermediate Catch Event**. 2. Select the applicable element and change its template to a **Slack Inbound connector**. 3. Fill in all required properties. 4. Complete your BPMN diagram. 5. Deploy the diagram to activate the webhook. 6. Navigate to the **Webhooks** tab in the properties panel to see the webhook URL. ## Make your Slack inbound connector for receiving event notifications executable 1. In the **Webhook Configuration** section, configure the **Webhook ID**. By default, **Webhook ID** is pre-filled with a random value. This value will be a part of the Slack event subscription or slash command URL. 2. In the **Webhook Configuration** section, configure the **Slack signing secret**. This value is unique to your Slack application and used to validate a Slack payload integrity. Read more about signing secrets in the [Slack documentation](https://api.slack.com/authentication/verifying-requests-from-slack). 3. In the **Activation** section, configure **Condition** when the Slack event or command can trigger a new BPMN process. The following example will trigger a new BPMN process for every `app_mention` Slack event type: `=(request.body.event.type = "app_mention")`. 4. In the **Variable mapping** section, fill the field **Result variable** to store the response in a process variable. For example, `myResultVariable`. 5. In the **Variable expression** section, fill the field to map specific fields from the response into process variables using [FEEL](/components/modeler/feel/what-is-feel.md). The following example will extract both Slack message sender ID and text from Slack `app_mention` event: `={senderId: request.body.event.user, text: request.body.event.text}`. ### Correlation The **Correlation** section allows you to configure the message correlation parameters. :::note The **Correlation** section is not applicable for the plain **start event** element template of the Slack connector. Plain **start events** are triggered by process instance creation and do not rely on message correlation. ::: #### Correlation keys - **Correlation key (process)** is a FEEL expression that defines the correlation key for the subscription. This corresponds to the **Correlation key** property of a regular **message intermediate catch event**. - **Correlation key (payload)** is a FEEL expression used to extract the correlation key from the incoming message. This expression is evaluated in the connector Runtime and the result is used to correlate the message. For example, given your correlation key is defined with `myCorrelationKey` process variable, and the request body contains `"event": {"text": "12345"}`, your correlation key settings will look like this: - **Correlation key (process)**: `=myCorrelationKey` - **Correlation key (payload)**: `=request.body.event.text` Learn more about correlation keys in the [messages guide](../../../concepts/messages). #### Message ID expression The **Message ID expression** is an optional field that allows you to extract the message ID from the incoming request. The message ID serves as a unique identifier for the message and is used for message correlation. This expression is evaluated in the connector Runtime and the result is used to correlate the message. In most cases, it is not necessary to configure the **Message ID expression**. However, it is useful if you want to ensure message deduplication or achieve a certain message correlation behavior. Learn more about how message IDs influence message correlation in the [messages guide](../../../concepts/messages#message-correlation-overview). For example, to set the message ID to the value of the `text` field of the incoming message, configure the **Message ID expression** as follows: ``` = request.body.event.text ``` #### Message TTL The **Message TTL** is an optional field that allows you to set the time-to-live (TTL) for the correlated messages. TTL defines the time for which the message is buffered in Zeebe before being correlated to the process instance (if it can't be correlated immediately). The value is specified as an ISO 8601 duration. For example, `PT1H` sets the TTL to one hour. Learn more about the TTL concept in Zeebe in the [message correlation guide](../../../concepts/messages#message-buffering). ## Make your Slack inbound connector for receiving slash command notifications executable 1. In the **Webhook Configuration** section, configure the **Webhook ID**. By default, **Webhook ID** is pre-filled with a random value. This value will be a part of the Slack event subscription or slash command URL. 2. In the **Webhook Configuration** section, configure the **Slack signing secret**. This value is unique to your Slack application and used to validate a Slack payload integrity. Read more about signing secrets in the [Slack documentation](https://api.slack.com/authentication/verifying-requests-from-slack). 3. In the **Activation** section, configure **Condition** when the Slack event or command can trigger a new BPMN process. The following example will trigger a new BPMN process for every `/test` Slack command type: `=(connectorData.command = "/test")`. 4. In the **Variable mapping** section, fill the field **Result variable** to store the response in a process variable. For example, `myResultVariable`. 5. In the **Variable expression** section, fill the field to map specific fields from the response into process variables using [FEEL](/components/modeler/feel/what-is-feel.md). The following example will extract both Slack message sender ID and text from Slack `/test hello` command: `={senderId: connectorData.user_id, text: connectorData.text}`. When using the **Slack inbound connector** with an **Intermediate Catch Event**, fill in the **Correlation key (process)** and **Correlation key (payload)**. - **Correlation key (process)** is a FEEL expression that defines the correlation key for the subscription. This corresponds to the **Correlation key** property of a regular **Message Intermediate Catch Event**. - **Correlation key (payload)** is a FEEL expression used to extract the correlation key from the incoming message. This expression is evaluated in the connector Runtime and the result is used to correlate the message. For example, given that your correlation key is defined with `myCorrelationKey` process variable, and the request body contains `text=hello}`, your correlation key settings will look like this: - **Correlation key (process)**: `=myCorrelationKey` - **Correlation key (payload)**: `=connectorData.text` Learn more about correlation keys in the [messages guide](../../../concepts/messages). ## Activate the Slack inbound connector by deploying your diagram Once you click the **Deploy** button, your **Slack inbound connector** will be activated and publicly available. URLs of the exposed **Slack inbound connector** adhere to the following pattern: `https:///inbound/` - `` is the URL of connectors component deployment. When using the Camunda 8 SaaS offering, this will typically contain your **region Id** and **cluster Id**, found in your client credentials under the **API** tab within your cluster. - `` is the ID (path) you configured in the properties of your **Slack inbound connector**. If you make changes to your **Slack Inbound connector** configuration, you need to redeploy the BPMN diagram for the changes to take effect. When you click on the event with **Slack inbound connector** applied to it, a new **Webhooks** tab will appear in the properties panel. This tab displays the URL of the **Slack inbound connector** for every cluster where you have deployed your BPMN diagram. :::note The **Webhooks** tab is only supported in Web Modeler as part of the Camunda 8 SaaS offering. You can still use Slack inbound connectors in Desktop Modeler, or with your Camunda 8 Self-Managed. In that case, Slack inbound connector deployments and URLs will not be displayed in Modeler. ::: ## Wiring with Slack ### Events API This is a simplified guide. For full guide, refer to the [official Slack documentation](https://api.slack.com/apis/connections/events-api). 1. Make sure you have sufficient permissions to modify your Slack application. 2. Open [Slack API portal](https://api.slack.com) and select your Slack application. 3. Navigate to the **Event Subscription** page. 4. Click **Enable Events**. 5. In the **Request URL** field, put the webhook URL. You can find it at the **Webhook** tab in the properties panel of you BPMN diagram. 6. Make sure that the **Request URL** indicates that endpoint is **Verified**. This process may take several seconds. 7. Click **Subscribe to bot events**. 8. Select all events you wish to receive. **Note:** some messages may produce several events. For example, a message `@YourBot test` will generate both `app-mention` and `message` events. 9. Click **Save** to apply new changes. 10. Install or re-install your app into your workspace. ### Slash commands This is a simplified guide. For a full guide, refer to the [official Slack documentation](https://api.slack.com/interactivity/slash-commands). 1. Make sure you have sufficient permissions to modify your Slack application. 2. Open [Slack API portal](https://api.slack.com) and select your Slack application. 3. Navigate to **Slash Commands**. 4. Click **Create New Command**. 5. Fill the fields **Command**, **Short Description**, and **Usage Hint** as you prefer. 6. In the **Request URL** field, put the webhook URL. You can find it at the **Webhook** tab in the properties panel of your BPMN diagram. 7. Click **Save** to apply new changes. ## Security considerations ### Integrity Each Slack message is signed with HMAC using a Slack signing key. The **Slack inbound connector** verifies HMAC integrity for every incoming request. Read more about signing secrets in the [Slack documentation](https://api.slack.com/authentication/verifying-requests-from-slack). ## Appendix ### Slack `app_mention` event example ``` POST https:///inbound/ connection: close content-type: application/json content-length: 429 x-slack-request-timestamp: 1687791117 x-slack-signature: v0=aaaaaaaabbbbbbbbcccccccddddddeeeeeeffffffff accept: application/json,*/* accept-encoding: gzip,deflate user-agent: Slackbot 1.0 (+https://api.slack.com/robots) host: { "token": "XXXXXXXX", "team_id": "XXXXXXXX", "api_app_id": "XXXXXXXX", "event": { "client_msg_id": "ffb7ded2-6f55-468d-926f-cad3195c8056", "type": "app_mention", "text": "<@XXXXXXXX> say hello", "user": "XXXXXXXX", "ts": "11111111.2222222", "blocks": [ { "type": "rich_text", "block_id": "rarsi", "elements": [ { "type": "rich_text_section", "elements": [ { "type": "user", "user_id": "XXXXXXXX" }, { "type": "text", "text": " say hello" } ] } ] } ], "team": "XXXXXXXX", "thread_ts": "1687864866.335329", "parent_user_id": "XXXXXXXX", "channel": "XXXXXXXX", "event_ts": "1687866358.496959" }, "type": "event_callback", "event_id": "XXXXXXXX", "event_time": 1687866358, "authorizations": [ { "enterprise_id": null, "team_id": "XXXXXXXX", "user_id": "XXXXXXXX", "is_bot": true, "is_enterprise_install": false } ], "is_ext_shared_channel": false, "event_context": "XXXXXXXX" } ``` ### Slack slash command example Given the following command is executed: `/test123 test`. ``` POST https:///inbound/ connection: close content-type: application/x-www-form-urlencoded content-length: 429 x-slack-request-timestamp: 1687792480 x-slack-signature: v0=aaaaaaaabbbbbbbbcccccccddddddeeeeeeffffffff accept: application/json,*/* accept-encoding: gzip,deflate user-agent: Slackbot 1.0 (+https://api.slack.com/robots) host: token=qQqQqQqQqQqQqQqQqQ &team_id=T05ABCDEFG &team_domain=yourdomain &channel_id=C05QQQQQQ &channel_name=channel1 &user_id=U05AAAAAAA &user_name=your.user &command=%2Ftest123 &text=test &api_app_id=A05DDDDDDD &is_enterprise_install=false &response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FXXXXXXXXX%2FYYYYYYYYYYY%2FZZZZZZZZ &trigger_id=111111111.222222222.33333333 ``` --- ## SQL connector The **SQL connector** is an outbound connector that allows you to connect your BPMN service with SQL databases (MariaDB, Microsoft SQL Server, PostgreSQL, MySQL). ## Prerequisites To use the **SQL connector**, ensure you have an SQL database instance running. To avoid exposing your sensitive data as plain text, use Camunda secrets. Follow our documentation on [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ## Create an SQL connector task ## Make your SQL connector executable To make your **SQL connector** executable, fill out the mandatory fields highlighted in red in the properties panel on the right side of the screen. ### Database Select the database type you want to connect to. The **SQL connector** supports the following databases: - MariaDB - Microsoft SQL Server - MySQL - PostgreSQL - **Oracle:** (See note below.) :::note The Oracle Database connector requires the Oracle JDBC driver, which Camunda cannot distribute due to licensing restrictions. To connect to an Oracle database, you must manually download the JDBC driver from [Oracle](https://www.oracle.com/database/technologies/appdev/jdbc-downloads.html) and run the connector in [hybrid mode](/components/connectors/use-connectors-in-hybrid-mode.md). When building a custom Docker image, include the driver by copying it into the image—for example, add `COPY ojdbc17.jar /opt/custom/` to your Dockerfile. This ensures the driver is on the classpath when the connector runtime starts. ::: ### Connection The **SQL connector** supports two types of connections: - [URI](#uri-connection): Use this option to connect to your database using a URI (similar to a connection string). - [Detailed](#detailed-connection): Use this option to connect to your database by providing detailed connection information (host, port, database name, username, password). #### URI connection If you choose the URI connection type, you need to provide: | Property | Type | Required | Description | Example | | ---------- | ---------------------------------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | | URI | String | Yes | The URI connection string for your database. The format of the URI depends on the database type you selected. You can find more details about the URI format in the relevant official documentation for [MariaDB](https://mariadb.com/kb/en/about-mariadb-connector-j/#connection-strings), [PostgreSQL](https://jdbc.postgresql.org/documentation/use/#connecting-to-the-database), [MySQL](https://dev.mysql.com/doc/connector-j/en/connector-j-reference-jdbc-url-format.html), and [Microsoft SQL Server](https://learn.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url?view=sql-server-ver16). | `jdbc:mysql://mysqlHost:3306/mydatabase?someOption=someValue` | | Properties | [Object](/components/modeler/feel/language-guide/feel-data-types.md#context) | No | Optional properties that can be used to configure the connection. These properties are appended to the URI. You can find more details about the properties in the relevant official documentation for [MariaDB](https://mariadb.com/kb/en/about-mariadb-connector-j/#optional-url-parameters), [PostgreSQL](https://jdbc.postgresql.org/documentation/use/#connection-parameters), [MySQL](https://dev.mysql.com/doc/connector-j/en/connector-j-reference-configuration-properties.html), and [Microsoft SQL Server](https://learn.microsoft.com/en-us/sql/connect/jdbc/setting-the-connection-properties?view=sql-server-ver16). | `={useSSL:false, requireSSL:false, user: "john", password:"securePwd"}` | #### Detailed connection If you choose the detailed connection type, provide the following: | Property | Type | Required | Description | Example | | ---------- | ---------------------------------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | | Host | String | Yes | The host of your database. | `localhost` | | Port | Number | Yes | The port of your database. | `3306` | | Username | String | No | The username to connect to your database. | `myuser` | | Password | String | No | The password to connect to your database. | `mypassword`, `{{secrets.MY_PASSWORD}}` | | Database | String | No | The name of your database. | `mydatabase` | | Properties | [Object](/components/modeler/feel/language-guide/feel-data-types.md#context) | No | Optional properties that can be used to configure the connection. You can find more details about the properties in the relevant official documentation for [MariaDB](https://mariadb.com/kb/en/about-mariadb-connector-j/#optional-url-parameters), [PostgreSQL](https://jdbc.postgresql.org/documentation/use/#connection-parameters), [MySQL](https://dev.mysql.com/doc/connector-j/en/connector-j-reference-configuration-properties.html), and [Microsoft SQL Server](https://learn.microsoft.com/en-us/sql/connect/jdbc/setting-the-connection-properties?view=sql-server-ver16). | `={useSSL:false, requireSSL:false}` | ### Query :::note You should pay extra attention to the query you are executing. Make sure it is safe and does not expose your database to SQL injection attacks. Use **[variables](#variables)** as much as possible to prevent SQL injection attacks. ::: | Property | Type | Required | Description | Example | | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------- | | [Return results](#return-results) | Boolean | Yes (default is `false`) | If the query should return results (when using `SELECT` or `RETURNING`), set this field to `true`. Otherwise (insert, update, delete, create table or database), leave the checkbox unchecked.This property will **change** the response type.See the details [here](#return-results). | `true` | | [Query](#query-description) | String | Yes | The SQL query you want to execute.See the details [here](#query-description). | `SELECT * FROM mytable WHERE field = :field` | | [Variables](#variables) | [List](/components/modeler/feel/language-guide/feel-data-types.md#list) or [object](/components/modeler/feel/language-guide/feel-data-types.md#context) | No | Variables that can be used in the query.See the details [here](#variables). | `={field: "theFieldValue"}`, `=[24]` | #### Return results - When `false`, the response (see the [output](#what-is-the-output-format-of-the-sql-connector) section) will consist of an object containing an integer (`modifiedRows`) representing the number of modified rows. This is applicable for: - `INSERT` - `UPDATE` - `DELETE` This will return `0` for: - `CREATE TABLE` - `CREATE DATABASE`, except for MySQL, where it will return `1` - When `true`, the response will be a list of objects. This list will contain the results of the `SELECT` query.For instance, `SELECT * FROM mytable`, where `mytable` is a table with columns' `name` and `age`, will return: ```json { "resultSet": [ { "name": "John Doe", "age": 29 }, { "name": "Jane Doe", "age": 27 } ] } ``` #### Query {#query-description} The query you want to execute. We currently support the following SQL queries: - `SELECT` - `INSERT` - `UPDATE` - `DELETE` - `CREATE TABLE` - `CREATE DATABASE` The query might contain variables that can be used in the query, and we recommend using them as a best practice to prevent SQL injection attacks. See the [variables](#variables) section for more details. #### Variables Variables need to be provided as a list or an object. We provide three ways to use variables in your query: | Type | Query example | Variables example | | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | | Named parameters | `SELECT * FROM mytable WHERE field = :field``INSERT INTO Employee (id, name, age, department) VALUES (:id, :name, :age, :department)` | `={field: "theFieldValue"}``={id: 1, name: "John", age: 34, department: "Dept"}` | | Positional parameters | `SELECT * FROM mytable WHERE field = ?``INSERT INTO Employee (id, name, age, department) VALUES (?, ?, ?, ?)` | `=["theFieldValue"]``=[1, "John", 34, "Dept"]` | | List parameters | `SELECT * FROM mytable WHERE field IN ()` | `={listField: ["val1", "val2"]}` | ## Appendix & FAQ ### How do I store secrets for my connector? Use Camunda secrets to avoid exposing your credentials. Follow our documentation on [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ### What is the output format of the SQL connector? Depending on the type of query you execute, the response will contain either the number of modified rows (an object with a `modifiedRows` attribute) or the result set (an object with a `resultSet` attribute). - If the query **doesn't return results** (insert, update, delete, create table or database), the response will consist of an integer representing the number of modified rows. See the [return results](#return-results) section for more details. In this case, the response will look like this: ```json { "modifiedRows": 1 } ``` - If the query is a `SELECT` query (or uses the `RETURNING` keyword), the response will be an object with a `resultSet` property (as a list). This list will contain the results of the `SELECT` query as explained in the [return results](#return-results) section. For instance, `SELECT * FROM mytable`, where `mytable` is a table with columns' `name` and `age`, will return: ```json { "resultSet": [ { "name": "John Doe", "age": 29 }, { "name": "Jane Doe", "age": 27 } ] } ``` --- ## Twilio connector The **Twilio connector** allows you to integrate your BPMN service with Twilio's messaging API. With this connector, you can send SMS messages, get messages, and more. This documentation will guide you through the process of setting up and using the **Twilio connector**. ## Prerequisites Before you can use the Twilio connector, create a Twilio account and obtain an account SID and auth token from the [Twilio Console](https://www.twilio.com/console). You will also need to have a phone number to use as the sender for your SMS messages. :::note Use Camunda secrets to store your account SID and auth token so you don't expose sensitive information directly from the process. See [managing secrets](https://docs.camunda.org/manual/latest/user-guide/process-engine/secrets/) to learn more. ::: ## Create a Twilio connector task ## Make your Twilio connector executable To work with the Twilio connector, choose the required operation type in the **Operation** section and complete the mandatory fields highlighted in red in the connector properties panel on the right side of the screen. ### Operation Choose an operation type of either sendSms, listMessages, or getMessage in the **Operation** section: - **Send SMS**: Send an SMS message to a specified phone number from your Twilio account. - **List messages**: Retrieve a list of messages sent from your Twilio account within specified filters. - **Get message**: Retrieve the details of a specific message sent from your Twilio account. ## Authentication To access the Twilio API, the connector needs the appropriate credentials. The following authentication options are available: - **Account SID**: Provide the Account SID for your Twilio account. - **Auth Token**: Provide the Auth Token for your Twilio account. **OR** - **API Key**: Provide the API Key for your Twilio account. - **API Secret**: Provide the API Secret for your Twilio account. The Account SID and Auth Token or API Key and API secret are required properties and must be provided to use the connector. If these properties are not set, the connector will not be able to authenticate with the Twilio API. For more information on authentication and security in Twilio, refer to the [Twilio documentation](https://www.twilio.com/docs/usage/security). ## Required fields ### Send SMS operation - `Body`: The content of the SMS message. - `To number`: The phone number that you want to send the SMS message to. - `From number`: The phone number to use as the sender of the SMS message. :::note See the [Twilio documentation](https://www.twilio.com/docs/sms/send-messages) for more details. ::: ### List messages operation - `Date sent after`: (Optional) The date and time to start retrieving messages from. Messages sent on or after this date and time will be included in the results. The date and time must be in ISO 8601 format, such as `2023-04-19T08:30:00Z`. - `Date sent before`: (Optional) The date and time to stop retrieving messages at. Messages sent before this date and time will be included in the results. The date and time must be in ISO 8601 format, such as `2023-04-19T08:30:00Z`. - `From`: (Optional) The phone number that the message was sent from. Only messages sent from this phone number will be included in the results. - `To`: (Optional) The phone number that the message was sent to. Only messages sent to this phone number will be included in the results. - `Page size`: (Optional) The maximum number of messages to retrieve per page. This value must be between 1 and 1000. :::note See the Twilio documentation on [filtering by date sent](https://www.twilio.com/docs/sms/api/message-resource?code-sample=code-read-list-messages-filter-by-before-sent-date&code-language=curl&code-sdk-version=json) and [getting filters](https://www.twilio.com/docs/sms/api/message-resource?code-sample=code-read-list-messages-matching-filter-criteria&code-language=curl&code-sdk-version=json) for more information. ::: ### getMessage operation - `Message SID`: The SID of the message you want to retrieve. See the [Twilio documentation](https://www.twilio.com/docs/sms/api/message-resource?code-sample=code-fetch-message&code-language=curl&code-sdk-version=json) for more details. ## Handle connector response The **Twilio connector** is a protocol connector built on top of the HTTP REST connector. Therefore, handling the response is still applicable and can be done as described in the [HTTP REST connector response documentation](/components/connectors/protocol/rest.md#response). When using the **Twilio connector**, the response from the Twilio API will be available in a temporary local response variable. This variable can be mapped to the process by specifying the Result Variable. For example, if you use the **Send SMS Message** method in the Twilio connector, the response may look like this: ```json { "status": 201, "headers": { "content-type": "application/json" }, "response": { "sid": "SM1234567890", "date_created": "2023-04-18T15:30:00Z", "date_updated": "2023-04-18T15:30:00Z", "date_sent": null, "account_sid": "AC1234567890", "from": "+1234567890", "to": "+0987654321", "body": "Hello, World!", "status": "queued", "num_segments": "1", "direction": "outbound-api", "api_version": "2010-04-01", "price": null, "price_unit": "USD", "error_code": null, "error_message": null, "uri": "/2010-04-01/Accounts/AC1234567890/Messages/SM1234567890.json", "subresource_uris": { "media": "/2010-04-01/Accounts/AC1234567890/Messages/SM1234567890/Media.json" } } } ``` In this example, the response variable contains an SID attribute that uniquely identifies the message that was sent. You can choose to unpack the content of your response into multiple process variables using the **Result Expression**, which is a FEEL Context Expression. The Result Expression allows you to access specific attributes from the response and assign them to process variables that can be used in subsequent steps of your process. ```feel = { sid: response.body.sid, date_created: response.body.date_created, from: response.body.from, to: response.body.to, body: response.body.body } ``` In this example, we are using the Result Expression to extract the `sid`, `date_created`, `from`, `to`, and `body` attributes from the response variable and assign them to process variables with the same name. You can then use these variables in subsequent steps of your process. :::note The syntax for accessing attributes in the Result Expression may vary depending on the structure of your response object. You can refer to the [FEEL Context Expression](/components/modeler/feel/language-guide/feel-context-expressions.md) documentation for more information on how to use the **Result Expression**. ::: ## Troubleshooting If you are having issues with the Twilio connector, try the following: - Ensure your Twilio credentials are correct. - Ensure you have set up your Twilio account and have a valid phone number. - Ensure your configuration properties are set correctly. - Check the logs for any error messages. - Contact [Camunda support](https://camunda.com/services/support/) if you need further assistance. For more information on using Twilio, visit the [official documentation](https://www.twilio.com/docs). ## Using Twilio connector Best Practices When using the Twilio connector in a BPMN process, it is important to keep in mind that there may be delays in message delivery or processing, and that some messages may fail to be delivered due to various reasons such as invalid phone numbers, network issues, etc. To ensure that messages are sent and delivered reliably, it is recommended to build your BPMN diagram to handle retries and error scenarios. One way to achieve this is by using an intermediate timer event to trigger a retry after a certain amount of time has elapsed, or by using an error boundary event to catch and handle errors in the process. :::note To avoid performance issues, it is recommended to limit the number of retries and to implement proper error handling mechanisms in your process. ::: To learn more about implementing retry and error handling logic in your BPMN diagram, you can refer to the [Camunda BPMN examples](https://camunda.com/bpmn/examples/) page, which includes examples of BPMN diagrams with timer and error configurations. The **Twilio Webhook connector** is an inbound connector that enables you to start a BPMN process instance triggered by a [Twilio event](https://www.twilio.com/docs/usage/webhooks). ## Create a Twilio Webhook connector task 1. Start building your BPMN diagram. You can use the **Twilio Webhook connector** with either a **Start Event** or an **Intermediate Catch Event** building block. 2. Select the applicable element and change its template to a **Twilio Webhook connector**. 3. Fill in all required properties. 4. Complete your BPMN diagram. 5. Deploy the diagram to activate the webhook. 6. Navigate to the **Webhooks** tab in the properties panel to see the webhook URL. 7. Run the process if you use the **Twilio Webhook Intermediate Catch Event connector**, and only deploy the process if the diagram starts from the **Start Event**. ## Make your Twilio Webhook connector for receiving messages executable ### Fill properties in the Webhook Configuration section 1. Choose one of the required methods in the **Webhook method** property. For example, if you know the webhook will be triggered by the **POST** method, choose **POST**. Alternatively, if it is not essential to specify a specific method for the webhook trigger, select **ANY**. 2. Configure the **Webhook ID**. By default, the **Webhook ID** is pre-filled with a random value. This value will be part of the Webhook URL. For more details about Twilio Webhook URLs, refer to the section below on [activating the Twilio Webhook connector by deploying your diagram](#activate-the-twilio-webhook-connector-by-deploying-your-diagram). 3. Select **Enabled** in **HMAC authentication** if you want to use HMAC authentication. After that, set the [Twilio Auth Token](https://support.twilio.com/hc/en-us/articles/223136027-Auth-Tokens-and-How-to-Change-Them) as the shared secret key in the **HMAC secret key** field property. :::note Use Camunda secrets to store your credentials securely. Refer to the [Camunda secrets documentation](/components/hub/organization/manage-clusters/manage-secrets.md) for more details. ::: ### Fill in the properties in the **Activation** and **Correlation** sections #### Activation condition Optionally, configure the **Activation Condition**. For example, if an external message has the body: ``` { "body": { "ApiVersion": "2010-04-01", "FromCountry": "EU", "Body": "Hello world", "SmsStatus": "received" ... } ... } ``` The **Activation Condition** value might look like this: ``` =(request.body.SmsStatus="received") ``` Leave this field empty to receive all messages every time. :::note The **Correlation** section is not applicable for the plain **start event** element template of the Twilio connector. Plain **start events** are triggered by process instance creation and do not rely on message correlation. ::: #### Correlation keys - **Correlation key (process)** is a FEEL expression that defines the correlation key for the subscription. This corresponds to the **Correlation key** property of a regular **Message Intermediate Catch Event**. - **Correlation key (payload)** is a FEEL expression used to extract the correlation key from the incoming message. This expression is evaluated in the connector Runtime, and the result is used to correlate the message. For example, if your correlation key is defined with a process variable named `myCorrelationKey`, and you want to correlate by the `Body` property in the request body, which contains: ``` { "body": { "ApiVersion": "2010-04-01", "FromCountry": "EU", "Body": "Continue process", "SmsStatus": "received" ... } ... } ``` your correlation key settings will look like this: - **Correlation key (process)**: `=myCorrelationKey` - **Correlation key (payload)**: `=request.body.Body` Learn more about correlation keys in the [messages guide](../../../concepts/messages). #### Message ID expression The **Message ID expression** is an optional field that allows you to extract the message ID from the incoming request. The message ID serves as a unique identifier for the message and is used for message correlation. This expression is evaluated in the connector Runtime and the result is used to correlate the message. In most cases, it is not necessary to configure the **Message ID expression**. However, it is useful if you want to ensure message deduplication or achieve a certain message correlation behavior. Learn more about how message IDs influence message correlation in the [messages guide](../../../concepts/messages#message-correlation-overview). #### Message TTL The **Message TTL** is an optional field that allows you to set the time-to-live (TTL) for the correlated messages. TTL defines the time for which the message is buffered in Zeebe before being correlated to the process instance (if it can't be correlated immediately). The value is specified as an ISO 8601 duration. For example, `PT1H` sets the TTL to one hour. Learn more about the TTL concept in Zeebe in the [message correlation guide](../../../concepts/messages#message-buffering). ## Activate the Twilio Webhook connector by deploying your diagram Once you click the **Deploy** button, your Twilio Webhook will be activated and publicly available. The URLs of the exposed Twilio Webhooks adhere to the following pattern: `http(s):///inbound/` - `` is the URL of the connectors component deployment. When using the Camunda 8 SaaS offering, this will typically contain your **region Id** and **cluster Id**, found in your client credentials under the **API** tab within your cluster. - `` is the ID (path) you configured in the properties of your Twilio Webhook connector. :::note If you make changes to your Twilio Webhook connector configuration, you need to redeploy the BPMN diagram for the changes to take effect. ::: When you click on the event with the Twilio Webhook connector applied to it, a new **Webhooks** tab will appear in the properties panel. This tab displays the URL of the Twilio Webhook connector for every cluster where you have deployed your BPMN diagram. :::note The **Webhooks** tab is only supported in Web Modeler as part of the Camunda 8 SaaS offering. You can still use the Twilio Webhook connector in the Desktop Modeler or with Camunda 8 Self-Managed. In that case, Twilio Webhook connector deployments and URLs will not be displayed in the Modeler. ::: ## Variable mapping The **Variable mapping** section allows you to configure the mapping of the webhook request to the process variables. - Use the **Result variable** to store the response in a process variable. For example, `myResultVariable`. - Use the **Result expression** to map specific fields from the response into process variables using [FEEL](/components/modeler/feel/what-is-feel.md). For example, given that the **Twilio Webhook connector** is triggered with the webhook: ``` { "body": { "ApiVersion": "2010-04-01", "FromCountry": "EU", "Body": "Hello world", "SmsStatus": "received" ... } ... } ``` and you would like to extract the `SmsStatus` as a process variable `mySmsStatus`, the **Result Expression** might look like this: ``` = { mySmsStatus: request.body.SmsStatus } ``` ## Configure your Twilio account To set a webhook URL in Twilio for SMS, follow these steps: 1. Log in to your Twilio account at [www.twilio.com/console](https://www.twilio.com/console). 2. Navigate to the **Phone Numbers** section, which you can find in the left-hand side menu. 3. Click on the phone number for which you want to set the webhook URL. 4. Scroll down to the **Messaging** section and locate the **A message comes in** field. 5. In the input box next to **A message comes in**, enter the URL where you want Twilio to send incoming SMS messages and choose the required method. 6. Save your changes. Once you have set the webhook URL, Twilio will send a `POST` or `GET` request to that URL whenever an incoming SMS message is received on the specified phone number. ## Next steps - Learn more about [Twilio webhooks](https://docs.github.com/en/developers/webhooks-and-events/webhooks/about-webhooks). - Read the [Twilio webhooks FAQ](https://www.twilio.com/docs/usage/webhooks/webhooks-faq). - Understand [Twilio webhooks security](https://www.twilio.com/docs/usage/webhooks/webhooks-security). - Learn about [other connectors available](./available-connectors-overview.md) in Camunda to integrate with different systems and services. - Learn more about using connectors [here](../use-connectors/index.md). - Learn more about inbound connectors [here](../use-connectors/inbound.md). --- ## UiPath connector The **UiPath connector** allows you to orchestrate a UiPath bot from your BPMN process with [UiPath](https://cloud.uipath.com). ## Prerequisites To use the **UiPath connector**, you need to have a [UiPath](https://cloud.uipath.com) account and configure your organization settings. See the [automation cloud guide](https://docs.uipath.com/automation-cloud/docs/introduction) to learn more. ## Create a UiPath connector task ## Operation types The UiPath connector currently supports two operation types in the **Operation type** dropdown list: _Add queue item_ and _Get queue item result by ID_. ### Authentication You can choose among the available UiPath connector authentication types according to your authentication requirements. ### UiPath connector (bearer token) #### Create a new connector secret We advise you to keep your **Bearer Token** safe and avoid exposing it in the BPMN `xml` file by creating a secret: 1. Follow our [guide for creating secrets](/components/hub/organization/manage-clusters/manage-secrets.md). 2. Name your secret (i.e `BEARER_TOKEN_UIPATH`) so you can reference it later in the connector. #### Configure the bearer token Select the **UiPath connector** and fill out the following properties under the **Authentication** section: 1. Click **Bearer Token** in the **Authentication** section. 2. Set **Bearer** to the secret you created (i.e. `{{secrets.UIPATH_BEARER_TOKEN}}`). ### UiPath connector (OAuth token) #### Create a new connector secret We advise you to keep your **Client ID** safe and avoid exposing it in the BPMN `xml` file by creating a secret: 1. Follow our [guide for creating secrets](/components/hub/organization/manage-clusters/manage-secrets.md). 2. Name your secret (i.e `UIPATH_CLIENT_ID`) so you can reference it later in the connector. #### Configure the OAuth Token Select the **UiPath connector** and fill out the following properties under the **Authentication** section: 1. Click **OAuth 2.0** in the **Authentication** section. 2. Set **Client ID** to the secret you created (i.e. `{{secrets.UIPATH_CLIENT_ID}}`). 3. Set **Client secret** to the secret you created (i.e. `{{secrets.UIPATH_CLIENT_SECRET}}`). 4. Choose **Client Authentication** from the dropdown menu (i.e. `{{Send client credentials in body}}`). Find more information about the OAuth client credentials flow in the [RFC reference](https://www.rfc-editor.org/rfc/rfc6749#section-4.4). ### Add queue item This operation allows you to create a new item and add it to a queue from UiPath Orchestrator. To execute it, take the following steps: 1. Select the operation **Add queue item** from the **Operation type** dropdown list. 2. Configure authentication as described in the [authentication](#authentication) section. 3. Fill out the input fields as described in the [configuration](#configuration) section. 4. Fill out the input fields as described in the [input](#input) section. 5. Fill out the response mapping as described in the [add queue item response](#add-queue-item-response) section. #### Configuration For this section, you must fill out the following fields: 1. **Cloud URL**: Comes with a default value of `cloud.uipath.com`. You can always change it, if needed. To use a connector secret, use a double curly braces notation, e.g. `{{secrets.MY_SECRET_VALUE}}`. 2. **Cloud organization**: The name of your organization. See [about organizations](https://docs.uipath.com/automation-cloud/docs/about-organizations) to learn more. To use a connectors secret, use a double curly braces notation, e.g. `{{secrets.MY_SECRET_VALUE}}`. 3. **Cloud tenant**: The name of the tenant. See [about tenants](https://docs.uipath.com/automation-cloud/docs/about-tenants) to learn more. To use a connectors secret, use a double curly braces notation, e.g. `{{secrets.MY_SECRET_VALUE}}`. 4. **Organization Unit ID**: Click **Orchestrator** and you will find the ID in the URL. For example, `https://cloud.uipath.com/MyOrg/MyTenant/orchestrator_/?tid=26929&fid=112233` where the **Organization Unit ID** is `112233`. To use a connectors secret, use a double curly braces notation, e.g. `{{secrets.MY_SECRET_VALUE}}`. #### Input For this section, fill out the following fields: 1. **Queue Name**: The queue where the QueueItem object is to be added. Check [queues and transactions](https://docs.uipath.com/orchestrator/docs/about-queues-and-transactions) to learn more. 2. _(Optional)_ **Defer date**: The earliest date and time at which the item is available for processing. If empty, the item can be processed as soon as possible. Expected date format is `yyyy-MM-dd`. 3. _(Optional)_ **Due date**: The latest date and time at which the item should be processed. If empty, the item can be processed at any given time. Expected date format is `yyyy-MM-dd`. 4. _(Optional)_ **Priority**: Select a value from the dropdown list to represent the priority level of the queue item to be added. This property is a criterion for the prioritization of queue items, alongside **Deadline** and **Postpone**. 5. _(Optional)_ **Specific Content for UiPath Job**: Data that will be passed in to the job. This must be in JSON format. ``` = { "Name":"testItemName", "Value":"testItemValue" } ``` 6. _(Optional)_ **Reference**: A string reference for the queue item. #### Add queue item response The operation **Add Queue Item** returns information about the newly created item in the queue. You can use an output mapping to map the response: 1. Use **Result Variable** to store the response in a process variable. For example, `myResultVariable`. 2. Use **Result Expression** to map fields from the response into process variables. It comes with a pre-filled value of `= {itemId: response.body.Id}`. To use operation _Get queue item result by ID_, you need an `itemId`. This expression will add it in the context for you. Learn more in [get queue item result by ID](#get-queue-item-result-by-id). Response example: ``` = { "status":201, "headers":{ "date":"Fri, 20 Jan 2023 10:13:20 GMT", "content-length":878, "server":"cloudflare", "expires":"-1", "cf-ray":"78c70973ce68153b-CDG", "api-supported-versions":"15.0", "x-frame-options":"Deny", "x-download-options":"noopen", "x-correlation-id":"7a211afe-53f1-4225-b77c-0fa477912685", "cf-cache-status":"DYNAMIC", "x-uipath-correlation-id":"undefined", "pragma":"no-cache", "strict-transport-security":"max-age=15724800; includeSubDomains", "request-context":"appId=cid-v1:354c7cb9-ae5a-4d16-84a7-f13242bbac6d", "content-security-policy":"default-src 'self';script-src 'self' https://orch-cdn.uipath.com https://use.typekit.net/ https://d2c7xlmseob604.cloudfront.net https://platform-cdn.uipath.com https://*.uipath.com https://*.pendo.io;style-src 'self' 'unsafe-inline' https://orch-cdn.uipath.com https://fonts.googleapis.com/css https://use.typekit.net https://p.typekit.net/ https://platform-cdn.uipath.com https://content.usage.uipath.com;img-src 'self' data: https://orch-cdn.uipath.com https://s.gravatar.com https://secure.gravatar.com https://*.wp.com https://*.googleusercontent.com https://i.ytimg.com https://platform-cdn.uipath.com https://*.pendo.io https://*.blob.core.windows.net https://*.amazonaws.com blob:;frame-src 'self' https://*.uipath.com https://*.pendo.io;font-src 'self' https://orch-cdn.uipath.com https://use.typekit.net/ https://fonts.gstatic.com https://platform-cdn.uipath.com data:;connect-src 'self' wss: https://orch-cdn.uipath.com https://primer.typekit.net https://use.typekit.net/ https://sentry.io https://studio-feedback.azure-api.net https://app.launchdarkly.com https://clientstream.launchdarkly.com https://events.launchdarkly.com https://api.smartling.com https://platform-cdn.uipath.com https://*.service.signalr.net https://*.uipath.com https://*.pendo.io https://cloud.uipath.com https://storage.googleapis.com https://*.blob.core.windows.net https://*.amazonaws.com dc.services.visualstudio.com;worker-src 'self' blob:", "x-xss-protection":"1", "x-content-type-options":"nosniff", "x-robots-tag":"noindex,nofollow", "content-type":"application/json; odata.metadata=minimal; odata.streaming=true", "location":"https://cloud.uipath.com/MyOrg/MyTenant/orchestrator_/odata/QueueItems(436141352)", "connection":"keep-alive", "cache-control":"no-cache, no-store, must-revalidate", "odata-version":"4.0" }, "body":{ "@odata.context":"https://cloud.uipath.com/MyOrg/MyTenant/orchestrator_/odata/$metadata#QueueItems/$entity", "QueueDefinitionId":165001, "Encrypted":false, "Status":"New", "ReviewStatus":"None", "Key":"2196eb07-c96a-4f47-a734-326dd5d58a9d", "Reference":"test", "Priority":"Low", "DeferDate":"2023-01-12T00:00:00Z", "SecondsInPreviousAttempts":0, "RetryNumber":0, "SpecificData":"{\"DynamicProperties\":{\"test\":\"test\"}}", "CreationTime":"2023-01-20T10:13:20.6603953Z", "RowVersion":"AAAAAE2f4GY=", "OrganizationUnitId":112233, "Id":436141352, "SpecificContent":{ "test":"test" } } } ``` ### Get queue item result by ID This operation allows you get an item from your UiPath Orchestrator. To execute it, take the following steps: 1. Select the operation **Get Queue Item result by ID** from the dropdown list **Operation type**. 2. Configure authentication as described in the [authentication](#authentication) section. 3. Fill out the **Item ID** field. This field supports FEEL, so you're able to fetch an item ID from the process context; for example, if you exported it while [adding a new queue item](#add-queue-item). #### Get queue item result by ID response Given you have a queue item ID previously added to a queue, the operation **Get queue item result by ID** returns information about a certain item. You can use an output mapping to map the response: 1. Use **Result Variable** to store the response in a process variable. For example, `myResultVariable`. 2. Use **Result Expression** to map fields from the response into process variables. It comes with a pre-filled value of `= {itemStatus: response.body.value[1].Status}`. You will see the `itemStatus` in the process variables. Its value will let you know if the item was processed or not. Response example: ``` { "status":200, "headers":{ "date":"Fri, 20 Jan 2023 10:13:21 GMT", "server":"cloudflare", "expires":"-1", "transfer-encoding":"chunked", "cf-ray":"78c709774a112a34-CDG", "api-supported-versions":"15.0", "x-frame-options":"Deny", "x-download-options":"noopen", "x-correlation-id":"8db50244-5f55-4598-82d3-1d6a00f806b0", "cf-cache-status":"DYNAMIC", "x-uipath-correlation-id":"undefined", "pragma":"no-cache", "strict-transport-security":"max-age=15724800; includeSubDomains", "request-context":"appId=cid-v1:354c7cb9-ae5a-4d16-84a7-f13242bbac6d", "content-security-policy":"default-src 'self';script-src 'self' https://orch-cdn.uipath.com https://use.typekit.net/ https://d2c7xlmseob604.cloudfront.net https://platform-cdn.uipath.com https://*.uipath.com https://*.pendo.io;style-src 'self' 'unsafe-inline' https://orch-cdn.uipath.com https://fonts.googleapis.com/css https://use.typekit.net https://p.typekit.net/ https://platform-cdn.uipath.com https://content.usage.uipath.com;img-src 'self' data: https://orch-cdn.uipath.com https://s.gravatar.com https://secure.gravatar.com https://*.wp.com https://*.googleusercontent.com https://i.ytimg.com https://platform-cdn.uipath.com https://*.pendo.io https://*.blob.core.windows.net https://*.amazonaws.com blob:;frame-src 'self' https://*.uipath.com https://*.pendo.io;font-src 'self' https://orch-cdn.uipath.com https://use.typekit.net/ https://fonts.gstatic.com https://platform-cdn.uipath.com data:;connect-src 'self' wss: https://orch-cdn.uipath.com https://primer.typekit.net https://use.typekit.net/ https://sentry.io https://studio-feedback.azure-api.net https://app.launchdarkly.com https://clientstream.launchdarkly.com https://events.launchdarkly.com https://api.smartling.com https://platform-cdn.uipath.com https://*.service.signalr.net https://*.uipath.com https://*.pendo.io https://cloud.uipath.com https://storage.googleapis.com https://*.blob.core.windows.net https://*.amazonaws.com dc.services.visualstudio.com;worker-src 'self' blob:", "x-xss-protection":"1", "x-content-type-options":"nosniff", "x-robots-tag":"noindex,nofollow", "content-type":"application/json; odata.metadata=minimal; odata.streaming=true", "connection":"keep-alive", "cache-control":"no-cache, no-store, must-revalidate", "odata-version":"4.0" }, "body":{ "@odata.context":"https://cloud.uipath.com/MyOrg/MyTenant/orchestrator_/odata/$metadata#QueueItems", "@odata.count":1, "value":[ { "QueueDefinitionId":165001, "Encrypted":false, "Status":"New", "ReviewStatus":"None", "Key":"2196eb07-c96a-4f47-a734-326dd5d58a9d", "Reference":"test", "Priority":"Low", "DeferDate":"2023-01-12T00:00:00Z", "SecondsInPreviousAttempts":0, "RetryNumber":0, "SpecificData":"{\"DynamicProperties\":{\"test\":\"test\"}}", "CreationTime":"2023-01-20T10:13:20.66Z", "RowVersion":"AAAAAE2f4GY=", "OrganizationUnitId":1964413, "OrganizationUnitFullyQualifiedName":"MyCorporateWorkspace", "Id":436141352, "SpecificContent":{ "test":"test" } } ] } } ``` ## Appendix ### Using UiPath connector best practice There is no guarantee a queue item will be processed right away. In that case, we suggest building your BPMN diagram to periodically retry polling. To learn more, see an entry _Solution with Timer and Loop_ at [Camunda BPMN examples](https://camunda.com/bpmn/examples/) page. :::note To avoid performance issues, it is recommended to limit the number of loop retries. ::: --- ## WhatsApp connector The **WhatsApp connector** is an outbound connector that allows you to send messages to users from your BPMN process. ## Prerequisites To start using the **WhatsApp connector**, you must have an approved Meta WhatsApp application; follow the [official guide](https://developers.facebook.com/docs/whatsapp/cloud-api/get-started) to obtain one. :::note WhatsApp webhooks are currently not supported by Camunda. ::: ## Create a WhatsApp connector task ## Authentication The **WhatsApp connector** supports authentication through Meta access tokens. Take a look at [this blog post](https://developers.facebook.com/blog/post/2022/12/05/auth-tokens/) to learn more on how to obtain one for yourself. Once the token is obtained, put it in the **Access token** field of the **Authentication** section. :::note Use Camunda secrets to avoid exposing your WhatsApp access token credentials as plain text. See our documentation on [managing secrets](/components/hub/organization/manage-clusters/manage-secrets.md) to learn more. ::: ## Sender and recipient Your WhatsApp application can have multiple phone numbers registered. Set your phone number ID in the **Sender phone number ID** field of the **Payload** section. You can find the phone number ID at the Meta developer portal. In the **Recipient phone number** field, enter a phone number you wish to send message to. ## Select operation to execute You can select one of the following operations from the **Message type** dropdown. ### Plain text When this option is selected, write any arbitrary text in the **Message text** field. This message will be sent to the target recipient. ### Message template When this option is selected, it is implied that you already have an approved WhatsApp message template. Read more bout message templates at the [official page](https://developers.facebook.com/docs/whatsapp/message-templates/guidelines/). 1. In the field **Template name**, set the name of your WhatsApp template. For example, **my_delivery_scheduled_template**. 2. In the field **Template language code**, specify the language code of your template. For example, **en_US**. 3. In the field **Header variables**, set the values for your variables only if the header has any. For example, `{"type": "text","text": "My header param"}`. 4. In the field **Body variables**, set the values for your variables only if the body has any. For example, `{"type": "text","text": "My body param"}`. See the [official Meta guide](https://developers.facebook.com/docs/whatsapp/cloud-api/guides/send-message-templates/) for more information and examples. ## Handle connector response The **WhatsApp connector** is a protocol connector, meaning it is built on top of the **HTTP REST connector**. Therefore, handling response is still applicable [as described](/components/connectors/protocol/rest.md#response). --- ## GraphQL connector The **GraphQL connector** is an outbound protocol connector that allows you to execute a GraphQL query or mutation from your BPMN process. ## Prerequisites The GraphQL connector allows you to connect to a GraphQL API endpoint. To use the GraphQL connector, you need to know the GraphQL endpoint URL, authentication, and available API methods. ## Create a GraphQL connector task ## Make your GraphQL connector executable To make the **GraphQL connector** executable, fill out the mandatory fields highlighted in red in the properties panel on the right side of the screen. :::note All the mandatory and non-mandatory fields are covered in the upcoming sections. Depending on the authentication selection you make, more fields might be required; this is covered in the next section. ::: ### Authentication You can choose among the available authentication types according to your authentication requirements using the **Authentication** section. ### None Click **None** in the **Authentication** section. No extra authentication configuration is required. ### Basic #### Create a new connector secret We advise you to keep your **Password** safe and avoid exposing it in the BPMN `xml` file by creating a secret: 1. Follow our [guide for creating secrets](/components/hub/organization/manage-clusters/manage-secrets.md). 2. Name your secret (i.e `GRAPHQL_PASSWORD`) so you can reference it later in the connector. ### Configure Basic Authentication Select the **GraphQL connector** and fill out the following properties under the **Authentication** section: 1. Click **Basic** in the **Authentication** section. 2. Set **Username** (i.e. `{{secrets.GRAPHQL_USERNAME}}`). 3. Set **Password** to the secret you created (i.e. `{{secrets.GRAPHQL_PASSWORD}}`). ### Bearer Token #### Create a new connector secret We advise you to keep your **Bearer Token** safe and avoid exposing it in the BPMN `xml` file by creating a secret: 1. Follow our [guide for creating secrets](/components/hub/organization/manage-clusters/manage-secrets.md). 2. Name your secret (i.e `GRAPHQL_BEARER_TOKEN`) so you can reference it later in the connector. #### Configure the Bearer Token Select the **GraphQL connector** and fill out the following properties under the **Authentication** section: 1. Click **Bearer Token** in the **Authentication** section. 2. Set **Bearer** to the secret you created (i.e. `{{secrets.GRAPHQL_BEARER_TOKEN}}`). ### OAuth token #### Create a new connector secret We advise you to keep your **OAUTH_TOKEN_ENDPOINT** safe and avoid exposing it in the BPMN `xml` file by creating a secret: 1. Follow our [guide for creating secrets](/components/hub/organization/manage-clusters/manage-secrets.md). 2. Name your secret (i.e `OAUTH_TOKEN_ENDPOINT`) so you can reference it later in the connector. #### Configure the OAuth Token Select the **GraphQL connector** and fill out the following properties under the **Authentication** section: 1. Click **OAuth 2.0** in the **Authentication** section. 2. Set **OAuth Token Endpoint** to the secret you created (i.e. `{{secrets.OAUTH_TOKEN_ENDPOINT}}`). 3. Set **Client ID** to the secret you created (i.e. `{{secrets.CLIENT_ID}}`). 4. Set **Client secret** to the secret you created (i.e. `{{secrets.CLIENT_SECRET}}`). 5. (Optional) Set **Scopes** (i.e. `read:clients`). Depending on the OAuth provider you're using, this may or may not be required. 6. Set **Audience** to the secret you created (i.e. `{{secrets.AUDIENCE}}`). This is an optional field depending on the OAuth provider you're using. 7. Choose **Client Authentication** from the dropdown menu (i.e. `Send client credentials in body`). Find more information about the OAuth client credentials flow in the [RFC reference](https://www.rfc-editor.org/rfc/rfc6749#section-4.4). ## HTTP endpoint Under the **HTTP Endpoint** section, fill in the **URL** with your desired endpoint and select the desired **Method**. ## GraphQL query ### Query/Mutation Insert your query or mutation you wish to execute here. This must be a syntactically valid instruction. For more details, see [the official documentation](https://graphql.org/learn/queries/). You can use [arguments](https://graphql.org/learn/queries/#arguments), [aliases](https://graphql.org/learn/queries/#aliases), [directives](https://graphql.org/learn/queries/#directives), and [fragments](https://graphql.org/learn/queries/#fragments) as well. For example: ```text query Root($id: ID) { person (id: $id) { id name } } ``` :::note Secrets are currently not supported in the **Query/Mutation** of a GraphQL connector. ::: :::note You can test your queries on publicly available GraphQL API [here](https://studio.apollographql.com/public/star-wars-swapi/home?variant=current). ::: #### Example ```text query Query { allFilms { films { title director releaseDate speciesConnection { species { name classification } } } } } ``` ### Variables You can specify [variables](https://graphql.org/learn/queries/#variables) to your queries/mutations. The **Variables** field can be configured using the [FEEL Map](/components/modeler/feel/language-guide/feel-data-types.md#context) data type. ```text = { "id": "{{secrets.GRAPHQL_ENTITY_ID}}", "includeDroids": false, } ``` :::note Secrets are not like regular variables and must be wrapped in double quotes (`"`) when used in an expression. ::: #### Example Query: ```text query Root($id: ID, $includeGender: Boolean!) { person (id: $id) { name, height, gender @include(if: $includeGender) } } ``` Variables: ```text { "id": "cGVvcGxlOjI=", "includeGender": false } ``` ### Network communication timeouts - **Connection timeout in seconds** determines the time frame in which the client will try to establish a connection with the server. If you do not specify a value, the system uses the default of 20 seconds. For cases where you need to wait indefinitely, set this value to 0. - **Read timeout in seconds** is the amount of time the client will wait to read data from the server after the connection has been made. The default is also set to 20 seconds. To allow an unlimited wait time for slow responses, set this to 0. ## Response mapping The HTTP response will be available in a temporary local `response` variable. This variable can be mapped to the process by specifying the **Result Variable**. The following fields are available in the `response` variable: - **status**: Response status - **body**: Response body of your request - **headers**: Response headers Additionally, you can choose to unpack the content of your `response` into multiple process variables using the **Result Expression**, which is a [FEEL Context Expression](/components/modeler/feel/language-guide/feel-context-expressions.md). ```text = { person: response.body.data.person } ``` The next steps in your process will have access to the `graphqlQueryResponse` variable that contain the full response and the mapped variable from the result expression: `person`. --- ## HTTP Webhook connector The **HTTP Webhook connector** is an inbound connector that allows you to start a BPMN process instance triggered by external HTTP call. ## Create an HTTP Webhook connector event 1. Start building your BPMN diagram. You can use HTTP Webhook connector with either **Start Event** or **Intermediate Catch Event** building blocks. 2. Select the applicable element and change its template to an HTTP Webhook. 3. Fill in all required properties. 4. Complete your BPMN diagram. 5. Deploy the diagram to activate the webhook. 6. Navigate to the **Webhooks** tab in the properties panel to see the webhook URL. ## Make your HTTP Webhook connector executable 1. In the **Webhook Configuration** section, configure the **Webhook ID**. By default, **Webhook ID** is pre-filled with a random value. This value will be part of the Webhook URL. You will find more details about HTTP Webhook URLs [below](#activate-the-http-webhook-connector-by-deploying-your-diagram). 2. Configure [**HMAC authentication**](https://en.wikipedia.org/wiki/HMAC) if required in the **Authentication** section: If you require HMAC authentication for your webhook, you can set up the HMAC shared secret key, HMAC header, HMAC hash algorithm, and an array of HMAC scopes. - **HMAC secret key**: Set the HMAC shared secret key that will be used to calculate the message hash. The value of this key should be provided by the webhook provider to ensure secure communication. - **HMAC header**: Set the HMAC header whose value contains an encrypted hash message. The exact value of this header should be provided by the external caller when invoking your webhook. - **HMAC hash algorithm**: Select the HMAC hash algorithm to be used in the HMAC signature calculation. The exact value of this algorithm should also be provided by the external caller when invoking your webhook. - **HMAC Scopes** (optional): Here, you can define an array of HMAC scopes to specify which parts of the webhook request are included in the HMAC signature calculation. The available HMAC scopes are: - `BODY` (default value): Includes the body of the webhook request in the HMAC signature calculation. - `URL`: Includes the URL of the webhook request in the HMAC signature calculation. - `PARAMETERS`: Includes the query parameters of the URL in the HMAC signature calculation. Example: `=["URL","PARAMETERS"]` Based on the selected HMAC scopes and the HTTP method used in the webhook request, the system will automatically determine the appropriate HMAC strategy to be used. The supported HMAC strategies are: - **Body Encoding Strategy (default)**: This strategy is used when the HMAC scopes only include `BODY` or empty. The signature data for the HMAC signature is generated from the body of the webhook request. - **URL and Parameters Encoding Strategy**: This strategy is used when the HMAC scopes include `URL`, and `PARAMETERS`, and the HTTP method is `GET`. The signature data for the HMAC signature is generated by combining the URL and the parameters of the webhook request. - **URL and Body Encoding Strategy**: This strategy is used when the HMAC scopes include `URL`, `BODY`. When the HTTP method is `GET`, it uses the **URL and Parameters Encoding Strategy** instead. The signature data for the HMAC signature is generated by combining the URL and the body of the webhook request. **Example for URL and Parameters Encoding Strategy**: Let's consider a sample webhook request: ``` HTTP Method: GET Webhook URL: "https://example.com/webhook?id=123456&name=John%20Doe" ``` In this example, the HMAC strategy will combine the URL and the query parameters to generate the signature data for the HMAC signature. The URL-encoded query parameters will be sorted alphabetically, and then, they will be concatenated with the URL: Signature Data: `https://example.com/webhook?name=John%20Doe&id=123456` The `Signature Data` will then be used to calculate the HMAC signature using the provided secret key and hash algorithm. **Example for URL and Body Encoding Strategy**: Let's consider another sample webhook request: ``` HTTP Method: POST Webhook URL: `https://example.com/webhook` Webhook Body: `{"id": 123456, "name": "John Doe", "age": 30}` ``` In this example, the HMAC strategy will combine the URL and the body of the webhook request to generate the signature data for the HMAC signature: Signature Data: `https://example.com/webhook{"id":123456,"name":"John Doe","age":30}` The `Signature Data` will then be used to calculate the HMAC signature using the provided secret key and hash algorithm. 3. Configure authorization if required in the **Authorization** section. The HTTP Webhook connector supports the following authorization methods: - **Basic** - The incoming requests must contain an `Authorization` header that contains the word `Basic` followed by a space and a base64-encoded string username:password. - Set the **Username** and **Password** properties which will be used to validate the incoming requests. - Provide the values in plain text, not base64-encoded. - **API Key** - The API key can be provided anywhere in the request, for example, in the `Authorization` header or in the request body. - Set the **API Key** property to the expected value of the API key. - Set the **API Key locator** property that will be evaluated against the incoming request to extract the API key. [See the example](#how-to-configure-api-key-authorization). - **[JWT authorization](https://jwt.io/)** - The token should be in the _Authorization_ header of the request in the format of Bearer `{JWT_TOKEN}`. - Set JWK URL which is used as a well-known public URL to fetch the [JWKs](https://auth0.com/docs/secure/tokens/json-web-tokens/json-web-key-sets). - Set JWT role property expression which will be evaluated against the content of the JWT to extract the list of roles. See more details on extracting roles from JWT data [here](#how-to-extract-roles-from-jwt-data). - Set the required roles which will be used to validate if the JWT contains all required roles. See more details on extracting roles from JWT data [here](#how-to-extract-roles-from-jwt-data). 4. Configure **Activation Condition**. For example, given external caller triggers a webhook endpoint with the body `{"id": 1, "status": "OK"}`, the **Activation Condition** value might look like `=(request.body.status = "OK")`. Leave this field empty to trigger your webhook every time. 5. Use **Variable Mapping** to map specific fields from the request into process variables using [FEEL](/components/modeler/feel/what-is-feel.md). For example, given the external caller triggers a webhook endpoint with the body `{"id": 1, "status": "OK"}` and you would like to extract `id` as a process variable `myDocumentId`, the **Result Expression** might look like this: ``` = { myDocumentId: request.body.id } ``` 6. Fill in the **Correlation** parameters if they are required by the element template. ### Correlation The **Correlation** section allows you to configure the message correlation parameters. :::note The **Correlation** section is not applicable for the plain **start event** element template of the Webhook connector. Plain **start events** are triggered by process instance creation and do not rely on message correlation. ::: #### Correlation key - **Correlation key (process)** is a FEEL expression that defines the correlation key for the subscription. This corresponds to the **Correlation key** property of a regular **Message Intermediate Catch Event**. - **Correlation key (payload)** is a FEEL expression used to extract the correlation key from the incoming message. This expression is evaluated in the connector Runtime and the result is used to correlate the message. For example, given that your correlation key is defined with `orderId` process variable, and the request body contains `{"orderId": "123"}`, your correlation key settings will look like this: - **Correlation key (process)**: `=orderId` - **Correlation key (payload)**: `=request.body.orderId` Learn more about correlation keys in the [messages guide](../../../concepts/messages). #### Message ID expression The **Message ID expression** is an optional field that allows you to extract the message ID from the incoming request. The message ID serves as a unique identifier for the message and is used for message correlation. This expression is evaluated in the connector Runtime and the result is used to correlate the message. In most cases, it is not necessary to configure the **Message ID expression**. However, it is useful if you want to ensure message deduplication or achieve a certain message correlation behavior. Learn more about how message IDs influence message correlation in the [messages guide](../../../concepts/messages#message-correlation-overview). For example, if you want to set the message ID to the value of the `transactionId` field in the incoming message, you can configure the **Message ID expression** as follows: ``` = request.body.transactionId ``` #### Message TTL The **Message TTL** is an optional field that allows you to set the time-to-live (TTL) for the correlated messages. TTL defines the time for which the message is buffered in Zeebe before being correlated to the process instance (if it can't be correlated immediately). The value is specified as an ISO 8601 duration. For example, `PT1H` sets the TTL to one hour. Learn more about the TTL concept in Zeebe in the [message correlation guide](../../../concepts/messages#message-buffering). ### XML payloads You can send XML to the webhook or return XML using the response expression, but XML content is treated as a plain string. It will not be parsed or extracted into process variables. If you need to use correlation keys with XML payloads, send the correlation key in a request header and retrieve it using the **Correlation key (payload)** property. For example: ```feel =request.headers["x-correlation-id"] ``` ### Response mode **Response mode** controls whether the webhook waits for the correlation to complete (synchronous) or returns immediately (asynchronous). Use synchronous mode only when the caller requires the process result: - **Start event**: Creates a new process instance and returns its result. - **Message start event** (and other message-based events): Correlates the message and returns the matched process instance key. In asynchronous mode: - **Start event**: Returns the process instance key of the newly created process. - **Message-based events**: Publishes the message and returns the message key. ## Activate the HTTP Webhook connector by deploying your diagram Once you click the **Deploy** button, your HTTP Webhook will be activated and publicly available. You can trigger it by making a POST request to the generated URL. :::warning Webhook endpoints and process versions When you deploy a new process version that uses the same webhook endpoint as an existing version, the new version won’t become active until no process instances are running on the older version. Webhook endpoints can’t be shared across multiple active process versions. To activate the new version, complete all process instances on the older version or migrate them using [process instance migration](/components/concepts/process-instance-migration.md). If you need both versions active, use a different webhook endpoint path in the new version. For related behavior across versions, see [Cross-version deduplication](../advanced-topics/deduplication.md#cross-version-deduplication). ::: URLs of the exposed HTTP Webhooks adhere to the following pattern: `http(s):///inbound/>` - `` is the URL of connectors component deployment. When using the Camunda 8 SaaS offering, this will typically contain your **region Id** and **cluster Id**, found in your client credentials under the **API** tab within your cluster. - `` is the ID (path) you configured in the properties of your HTTP Webhook connector. If you make changes to your HTTP Webhook connector configuration, you need to redeploy the BPMN diagram for the changes to take effect. When you click on the event with HTTP Webhook connector applied to it, a new **Webhooks** tab will appear in the properties panel. This tab displays the URL of the HTTP Webhook connector for every cluster where you have deployed your BPMN diagram. :::note The **Webhooks** tab is only supported in Web Modeler as part of the Camunda 8 SaaS offering. You can still use HTTP Webhook connector in Desktop Modeler, or with your Camunda 8 Self-Managed. In that case, HTTP Webhook connector deployments and URLs will not be displayed in Modeler. ::: For self-managed installations, the webhook URL format is: `http(s):////inbound/` If no `contextPath` is specified in the Helm chart, it must be omitted from the URL. ### Example Give a use-case when you need to configure a GitHub webhook with an **HTTP Webhook connector** in such a way that: (1) your BPMN process starts on every opened PR, and (2) the PR URL is exposed as a process variable. Let's say you choose `mySecretKey` as a shared secret passphrase. GitHub [declares](https://docs.github.com/en/developers/webhooks-and-events/webhooks/securing-your-webhooks) that they use `X-Hub-Signature-256` header for `SHA-256` HMAC. Therefore, you would need to set the following: 1. **Webhook ID**: any unique to your cluster webhook ID. This will generate a URL to trigger your webhook. In example, `myWebhookPath`. 2. **HMAC Authentication**: `enabled`. 3. **HMAC Secret Key**: `mySecretKey` or `{{secrets.MY_GH_SECRET}}`. 4. **HMAC Header**: `X-Hub-Signature-256`. 5. **HMAC Algorithm**: `SHA-256`. 6. **HMAC Scopes**: `=["BODY"]` or leave empty. 7. **Activation Condition**: `=(request.body.action = "opened")`. 8. **Variable Mapping**: `={prUrl: request.body.pull_request.url}`. 9. Click **Deploy**. ### How to configure API key authorization External callers can provide an API key anywhere in the requests. Some webhook providers use an `Authorization` header, while others pass the API key in the request body. To support any scenario, you can configure the HTTP Webhook connector to extract the API key from the request. Use the **API Key locator** field to provide a FEEL expression that will be evaluated against the request to extract the API key. The result of this expression will be used as the API key and compared against the expected API key value. Use the **API Key** field to provide the expected API key value. #### API key locator examples Suppose an external caller triggers a webhook endpoint with the following request body: ```json { "id": 1, "status": "OK", "secret": "my_secret" } ``` You want to extract the `secret` field and use it as the API key to authorize the webhook request. In this case, you can set the **API Key locator** to: ```feel =request.body.secret ``` The expression above will be evaluated to `my_secret`, which will be used as the API key. Alternatively, you can use the **API Key locator** to extract the API key from the `Authorization` header: ```feel =request.headers.authorization ``` If your `Authorization` header contains the **Bearer** prefix, you can use the [`split`](/components/modeler/feel/builtin-functions/feel-built-in-functions-string.md#splitstring-delimiter) function to remove it: ```feel =split(request.headers.authorization, " ")[2] ``` ### How to extract roles from JWT data To extract roles from the JWT payload, specify the **JWT role property expression** using the FEEL expression syntax. :::note This expression will be evaluated only against the JWT payload, therefore you cannot access process variables or secrets here. ::: #### JWT payload and role property expression example Let's observe a typical JWT payload example below: ```json { "iss": "https://idp.local", "aud": "api1", "sub": "5be86359073c434bad2da3932222dabe", "client_id": "my_client_app", "exp": 1786822616, "iat": 1686819016, "jti": "114f8c84c53703ac2120d302611e358c", "roles": ["admin", "superadmin"], "admin": true } ``` To extract the roles you can set the **JWT role property expression** to: ```feel if admin = true then ["admin"] else roles ``` Note: the result of this expression should always be an array. In this particular case, the if statement is evaluated to true, the extracted roles will be: ```feel ["admin"] ``` If you provide _["admin"]_ for **Required roles**, the message _can be correlated_. If you provide _["superadmin"]_ or _["admin","superadmin"]_, for **Required roles**, for example, the message _can NOT be correlated_ and the connector will throw an exception. :::note For GitHub, there is a simplified [GitHub Webhook connector](/components/connectors/out-of-the-box-connectors/github.md). ::: ## Return data from your HTTP Webhook connector Below, find several ways to return data from your Webhook connector. ### Verification expression The verification expression allows you to return a custom HTTP response without triggering process correlation. This feature supports multiple scenarios beyond one-time verification challenges, including: - **One-time verification challenges**: Respond to webhook provider verification requests (e.g., Slack, GitHub). - **Request validation**: Reject invalid or malformed requests before a process instance is created. - **Conditional responses**: Return different responses based on request content. #### How it works When the verification expression evaluates to a **non-null** value: 1. The webhook returns the specified response immediately. 2. Process correlation does not occur. 3. Activation conditions, HMAC validation, and the rest of the webhook pipeline are skipped. When the verification expression evaluates to **null**: 1. The webhook continues with normal processing. 2. Activation conditions are evaluated. 3. HMAC validation (if configured) is performed. 4. Process correlation proceeds as usual. #### Response format Use the following references to access incoming request data: - Body: `request.body.` - Headers: `request.headers.` - URL parameters: `request.params.` To construct a response, use these fields in the returned object: - **`body`** – for example: `{"body": {"challenge": request.body.challenge}}` - **`statusCode`** – for example: `{"statusCode": 201, "body": {"result": "ok"}}` If omitted, the default status code is **200**. - **`headers`** – for example: `{"headers": {"Content-Type": "application/json"}, "body": {"result": "ok"}}` Use FEEL expressions to build and transform the response content as needed. #### Example: One-time verification challenge A common use-case is a [one-time verification challenge](https://webhooks.fyi/security/one-time-verification-challenge). For example, consider the following verification challenge from [Slack](https://slack.com/): ```json { "token": "Jhj5dZrVaK7ZwHHjRyZWjbDl", "challenge": "3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P", "type": "url_verification" } ``` To confirm the Slack events subscription, you must return the following response: ``` HTTP 200 OK Content-type: application/json {"challenge":"3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P"} ``` To do so, the **Verification expression** field may look like: ```feel =if request.body.type = "url_verification" then {"body": {"challenge": request.body.challenge}, "statusCode": 200} else null ``` #### Example: Request validation You can validate incoming requests and reject invalid payloads before creating a process instance: ```feel =if request.body.requiredField = null then {"body": {"error": "Missing required field"}, "statusCode": 400} else null ``` This expression checks if `requiredField` is missing and returns a `400 Bad Request` status with an error message. If the field is present, it returns `null`, allowing normal webhook processing to continue. #### Example: Custom status codes You can return any HTTP status code, including non-standard or conflict codes: ```feel =if request.body.challenge != null then {"body": {"challenge": request.body.challenge}, "statusCode": 409} else null ``` #### Example: Custom headers You can include custom headers in your response: ```feel =if request.body.challenge != null then { "body": {"challenge": request.body.challenge}, "headers": {"Content-Type": "application/camunda-bin"} } else null ``` #### Example: Nested body structure You can access and respond to nested data structures in the request: ```feel =if request.body.event_type = "verification" then {"body": {"challenge": request.body.event.challenge}} else null ``` This example extracts the challenge from a nested `event` object and returns it in the response. ### Response expression :::note Prior to 8.6, the HTTP Webhook connector supported a response body expression. As of 8.6, this was replaced with a more powerful construct that allows control over not only the response body, but also the headers and the HTTP status returned by the connector. ::: #### Use the request You can use a response expression to return data after the webhook has been invoked. You can use FEEL to return the request body, headers, and the HTTP status to the client invoking the Webhook connector endpoint. For example, given a webhook request with the payload body: ```json { "myDataKey1": "myValue1", "myDataKey2": "myValue2" } ``` You can return `myValue1` in a new key `myCustomKey` with a response body expression such as: ```json ={ "body": {"myCustomKey": request.body.myDataKey1} } ``` The default HTTP status code is `200`. You can change it by including a `statusCode` key in your expression: ```json ={ "body": "hello", "statusCode": 201 } ``` Headers are also supported, using the `headers` key in the response expression: ```json ={ "headers": {"Content-Type": "text/html"}, "body": "Hello world!" } ``` When working with `request` data, use the following references to access data: - Body: `request.body.`. - Headers: `request.headers.`. - URL parameters: `request.params.`. You can also use FEEL expressions to modify the data you return. #### Use the `correlation` object When using the Webhook connector with a start event that correlates a message, you can access the `correlation` object in the response expression. In addition to the `request` object you have access to the `correlation` result. The data available via the `correlation` object depends on the type of BPMN element you are using the Webhook connector with. The following overview summarizes the different data points available when using the Webhook connector with different element types and configuration options. **`correlation` payload by response mode** The table below shows which `correlation` result type is produced for each supported BPMN element type and response mode. The result type determines what is available in the **response expression** context (`correlation` property) and in the HTTP response returned to the caller. *Element Type* Response mode: **Synchronous** Response mode: **Asynchronous** **Start Event** ``` { "processInstanceKey": 123, "tenantId": "abc", "variables": {...} } ``` The `variables` contain the result of the process execution. ``` { "processInstanceKey": 123, "tenantId": "abc" } ``` The result of the process execution is not available, but it can be fetched via the API using the `processInstanceKey`. **Message Start Event** **Intermediate Catch Event** **Boundary Event** **Receive Task** ``` { "processInstanceKey": 123, "messageKey": 123, "tenantId": "abc" } ``` The `processInstanceKey` contains the first process instance key with which the message correlated. ``` { "messageKey": 123, "tenantId": "abc" } ``` The message is buffered and only the `messageKey` is returned. Correlation to a subscription happens asynchronously in the engine. A start event with a message definition uses message publishing internally to correlate an incoming request with Zeebe. A successful correlation therefore publishes a message, and the `correlation` object contains the following properties when using the `asynchronous` response mode: ```json { "messageKey": 2251799813774245, "tenantId": "" } ``` If a Webhook request is processed more than once using the same _Message ID_ (for example, because of a retry), the `correlation` object is empty. A start event without a message creates a new process instance. You therefore have access to the newly created process instance key when accessing the `correlation` object: ```json { "processInstanceKey": 6755399441144562, "tenantId": "" } ``` If the `synchronous` response mode is selected, the response also contains the result `variables` from the execution. ```json { "processInstanceKey": 6755399441144562, "tenantId": "", "variables": {...} } ``` #### Use the `documents` object You can access created documents in both the response expression and the result expression. The `documents` object contains the references for created documents. See additional details and limitations in [document handling](/components/document-handling/getting-started.md). **Example response expression** ```json { "body": { "message": "Document created", "documents": documents } } ``` If the `documents` list is not empty, document items are returned in the following format (example values provided): ```json { "storeId": "in-memory", "documentId": "2b7215da-12b1-4374-8743-85d6854fcba5", "metadata": { "size": 405551, "expiresAt": null, "fileName": "my-image.jpg", "customProperties": null, "contentType": "image/jpeg" } } ``` :::note Request parts are automatically stored in the configured document store when sending a multipart request. ::: --- ## HTTP Polling connector The **HTTP Polling connector** polls an endpoint at regular intervals, enabling periodic data fetching as an intermediate step in your BPMN processes. This connector is built on top of the [REST connector](../protocol/rest.md), ensuring consistent functionality and reliability. :::caution If you use the HTTP Polling connector, ensure you do not have any process variable named in the list below, as these are reserved words for this connector: - body, url, method, headers, authentication, queryParameters, connectionTimeoutInSeconds, httpRequestInterval ::: ## Prerequisites Ensure that you have: - An HTTP endpoint that supports polling. - Necessary credentials if the endpoint demands authentication. :::note Execution Exception Handling If the HTTP Polling connector encounters an execution exception while polling, it will ignore the exception and attempt to execute the request again after the next interval delay. Ensure to monitor your logs for any recurring issues. ::: ## Setting up the HTTP Polling connector 1. Add an **Intermediate Event** to your BPMN diagram. 2. Change its template to the **HTTP Polling connector**. 3. Populate all mandatory fields, like the endpoint URL, polling interval, and required headers. 4. Complete your BPMN diagram. 5. Deploy the diagram to activate the **HTTP Polling connector**. ## Configuring the HTTP Polling connector ### Authentication Navigate to the **Authentication** section and select your desired **Authentication type** (e.g., Basic, OAuth). Refer to the [Authentication section of the REST connector documentation](/components/connectors/protocol/rest.md#authentication) for a comprehensive guide. ### HTTP polling configuration - **Method**: Choose the HTTP method for your request, e.g., GET, POST, PUT. - **URL**: Enter the URL of the targeted HTTP endpoint. - **Headers** (Optional): Input required headers as per the external service. Learn more about headers in the [REST connector headers](/components/connectors/protocol/rest.md#http-headers) section. - **Query Parameters** (Optional): Add necessary query parameters for the endpoint. More details can be found in the [REST connector query parameters](/components/connectors/protocol/rest.md#query-parameters) section. - **Interval** (Optional): Set the frequency for polling the endpoint in ISO 8601 durations format. The default interval is 50 seconds. Review [how to configure a time duration](../../modeler/bpmn/timer-events/timer-events.md#time-duration) for details. - **Connection Timeout**: Define how long (in seconds) the connector waits before timing out. Further information on this can be found [here](/components/connectors/protocol/rest.md#connection-timeout). ### Payload configuration (optional) In the **Payload** section, you can include a **request body**. Learn more about this [here](/components/connectors/protocol/rest.md#request-body). ### Condition to proceed 1. **Correlation key (process)**: Defines the correlation key based on the process instance. - **Example**: Using a process variable named `orderId`: ``` Correlation key (process): =orderId ``` 2. **Correlation key (payload)**: Extracts the correlation key from the polled data. - **Example**: With data like `{"orderId": "123"}`: ``` Correlation key (payload): =body.orderId ``` 3. **Activation Condition**: Checks if the polled data meets criteria to activate the intermediate catch event. - **Example**: If the data should have a `status` of "OK": ``` Activation Condition: =(body.status = "OK") ``` For more information about correlation keys, review the [messages guide](../../../concepts/messages). ## Handling HTTP connector responses The response from any HTTP connector contains the status, headers, and body. Learn more about the response structure [here](/components/connectors/protocol/rest.md#response). To structure and utilize the response: 1. Set a **Result Variable** to store the HTTP response, e.g., `pollingData`. 2. Use a **Result Expression** to extract specific fields from the `={fieldProperty:body.fieldProperty}`. ## Examples ### Scenario 1: Monitoring GitHub issues Monitor a GitHub issue to see when it's closed and if it has a specific label ('needs review'). #### Steps 1. Drag an intermediate event onto your BPMN diagram. 2. Choose the HTTP Polling connector template. 3. Configure the connector with the relevant details: - **URL**: `https://api.github.com/repos/[YourRepoOwner]/[YourRepoName]/issues/[IssueNumber]` - **Authorization Type**: Bearer token - **Bearer token**: `{{secrets.BEARER_TOKEN}}` - **Method**: `GET` - **Headers**: `={"Content-Type": "application/vnd.github+json","X-GitHub-Api-Version": "2022-11-28"}` - **Interval**: `PT10M` (Every 10 minutes) – This checks the GitHub issue every 10 minutes. - **Correlation Key (process)**: `=issueNumber` - **Correlation Key (payload)**: `=body.number` - **Activation Condition**: `=(body.state = "closed")` - **Result Expression**: `={issueUrl:body.html_url, needsReview: list contains((body.labels).name, "needs review")}` - Extract the issue URL and check if the label 'needs review' is present. #### Example response ```json { "status": 200, "body": { "number": 212, "title": "Important Issue", "labels": [{ "name": "bug" }, { "name": "needs review" }], "state": "closed", "html_url": "https://github.com/YourRepoOwner/YourRepoName/issues/212" } } ``` In this scenario, once the issue #212 titled **Important Issue** is closed, the process will proceed. If the issue is also labeled **needs review**, this label can be leveraged in the next steps of the process. For instance, it can trigger the creation of a new issue for review or initiate other related actions. ### Scenario 2: Monitoring product stock levels Suppose you're overseeing an e-commerce platform. It's vital to ensure certain popular products remain stocked to guarantee user satisfaction. Avoiding stock-outs is essential to prevent lost sales and keep customers happy. With Camunda's HTTP Polling connector, you can maintain a real-time stock level check. #### Steps 1. Drag an intermediate event onto your BPMN diagram. 2. Choose the HTTP Polling connector template. 3. Configure the connector as follows: - **URL**: `https://inventory.yourstore.com/api/v2/products/12345/stock` - **Authorization Type**: Basic Authentication - **Username**: `[YourInventoryAPIUsername]` - **Password**: `{{secrets.PASSWORD}}` - **Interval**: `PT1H` (Every hour) - **Correlation Key (process)**: `=productID` - **Correlation Key (payload)**: `=body.productID` - **Activation Condition**: `=(body.stockLevel < 10)` - **Result Expression**: `={stockLevelResponse:body.stockLevel}` #### Example response ```json { "status": 200, "body": { "productID": 12345, "productName": "Wireless Bluetooth Earbuds", "stockLevel": 8, "lastUpdated": "2023-09-17T11:20:32Z" } } ``` Whenever the stock level of this particular product goes below 10 units, the BPMN process can be set up to perform tasks such as notifying the supply chain, alerting marketing teams, or showcasing a "Low in Stock" badge on the product's webpage. ## Next steps - Dive deeper into the [REST connector](/components/connectors/protocol/rest.md) to understand its capabilities and configurations. - Explore [other connectors available](../out-of-the-box-connectors/available-connectors-overview.md) in Camunda to integrate with various systems and services. - Get a comprehensive understanding of how to use connectors in your BPMN processes [here](../use-connectors/index.md). - Learn about the specifics of inbound connectors and how they can be used [here](../use-connectors/inbound.md). --- ## REST connector :::caution If you use the REST connector, ensure you do not have any process variable named in the list below: - `body`, `url`, `method`, `headers`, `authentication`, `queryParameters`, `connectionTimeoutInSeconds`, `readTimeoutInSeconds` ::: The **REST connector** is an outbound protocol connector that allows you to make a request to a REST API and use the response in the next steps of your process. ## Create a REST connector task ## Make your REST connector executable To make the **REST connector** executable, choose the required authentication type and fill out the mandatory fields highlighted in red in the properties panel on the right side of the screen. :::note All the mandatory and non-mandatory fields will be covered in the upcoming sections. Depending on the authentication selection you make, more fields might be required. We will also cover this in the next section. ::: ### Configure a proxy server in Self-Managed In Self-Managed environments, you can configure the connector runtime to route HTTP requests through a proxy server using JVM properties or environment variables. See [HTTP proxy configuration](/self-managed/components/connectors/http-proxy-configuration.md) for details. ### Authentication You can choose among the available authentication type according to your authentication requirements. ### REST connector (None) Click **None** in the **Authentication** section. No extra authentication configuration is required; you can jump to the [next section](#request). ### REST connector (API key) For services that require an API key for authentication, you can configure the REST connector to include your API key in the request. #### Create a new connector secret We advise you to keep your **API key** safe and avoid exposing it in the BPMN `xml` file by creating a secret: 1. Follow our [guide for creating secrets](/components/hub/organization/manage-clusters/manage-secrets.md). 2. Name your secret (i.e `REST_API_KEY_SECRET`) so you can reference it later in the connector. ### Configure API key authentication Select the **REST connector** and fill out the following properties under the **Authentication** section: 1. In the **Authentication** section, select **API key**. 2. Choose the location where the API key should be included: - **Query parameters**: The API key will be added to the URL as a query string. - **Headers**: The API key will be included in the request headers. 3. Specify your API key details: - **API key name**: Enter the parameter name expected by the API (e.g., apiKey). - **API key value**: Reference the secret you created for your API key (e.g., `{{secrets.REST_API_KEY_SECRET}}`). ### REST connector (Basic) ##### Create a new connector secret We advise you to keep your **Password** safe and avoid exposing it in the BPMN `xml` file by creating a secret: 1. Follow our [guide for creating secrets](/components/hub/organization/manage-clusters/manage-secrets.md). 2. Name your secret (i.e `REST_BASIC_SECRET`) so you can reference it later in the connector. ### Configure Basic Authentication Select the **REST connector** and fill out the following properties under the **Authentication** section: 1. Click **Basic** in the **Authentication** section. 2. Set **Username** (i.e. `{{secrets.YOUR_USERNAME}}`). 3. Set **Password** to the secret you created (i.e. `{{secrets.REST_BASIC_SECRET}}`). ### REST connector (Bearer Token) #### Create a new connector secret We advise you to keep your **Bearer token** safe and avoid exposing it in the BPMN `xml` file by creating a secret: 1. Follow our [guide for creating secrets](/components/hub/organization/manage-clusters/manage-secrets.md). 2. Name your secret (i.e `REST_BEARER_TOKEN`) so you can reference it later in the connector. #### Configure the Bearer token Select the **REST connector** and fill out the following properties under the **Authentication** section: 1. Click **Bearer token** in the **Authentication** section. 2. Set **Bearer** to the secret you created (i.e. `{{secrets.REST_BEARER_TOKEN}}`). ### REST connector (OAuth token) #### Create a new connector secret We advise you to keep your **OAUTH_TOKEN_ENDPOINT** safe and avoid exposing it in the BPMN `xml` file by creating a secret: 1. Follow our [guide for creating secrets](/components/hub/organization/manage-clusters/manage-secrets.md). 2. Name your secret (i.e `OAUTH_TOKEN_ENDPOINT`) so you can reference it later in the connector. #### Configure the OAuth token Select the **REST connector** and fill out the following properties under the **Authentication** section: 1. Click **OAuth 2.0** in the **Authentication** section. 2. Set **OAuth token endpoint** to the secret you created (i.e. `{{secrets.OAUTH_TOKEN_ENDPOINT}}`). 3. Set **Client ID** to the secret you created (i.e. `{{secrets.CLIENT_ID}}`). 4. Set **Client secret** to the secret you created (i.e. `{{secrets.CLIENT_SECRET}}`). 5. (Optional) Set **Scopes** (i.e. `read:clients`). Depending on the OAuth provider you're using, this may or may not be required. 6. Set **Audience** to the secret you created (i.e. `{{secrets.AUDIENCE}}`). It is an optional field. Depending on the OAuth provider you're using, you should fill this field or not. 7. Choose **Client authentication** from the dropdown menu (i.e. `Send client credentials in body`). Find more information about the OAuth client credentials flow at the [RFC reference](https://www.rfc-editor.org/rfc/rfc6749#section-4.4). ## Request Under the **HTTP Endpoint** section, select the desired **Method** and fill the **URL** with your desired REST API. ### Query parameters The **Query parameters** field can be configured using the [FEEL Map](/components/modeler/feel/language-guide/feel-data-types.md#context) data type. ```text = { q: "Berlin", appid: "{{secrets.OPEN_WEATHER_MAP_API_KEY}}", units: "metric", lang:"en" } ``` :::note Secrets are not like regular variables and must be wrapped in double quotes (`"`) when used in an expression. ::: ### HTTP Headers Similarly to the Query Parameters, the **HTTP headers** can be specified using the [FEEL Map](/components/modeler/feel/language-guide/feel-data-types.md#context) data type. ``` = { Origin: "https://modeler.camunda.io/" } ``` #### Content-Type If you do not set the `Content-Type` header in your HTTP headers, the connector will automatically set the `Content-Type` to `application/json`. If you set the `Content-Type` header to `multipart/form-data`, only body fields that are documents or plain strings will be sent as multipart form data. All other fields, such as JSON objects, will be ignored. ### Request body When you are making a PUT, POST, or PATCH request, you might need to provide a body. You can provide a body for your request under the **Payload** section in the **Request body** field. ``` = { "temp": 25, "pressure": 1013, "humidity": 44, "temp_min": 16, "temp_max": 30 } ``` #### File upload To upload a file, you can take advantage of [Camunda document handling](/components/document-handling/getting-started.md). Depending on the `Content-Type`, the file will be uploaded as a binary or a JSON object (base64 encoded). - **Binary**: The file will be uploaded as a binary object. The `Content-Type` header **must** be set to `multipart/form-data`. The body must a map, where the key is the name of the file field and the value is a document reference. ![connectors-rest-upload](../../../images/connectors/connectors-rest-upload.png) - **JSON**: The file will be uploaded as a JSON object. The `Content-Type` header **must** be set to `application/json` (this is the default). The body must be a map, where the key is the name of the file field and the value is a document reference, similar to the binary upload. The file will be **base64 encoded** and included in the JSON object. ### Allow redirects - **Follow redirects**: When enabled, the connector automatically follows HTTP 3xx redirect responses, resolving the final destination URL. When disabled (default), the connector returns the original 3xx response including the `Location` header, leaving redirect handling to your process logic. ### Encoding In certain scenarios, such as when working with APIs that require pre-encoded URL elements, the REST connector's default behavior may inadvertently modify encoded segments. To avoid this, set the `skipEncoding` value to `"true"` in the XML. This disables the automatic decoding and re-encoding process, ensuring the URL is sent to the server exactly as provided. ### Network communication timeouts - **Connection timeout in seconds** determines the time frame in which the client will try to establish a connection with the server. If you do not specify a value, the system uses the default of 20 seconds. For cases where you need to wait indefinitely, set this value to 0. - **Read timeout in seconds** is the amount of time the client will wait to read data from the server after the connection has been made. The default is also set to 20 seconds. To allow an unlimited wait time for slow responses, set this to 0. ## Response The following variables are available in the context of the response expression: - **status**: Response status. - **body**: Response body of your request. - **headers**: Response headers. - **document**: If the `Store response` checkbox is selected, this field represents the stored document: - **documentId**: The ID of the stored document. - **contentHash**: The hash of the stored document. - **storeId**: The store ID. - **metadata**: Metadata of the stored document (if available). - **size**: Size of the stored document (in bytes). - **expiresAt**: Expiration date of the stored document. - **fileName**: Name of the stored document. - **customProperties**: Custom properties of the stored document. - **contentType**: Content type of the stored document. :::note Starting from version 8.7.0, the REST connector supports storing the response as a document. See additional details and limitations in [document handling](/components/document-handling/getting-started.md). ::: ## Output mapping ### Result variable You can export a complete response from an HTTP REST call into a dedicated variable accessible anywhere in a process. To do so, just input a variable name in the **Result variable** field. We recommend using a unique name to avoid variables being overwritten, for example `currentWeather`. ## Result expression Additionally, you can choose to unpack the content of your `response` into multiple process variables using the **Result expression**, which is a [FEEL Context Expression](/components/modeler/feel/language-guide/feel-context-expressions.md). ``` = { actual_temp: response.body.main.temp, feel_temp: response.body.main.feels_like, weather: response.body.weather[1].main, weather_report_id: response.document.documentId } ``` ## Error handling If an error occurs, the connector throws an error and includes the error response in the `error` variable in Operate. Click on the REST connector in Operate to see this variable. The following example shows the `error` variable in an error response: ```json { "code": "400", "variables": { "response": { "headers": { "Content-Length": "70", "Date": "Thu, 17 Oct 2024 09:31:51 GMT", "Content-Type": "application/json" }, "body": { "temperature": 36, "message": "My custom error message", "booleanField": true } } }, "message": "Bad Request", "type": "io.camunda.connector.api.error.ConnectorException" } ``` You can handle this error using an Error Boundary Event and the following error expression: ```json if matches(error.code, "400") and error.variables.response.body.temp = 36 then bpmnError("Too hot", error.variables.response.body.message, error.variables.response.body) else null ``` In this example, passing `error.variables.response.body` as the third argument to the `bpmnError` function allows you to pass additional information about the error to the error boundary event. For example, the `message`, `temperature` and `booleanField` fields from the error response are passed to the error boundary event. ## OData support The **REST connector** supports JSON-based [OData protocol](https://www.odata.org/). ### Requesting resources Requesting resources works the same way regular REST requests work: 1. Under the **HTTP Endpoint** section, select the desired **Method**. 2. Fill in the **URL** with your desired REST API. 3. Depending on your provider, you may also need to set `OData-Version` and `OData-MaxVersion` headers. ### Requesting an individual resource Similar to requesting resources, to request an individual resource you need to have a process variable. Use a FEEL string concatenation function when building **URL**, e.g. `="https://my.odata.service/v4/Service/Resource('" + resourceId + "')"`. ### Queries Additionally, if your provider supports OData queries, such as `$top`, you can use these when defined in the **Query parameters** field, e.g. `={"$top": 3, "$select": "FirstName, LastName"}`. --- ## SOAP connector :::note The **SOAP connector** is only supported by Self-Managed and Hybrid Camunda 8 instances. ::: **Simple Object Access Protocol (SOAP)** is a messaging protocol specification for exchanging structured information in the implementation of web services in computer networks. The **SOAP connector** allows you to interact with [SOAP](https://www.w3.org/TR/soap/) service endpoints from your BPMN process. ## Prerequisites To use the **SOAP connector**, ensure you have an active SOAP service. ## Create a SOAP connector task ## Connection Enter your SOAP service URL in the field **Service URL**, for example `https://myservice.com/service/MyService.wso`. ## Authentication Select the authentication type from the **Authentication** dropdown. ### None Use **None** if the SOAP service does not require authentication. ### WSS username token Use **WSS username token** in the **Authentication** dropdown when the requested SOAP endpoint requires [username token extension](https://docs.oasis-open.org/wss/v1.1/wss-v1.1-spec-pr-UsernameTokenProfile-01.htm#_Toc104276211). Enter **Username**, **Password**, and indicate if the password is encoded. :::note The **SOAP connector** currently supports only `SHA-1` password encoding. ::: ### WSS signature Use the **WSS signature** in the **Authentication** dropdown when the requested SOAP endpoint requires a message to be cryptographically signed with a [signature](http://docs.oasis-open.org/wss-m/wss/v1.1.1/cs01/wss-SOAPMessageSecurity-v1.1.1-cs01.html#_Toc307407954). Enter all necessary fields according to your service specification. ## SOAP message ### SOAP version Select the desired version of the SOAP service. ### SOAPAction HTTP header Enter the SOAPAction HTTP header that will be used in the request. Leave this value blank if the SOAPAction HTTP header won't be used in your request. This field is only required by SOAP version 1.1. ### SOAP header From the dropdown, select whether the **SOAP header** is required, and if so, in which format you wish to provide it. ### SOAP body From the **SOAP body** dropdown, select whether you will provide the SOAP request body in a form of **Template**, or **XML compatible JSON**. #### Template When **Template** is chosen, enter the **XML template** value, for example `{{paramValue}}`. Enter the **XML template context** value, for example `={paramValue: 1234567890}`, and enter the **Namespaces** value, for example `={"camunda":"http://my.service.com/webservicesserver/"}`. #### XML compatible JSON When **XML compatible JSON** is chosen, enter the **JSON definition**, for example ```json = { "camunda:Object01": { "camunda:Object02": myObjectValue } } ``` Enter the **Namespaces** value, for example `={"camunda":"http://my.service.com/webservicesserver/"}`. ## Output mapping ### Result variable You can export a complete response from a SOAP call into a dedicated variable accessible anywhere in a process. To do so, input a variable name in the **Result variable** field. Use a unique name to avoid overwriting variables. A typical response may look like as follows: ```json { "Envelope": { "Header": { "MyHeader": "Header value" }, "Body": { "MyResponseObject": { "MyResponseObjectField": "My result value" } } } } ``` ### Result expression Additionally, you can choose to unpack the content of your `response` into multiple process variables using the **Result expression**, which is a [FEEL Context Expression](/components/modeler/feel/language-guide/feel-context-expressions.md). Given SOAP service response that looks like as follows: ```json { "Envelope": { "Header": { "MyHeader": "Header value" }, "Body": { "MyResponseObject": { "MyResponseObjectField": "My result value" } } } } ``` To extract the `MyResponseObjectField` value into its own variable, you can do: ``` = { MyResponseObjectResult: response.Envelope.Body.MyResponseObject.MyResponseObjectField } ``` ## Usage examples ### Example 1 For example, imagine you want to send the following SOAP request: URL: `https://myservice:8888/webservice.wso` Body: ```xml 12345 ``` In your BPMN diagram, set the field **Service URL** as `https://myservice:8888/webservice.wso`, and **SOAP body** as: ```json { "Object01": { "Object02": 12345 } } ``` ### Example 2: Pre-defined namespaces Consider a namespace is defined within your objects, and you want to send the following request: URL: `https://myservice:8888/webservice.wso` Body: ```xml 12345 ``` In your BPMN diagram, set the field **Service URL** as `https://myservice:8888/webservice.wso`, and **SOAP body** as: ```json { "ns:Object01": { "ns:Object02": 12345 } } ``` :::note Here, we introduced a new `ns:` prefix. The prefix can be any arbitrary string that is not defined as a namespace. ::: Now, you'll need to associate a namespace. Set the following value at the **Namespaces** field. For the given example, it should be set as: ```json { "ns": "http://www.my.namespace.com/namespace/" } ``` ### Example 3: Using templates As an alternative, you can use templates to send SOAP messages. URL: `https://myservice:8888/webservice.wso` Body: ```xml 12345 ``` Set the **SOAP body** dropdown to **Template**. In the **XML template** field, define the template. For example: ```xml {{myObjectValue}} ``` In the **XML template context** field, define context JSON. For example: ```json { "myObjectValue": 12345 } ``` --- ## Integrate a built-in connector The launch of [Camunda 8](/components/components-overview.md) also introduced an integration framework with a key goal: integrate faster to reduce the time it takes to automate and orchestrate business processes across systems. :::note New to connectors? Review our [introduction to connectors](/components/connectors/introduction.md) to get familiar with their capabilities. ::: [Connectors](/components/connectors/introduction.md) achieve this goal. Ready to use out of the box, connectors help automate complex [business processes](/components/concepts/processes.md) by inserting them into [BPMN diagrams](/components/modeler/bpmn/automating-a-process-using-bpmn.md) within [Web Modeler](/components/modeler/about-modeler.md), and configuring them via the properties panel on the right side of the screen. You can also orchestrate APIs, for example by working with a [REST connector](/guides/getting-started-orchestrate-apis.md). Learn more about [types of connectors](/components/connectors/connector-types.md). Connectors technically consist of two parts: the business logic is implemented as a [job worker](/components/concepts/job-workers.md), and the user interface during modeling is provided using an element template. In this guide, we'll walk step-by-step through the implementation of a sample connector. ## Set up We'll implement our connector with [Modeler](/components/modeler/about-modeler.md). To get started, ensure you’ve [created a Camunda 8 account](/components/hub/organization/manage-organization-settings/manage-plan/create-account.md). You'll also need to [create a SendGrid account](https://signup.sendgrid.com/) if you don't have one already, as we'll use SendGrid in our example connector. Once you've created your account, you will immediately be prompted to create a [sender](https://docs.sendgrid.com/ui/sending-email/senders). ### Create a cluster ## Getting started Once logged in to your Camunda 8 account, take the following steps: 1. From Modeler, click **New project > Create new > BPMN diagram**. 2. Name your project by replacing the **New Project** text at the top of the page. In this example, we'll name ours `Expense process`. 3. Select **Create new > BPMN diagram**. 4. Give your model a descriptive name by replacing the **New BPMN Diagram** text at the top of the page. Then, give your model a descriptive ID within the **General** tab inside the properties panel on the right side of the screen. In this case, we've named our model `Submit expense` with an ID of `submitting-expense`. ## Build a BPMN diagram Use Web Modeler to design a BPMN flow with the appropriate tasks. To get started, create a task by dragging the rectangular task icon from the palette, or click the existing start event and the displayed task icon. In this example, we've designed the following BPMN diagram: ![bpmn example diagram](./img/bpmn-expense-sample.png) :::note To learn more about building your own BPMN diagram from scratch, visit our guide on [automating a process using BPMN](/components/modeler/bpmn/automating-a-process-using-bpmn.md). ::: ## Add a connector Here, a receipt is initially uploaded for review. The first task we need to complete is notifying the manager of the uploaded receipt. If we want to leverage our email service to notify the manager, we can utilize a productivity applications connector to replace this task. :::note Camunda offers a variety of available connectors. For example, utilize cloud connectors to communicate with cloud-native applications and conform to REST, GraphQL, or SOAP protocols. Or, employ service connectors to integrate with technology enablers like RPA, AI or IOT services. Learn more about our [available connectors](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md) to find out which may best suit your business needs. ::: To add our productivity applications connector, take the following steps: 1. Click the start event. A context pad to the right of the start event will appear. 2. Click the **Append connector** item in the panel. 3. To send an email via SendGrid, for example, select the **SendGrid Email connector** option. Name this newly-created task `Notify manger of receipt`. This now replaces our original task. ![adding a connector](./img/adding-connector.png) 4. You need to fill out the required information in the properties panel of this task on the right side of the screen. Here, we'll add an example API key obtained from our [SendGrid account](https://app.sendgrid.com/settings/api_keys), a sender and receiver name and email address, and the email message content. ![filling out connector properties panel](./img/connector-properties-panel.png) Our connector is now attached and ready to use. Your completed diagram should look like the following: ![completed connectors and BPMN diagram](./img/connectors-bpmn-diagram.png) ## Execute your process diagram :::note If you change a diagram and it is auto-saved, this has no effect on your cluster(s). When you deploy the diagram, it becomes available on the selected cluster and new instances can start. ::: To execute your completed process diagram, click **Deploy**. You can now start a new process instance to initiate your process diagram. Click **Run**. You can now monitor your instances in [Operate](/components/operate/operate-introduction.md). :::note Variables are part of a process instance and represent the data of the instance. To learn more about these values, variable scope, and input/output mappings, visit our documentation on [variables](/components/concepts/variables.md). ::: ## Observe your running process After the [user task](/guides/getting-started-orchestrate-human-tasks.md) **Upload receipt** is completed in [Tasklist](/components/tasklist/introduction-to-tasklist.md), an email is automatically sent to the address as specified in the connectors properties panel we configured earlier. ![email via SendGrid](./img/sendgrid-email.png) In [Operate](/components/operate/operate-introduction.md), you will now see the process move forward to **Review receipt**. ![operate example](./img/operate-example.png) ## Additional resources and next steps - [Use connectors in your BPMN process](/components/connectors/use-connectors/index.md) - [Available connectors](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md) - [Connectors & Integration Framework](https://camunda.com/platform/modeler/connectors/) - [Camunda BPMN Tutorial](https://camunda.com/bpmn/) - [Camunda Academy: How To Configure the SendGrid Connector](https://academy.camunda.com/c8-h2-sendgrid-connector/) --- ## Use an inbound connector [Inbound connectors](/components/connectors/connector-types.md#inbound-connectors) enable workflows to receive data or messages from external systems or services. Review our [list of existing inbound connectors](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md) for more information. ## Creating the connector event Inbound connectors are modeled as **catch events** in [BPMN](/components/modeler/bpmn/bpmn.md). Connectors can be modeled as: - **Start events** – Start a new process instance. - **Message start events** – Start a new process instance with a `messageId` to prevent duplicate process instance creation. - **Intermediate catch events** – Receive messages in an already running process instance. - **Boundary events** – Receive messages in an already running process instance, attached to a task. - **Boundary events** – Receive messages in an already running process instance, attached to a task. - **Receive tasks** - Receive messages in an already running process instance. :::info If **idempotency** is a concern for the process creation and reprocessing of messages should never lead to a duplicate process instance creation, use the **message start event** element for an inbound connector as it relies on publishing a message. Unlike plain **start event** elements, **message start events** support the **message ID expression** property that allows you to derive a unique value from the connector output. This value is used by Zeebe to [guarantee uniqueness](/components/concepts/messages.md#message-uniqueness) in case other messages are published using the same **Message ID**. ::: When you **deploy** such a BPMN diagram with an inbound connector, the connector becomes ready to receive incoming requests. The outcome depends on the connector type: - **Webhook connectors** become available via the **webhook endpoint**. - **Subscription connectors** start listening to the **message queue**. - **Polling connectors** start polling the **external system**. ### Modeling the connector start event 1. Start building your BPMN diagram with a start event building block. 2. Change its template to an inbound connector of your choice (e.g., HTTP webhook, or a message queue subscription). 3. Fill in all required properties. 4. Complete your BPMN diagram. 5. Deploy it to your Camunda 8 instance. :::note You can still start instances of that process manually via Modeler, which is sometimes useful during testing. ::: ### Modeling the connector message start event 1. Start building your BPMN diagram with a message start event (non-interrupting). 2. Change its template to an inbound connector of your choice (for example, HTTP webhook or a message queue subscription), by clicking the **Select** button in the **Template** section in the **Properties** sidebar of the start event element. 3. Fill in all required properties. 4. Configure the **Correlation** section, if an event subprocess is used. - If you are setting up a non-interrupting message start event for a subprocess, select **Correlation required** and specify the **Correlation key (process)** and **Correlation key (payload)** values. - If you are setting up a message start event for a regular process (not a subprocess), skip the correlation settings. 5. Complete your BPMN diagram. 6. Deploy it to your Camunda 8 instance. ### Modeling the connector intermediate message catch event 1. Start building your BPMN diagram with an **Intermediate Catch Event** building block. 2. Change its template to an inbound connector of your choice (e.g., HTTP webhook, or a message queue subscription). 3. Fill in all required properties. 4. Complete your BPMN diagram. 5. Deploy it to your Camunda 8 instance. ### Modeling the connector boundary event 1. Start building your BPMN diagram with any **Task** building block. 2. Attach a **Boundary event** to a **Task** at your diagram. 3. Change its template to an inbound connector of your choice (e.g., HTTP webhook, or a message queue subscription). 4. Fill in all required properties. 5. Complete your BPMN diagram. 6. Deploy it to your Camunda 8 instance. ### Modeling the connector receive task 1. Start building your BPMN diagram with an **Receive Task** building block. 2. Change its template to an inbound connector of your choice (e.g., HTTP webhook, or a message queue subscription). 3. Fill in all required properties. 4. Complete your BPMN diagram. 5. Deploy it to your Camunda 8 instance. ### Example: Configuring an HTTP webhook Different connector types have different configuration options, but parts like **Result expression**, or **Correlation key** are common for all of them. Let's take a look at an example of configuring an HTTP webhook. To deploy and use the webhook, you need to fill in several fields: 1. **Webhook method** - HTTP method for your inbound webhook. You can either set a specific one or choose `Any` if all methods need to be supported. 2. **Webhook ID** - Context path for your inbound webhook. This is used to build a URL endpoint for your webhook. For example, given the `Webhook ID` value is `myWebhookPath`, the complete webhook URL endpoint will be `http(s):///inbound/myWebhookPath`. 3. **HMAC authentication** - If an external caller uses HMAC as a means of request validation and authentication, you can `enable` this property. In that case, you'll need to specify additional field values. Read more about the [generic HTTP webhook configuration](/components/connectors/protocol/http-webhook.md). 4. **Authorization** - Authorization method of the webhook. 5. **Activation condition** - FEEL expression that assesses trigger conditions. Note: Unlike other properties, in the activation condition, you cannot use the process variables. For example, given external caller triggers a webhook endpoint with body `{"id": 1, "status": "OK"}`, the **Activation Condition** value might look like `=(request.body.status = "OK")`. Leave this field empty to trigger your webhook every time. 6. **Result variable** - Name of the process variable that will be created or updated with the result of the webhook. For example, if you want to save the result of the webhook in a variable called `myDocumentId`, you would specify `myDocumentId` as the **Result variable** value. 7. **Result expression** - FEEL expression that transforms incoming body into BPMN process variables. For example, given external caller triggers a webhook endpoint with body `{"id": 1, "status": "OK"}` and you would like to extract `id` as a process variable `myDocumentId`. In that case, the **Variable mapping** might look as `={myDocumentId: request.body.id}`. 8. **Response body expression** - FEEL expression that forces a webhook to return a specific response. This might be useful for one-time challenge verification, or acknowledgement response. Given your webhook triggered with body `{"id": 1, "status": "OK"}`, if you wish to return acknowledgement, you can specify the following expression `={message: "received document ID " + string(request.body.id)}` which will produce `{"message":"received document ID 123"}` as a response. Another example, when you wish to return a one-time subscription challenge. Given your webhook triggered with body `{"event": "subscribe", "challenge":"myRandomChallenge"}`. You can return challenge back with the following expression `=if request.body.event = "subscribe" then request.body.challenge else null` which will produce a plain string `"myRandomChallenge"` as a response. If the Webhook connector is applied to an **intermediate catch event**, you also need to specify the following fields: 9. **Correlation key (process)** - a FEEL expression that defines the correlation key for the subscription. This corresponds to the **Correlation key** property of a regular **message intermediate catch event**. 10. **Correlation key (payload)** is a FEEL expression used to extract the correlation key from the incoming message. This expression is evaluated in the connector Runtime and the result is used to correlate the message. For example, given that your correlation key is defined with `requestIdValue` process variable, and the request body contains `{"request": {"id": 123}}`, your correlation key settings will look like this: - **Correlation key (process)**: `=requestIdValue` - **Correlation key (payload)**: `=request.body.request.id` See the [webhook documentation](/components/connectors/protocol/http-webhook.md) or the documentation of [other connector types](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md) for more details. ## Consume unmatched events You can configure a connector to consume all unmatched events from the event source by enabling the **Consume unmatched events** checkbox in the **Activation** section of the connector properties. - When this option is enabled, the connector will consume all events that do not match the activation condition of any other connector with the same deduplication ID. This is useful in scenarios where you want to ensure that all events are processed, even if they do not match any specific activation condition. - When this option is disabled, the connector will only consume events that match its activation condition. Events that do not match any activation condition will lead to an error. Here are some examples of how this option will affect the behavior of the connector when enabled or disabled, when the activation conditions of all the connectors with the same deduplication ID do not match the incoming event: Connector Consume unmatched events ✅ Enabled ◻️ Disabled Webhook Connector Return a success response (200) Return an error response (422, or specific HTTP status code depending on the error) Kafka Connector Commit the message offset Do not commit the message offset RabbitMQ Connector Acknowledge the message Reject the message AWS SQS Connector Delete the message from the queue Do not delete the message from the queue Email Connector Mark the email as processed (e.g. marked as read, deleted, or moved) Do not mark the email as processed ## Working with request context You can access request context in the **Activation condition**, **Result expression**, and **Response body expression**. Let's consider the following cURL query: `curl -X POST -H "Content-Type: application/json" -H "MyHeader: myValue" -d '{"status": "OK", "id": 123}' "http:///inbound/myWebhook?param1=val1"`. A webhook connector context data will arrive as follows: ```json { "request": { "body": { "status": "OK", "id": 123 }, "headers": { "host": "YOUR_HOST", "user-agent": "curl/7.88.1", "accept": "*/*", "content-type": "application/json", "myheader": "myValue", "content-length": "27" }, "params": { "param1": "val1" } }, "connectorData": {} } ``` This means in scope of the fields **Activation condition**, **Result expression**, and **Response body expression**, you can use not only `request.body.` but also access headers via `request.headers.myheader` or params `request.params.param1`. There is also a connector-specific special case of `connectorData` that is usually empty and used in rare cases, when body has to be crafted in a special way, but a connectors user might still want access context data. See a list of [available inbound connectors](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md) and their respective specific configuration instructions. --- ## How to use connectors Any task can be transformed into a connector task. This guide details the basic functionality all connectors share. Find available connectors in [out-of-the-box connectors](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md). To add connectors from your BPMN diagram, visit the [Camunda Marketplace](/components/hub/workspace/modeler/modeling/camunda-marketplace.md). :::note Learn how to [install connectors in Self-Managed](/self-managed/components/connectors/overview.md). New to modeling with Camunda? The steps below assume some experience with Camunda modeling tools. [Model your first diagram](/components/hub/workspace/modeler/modeling/model-your-first-diagram.md) to learn how to work with Web Modeler. ::: ## Using secrets :::danger `secrets.*` is a deprecated syntax. Instead, use `{{secrets.*}}` ::: You can use sensitive information in your connectors without exposing it in your BPMN processes by referencing secrets. Use Camunda Hub to [create and manage secrets](/components/hub/organization/manage-clusters/manage-secrets.md). You can reference a secret like `MY_API_KEY` with `{{secrets.MY_API_KEY}}` in any connector field in the properties panel that supports this. Each of the [out-of-the-box connectors](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md) details which fields support secrets. Secrets are not variables and must be wrapped in double quotes as follows when used in a FEEL expression: ```feel = { myHeader: "{{secrets.MY_API_KEY}}"} ``` Using the secrets placeholder syntax, you can use secrets in any part of a text, like in the following FEEL expression: ```feel = "https://" + baseUrl + "/{{secrets.TENANT_ID}}/accounting" ``` This example assumes there is a process variable `baseUrl` and a configured secret `TENANT_ID`. The engine will resolve the `baseUrl` variable and pass on the secrets placeholder to the connector. Assuming the `baseUrl` variable resolves to `my.company.domain`, the connector receives the input `"https://my.company.domain/{{secrets.TENANT_ID}}/accounting"`. The connector then replaces the secrets placeholder upon execution. For further details on how secrets are implemented in connectors, consult our [Connector SDK documentation](/components/connectors/custom-built-connectors/connector-sdk.md#secrets). :::warning `secrets.*` is a reserved syntax. Don't use this for other purposes than referencing your secrets in connector fields. Using this in other areas can lead to unexpected results and incidents. ::: ## Variable and response mapping When a connector is expected to return a result, connectors feature a dedicated section known as **Response Mapping**, comprising two essential fields: **Result Variable** and **Result Expression**. These fields export responses from external connector calls into process variables. ### Result variable This field declares a singular process variable designated for the export of responses from a connector call. The resulting process variable can be subsequently utilized within the ongoing process. #### Example If you set `result` inside the **Result Variable** field of the REST outbound connector, this variable is available: ```json { "result": { "status": 200, "headers": { "date": "Thu, 03 Apr 2025 07:05:19 GMT", "server": "nginx", "content-type": "text/html; charset=UTF-8" }, "body": { "orderNumber": "1234", "date": "2025-04-01", "customerId": "567", "address": { "streetAddress": "1234 Elm Street", "city": "Paris", "state": "CA", "postalCode": "90210", "country": "USA" } }, "reason": "OK", "document": null } } ``` ### Result expression This field facilitates the mapping of a connector response into multiple process variables, providing further flexibility of the variable utilization within the ongoing process. Additionally, the extracted values can be transformed with [FEEL expressions](/components/concepts/expressions.md). To ensure process isolation, note that connectors do not have access to process variables. :::note While using this field, a process variable with the name `response` is reserved. It should only be used when a connector returns atomic values like a string or a number. ::: #### Example If you set `{ "bodyReceived": body }` inside the **Result Expression** field of the REST outbound connector, this variable is available: ```json { "bodyReceived": { "orderNumber": "1234", "date": "2025-04-01", "customerId": "567", "address": { "streetAddress": "1234 Elm Street", "city": "Paris", "state": "CA", "postalCode": "90210", "country": "USA" } } } ``` ### Example Imagine your connector makes an external call to an arbitrary weather service. The weather service returns the following response: ```json { "status": 200, "headers": { "date": "Thu, 19 Jan 2023 14:02:29 GMT", "transfer-encoding": "chunked", "content-type": "application/json; charset=utf-8", "connection": "keep-alive" }, "body": { "latitude": 52.52, "longitude": 13.4, "generationtime_ms": 0.22804737091064453, "utc_offset_seconds": 0, "timezone": "GMT", "timezone_abbreviation": "GMT", "elevation": 45.0, "current_weather": { "temperature": 1.0, "windspeed": 10.1, "winddirection": 186.0, "weathercode": 2, "time": "2023-01-19T14:00" } } } ``` If you declare a variable `myWeatherResponse` in the **Result Variable** field, the entire response is mapped to the declared variable. Now, let's imagine that you wish to extract only temperature into a process variable `berlinWeather` and wind speed into `berlinWindSpeed`. Let's also imagine you need weather in Fahrenheit declared in `berlinWeatherInFahrenheit`. In that case, you could declare **Result Expression** as follows: ``` = { berlinWeather: response.body.current_weather.temperature, berlinWindSpeed: response.body.current_weather.windspeed, berlinWeatherInFahrenheit: response.body.current_weather.temperature * 1.8 + 32 } ``` ## Activation The **Activation** section pertains specifically to [inbound connectors](/components/connectors/connector-types.md). ### Activation condition The **Activation condition** field evaluates conditions against the incoming message payload. It enables filtering of payloads that can initiate a process. If left empty, all valid incoming messages will trigger a new process—except those that fail pre-validation checks, such as HMAC signature verification for specific connectors. ## Correlation ### Correlation key (process) The **Correlation key (process)** field specifies which variable from the connector output should serve as the process correlation key. Learn more about [message correlation](/components/concepts/messages.md#message-correlation-overview). ### Correlation key (payload) The **Correlation key (payload)** field tells the connector how to extract the correlation value from the incoming message payload. ### Message ID expression The **Message ID expression** field defines how to extract a unique identifier from the incoming message payload. Messages that share the same identifier within the defined time-to-live (TTL) will be correlated only once. Leaving this field empty may cause identical messages to be submitted and processed multiple times. ## BPMN errors and failing jobs {#bpmn-errors} Being able to deal with exceptional cases is a common requirement for business process models. Read more about our general best practices around this topic in [dealing with problems and exceptions](/components/best-practices/development/dealing-with-problems-and-exceptions.md). Connectors share this requirement for exception handling like any other task in a model. However, connectors define reusable runtime behavior that is not aware of your specific business use case. Thus, they cannot determine if an exceptional case is a technical or business error. Therefore, a connector's runtime behavior cannot throw BPMN errors, but only technical errors. However, those technical errors can optionally contain an error code as structured data that can be reused when configuring a connector task. :::note There may be situations where technical errors cannot be detected by the runtime and they must be thrown explicitly. ::: ### Error expression To support flexible exception handling, the [out-of-the-box connectors](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md) allow users to define an **Error Expression** in the **Error Handling** section at the bottom of the properties panel. The example below uses this property to automatically inform the right group of people depending on the result of an HTTP request against an internal website. If the website returns a valid result, this data is passed on to the regular team. In case of a [404](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404) website response, the administrator is informed, so they can check why the website cannot be reached. HTTP responses with status [500](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) indicate internal website errors, which is why the website team is informed. ![feel connectors](../img/use-connectors-error-general.png) The **Error Expression** property requires a [FEEL](/components/modeler/feel/what-is-feel.md) expression that yields a BPMN error object in the end. The BPMN error object can be an empty [context](/components/modeler/feel/language-guide/feel-data-types.md#context), [null](/components/modeler/feel/language-guide/feel-data-types.md#null), or a context containing at least a non-empty `errorType` and a non-empty `code` if the error type is `bpmnError`. You can use all available functionality provided by FEEL to produce this result. Use the provided FEEL functions: - [`bpmnError`](#function-bpmnerror) to create a BPMN error object. This triggers a [ThrowError call](/components/best-practices/development/dealing-with-problems-and-exceptions.md) to the workflow engine. - [`jobError`](#function-joberror) to create a fail job object. This triggers a [FailJob call](/components/best-practices/development/dealing-with-problems-and-exceptions.md) to the workflow engine. - [`ignoreError`](#function-ignoreerror) to recover from an error and complete a job successfully. This triggers a [CompleteJob call](/apis-tools/orchestration-cluster-api-rest/specifications/complete-job.api.mdx) to the workflow engine. The `bpmnError` FEEL function can be called with one, two, or three parameters. Optionally, you can pass variables as the third parameter and combine this with a boundary event to use the variables in condition expressions when handling the error event. Example FEEL expression: ``` if response.body.status = "failed" then bpmnError("FAILED", "The action failed", response.body) else null ``` Within the FEEL expression, you access the following temporary variables: - The result of the connector in `response`. - The job of the invocation in `job` with the fields: `retries` - Any result variables created by the **Result Variable** and **Result Expression** properties (see the [REST connector](/components/connectors/protocol/rest.md#response), for example). - The technical exception that potentially occurred in `error`, containing a `message` and optionally a `code`. The code is only available if the connector's runtime behavior provided a code in the exception it threw. Building on that, you can cover those use cases with BPMN errors that you consider as exceptional. This can build on technical exceptions thrown by a connector as well as regular results returned by the external system you integrated. The [example expressions](#bpmn-error-examples) below can serve as templates for such scenarios. ### Function bpmnError Returns a context entry with an `errorType`, `errorCode` and `errorMessage`. - parameters: - `errorCode`: string - `errorMessage`: string (optional) - result: context ```feel bpmnError("123", "error received") // { errorType: "bpmnError", errorCode: "123", errorMessage: "error received" } bpmnError("123") // { errorType: "bpmnError", errorCode: "123" } ``` ### Function bpmnError with variables Returns a context entry with an `errorType`, `errorCode`, `errorMessage`, and `variables`. - Parameters: - `errorCode`: string - `errorMessage`: string - `variables`: context - Result: context ```feel bpmnError("123", "error received", {myVar: myValue}) // { errorType: "bpmnError", errorCode: "123", errorMessage: "error received", variables: {myVar: myValue}} ``` ### Function jobError Returns a context entry with an `errorType`, `errorMessage`, `variables`, `retries`, and `retryBackoff`. - Parameters: - `errorMessage`: string - `variables`: context _(optional), default_ `{}` - `retries`: number _(optional), default_ `0` - `retryBackoff`: days-time-duration _(optional), default_ `PT0S` - Result: context Optional parameters can be omitted if no parameter needs to be set after. ```feel jobError("job failed", {myVar: myValue}, 2, @"PT30S") // { errorType: "jobError", errorMessage: "job failed", variables: {myVar: myValue}, retries: 2, retryBackoff: @"PT30S" } ``` ```feel jobError("job failed", {myVar: myValue}, 2) // { errorType: "jobError", errorMessage: "job failed", variables: {myVar: myValue}, retries: 2, retryBackoff: @"PT0S" } ``` ```feel jobError("job failed", {myVar: myValue}) // { errorType: "jobError", errorMessage: "job failed", variables: {myVar: myValue}, retries: 0, retryBackoff: @"PT0S" } ``` ```feel jobError("job failed") // { errorType: "jobError", errorMessage: "job failed", variables: {}, retries: 0, retryBackoff: @"PT0S" } ``` ### Function ignoreError Allows you to complete a job successfully when an error occurs and returns a context entry with an optional `variables` property. These `variables` are used when sending the complete job command to the engine: ```feel ignoreError({"status":"ok"}) ``` You can also ignore the error without providing `variables` leading to a job completion without any variables: ```feel ignoreError() ``` After defining error-handling functions, you can use them in FEEL expressions as shown in the following examples. ### BPMN error examples #### HTTP errors to BPMN errors Using the [REST connector](/components/connectors/protocol/rest.md), you can handle HTTP errors directly in your business process model by setting a header named `errorExpression` with the following value: ```feel if error.code = "404" then bpmnError("404", "Got a 404") else if error.code = "500" then bpmnError("500", "Got a 500") else if response.body.status = "failed" then bpmnError("FAILED", "Action failed", response.body) else if error.code = "409" then ignoreError({"created":true}) else null ``` This will create BPMN errors for HTTP requests that return with a status [404](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404) or [500](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500). You can extend that list to all HTTP errors you can handle as business use cases, for example by informing a website administrator directly via Slack using the [Slack connector](/components/connectors/out-of-the-box-connectors/slack.md). #### Response value to BPMN error Using the [REST connector](/components/connectors/protocol/rest.md) or any other connector that returns a result, you can handle a response as a BPMN error based on its value, by setting a header named `errorExpression` with the following value: ```feel if response.body.main.humidity < 0 then bpmnError("HUMIDITY-FAIL", "Received invalid humidity") else null ``` This is assuming you requested data from a local weather station and received a value that is technically valid for the REST connector. However, you could define that for your business case a humidity value below `0` must be an error that should be checked manually. You could automatically send a message to a technician to check the weather station. #### Generic Header to transform a connectorException to a BPMN Error If the connector throws a `ConnectorException` like: ```java throw new ConnectorException("HUMIDITY-FAIL","Received invalid humidity"); ``` Then you can transform this exception to a BPMN error with this expression in a Header item named `errorExpression`: ```feel if is defined(error) then bpmnError(error.code, error.message) else null ``` ### Fail job examples #### HTTP errors to fail job Using the [REST connector](/components/connectors/protocol/rest.md), you can handle HTTP errors directly in your business process model by setting a header named `errorExpression` with the following value: ```feel if error.code = "404" then jobError("Resource not found") else if error.code = "504" then jobError("Gateway timeout", {},job.retries - 1, @"PT30S") else if response.body.status = "technicalProblem" then jobError("Technical Problem", response.body) else null ``` This will allow you to control the job failure for HTTP requests that return with status [404](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404) or [504](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/504). You can extend that list to all HTTP errors you can handle as a custom fail job; for example, to go to 0 retries instantly or increase the retry timeout. --- ## Use an outbound connector [Outbound connectors](/components/connectors/connector-types.md#outbound-connectors) allow workflows to trigger external systems or services. ## Creating the BPMN task ## Configuring the outbound connector Once a connector task is selected, the available configuration is visible in the properties panel on the right side. The required fields are highlighted with an error message. Fields in the properties panel marked with an equals sign inside a circle indicate that [FEEL](/components/modeler/feel/what-is-feel.md) can be used to configure the property. If the icon includes an equals sign marked with a star, FEEL is required. Using FEEL allows the user to reference process data from variables in the expression to configure the properties. Each connector defines its own set of properties you can fill in. Find the details for connectors provided by Camunda in the [out-of-the-box connectors](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md) documentation. ## Retries By default, connector execution is repeated `3` times if execution fails. The retries are executed sequentially without delays. To change the default retries value, edit the BPMN XML file and set the `retries` attribute at the `zeebe:taskDefinition`. For example: ```xml ... ... ``` The connector runtime also supports custom intervals between retries (**retry backoff**). To configure a custom retry interval, you need to add a special property to the connector element template. The property must bind to the `retryBackoff` task header, and the value must be a valid [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) duration. ```json { "value": "PT30S", "binding": { "key": "retryBackoff", "type": "zeebe:taskHeader" }, "type": "Hidden" } ``` In the example above, the retry attempts will be spaced 30 seconds apart, instead of the default behavior of retrying immediately. If necessary, the **retry backoff** property can be made visible and editable in the properties panel by changing the property `type` to `String`. `retryBackoff` is a reserved task header key recognized by the connector runtime. You don't need to handle this input in your connector implementation, as the runtime handles it automatically for all outbound connectors. ## Downloading documents To download documents without an additional step, review [uploading a document when using connectors](/components/document-handling/upload-document-to-bpmn-process.md#upload-a-document-when-using-any-connector). --- ## Use connectors in hybrid mode Outbound Inbound :::note Hybrid mode is supported as of the connectors `0.23.0` release. ::: **Hybrid mode** is where you can run a Self-Managed connector runtime instance attached to a Camunda SaaS cluster or another Self-Managed cluster that has another instance of the connector runtime attached. To name few use-cases where this approach might be useful: - When you deal with services that must be isolated within private network and must never be exposed to the public internet. - Infrastructure amendments need to be applied to the connector runtime, such as SSL certificates, mounted volumes, etc. - Code modifications applied to connector runtime, or specific connector logic. ## How it works Every connector has its ID (type definition), and name. Every connector element template has a hidden property that defines which connector is to be used to execute with a given template. For example, see a relation between [Kafka element template](https://github.com/camunda/connectors/tree/main/connectors/kafka/element-templates) and [Kafka connector](https://github.com/camunda/connectors/blob/main/connectors/kafka/src/main/java/io/camunda/connector/kafka/inbound/KafkaExecutable.java#L20). For the hybrid connector runtime to work properly, you must override the connector type. For the purpose of this guide, imagine you would like to override an HTTP REST connector with type `io.camunda:http-json:1`. Refer to the [element template](https://github.com/camunda/connectors/blob/main/connectors/http/rest/element-templates/http-json-connector.json#L50) and its related [runtime](https://github.com/camunda/connectors/blob/main/connectors/http/rest/src/main/java/io/camunda/connector/http/rest/HttpJsonFunction.java#L43). ## Start connector runtime in hybrid mode ### Prerequisites Ensure you have a running Camunda cluster, and a pair of `Client ID`/`Client Secret` with `Orchestration Cluster REST API` scope. Learn more about [how to obtain required credentials](/components/hub/organization/manage-clusters/manage-api-clients.md). To use secrets managed by the SaaS environment, add the `Secrets` scope. ### Option 1: Get connector runtime from Docker registry :::note When to use? Use this option when you don't need to make any code modifications to either connector runtime, or a specific connector. This option allows you to start the connector runtime bundle that runs all of [Camunda's officially-supported connectors](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md). ::: Run the following script: ```shell docker run --rm --name=HybridConnectorRuntime \ -e CAMUNDA_CLIENT_MODE=saas \ -e CAMUNDA_CLIENT_CLOUD_CLUSTERID='' \ -e CAMUNDA_CLIENT_CLOUD_REGION='' \ -e CAMUNDA_CLIENT_AUTH_CLIENTID='' \ -e CAMUNDA_CLIENT_AUTH_CLIENTSECRET='' \ -e CONNECTOR_HTTP_REST_TYPE='io.camunda:http-json:local' \ camunda/connectors-bundle: ``` ### Option 2: Build your own runtime :::note When to use? Use this option when you make modifications to the original connector runtime, existing connectors, or other related changes. This option allows you to start the connector runtime bundle with provided connectors. ::: 1. Ensure `docker` is installed. 2. Clone [https://github.com/camunda/connectors](https://github.com/camunda/connectors). 3. Go to `/bundle/default-bundle`. 4. Build a connector image, e.g. `docker build -f Dockerfile -t myorg/my-connectors-bundle: .`. 5. Run the same `docker run ...` command as in [Option 1](#option-a-get-connectors-runtime-from-docker-registry). ### Explanation Note the line `-e CONNECTOR_HTTP_REST_TYPE='io.camunda:http-json:local'`. This line means we have to override `CONNECTOR_X_TYPE` with a given type. In this case, we want to register a local Self-Managed HTTP REST connector as `io.camunda:http-json:local`. The `X` is normalized to the environment variable connector name. For example, the [HTTP REST connector](https://github.com/camunda/connectors/blob/main/connectors/http/rest/src/main/java/io/camunda/connector/http/rest/HttpJsonFunction.java#L33) `HTTP REST` name becomes `HTTP_REST`, or the [Kafka Consumer connector](https://github.com/camunda/connectors/blob/main/connectors/kafka/src/main/java/io/camunda/connector/kafka/inbound/KafkaExecutable.java#L20) name becomes `KAFKA_CONSUMER`. Therefore, to override it one would need to pass in the `CONNECTOR_KAFKA_CONSUMER_TYPE=xxx` environment variable. ## Using SaaS secrets If you add the `Secrets` scope to your API client, you can access cluster [secrets](/components/connectors/use-connectors/index.md#using-secrets) in a hybrid setup. Enable the SaaS secret provider via an environment variable or in your application config file: **Environment variable:** ``` CAMUNDA_CONNECTOR_SECRETPROVIDER_CONSOLE_ENABLED = true ``` **Properties file:** ``` camunda.connector.secretprovider.console.enabled = true ``` After enabling Camunda Hub, secret provider secrets used in an external connector's runtime will be resolved by fetching them from Camunda Hub. ## Preparing element template for hybrid mode As mentioned, to relate connector element templates with connector runtime, you must modify the task definition type. To do this, take the following steps: 1. Obtain a copy of the element template you wish to override. All latest versions of the official element templates can be found in the [official connectors repository](https://github.com/camunda/connectors) at path `connectors//element-templates/`. 2. Modify the `value` to the desired new type of the property. Use `zeebe:taskDefinition:type` for outbound connectors, or `inbound.type` for inbound ones. 3. Publish the new element template, and use it in your BPMN diagram. There are several options to deliver element templates to the target user: ### Option 1: Hide task definition type value Use this option when you plan to clearly indicate that a specific connector will only be used in a specific use-case. Otherwise, users might be confused between two of the same connector types. For example, if you defined `CONNECTOR_HTTP_REST_TYPE='io.camunda:http-json:local'` argument variable when running connectors runtime, you must implement the following in the element template for it to function properly: ```json { "type": "Hidden", "value": "io.camunda:http-json:local", "binding": { "type": "zeebe:taskDefinition:type" } } ``` ### Option 2: Expose task definition type as plain text Use this option when the target user building a BPMN process is deciding which connector to use, or you have more than one dedicated Self-Managed connector instance. Be mindful that the user will be dealing with different task definition types and has to know which is what. For example, if you defined `CONNECTOR_HTTP_REST_TYPE='io.camunda:http-json:local'` in runtime, you must implement the following in the element template for it to function properly: ```json { "type": "String", "label": "Task definition type", "value": "io.camunda:http-json:local", "binding": { "type": "zeebe:taskDefinition:type" } } ``` However, the target user can change the value back to the original `"value": "io.camunda:http-json:1",` to execute the process in a SaaS environment. You can also add this field to a group for UX purposes. ### Option 3: Expose task definition type as dropdown Use this option if you would like to achieve the most user-friendly experience. However, this approach may take a larger time investment in modifying element templates, plus additional time to support whenever you launch a new Connector runtime or disable an old one. The following example demonstrates this approach: ```json { "label": "Task definition type", "type": "Dropdown", "value": "io.camunda:http-json:1", "choices": [ { "name": "SaaS environment", "value": "io.camunda:http-json:1" }, { "name": "SM environment 1", "value": "io.camunda:http-json:local1" }, { "name": "SM environment 2", "value": "io.camunda:http-json:local2" } ], "binding": { "type": "zeebe:taskDefinition:type" } } ``` ## Appendix See ready-to-use hybrid element templates examples for [HTTP REST](https://github.com/camunda/connectors/blob/main/connectors/http/rest/element-templates/hybrid/http-json-connector-hybrid.json) and [Kafka Consumer](https://github.com/camunda/connectors/tree/main/connectors/kafka/element-templates/hybrid). --- ## Display and download a document In this section, learn how to build a form for document preview and download, and display and download a document from a user task in Tasklist. ## Build a form for document preview and download To display and allow downloading of a document you can use the [document preview component](/components/modeler/forms/form-element-library/forms-element-library-document-preview.md) in [forms](/components/modeler/forms/camunda-forms-reference.md). Although this example focuses on [Web Modeler](/components/hub/workspace/modeler/launch-modeler.md), you can also build a form for document preview and download in [Desktop Modeler](/components/modeler/desktop-modeler/index.md). :::note The document preview component offers previews in forms of PDF documents and images as the most common file types. Other document types are supported, but listed without the preview and show the file name with the option to download the file. ::: In the component's configuration, provide a document reference as an array of document metadata. ![document preview for form](./img/document-preview-in-form.png) For example, an array may look as follows: ``` [ { "documentId": "u123", "endpoint": "https://api.example.com/documents/u123", "metadata": { "fileName": "Document.pdf", "contentType": "application/pdf" } } ] ``` For an example of the document reference as an array on the page for the document preview form component, review our documentation on [document preview](/components/modeler/forms/form-element-library/forms-element-library-document-preview.md). ## Display and download a document from a user task in Tasklist A document can be displayed in a user task form in [Tasklist](/components/tasklist/introduction-to-tasklist.md). When a user opens the task, they can view and download the document directly from the form. ![Document preview for task in Tasklist](./img/task-with-document-preview-tasklist.png) ## Download a document using the Orchestration Cluster REST API You can also download a document from your Camunda 8 cluster using the Orchestration Cluster REST API. :::note This is currently supported for document stores of type: [AWS, GCP, in-memory (non-production), local (non-production)](/self-managed/concepts/document-handling/configuration/index.md). ::: Learn more about this request in the [Orchestration Cluster REST API docs](/apis-tools/orchestration-cluster-api-rest/specifications/get-document.api.mdx). --- ## Getting started Camunda 8 provides built-in support for storing, tracking, and managing documents using Camunda Forms, connectors, Tasklist, and the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/specifications/create-documents.api.mdx) in both SaaS and Self-Managed. :::note For a closer look at storage options for handling documents in Self-Managed environments, visit the [Self-Managed configuration docs](/self-managed/concepts/document-handling/configuration/index.md). ::: Document handling is automatically integrated into each SaaS cluster, allowing you to manage binary data, like PDFs, images and other file types, across development and production environments without needing to configure or maintain storage infrastructure yourself. ## Use cases and capabilities Document handling can be beneficial for different process use cases, such as uploading a document to a BPMN process, displaying and downloading a document, sending a document to an external system via a connector, and automating documents with [intelligent document processing](/components/hub/workspace/modeler/idp/idp-example.md). Step through all of these capabilities in the [use cases](/components/document-handling/overview.md) section. ## Storage options ### SaaS Camunda SaaS manages storage for you by integrating with [**Google Cloud Platform (GCP)**](https://cloud.google.com/storage) and [**AWS S3**](https://aws.amazon.com/s3/) bucket storage. - Each cluster automatically includes one pre-configured storage bucket. Clusters hosted on GCP use a GCP bucket. Clusters hosted on AWS use an AWS S3 bucket. - **Maximum upload size per request (whether you're uploading one or multiple files in that request)**: 10 MB - **File expiration time/time-to-live (TTL) policy**: 30 days. A custom expiration date can be specified via metadata for each document. The [document upload API](/apis-tools/orchestration-cluster-api-rest/specifications/create-document.api.mdx) allows this. For forms, this defaults to the cluster configuration as there is no set custom TTL for forms. ### Self-Managed If you're deploying Camunda in a Self-Managed environment, document storage must be configured manually. To learn more, visit the [Self-Managed configuration docs](/self-managed/concepts/document-handling/configuration/index.md). :::note For storage options in SaaS, you cannot combine AWS and GCP, but you can in Self-Managed (for example, a cluster on GCP and document storage on AWS). This may be the case based on your existing infrastructure. However, having a cluster and document storage by the same provider (GCP or AWS) is more practical. In this case, you may reduce latency, simplify configuration, and avoid potential cross-cloud data transfer costs. ::: --- ## Use cases Offering robust [document handling](/components/document-handling/getting-started.md) capabilities within Camunda 8, users can efficiently manage large volumes of binary data such as PDFs and images across both development and production environments in SaaS and Self-Managed. In this section we will cover three main use cases: - [Upload a document to a BPMN process](/components/document-handling/upload-document-to-bpmn-process.md) - [Display and download a document](/components/document-handling/display-and-download-document.md) - [Send a document to an external system via a connector](/components/document-handling/send-document-to-external-system.md) ## Automate documents with intelligent document processing Document handling can be integrated with intelligent document processing (IDP). This allows you to extract specific data from a high volume of documents using an IDP application, and use the extracted data throughout your BPMN process. Learn more about this in the IDP documentation: --- ## Send documents with outbound connectors You can reference a document in an [outbound connector](/components/connectors/connector-types.md#outbound-connectors). Connectors can use variables with document metadata as an input. The format of inputs will depend on the connector, as each connector has a different input structure. The [connector SDK](/components/connectors/custom-built-connectors/connector-sdk.md) provides document support in property/variable bindings. In most cases for the following outbound connectors, you can include a **Request body** under **Payload** in the properties panel to send with your request: ![example REST configuration](./img/rest-outbound-document.png) ## Outbound connectors that support document handling | Connector | Support details | | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [Amazon Bedrock](/components/connectors/out-of-the-box-connectors/amazon-bedrock.md) | Supports consuming documents as inputs for conversations. Review the **Document** field in the properties panel where the document reference can be provided. | | [Amazon S3](/components/connectors/out-of-the-box-connectors/amazon-s3.md) | Supports uploading documents from (or downloading documents to) the Camunda document store. Review the **Document** field in the properties panel where the document reference can be provided. | | [Amazon Textract](/components/connectors/out-of-the-box-connectors/amazon-textract.md) | Can read the input document directly from the Camunda document store. Review the **Document** field in the properties panel where the document reference can be provided. | | [Azure Blob Storage](/components/connectors/out-of-the-box-connectors/azure-blob-storage.md) | Supports uploading documents from (or downloading documents to) the Camunda document store. Review the **Document** field in the properties panel where the document reference can be provided. | | [Box](/components/connectors/out-of-the-box-connectors/box.md) | Supports uploading documents from (or downloading documents to) the Camunda document store. Review the **Document** field in the properties panel where the document reference can be provided. | | [Email](/components/connectors/out-of-the-box-connectors/email-outbound.md#response-structure-1) | Supports sending Camunda documents as attachments, or storing incoming attachments as Camunda documents. These documents are automatically stored in the Camunda document store and available to map in the result expression. | | [Google Drive](/components/connectors/out-of-the-box-connectors/googledrive.md) | Supports document upload and download. | | [Microsoft Teams](/components/connectors/out-of-the-box-connectors/microsoft-teams.md) | Supports sending documents to channels. | | [REST](/components/connectors/protocol/rest.md) | Supports storing the response as a document. | | [SendGrid](/components/connectors/out-of-the-box-connectors/sendgrid.md) | Provides attachment support. | | [Slack](/components/connectors/out-of-the-box-connectors/slack.md) | Supports adding attachments and increasing template versions. | ## Inline documents An inline document embeds content directly in a process variable, with no document store upload required. This is useful when you want to generate a document on-the-fly from a FEEL expression and pass it immediately to a connector. To create an inline document, set a process variable to the following structure: ```json { "camunda.document.type": "inline", "content": "Invoice #1234 — Amount due: $99.00", "name": "invoice.txt", "contentType": "text/plain" } ``` You can also construct this with a FEEL expression, for example to build the content dynamically from other process variables: ```feel = { "camunda.document.type": "inline", "content": "Invoice #" + invoiceId + " — Amount due: $" + string(amount), "name": "invoice-" + invoiceId + ".txt" } ``` ### Fields | Field | Required | Description | | ----------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `camunda.document.type` | Yes | Must be `"inline"`. | | `content` | Yes | The document content. | | `name` | No | The filename. If omitted, a UUID is generated automatically. | | `contentType` | No | The MIME type of the content. If omitted, the type is inferred from the file extension of `name`. If the extension is unrecognized or no name is provided, defaults to `application/octet-stream`. | ## Additional resources - [Camunda Academy: How To Handle Documents with Email Connector](https://academy.camunda.com/c8-h2-handle-documents-email-connector) --- ## Upload a document to a BPMN process You can implement document uploads in your BPMN processes using [forms](#build-a-form-for-document-upload), [inbound connectors](#upload-a-document-via-inbound-webhook-connector), and the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/specifications/create-document.api.mdx). ## Build a form for document upload When [building a form](/components/modeler/forms/utilizing-forms.md) for a process, you can use the [Filepicker form component](/components/modeler/forms/form-element-library/forms-element-library-filepicker.md) to allow users to upload files. In the Filepicker configuration, you can specify whether users can upload a single file or [multiple files](/components/modeler/forms/form-element-library/forms-element-library-filepicker.md#configurable-properties) and define the list of [supported file formats](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#unique_file_type_specifiers). Although this example focuses on [Web Modeler](/components/hub/workspace/modeler/launch-modeler.md), you can also build a form for document upload in [Desktop Modeler](/components/modeler/desktop-modeler/index.md). The Filepicker form component is available in both environments. ![Form with Filepicker](./img/form-with-file-picker.png) A designed form can be [linked](/components/hub/workspace/modeler/modeling/advanced-modeling/form-linking.md) to a [user task](#upload-a-document-from-a-user-task-in-tasklist) or used to [start a process](#upload-a-document-to-start-a-process). Documents uploaded with the form can then be [referenced](#get-reference-to-an-uploaded-document) later in the process. The Filepicker always returns an array with metadata for a single or multiple files, for example: ```json [ { "documentId": "u123", "endpoint": "https://api.example.com/documents/u123", "metadata": { "fileName": "Document.pdf", "contentType": "application/pdf" } } ] ``` ### Upload a document from a user task in Tasklist When the process is deployed and running, users can access and complete user tasks that include a form with the Filepicker component in [Tasklist](/components/tasklist/introduction-to-tasklist.md): ![document handling in tasklist](./img/task-with-file-picker-tasklist.png) ### Upload a document to start a process You can configure a form with the Filepicker for a start event of a BPMN process to allow users to upload documents when initiating the process. This is supported in [Tasklist](/components/tasklist/introduction-to-tasklist.md) and is available to logged-in users. :::note Only logged-in users can upload files when starting a process from Tasklist. ::: ### Get reference to an uploaded document Uploaded documents can be referenced later in the process. Filepicker's output variable is an array of objects with document metadata. It always returns an array of objects, whether a user uploads a single document or multiple documents. Single document uploads are accessible using `value[1]` (since [FEEL](/components/modeler/feel/what-is-feel.md) uses 1-based indexing). Refer to the example array below: ``` [ { "documentId": "u123", "endpoint": "https://api.example.com/documents/u123", "metadata": { "fileName": "Document.pdf", "contentType": "application/pdf" } } ] ``` ## Upload a document when using any connector Documents available for download from an unprotected URL can be added to a process in any connector by specifying them. For example: ```json { "camunda.document.type": "external", "url": "https://www.example.com/file.pdf", "name": "my-test-file.pdf" } ``` The `name` field is optional. If not provided, the filename is taken from `content-type` and `content-dispotition` http headers, and a random UUID will be used as a fallback. ## Upload a document via inbound webhook connector Documents can be added to a process using the [inbound](/components//connectors/connector-types.md#inbound-connectors) [HTTP webhook connector](/components/connectors/protocol/http-webhook.md). You can pass the documents in both the response expression and the result expression, where the `documents` object contains the references for created documents. Below, review an example of a webhook configuration: ![Example payload of inbound webhook connector](./img/inbound-webhook-connector-example.png) In this example, the result expression may look as follows, where `applicationDocument` can be later used by the process to retrieve documents: ``` { applicationDocument: documents[1] } ``` The document reference received as an output of one connector should be stored in process variables by using the result expression or result variable. To call the webhook sending a file, see the following example: ```curl curl --location 'https://some.dev.environment/uploadDocument' \ --form 'file=@"/path-to-file/file.pdf"' ``` :::note This example uses Postman to obtain the result, so your `user-agent` value may look different. ::: The result variable will have the following structure: ``` { "request": { "body": {}, "headers": { "host": "example.host.io", "x-request-id": "34509f2d9293cdfj49875rjf03", "x-real-ip": "some.example.ip.address", "x-forwarded-host": "example.host.io", "x-forwarded-port": "443", "x-forwarded-proto": "https", "x-forwarded-scheme": "https", "x-scheme": "https", "content-length": "70484", "user-agent": "PostmanRuntime/7.43.0", "accept": "*/*", "cache-control": "no-cache", "postman-token": "my-example-token", "accept-encoding": "gzip, deflate, br", "content-type": "multipart/form-data; boundary=--------------------------3007423254435453453514" }, "params": {} }, "connectorData": {}, "documents": [ { "storeId": "gcp", "documentId": "example-document-id", "contentHash": "fwkhkj34843rfhfwho3297ufdsj0df09", "metadata": { "contentType": "application/pdf", "size": 70266, "fileName": "file.pdf" }, "camunda.document.type": "camunda" } ] } ``` ## Upload a document using the Orchestration Cluster REST API You can also upload a document to your Camunda 8 cluster using the Orchestration Cluster REST API. :::note This is currently supported for document stores of type: [AWS, GCP, in-memory (non-production), local (non-production)](/self-managed/concepts/document-handling/configuration/index.md). ::: Learn more about [uploading a single document](/apis-tools/orchestration-cluster-api-rest/specifications/create-document.api.mdx) and [uploading multiple documents](/apis-tools/orchestration-cluster-api-rest/specifications/create-documents.api.mdx) using the Orchestration Cluster REST API. --- ## A2A Client connector The A2A Client connector is used to interact with A2A agents, by retrieving remote agent’s [Agent Card](https://a2a-protocol.org/v0.3.0/specification/#5-agent-discovery-the-agent-card) and sending messages to that agent. ## Create an A2A Client connector task ## Make the A2A Client connector executable To make the A2A Client connector executable, fill out the mandatory fields highlighted in red in the properties panel on the right. The A2A Client connector supports two operational modes to accommodate different use cases: _standalone_ and _AI Agent tool_. ### Standalone mode In standalone mode, you use the connector directly in a BPMN process as a service task. You explicitly configure the operation to perform and all associated parameters. Configure the following: - HTTP connection: - **A2A server URL** (required): Base URL of the A2A server. For example, `http://localhost:10000`. - **Agent Card location** (optional): Path to the Agent Card endpoint relative to the base URL. By default, `.well-known/agent-card.json`. - Connector mode: Select **Standalone**. - Operation: Choose between two operations: 1. [Fetch Agent Card](#fetch-agent-card). 2. [Send message](#send-message). #### Fetch Agent Card Use this operation to retrieve the Agent Card from the remote A2A server. The Agent Card describes the agent's capabilities and skills. ##### Response This operation returns a JSON object containing the most relevant Agent Card details. Here's an example: ```json { "result": { "name": "Weather agent", "description": "Helps with weather information", "skills": [ { "id": "weather-forecast", "name": "Weather Forecast", "description": "Get weather forecast information", "tags": ["forecast", "weather"], "examples": [ "What's the weather like today in Berlin?", "Will it rain tomorrow in London?" ], "inputModes": ["text"], "outputModes": ["text"] } ], "kind": "agentCard" }, "pollingData": { "id": "20be510a-9e12-40c2-ad4d-899ded442db3" } } ``` See the [Response structure](#response-structure) section below for more details. #### Send message Use this operation to send a message to the remote agent and handle the response based on the configured retrieval method. ##### Configuration - **Text** (required): Message to send to the remote agent. - **Context ID** (optional): Identifier for grouping related interactions. Typically generated by the remote server; omit for first message or a new context. - **Task ID** (optional): ID of the task this message belongs to. Generated by the remote server; omit if not part of a specific task. - **Reference task IDs** (optional): List of task IDs this message references for context. FEEL expression; for example: `= ["e240a538-a857-4e88-b3d0-4ddaf4882cc6", "eb086e57-43e3-45c1-a944-9a23a8b823df"]`. - **Documents** (optional): List of [Camunda document references](/components/document-handling/getting-started.md) to include with the message. This supports the same document types as the [AI Agent connector](../../../connectors/out-of-the-box-connectors/agentic-ai-aiagent-documents.md#supported-document-types). - **Response retrieval** (required): How to receive the final response from the remote agent - `Blocking`: Waits synchronously for the agent's response. - `Polling`: Returns immediately with a task with `submitted` or `working` state; use the A2A Client Polling Connector to retrieve results. - `Notification`: Configures a webhook for the remote agent to push task-status updates; use the A2A Client Webhook Connector to receive notifications. - **Webhook URL** (required): URL where the agent will send the response. See [A2A Client Webhook Connector](./a2a-client-webhook-connector.md) for how to set up a webhook endpoint. - **Token** (optional): Unique token for validating notifications. Use a static value or a FEEL expression, such as `= uuid()`. - **Authentication schemes** (optional): List of auth schemes required by the webhook. - **Authentication credentials** (optional): Credentials to authenticate webhook requests. :::note You can use the `Notification` response retrieval method only if the remote agent supports it. Check the agent's capabilities in its Agent Card. ::: - **History length** (optional): Number of most recent messages from task history to include in the response. By default, it is three. - **Response timeout** (optional): Maximum time to wait for (the first) response, as ISO-8601 duration. By default, `PT1M` is one minute. ##### Response This operation returns a JSON object containing the remote agent's response. Here's an example when the remote agent replies with a [message](https://a2a-protocol.org/v0.3.0/specification/#64-message-object): ```json { "result": { "kind": "message", "role": "agent", "messageId": "202dc848-05d7-4aff-a147-45ba42c765b1", "contextId": "5b70baac-065e-4680-ace9-81289e67f51d", "contents": [ { "type": "text", "text": "Based on the weather forecast, it looks like it will be sunny in Berlin today with a high of 25°C and a low of 15°C." } ] } } ``` Here's an example when the remote agent replies with a [task](https://a2a-protocol.org/v0.3.0/specification/#61-task-object) in the `submitted` state: ```json { "result": { "kind": "task", "id": "1f184812-0926-4f2c-881c-65c158a6dd11", "contextId": "8802db7b-8401-46b0-89f8-1ec3d5e81194", "status": { "state": "submitted", "message": { "kind": "message", "role": "agent", "messageId": "3fc30325-d5d2-4d7a-a472-e197fb478acc", "contextId": "8802db7b-8401-46b0-89f8-1ec3d5e81194", "taskId": "1f184812-0926-4f2c-881c-65c158a6dd11", "contents": [ { "type": "text", "text": "Your weather forecast request has been submitted and is being processed. I'm gathering data from multiple weather stations and satellite imagery for your location." } ] }, "timestamp": "2025-11-27T15:45:47.542Z" }, "history": [ { "kind": "message", "role": "user", "messageId": "a35df285-783d-4ad7-87b9-16a0bbff7f30", "contextId": "8802db7b-8401-46b0-89f8-1ec3d5e81194", "taskId": "1f184812-0926-4f2c-881c-65c158a6dd11", "contents": [ { "type": "text", "text": "Please provide a 7-day weather forecast for San Francisco, CA including temperature, precipitation, and wind conditions." } ] } ] }, "pollingData": { "id": "1f184812-0926-4f2c-881c-65c158a6dd11" } } ``` :::note In this example, `pollingData` holds the task identifier that the A2A Client Polling connector uses internally. This assumes the response retrieval method is set to `Polling`. ::: See the [Response structure](#response-structure) section below for more details. ### AI Agent tool mode In AI Agent tool mode, the connector is registered as a tool that the AI Agent connector can invoke dynamically. This enables the AI agent to discover and interact with remote A2A agents autonomously based on user requests. Configure the following: - HTTP connection: Same as in Standalone mode. - Connector Mode: Select **AI Agent tool**. - Operation: In AI Agent tool mode, this is dynamically determined by the AI agent: - **Operation** (required): Automatically populated from `toolCall.operation`.Possible values: `fetchAgentCard`, `sendMessage`. - **Parameters** (optional): Automatically populated from `toolCall.params`. It contains the dynamic parameters for the `sendMessage` operation. - **Response retrieval** (required): Same as in Standalone mode. - **History length** (optional): Same as in Standalone mode. - **Response timeout** (optional): Same as in Standalone mode. #### How it works 1. The AI Agent connector detects this connector as an available tool. 2. The AI agent performs tool discovery using the `fetchAgentCard` operation. 3. When the AI agent decides it needs to interact with the A2A agent, it issues a tool call with the required parameters. 4. The tool call is translated into a `sendMessage` operation. 5. The connector executes `sendMessage`. 6. Results are returned to the AI agent. 7. If needed, the AI agent continues a multi‑turn conversation with the A2A agent by issuing additional tool calls that reference the same context and task IDs (for example, when the remote agent requests more information). 8. If needed, the AI agent can start new interactions with the A2A agent by issuing tool calls without context and task IDs. :::info When the AI Agent connector detects at least one A2A Client connector that is configured as a tool, it injects instructions into the system prompt to guide the LLM on using A2A tools. ::: ## Response structure The response from the A2A Client connector is a JSON object with the following fields: - `result`: The remote agent's response. Depending on the operation, this is an Agent Card, a message, or a task object. - `pollingData`: Internal data used to poll task status when the response retrieval method is `Polling`. - `notificationData`: Internal data used when the response retrieval method is `Notification`. --- ## A2A Client Polling connector The A2A Client Polling connector polls for responses from asynchronous A2A tasks. It is typically paired with the [A2A Client connector](./a2a-client-connector.md) when using the `Polling` response retrieval method. ## Create an A2A Client Polling connector task 1. Start building your BPMN diagram. You can use the A2A Client Polling connector with an **Intermediate Catch Event** or with a **Receive Task**. 2. Select the applicable element and change its template to a **A2A Client Polling connector**. 3. Fill in all required properties. 4. Complete your BPMN diagram. 5. Deploy the diagram to activate the A2A Client Polling connector. ## Make the A2A Client Polling connector executable To make the A2A Client Polling connector executable, fill out the mandatory fields highlighted in red in the properties panel on the right. Configure the following: - Connection: Match values used in the A2A Client connector: - **A2A server URL** (required): Base URL of the A2A server. - **Agent Card location** (optional): Path to the Agent Card endpoint. - **A2A Client response** (required): FEEL expression referencing the response from the A2A Client connector. - **History length** (optional): Number of messages to return as part of the history when polling. By default, it is three. - **Task polling interval** (optional): Delay between polling requests as ISO-8601 duration. By default, `PT10S` is 10 seconds. - **Activation condition**: FEEL expression to determine whether the polled data meet the criteria to activate the intermediate catch event. In most cases, the default value is sufficient; it checks if the polled task is in a final state. - **Message ID expression** (optional): Extracts the message ID from the incoming request. The message ID uniquely identifies the message, is evaluated in the connector runtime, and the result is used to correlate the message. In most cases, you don’t need to configure it, but it’s useful for message deduplication or specific correlation behavior. See [Message correlation](../../../concepts/messages.md#message-correlation-overview) for more details. - **Message TTL** (optional): Sets the time-to-live (TTL) for correlated messages. TTL defines the time a message is buffered in Zeebe before correlation (if it can't be correlated immediately). The value is specified as an ISO-8601 duration. For example, `PT1H` sets the TTL to one hour. See [Message buffering](../../../concepts/messages.md#message-buffering) for more details. :::note **Correlation key (process)** and **Correlation key (payload)** are prefilled with the required values and hidden; there’s no need to modify them. ::: - **Result variable** (optional): Typically leave empty. - **Result expression**: FEEL expression to extract the result from the polled response. Typically, extract the `result` field from the [response](./a2a-client-connector.md#response-structure) using a FEEL expression such as `= {a2aAgentResponse: result}` or `= {toolCallResult: result}`. --- ## A2A Client usage patterns The [A2A Client connectors](./a2a-client.md) often work in combination to interact with remote A2A agents. This page outlines common usage patterns for the A2A Client connectors. ## Synchronous request-response The most straightforward usage pattern is when the A2A Client connector sends a request to a remote A2A agent and waits for the response. This is suitable for scenarios where you need immediate results. The diagram below illustrates this pattern, with an A2A Client connector being used as a tool by an [AI Agent connector](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md): ![A2A Client connector - Blocking](img/a2a-blocking.png) ### Configure the A2A Client connector - **A2A server URL**: For example `https://a2a-agent.example.com`. - **Agent Card location**: If different from the default `.well-known/agent-card.json`. - **Response retrieval**: `Blocking`. - **Result expression**: `={toolCallResult: result}`. :::note Per the [A2A specification](https://a2a-protocol.org/v0.3.0/specification/#712-messagesendconfiguration-object), the remote A2A server might reject the request if the task is long-running. ::: ## Asynchronous polling For long-running tasks, the A2A Client connector can send a request to a remote A2A agent and then poll for the result at regular intervals. This pattern is suitable when your task may take an extended period to complete. The diagram below shows this pattern, with an A2A Client connector and an A2A Client Polling connector used as a tool by an AI Agent connector: ![A2A Client connector - Polling](img/a2a-polling-direct.png) ### Configure the connectors #### A2A Client connector - **A2A server URL**: For example `https://a2a-agent.example.com`. - **Agent Card location**: If different from the default `.well-known/agent-card.json`. - **Response retrieval**: `Polling`. An A2A Client Polling connector is used to fetch the result. - **Result variable**: A variable name to store the result used by the A2A Client Polling connector, e.g. `a2aAgentResponse`. #### A2A Client Polling connector - **A2A server URL**: Same as used in the A2A Client connector. - **Agent Card location**: Same as used in the A2A Client connector. - **A2A Client response**: The variable name used in the A2A Client connector, e.g. `=a2aAgentResponse`. - **Task polling interval**: For example `PT5S`. - **Result expression**: `={toolCallResult: result}`. ### Polling behavior The A2A Client Polling connector correlates the message immediately when polling is not required. This occurs in the following cases: 1. The operation is _Fetch Agent Card_. 2. The remote agent responds with a _message_. 3. The remote agent responds with a _task_ that is not in a `submitted` or `working` state. To enable the A2A Client Polling connector only when needed, place a gateway before the connector. The diagram below illustrates this pattern, with a gateway controlling whether the A2A Client Polling connector runs: ![A2A Client connector - Conditional Polling](img/a2a-polling-conditional.png) Use the following expression as the exclusive gateway condition to check whether polling is needed: ```feel a2aAgentResponse.result.kind = "task" and list contains(["submitted", "working"], a2aAgentResponse.result.status.state) ``` Configure the following output on the Completed synchronously throw event: - **Process variable name**: `toolCallResult`. - **Variable assignment value**: `=a2aAgentResponse.result`. :::tip You can use a [Receive task](/components/modeler/bpmn/receive-tasks/receive-tasks.md) instead of an intermediate catch event. This allows you to attach boundary events for timeouts or error handling. Assign the A2A Client Polling receive task connector template to the receive task and configure it as described above. ::: ## Asynchronous push notifications When the remote A2A agent supports push notifications, you can configure the A2A Client connector to send a request to the remote agent and then wait for task-update notifications. This pattern is suitable when the task may take an extended period to complete and push notifications are preferred over polling. The diagram below illustrates this pattern, showing an A2A Client connector combined with an A2A Client Webhook connector used as a tool by an AI Agent connector: ![A2A Client connector - Webhook](img/a2a-webhook.png) ### Configure the connectors #### A2A Client connector - **A2A server URL**: For example `https://a2a-agent.example.com`. - **Agent Card location**: If different from the default `.well-known/agent-card.json`. - **Response retrieval**: `Notification`. An A2A Client Webhook connector is used to wait for the notifications. - **Webhook URL**: For example `http://some-camunda-cluster.com/inbound/22083f06-72fa-4f09-b4c5-8e83a0d66cb1`. - **Result variable**: A variable name to store the result used by the A2A Client Polling connector, e.g. `a2aAgentResponse`. #### A2A Client Webhook connector - **Webhook ID**: For example `22083f06-72fa-4f09-b4c5-8e83a0d66cb1`. - **A2A agent response**: The variable name used in the A2A Client connector, e.g. `=a2aAgentResponse`. - **Result expression**: `={toolCallResult: request.body}`. Use the following expression as the exclusive gateway condition to check whether push notifications are needed: ```feel a2aAgentResponse.result.kind = "task" and list contains(["submitted", "working"], a2aAgentResponse.result.status.state) ``` Configure the following output on the Completed synchronously throw event: - **Process variable name**: `toolCallResult`. - **Variable assignment value**: `=a2aAgentResponse.result`. :::tip You can use a [Receive task](/components/modeler/bpmn/receive-tasks/receive-tasks.md) instead of an intermediate catch event. This allows you to attach boundary events for timeouts or error handling. Assign the A2A Client Webhook receive task connector template to the receive task and configure it as described above. ::: --- ## A2A Client Webhook connector The A2A Client Webhook connector receives callbacks from remote A2A agents via HTTP webhooks. It is typically paired with the [A2A Client connector](./a2a-client-connector.md) when using the `Notification` response retrieval method. This connector is based on the [HTTP Webhook connector](../../../connectors/protocol/http-webhook.md) and shares most of its configuration options. ## Create an A2A Client Webhook connector task 1. Start building your BPMN diagram. You can use the A2A Client Webhook connector with an **Intermediate Catch Event** or with a **Receive Task**. 2. Select the applicable element and change its template to a **A2A Client Webhook connector**. 3. Fill in all required properties. 4. Complete your BPMN diagram. 5. Deploy the diagram to activate the A2A Client Webhook connector. 6. Navigate to the **Webhooks** tab in the properties panel to see the webhook URL. :::warning Use the URL from step 6 in the A2A Client connector configuration, then redeploy your BPMN diagram. There’s currently no automatic way to inject the webhook URL into the A2A Client connector configuration. ::: :::note In Camunda 8 Self-Managed, you must construct the webhook URL manually. See [HTTP webhook connector documentation](../../../connectors/protocol/http-webhook.md#activate-the-http-webhook-connector-by-deploying-your-diagram) for more information. ::: ## Make the A2A Client Webhook connector executable To make the A2A Client Webhook connector executable, fill out the mandatory fields highlighted in red in the properties panel on the right. Configure the following: - **Webhook ID** (required): Unique identifier for the webhook endpoint. This ID becomes part of the webhook URL. - **A2A Client Response** (required): FEEL expression referencing the response from the A2A Client connector. :::note For authentication and authorization, see [HTTP Webhook connector documentation](../../../connectors/protocol/http-webhook.md) for more information. ::: - **Activation condition**: FEEL expression to determine if the payload meets the criteria to activate the intermediate catch event. In most cases, the default value is sufficient; it checks whether the received task is in a final state. - **Message ID expression** (optional): Extract the message ID from the incoming request. The message ID uniquely identifies the message, is evaluated in the connector runtime, and the result is used to correlate the message. In most cases you don’t need to configure this, but it’s useful for deduplication or specific correlation behavior. See [Message correlation](../../../concepts/messages.md#message-correlation-overview) for more details. - **Message TTL** (optional): Sets the time-to-live (TTL) for correlated messages. TTL defines the time a message is buffered in Zeebe before correlation (if it can't be correlated immediately). The value is specified as an ISO-8601 duration. For example, `PT1H` sets the TTL to one hour. See [Message buffering](../../../concepts/messages.md#message-buffering) for more details. :::note **Correlation key (process)** and **Correlation key (payload)** are prefilled with the required values and hidden; there’s no need to modify them. ::: - **Result variable** (optional): Typically leave empty. - **Result expression**: FEEL expression to extract the result from the payload. Typically, extract the `request.body` field using a FEEL expression such as `= {a2aAgentResponse: request.body}` or `= {toolCallResult: request.body}`. :::tip As in the HTTP Webhook connector, the payload is accessible via the `request` variable. Use the following references to access data: - Body: `request.body.`. - Headers: `request.headers.`. - URL parameters: `request.params.`. ::: --- ## A2A Client The Agent-to-Agent (A2A) Client connectors enable Camunda processes to interact with remote agents using the [A2A protocol](https://a2a-protocol.org/v0.3.0/specification/). ## About Combined with the [AI Agent connector](../../../connectors/out-of-the-box-connectors/agentic-ai-aiagent.md), the A2A Client suite of connectors supports multi-agent collaboration scenarios. It also provides capabilities to discover remote agents, send messages, and receive responses through multiple mechanisms. :::info A2A Client connectors do not currently support authentication against a remote A2A server. This feature will be added in a future release. ::: ## Prerequisites To use any A2A Client connector, you need: - Access to an A2A-compliant agent. - Its [Agent Card URL](https://a2a-protocol.org/v0.3.0/specification/#5-agent-discovery-the-agent-card). ## A2A Client connectors The A2A Client includes three connectors. Together, these connectors enable seamless integration with A2A-compliant agents, allowing you to build multi-agent workflows within Camunda processes. Explore common A2A Client usage patterns ## HTTP proxy support In Self-Managed environments, the A2A Client connector supports HTTP proxy configuration, including [plain proxy variables](/self-managed/components/connectors/http-proxy-configuration.md#plain-proxy-variables). See the [AI Agent connector proxy configuration](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-customization.md#http-proxy-configuration) for details, including how to disable proxy support. --- ## Alpha features You can use alpha features to learn about upcoming changes, try them out, and share feedback. :::info To understand the difference between an alpha feature and an alpha release, see [alpha features and releases](/reference/announcements-release-notes/release-policy.md#alpha-features-and-releases). ::: ## Alpha Selected Camunda features and components are released as **alpha** versions. We release these in an early state for you to test and participate in development by sharing your feedback before they reach [general availability](/reference/announcements-release-notes/release-policy.md#general-availability-ga). Limitations of alpha features and components include: - Not for production use. - APIs, dependencies, and configuration are likely to change. - Not necessarily feature-complete. - Might lack full documentation. - No guaranteed updates to newer releases. - Alpha releases cannot be updated to newer releases and are not suitable for use in production. - Support based on SLAs agreed with you, but bugs are treated with the same priority as feature or help requests. See [Camunda Enterprise Support Guide](https://camunda.com/services/enterprise-support-guide/). - No maintenance service. - (SaaS) No availability targets. - Released outside the standard [release policy](/reference/announcements-release-notes/release-policy.md). To learn more about using alpha features, see [enabling alpha features](/components/hub/organization/manage-organization-settings/enable-alpha-features.md). ## Available alpha features The following documented alpha features are currently available: :::note - Alpha features can also be included in a minor version (stable) release. - Although there is no maintenance service, customers can still provide feedback through designated support channels, depending on their SLAs. These channels include filing issues in the respective [GitHub repositories](https://github.com/camunda) and submitting questions and suggestions by [contacting us](/reference/contact.md). - Alpha releases can also have **limited availability**, such as features that are only available to enterprise customers. ::: --- ## BPMN Copilot Camunda 8 SaaS only Chat with the AI BPMN Copilot for help generating new BPMN process diagrams in [Web Modeler](/components/hub/workspace/modeler/launch-modeler.md) based on a process description. You can also ask the BPMN Copilot questions about existing diagrams. :::note Terms of use By using this tool, you agree to Camunda's use of the anonymized input and output data and anonymized feedback to improve it. While your latest BPMN diagram will be saved in your account, Camunda does not save your prompts and the resulting BPMN diagrams. ::: ## Get started :::warning Creating a BPMN process diagram with the BPMN Copilot will overwrite existing work. ::: To use the BPMN Copilot in Web Modeler, take the following steps: 1. If you have not already done so, [opt in](/components/hub/organization/manage-organization-settings/enable-alpha-features.md#enable-ai-powered-features) to use this feature. 2. Log in to [Web Modeler](/components/hub/workspace/modeler/launch-modeler.md). 3. Click **New project > Create new > BPMN diagram** to open the Camunda Copilot chat window. 4. In the chat box, enter your prompt. A prompt should be a simple, clear, and concise request. For example, "Generate a mortgage loan process diagram" or "Explain this process". Note that more complex requests may take longer to process. 5. Wait for the BPMN Copilot to respond. This typically takes between 20-50 seconds, depending on the prompt. :::note Timeouts can occur during this step if your query is too complex. ::: ### Example explanation prompts - "Describe this process in plain language" - "What KPIs would you recommend for this process?" - "Estimate the median and 95th percentile duration for this process" - "What does this symbol do?" [after selecting a BPMN element in the process] - "Give me a list of test cases to ensure this process completes as expected" - "Summarize this process for a new employee" - "Give me a prioritized list of recommended improvements to make the 95% of instances complete within 4 hours" ### Example creation prompts - "Create an absence request diagram" - "Design a process that uses data objects to drive process logic, showing how data influences process flow and decisions" - "Model a complex service orchestration process involving multiple service tasks, conditional routing, and parallel execution of tasks" - "Create a service order management process for a telco company. The process starts with a fraud check. First, calculate price dimensions via a script task, then check if the price dimensions are normal or high with an exclusive gateway. If high, escalate to a fraud investigation subprocess." - Paste existing text documentation of a process or of process requirements (for example, a Confluence page) - Paste a process hard-coded in any language (for example, BPEL, Java, COBOL, Python) - Paste a description of a diagram. Any LLM (like ChatGPT) can generate this description from a screenshot or an image of the diagram. ## Follow-up prompts The BPMN Copilot can also translate business-focused intent into actionable changes for the diagram it creates. For example: - "Modify this process to maximize its changes of adhering to a 1 day SLA" - "Reduce the operating cost of this process" - "Show me ways to integrate AI into the process" - "Consider unhappy paths as well" - "Add error handling" - "Improve the user experience" :::note Requesting specific modifications to one or several sections of the BPMN diagram may impact unrelated sections. ::: ### Limitations - The BPMN Copilot only supports processes up to approximately 400 KB in size. - The BPMN Copilot officially supports only modifying diagrams that were created by the BPMN Copilot itself. It does not officially support modifying other existing diagrams (including diagrams with implementation details). - The BPMN Copilot does not support pools, lanes, and collaborations. --- ## FEEL Copilot Chat with the AI FEEL Copilot for help generating [FEEL (Friendly Enough Expression Language)](/components/modeler/feel/what-is-feel.md) expressions in [Web Modeler](/components/hub/workspace/modeler/launch-modeler.md). :::note Terms of use By using this tool, you agree to Camunda's use of the anonymized input and output data and anonymized feedback to improve it. Camunda does not save your prompts and the resulting FEEL expressions to your account. ::: ## Get started :::warning - Clicking **Use Expression** on a FEEL Copilot response will overwrite your existing work. - As the FEEL Copilot can produce errors, you **must** check its output before saving the results to your diagram. ::: To use the FEEL Copilot in Web Modeler, take the following steps: 1. If you have not already done so, [opt in](/components/hub/organization/manage-organization-settings/enable-alpha-features.md#enable-ai-powered-features) to use this feature. 1. Log in to [Web Modeler](/components/hub/workspace/modeler/launch-modeler.md). 1. Click **New project > Create new > BPMN diagram**. 1. Open any FEEL Popup. :::note To open a FEEL popup from an empty diagram: 1. In a new diagram, click the Start Event (the circle in the diagram). 1. In the sidebar, click the plus (**+**) next to **Outputs**, and click **Variable assignment value**. 1. Click the **Open popup editor** icon in the field to open the FEEL popup. ::: 1. Open the FEEL Copilot panel. 1. In the chat box, enter your prompt and click **Send**. A prompt should be a simple, clear, and concise request. See [Example Prompts](#example-prompts) for ideas. Note that more complex requests may take longer to process. 1. Wait for the FEEL Copilot to respond. This typically takes between 15-30 seconds, depending on the complexity of the prompt. 1. Click **Use expression** to save the expression in the FEEL popup. 1. Close the FEEL popup to save the expression to your diagram. :::note If your query is too complex, timeouts can occur when waiting for the FEEL Copilot to process a request. ::: ## Example prompts ### Generate FEEL expressions - "Find the difference between two dates" - "Get the name from \{"name": "Alice", "id", 123\}" - "Check if a number is greater than 10" - "What would this Java be in FEEL: input.trim().toUpperCase().replace(" ", "\_");" ### Translate code to FEEL - "Translate from JUEL" - "Translate from Python" - "Translate from JavaScript" ### Debug & refactor FEEL - "Fix this expression" - "Why am I getting a null response?" - "Make it more compact" ### Explain FEEL - "How does this work?" - "What does [FEEL function] do?" - "Document how this FEEL expression works" ### Examples - "What can the FEEL Copilot do? (list of use cases)" - "Give me sample input data" - "Give me an example FEEL expression" ### Limitations - The FEEL Copilot only supports prompts up to approximately 4 MB in size. --- ## Early access Explore new features and components currently in development by Camunda. ## Alpha features [Alpha features](/components/early-access/alpha/alpha-features.md) are more developed and closer to becoming part of the product but may not yet be fully optimized or supported. Selected Camunda features and components are released as alpha versions to provide early access. By testing these features, you have the opportunity to participate in their development by sharing feedback before they reach [general availability](/reference/announcements-release-notes/release-policy.md#general-availability-ga). ## What to expect | | Alpha features | | ---------------------------------- | ------------------------------------------------------------------------------------------------ | | Purpose | Test upcoming features that could make it to GA | | Suitable for production use | No | | Stability | APIs, dependencies, and configuration are likely to change. | | Feature complete | No | | Documentation | May have some documentation. | | Updates | No guaranteed updates to newer releases, but likely to continue development. | | Support | Support based on SLAs, with bugs treated as part of regular feature/help requests. | | Maintenance service | No | | Available on | SaaS/Self-Managed | | Release cycle | Outside the standard [release policy](/reference/announcements-release-notes/release-policy.md). | | Admin/owner access required | Yes | --- ## Features and integrations Get started with selected key features and integrations. ## Features Learn more about selected key features. ## Camunda integrations Learn more about Camunda integrations. Camunda integrations ## Early access Introducing early access - a space to explore new features and components currently in development by Camunda Early access --- ## Camunda Hub Manage organizational resources, analyze operations and business value, and deliver agentic processes at scale with Camunda Hub. ## About Camunda Hub Camunda Hub is the unified platform where: - **Center of excellence teams** manage infrastructure, member access, and workspaces, so delivery teams have the environments and tools they need to ship process solutions at scale. - **Delivery teams** collaborate in managed workspaces, discover and use approved catalog assets, and model and deploy business processes. ## Manage organizational resources Manage organizational resources, including clusters and workspaces, and govern the use of reusable assets: ## Build within a workspace Discover and use approved reusable assets, manage projects, and deliver business processes: --- ## Analyze operations Monitor cluster health, track job and process execution, and measure business value across your Camunda organization. --- ## Job dashboard Use the job dashboard to see which job types are active, how many jobs are created, completed, and failed, and which job workers are involved. ## Availability and permissions The job dashboard is available for clusters running Camunda 8.9+. For Software as a Service (SaaS): - Available for clusters running Camunda 8.9+. - Camunda manages the underlying job metrics configuration for you. - If the **Jobs** card is missing or shows **Access restricted**, check that your user has permission to view job metrics in Camunda Hub. If the issue persists, contact your organization owner or Camunda Support. For Self-Managed: - Requires Camunda 8.9+ (Zeebe and Camunda Hub). - Configure job metrics in the engine configuration (`camunda.monitoring.metrics.job-metrics.*`). These options and their default values are available in the auto-generated `defaults.yaml` file and the Helm values. - If the **Jobs** card is missing or shows **Access restricted**, verify that: - The cluster is running Camunda 8.9+. - Job metrics are enabled in the engine configuration. - Camunda Hub can connect to the cluster. - Your user has permission to view job metrics. :::info Camunda Hub is introduced in 8.10. If you're using 8.9, refer to [that version's documentation, which uses Camunda 8 Console](/versioned_docs/version-8.9/components/console/job-dashboard/job-dashboard.md). ::: Access control: - The global `READ_JOB_METRICS` permission is the only Camunda Hub permission required to use the job dashboard. - Operate permissions are still required to view underlying instances when you click **View errors**. ## When to use the job dashboard With the job dashboard, you can: - Check whether jobs flow through the system (created, completed, failed) for each job type. - See which job workers process a given job type and how many jobs they handle. - Investigate error patterns for a job type before drilling into individual process instances in Operate. - Avoid building and maintaining custom job-monitoring dashboards. ## Open the job dashboard ### 1. Open the Jobs overview 1. In Camunda Hub, go to **Clusters**. 2. Select a cluster. 3. On the **Overview** tab, locate the **Jobs** card. 4. Click **View jobs** to open the **Job types** page. ![Cluster overview with Jobs card](img/сluster-overview-jobs.jpg) ### 2. Job types overview The **Job types** page shows all job types running against the selected cluster. ![Jobs overview with Job types table](img/jobs-overview.png) Key elements: - **Last updated** timestamp (based on statistics responses). - **Search** box to filter job types. - **Time range** selector (for example, **Last 24 hours**) that controls all metrics on the page. - Table columns: - **Job type** - **Assigned workers** - **Created jobs** - **Completed jobs** - **Not completed jobs** - **Last completed** If the selected date range hits internal limits, Camunda Hub shows a warning that not all data is displayed. Narrow the time range to see a more complete view. Job metrics are stored internally in the engine and exported in batches every five minutes. As a result, metrics in the UI can be delayed by up to five minutes. Configuration limits protect the system and include: - Maximum string lengths for job type, job worker, and tenant ID - Maximum number of unique keys (jobType × tenantId × job worker combinations) If a limit is exceeded, the batch is marked as incomplete, and the UI shows the **Data loading limit reached** warning. In this case: - Narrow the time range to reduce the amount of data - Filter by job type to focus on a smaller subset - In Self-Managed environments, adjust configuration limits if needed Treat the counts as partial for the affected time range. To drill down into a specific job type, click its **Job type** link (for example, `send-email`). ## Job type details The **Job type details** page shows metrics and errors for a single job type. ![Job type details view](img/job-activity-log.png) ### Job workload The **Job workload** chart shows how many jobs were **created**, **completed**, and **failed** over time for the selected job type and time range. Failed counts include jobs that ended in an error state (error thrown, failed, or timed out). Canceled, migrated, or retry-update-only states are not counted. ### Job completion rate The **Job completion rate** donut chart shows three groups for the selected time range: - **Created**: All jobs created, regardless of whether they are still running, completed, or failed. - **Completed**: Jobs that have finished executing successfully. - **Failed**: Jobs that ended in an error state (error thrown, failed, or timed out). Multiple failed attempts for the same job are counted separately. Canceled or migrated jobs are not included. Use this chart to see at a glance whether most jobs finish successfully or many end in a failed state. ### Job workers table The **Job workers** table shows which job workers processed this job type and how many jobs they handled: - **Worker name** - **Created jobs** - **Completed jobs** - **Last completed** Use this table to see which job workers are active and whether failures are concentrated on specific job workers. ### Failed jobs by error type The **Failed jobs by error type** table groups failed jobs by error so you can quickly see the most common problems: - Search by error type or message. - Columns: - **Error type** - **Error message** - **Jobs with error** Click **View errors** to open related instances in **Operate**, with the **Error Message** filter prefilled so you see only instances that failed with that error. ## Empty states and access restrictions ### No jobs in the queue If there are no jobs for the cluster or selected time range, the Jobs page shows: - Heading: **No jobs in the queue** - Message: **No jobs found.** - Link: **Learn more about Jobs and Job Workers** This means there is no job activity to display. ### Jobs card access restricted If the feature is disabled for the cluster or you don't have permission, the **Jobs** card on the cluster overview shows: - Status: **Access restricted** - Message explaining that the feature is restricted or disabled and you must contact an administrator. - Link: **Learn more about roles and restrictions** ### Missing permissions when viewing errors If you click **View errors** but lack permissions in Operate, you may see messages like: - **Missing permissions to view the Definition** - **Missing permissions to access Instance History** - **Missing permissions to access Variables** In this case, contact your organization owner or admin to request the necessary Operate permissions. ## SaaS vs Self-Managed The Camunda Hub UI and flows are the same in SaaS and Self-Managed. - **SaaS:** The job dashboard is available for Camunda 8.9+ clusters. Camunda manages the underlying job metrics configuration. - **Self-Managed:** You enable and configure job metrics in the engine and Helm charts. For details on available options and defaults, see the job metrics [configuration reference](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md). --- ## Organization Manage organizational resources, including clusters and workspaces, and govern the use of reusable assets. ## About organization-level management With Camunda Hub, you'll manage your infrastructure, member access, and workspaces, so your delivery teams have the environments and tools they need to ship process solutions at scale. This governance happens at the organization level. ## Manage workspaces Create and manage workspaces within your organization: [Get started](./manage-workspaces/index.md) ## Manage clusters Create, monitor, and assign clusters for seamless execution across all rollout stages: ## Manage the catalog Establish a git repository with catalog assets, upload the assets in a CI/CD pipeline, and approve them for use within your organization: [Get started](./manage-catalog/index.md) ## Manage members Manage the users, user groups, and roles in your organization: ## Manage organization settings Manage organizational settings, and view usage alerts and history: ## Analyze operations Monitor cluster health, track job and process execution, and measure business value across your Camunda organization: --- ## Manage the catalog Establish a Git repository with catalog assets, upload the assets in a CI/CD pipeline, and approve them for use within your organization. ## About the catalog In Camunda Hub, the catalog is a collection of reusable automation assets, such as element templates. Catalog assets live in your own Git repository and are uploaded to Camunda Hub, where: - Center of excellence teams assess and approve them for use within the organization. - Delivery teams discover and use the assets when they model business processes. --- ## Backups If your organization works within Camunda's [Enterprise](https://camunda.com/pricing/) plan, you can create a manual and scheduled [backups](/components/saas/backups.md) of your cluster. :::caution Only the three most recent successful backups of each type are kept, meaning you can have three manual and three scheduled backups. If you already have three backups of a type, the oldest backup is automatically removed. ::: ## Create a manual backup You can create a manual backup every five hours. To create a manual backup, take the following steps: 1. Select the **Backups** tab. 2. Click **Create manual backup**. ![cluster-details](./img/cluster-detail-backups.png) 3. A popup modal will appear with more information about manual backups, including retention. Click **Create backup**. ![cluster-details](./img/cluster-detail-backups-manual.png) ## Create a scheduled backup To create a scheduled backup, take the following steps: 1. Select the **Backups** tab. 2. Click **Set up schedule**. 3. Use the dropdown to schedule the frequency for backups - daily or weekly. 4. Select the time of day you would like backups to be taken at this frequency. 5. Click **Create schedule**. ![cluster-details](./img/cluster-detail-create-scheduled-backup.png) --- ## Cluster load Use the cluster load metric to view and manage your cluster load and utilization. ## About cluster load The cluster load metric provides a high-level overview of how well a cluster is coping with its current workload. - Use this information to check and monitor if a cluster is appropriately sized for its workload. - Cluster load can also be used as an indicator of cluster health. For example, a cluster running at maximum load can indicate poor cluster responsiveness. A general guideline to follow when using the cluster load metric is: - **High cluster load percentage**: The higher the cluster load percentage value, the more likely it is that things will slow down, time out, requests will fail, and so on. For example, if a cluster is continually running at 95% load, this means the cluster is probably overloaded and may not be performing well. - **Low cluster load percentage**: The lower the cluster load percentage value, the more you could increase the cluster workload as the cluster is probably underused. For example, if a cluster load is only 5% then the cluster can probably accept more workload and may be underused. :::info To understand how cluster load is calculated, see [how cluster load is calculated](#load-calculation). ::: ## View cluster load If your cluster supports load monitoring, the cluster load percentage is shown in the **Load** column on the Camunda Hub **Clusters** tab. Select a cluster to view detailed cluster load information on the cluster **Overview** tab. - The current cluster load is shown as a percentage bar at the top of the section. - The chart shows detailed cluster load data for the last 24 hours, 7 days, or 30 days. - Select the time period you want to view data for. - Hover over individual nodes in the chart to view data for a specific time or day. - Click the **Refresh** icon to refresh and update the cluster load data. ## Manage cluster load Cluster load will fluctuate based on incoming user requests, and internal processing load. - User requests are those sent directly by an external client. - Internal load refers to all other processing that is **not** directly triggered by a client. For example, timer events, a job being made available after back off, and so on. This means that cluster load could fluctuate throughout the day. For example: - Cluster load might be higher during business hours, but lower overnight when the cluster is unused. - Certain processes can cause a "fan-out" effect, where even though creating a process instance is only a single user request, it could require computation on a very large multi-instance collection, resulting in a high cluster load spike. - You could have a spike in cluster load if a large number of timers are triggered at the same time. ### High cluster load percentage A high cluster load percentage and utilization does not necessarily mean that a cluster needs to be resized, but it could indicate that your cluster is overloaded. > A high cluster load percentage benchmark is relative, but generally **anything above 60%** is considered high. If your cluster load and utilization is too high, but your operations are completing within an acceptable timeframe, requests are successful, and so on, you do not need to take any action. However, if you find yourself experiencing any of the following issues, you might need to investigate and take action: - REST clients are receiving a high number of `429` errors. - gRPC clients are receiving a high number of `RESOURCE_EXHAUSTED` errors. - More and more commands or queries are timing out in your clients. - Web components are slowing down, or showing data that is severely out of date (for example, 1 hour). In this scenario, you should look at reducing the overall load on the cluster. ### Reduce cluster load If your cluster load and utilization is too high, you can help reduce it by: - Scaling down the load. For example, by stopping certain clients. Start with your least critical load, and continue from there. - Check your running process instances for known issues that cause high processing, such as: - Straight-through processing loops, where there are no wait states. For example, a sub process with an error boundary event which loops back to an activity leading into the sub-process. If you have an activity which consistently throws an error, this will result in a subtle infinite loop where the engine is stuck and cannot process anything else. You would have to cancel this instance, or contact support to force cancel it for you. :::important If your cluster load percentage remains high even after attempts to reduce it, you might need to increase your cluster size and scale. See [cluster size](/components/concepts/clusters.md#cluster-size) and [sizing your environment](/components/best-practices/architecture/sizing-your-environment.md). ::: ## How cluster load percentage is calculated {#load-calculation} Cluster load percentage is based on the cluster's [flow control configuration](/self-managed/operational-guides/configure-flow-control/configure-flow-control.md). Essentially, if flow control is configured, every partition is assigned a [write rate](/self-managed/operational-guides/configure-flow-control/configure-flow-control.md#exporting-and-write-rate) and a [write rate limit](/self-managed/operational-guides/configure-flow-control/configure-flow-control.md#write-rate-limit). This means a partition's cluster load is defined as: `(writeRate / writeRateLimit)` This gives a value between 0 and 1, which is multiplied by 100 to give the partition load as a percentage. The cluster load percentage is then the average of the load of all partitions in the cluster. :::note - The partition load is _only_ calculated and reported on the current partition leader. - It typically takes about five minutes for cluster load data to update with the latest information. ::: --- ## Configure the audit log Configure the [audit log](../../../audit-log/overview.md) in Camunda 8 SaaS. ## About The audit log is an important feature with which you can meet regulatory requirements and maintain operational integrity by accessing a record of operations. These records include who performed the operations, when, and on which entities. The audit log is enabled by default, and the storage it requires may result in increased costs. To mitigate these resource costs, only user operations are tracked by default, not [client](../../../zeebe/technical-concepts/architecture.md#clients) operations. In Camunda 8 SaaS, you can change the default behavior in Camunda Hub. You can choose which user and client operations are recorded to fine-tune log thoroughness and resource usage according to your needs. You can also disable the audit log. :::note This feature is only available for SaaS clusters using Camunda 8.9 and above. If you're using Camunda 8 Self-Managed, see the [Self-Managed guide](../../../../self-managed/concepts/audit-log/configure.md). ::: ## Configure the audit log 1. In **Camunda Hub**, open **Clusters**. 2. Select an applicable cluster. 3. From the cluster configurations tabs, select **Audit Log**. 4. Select the user operations and client operations to record. 5. Click **Update**. ## Enable or disable the audit log 1. In **Camunda Hub**, open **Clusters**. 2. Select an applicable cluster. 3. From the cluster configurations tabs, select **Audit Log**. 4. Click **Enable audit log** or **Disable audit log**, depending on the state of your cluster. When disabled, new operations are not recorded. Changing this setting doesn't cause the existing audit log data to be immediately purged. Instead, it will be cleaned up according to the secondary storage retention settings. Until then, you can continue to access the data in [Operate](../../../operate/userguide/audit-operations.md), [Tasklist](../../../tasklist/userguide/audit-task-history.md), [Admin](../../../admin/audit-operations.md), and the [Search API](/apis-tools/orchestration-cluster-api-rest/specifications/search-audit-logs.api.mdx). --- ## Create Cluster Include To deploy and run your process, you must create a [cluster](/components/concepts/clusters.md) in Camunda 8. 1. To create a cluster, navigate to **Camunda Hub**, click the **Clusters** tab, and click **Create new cluster**. 1. Name your cluster. 1. Select a [cluster type](/components/concepts/clusters.md#cluster-type) and [cluster size](/components/concepts/clusters.md#cluster-size). 1. Assign a cluster tag to indicate what type of cluster it is. 1. Select your [region](/components/saas/regions.md). 1. Select your [encryption at rest protection level](/components/saas/encryption-at-rest.md) (enterprise only). 1. Select a channel and release. For the purpose of this guide, we recommend using the **Stable** channel and the latest generation. 1. If you are using a generation of version 8.8 or higher, select if you want to enable [authorization-based access control](/components/concepts/access-control/authorizations.md). 1. Click **Create cluster**. 1. Your cluster will take a few moments to create. Check the status on the **Clusters** page or by clicking into the cluster itself and looking at the **Applications** section. :::note - If you haven't created a cluster yet, the **Clusters** page will be empty. - You can start modeling even if the cluster shows a **Creating** status. ::: ![cluster-creating-modal](./img/cluster-creating-modal.png) 1. After creating the cluster, you can view the new entry in the **Clusters** tab: ![cluster-creating](./img/cluster-overview-new-cluster-creating.png) 2. The cluster is now being set up. During this phase, its state is **Creating**. After one or two minutes, the cluster is ready for use and changes its state to **Healthy**: ![cluster-healthy](./img/cluster-overview-new-cluster-healthy.png) 3. After the cluster is created, click on the cluster name to visit the cluster detail page. ## Tag your cluster You can tag your cluster for `dev`, `test`, `stage`, or `prod`. - Assigning a tag makes it easier for team members to clearly distinguish between different stages of the software development lifecycle. - Tags have no impact on performance and can be changed later in the cluster details section of the cluster overview page. - [Authorization-based access control](/components/concepts/access-control/authorizations.md) is disabled by default for `dev` and `test` clusters, and enabled for `stage` and `prod` clusters. You can change this setting during and after cluster creation. See [Clusters](/components/concepts/clusters.md) for more details. --- ## Create a cluster --- ## Alerts Camunda 8 can notify you when process instances stop with an error. There are two forms of notification: - By email to the email address of your user account - By webhook ## Create an alert To create a new alert, take the following steps: 1. Click on your cluster and select the **Alerts** tab. ![cluster-details](./img/cluster-detail-alerts.png) 2. Click **Create an alert**. ![create-alert](./img/cluster-detail-create-alert.png) 3. Choose between **Email** and **Webhook**. 4. If you select **Email**, click **Create**. No further information is needed. For **Webhook**, complete the additional steps below. 5. To create a webhook alert, provide a valid webhook URL that accepts `POST` requests. If your webhook requires [HMAC authentication](https://www.okta.com/identity-101/hmac/), you can specify an HMAC secret. The SHA-256 hash of the request body will then be generated using your HMAC secret, and it is included it in the HTTP header `X-Camunda-Signature-256` each time we send out a webhook alert to your endpoint. You will have one email alert per cluster, but you can create multiple webhook alerts if needed. ## Webhook alerts Webhook alerts contain a JSON body with following structure: ```json { "clusterName": "cluster-name", "clusterId": "88d32bfc-4f8e-4dd3-9ae2-adfee281e223", "operateBaseUrl": "https://console.camunda.io/org/2b3bc239-ad5b-4eef-80e0-6ef5139ed66a/cluster/88d32bfc-4f8e-4dd3-9ae2-adfee281e223/operate", "clusterUrl": "https://console.camunda.io/org/2b3bc239-ad5b-4eef-80e0-6ef5139ed66a/cluster/88d32bfc-4f8e-4dd3-9ae2-adfee281e223", "alerts": [ { "operateUrl": "https://console.camunda.io/org/2b3bc239-ad5b-4eef-80e0-6ef5139ed66a/cluster/88d32bfc-4f8e-4dd3-9ae2-adfee281e223/operate/#/instances/2251799829404548", "processInstanceId": "1234567890123456", "errorMessage": "something went wrong", "errorType": "JOB_NO_RETRIES", "flowNodeId": "node-id", "jobKey": 1234567890123456, "creationTime": "2021-07-22T08:00:00.000+0000", "processName": "process-name", "processVersion": 1, "processVersionTag": "versionTag" } ] } ``` :::caution breaking change The JSON format was changed in 8.8.9. See [release announcements](/reference/announcements-release-notes/880/880-announcements.md#apis--tools#webhook-alerts-json-format) for more information and required actions. ::: --- ## Manage API clients To interact with an orchestration cluster from the outside, every client application must authenticate itself. An **OAuth Flow** is therefore used for authentication: ![auth-flow](./img/client-auth.png) The application authenticates itself with OAuth service using `Client Id` and `Client Secret`, OAuth service validates this information and returns an access token, and the application can then use this access token to interact with an orchestration cluster. The client configuration is shown at the bottom of the cluster detail view. Create a new client and all necessary information is displayed. For the `Client Id` and `Client Secret`, a client application can request an access token at the authentication URL (**Steps 1 and 2**). The access token is necessary to interact with an orchestration cluster (**Step 3**). :::note Access tokens have a validity period that can be found in the access token. After this time, a new access token must be requested. ::: ## Create a client Camunda 8 SaaS supports the following scopes: - **Orchestration Cluster API**: Access the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) and the [Zeebe gRPC API](/apis-tools/zeebe-api/grpc.md). - **Optimize API**: Access the [Optimize REST API](/apis-tools/optimize-api/overview.md). - **Administration API (Secrets resource)**: Access cluster secrets in a [hybrid setup](/components/connectors/use-connectors-in-hybrid-mode.md). To create a client, take the following steps: 1. Navigate into the **API** tab. 2. Click to **create a new client**. ![cluster-details](img/cluster-detail-clients.png) 3. In the opened dialog, name your client and define it's **scope**. ![create-client](img/cluster-details-create-client.png) 4. After creating the client, you can select the format for your credentials. Ensure you keep the generated client credentials in a safe place. The **client secret** will not be shown again. For your convenience, you can also download the client information to your computer. ![created-client](img/cluster-details-created-client.png) The downloaded file contains all necessary information to communicate with your Zeebe instance in the future: - `ZEEBE_ADDRESS`: Address where your cluster can be reached. - `ZEEBE_CLIENT_ID` and `ZEEBE_CLIENT_SECRET`: Credentials to request a new access token. - `ZEEBE_AUTHORIZATION_SERVER_URL`: A new token can be requested at this address. - `ZEEBE_TOKEN_AUDIENCE`: The audience for a Zeebe token request. - `CAMUNDA_CLUSTER_ID`: The UUID of the cluster. - `CAMUNDA_CLUSTER_REGION`: The region of the cluster. - `CAMUNDA_CREDENTIALS_SCOPES`: A comma-separated list of the scopes this credential set is valid for. - `CAMUNDA_OAUTH_URL`: A new token can be requested at this address using the credentials. Duplicates the earlier Zeebe-focused variable. Depending on the scopes granted to these client credentials, the following variables may also be present: - `CAMUNDA_TASKLIST_BASE_URL`: The base URL for Tasklist. - `CAMUNDA_OPERATE_BASE_URL`: The base URL for Operate. - `CAMUNDA_OPTIMIZE_BASE_URL`: The base URL for Optimize. ## Rate limiting The OAuth service rate limits about one request per second for all clients with the same source IP address. :::note All token requests count toward the rate limit, whether they are successful or not. If any client is running with an expired or invalid API key, that client will continually make token requests. That client will therefore exceed the rate limit for that IP address, and may block valid token requests from completing. ::: The officially offered [client libraries](/apis-tools/working-with-apis-tools.md#official-camunda-clients-and-sdks) (as well as the Node.js and Spring clients) have already integrated with the auth routine, handle obtaining and refreshing an access token, and make use of a local cache. If too many token requests are executed from the same source IP address in a short time, all token requests from that source IP address are blocked for a certain time. Since the access tokens have a 24-hour validity period, they must be cached on the client side, reused while still valid, and refreshed via a new token request once the validity period has expired. When the rate limit is triggered, the client will receive an HTTP 429 response. Note the following workarounds: - Cache the token as it is still valid for 24 hours. The official SDKs already do this by default. - Keep the SDK up to date. We have noted issues in older versions of the Java SDK which did not correctly cache the token. - Given the rate limit applies to clients with the same source IP address, be mindful of: - Unexpected clients running within your infrastructure. - Updating all clients to use a current API key if you delete an API key and create a new one. --- ## Manage your cluster Learn how to rename, resume, update, resize, or delete your cluster. ## Rename a cluster A cluster can be renamed at any time. To rename your cluster, follow the steps below: 1. Open the cluster details by clicking on the cluster name. 2. Select the three vertical dots next to the cluster name near the top of the page to open the cluster's menu. 3. Click **Rename**. ![cluster-rename](./img/cluster-rename.png) ## Resume a cluster You can resume your paused cluster during deployment, or from Camunda Hub at any time. ### Resume during deployment During deployment, you can resume the selected cluster if it is paused. ![Resume a paused cluster during deployment](./img/cluster-resume-deploy.png) 1. Select your paused cluster during deployment. 1. Select **Resume** in the paused cluster notification. ### Resume from Camunda Hub You can resume your paused cluster from Camunda Hub at any time. ![Resume a paused cluster from Camunda Hub](./img/cluster-resume-console.png) 1. In Camunda Hub, select the **Clusters** tab. 1. The cluster **Status** shows "Paused" if a cluster is paused. Select the cluster that you want to resume. 1. On the cluster **Overview** tab, select **Resume cluster** in the **Status** row of the **Cluster Details**. ## Update a cluster :::caution Updating a cluster is permanent. Updated clusters cannot be reverted to the previous version. ::: You can manually or automatically update a cluster to a new Camunda 8 version. - If a cluster can be updated, a **Review Update** button is shown. - Currently, updates do not automatically trigger backups. You can start a manual backup via the Camunda Hub **Backups** tab. ### Minor updates If you update a cluster to another minor version, you cannot immediately update the cluster again until a 24-hour period has elapsed. This ensures all background processes have completed and the cluster is ready for further updates. This does not apply when upgrading between generations of the same minor version. | Example scenario | Time limit applied? | | :------------------------- | :--------------------------------------------------------- | | `8.8 gen22` to `8.9 gen1` | 24 hours required before the cluster can be updated again. | | `8.8 gen22` to `8.8 gen23` | No time limit applied. | :::note Clusters must be healthy before an update can be performed. ::: ### Update a cluster manually If an update is available, a **Review Update** button is shown. :::note This button is not available for clusters enrolled in [automatic updates](/components/saas/auto-updates.md). ::: ### Automated cluster updates You can decide if you want to have [automated updates](/components/saas/auto-updates.md) to new versions of Camunda 8 activated. You can also toggle this feature anytime later in the **Settings** tab of your cluster. ## Resize a cluster You can increase or decrease the [cluster size](/components/concepts/clusters.md#cluster-size) at any time. For example, increase the cluster size to improve performance and add capacity, or decrease the cluster size to free up reservations for another cluster. 1. Open the cluster details by clicking on the cluster name. 1. Select **Resize cluster** next to the cluster type. 1. Select the new cluster size from the available sizes. 1. Click **Confirm** to resize the cluster, or **Cancel** to close the modal without resizing the cluster. :::note To increase the cluster size beyond the maximum 4x size, [reach out to Camunda](https://camunda.com/contact-us/). This requires custom sizing and pricing. ::: ## Delete a cluster :::caution Deleting a cluster is **permanent** and cannot be undone. ::: A cluster can be deleted at any time. To delete your cluster, navigate to the **Clusters** tab in the top navigation and click **Delete** to the far right of the cluster name. --- ## Manage your connectors Monitor and manage inbound connectors running on your cluster. ## About connector management Cluster connector management allows you to monitor and manage your running inbound connector [webhooks, message queue subscriptions, and polling subscriptions](/reference/glossary.md#inbound-connector). - Use this feature to check your inbound connectors are healthy and running, and troubleshoot unhealthy connectors. - For example, you can see if a connector instance is unhealthy, and use the [activity log](#activity-log) to troubleshoot and resolve issues. ## Connector Management To open the **Connector Management** page, on the cluster **Overview** tab, click **Manage** on the Connectors component tile. The **Connector Management** page provides an overview of the inbound connectors running on a cluster: - Each inbound connector running on the cluster is shown on a separate row. - **Name**: The name of the connector. Click to view details of the connector instances for this connector. - **Active instances**: How many process instances are running for the connector. The icon indicates if the running connector instances are healthy or require attention. :::note [Webhook connector](/components/connectors/protocol/http-webhook.md) names also include the names of any connector based on the webhook. For example, "_Webhook (aws:eventbridge, GitHubWebhook)_". ::: ## View connector instances Select an individual connector to view the running instances for the connector. - Each connector process instance is shown on a separate row. - **Connectors instance ID**: The ID of the connector instance. Click to view further details for an individual connector instance. - **Elements**: The element that the process instance is active for. This helps you locate the element in your BPMN diagram. - **Process** The process instance ID and version. - **Activation date**: The date and time when the instance was activated. - **Status**: The health of the connector instance. - **Healthy**: The connector is running without problems in the process. - **Unhealthy**: There are unresolved issues with the connector instance. View the details of the instance to troubleshoot the problem, for example by using the activity log to determine what the issue is and how to resolve it. ## View connectors instance details Select an individual connector running instance to view additional details and troubleshoot issues. ### Activity log Shows details of the last ten activities recorded for the connector, such as an API method (GET, POST, PUT, and DELETE) or message subscription. You can use these logs to troubleshoot unhealthy connector instances. For example, the following activity log shows that there is an exception caused by an invalid URL in a Kafka connector. ### Properties Shows general properties for the connector template as a JSON object. For example: ```json { "deduplicationModeManualFlag": "false", "schemaStrategy.type": "noSchema", "topic.topicName": "rereer", "authenticationType": "credentials", "correlationRequired": "notRequired", "topic.bootstrapServers": "eererreer", "autoOffsetReset": "latest" } ``` ### Process info Shows more detailed information of the BPMN process instance and its associated connector as a JSON object. You can view additional metadata about the process, the connector template, and the connector's configuration properties. For example: ```json [ { "bpmnProcessId": "Process_0wjo4ez", "version": 1, "processDefinitionKey": 2251799813686169, "elementId": "StartEvent_1", "elementName": null, "elementType": "startEvent", "tenantId": "", "elementTemplateDetails": { "id": "io.camunda.connectors.inbound.KafkaMessageStart.v1", "version": "6", "icon": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0nMTgnIGhlaWdodD0nMTgnIHZpZXdCb3g9JzAgMCAyNTYgNDE2JyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHByZXNlcnZlQXNwZWN0UmF0aW89J3hNaWRZTWlkJz4KICAgIDxwYXRoIGQ9J00yMDEuODE2IDIzMC4yMTZjLTE2LjE4NiAwLTMwLjY5NyA3LjE3MS00MC42MzQgMTguNDYxbC0yNS40NjMtMTguMDI2YzIuNzAzLTcuNDQyIDQuMjU1LTE1LjQzMyA0LjI1NS0yMy43OTcgMC04LjIxOS0xLjQ5OC0xNi4wNzYtNC4xMTItMjMuNDA4bDI1LjQwNi0xNy44MzVjOS45MzYgMTEuMjMzIDI0LjQwOSAxOC4zNjUgNDAuNTQ4IDE4LjM2NSAyOS44NzUgMCA1NC4xODQtMjQuMzA1IDU0LjE4NC01NC4xODQgMC0yOS44NzktMjQuMzA5LTU0LjE4NC01NC4xODQtNTQuMTg0LTI5Ljg3NSAwLTU0LjE4NCAyNC4zMDUtNTQuMTg0IDU0LjE4NCAwIDUuMzQ4LjgwOCAxMC41MDUgMi4yNTggMTUuMzg5bC0yNS40MjMgMTcuODQ0Yy0xMC42Mi0xMy4xNzUtMjUuOTExLTIyLjM3NC00My4zMzMtMjUuMTgydi0zMC42NGMyNC41NDQtNS4xNTUgNDMuMDM3LTI2Ljk2MiA0My4wMzctNTMuMDE5QzEyNC4xNzEgMjQuMzA1IDk5Ljg2MiAwIDY5Ljk4NyAwIDQwLjExMiAwIDE1LjgwMyAyNC4zMDUgMTUuODAzIDU0LjE4NGMwIDI1LjcwOCAxOC4wMTQgNDcuMjQ2IDQyLjA2NyA1Mi43Njl2MzEuMDM4QzI1LjA0NCAxNDMuNzUzIDAgMTcyLjQwMSAwIDIwNi44NTRjMCAzNC42MjEgMjUuMjkyIDYzLjM3NCA1OC4zNTUgNjguOTR2MzIuNzc0Yy0yNC4yOTkgNS4zNDEtNDIuNTUyIDI3LjAxMS00Mi41NTIgNTIuODk0IDAgMjkuODc5IDI0LjMwOSA1NC4xODQgNTQuMTg0IDU0LjE4NCAyOS44NzUgMCA1NC4xODQtMjQuMzA1IDU0LjE4NC01NC4xODQgMC0yNS44ODMtMTguMjUzLTQ3LjU1My00Mi41NTItNTIuODk0di0zMi43NzVhNjkuOTY1IDY5Ljk2NSAwIDAgMCA0Mi42LTI0Ljc3NmwyNS42MzMgMTguMTQzYy0xLjQyMyA0Ljg0LTIuMjIgOS45NDYtMi4yMiAxNS4yNCAwIDI5Ljg3OSAyNC4zMDkgNTQuMTg0IDU0LjE4NCA1NC4xODQgMjkuODc1IDAgNTQuMTg0LTI0LjMwNSA1NC4xODQtNTQuMTg0IDAtMjkuODc5LTI0LjMwOS01NC4xODQtNTQuMTg0LTU0LjE4NHptMC0xMjYuNjk1YzE0LjQ4NyAwIDI2LjI3IDExLjc4OCAyNi4yNyAyNi4yNzFzLTExLjc4MyAyNi4yNy0yNi4yNyAyNi4yNy0yNi4yNy0xMS43ODctMjYuMjctMjYuMjdjMC0xNC40ODMgMTEuNzgzLTI2LjI3MSAyNi4yNy0yNi4yNzF6bS0xNTguMS00OS4zMzdjMC0xNC40ODMgMTEuNzg0LTI2LjI3IDI2LjI3MS0yNi4yN3MyNi4yNyAxMS43ODcgMjYuMjcgMjYuMjdjMCAxNC40ODMtMTEuNzgzIDI2LjI3LTI2LjI3IDI2LjI3cy0yNi4yNzEtMTEuNzg3LTI2LjI3MS0yNi4yN3ptNTIuNTQxIDMwNy4yNzhjMCAxNC40ODMtMTEuNzgzIDI2LjI3LTI2LjI3IDI2LjI3cy0yNi4yNzEtMTEuNzg3LTI2LjI3MS0yNi4yN2MwLTE0LjQ4MyAxMS43ODQtMjYuMjcgMjYuMjcxLTI2LjI3czI2LjI3IDExLjc4NyAyNi4yNyAyNi4yN3ptLTI2LjI3Mi0xMTcuOTdjLTIwLjIwNSAwLTM2LjY0Mi0xNi40MzQtMzYuNjQyLTM2LjYzOCAwLTIwLjIwNSAxNi40MzctMzYuNjQyIDM2LjY0Mi0zNi42NDIgMjAuMjA0IDAgMzYuNjQxIDE2LjQzNyAzNi42NDEgMzYuNjQyIDAgMjAuMjA0LTE2LjQzNyAzNi42MzgtMzYuNjQxIDM2LjYzOHptMTMxLjgzMSA2Ny4xNzljLTE0LjQ4NyAwLTI2LjI3LTExLjc4OC0yNi4yNy0yNi4yNzFzMTEuNzgzLTI2LjI3IDI2LjI3LTI2LjI3IDI2LjI3IDExLjc4NyAyNi4yNyAyNi4yN2MwIDE0LjQ4My0xMS43ODMgMjYuMjcxLTI2LjI3IDI2LjI3MXonCiAgICAgICAgICBzdHlsZT0nZmlsbDojMjMxZjIwJy8+Cjw" }, "properties": { "deduplicationMode": "AUTO", "deduplicationModeManualFlag": "false", "schemaStrategy.type": "noSchema", "topic.topicName": "rereer", "consumeUnmatchedEvents": "true", "inbound.type": "io.camunda:connector-kafka-inbound:1", "authenticationType": "credentials", "correlationRequired": "notRequired", "topic.bootstrapServers": "eererreer", "autoOffsetReset": "latest" } } ] ``` :::note If you are using deduplication, each connector occurrence in the BPMN diagram is shown in the array. ::: --- ## IP allowlists :::info Web Modeler access In Camunda SaaS, Web Modeler has access to clusters by default, as allowlist assignments for IP addresses used by Web Modeler are managed automatically. ::: If your organization works within Camunda's [Enterprise](https://camunda.com/pricing/) plan, you can restrict access to clusters with an IP allowlist. ## Create an IP allowlist To create an IP allowlist, take the following steps: 1. Select the **IP Allowlist** tab. ![cluster-details](./img/cluster-detail-ip-whitelists.png) 2. Click **Create an entry** to create an IP allowlist. ![create-alert](./img/cluster-detail-create-ip-whitelist.png) 3. Enter a list of IPs or CIDR blocks separated by commas. 4. Enter an optional description for the allowlist. --- ## Connector secrets Create secrets and reference them in your connectors without exposing sensitive information in your BPMN processes. :::caution **Connector secrets** are managed at the cluster level, so ensure you deploy your processes to the cluster that contains the necessary secrets. If you deploy and the secret is missing, [Operate](../../../operate/operate-introduction.md) will show an incident. ::: To create a new secret, go to your cluster and take the following steps: 1. Select the **Connector secrets** tab. ![secrets](./img/cluster-detail-secrets.png) 1. Click **Create new secret**. 2. Provide a **Key** for your secret that you will use to reference your secret from your connector. 3. Provide the **Secret** that will be assigned to the **Key**. ![secrets-create](./img/cluster-detail-secrets-create.png) 4. Click **Create** and view your new secret in the list. ![secrets-view](./img/cluster-detail-secrets-view.png) ## Use secrets in a workflow Secrets are used inside connector tasks in your BPMN model. Add a connector task, then reference the secret key in a field that supports secrets. Example for a plain text field (for example, an authorization header value): ``` Bearer {{secrets.MY_API_KEY}} ``` Example for a FEEL expression (note the double quotes around the placeholder): ```feel = { myHeader: "{{secrets.MY_API_KEY}}" } ``` For more details on where secrets are supported, see the [Connectors guide](/components/connectors/use-connectors/index.md#using-secrets). Now you can reference your secret in any connector as described in the [Connectors guide](/components/connectors/use-connectors/index.md#using-secrets). :::note Find more information on managing [Connector secrets](/self-managed/components/connectors/connectors-configuration.md). ::: --- ## Settings Manage your cluster settings using authorizations, automatic cluster updates, and user task restrictions, or permanently delete the cluster. ## Manage cluster settings To manage your cluster settings: 1. Navigate to **Camunda Hub**, and select the **Clusters** tab. 2. Select the cluster you want to manage, and select the **Settings** tab. 3. Enable/disable cluster settings as required, or delete the cluster. ![Cluster settings](./img/cluster-settings.png) ## Authorizations You can enable authorizations on a per-cluster basis to control the level of access users and clients have over Orchestration Cluster resources. - Enable this setting to use [authorizations](/components/concepts/access-control/authorizations.md) in the cluster. - Disable this setting if you do not want to use authorizations in the cluster. You can still configure authorizations in the Orchestration Cluster Admin, but they are only applied to cluster when you enable this setting. :::tip For more information, see [authorizations](/components/concepts/access-control/authorizations.md). ::: ## Automatic cluster updates You can set the cluster to automatically update to newer versions of Camunda 8 when they are released. - Enable this setting to automatically update the cluster when a new patch release is available. During an update, the cluster may be unavailable for a short time. You can still manually update the cluster. - Disable this setting if you do not want the cluster to automatically update. You must manually update the cluster. :::tip For more information on updating clusters, see [update your cluster](/components/hub/organization/manage-clusters/manage-cluster.md#update-a-cluster). ::: ## Enforce user task restrictions Starting with Camunda 8.10, this cluster setting is no longer available because user task access restrictions were removed together with Tasklist V1. :::note Use [authorization-based access control](../../../concepts/access-control/authorizations.md) and [user task authorization](/components/tasklist/user-task-authorization.md) to control task visibility and operations in current Tasklist deployments. ::: ## Delete this cluster You can _permanently_ delete the selected cluster. See [delete your cluster](/components/hub/organization/manage-clusters/manage-cluster.md#delete-a-cluster). :::caution Deleting a cluster is permanent. You cannot reuse a cluster after it has been deleted. ::: --- ## Troubleshoot clusters Review common issues and how to resolve them. ## I cannot connect to Zeebe - Check if your [API client](./manage-api-clients.md) has the necessary rights. To interact with Zeebe, the **Scope** `Zeebe` must be set. - Check if your credentials are configured correctly. There is a community-supported CLI tool that allows you to check the status: [`zbctl`](https://www.npmjs.com/package/zbctl). With the command `zbctl status`, you can read the topology. If this command works, the connection can be established. - Check if your cluster is **Healthy**: A Zeebe cluster may be temporarily unavailable. To check if your cluster is healthy, navigate to the **Clusters** tab in the top navigation. Click on the cluster to view its details for a closer view of the status over all components (Zeebe, Operate, Tasklist, Optimize). --- ## Manage user groups Organize users into groups within your organization. ## Creating a group To create a group, navigate to the **Organization** section of Camunda Hub and click on the **Groups** tab. ![Groups Management](./img/group-management.png) Click **Create a group** and enter the name of the group. ![Create a group](./img/create-group.png) ## Adding users to a group To add users to a group, navigate to the **Organization** section of Camunda Hub and click **Users > Assign members**. ![Groups Members](./img/group-members.png) Select the user you want to add to a group and click **Assign**. ![Assign a Member](./img/assign-member.png) ## User task access restrictions :::info User task access restrictions were removed in Camunda 8.10 together with Tasklist V1. Use [user task authorization](/components/tasklist/user-task-authorization.md) and [authorization-based access control](/components/concepts/access-control/authorizations.md) for current task visibility rules. ::: --- ## Manage users in your organization When a user signs up for Camunda 8 as the first user from their organization, company, or group, they become the owner of the Camunda organization. This organization owns Modeler files and Zeebe clusters. The owner and any admins they assign can control access to these resources through managing their organization. ## Users An owner has all rights in an organization and can manage all settings accordingly. An organization cannot have more than one owner. To change the owner of the organization, utilize the user administration. The current owner selects another member of the organization, and selects **Assign as owner** from the menu. In the dialog that appears, select which new roles are to be assigned to the current owner. ### Roles and permissions In addition to the owner, the **Admin** role is available as a second role with comprehensive rights. The admin role has full access to the platform, process resources, and clusters, but cannot manage other admins. The following roles are additionally available, providing dedicated rights for specific elements in Camunda 8: - **Modeler**: Access to Web Modeler for creating and collaborating on projects, except permissions to deploy and run processes. Read-only access to Camunda Hub. - **Analyst**: Includes Modeler permissions and has full access to Optimize to build process dashboards and reports. Starting with version 8.8, user access to clusters is managed independently. To control what a user can access, define their authorizations in the cluster's Admin. Learn more [here](/components/admin/authorization.md). If cluster authorizations are disabled, the user will have full access to the cluster and its components. Users can be assigned multiple roles. For example, a user can have both **Modeler** and **Analyst** roles, giving them access to Web Modeler and Optimize. Users are invited to a Camunda 8 organization via their email address, which must be accepted by the user. The user remains in the `Pending` state until the invitation is accepted. People who do not yet have a Camunda 8 account can also be invited to an organization. To access the organization, the invited individual must first create a Camunda 8 account by following the instructions in the invitation email. ## Resource-based authorizations Resource authorizations control a user's access to specific resources. To create, update, or delete a user's resource authorizations, select the user's row in the users table. As of 8.8, authorizations for Orchestration Cluster applications (Zeebe, Operate, and Tasklist) are managed as part of the Orchestration Cluster and configured in [Admin](/self-managed/components/orchestration-cluster/admin/overview.md). ### Creation To initiate the creation flow, click **Create resource authorization**. ![User Details](./img/user-details-authorized-resources.png) ### Updating and deleting To update an existing authorization, click on the **pencil icon** of the relevant row. To delete an existing authorization, click the **trash can** icon. ![Authorized Resources](./img/user-details-authorized-resources-example.png) ## User task access restrictions :::note User task access restrictions were removed in Camunda 8.10 together with Tasklist V1. Use [authorization-based access control](../../../concepts/access-control/authorizations.md) and [user task authorization](/components/tasklist/user-task-authorization.md) to control user access to tasks in the current version. ::: ## Limitations Depending on the plan to be used, the number of users that can be part of an organization varies. ## Restrictions In Enterprise plans, the hostname section of the email address for invites can be restricted to meet your internal security policies. [Contact Camunda support](https://camunda.com/services/support/) to get this configured according to your needs. --- ## Advanced search To ease navigation throughout Camunda Hub, utilize the **search functionality** available in Camunda 8 SaaS under the **Camunda Hub** component. This search functionality allows users to: - Navigate between apps, actions, and tasks (e.g. go to Modeler, invite users, API clients, etc.) - Locate Camunda project assets (e.g. BPMN, DMN models, and clusters). - Find all entries in the Camunda forum, documentation, and public GitHub issues. ## Open the search bar Press `Ctrl+K`, `⌘+K`, or click the magnifier in the top navigation bar to open the search bar. ![Open the search bar](./img/open_console_search.png) ## Tips - Type `>` to execute an action in Camunda Hub. By using `#` as a prefix, you search only in docs. - Use the keyboard arrows (◀ ▲ ▼ ▶) to navigate through results. - If you observe a ▶ symbol on the right-hand side, click to reveal more information. - To select a result, press **Enter**. --- ## Delete your Camunda account To delete your Camunda account, take the following steps: 1. Log in to your Camunda account. 2. Ensure you are in Camunda Hub. To do this, select the square-shaped **Camunda components** icon in the top left corner of your screen and select **Camunda Hub**. 3. Select the top right **Open Settings** user icon. 4. Select **Delete account** at the bottom of the panel. ![avatar-menue](./img/delete-account.png) If you are the only member of this organization, your free trial organization and its cluster will be deleted as well. --- ## Enable alpha features :::note Opting-in to Camunda alpha terms currently only applies to Enterprise SaaS subscriptions. Customers in other subscriptions can still turn on/off these settings directly from Camunda Hub. ::: If you aren't already familiar with accessing **alpha features**, learn more in our [alpha feature documentation](/components/early-access/alpha/alpha-features.md). Alpha terms typically refer to the specific terms and conditions that govern the use and testing of this software during its alpha phase. These terms outline the rights, responsibilities, and limitations of both the software provider (Camunda) and the users (alpha testers or early adopters) during the testing and evaluation period. Alpha terms help protect Camunda´s interests (such as protecting our intellectual property, disclaiming warranties, or limiting our liability for any issues or damages that may arise during the alpha phase), manage user expectations, encourage active participation and feedback, and ensure legal compliance during the pre-release phase of software development. :::note Enabling alpha features is limited to [admin users and owners](/components/hub/organization/manage-members/manage-users.md) of Camunda products. ::: ## Accept alpha terms To accept alpha terms for Camunda products, follow the steps below: 1. Log in to Camunda Hub, and click the **Organization** tab to view the overview for **Organization Management**. 2. Under the **Settings** tab, click **Opt-in** under the **Alpha features** box. 3. Note the **Alpha Terms** modal. As the admin accepting the alpha terms, you must scroll and read through the terms before accepting. 4. Once you have read the terms and scrolled through the modal, tick the box at the bottom reading **"I understand and agree to Alpha Terms"**. The system will confirm your acceptance and send a copy of the accepted alpha terms to your email address. ## Manage alpha features Once you accept the alpha terms, you can enable and disable any features you would like to use, and learn more about them: - In the **Settings** tab, toggle the switch under **Status** to enable and disable the feature. - Click **View docs** under **Documentation** to learn more about the feature. - Admins can know when someone accepts the alpha terms and when features are enabled or disabled under the **Activity** tab in **Organization Management**. ### Enable AI-powered features To use AI-powered alpha features, the **Terms for AI Usage** must be agreed to before the toggle will appear in the **Status** column. 1. In the **Settings** tab, click the **Opt-in to enable** link under **Status**. 2. A dialog will appear containing the **Terms for AI Usage**. You must read to the end of the terms before accepting. 3. To agree to the terms, select the box labeled **I understand and agree to the Terms for AI Usage**. 4. The AI-powered features toggle will now be available in **Settings**. Set this toggle to **Enabled** to turn on AI features. --- ## Connect to an identity provider(Manage-organization-settings) Enterprise plan customers can integrate an external identity provider (IdP) with Camunda. This means users within your organization do not need to sign up by creating a Camunda account. ## Onboarding procedure We currently support both SAML and Azure Active Directory (Azure AD). As this requires changes in our environment, first raise a ticket in the [support queue](https://jira.camunda.com/projects/SUPPORT/). :::info Expiring certificates or secrets must be renewed prior to their expiration date to avoid a service interruption. ::: ### SAML After opening the ticket in the support queue, we will provide you: - **Assertion Customer URL**: e.g. `https://weblogin.cloud.camunda.io/login/callback?connection=CUSTOMER_CONNECTION` - **Entity ID**: e.g. `urn:auth0:camunda:CUSTOMER_CONNECTION` You will then need to provide: - The domain used for the login email addresses - A sign-in URL - A x509 signing certificate ### Azure AD For Azure AD, you will need to provide: - The domain used for the login email addresses - The Microsoft Azure AD domain - The generated client ID - The client secret value To generate the client on your end, you will need to use the Camunda **Redirect URL** `https://weblogin.cloud.camunda.io/login/callback `. Ensure you attach the user permissions `Users > User.Read`. ### Default organizations Enterprise only :::info Default organizations for external identity providers are only available for organizations on an Enterprise plan. ::: By setting up an external identity provider, it is possible to configure up to 10 default organizations. The following information must be added in the ticket so that the support team can configure the default organizations: - Organization ID - Default organization roles If a user logs in with the configured connection, the user is automatically assigned to these organizations with the corresponding roles. ### Additional information In some situations, you might need to access `openid-configuration` to establish the connection from your end. See [this OpenID configuration](https://weblogin.cloud.camunda.io/.well-known/openid-configuration) as an example. ## Login If your organization is using social login procedures (like GitHub or Google), these procedures will not work when using your own IdP with Camunda. Users must log in by providing their email address on the login screen. --- ## Available plans The following Camunda 8 plans are available: | Plan | Description | | :--------- | :----------------------------------------------------------------------------- | | Free trial | Create a free account to try Camunda in our cloud. | | Enterprise | Made for high volume automation and end-to-end process orchestration. We host. | :::info - To sign up for Camunda 8 and compare plan features, refer to [Camunda 8 pricing](https://camunda.com/pricing/?utm_source=docs.camunda.io&utm_medium=referral). - For more information on Camunda 8, refer to [Camunda 8 product](https://camunda.com/products/cloud/). ::: --- ## Create an account Create a Camunda 8 account to create clusters, deploy processes, and create a new instance. :::note We're gradually rolling out changes that affect this page to users; your experience may vary. ::: Visit [signup.camunda.com/accounts](https://signup.camunda.com/accounts?utm_source=docs.camunda.io&utm_medium=referral) to create your account: ![signup](./img/signup.png) ### Create an account Fill out the form and submit, or sign up using the social sign up buttons like Google or GitHub. When you fill out the form, you'll receive a confirmation email. Click on the link to verify your email address. If you choose to create an account through the social sign up buttons, you'll be redirected to [Camunda Hub](/components/hub/index.md) directly. ## Log in to your Camunda 8 account Log in with the email address and password you used in the previous form, or use the social login buttons. To access the login site directly, navigate to [camunda.io](https://weblogin.cloud.camunda.io/). ![login](./img/login.png) After login, select the square-shaped **Camunda components** icon in the upper-left corner, and select Camunda Hub to view the Camunda Hub overview page. This is the central place to manage the clusters, diagrams, and forms you want to deploy to Camunda 8. ![overview-home](./img/home.png) ## Additional resources - [Camunda Academy: Create a Camunda 8 Account](https://academy.camunda.com/c8-h2-create-account?reg=1) --- ## Update billing reservations :::note This setting is only visible in the **Enterprise plan** for owners and admins. ::: ## Managing hardware packages Once signed up for the **Enterprise plan**, you have access to the **Billing** page. - The created process instances from the current period are displayed at the top of the page. - Find a history of the metrics on a monthly basis at the bottom of the page. - View how many hardware packages are included on the right side of the page. Reservations control how many clusters you can deploy. Increasing the number of reservations allows you to deploy more clusters, while decreasing the number of reservations allows you to deploy fewer clusters. You can access the **Billing** page by selecting **Organization management** in the Camunda Hub navigation bar. ![billing-overview](./img/billing-overview.png) --- ## Upgrade to an Enterprise plan To upgrade to the Enterprise plan, select the Organization management **Plans and pricing** tab, and click **Request quote**. ![paid-request](./img/checkout.png) --- ## Organization management Organization management can be accessed via the **Open Organizations** icon in the navigation bar. ![Open Organizations icon in navigation bar](./img/avatar-menue.png) Using the context menu of each organization, you can manage or leave an organization. ### Overview The overview provides a summary of the organization, including: - Organization name - Pricing plan - Owner of the organization Here, owners of an organization can also manage users, view organization history, and create client credentials. #### Rename organization If you are the owner of the organization, you can change the name of your organization in the **Overview** tab. ## Next steps - [Manage users in your organization](../manage-members/manage-users.md) - [View organization activity](./view-organization-activity.md) --- ## Switch organization If a user is assigned to more than one organization, the organization can be changed by clicking the **Open Organizations** icon in the top right corner of the screen. ![open organizations icon in top right corner](./img/avatar-menue-multiple-organisations.png) Here, you can view the organization you are actively in, or click into another organization below the **Other Organizations** header. You can also click **Manage** to go to the **Organization management** page. --- ## Usage alerts :::note Usage alerts apply **only to production clusters** and are visible only to owners and admins in **Starter** and **Enterprise** organizations. ::: Under the **Billing** tab, organization owners and admins can set up alerts for process instances, decision instances and task users. Usage is calculated daily. When the threshold for an alert is met, all organization owners and admins are alerted via email and in-app notification. ## Creating a usage alert To create a usage alert, take the following steps: 1. From the Camunda Hub, click **Organization > Billing**. 2. On the **Billing** page, select **Edit alert** next to the metric you want to configure the usage alert for (e.g. process instances). ![Edit Usage Alert](./img/edit_usage_alert.png) 3. In the modal, define the percentage threshold (e.g. 80 for 80%), turn on the alert, and click **Save**. Note that the threshold can be between 1% and 4999%. ![Set Alert](./img/set_up_usage_alert.png) :::note The threshold set is calculated on a percentage ratio between your consumption and the amounts included in your plan. For example, when a threshold is set at 50% for a plan that includes 200 process instances, an alert is sent when 100 process instances are reached. ::: Usage is calculated daily. When the threshold for an alert is met, all org owners and admins are alerted via email and in-app notification (examples below). ![Set Alert](./img/email_usage_alert.png) ![Set Alert](./img/notification_usage_alert.png) ## Editing a usage alert Usage alerts can be edited and turned on or off anytime by selecting **Edit alert** and updating the toggle. ![Turn Off Alert](./img/turn_off_usage_alert.png) ## Viewing an alert change log Users can track changes in the usage alerts under the logs of the **Activity** tab. ![Usage Alert Logs](./img/usage_alerts_logs.png) --- ## Usage history :::note The usage history is visible only to owners and admins for Trial, Enterprise, and Starter organizations. ::: Three key metrics play a role in paid plans: the number of started process instances, decision instances, and the number of task users. The **Usage** tab, under the **Organization management**, provides a usage view for these metrics across the organization. The section is split into two areas: **Table view**, where data from production clusters is displayed aggregated on a monthly basis: ![Usage History - Table View](./img/plans_usage_history_table_view.png) **Chart view**, where data from production and development clusters is displayed in charts and allows a closer look into usage patterns and spikes by customizing the date ranges. ![Usage History - Chart View](./img/plans_usage_history_chart_view.png) --- ## View organization activity You can view all activity within an organization on the **Activity** tab. For example, you can see details for cluster creation, deletion, updates, and user invitations. ![activity-view](./img/activity-view.png) ## Export activity Click **Export activity**, and select whether to export and download the activity list as a JSON or CSV file. --- ## Manage workspaces Create and manage workspaces within your organization. ## About workspaces In Camunda Hub, a workspace is a collaboration environment within an organization, representing a team or business domain. A workspace is assigned members, roles, projects, and clusters, so all related work happens in one shared space. You can create and manage workspaces at the organization level. :::info You can only manage workspaces at the organization level if you're an **Organization admin** or **Organization owner**. ::: --- ## Workspace Discover and use approved reusable assets, manage projects, and deliver business processes. ## About the workspace With Camunda Hub, you'll collaborate in managed workspaces, discover and use approved catalog assets, and model and deploy business processes. ## Manage projects Develop project releases through the stages of a typical development lifecycle: ## Manage workspace settings Manage workspace members, clusters, and general information: [Get started](./manage-workspace/index.md) ## Model business processes Collaboratively design executable processes as the foundation for scalable IT and business automation: --- ## Create a project Create a project work on and deploy a set of related files. ## Create a project To create a project: - Create a workspace, if you don't already have one. - In the workspace, select **New Project**. ### Select a default cluster Camunda 8 SaaS only When you create a project, you must select a development cluster to deploy to. - If you have already created a cluster, turn on the toggle next to the cluster name in **Select a development cluster**. - If you have not yet [created a cluster](/components/hub/organization/manage-clusters/create-cluster.md), or if **No Cluster has been created yet** is shown, select **Create new dev cluster**. The Camunda Hub **Clusters** tab opens so you can create a new cluster. :::note - If your modeling plan does not allow you to create more clusters, a `Cluster creation is not available on this modeling plan` warning is shown. Contact the administrator or owner of your organization to upgrade your plan. - If you do not have the proper permissions to create a cluster, a `Missing permissions` warning is shown. Contact the administrator or owner of your organization to create a cluster. ::: ### Project homepage After creating the project, you land on the project homepage. From there, you can manage and explore files, preview the README if it exists, create and manage versions, set up and sync with a Git repository, connect clusters to the project, and deploy or run the project. :::note You may not see some of these options depending on your project permissions. ::: ### Add files to a project When you create a project, a new BPMN diagram is automatically created. To add more files to the project, either: - Select the **Create new** dropdown on the project page to create and add a new file. - Drag and drop files from your local computer. - Move an existing file into the project from a different location in Web Modeler. You can also create subfolders to organize files within the project. :::note Process IDs, decision IDs, and form IDs must be unique across all files within a project. This avoids ambiguity and conflicts when you link resources and deploy the project. ::: --- ## Validate and deploy your project Validate your project in development before deploying it to testing, staging, or production. ## Validate your project Use [Play mode](/components/hub/workspace/modeler/validation/play-your-process.md) to validate your project in development. 1. Open the BPMN diagram in the project that you want to validate. 1. Select the **Play** tab to play the project using your selected development cluster. 1. Perform validation as required, for example, debug your process logic and test the project. :::info To learn more about using Play for validation, see [Play mode for rapid validation](/components/hub/workspace/modeler/validation/play-your-process.md) ::: ## Deploy your project ### Before deploying a project - If the target cluster has [authorizations](/components/admin/authorization.md) enabled, make sure that the deploying users have `CREATE` permission to the `RESOURCE` resource type. Once validation is complete, deploy your project to cluster stages in your [development lifecycle](./project-pipeline.md), such as testing, staging, or production. For example, deploy to your testing cluster to run automated tests or make it available for testing. 1. Open the [project homepage](create-a-project.md#project-homepage). 1. Select the **Deploy latest changes** option from the **Deploy & run** combo button to open the **Deploy & run** modal. 1. Turn on the toggle for the cluster stage you want to deploy to. In Self-Managed, you may be prompted to enter your cluster details manually if no [configuration](/self-managed/components/hub/configuration/modeler-configuration.md#clusters) is provided. 1. Perform any other actions as required, such as: - Unpausing the chosen cluster if it has been auto-paused. Select **Resume cluster** within the **Cluster Details**. - Managing the cluster. Select **Manage**. 1. Select **Deploy** to deploy the project to the selected cluster. When you deploy from the project homepage, all BPMN, DMN, and form files in the project are deployed as a single bundle. In Self-Managed, you can deploy your project to the cluster defined in your Web Modeler [configuration](/self-managed/components/hub/configuration/modeler-configuration.md#clusters). :::note If any resource fails to deploy, the whole deployment [fails](#deployment-errors) and the cluster state remains unchanged. This safely ensures that a project cannot be deployed incompletely or in an inconsistent state. ::: You can also open the deployment modal from the details page of any deployable file in the project. In that case, the modal includes an additional option to select the resources to deploy. ## Run your project You can manually [run](/components/hub/workspace/modeler/run-or-publish-your-process.md#run-a-process) your project to test it after it has been deployed to a testing, staging, or production cluster. :::note Use Play to validate your project in a development cluster, and only use Run when interacting with other stages such as testing, staging, or production. ::: To run your project: 1. Open the [project homepage](create-a-project.md#project-homepage). 1. Select **Deploy & run** to open the **Deploy & run** modal. 1. Select the process for which you want to start a new instance in **Process to run**. 1. Select **Deploy & run** to start a new instance.Before the process instance starts, all resources are redeployed if required so the new instance uses their latest state.After the process instance starts, you will receive a notification with a link to the process instance view in [Operate](/components/operate/operate-introduction.md). Open this link to monitor the process instance. If the target cluster has [authorizations](/components/admin/authorization.md) enabled, make sure you have the following permissions to be able to view the process instance in Operate:`READ_PROCESS_DEFINITION` and `READ_PROCESS_INSTANCE` permissions on the `PROCESS_DEFINITION` resource type`operate` permission to the `COMPONENT` resource type You can also open the **Deploy & run** modal from the details page of any BPMN file in the project. In that case, the current process is run and the modal includes an additional option to select the resources to deploy. ## Deployment errors If the deployment of a project fails (for example, because one or more of the contained resources has invalid implementation properties), a modal is shown containing the error message thrown by the Zeebe engine. The message typically provides the name of the affected resource, the ID of the invalid diagram element, and the error details. ### Deployment of external resources You can link BPMN processes, DMN decisions, or forms that are not part of the project itself (external resources) from any process inside a project. When you deploy the project, linked resources located outside the project are _not_ deployed with the project, so you must deploy them separately. --- ## Git sync Organization owners and administrators can connect their Web Modeler projects to GitHub, GitLab, and Azure DevOps, allowing users to keep their Web Modeler and Desktop Modeler projects in sync with their version control repositories. Once the connection is configured by an organization owner or administrator, project administrators and editors can use the built-in button to pull changes from the remote repository, integrate contributions from Desktop Modeler users, and merge their own work. ## Connect to a remote repository Select your Git repository host: Create a new GitHub App Web Modeler requires a GitHub App to sync changes with your GitHub repository. Follow the [GitHub documentation](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app) to create a new GitHub App for your organization or account with the following configuration: - Under **Webhooks**, deselect **Active** - Under **Permissions > Repository permissions**, enable **Read and write** for the following options: - Commit statuses - Contents - Pull requests Click **Create GitHub App** to finish. Generate a private key 1. In your new application's setting page, navigate to **General > Private keys**. 2. Select **Generate a private key**. This key is automatically downloaded as a .pem file when created, and can be opened in a text editor to copy and paste the contents into Web Modeler. Install the GitHub App 1. In your application's setting page, navigate to **Install app**. 2. Click on the **Install** button for your organization or account. 3. Select **Only select repositories**, and choose the repository to sync with Web Modeler. 4. Once redirected to your application's installation page, copy the **Installation ID** located at the end of the page's URL: `https://github.com/settings/installations/{installation_id}`. Configure GitHub in Web Modeler 1. Within Web Modeler, navigate to the project you would like to connect to GitHub, and click **Connect repository**. 2. Select the **GitHub** tile (if not already selected), located at the top of the modal. 3. Provide the following information in the **Configure GitHub** modal: - **Client ID:** Found in your GitHub App's settings page. You can also use Application ID as an alternative. (If you are using GitHub Enterprise Server 3.13 or prior, Application ID is required.) - **Installation ID:** Found in the URL of your GitHub App's installation page. - **GitHub API Base URL:** The base URL of your [GitHub installation's REST API](https://docs.github.com/en/enterprise-server@3.15/rest/enterprise-admin?apiVersion=2022-11-28#endpoint-urls). This is optional and only required for GitHub Enterprise instances. If left empty, Web Modeler uses the default GitHub Cloud REST API URL (`https://api.github.com`). :::note If you're using a self-hosted GitHub instance, see [Self-Managed Git sync](/self-managed/components/hub/configuration/modeler-configuration.md#git-sync) for configuration details. Refer to [GitHub documentation](https://docs.github.com/en/enterprise-server@3.15/rest/enterprise-admin?apiVersion=2022-11-28#endpoint-urls) for more information. ::: - **Private Key:** The contents of the .pem file downloaded from your GitHub App's settings page. - **Repository URL:** The base URL of the repository you want to sync with, for example `https://github.com/camunda/example-repo`. The URL cannot contain the `.git` extension or a folder path. - **Branch name:** The branch name to use for merging and managing changes. - **Repository path:** (optional) The path to the folder containing your project files. If left empty, Web Modeler syncs with the root of the repository. This path is automatically created if it does not exist. 4. Click **Open repository** to test your configuration. The repository for the provided branch and optional path opens in a new tab. 5. Click **Save Configuration**. When successful, your project will display a new **Sync with GitHub** button. ![The Sync with GitHub within Web Modeler](./img/git-sync.png) Create a new access token Web Modeler requires an access token to sync changes with your GitLab repository. You can use one of the following options: - **Project access token** (recommended) - Group access token - Personal access token To generate a project access token, follow the [GitLab documentation](https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html#create-a-project-access-token) and use the following configuration: - Assign the token to a user with the `developer` or `maintainer` role. - Enable the following [**scopes**](https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html#scopes-for-a-project-access-token): - `api` - `read_api` - `read_repository` - `write_repository` Get the project ID 1. Navigate to your GitLab project. 2. Click the menu icon in the top right corner and select **Copy project ID**. Configure GitLab in Web Modeler 1. In Web Modeler, navigate to the project you want to connect to GitLab, and click **Connect repository**. 2. In the modal, select the **GitLab** tile at the top. 3. In the **Configure GitLab** modal, provide the following information: - **Access token:** The project, group, or personal access token you generated. - **Project ID:** The ID copied from your GitLab project settings. - **GitLab API base URL:** The base URL of your [GitLab installation's REST API](https://docs.gitlab.com/api/rest/#make-a-rest-api-request), for example, `https://gitlab.example.com/api/v4`. This is optional and only required for self-hosted GitLab instances. If left empty, Web Modeler uses the default GitLab Cloud REST API URL (`https://gitlab.com/api/v4`). :::note If you're using a self-hosted GitLab instance, see [Self-Managed Git sync](/self-managed/components/hub/configuration/modeler-configuration.md#git-sync) for configuration details. ::: - **Repository URL:** The base URL of the repository you want to sync with (e.g., `https://gitlab.com/camunda/example-repo`). The URL must not include the `.git` extension or a folder path. - **Branch:** The name of the branch to use for merging and managing changes. - **Repository path:** (optional) The folder path that contains your project files. If left empty, Web Modeler syncs with the root of the repository. The path is created automatically if it doesn't exist. 4. Click **Open repository** to test the configuration. The repository for the selected branch and optional path will open in a new browser tab. 5. Click **Save Configuration**. Once connected successfully, your project will display a **Sync with GitLab** button. Register an App in Microsoft Entra :::note Web Modeler SaaS supports authenticating against `Microsoft Entra ID (global service)`. Other [national clouds](https://learn.microsoft.com/en-us/entra/identity-platform/authentication-national-cloud#microsoft-entra-authentication-endpoints) can be used in Self-Managed by setting the environment variable `CAMUNDA_MODELER_GITSYNC_AZURE_AUTHORITY_BASE_PATH`. ::: Web Modeler requires an application to be registered with Microsoft Entra ID to sync changes with your Azure repository. 1. Follow the [Microsoft Entra ID](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app) documentation to register an application. Be sure to save your `Application (client) ID` and `Directory (tenant) ID`. 2. Configure your application to use [client-certificate credentials](https://learn.microsoft.com/en-us/entra/identity-platform/how-to-add-credentials?tabs=certificate). You need a PEM-encoded, [PKCS#8](https://en.wikipedia.org/wiki/PKCS_8) private key and a PEM-encoded certificate in `X509` format generated from that key. You will need both later when configuring the connection in Web Modeler. 3. Configure [scoped permissions](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-configure-app-access-web-apis) for your app so it can update the content of your Azure repositories. Ensure `Azure DevOps > vso.code_write` is configured, and `Admin consent required` is set to `No`. Grant access to the App in the desired Azure projects Follow the [documentation on how to add users](https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/add-organization-users?view=azure-devops&tabs=browser#add-users-to-your-organization) to add the created application to your Azure organization. Ensure the following: - _Access level_ is set to `Basic`. - Add this to all projects that will be using this integration. - _Azure DevOps Groups_ is set to `Project Contributors`. Configure Azure in Web Modeler :::note If you're using a self-hosted Azure DevOps Server instance, see [Self-Managed Git sync](/self-managed/components/hub/configuration/modeler-configuration.md#git-sync) for configuration details. ::: 1. Within Web Modeler, navigate to the project you would like to connect to Azure, and select **Connect repository**. 2. Select the **Azure** tile, located at the top of the modal. 3. Provide the following information in the **Configure Azure** modal: - **Application (client) ID:** Can be found on the applications registration page. - **Directory (tenant) ID:** Your Microsoft Entra tenant unique identifier. Can also be found on the applications registration page. - **Private Key:** The private key used to generate the certificate in PEM format. - **Certificate:** The certificate used to register the application in PEM format. - **Repository URL:** The base URL of the repository you want to sync with, for example `https://dev.azure.com/camunda/my-project/_git/example-repo`. The URL cannot contain the `.git` extension or a folder path. By default, the first repository you create will have the same name as the project and the URL won't explicitly have the project name in it, for example `https://dev.azure.com/camunda/_git/example-repo`. - **Branch:** The name of the branch to use for merging and managing changes. - **Repository path:** (optional) The path to the folder containing your project files. If left empty, Web Modeler syncs with the root of the repository. This path is automatically created if it does not exist. 4. Click **Open repository** to test your configuration. The repository for the provided branch and optional path opens in a new tab. 5. Click **Save Configuration**. When successful, your project will display a new **Sync with Azure** button. Generating a private key and certificate Follow these steps to generate a private key and self signed certificate that can be used to connect Web Modeler with your Azure repository: 1. Generate private key Generate a new RSA private key and save it to a file called `private_key.pem`. ```sh openssl genrsa -out private_key.pem 2048 ``` 2. Create a certificate signing request Generate a Certificate Signing Request (CSR) using the key created in step 1. ```sh openssl req -new -key private_key.pem -out cert.csr ``` 3. Create a self-signed certificate Using the private key and the certificate signing request, create a certificate. ```sh openssl x509 -req -days 365 -in cert.csr -signkey private_key.pem -out cert.crt ``` This generates a self-signed certificate named `cert.crt` valid for 365 days. You can now use it with the private key to register an app in Microsoft Entra, and configure your Azure git sync configuration. Create a new access token Web Modeler requires an access token to sync changes with your Bitbucket Cloud repository. You can use one of the following token types: - **Repository access token** (recommended) - Project access token - Workspace access token Follow the [Bitbucket documentation](https://support.atlassian.com/bitbucket-cloud/docs/create-a-repository-access-token/) to generate a new repository access token for your repository with the following configuration: - Enable the following [**scopes**](https://support.atlassian.com/bitbucket-cloud/docs/repository-access-token-permissions/) for your token: - `repositories:read` - `repositories:write` Configure Bitbucket Cloud in Web Modeler 1. In Web Modeler, navigate to the project you want to connect to Bitbucket Cloud, and click **Connect repository**. 2. Select the **Bitbucket** tile at the top of the modal. 3. Fill in the **Configure Bitbucket** modal with the following information: - **Access token:** The repository, project, or workspace access token you generated. - **Bitbucket API Base URL:** Leave empty. Only required for [Bitbucket Data Center](./git-sync.md?platform=bitbucket-data-center) instances. - **Repository URL:** The base URL of the repository you want to sync with, e.g., `https://bitbucket.org/camunda/example-repo`. The URL must not include the `.git` extension or any folder path. - **Branch name:** The branch to use for merging and managing changes. - **Repository path:** (optional) The folder path containing your project files. If left empty, Web Modeler syncs with the repository root. This path will be created automatically if it does not exist. 4. Click **Open repository** to test your configuration. The repository for the specified branch and optional path will open in a new tab. 5. Click **Save Configuration**. Once successful, your project will display a new **Sync with Bitbucket** button. :::info Atlassian Data Center End of Life Bitbucket Data Center reaches its end of life on March 28, 2029. See [Atlassian's announcement](https://www.atlassian.com/licensing/data-center-end-of-life). Consider migrating to Bitbucket Cloud or another supported Git provider for continued support and updates. ::: :::warning Limitations Due to [limitations in the Bitbucket Data Center API](https://jira.atlassian.com/browse/BSERV-14381), Web Modeler cannot push file deletions to Bitbucket Data Center repositories. If you delete, move, or rename files in Web Modeler, the original will remain in the remote repository after synchronization. ::: Create a new access token Web Modeler requires a **user** HTTP access token to sync changes with your Bitbucket Data Center repository. Repository or project access tokens are not supported. Follow the [Bitbucket documentation](https://confluence.atlassian.com/bitbucketserver/http-access-tokens-939515499.html#HTTPaccesstokens-CreateHTTPaccesstokens) to generate a new user access token for your repository with the **Repository write** permission. Configure Bitbucket Data Center in Web Modeler 1. In Web Modeler, navigate to the project you want to connect to Bitbucket Data Center, and click **Connect repository**. 2. Select the **Bitbucket** tile at the top of the modal. 3. Fill in the **Configure Bitbucket** modal with the following information: - **Access token:** The user access token you generated. - **Bitbucket API Base URL:** The base URL of your [Bitbucket installation's REST API](https://developer.atlassian.com/server/bitbucket/rest/v1000/intro/#structure-of-the-rest-uris), for example `https://bitbucket.example.com/rest/api/latest`. This is required for Bitbucket Data Center instances. If left empty, Web Modeler uses the default Bitbucket Cloud REST API URL (`https://api.bitbucket.org/2.0/repositories`). :::note If you're using a self-hosted Bitbucket Data Center instance, see [Self-Managed Git sync](/self-managed/components/hub/configuration/modeler-configuration.md#git-sync) for configuration details. Refer to [Bitbucket documentation](https://developer.atlassian.com/server/bitbucket/rest/v1000/intro/#structure-of-the-rest-uris) for more information. ::: - **Repository URL:** The base URL of the repository you want to sync with, e.g., `https://bitbucket.example.com/projects/camunda/repos/example-repo`. The URL must not include the `.git` extension or any folder path. For personal repositories, use `~{user}` as the project ID (for example, `projects/~alice/repos/example-repo`). - **Branch name:** The branch to use for merging and managing changes. - **Repository path:** (optional) The folder path containing your project files. If left empty, Web Modeler syncs with the repository root. This path will be created automatically if it does not exist. 4. Click **Open repository** to test your configuration. The repository for the specified branch and optional path will open in a new tab. 5. Click **Save Configuration**. Once successful, your project will display a new **Sync with Bitbucket** button. ## Sync with remote repository Organization owners/administrators, project administrators, and project editors can sync their version of Web Modeler with the connected repository at any time. 1. In your connected project, click **Sync with _GitProvider_** button. 2. Enter a [version number](manage-projects.md#project-versioning) to create a new version for your project. The new version will be created prior to pushing your changes to the central repository. 3. Click **Synchronize**. In the case of a merge conflict, select between your local Web Modeler changes and the changes in the remote repository to continue. Once the pull is complete and any merge conflicts are resolved, Web Modeler will push its changes. The newly created version is now accessible via the **View version** button in the success notification. ## Manage existing configurations Existing Git configurations can be edited from the gear icon beside the **Sync with _GitProvider_** button. Permission to update these settings are limited to **project administrators**. ## Change Git provider To switch between Git providers, update your configuration with the following steps: 1. Disconnect your current Git provider by clicking the gear icon beside the **Sync with _GitProvider_** button, and clicking the **Delete provider connection** button at the bottom of the modal. 2. After confirming the operation, open the **Connect repository** modal and provide the necessary information for the new Git provider, following the steps outlined for [GitHub](./git-sync.md?platform=github#connect-to-a-remote-repository), [GitLab](./git-sync.md?platform=gitlab#connect-to-a-remote-repository), [Azure](./git-sync.md?platform=azure#connect-to-a-remote-repository), or [Bitbucket Cloud](./git-sync.md?platform=bitbucket#connect-to-a-remote-repository). ## Advanced use cases Git sync supports a variety of development workflows, including the following advanced use cases. ### Monorepos A monorepo is a single repository containing multiple logical projects that each have disparate workflows and release cadences. To set up Git sync with a monorepo, you can specify the **path** to your project during the configuration. This allows you to keep multiple projects in one repository, each with its own sync configuration. :::note If you are using Git sync to work with monorepos, you should pull changes regularly, as the GitHub API is limited to a fixed amount of files and commits per synchronization action. See [troubleshooting](#troubleshooting) for more information. ::: ### Parallel feature development Git sync supports parallel feature development by allowing multiple projects to be connected to different feature branches. This allows teams to work on multiple features simultaneously without interfering with each other's work. To use Git sync for parallel feature development: 1. Create a new [project](create-a-project.md) in Modeler for each active feature branch you want to develop. 2. Configure Git sync for each instance by connecting it to the corresponding feature branch in your repository. 3. Work on your feature in Modeler, using **Sync with _GitProvider_** to pull and push changes as needed. 4. Once the feature is complete and merged into the main branch, you can delete the project associated with the feature branch. To perform hotfixes or patches of production or production-bound processes, sync a copy of the project to the `main` branch. :::caution Creating multiple copies of a project can complicate navigation and deployment if you have multiple files with the same ID in a project. To avoid this, you can create copies of the project in different projects. ::: ## Self-Managed environment variables Refer to [Configuration of the restapi component](/self-managed/components/hub/configuration/modeler-configuration.md#git-sync) for details on configuring environment variables. ## Troubleshooting ### File and folder names - Duplicate file names of the same file type are not allowed within the same folder. - Duplicate folder names are not allowed within the same parent folder. - Characters with special meaning in Git (for example, `/`), or characters disallowed by Git, are not allowed in either branch, file, or folder names. ### File extensions - `.json` files are parsed as either a Connector template or a test scenario file. The operation will fail if the file contents are not valid for either type. If the remote repository contains any `.json` files that are not valid Web Modeler files, place them in a subfolder so they are automatically ignored during synchronization. - Git Sync only supports `.md` files named exactly `README.md` (case-sensitive). Multiple `README.md` files are supported in a single repository, including in subfolders. ### Synchronization - Actions which alter the SHA of the commit to which Web Modeler is synced (for example, squash) may cause synchronization errors. - Timeouts may occur during a sync. In the event of a timeout, close the modal and retry the synchronization. - Using self-hosted instances of Git providers may require additional configuration. Refer to the Web Modeler configuration part for your [git host](#connect-to-a-remote-repository) and available [environment variables](#self-managed-environment-variables) for more details. - **(GitHub specific)** A single synchronization action is limited to incorporating a maximum of 250 commits or making changes to up to 300 files, regardless of whether these changes affect the Web Modeler files directly. Web Modeler does not provide a notification when these thresholds are exceeded. Should you encounter this limitation, it may be necessary to initiate a fresh synchronization. A fresh synchronization fetches all the files in the repository without relying on the incremental changes, thus bypassing the limitations. This can be achieved by either changing the branch or modifying the GitHub repository URL. --- ## Projects(Manage-projects) In Camunda Hub, a [project](/components/concepts/process-applications.md) is a type of folder that contains a set of related files you can work on and [deploy](./deploy-project.md) as a single bundle. For example, a project for a consumer loan application might consist of a BPMN diagram as an entry point and a number of additional supporting files, such as DMN diagrams and forms. ## Project development lifecycle In Camunda Hub, you can quickly develop project releases through the stages of a typical development lifecycle. - [Project development lifecycle](project-pipeline.md) ## Create a project Get started by creating a new project. - When you [create a project](./create-a-project.md), you must select a cluster to use for deployment during development. - You can [add files](./create-a-project.md#add-files-to-a-project) to the project as required. ## Validate and deploy your project Validate your project in development before deploying it to testing, staging, or production. - [Validate and deploy your project](deploy-project.md) ## Project versioning Use versioning to save a single snapshot of all the project files in one action. - [Project versioning](project-versioning.md) ## Known limitations You should be aware of the following limitations when working with projects. ### General limitations - Self-Managed does not support defining cluster stages, identifying clusters by tags, or cluster promotion. ### Deployment limitations - Projects can only be deployed to a Zeebe cluster in version 8.4.0 or higher. - The overall size of the deployment bundle is limited due to a maximum [record](/components/zeebe/technical-concepts/internal-processing.md) size of 4 MB in Zeebe. - The limit is effectively between 2 and 3 MB, as Zeebe writes more data to the log stream than just the raw deployment. - If you exceed the limit, you are shown an [error message](deploy-project.md#deployment-errors): `Command 'CREATE' rejected with code 'EXCEEDED_BATCH_RECORD_SIZE'`. --- ## Project development lifecycle You can use Web Modeler to quickly develop project releases through the stages of a typical development lifecycle. :::caution For business-critical and higher-risk processes that require strict governance and/or quality requirements, you can [integrate Web Modeler into your CI/CD pipelines](/components/hub/workspace/modeler/integrate-modeler-in-ci-cd.md). ::: ## Model During the modeling stage, you will typically: - [Create a project](create-a-project.md) and select a default development cluster to deploy to. - Invite other users to collaborate on the project. - Define and set up the clusters and deployment stages you will use in your development pipeline. - Model your diagrams and associated resources, and fix errors shown in the modeler. - Use [token simulation](/components/hub/workspace/modeler/validation/token-simulation.md) to correct and optimize your process flow. ### Deployment pipeline stages You can use the provided Web Modeler deployment pipeline to manage your application process deployment. The deployment pipeline has four stages named **Development**, **Testing**, **Staging**, and **Production**. | Stage | Description | | :---------- | :-------------------------------------------------------------------------------------------------------------- | | Development | Use to create and test new software features and changes. | | Testing | Use for quality checks, ensuring software meets defined standards before release. | | Staging | Use for controlled testing where changes are validated before deployment to production. | | Production | The live system with the latest software. Only administrators and organization owners can deploy to this stage. | To define your deployment pipeline stages: 1. Open the [project homepage](create-a-project.md#project-homepage). 1. Select **Configure** in the **Connected clusters** section to open the **Define stages** modal. 1. Select and assign a cluster to each deployment stage that you want to use in your deployment pipeline. 1. Select **Save** to save your changes and close the modal. :::note - An administrator must define the cluster to deploy to for each stage. - During deployment, the next stage is not automatically selected. You must select the stage you want to promote to. - You must select a cluster for at least one stage to be able to deploy. An **Undefined stages** warning is shown if no cluster is selected for at least one stage. ::: ## Validate When your project is ready for validation you can deploy it to your development cluster. - Use [Play mode](/components/hub/workspace/modeler/validation/play-your-process.md) to quickly validate the process behavior and play different scenarios. - Validate that all files and resources are correctly deployed. :::note Play is being rebuilt and progressively rolled out to more users. See [Play limitations and availability](/components/hub/workspace/modeler/validation/play-your-process.md#limitations-and-availability) for Play limitations and why you might not see the **Play** tab. ::: ## Review After validation is complete, you can [create a new version of your project](./project-versioning.md#version-creation) and request a review: 1. Use [project versioning](./project-versioning.md) to create a version for all files in the project. You can [compare versions](/components/hub/workspace/modeler/modeling/versions.md#compare-versions) to visually review changes between two versions of a BPMN file, or view code changes for other files. 2. Request a review for the newest version of the project from the version history page of the project. Collaborators with edit permission in your project will see a notification on the process diagram page once you have requested a review. 3. Reviewers can view the changes, comment, request changes, or approve the project version. 4. After a user has submitted their review, the project version is marked as reviewed and the review status is shown in the version history. 1. Any user with edit permissions can go back and edit the review at any point in time to update the assessment. 5. If the reviewer has marked the version with "changes requested", you can address the feedback by performing the requested changes, creating a new version, and requesting a review for the new version. This review capability is most useful for reviews on a business level. For technical reviews, you may instead use [Git Sync](git-sync.md) to put changes into a technical context with related code changes. :::info To ensure proper evaluation, users (except organization administrators) cannot review versions they create. ::: ## Promote After the review is complete, you can promote the versioned project to the next stage(s) of the deployment pipeline. For example, promote to your testing cluster/stage, then to staging, and finally to production. :::info If you want to use your own deployment pipeline after the review is complete, you can use [Git Sync](git-sync.md) at this point to deploy and promote the project through your own pipeline. ::: ### Deployment policy By default, only organization administrators can deploy projects to the `prod` tagged clusters. Organization administrators can change this policy in the [project deployment settings](/components/hub/workspace/modeler/modeler-settings.md#project-deployment). From these settings, you can enable non-admin users with deployment permissions to deploy projects to prod after a collaborator has approved the process app version using the [project version review](project-pipeline.md#review). ## Process governance The Web Modeler development lifecycle provides the following process governance: | Governance | Description | | :----------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Change control | Reviews cannot be performed by the user who created the project version unless the user is an organization administrator. Reviews can be edited. | | Deployment control | Deployments can only be made to the pre-defined set of approved clusters.Only users with correct privileges can deploy. Depending on the [deployment settings](/components/hub/workspace/modeler/modeler-settings.md#project-deployment), organization administrators can deploy to `prod` tagged clusters, or only approved versions can be deployed.Each deployment action is logged with information on the user and stage it was deployed to. | --- ## Project versioning Projects support versioning, allowing you to create distinct versions for the entire project. You can use versioning to save a single snapshot of all the project files in one action. This helps you track a project throughout its development lifecycle and ensures the correct version is referenced. In this context, a version is a Web Modeler project snapshot, not a deployed process definition version. See [version](/reference/glossary.md#version). ## Version creation To create a project version: 1. Open the [project homepage](create-a-project.md#project-homepage) and click **Create version** in the **Versions** section. 2. Enter a **Version tag** in the version creation modal. 3. Select the **Create** button. On the versions page, which you open with **See full list**, you can use the file navigation header buttons to switch between files and view their content. See also [compare versions](/components/hub/workspace/modeler/modeling/versions.md#compare-versions). ## Version actions You can perform the following actions on a project version: 1. View details, which opens the version details page so you can review the contents of all files in the version. 1. Restore as latest, which is useful for reverting changes, making further edits, [git syncing](git-sync.md), downloading, or using Play. 1. Edit 1. Deploy, especially after the version has been [reviewed](./project-pipeline.md#review). 1. Download 1. Delete 1. Copy, which creates a new project with the files from the version. --- ## Manage workspace settings Manage workspace members, clusters, and general information. ## About workspaces In Camunda Hub, a workspace is a collaboration environment within an organization, representing a team or business domain. A workspace is assigned members, roles, projects, and clusters, so all related work happens in one shared space. You can manage a workspace's members, clusters, and general information at the workspace level. :::info You can only manage a workspace's settings at the workspace level if you're a **Workspace admin**, **Organization admin**, or **Organization owner**. ::: --- ## Collaborate with modes Camunda 8 only Collaboration between business and IT professionals can be challenging, which is why we introduced three modes in BPMN diagrams that help users with different technical backgrounds to collaborate effectively: **design**, **implement**, and **play**. The **Design** mode view is tailored to business users, and the **Implement** and **Play** mode views are tailored to developers. Business users can now focus on modeling, sharing, and collaborating, while developers can work on implementation and debugging with ease. When accessing a BPMN diagram for the [first time](/components/hub/workspace/modeler/modeling/model-your-first-diagram.md), the **Design** mode is the first selected option. To switch between modes, you can select one of the tabs on the left side of the screen, above the diagram; any further selection is remembered and kept for the next sessions. ![modes tab navigation](../img/mode-tab-navigation.png) :::note When a process template is selected, the default mode is **Implement**. ::: ### Modes and roles - **Project Admin** and **Editors** can access all modes. - **Commenters** can access all modes, but with read-only permission. This role can be assigned to stakeholders who need to see the implementation properties without the ability to modify them. ![read only properties](../img/read-only-properties.png) - **Viewers** can access all modes with read-only permissions and cannot leave comments. This role is suitable for stakeholders who only need to view the process design and implementation without making any changes or comments. Read more about the [different roles and how to assign them](./collaboration.md#access-rights-and-permissions). --- ## Collaborate with your team Camunda 8 only ## Projects Files and folders are stored in projects. The user access on files and folders is defined at the project level. When you access Web Modeler via the Camunda 8 dashboard, you can note the **Home** page with all the projects you can access: ![home page](img/web-modeler-home.png) ### Access rights and permissions Users can have various levels of access to a project in Web Modeler, outlined in this section. After creating a project, you can invite members of your Camunda 8 organization to collaborate in Web Modeler. There are four roles with different levels of access rights that can be assigned to each user: - **Project Admin**: The user can edit the project itself, all folders, and diagrams within the project, and invite more users to collaborate. - **Editor**: The user can edit all folders and diagrams within the project. - **Commenter**: The user cannot edit folders or diagrams or invite users, but can view diagrams and properties and leave comments. - **Viewer**: The user cannot edit folders or diagrams nor leave comments, but can only view diagrams. Additionally, users with elevated access have special privileges to do administrative tasks in **super-user mode**. #### Super-user mode Super-user mode is only available to users with elevated access and can be enabled via the user menu in Web Modeler: The main purpose of this mode is to assign collaborators to orphaned projects (which have no collaborators). Ordinarily, these projects would not be accessible or visible to any users. When a user activates super-user mode, they are temporarily granted **Project Admin** access to all projects of the organization. This allows them to assign collaborators to orphaned projects and gives them full access when none of the ordinary collaborators are available. ##### Required roles/permissions for super-user mode access {#elevated-access} The user must be assigned the organization **Owner** or **Admin** role. The user must be assigned the **Web Modeler Admin** role. If the role is not pre-existing, it can be created with the following permissions: - Web Modeler Internal API - `write:*` - Web Modeler Internal API - `admin:*` - Camunda Identity Resource Server - `read:users` Refer to the documentation pages about [assigning roles](../../../../../self-managed/components/management-identity/application-user-group-role-management/manage-roles.md) and [adding permissions](/self-managed/components/management-identity/access-management/access-management-overview.md) for detailed instructions. ### Add users to projects :::note Users without email addresses will not receive any kind of notification about project invitations. ::: On the right side of a project, view a list of your collaborators and invite more by taking the steps below: 1. Click **Add user**. ![invite user](img/web-modeler-collaborator-invite-modal-opened.png) 2. Choose a role for your new collaborator. ![invite choose role](img/web-modeler-collaborator-invite-choose-role.png) 3. Begin typing the name or email of the individual and Web Modeler will suggest Camunda 8 organization members that you can invite to the project. ![invite suggestions](img/web-modeler-collaborator-invite-suggestions.png) 4. Write a message to your new collaborator about their invitation to the project. ![invite type message](img/web-modeler-collaborator-invite-type-message.png) 5. Click **Add collaborator**. Your new collaborator will be added to the project and notified via email. ![invite added](img/web-modeler-collaborator-invite-added.png) ![invite email](img/web-modeler-collaborator-invite-email.png) If the individual is not a member of your organization, they will first receive an organization invitation. After accepting the invitation and logging into Web Modeler, they will be added to the project. They will appear as "invited" in the collaborator list until they accept. ![invite sent](img/web-modeler-collaborator-invite-sent.png) #### Invite the entire organization You can invite all existing members of your Camunda 8 organization to the project at once by using the **All users in the organization** option. On the right side of a project, view a list of your collaborators and invite more by following the steps below: 1. Click **Add user**. ![invite user](img/web-modeler-collaborator-invite-modal-opened.png) 2. Choose a role for your new collaborator. ![invite choose role](img/web-modeler-collaborator-invite-choose-role.png) 3. Begin typing the individual's name or email. Web Modeler will suggest members who have already logged into Web Modeler at least once and whom you can invite to the project. ![invite suggestions](img/web-modeler-collaborator-invite-suggestions.png) 4. Write a message to your new collaborator about their invitation to the project. ![invite type message](img/web-modeler-collaborator-invite-type-message.png) 5. Click **Add collaborator**. Your new collaborator will be added to the project and notified via email. ![invite added](img/web-modeler-collaborator-invite-added.png) ![invite email](img/web-modeler-collaborator-invite-email.png) If the member has not logged into Web Modeler before, they will not appear in the suggestions, but you can still invite them by typing their full email address. They will appear as "invited" in the collaborator list until they log into Web Modeler for the first time. After logging in, they will be added to the project. ![invite sent](img/web-modeler-collaborator-invite-sent.png) #### Invite the entire organization You can invite all members who logged into Web Modeler at least once to the project at once by using the **All users in the organization** option. :::info Self-Managed license restrictions For Self-Managed non-production installations, the number of collaborators per project is limited to **five**, including the project administrator. For more information, refer to the [licensing documentation](/reference/licenses.md#web-modeler). ::: ### Folders You can create folders in a project to semantically group and organize your diagrams. The user access on a folder is inherited from the project. ## Sharing and embedding diagrams Diagrams can also be shared with others in read-only mode via a sharing link. This link can also be protected with an additional password. 1. Navigate to a diagram and click on the share icon button. ![share button](img/web-modeler-share-icon-button.png) 2. Click **Create link**. ![share create link](img/web-modeler-share-modal.png) 3. Click **Copy** to copy the link to your clipboard. ![share copy link](img/web-modeler-share-modal-create.png) 4. Click **Add** and type a new password to protect your link. ![share copy link](img/web-modeler-share-modal-password-protect.png) 5. Click **Email** to share the new link with multiple recipients. ![share copy link](img/web-modeler-share-modal-email.png) Similar to the sharing link, a diagram can be embedded into HTML pages via an iframe tag. The iframe tag can be copied from the sharing dialog via the **Embed** button. For wiki systems like [Confluence](https://www.atlassian.com/software/confluence), we recommend using the HTML macro and adding the iframe tag from the sharing dialog. This way, diagrams can be easily included in documentation pages. To adjust the dimensions of the diagram, the width and height values of the iframe tag can be modified. ## Comments When selecting an element of the BPMN diagram, a discussion can be attached to this element. If no element is selected, the discussion will be attached directly to the diagram. Switch between the **Properties Panel** and **Comments** using the two tabs present at the top of the right side panel. ![comment](img/web-modeler-comment-type-here.png) New comments can be added to the discussion by any collaborator with Admin, Editor, or Commenter access rights. Afterwards, the comment can be edited or deleted via the context menu icon. ![comment context menu](img/web-modeler-comment-with-context-menu.png) Elements with discussions attached will always have a visible blue overlay, so you can easily identify discussion points. ![comment context menu](img/web-modeler-comment-overlay-on-diagram.png) ### Mention others in comments By typing the **@** character, you are able to filter the collaborators on the project and select one of them. ![comment suggestion](img/web-modeler-comment-mention-suggestions.png) When submitting the comment, this user will receive an email as a notification about the new comment. :::note Users without email addresses will not receive any kind of notification about being mentioned in a comment. ::: ![comment suggestion email](img/web-modeler-comment-mention-email.png) ## Interact with your collaborators ### Model a diagram together When others are opening the same diagram as you, the updates on the diagram are sent in real time. You can also note who is in the diagram with you. ![real time collaboration](../img/real-time-collaboration.png) ### Canvas lock To prevent conflicts and broken sessions when multiple people open the same diagram, Web Modeler automatically locks the canvas. When a user with edit permissions starts editing a diagram, the canvas is automatically locked. While the lock is active, no other users can modify the diagram — this prevents conflicting edits. Other collaborators can still do the following: - Open and view the diagram in real time - Switch [modes](./collaborate-with-modes.md) - Navigate the canvas - Drill down into subprocesses - Inspect properties and linked assets - Add comments (if they have permission) #### Take over editing If another user with edit permissions needs to continue working, they can take control by clicking the **Take over** button in the canvas lock bar. This releases the current lock and immediately assigns edit control to the new user. This approach enables predictable handovers and prevents conflicting edits while keeping the diagram accessible to all viewers. ### Undo/redo management limitations When collaborating with others on a diagram, you can only undo or redo your own actions until another collaborator makes a change, as the undo/redo history is reset each time another collaborator makes a change. ### Draw other's attention Whether you are in a presentation or if others are in the same diagram as you are, use the attention grabber pointer to draw attention to a specific part of the diagram. To do this, take the following steps: 1. Switch on the attention grabber pointer from the canvas tools. ![attention grabber](../img/attention-grabber.png) 2. Drop the pointer by clicking anywhere on the canvas. ![attention grabber](../img/attention-grabber-pointer-pulse.png) The pointer will pulsate to draw attention and will match your avatar color. It can also be seen in real-time by others that are looking at the same diagram as you. --- ## Design mode for business users Camunda 8 only In the **Design** mode view, business users have access to a different workspace of Web Modeler with a reduced properties panel. Only the documentation property and comments are shown, which provides a decluttered user interface. All the technicalities, such as triggers to deploy the diagram or start the instance, are hidden. Linting is disabled, and problem annotations are discarded. The sidebar on the right-hand side of the screen is collapsed when switching to the design mode, and when expanded, the state is persisted even when switching to another diagram. As a business user, you can [**link decision models**](/components/hub/workspace/modeler/modeling/advanced-modeling/business-rule-task-linking.md) and [**process models**](/components/best-practices/modeling/creating-readable-process-models.md) via [call activities](/components/modeler/bpmn/call-activities/call-activities.md), and you can still be a [**project owner**](./collaboration.md#access-rights-and-permissions), even if you don't execute implementation. ![design mode](../img/design-mode.png) With the **Design** mode view, users can model a process without need for a complex development tool that does not speak their language. This provides a clear journey for the user, all while incorporating modeling, sharing, and collaborating in a user-friendly way. --- ## Implement mode for developers Camunda 8 only In the **Implement** mode view, developers have access to a full, implementation-focused workspace of Web Modeler. The view offers all possible implementation details, and the problems panel (accessed by clicking **Problems** in the bottom left corner) shows all implementation problems that need fixing before deployment. The properties panel automatically opens when switching to the **Implement** mode, and if collapsed, it stays collapsed as long as you navigate between diagrams. Developers can switch between the modes as they like, and when they open a process template, it opens in **Implement**. ![implement mode](../img/implement-mode.png) --- ## Use a workspace for organization-wide collaboration Camunda 8 only Organizations often manage process documentation across multiple layers, from high-level strategic models to detailed operational workflows. [Process landscape visualization](/components/hub/workspace/modeler/process-landscape-visualization.md) streamlines this complexity by supporting different levels of abstraction, enabling seamless collaboration across teams, and providing tailored insights for various stakeholders. ## About this guide This guide shows how you can consolidate these perspectives into a single workspace, ensuring clarity, reusability, and governance within your organization’s efforts. Teams can use workspaces to: - Prevent duplicate work - Maintain a single source of truth - Enable cross-project navigation and transparency - Govern access to organization resources effectively ## Create a workspace and invite collaborators If you have not yet already, [create a Camunda 8 account](/components/hub/organization/manage-organization-settings/manage-plan/create-account.md) To create a workspace as a space for your organization's resources: 1. Open [Web Modeler](/components/hub/workspace/modeler/launch-modeler.md) and select **Create new project** (if you do not have any existing workspaces) or **New project** (if you already have existing workspaces). 2. Name your workspace accordingly. ![web modeler empty home](../img/web-modeler-new-user-home.png) 3. On the right side of the page under **Collaborators**, select **Add user**. 4. To add collaborators to your organization and workspace, enter the email address of the collaborator you would like to invite and select `Enter` or `Tab` between each entry. You may enter up to 20 email addresses. To add **all** existing organization members to your workspace, use the **All users in the organization** option to send a bulk invitation to all colleagues. :::info Self-Managed restrictions For Self-Managed installations, the bulk invitation only works for users who have already logged into Web Modeler at least once. Users who have not yet logged in must be invited individually. ::: 5. (optional) Enter an invitation message in the text box below your invites. 6. Select **Send invite**. This will invite the users to both the organization and workspace. Once your colleague clicks **Accept invitation**, they can access the workspace with the assigned role and permissions. ## Assign workspace and organization roles to collaborators To understand what permissions each role has, review [access rights and permissions](/components/hub/workspace/modeler/collaboration/collaboration.md#access-rights-and-permissions). You can also [assign roles specific to the entire organization](/components/hub/organization/manage-members/manage-users.md). If you previously invited collaborators to your organization _and_ workspace in the section prior, select the three vertical dots to the right of the users' name and select **Edit user**. Check which roles the user should be assigned and select **Confirm**. ## Share resources within a workspace You can publish a specific [version](/components/hub/workspace/modeler/modeling/versions.md) of a file or project within the workspace. 1. From the BPMN diagram, select the **Versions** dropdown in the top right corner. 2. Select the three vertical dots on the right side of the version labeled **Browse version actions**. 3. Select **Copy to...**, choose the workspace you want to store the shared resource in, and select **Copy here** to make it available for all workspace members. ![share asset version](../img/share-asset-version.png) Users can also reuse published resources by copying them into their own workspaces: 1. Open the workspace and navigate to the file. 2. In the versions list, choose the version to reuse and select the three vertical dots to open the actions menu. 3. Select **Copy to...**. 4. Select the target workspace and click **Copy**. ## Browse the process landscape of shared organization resources The [process landscape view](/components/hub/workspace/modeler/process-landscape-visualization.md) offers a visual map of BPMN files and their interfile connections (such as call activities). You can open the process landscape view by clicking **View landscape** from any of the following views: - Workspace view - Folder view - [Project](/components/hub/workspace/manage-projects/manage-projects.md) view ### Landscape view interaction 1. **Select a BPMN file:** Click on any node to see the BPMN file’s information including the latest version of the process on the sidebar. :::note For projects, version tags represent a unified "versioned" snapshot of all project files rather than separate versions for each file as with simple BPMN files. ::: ![selected node information](../img/process-landscape-node-information.png) 2. **Search**: Press `Ctrl+F` or `⌘+F` to search. Enter the name or identifier of a BPMN file to quickly find, highlight, and jump to the corresponding node. ![landscape search](../img/process-landscape-search.png) 3. **Highlight paths:** Click on a node or connection to highlight the entire chain of related connections. ![landscape selected node connections](../img/process-landscape-connection.png) ### README documentation To access the associated README file for a process within the workspace: 1. Open the process landscape view. 2. Click on a node in the landscape. 3. The README file (if one is associated) is displayed in the sidebar, providing a high-level overview of the process. ## Next steps Organization owners and administrators can connect their Web Modeler projects to GitHub and GitLab, allowing users to keep their Web Modeler, Desktop Modeler, and official version control projects synced: ![process org landscape architecture](../img/process-org-landscape-architecture.png) To keep your process landscape up to date with all changes in production, take the following steps: 1. Sync the project to the feature branch of a subfolder in a repository. 2. Merge the feature branch to main. 3. Begin the [CI/CD pipeline](/components/hub/workspace/modeler/integrate-modeler-in-ci-cd.md). 4. Sync your main branch to the workspace as part of the CI/CD pipeline. Alternatively, you can trigger a quick automation like a GitHub Action to keep your process landscape updated. Once the basic integration is configured by an organization owner or organization administrator, workspace administrators and editors can use the built-in button to pull changes from GitHub, integrate contributions from Desktop Modeler users, and merge their own work. Learn more in Camunda's [Git sync documentation](/components/hub/workspace/manage-projects/git-sync.md). --- ## Best practices for custom-built element templates When creating custom-built element templates, consider the following best practices to ensure they are effective, user-friendly, and maintainable. :::note Connector templates are a specific type of [element template](/components/concepts/element-templates.md). ::: ## Naming, description, and icon ### Sentence case Use sentence case when naming element and connector templates. For example: - ❌ GitHub Webhook Intermediate Catch Event connector - ✅ GitHub webhook intermediate catch event connector ### Choosing a name Choose a clear, easily understandable name. Include the brand name if the template connects to a service or tool; otherwise, describe its main feature. The template's name and description appear in the Modeler element template list and properties panel. ### Description Keep descriptions brief and clear. Explain what the template does and why it’s helpful in a couple of lines. Avoid technical jargon or complex language. ## Icon - **Prefer SVG format:** Use SVG files for icons, as they are scalable without losing quality. If an SVG icon isn't available, PNG or JPEG format is acceptable, but may not display well. - **Size:** You can upload an image file with a maximum size of **8 KB**. If you need to use a PNG or JPEG icon, ensure it is **512x512** pixels. Icons appear as **18x18** pixels in the element on the modeling canvas, and as **32x32** pixels in the properties panel. - **Check for official logo:** If the template connects to a service or tool, look for an official logo on the service's or tool's website. - **License compliance:** Ensure icons have the appropriate license (Public Domain CC0 or Creative Commons CC BY) for commercial use. ## Properties panel UI The element template defines interaction methods, visible and hidden entries, and mandatory fields, which are reflected in the Modeler properties panel UI. Follow these guidelines for a consistent user experience. ### Property naming - Use readable property names instead of technical identifiers. - Use sentence case. - Align operation or property IDs with Java method naming conventions (IDs are referenced in the template code, not displayed). Example: - ID aligned with Java method: `addQueueItem` - Label: - ✅ Add queue item - ❌ addQueueItem ### Property description - Ensure accuracy in property descriptions. - Use tooltips for brief explanations. - Link to relevant documentation for detailed explanations. Example: - ❌ "Your application's Client ID from the OAuth client." - ✅ "UIPath OAuth Client ID: Retrieve from UIPath external apps configuration." ### Placeholders - Placeholders appear as lighter text inside input fields. Use them to indicate the expected format or example input. - Avoid placeholders that duplicate the label. - Ensure placeholders are understandable and helpful. ### Variable naming Variables are not displayed in the properties panel but are referenced inside the template code. **General rule:** Use lower camel case (start lowercase, capitalize subsequent words). Avoid starting variables with underscores ("\_"). Example: - ❌ \_MyTestVariable - ✅ myTestVariable **Exception for method-specific properties:** Underscores may separate the method indication from the property name. Example: - ✅ chatCompletion_apiVersion - ✅ completion_apiVersion ### Versioning If you plan to make changes to your template over time and want to support [template evolution](https://github.com/bpmn-io/element-templates/blob/main/docs/LIFE_CYCLE.md#overview), include a version number property in your template, starting from 1. Templates with the same ID and different version values offer an upgrade path. ```json { "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", "name": "My Connector", "id": "org.my.connector", "version": 1 } ``` ### JSON structure The JSON structure should mirror the visual presentation to maintain alignment with the UI order. This ensures groups and properties are organized accordingly, even if some elements are hidden. If you want to use a FEEL expression that references other variables, the properties defining those variables must be declared before the property where the FEEL expression is used. ### Secret handling - Follow the rules of the system you’re adding to. - Do not save secret information in the JSON. - Use the connectors' [secret handling](/components/connectors/use-connectors/index.md#using-secrets). ### Field types The following guidelines ensure templates are intuitive and designed to minimize errors. This helps to create a good user experience. #### Hidden vs. visible fields If certain values are static and do not require user input, prefill them. Hidden fields offer no additional benefit by being visible. **Hidden fields example:** - HTTP method - Static URL endpoint - Static header #### Required vs. optional - **Required:** Properties essential for the template to function. Without these, the template cannot operate. - **Optional:** Properties that are not mandatory but provide additional functionality. There are two mechanisms to define property behavior depending on whether a field is required or optional: - [**“Not empty” constraint**](/components/modeler/element-templates/template-properties.md#validating-user-input-constraints): Displays an error if the field is left empty. - [**Optional bindings**](/components/modeler/element-templates/template-properties.md#preventing-persisting-empty-values-optional): Does not persist empty properties in the BPMN XML. Use **optional bindings** when a property is not required and you want to avoid storing empty values in the BPMN XML. In most cases, required fields should use the "Not empty" constraint for validation. #### Mandatory FEEL vs. optional FEEL FEEL expressions should only be required when necessary. For straightforward inputs, expressions can be optional. More details: [FEEL editor support](/components/modeler/element-templates/template-properties.md#adding-feel-editor-support-feel). #### Free input vs. dropdown vs. constraints - Use a dropdown when selection options are predefined. - Use free input with constraints when there is a wide range of input possibilities. **Example:** ```json { "label": "Priority", "id": "priority", "group": "input", "description": "The priority to apply to the queue item.", "value": "Low", "type": "Dropdown", "choices": [ { "name": "Low", "value": "Low" }, { "name": "Normal", "value": "Normal" }, { "name": "High", "value": "High" } ], "binding": { "type": "zeebe:input", "name": "priority0" }, "condition": { "property": "operationType", "oneOf": ["addQueueItem"] } } ``` --- ## Generate an element template You can configure and automatically generate a custom element template in Web Modeler. ## Use this page Use this page for both BPMN element templates and connector templates: - [Generate a BPMN element template](#generate-a-bpmn-element-template). - [Generate a connector template](#generate-a-connector-template). - To customize a generated connector template after creation, see [connector templates](/components/connectors/custom-built-connectors/connector-templates.md). ## Best practices When creating custom-built element templates, consider the [recommended best practices](best-practices.md) to ensure they are effective, user-friendly, and maintainable. ## Generate a BPMN element template 1. Select the Web Modeler project where you want to create the template. Camunda recommends storing element templates in root project folders, not projects, to simplify template management and avoid unnecessary versioning overhead. 2. Click **Create new**, then select **Element template** to open the **Create new element template** screen. ![Create the new element template](./img/element-template-generator/configure-element-template-details.png) 3. Select the template starting point: - **Call activity**: Start from a call activity template. - **Event**: Start from an event template. You can choose from predefined event templates, such as message event, timer event, signal event. - **Task**: Start from a task template. You can choose from predefined task templates, such as user task, service task, script task, and more. - **Blank**: Start from a blank template. A blank template includes only the basic properties shared by all BPMN elements and allows you to customize the template from scratch. Continue with [**configure the template details**](#configure-the-template-details) below. ## Generate a connector template Use this section to create a connector template in Web Modeler. You can start from a blank template or import an existing API definition such as an [OpenAPI specification](https://swagger.io/resources/open-api/), [Swagger specification](https://swagger.io/resources/open-api/), or a [Postman collection](https://www.postman.com/collection/). For example, download a Postman collection as a YAML file, import it into the generator, and choose which methods to include in the generated template. To generate a connector template: 1. Select the Web Modeler project where you want to create the template. Camunda recommends storing element templates in root project folders, not projects, to simplify template management and avoid unnecessary versioning overhead. 2. Click **Create new**, select **Element template**, and then choose the **Connector** tab. ![Create the new element template](../../../../connectors/custom-built-connectors/img/configure-connector-template-details.png) 3. Select the template starting point: - **From API definition**: Import an existing API definition file as a starting point. The **Import data source** section will appear below the template details. - **From blank**: Start from a blank template. Continue with [**configure the template details**](#configure-the-template-details) below. ## Configure the template details In the **Configure template details** section, provide the following information: - **Name:** Enter a clear and descriptive name for the template. For example, include the brand name if the template connects to a service, or indicate its main feature. - **Description:** Describe the template’s main features and benefits. - **Icon:** Use a default BPMN symbol or upload a custom icon. Supported formats: SVG, PNG, JPEG. Maximum file size: 8 KB. Minimum dimensions: 512 × 512 pixels. To add an icon, enter the image URL and click **Import icon**, or drag and drop a file into the upload area. :::note If you do not configure template details at this stage, a default name and BPMN symbol are assigned. You can edit them later. ::: ## Only for connector templates: Import an API definition 1. If you selected **From API definition**, the **Import data source** section appears. 1. Select the format to import (OpenAPI or Postman) and click **Import file**. 2. Import the API definition: - **Import file from URL:** Enter the API definition URL and click **Import icon**. - **Upload file:** Drag and drop a file into the upload area, or click the link to select a file. 3. After importing, select which actions to include from the generated list of supported methods. ![List of imported methods](../../../../connectors/custom-built-connectors/img/Imported-methods.png) :::info For more information on working with and configuring connector templates, see [Connector templates](/components/connectors/custom-built-connectors/connector-templates.md). ::: 2. Click **Create template** to generate the new template and open it in the [element template editor](/components/connectors/manage-connector-templates.md). --- ## Manage element templates export const UploadIcon = () => ; Camunda 8 only You can create and manage [element templates](/components/concepts/element-templates.md) just as any other asset in a Web Modeler project. ## Create an element template To create a new element template, follow the steps described in [Generate an element template](./element-template-generator.md). You will be taken to the **Element template editor** screen. In this screen, you can define the element template by writing the template JSON. The template editor supports you in writing the template by providing autocompletion, error highlighting, and a live preview. ![Template editor](img/connector-templates/connector-template-editor.png) The components of the editor interface are as follows: - On the left, you find the **template JSON editor**. Here, you define the actual [template descriptor](/components/modeler/element-templates/defining-templates.md). The descriptor follows the [most recent element template schema](https://github.com/camunda/element-templates-json-schema). :::info Starting with 8.8, the following properties are not managed by Web Modeler anymore, and you can freely edit them: - `name`: Human-friendly name shown when selecting a template and in the properties panel after the template has been applied. The value can be different from the file name. - `id`: Identifier of the template. Changing this value creates a new template. We recommend setting a meaningful value (for example, "PaymentConnector", "CreateUserTemplate"). - `version`: Integer-based version number. Combined with the `id`, it defines a unique template version. When [publishing](#publish-a-connector-template) a new version, you need to update the version number manually. The value of the `$schema` property is still fixed; manual changes will not be saved. ::: - On the right, you observe the live **Visual Preview**. The live preview shows how the properties panel will look when you apply the template to an element. It automatically updates on every valid change, and reflects the latest valid state of the template. The preview allows you to interactively check your template before publishing it. You can also use the **Update JSON** button to update the template's JSON properties with the current input values from the visual preview. - In the upper right, you can **Add an icon** for your template. You can upload an image file with a maximum size of 8 KB. We recommend using squared SVG graphics. Icons appear as 18x18 pixels in the element on the modeling canvas, and as 32x32 pixels in the properties panel. On every valid change, the template is saved automatically. If there are errors in the JSON file, the template will not be saved. Ensure all [errors are resolved](#fixing-template-problems) for the template to save successfully. ## Publish an element template After finalizing your element template, click **Publish to project** to activate it within the project context. In the modal that opens: - Update the version number if necessary. You don't need to change it for the initial version or if you have updated it already in the template editor. The value entered here is saved to the `version` property in the JSON. - Assign a distinct version name for effective version management. - Add a description to explain what changed since the previous version. ![Publishing a template](img/connector-templates/publish-version-to-project.png) Web Modeler checks the template for conflicts with already-published template versions. You cannot publish a new version if: - The template's ID is already used in a published version of a different template file. - The version number is equal to or lower than the last published version of the same template file with the same template ID. Web Modeler also shows a warning if the template ID has changed since the last published version. You can still publish the new version in this case. As a [user with elevated access](/components/hub/workspace/modeler/collaboration/collaboration.md#elevated-access), you can publish an element template version within the organization context, enabling all organization members to use it in their diagrams. To do so, click **Publish > Publish to organization** on the editor screen or promote a template version via the [versions list](#versioning-connector-templates). ### Manage published element templates After publishing, an element template version can be applied across all models within the same project or organization, depending on its publication status. You can review the publication status of template versions in the [versions list](#versioning-connector-templates). On the Web Modeler home page, you can find an overview of all shared resources within your organization. [Users with elevated access](/components/hub/workspace/modeler/collaboration/collaboration.md#elevated-access) can: - View additional details about the published version. - Open the resource's versions list (if they are in [super-user mode](/components/hub/workspace/modeler/collaboration/collaboration.md#super-user-mode) or are a [project admin or editor](/components/hub/workspace/modeler/collaboration/collaboration.md#access-rights-and-permissions) of the resource's project). - Unpublish an element template directly from this view. ![Manage published templates - elevated access](img/connector-templates/manage-connector-templates-org-privileges.png) Organization members without special organization permissions can: - View all the resources published within the organization. - Open the resource's versions list (if they are a [project admin or editor](/components/hub/workspace/modeler/collaboration/collaboration.md#access-rights-and-permissions) of the resource's project). ![Manage published templates - no special organization permissions](img/connector-templates/manage-connector-templates-no-org-privileges.png) ### Versioning element templates You can version your element templates [similar to diagrams](/components/hub/workspace/modeler/modeling/versions.md). If you publish a new version of an element template and an older version is already being used in diagrams, the user can either: - [Update the diagram elements](/components/modeler/desktop-modeler/element-templates/using-templates.md#updating-templates) to use the most recent version of the element template. You cannot undo this action. - Continue using the older version of the element template in their diagrams. ## JSON editor features The JSON editor is based on the [Monaco Editor](https://microsoft.github.io/monaco-editor/). The Monaco Editor is the editor that powers VS Code. As a result, the template editor supports many familiar features, such as auto-formatting, indentation support, code completion, and error highlighting. With code completion, you can add a complete property object when you press `Ctrl+Space` at a location for a new property. When you press `Ctrl+Space` to create a new attribute, you get proposals for all available attributes. When the domain for values is defined, you can select one by pressing `Ctrl+Space` in a value. Read the [Visual Studio Code editor docs](https://code.visualstudio.com/docs/editor/editingevolved) for a full overview of features. ## Fixing problems in your templates {#fixing-template-problems} While working on a template, the template will be in invalid intermediate states. For instance, when you add a new property, it must contain various mandatory attributes. Unless all mandatory attributes are defined, the template will not be saved, and the preview is not updated. This ensures that you can never publish an invalid or broken template. The editor toolbar indicates if the template is currently in a valid state or not. The JSON editor provides you with error highlighting, allowing you to add mandatory values and resolve problems without missing anything. ![Indicating problems in element templates](img/connector-templates/fix-connector-template-problems-1.png) If there are problems at the root level of the JSON (such as a missing or misspelled mandatory attribute), the error is highlighted in the first line of the editor. Click the error marker at the curly bracket to expand the error message. ![Some element template problems highlighted in the first line](img/connector-templates/fix-connector-template-problems-2.png) ## Importing an existing element template If you have created templates for Desktop Modeler and want to reuse them in Web Modeler, you need to make some adjustments to the template files: 1. **Split the files**. Web Modeler maintains a 1:1 relation between element templates and files. Since Desktop Modeler allows you to keep multiple template definitions in a single file, you must split the file in advance to one file per template before uploading. 2. **Remove the brackets**. Remove the list brackets from the element template file before uploading. Even if a template file for Desktop Modeler contains only a single template, it is always wrapped in a list. Once your file follows the requirements, you can upload it. There are two ways to do so: 1. Upload it as a new element template via the **Upload files** action in the project view. 2. Update an existing template via the **Replace via upload** action in the breadcrumbs of the editor view. :::info Desktop Modeler support The element template editor is currently only available in Web Modeler. Refer to the [Desktop Modeler documentation](/components/modeler/desktop-modeler/element-templates/configuring-templates.md) for instructions on configuring element templates in Desktop Modeler. ::: --- ## Save activity and event properties as reusable element templates Element templates allow you to create reusable, configurable building blocks that can be shared across your organization. With the **Save as template** feature, you can convert any supported element into an element template. ## Supported element types The **Save as template** feature is available for the following BPMN element types: **Activities** - Service task - User task - Send task - Receive task - Business rule task - Script task - Manual task - Call activity You cannot create a template for the [undefined task type](../../../../modeler/bpmn/undefined-tasks/undefined-tasks.mdx). **Events** - Message events - Signal events - Timer events - Conditional events ## Create an element template from an element You can save any supported element as an element template directly from the properties panel in Web Modeler. Configure your element with the desired properties, save it as a template, and optionally customize it further in the template editor. ### Prerequisites Before saving an element as a template: - Ensure the element is properly configured with the desired properties. - Resolve any validation errors on the element. The **Save as template** button is disabled if errors are present. ### Step 1: Configure your element First, configure your element with all the necessary properties you want to include in your template. The configuration depends on your element type and use case. In this example, we'll configure a business rule task for fraud detection: 1. Select an element in your BPMN diagram. 2. Configure the element with the properties you need. For example, set up a business rule task by defining: - **Implementation**: Choose the implementation type (for example, DMN decision). - **Called decision**: Reference the decision to be invoked. - **Binding type**: Select the [resource binding type](/components/best-practices/modeling/choosing-the-resource-binding-type.md). We recommend using `versionTag` to ensure that the template always references a compatible resource version. - **Result variable**: Define where to store the decision result. - Add any required input/output mappings for your business logic. ### Step 2: Save the element's properties as a template 1. With your configured element selected, look for the **Save as** button (template icon) in the top-right corner of the properties panel. 2. Click the **Save as** button to open the template configuration dialog. 3. In the **Save element properties as a new template** dialog, provide: - **Name**: Enter a descriptive name (for example, "Determine fraud rating confidence template"). - **Description**: Provide a detailed explanation of what the template does (for example, "Checks if the transaction is a refund and if there is a high fraud rating"). 4. Click **Save** to create your template. 5. You'll see an "Element template saved" notification with an option to **Edit template**. ### Step 3: Customize your template If you want to further customize or publish your template: 1. Click **Edit template** from the notification to open the template editor. 2. The template editor allows you to: - Modify template properties and [bindings](/components/modeler/element-templates/defining-templates.md#bindings). - Set up validation and [constraints](/components/modeler/element-templates/defining-templates.md#constraints) for user input. - Configure template groups and categories. By default, all properties are set to `"type": "String"`, making them visible and editable in the properties panel. To make your template easier to use, hide unnecessary properties with `"type": "Hidden"`, or use a dropdown (`"type": "Dropdown"`) with limited options. ### Step 4: Publish your template 1. When you're ready to make the template available to your team, click **Publish** to add it to your project or organization's shared resources. 2. If your template references dependencies (for example, another process or a DMN decision), a warning will remind you to ensure these dependencies are deployed in the runtime environment. Dependencies are not included in the template and must be managed separately. ### Step 5: Use your new template Once you've created and published a template, you can use it in a diagram: 1. Add a new element to your diagram or select an existing one. Make sure it matches the `appliesTo` type defined in your template. 2. Apply the template by: - Clicking **Change element** on the element, or - Clicking **Select** in the Template section of the properties panel. 3. In the template selection dialog, find your template (for example, "Determine fraud rating confidence template") under the assigned category. By default, templates are listed under "Templates". 4. Select the template to apply it to your element. The element will automatically be configured with all template settings, including decision references, bindings, and variable mappings. ## Understand template property bindings When you save an element as a template, Web Modeler automatically converts the element's properties into template bindings: - Input/output mappings - Task headers - Zeebe properties - Element-specific properties (for example, `calledDecision`, `calledElement`) - Message references (for message-related elements) Only properties supported by element templates are included. Unsupported properties remain visible in the properties panel after you apply the template. For a list of supported properties, see the [element templates reference](/components/modeler/element-templates/defining-templates.md#defining-template-properties). ## When template creation is unavailable The **Save as template** button is disabled in the following scenarios: - You are not in Web Modeler's implementation mode. - The element has validation issues. - The element type is not supported (blank tasks, error events, subprocesses, etc.). - You don't have permissions to create templates. Fixing validation issues will enable the button if the element type is supported. ## Best practices When creating templates from elements: - **Create focused templates**: Each template should serve a clear purpose. - **Hide details**: Expose only the necessary properties. - **Validate input**: Use [constraints](/components/modeler/element-templates/defining-templates.md#constraints) to enforce valid input and provide meaningful errors. - **Manage dependencies**: Ensure referenced decisions or variables exist in the runtime environment. Use `versionTag` bindings for dependencies to avoid version conflicts. - **Use meaningful parameter names**: Give configurable fields descriptive names. - **Test your templates**: Apply them to an element to confirm they work as expected. ## Related topics - [Element templates reference](/components/modeler/element-templates/about-templates.md) - [Custom connectors](/components/connectors/custom-built-connectors/create-connector-from-rest.md) - [Task types](/components/modeler/bpmn/tasks.md) --- ## Using templates ## Applying templates If an [element template](/components/modeler/element-templates/about-templates.md) matches a selected diagram element, the blue template catalog button, **Select**, is shown in the properties panel on the right side of the screen. ![Template Chooser](./img/chooser.png) Clicking **Select** opens a popup menu, allowing you to browse and search available templates for the selected element. ![Popup Menu](./img/modal.png) You can also click the **blue shop icon** in the upper right of the popup to open the [Camunda Marketplace](/components/hub/workspace/modeler/modeling/camunda-marketplace.md). ![Marketplace Menu](./img/marketplace.png) Applying a template stores it via the `modelerTemplate` property and the optional `modelerTemplateVersion` property on the selected element: ```xml ``` It also sets up custom fields on the diagram element and makes these available for inspection and editing. Properties which were not configured in the element template using custom fields will not be available for editing. ## Removing templates To remove an applied template from an element, either the _Unlink_ or _Remove_ function can be used: - **Unlink**: Remove the element template from the `modelerTemplate` property but keep the properties which were set. - **Remove**: Remove the element template from the `modelerTemplate` property and reset all properties of the respective element. ![Unlink or Remove](./img/unlink-remove.png) ## Updating templates If a template is applied and a new version of the template is found you can _update_ the template. ![Update Template](./img/update-template.png) Templates are updated according to the following rules: - If the property is set in the new template, it will override the existing value — unless the value was originally set by the old template and has been manually changed since. - If the property is not defined in the new template, it will unset. - Sub-properties of complex properties (for example, `zeebe:input`, `zeebe:output`) are handled according to these rules if they can be identified. ### Replacing templates If a template is deprecated with a new element template and you want to keep the same input values as in the deprecated template, you can: 1. **Unlink**: Remove the current template that is deprecated from the `modelerTemplate` property, but keep the properties which were set. 2. **Select** and apply the new element template. ## Missing templates If a template was applied to an element but cannot be found, editing of the element is disabled. To re-enable editing, either _unlink_ or _remove_ the template, or make it available by publishing a template with the same ID or creating a new one. ![Template not Found](./img/template-not-found.png) ## Creating templates from existing elements If a diagram element supports being used as a template, a _Save as_ link will be displayed next to the header. Once clicked, you will be able to select a name and description for the template. This allows you to easily reuse pre-configured elements. ![Template Save as](./img/save-as.png) The template can be further customized by [editing it](/components/connectors/manage-connector-templates.md). To use the template, you first need to [publish it to the project or organization](/components/hub/workspace/modeler/element-templates/manage-element-templates.md#publish-an-element-template). Learn more about [saving elements as templates](./save-as-element-templates.md). --- ## IDP applications Create and manage your IDP document extraction and classification templates in an **IDP application**. ## Create an IDP application To create an IDP application: 1. In a Web Modeler project, select **Create new** > **IDP application** to open the **Create an IDP application** modal. - **Name**: Enter a name for the IDP application. - **Select a cluster**: Select the cluster you want to use for modeling and testing your document extraction and classification. 1. Click **Create** to create the IDP application. 1. You can now create templates inside your IDP application folder: - [Document extraction](idp-document-extraction.md) templates to extract data from structured or unstructured documents. - [Document classification](idp-document-classification.md) templates to automatically classify documents by type. :::note Document classification templates require cluster version 8.9-alpha5 or later. If your cluster does not meet this requirement, only extraction templates are available. ::: :::note - Camunda recommends using a development (dev) cluster for your IDP applications. - Ensure that the selected cluster meets the [IDP application cluster requirements](idp-configuration.md#cluster-requirements). ::: ## Change IDP application cluster Open the **IDP application setting** modal to change the cluster being used by an IDP application. 1. Click **Settings** to open the IDP application settings. 1. Change the cluster you want to use for the IDP application. 1. Click **Save** to save and apply your changes. --- ## Configure IDP Configure IDP for your Camunda 8 setup and make sure IDP can access the required components and credentials. ## Prerequisites The following prerequisites are required for IDP: ### Amazon Web Services (AWS) When using AWS as your cloud provider, the following AWS-specific prerequisites are required. IDP supports both structured and unstructured document extraction with AWS services: | Prerequisite | Description | | :------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [Amazon Web Services (AWS) IAM user and permissions](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users.html) | A valid AWS Identity and Access Management (IAM) user with permissions configured to allow access to Amazon Bedrock, Amazon S3, and Amazon Textract, such as:`AmazonBedrockFullAccess``AmazonTextractFullAccess`Access to the IDP Amazon Bedrock foundation models:For a list of models suported by IDP, see [extraction models](idp-reference.md#extraction-models).To learn more about configuring access to foundation models, refer to [add or remove access to Amazon Bedrock foundation models](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access-modify.html).The [access key pair](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html) (_access key_ and _secret access key_) for this IAM user. This is required during IDP configuration. | | [Amazon S3 bucket](https://aws.amazon.com/s3/) | An Amazon S3 bucket that can be used by IDP for document storage during document analysis and extraction.The bucket name must be unique across all your AWS accounts. | ### Google Cloud Platform (GCP) When using GCP as your cloud provider, the following GCP-specific prerequisites are required. IDP supports both structured and unstructured document extraction with GCP services: | Prerequisite | Description | | :---------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [Google Cloud Vertex AI](https://cloud.google.com/vertex-ai/docs) (required for unstructured extraction) | Access to Vertex AI is required for performing unstructured document extractions using Google's generative AI models.Vertex AI provides access to foundation models and machine learning capabilities that power IDP's intelligent document processing features.To learn more about Vertex AI, refer to [Vertex AI documentation](https://cloud.google.com/vertex-ai/docs/start/introduction-unified-platform). | | [Google Cloud Storage (GCS) bucket](https://cloud.google.com/storage/docs) (required for unstructured extraction) | A Google Cloud Storage bucket that can be used by IDP for temporary document storage during Vertex AI analysis and unstructured extraction.The bucket must be accessible by the service account and located in the same region as your Vertex AI resources for optimal performance.To learn more about creating and managing Cloud Storage buckets, refer to [Cloud Storage documentation](https://cloud.google.com/storage/docs/creating-buckets). | | [Google Cloud Document AI](https://cloud.google.com/document-ai/docs) (required for structured extraction) | Access to Document AI is required for performing structured document extractions from forms, invoices, and other templated documents.Document AI provides specialized processors for extracting structured data from various document types with high accuracy.To learn more about Document AI capabilities, refer to [Document AI overview](https://cloud.google.com/document-ai/docs/overview). | | [Google Cloud Service Account](https://cloud.google.com/iam/docs/service-account-overview) | A valid GCP service account with appropriate permissions configured to allow access to Vertex AI, Document AI, and Cloud Storage services.The service account must have IAM roles that allow use of the services above based on your extraction needs (unstructured and/or structured).The [service account JSON key file](https://cloud.google.com/iam/docs/service-account-creds#user-managed-keys) is required during IDP configuration for authentication. | ### Microsoft Azure When using Azure as your cloud provider, the following Azure-specific prerequisites are required. IDP currently supports unstructured document extraction with Azure services: | Prerequisite | Description | | :----------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [Azure AI Document Intelligence](https://learn.microsoft.com/en-us/azure/ai-services/document-intelligence/) | Access to Azure AI Document Intelligence is required for extracting text and structural information from uploaded documents during unstructured extraction.Document Intelligence uses machine learning models to automate data processing and provides essential capabilities for document analysis.Authentication is handled using the service endpoint and access keys provided by the Azure AI Document Intelligence resource.To learn more about Document Intelligence, refer to [Document Intelligence overview](https://learn.microsoft.com/en-us/azure/ai-services/document-intelligence/overview). | | [Azure AI Foundry](https://learn.microsoft.com/en-us/azure/ai-foundry/) | Access to Azure AI Foundry is required for using different large language models (LLMs) during unstructured document extraction.Azure AI Foundry provides access to various foundation models and AI capabilities that power IDP's intelligent document processing features.Authentication is handled using the service endpoint and access keys provided by the Azure AI Foundry resource.To learn more about Azure AI Foundry, refer to [Azure AI Foundry documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/what-is-azure-ai-foundry). | ### OpenAI Compatible Provider When using an OpenAI Compatible provider, you can bring your own model (BYOM) for unstructured document extraction. Any provider that implements the `/chat/completions` API endpoint is supported: | Prerequisite | Description | | :----------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | OpenAI Compatible API endpoint | Access to any provider that implements the OpenAI `/chat/completions` API for performing unstructured document extractions using your own model.This enables a bring your own model (BYOM) approach, allowing you to use custom or third-party models that are compatible with the OpenAI API specification.Authentication is handled through custom headers that you configure based on your provider's requirements.Examples of compatible providers include [Ollama](https://ollama.com/), self-hosted models, third-party API services, or custom inference endpoints that implement the OpenAI chat completions interface.To learn more about the API specification, refer to the [OpenAI API documentation](https://platform.openai.com/docs/api-reference/chat/create). | ### ABBYY Vantage [ABBYY Vantage](https://www.abbyy.com/vantage/) can be used as a third-party [text extraction engine](idp-reference.md#extraction-engines) for unstructured extraction and document classification. ABBYY runs independently of your cloud provider, so it can be combined with any provider's LLM. | Prerequisite | Description | | :------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ABBYY Vantage tenant | An ABBYY Vantage tenant in one of the supported regions:North America: `https://vantage-us.abbyy.com`Western Europe: `https://vantage-eu.abbyy.com`Australia: `https://vantage-au.abbyy.com`The region is set when the tenant is provisioned. Contact your ABBYY administrator if you do not know which region your tenant uses. | | ABBYY Vantage Public API client | An ABBYY Vantage Public API client used to authenticate via the OAuth2 client credentials flow.Created in the ABBYY Vantage web console under **Settings > Public API Clients**. Requires administrator permissions in your ABBYY Vantage tenant.Both the **Client ID** and **Client Secret** are required during IDP configuration. The Client Secret is only displayed at creation time. | | ABBYY OCR skill | An OCR skill in your ABBYY Vantage tenant that is configured to **output Text format**.The default ABBYY OCR skill output format cannot be edited. Copy the default OCR skill in the Skill Designer and configure the copy to output Text format.The **Skill ID** is the GUID shown in the URL when the skill is opened, and is required during IDP configuration. | ### General requirements The following prerequisites apply regardless of your cloud provider: | Prerequisite | Description | | :----------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [Web Modeler](/components/hub/workspace/modeler/launch-modeler.md) | Web Modeler is required to create, manage, publish, and integrate [IDP applications](idp-applications.md), [document extraction](idp-document-extraction.md) templates, and [document classification](idp-document-classification.md) templates.IDP does not support Desktop Modeler. | ## Cluster requirements {#cluster-requirements} The following requirements apply for IDP application clusters: | Requirement | Description | | :-------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [Connector secrets](#aws-secrets) | You must configure the required IDP AWS connector secrets on any cluster used with IDP. | | [Document handling](/components/document-handling/getting-started.md) | IDP requires a cluster that supports document handling. For example, a version 8.7 or higher cluster. | | Cluster health | IDP applications and projects are only fully operational when linked to a healthy, active cluster. If needed, you can select an unstable or unhealthy cluster when first creating an IDP application, and change to a stable cluster when one is available. | :::info To learn more about storing, tracking, and managing documents in Camunda 8, see [document handling](/components/document-handling/getting-started.md). ::: ### Identity {#identity} If you are using an identity-enabled cluster, the following authorizations are required for IDP operations: | Resource type | Permission | Owner type | Owner | Description | | :----------------- | :---------------------- | :--------- | :------------- | :------------------------------------------------------------------- | | DOCUMENT | READ | Role | Connectors | Required for the idp connector to read the document from the cluster | | DOCUMENT | CREATE | User | `user's email` | Required to upload documents to the cluster during IDP extraction | | RESOURCE | CREATE | User | `user's email` | Required to deploy process instances | | PROCESS_DEFINITION | CREATE_PROCESS_INSTANCE | User | `user's email` | Required to start process instances | ## Configure IDP Once you have completed all the required prerequisites, configure IDP in a suitable `dev` cluster as follows. You only need to add the connector secrets for the cloud provider you plan to use. ### Add AWS connector secrets to cluster {#aws-secrets} If you are using AWS as your cloud provider, add the following AWS connector secrets required for IDP. - **SaaS:** Create and configure as [connector secrets](/components/hub/organization/manage-clusters/manage-secrets.md). - **Self-Managed:** Connector secrets are generally provided as environment variables, set via `values.yaml` or the command line. Add these connector secrets as environment variables for the Tasklist and Zeebe components. To learn more about using connector secrets in Self-Managed, see [managing secrets in Helm charts](/self-managed/deployment/helm/configure/secret-management.md) and [secrets in manual installations](/self-managed/components/connectors/connectors-configuration.md#secrets). | Connector secret Key | Required | Description | | :-------------------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `IDP_AWS_ACCESSKEY` | Yes | The AWS access key ID used to interact with the Amazon S3 bucket. | | `IDP_AWS_SECRETKEY` | Yes | The AWS secret access key associated with the `IDP_AWS_ACCESSKEY`. | | `IDP_AWS_REGION` | Yes | The AWS region where documents can be temporarily stored during Amazon Textract analysis. This should match the region where the Amazon S3 bucket is located.Example: `us-east-1` (default) | | `IDP_AWS_BUCKET_NAME` | Yes | The name of the Amazon S3 bucket you want to use for document storage during extraction.Example: `idp-extraction-connector` | ### Add GCP connector secrets to cluster {#gcp-secrets} If you are using GCP as your cloud provider, add the following GCP connector secrets required for IDP. The secrets you need depend on which type of extraction you plan to use. | Connector secret Key | Required for Unstructured Extraction | Required for Structured Extraction | Description | | :--------------------------------- | :----------------------------------: | :--------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------- | | `IDP_GCP_SERVICE_ACCOUNT` | Yes | Yes | The GCP service account JSON key file content for authentication with GCP services. | | `IDP_GCP_VERTEX_REGION` | Yes | No | The GCP region where Vertex AI resources are located.Example: `us-central1` | | `IDP_GCP_VERTEX_PROJECT_ID` | Yes | No | The Vertex project ID where Vertex AI resources are configured.Example: `my-gcp-project-id` | | `IDP_GCP_VERTEX_BUCKET_NAME` | Yes | No | The name of the Google Cloud Storage bucket for temporary document storage during Vertex AI analysis.Example: `idp-vertex-extraction-bucket` | | `IDP_GCP_DOCUMENT_AI_REGION` | No | Yes | The GCP region where Document AI resources are located. Must be either `eu` or `us`.Example: `us` | | `IDP_GCP_DOCUMENT_AI_PROJECT_ID` | No | Yes | The DocumentAI project ID where Document AI resources are configured.Example: `my-gcp-project-id` | | `IDP_GCP_DOCUMENT_AI_PROCESSOR_ID` | No | Yes | The Document AI processor ID for the specific processor you want to use for structured extraction.Example: `1234567890abcdef` | ### Add Azure connector secrets to cluster {#azure-secrets} If you are using Azure as your cloud provider, add the following Azure connector secrets required for IDP. | Connector secret Key | Required | Description | | :----------------------------------------- | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `IDP_AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT` | Yes | The endpoint URL for your Azure AI Document Intelligence resource. | | `IDP_AZURE_DOCUMENT_INTELLIGENCE_KEY` | Yes | The access key for your Azure AI Document Intelligence resource. | | `IDP_AZURE_AI_FOUNDRY_ENDPOINT` | Yes | The endpoint URL for your Azure AI Foundry resource. Construct this URL using the pattern: `https://.services.ai.azure.com/models`. | | `IDP_AZURE_AI_FOUNDRY_KEY` | Yes | The access key for your Azure AI Foundry resource. You can find this key in the details page of deployed base models or on the Azure AI Foundry "Overview" page. | | `IDP_AZURE_OPEN_AI_ENDPOINT` | Optional | The endpoint URL for your Azure OpenAI resource. Required only if you want to use OpenAI models. You can find this endpoint in the "Models + endpoints" page in the Azure AI Foundry dashboard. | | `IDP_AZURE_OPEN_AI_KEY` | Optional | The access key for your Azure OpenAI resource. Required only if you want to use OpenAI models. You can find this key in the "Models + endpoints" page in the Azure AI Foundry dashboard. | ### Add OpenAI Compatible provider secrets to cluster {#openai-compatible-secrets} If you are using an OpenAI Compatible provider, add the following connector secrets required for IDP. | Connector secret Key | Required | Description | | :------------------------------- | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `IDP_OPENAI_COMPATIBLE_ENDPOINT` | Yes | The base URL where IDP can call the chat completions request. This should be the endpoint for your OpenAI Compatible provider's API. | | `IDP_OPENAI_COMPATIBLE_HEADERS` | Yes | Authentication headers for your OpenAI Compatible provider. If no authentication is required, use `{}`. For providers requiring authentication, include the necessary headers in JSON format (e.g., `{"Authorization": "Bearer your-api-key"}`). | ### Add ABBYY Vantage connector secrets to cluster {#abbyy-secrets} If you want to use [ABBYY Vantage](#abbyy-vantage) as a [text extraction engine](idp-reference.md#extraction-engines), add the following connector secrets. ABBYY Vantage can be combined with any cloud provider you have configured. | Connector secret Key | Required | Description | | :------------------------ | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `IDP_ABBYY_BASE_URL` | Yes | The base URL of your ABBYY Vantage instance.Example: `https://vantage-eu.abbyy.com` | | `IDP_ABBYY_CLIENT_ID` | Yes | The OAuth2 Client ID generated in the ABBYY Vantage **Public API Clients** settings. | | `IDP_ABBYY_CLIENT_SECRET` | Yes | The OAuth2 Client Secret paired with `IDP_ABBYY_CLIENT_ID`. | | `IDP_ABBYY_SKILL_ID` | Yes | The Skill ID of the ABBYY Vantage OCR skill used for text extraction. The skill must be configured to **output Text format**. The Skill ID is the GUID shown in the skill URL. | :::note - These connector secrets are used in IDP document extraction templates. See [integrate IDP into your processes](idp-integrate.md). - You can rename these connector secrets if you want to change the testing configuration used in other environments (such as `test`, `stage` or `prod`). If you do this, you must also change these names to match within the **Authentication** section of the Properties panel for any related published document extraction templates. ::: ## Example IDP deployment {#examples} The following examples show how you can deploy and configure IDP in your local development environment. ### Camunda 8 Run {#idp-c8run-example} To use [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) to deploy and run Camunda 8 with IDP in a local development environment: 1. Ensure you have completed the IDP [Amazon Web Services (AWS) prerequisites](#prerequisites) and have obtained your AWS [access key pair](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html) (_access key_ and _secret access key_). 1. [Install Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run/install-start.md#install-and-start-camunda-8-run). For example, download the latest release of Camunda 8 Run for your operating system and architecture and open the .tgz file to extract the Camunda 8 Run script into a new directory. 1. Navigate to the `docker-compose-8.x` folder in the new c8run directory. 1. Open the `connector-secrets.txt` file, and add your AWS connector secrets. For example: ``` IDP_AWS_ACCESSKEY=AWSACCESSKEYID IDP_AWS_SECRETKEY=AWSSECRETACCESSKEYGOESHERE IDP_AWS_REGION=us-east-1 IDP_AWS_BUCKET_NAME=idp-extraction-connector ``` 1. Save and close the file. 1. Configure [document handling environment variables](/components/document-handling/getting-started.md) for the Tasklist and Zeebe components (for example, in the `.env` file). 1. Start Camunda 8 with Docker Compose. For example, run `docker compose up -d` in that directory. 1. Launch Web Modeler at http://localhost:8070 and log in with the username `demo` and password `demo`. 1. Get started with IDP by creating a new [IDP application](idp-applications.md) in a Web Modeler project. :::info To learn more about Docker Compose configurations and commands for local development, see [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md). ::: ### Docker {#idp-docker-example} To use [Docker](/self-managed/deployment/docker/docker.md) to deploy and run Camunda 8 with IDP in a local development environment: 1. Ensure you have completed the IDP [Amazon Web Services (AWS) prerequisites](#prerequisites) and have obtained your AWS [access key pair](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html) (_access key_ and _secret access key_). 1. Download the latest Camunda Docker Compose release artifact from the [camunda-distributions](https://github.com/camunda/camunda-distributions/releases) GitHub repository, and extract the file contents to your desired directory. 1. In the extracted directory: 1. Open the `connector-secrets.txt` file, and add your AWS connector secrets. For example: ``` IDP_AWS_ACCESSKEY=AWSACCESSKEYID IDP_AWS_SECRETKEY=AWSSECRETACCESSKEYGOESHERE IDP_AWS_REGION=us-east-1 IDP_AWS_BUCKET_NAME=idp-extraction-connector ``` 1. Save and close the file. 1. Configure [document handling environment variables](/components/document-handling/getting-started.md) for the Tasklist and Zeebe components. 1. [Configure Docker Compose environments](/self-managed/quickstart/developer-quickstart/docker-compose/configuration.md#choose-a-docker-compose-configuration). For example, run the full configuration in the extracted directory: ``` docker compose -f docker-compose-full.yaml up -d ``` 1. Launch Web Modeler at http://localhost:8070 and log in with the username `demo` and password `demo`. 1. Get started with IDP by creating a new [IDP application](idp-applications.md) in a Web Modeler project. :::info To learn more about using Docker Compose to run Camunda Self-Managed locally, see [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md). ::: --- ## Document automation :::info Document automation has been superseded by [document classification](idp-document-classification.md) templates, which use LLMs to automatically classify documents by type. To learn more, see [document classification](idp-document-classification.md). ::: --- ## Document classification Automatically classify documents by type using LLM-powered classification templates. ## About document classification Document classification templates use [LLM foundation models](./idp-key-concepts.md#llms) to analyze and categorize documents into defined types based on their content. For example, incoming documents can be classified as invoices, contracts, identity documents, or any custom type you define. - Create a document classification template to categorize documents before routing them to the appropriate process step or [document extraction](./idp-document-extraction.md) template. - Classification templates are published as connector templates that can be [integrated into your processes](./idp-integrate.md), enabling you to route different document types to the correct downstream automation. - Choose and test different LLM models to find the model that best suits your budget and accuracy requirements. :::important Document classification templates require cluster version 8.9-alpha5 or later. ::: ## Create a classification template To create a new document classification template: 1. In your [IDP application](./idp-applications.md), click **Create new** and select **Classification template**. 2. **Name**: Enter a descriptive name for the classification template, such as "Incoming document classifier". 3. **Description**: Enter a description to provide more information about what types of documents this template classifies. 4. **Provider**: Select the cloud provider you want to use for classification. The available providers depend on the [connector secrets](./idp-configuration.md) configured for your cluster. 5. Click **Create** to create and open the new classification template. ## Configure a classification template Complete the following steps to configure and publish a document classification template. ### Step 1: Define document types {#define-document-types} On the **Define classifications** tab, configure the document types that the LLM uses to classify incoming documents. #### Add document types You must define at least two document types before you can publish a classification template. You can add document types from preconfigured types or create custom types: **Add preconfigured document types** IDP provides a set of preconfigured document types (such as invoice, contract, identity document) to help you get started quickly. 1. Click **Add document type**. 2. Browse or search the list of available preconfigured document types. 3. Select the document types you want to add. **Create custom document types** Create your own document types for document categories specific to your business. 1. Click **Add document type** and select **Create custom type**. 2. **Name**: Enter a descriptive name for the document type (for example, "Purchase Order" or "Medical Claim"). 3. **Description**: Enter a description to help the LLM understand the characteristics of this document type. 4. **Classification instructions**: Provide specific instructions to guide the LLM in recognizing this document type. For example, describe key features, typical content, or distinguishing characteristics. #### Edit document types You can edit all aspects of a document type at any time, including: - **Name**: The display name of the document type. - **Description**: A description of the document type's characteristics. - **Classification instructions**: Instructions that guide the LLM in classifying documents of this type. - **Output value**: The value returned in the process when a document is classified as this type. For example, the document type "ID Document" might have an output value of `id-document`. This allows you to align classification output with your business process definitions. To edit a document type, select it from the list and modify the fields as needed. #### Remove document types To remove a document type, select the document type and use the actions menu to delete it. :::note You must have at least two document types defined to publish a classification template. ::: #### Configure fallback behavior {#fallback} You can configure the **fallback output value**, which is the value returned when a document cannot be classified as any of the defined types. By default, this value is `unclassified-document`. You can customize this value to align with your process routing logic. ### Step 2: Test classification {#test-classification} On the **Test template** tab, upload sample documents and evaluate the classification results using different LLM models. #### Upload test documents Upload sample documents that represent the types of documents you expect to classify. 1. Click **Upload documents** to browse for and upload your sample documents. Batch upload is supported. 2. For each upload batch, assign an **expected document type** to enable validation of classification results. The available document types are those you defined in [Step 1](#define-document-types). #### Run classification tests {#run-tests} Select an extraction engine and LLM model, then run classification tests against your uploaded documents. 1. **Extraction engine**: Select the [text extraction engine](./idp-key-concepts.md#extraction-engines) to use for text extraction before classification. 2. **Extraction model**: Select the LLM model to use for classification. 3. Click **Classify documents** to run the classification. #### Review classification results {#review-results} After running a classification test, the results are displayed for each document: - **Classified document type**: The document type assigned by the LLM. - **Reasoning**: The LLM's explanation for why it chose this document type. - **Tokens used**: The number of tokens consumed during classification. - **Latency**: The time taken for classification. Validation indicators show whether the classification matches the expected type. A **summary** of the classification results is shown, allowing you to quickly compare the success rate across different models. :::note Test different combinations of extraction engines and LLM models to find the combination that best suits your document types, budget, and accuracy requirements. ::: ### Step 3: Publish classification template {#publish-template} Publish the classification template to make it available for [integration into your processes](idp-integrate.md). 1. Click **Publish** and select either: - **Publish to project**: Only users in the Web Modeler project can access the classification template. - **Publish to organization**: The classification template is made available as a shared resource within your organization. This option is only available for organization owners or users with the Admin role. 2. On the **Publish Classification Template** dialog, configure the publish settings: - **Extraction engine**: Select the text extraction engine to use for the published classification template. - **Extraction model**: Select the LLM model to use for the published classification template. - **Version name**: Enter a version name for the published classification template. - **Version description**: Enter a description for this version. 3. Click **Publish** to make the classification template available for [integration into your processes](./idp-integrate.md). :::note You must have at least two document types defined before you can publish a classification template. ::: ## Manage versions {#versions} Click **Versions** to view and manage your published classification template versions. ### Compare versions You can compare the change history between two template versions as JSON code in the diff layout. 1. Ensure that the sidebar **Show changes** toggle is turned on. 2. Select the version that you want to compare. The previous version is automatically selected for comparison. ### Restore a version 1. In the sidebar **Versions** list, hover over the version you want to restore. 2. Select the three vertical dots to open the actions menu. 3. Select **Restore as latest**. ### Update a version 1. In the sidebar **Versions** list, hover over the version you want to update. 2. Select the three vertical dots to open the actions menu. 3. Select **Edit** and enter a new name and/or description for the version. ### Delete a version 1. In the sidebar **Versions** list, hover over the version you want to delete. 2. Select the three vertical dots to open the actions menu. 3. Select **Delete**. 4. You are prompted to confirm the deletion. :::caution Deleting a classification template version is permanent. ::: --- ## Document extraction Extract data from a single type of structured or unstructured document. ## About document extraction Document extraction templates form the basis for using IDP in your end-to-end processes. - Create a separate document extraction template for each type of document you want to categorize and extract data from, such as an invoice, a report, identity document, and so on. - Published document extraction templates can then be used to [integrate IDP into your processes](idp-integrate.md). ## Create document extraction template To create a new document extraction template: 1. In your [IDP application](idp-applications.md), click **Create new** and select **Extraction template** to open the Create new project modal. 1. Select the **Extraction method** depending on whether your documents contain structured or unstructured data. - **Unstructured data extraction**: Extract data from unstructured documents. - **Structured form extraction**: Extract data from structured documents. 1. **Name**: Enter a descriptive name for the type of document, such as “Invoice type A” for example. 1. **Description**: Enter a description to provide more detailed information about the document type. 1. **Provider**: Select the cloud provider you want to use for document extraction. The available providers depend on the connector secrets configured for your cluster. The four supported providers are: - **AWS**: Amazon Web Services with Bedrock and Textract (supports both structured and unstructured extraction) - **Azure**: Microsoft Azure with AI Document Intelligence and AI Foundry (unstructured extraction only) - **GCP**: Google Cloud Platform with Vertex AI and Document AI (supports both structured and unstructured extraction) - **OpenAI compatible**: Any provider that implements the OpenAI `/chat/completions` API (unstructured extraction only) :::note If the connector secrets for a specific provider are missing from your cluster configuration, that provider will be unavailable for selection. To enable additional providers, configure the required connector secrets as described in the [IDP configuration guide](idp-configuration.md). ::: 1. Click **Create** to create and open the new document extraction template. 1. Configure and publish the template: - [Extract unstructured data](idp-unstructured-extraction.md): Configure and publish an unstructured data extraction template. - [Extract structured data](idp-structured-extraction.md): Configure and publish a structured data extraction template. :::tip Not sure which extraction method to use? See [structured and unstructured documents](idp-key-concepts.md#structured-and-unstructured-documents) to help determine what type of document(s) you will be processing. ::: --- ## Example IDP integration This worked example shows how you can [integrate IDP](idp-configuration.md) into a simple process. ## About this worked example This worked example demonstrates how a published [document extraction template](idp-document-extraction.md) can be used to extract data from a document uploaded via [Tasklist](/components/tasklist/introduction-to-tasklist.md). In this example, a process is set up with the following steps: 1. **Upload document**: A PDF document is uploaded manually in Tasklist. 1. **Extract data**: A published document extraction template automatically extracts the required data from the PDF document. 1. **View results**: The extraction results are viewed. ## Document extraction template The document extraction template used in this example uses the following extraction fields and sample document. | Field name | Field type | Prompt | | :-------------- | :--------- | :------------------------ | | invoiceType | String | Find the type of invoice. | | invoiceCustomer | String | The invoice customer. | | invoiceId | String | The invoice ID. | ## Upload document In the first step of the process, a [user task](/components/modeler/bpmn/user-tasks/user-tasks.md) and linked [form](/components/modeler/forms/camunda-forms-reference.md) allows a document to be uploaded in Tasklist. - The form uses the [Filepicker](/components/modeler/forms/form-element-library/forms-element-library-filepicker.md) form element to upload a document. - The Filepicker element **Key** is set to `documents`. This is then bound to the **Document** input in the document extraction template. :::info You can also use the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) to upload documents for IDP. To learn more about storing, tracking, and managing documents in Camunda 8, see [document handling](/components/document-handling/getting-started.md). ::: ## Extract data In this step, the document extraction template is [applied to a task](idp-integrate.md#create-and-configure-an-idp-task) to automatically extract data from the uploaded document. - **Input message data**: The **Document** input uses the FEEL expression `documents[1]` to get the first document in the FEEL array, as per the uploaded document **Key**. - **Output mapping**: The extracted data is stored as JSON in a **Result variable** named `idpResult`. ## View results Once the process completes, the results of the extraction are available in the `idpResult` variable. For example, viewing the process in Operate shows the data was accurately extracted from the document as follows: ``` { "extractedFields": { "invoiceType": "A", "invoiceId": "A/3454", "invoiceCustomer": "Camunda" } } ``` :::note This step in the process could be one of many types of element, depending on what you want to do with the extraction results. For example, you might want to display, check, or summarize the extracted data, or route to further actions in the process depending on the document data extracted by IDP. ::: --- ## Integrate IDP into your processes Integrate your published document extraction and classification templates into your end-to-end processes in Web Modeler. :::tip New to IDP integration? See the [example IDP integration](idp-example.md) for a worked example of a simple IDP process. ::: ## About IDP integration When you publish a [document extraction](idp-document-extraction.md) or [document classification](idp-document-classification.md) template, IDP generates a connector template that you can apply to tasks in your BPMN diagrams. Each published template appears as a selectable element template in Web Modeler. ## Create and configure an IDP task You can apply a published IDP template to a task or event via the append menu. For example: - **From the canvas:** Select an element and click the **Change element** icon to change an existing element, or use the append feature to add a new element to the diagram. - **From the properties panel:** Navigate to the **Template** section and click **Select**. - **From the side palette:** Click the **Create element** icon. You can then configure the document extraction template in the properties panel, via the following sections: ## Input message data ### Document Specify the document object variable used for document handling, provided as a [FEEL expression](/components/modeler/feel/what-is-feel.md) with the document reference. For example, if you have uploaded a document via form upload using a `documents` **Key**, you can specify `documents[1]` to retrieve the first document in the array. Example: `documents[1]`. :::info To learn more about storing, tracking, and managing documents in Camunda 8, see [document handling](/components/document-handling/getting-started.md). ::: ## Provider authentication ### Authentication Ensure the **Credentials** AWS authentication type is selected. ### Access key Specify your AWS _access key_ connector secret, provided as a [FEEL expression](/components/modeler/feel/what-is-feel.md). Example: `{{secrets.IDP_AWS_ACCESSKEY}}` ### Secret key Specify your AWS _secret access key_ connector secret, provided as a [FEEL expression](/components/modeler/feel/what-is-feel.md). Example: `{{secrets.IDP_AWS_SECRETKEY}}` ## Provider configuration ### AWS S3 Bucket name Specify the name of the Amazon S3 bucket where documents can be temporarily stored during Amazon Textract analysis as a connector secret, provided as a [FEEL expression](/components/modeler/feel/what-is-feel.md). Example: `{{secrets.IDP_AWS_BUCKET_NAME}}` (for the Amazon S3 bucket used for document storage during extraction). :::note The Amazon S3 bucket name must be unique across all your AWS accounts. ::: ### Region Specify the region where documents can be temporarily stored during Amazon Textract analysis as a connector secret, provided as a [FEEL expression](/components/modeler/feel/what-is-feel.md). This should match the region where the AWS S3 bucket is located. The default region is `us-east-1`. Example: `{{secrets.IDP_AWS_REGION}}` ## Output mapping Specify the process variables that you want to map and export the IDP extraction connector response into. :::info To learn more about output mapping, see [variable/response mapping](/components/connectors/use-connectors/index.md#variableresponse-mapping). ::: ### Result variable You can export the complete IDP extraction connector response (for example, the key value pairs extracted from the document) into a dedicated variable that you can then access anywhere in a process. To do this, enter a unique dedicated variable name in the **Result variable** field. Example: `IDPResult` ### Result expression In addition, you can choose to unpack the content of the response into multiple process variables using the **Result expression** field, as a [FEEL Context Expression](/components/concepts/expressions.md). ## Error handling If an error occurs, the IDP extraction connector throws an error and includes the error response in the error variable in Operate. ### Error expression You can handle an IDP extraction connector error using an Error Boundary Event and [error expressions](/components/connectors/use-connectors/index.md#error-expression). ## Retries ### Retries Specify the number of [retries](/components/connectors/use-connectors/outbound.md#retries) (times) the IDP extraction connector repeats execution if it fails. ### Retry backoff Specify a custom **Retry backoff** interval between retries instead of the default behavior of retrying immediately. ## Execution listeners Add and manage [execution listeners](/components/concepts/execution-listeners.md) to allow users to react to events in the workflow execution lifecycle by executing custom logic. --- ## IDP concepts When using IDP it is helpful to understand the following key concepts and terms. ## Structured and unstructured documents {#documents} Documents are typically classified as containing either structured or unstructured data. ### Structured documents {#structured} Structured documents have a predefined, consistent layout and fixed format, such as rows and columns in a database or spreadsheet, or fields in a standardized form. Data in a structured document has a fixed location. For example, the ID, date, and company name are always located in the same place. Example structured documents include: - Invoices/ customer records - Forms - Identity documents ### Unstructured documents {#unstructured} Unstructured documents have a less defined, free-form layout that can be more difficult to extract structured data from, such as free-text paragraphs where key information is located in unpredictable places. IDP uses an [LLM foundation model](#llms) to extract data from this document type. Example unstructured documents include: - Emails - Reports - Memos ## Document classification {#classification} Document classification uses an [LLM foundation model](#llms) to analyze, categorize, and assign a document type to incoming documents based on their content. - Create a [document classification template](idp-document-classification.md) to define the document types you want to classify (such as invoices, contracts, or identity documents), test classification accuracy, and publish the template for use in your processes. - Classification enables you to route different document types to the correct downstream process step or [document extraction](idp-document-extraction.md) template. - Classification accuracy is improved with well-defined document types (including clear descriptions and classification instructions) and a set of test documents that accurately represents each type of document you want to process. ### Fallback output value {#fallback} The fallback output value is the value returned when a document cannot be classified as any of the defined types. By default, this value is `unclassified-document`, but you can customize it to align with your process routing logic. ### Preconfigured document types {#preconfigured-types} IDP provides a set of preconfigured document types (such as invoice, contract, identity document) to help you get started quickly when creating a [classification template](idp-document-classification.md#define-document-types). You can also create custom document types for categories specific to your business. ## Extraction model/Large Language Models (LLM) {#llms} LLM Foundation models are large-scale, pre-trained AI models that can be adapted for various document processing tasks without extensive retraining. - For IDP, these models serve as a powerful base for extracting, understanding, and processing data from diverse document types. Algorithms are used to learn document patterns and to improve data extraction accuracy over time. - IDP allows you to work with and test different extraction models until you find the model that best suits your budget and accuracy requirements. - See [extraction models](idp-reference.md#extraction-models) for a list of currently supported LLM extraction models. ## Text extraction engines {#extraction-engines} A text extraction engine determines how text is extracted from a document before the LLM processes its content. Different document types and quality levels benefit from different extraction approaches. - **Lightweight parsing** (Fast Extract): For digitally generated PDFs where text is already embedded, a fast built-in parser can extract text without OCR, reducing processing time and cost. - **OCR-based extraction** (AWS Textract, Azure Document Intelligence, GCP Document AI, ABBYY Vantage): For scanned or image-based documents, OCR engines provide high-accuracy text recognition from images. ABBYY Vantage is a third-party OCR engine that can be combined with any cloud provider's LLM. - **Multimodal**: For LLMs that support vision capabilities, the document can be sent directly to the model for native interpretation, bypassing a separate text extraction step entirely. You can select the extraction engine per unstructured extraction template during [extraction testing](idp-unstructured-extraction.md#extract-data), [validation](idp-unstructured-extraction.md#validate-extraction), and [publishing](idp-unstructured-extraction.md#publish-template), to optimize accuracy, performance, and cost for each document type. :::info For a full list of available extraction engines, see [text extraction engines](idp-reference.md#extraction-engines). ::: ## Extraction fields {#fields} Extraction fields are the data fields you want to extract from a document, such as an invoice ID, date, customer name, and so on. - You must add a separate field for each piece of information you want to extract from a document. - For example, for an invoice, add a separate field for the invoice ID, date, customer name, invoice amount, and so on. :::info To learn more about extraction field data types, see [extraction field data types](idp-reference.md#data-types). ::: --- ## IDP reference Technical reference information for IDP, including technical architecture, supported documents, and known limitations. ## Technical architecture {#architecture} IDP offers a composable architecture that allows you to customize and extend IDP capabilities as needed. This flexibility enables you to adapt quickly to evolving business needs while maintaining a streamlined and manageable system. IDP allows you to create, configure, and publish **document extraction templates** and **document classification templates**. These are types of [connector templates](/components/connectors/custom-built-connectors/connector-templates.md). - **Document extraction templates** extract specific data fields from documents. See [document extraction](idp-document-extraction.md). - **Document classification templates** classify documents by type using LLMs. See [document classification](idp-document-classification.md). The document extraction template integrates with Camunda document handling connectors and APIs such as [Amazon S3](/components/connectors/out-of-the-box-connectors/amazon-s3.md), [Amazon Textract](/components/connectors/out-of-the-box-connectors/amazon-textract.md), [Amazon Comprehend](/components/connectors/out-of-the-box-connectors/amazon-comprehend.md), and [Amazon Bedrock](/components/connectors/out-of-the-box-connectors/amazon-bedrock.md) to retrieve, analyze, and process documents. 1. **Document upload**: The template accepts uploaded documents as input. These documents can be uploaded to a local document store, and their references used in the extraction process. For example, the connector uploads the document to an Amazon S3 bucket for extraction. 1. **Amazon Textract**: Uploaded documents are analyzed by Amazon Textract, which extracts text data and returns the results. The template configuration includes specifying the document, the S3 bucket name for temporary storage during Amazon Textract analysis, and other required parameters such as extraction fields and Amazon Bedrock Converse parameters. 1. **Amazon Bedrock**: Your [extraction field](idp-key-concepts.md#extraction-fields) prompts are used by Amazon Bedrock to extract data from the document. The extracted content is mapped to process variables, and the results stored in a specified result variable. :::note - You may encounter errors during extraction and validation if you have not added your Amazon AWS IAM account `access key` and `secret key` as a [connector secret](/components/hub/organization/manage-clusters/manage-secrets.md) to your cluster. See [configuring IDP](idp-configuration.md). ::: ### Document storage {#storage} IDP stores documents as follows during the different extraction stages: 1. Web Modeler: [Uploaded sample documents](idp-unstructured-extraction.md#upload-documents) are stored within Web Modeler itself (SaaS) or the database (Self-Managed). 1. Cluster: During [extraction testing](idp-unstructured-extraction.md#extract-fields) (for example, when you click **Extract document**) the document is stored in the cluster using the [document handling](/components/document-handling/getting-started.md) API. 1. Extraction: Finally, when you extract content using a document extraction template, it is stored in an [Amazon AWS S3 bucket](idp-configuration.md#prerequisites), where it can be accessed by AWS Textract. :::info To learn more about storing, tracking, and managing documents in Camunda 8, see [document handling](/components/document-handling/getting-started.md). ::: ## Document file formats {#file-formats} IDP currently only supports data extraction from the following uploaded document file formats. | File format | Description | | :---------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | PDF | PDF documents must not be password protected.Maximum document file size is 4MB for all IDP operations.Both text and image content can be extracted from a PDF document. For example, data can be extracted from a scanned image that has been converted to PDF. | ## Document language support {#languages} IDP supports data extraction and processing of documents in multiple languages. Language support depends on the [text extraction engine](#extraction-engines) you use. For example, with the AWS provider, IDP integrates with [Amazon Textract](/components/connectors/out-of-the-box-connectors/amazon-textract.md), which supports multilingual text extraction and can detect and extract text in multiple languages. Other extraction engines (Azure Document Intelligence, GCP Document AI) also support multiple languages. Refer to the respective provider documentation for details. :::note At the time of the 8.7 release (April 2025), Amazon Textract can detect printed text and handwriting from the Standard English alphabet and ASCII symbols, and can extract printed text, forms and tables in English, German, French, Spanish, Italian and Portuguese. Refer to [Amazon Textract FAQs](https://aws.amazon.com/textract/faqs/) for current information on supported languages. ::: ## Extraction field data types {#data-types} Specify the [extraction field](idp-key-concepts.md#extraction-fields) data type to indicate to the LLM what type of data it should be trying to extract. This helps the LLM more accurately analyze and extract the correct data. For example, if you want to extract an expected numeric value (such as a monetary value), select the `Number` data type for the extraction field. ### Supported data types You can specify the following extraction field data types. | Data type | Description | | :-------- | :------------------------------------------------------------------ | | Boolean | The LLM should expect a true or false value, such as "yes" or "no". | | Number | The LLM should expect to extract a numeric value. | | String | The LLM should expect to extract a sequence of characters. | ## Extraction models {#extraction-models} You can choose from the following supported LLM extraction models during [data extraction](idp-unstructured-extraction.md#extract-fields). The available models depend on the cloud provider you configure for your document extraction template. ### AWS extraction models The following models are available when you use the AWS provider with Amazon Bedrock: | Extraction model | Model provider | Documentation | | :------------------- | :----------------------------------------- | :------------------------------------------------------------------------------------------------------ | | Claude Sonnet 4 | [Anthropic](https://www.anthropic.com/) | [Anthropic's Claude in Amazon Bedrock](https://aws.amazon.com/bedrock/claude/) | | Claude 3.5 Sonnet | [Anthropic](https://www.anthropic.com/) | [Anthropic's Claude in Amazon Bedrock](https://aws.amazon.com/bedrock/claude/) | | Claude 3 Sonnet | [Anthropic](https://www.anthropic.com/) | [Anthropic's Claude in Amazon Bedrock](https://aws.amazon.com/bedrock/claude/) | | Claude 3 Haiku | [Anthropic](https://www.anthropic.com/) | [Anthropic's Claude in Amazon Bedrock](https://aws.amazon.com/bedrock/claude/) | | Llama 3 70B Instruct | [Meta](https://www.meta.com/gb/) | [Meta's Llama in Amazon Bedrock](https://aws.amazon.com/bedrock/llama/) | | Llama 3 8B Instruct | [Meta](https://www.meta.com/gb/) | [Meta's Llama in Amazon Bedrock](https://aws.amazon.com/bedrock/llama/) | | Titan Text Premier | [Amazon AWS](https://docs.aws.amazon.com/) | [Amazon Titan Text models](https://docs.aws.amazon.com/bedrock/latest/userguide/titan-text-models.html) | :::note Amazon Bedrock LLM extraction models are only available in specific regions. - You must ensure your selected cluster region supports the LLM extraction model you want to use. For example, if you are using the `eu-central-1` region, you cannot use Claude 3 Haiku as it is only available in US regions. - If you have chosen a model not supported in your region, you will receive a 403 "You don't have access to the model with the specified model ID" exception error. - Some newer models (including Claude Sonnet 4) require cross-region inference profiles and are automatically handled by IDP. When you select these models, IDP infers the appropriate regional prefix (`us.`, `eu.`, `apac.`, or `us-gov.`) from your configured AWS region and adds it to enable access across supported regions within your geographic area. For current regional support information, refer to [supported foundation models in Amazon Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/models-supported.html). For more details about cross-region inference, see [inference profiles](https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html). ::: ### Azure, GCP, and OpenAI Compatible extraction models When using Azure, GCP, or an OpenAI Compatible provider, the available extraction models depend on the models deployed and accessible through your provider configuration: - **Azure**: Models available through your [Azure AI Foundry](https://learn.microsoft.com/en-us/azure/ai-foundry/) deployment, including Azure OpenAI models if configured. See [Azure connector secrets](idp-configuration.md#azure-secrets). - **GCP**: Models available through your [Google Cloud Vertex AI](https://cloud.google.com/vertex-ai/docs) deployment. See [GCP connector secrets](idp-configuration.md#gcp-secrets). - **OpenAI Compatible**: Any model accessible through your OpenAI Compatible API endpoint. The **Extraction model** field accepts custom model IDs, allowing you to specify the exact model to use. See [OpenAI Compatible connector secrets](idp-configuration.md#openai-compatible-secrets). ## Text extraction engines {#extraction-engines} Text extraction engines determine how text is extracted from your documents before the LLM processes the content. You can [select an extraction engine](idp-unstructured-extraction.md#extract-data) per unstructured extraction template to optimize for accuracy, performance, and cost based on your document type. | Extraction engine | Provider | Best for | Description | | :-------------------------- | :------------------ | :---------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Fast Extract | Built-in | Digitally generated PDFs | A lightweight, built-in PDF text parser. Faster and lower cost than OCR-based engines, but does not support scanned or image-based documents. | | Multimodal | Provider LLM | Documents where the LLM has vision capabilities | Sends the document directly to the LLM for native interpretation, bypassing a separate text extraction step. Useful when the LLM supports multimodal (text and image) input. | | AWS Textract | AWS | Scanned or image-based documents | Uses [Amazon Textract](/components/connectors/out-of-the-box-connectors/amazon-textract.md) OCR for high-accuracy text extraction. Requires AWS provider configuration. | | Azure Document Intelligence | Azure | Scanned or image-based documents | Uses [Azure AI Document Intelligence](https://learn.microsoft.com/en-us/azure/ai-services/document-intelligence/) for OCR-based text extraction. Requires Azure provider configuration. | | GCP Document AI | GCP | Scanned or image-based documents | Uses [Google Cloud Document AI](https://cloud.google.com/document-ai/docs) for OCR-based text extraction. Requires GCP provider configuration. | | ABBYY Vantage | ABBYY (third-party) | Scanned or image-based documents | Uses [ABBYY Vantage](https://www.abbyy.com/vantage/) OCR for text extraction. Available across all cloud providers when [ABBYY connector secrets](idp-configuration.md#abbyy-secrets) are configured for your cluster. | :::note The available extraction engines depend on the configuration of your cluster and the cloud provider selected for your document extraction template. For example, AWS Textract is only available when using the AWS provider, while ABBYY Vantage is available across all providers once its connector secrets are configured. See [configuring IDP](idp-configuration.md) for setup details. ::: ## Optical Character Recognition (OCR) {#ocr} Optical Character Recognition (OCR) technology is used by several [text extraction engines](#extraction-engines) to detect and extract text and layout from scanned or digital documents. You can use the following OCR-based extraction engines: - **AWS Textract**: Used for both structured and unstructured extraction with the AWS provider. - **Azure Document Intelligence**: Used for unstructured extraction with the Azure provider. - **GCP Document AI**: Used for both structured and unstructured extraction with the GCP provider. - **ABBYY Vantage**: A third-party OCR engine available for unstructured extraction and document classification across all cloud providers. Requires the [ABBYY connector secrets](idp-configuration.md#abbyy-secrets) to be configured for your cluster. ### AWS Textract OCR capabilities Structured data extraction with the AWS provider uses Amazon Textract: - Extracts text, layout, and key-value pairs. - Supports horizontal text only. - Supports English handwriting. - Supported languages for typed characters: Spanish, German, French, Italian, Portuguese. Known limitations: - No language detection. - No vertical text support. - Limited support for complex custom fields. - No detection of table headers. ## Table data extraction {#table-data} IDP can extract table data using LLM foundation models to identify and structure tabular data based on your prompts. ### Default JSON extraction format When extracting repeated elements from a document, the extraction defaults to JSON format unless instructed. In this format: - Table data is represented as an array of objects. - Each object corresponds to a row. - Column names are used as object keys, with values mapped accordingly. #### Example JSON output **Prompt:** "Extract a list of name and ages of patients on floor 1". ```json [ { "name": "Kaitlin Jones", "age": 41 }, { "name": "Thomas Hampton", "age": 57 } ] ``` ### CSV extraction To extract table data in CSV format, specify this in the prompt. The output is then structured in a CSV-compatible format. #### Example CSV output **Prompt:** "Extract a list of name and ages of patients on floor 1 as CSV". ```csv Name,Age Kaitlin Jones,41 Thomas Hampton,57 ``` ### Customize table data extraction You can further refine table extraction by: - Explicitly specifying column headers. - Defining delimiter preferences for CSV. - Requesting additional context for ambiguous data. ## Access rights and permissions Access to IDP features is determined by your Web Modeler user role and associated [access rights and permissions](/components/hub/workspace/modeler/collaboration/collaboration.md#access-rights-and-permissions). For example, users with a Viewer or Commenter role only have read-only access to IDP features, and cannot upload documents, manage extraction fields, or publish templates. | Feature | Viewer/Commenter | Editor/Project Admin | Super-user | | :----------------------------------------- | :-----------------------------------------------------------------------: | :-------------------------------------------------------------------: | :-------------------------------------------------------------------: | | View IDP application | | | | | View document extraction | | | | | View document classification | | | | | View documents | | | | | View extraction fields/prompts | | | | | View validate extraction | | | | | Create/edit/delete IDP application | | | | | Create/edit/delete document extraction | | | | | Create/edit/delete document classification | | | | | Add/edit/delete document types | | | | | Upload/delete documents | | | | | Add/edit/delete extraction fields/prompts | | | | | Extract data | | | | | Test classification | | | | | Save as test case | | | | | Validate extraction (test documents) | | | | | Publish template | | | | | View versions | | | | | Manage versions (edit, restore, delete) | | | | **Key:** Full access | Read-only access ## Validation status {#status} During validation, a validation status is shown for extraction fields to indicate the accuracy of the extracted data. | Icon | Status | Description | | :-------------------------------------------------------------------------- | :------ | :------------------------------------------------------------------------------------------------------------------------------ | | | Pass | The document validation passed with accurate and expected results. | | | Caution | A test case is missing for comparison. Click **Save test case** to create a test case for this field. | | | Fail | The validation results do not match the expected output for the document. Click **Review document** to investigate and resolve. | ### Example The following example shows the results of a partially successful extraction against three documents. The expanded `contract_start_date` field shows that each document returned different validation results. - The first document passed the validation, with the **Extracted value** matching the **Expected test case output**. - The second document could not be validated as a test case was not found for comparison. Click **Save test case** to create a test case for the document. - The third document failed validation as the **Extracted value** did not match the **Expected test case output**. Click **Review document** to open the document again and check the prompt for this field. --- ## Extract structured data Use this extraction method to extract data from [structured documents](idp-key-concepts.md#structured-documents). ## About structured data extraction Use this extraction method for documents with a consistent layout, such as invoices, tax forms (for example, W-2s, VAT declarations), and loan or insurance applications. Structured extraction allows you to: - Upload a sample document. - Automatically discover fields and tables. - Configure the fields and tables you want included in your template. - Receive confidence scores for each extracted value. - Build reusable templates. - Integrate extracted data into BPMN processes via variables. :::info Structured extraction uses [Optical Character Recognition (OCR)](idp-reference.md#ocr) technology. ::: ## Data extraction steps Complete the following steps to configure and publish a structured data extraction template: 1. [Create template and upload sample document](#create): Create a new extraction template, add a name and description, select a provider, and upload a sample document. 2. [Configure template](#configure): Review the extracted results, select which fields and tables to include, and adjust field names if necessary. 3. [Test data extraction](#test): (Optional) Test the configuration of your template against other documents to evaluate how well it performs. 4. [Publish](#publish): Publish the template to make it available for use in your BPMN diagrams, processes, and [document automation](idp-document-automation.md) projects. ## Step 1: Create template and upload sample document {#upload} In the IDP application, click **Create new**, select **Extraction template**, then select **Structured form extraction**, and enter a name, description, and select the provider. :::note After publishing, the template name and description is shown in the element selector when used in a process diagram. Use a clear name and concise description to help other users find and understand when to use the template. ::: ![Create form extraction template](img/idp-create-extraction-project.png) You can edit the description and provider later via the menu button, but changes to the template are only applied after republishing. After creating the template, the new template screen opens. You can upload a sample document that represents the type of document you want to extract data from. ![Upload a sample document](img/idp-structured-instructions-upload.png) To upload your sample document: 1. Drag your sample document into the box or click **Drag and drop a PDF file here or click to upload a file** to browse and upload your sample document. 2. Once you have finished uploading your sample document, the extraction process starts automatically. - The extraction process retrieves the fields and tables from the document. - The extracted fields and tables are displayed in the **Fields** and **Tables** tabs. ![Extracted fields and tables](img/extracted-fields-and-tables.png) ## Step 2: Configure template {#configure} After the extraction process of the sample document is complete, you can configure the template to include only the fields and tables you want to be part of your template. 1. Select the fields you want to include in your template by clicking the checkbox next to each field. ![Extracted fields and tables - select fields](img/extracted-fields-and-tables-configure.png) 2. Select the tables you want to include in your template by clicking the checkbox next to each table. ![Extracted fields and tables - select table](img/extracted-fields-and-tables-configure-tables.png) ### Extracted Fields - **Field name:** Enter a descriptive name for the field, used to identify the field in your template. You can change the name as required. - **Key:** The field key. This matches the key of the extracted field from the uploaded document. - **Value:** The extracted value. - **Confidence score:** How confident the model is in the extracted value. ### Extracted Tables - **Table name:** Enter a descriptive name for the table, used to identify the table in your template. You can change the name as required. - **Min confidence score:** The minimum confidence score of a field in the table. - **Average confidence score:** The average confidence score of all fields in the table. Once you are satisfied with your template configuration, you can test it to validate how well it performs on other documents, or you can publish it directly from this tab. ## Step 3: Test data extraction (optional) {#test} Testing is optional but recommended, as you can evaluate the performance of the extraction template before publishing. testing allows you to see how accurately the template extracts data from other documents of the same type. This ensures better results when using the template in your processes. ![Upload document for testing](img/idp-upload-test-template-empty.png) To test the data extraction: 1. Drag your test document in the box or click **Drag and drop a PDF file here or click to upload a file** to browse and upload your test document. 2. Once you have finished uploading your test document, click **Test extraction template**. 3. The extraction process starts looking for the fields and tables you have selected in your template. ![Extracted fields and tables - test](img/idp-upload-test-template.png) ### Test summary results After the extraction is complete, a summary of the test results is shown. - **Average confidence**: The overall confidence score (as a percentage) for all extracted data. - **Average number of fields extracted**: Number of fields successfully extracted compared to the total expected (for example, "3 / 3"). - **Average number of tables extracted**: Number of tables successfully extracted compared to the total expected (for example, "0 / 1"). ### Detailed results The detailed results section provides a comprehensive view of each tested document: - **Filename**: The name of the uploaded test document. - **Avg. confidence**: A visual confidence bar showing the extraction confidence as a percentage. - **Extracted fields**: Number of successfully extracted fields out of the total. - **Extracted tables**: Number of successfully extracted tables out of the total. - **Actions**: - **View Extraction**: Click to see the detailed extraction results for each field and table. ![View Extraction test template](img/idp-test-template-view-extraction.png) - **Remove**: Delete the test document from the results. You can test multiple documents by: - Clicking **Upload documents** to add more test files. - Clicking **Rerun tests** to test additional documents. ![Extracted test templates](img/idp-extracted-test-template.png)\*\*\*\* ## Step 4: Publish {#publish} Publish the document extraction template to make it available for [integration into your processes](idp-integrate.md). 1. Click **Publish** and select either: - **Publish to project**: Only users in the Web Modeler project can access the document extraction template. - **Publish to organization**: The document extraction template is made available as a shared resource within your organization. This option is only available for organization owners or users with the Admin role. 1. On the **Publish Extraction Project** dialog, configure the publish settings. - **Version name**: Enter a version for the published document extraction template. - **Version description**: Enter a description for the published document extraction template version. 1. Click **Publish** to make the document extraction template available for [integration into your processes](idp-integrate.md). ![IDP template element](img/idp-project-in-bpmn.png) ## Versions {#versions} Click **Versions** to view and manage your published document extraction template versions. ### Compare versions You can compare the change history between two template versions as JSON code in the diff layout. 1. Ensure that the sidebar **Show changes** toggle is turned on. 1. Select the version that you want to compare. The previous version is automatically selected for comparison. The JSON for the previous version is shown on the left, with the currently selected version shown on the right. - Differences between the versions are highlighted in the JSON. - For example, if an extraction field was added, this change is highlighted in green. ### Restore a version You can restore a version to revert to a previous snapshot of a document extraction template. 1. In the sidebar **Versions** list, hover over the version you want to restore. 1. Select the three vertical dots to open the actions menu. 1. Select **Restore as latest**. ### Update a version You can update a version name and description at any time. 1. In the sidebar **Versions** list, hover over the version you want to rename. 1. Select the three vertical dots to open the actions menu. 1. Select **Edit** and enter a new name and/or description for the version. ### Delete a version You can permanently delete a document extraction template version. 1. In the sidebar **Versions** list, hover over the version you want to rename. 1. Select the three vertical dots to open the actions menu. 1. Select **Delete**. 1. You are prompted to confirm the deletion. - Select **Delete version** to permanently delete the version. - Select **Cancel** to cancel the deletion and return to the versions list. :::caution Deleting a document extraction template version is permanent. ::: --- ## Extract unstructured data Use this extraction method to extract data from [unstructured documents](idp-key-concepts.md#unstructured-documents). ## Data extraction steps {#date-extraction} Complete the following steps to configure and publish an unstructured data document extraction template: 1. [Upload documents](#upload-documents): Upload a set of sample documents to use for training the extraction model. 1. [Extract fields](#extract-fields): Add and configure the [extraction fields](idp-key-concepts.md#fields) you want to use to extract data. 1. [Validate extraction](#validate-extraction): Test and evaluate the data extraction results, and publish the document extraction template. ## Step 1: Upload documents {#upload-documents} Start by uploading a set of sample PDF documents that represent the specific document type you want to extract data from. You will use these documents throughout the data extraction process. To upload your sample document(s): 1. Click **Upload documents** to browse for and upload your sample document(s). 1. Once you have finished uploading your sample document(s) and want to start testing data extraction, either: - Select the **Extract fields** tab. - Click the **Extract** icon for the document you want to extract data from. ### Document upload guidelines {#document-upload-guidelines} Start by uploading a sample document that contains all the data fields you want to extract for this type of document. - If a single document does not include all the data fields you require, upload multiple documents to cover all variations of the document type. The number and range of documents you need to upload depends on the complexity of your unstructured data and your requirements. - For example, you must upload at least one sample document for each variation of a document type. This should provide enough extraction accuracy if it is an exact representation of the specific type of document, with no variations in layout or content. However, it is more likely that you will need to upload multiple documents to ensure acceptable extraction accuracy. - When choosing your sample documents, variation is important to ensure the system captures the full range of document types it will encounter. As a general guideline, Camunda recommends starting with three to five documents, and uploading more as needed to represent the full range of possible data types. ## Step 2: Extract fields {#extract-fields} On the **Extract fields** tab, add the data [extraction fields](idp-key-concepts.md#fields) you want to populate with data from your document(s). - Add a separate extraction field for each piece of information you want to extract. For example, for an invoice, you might add a field for the invoice ID, date, customer name, amount, and so on. - You can then extract data from your sample document(s) using your chosen LLM extraction model, edit and refine your fields, and save the extracted data as a test case to compare outcomes across different extraction models. ### Add extraction fields {#add-fields} Add an extraction field for each piece of data you want to extract from your document(s). 1. **Field name**: Enter a descriptive name for the field. - The name format should follow [FEEL naming convention](/components/modeler/feel/language-guide/feel-variables.md#variable-names), for example it is case sensitive and should not include spaces. - The **Field name** is used as an output variable in a BPMN process. - Example: "invoiceId” or "invoice_id". 1. **Type**: Select the data type you want/expect the field to be populated with. - This helps the LLM more accurately extract data. See [extraction field data types](idp-reference.md#data-types). - Example: “Number” for a monetary field (“invoiceAmount”). 1. **Prompt**: Enter a clear and specific prompt to guide the LLM in accurately extracting data. - Try to describe the expected outcome in the prompt in clear and concise terms. For guidance and best practice when writing prompts, refer to the [documentation for your chosen LLM extraction model](idp-reference.md#extraction-models). - Example: For an "invoiceDate" field, you might use "The date when the invoice was issued". 1. Click **Add** to add the field. 1. Repeat the process until you have added all required extraction fields. :::note You can edit and delete extraction fields at any time. Click the three vertical dots next to the field to open the Options menu. ::: ### Extract data and save as test case {#extract-data} Once you have added your extraction fields, select a text extraction engine and LLM model, and test the data extraction. #### Extraction engine selection With the **Extraction engine** dropdown, you can choose how text is extracted from your documents before the LLM processes the content. Select the engine that best matches your document type and processing needs: - **Fast Extract**: A lightweight, built-in PDF text parser for digitally generated PDFs. This option is faster and lower cost, but does not support scanned or image-based documents. - **Multimodal**: Sends the document directly to the LLM for native interpretation, bypassing a separate text extraction step. Useful when the LLM supports vision/multimodal capabilities. - **AWS Textract**: Uses Amazon Textract OCR for high-accuracy text extraction from scanned or image-based documents. - **Azure Document Intelligence**: Uses Azure AI Document Intelligence for OCR-based text extraction from scanned or image-based documents. - **GCP Document AI**: Uses Google Cloud Document AI for OCR-based text extraction from scanned or image-based documents. - **ABBYY Vantage**: Uses [ABBYY Vantage](https://www.abbyy.com/vantage/) OCR for text extraction. Unlike provider-specific engines, ABBYY Vantage is available across all cloud providers once its [connector secrets](idp-configuration.md#abbyy-secrets) are configured for your cluster. :::note The available extraction engines depend on your cluster configuration and the cloud provider you select for your document extraction template. AWS Textract, Azure Document Intelligence, and GCP Document AI are only available when using their respective provider, while ABBYY Vantage is available across all providers when its connector secrets are configured. ::: #### Model selection The **Extraction model** field is both a dropdown and an input field, giving you flexibility in model selection: - **Dropdown selection**: Choose from a list of pre-configured models available in the dropdown. - **Custom model input**: If you want to use a model ID that is not part of the dropdown, you can type it directly into the field. This is useful for custom models or specific model versions that may not be listed in the default options. #### Extract and test 1. **Extraction engine**: Select the text extraction engine you want to use. 1. **Extraction model**: Select the LLM model you want to use. 1. Select the document you want to test the data extraction against. 1. Click **Extract document**. 1. The **Extraction fields** are populated with the extracted document data. - Check the extracted data is accurate and matches what you require from the document. - For incorrect field results, edit the field **Prompt** and retry the data extraction until the results are accurate. - Add additional fields as required during testing. 1. Click **Save as test case** to save the results as a test case. - The **Expected output** for each field is now shown below the actual extracted value. - Any unexpected extraction results for the field are highlighted. 1. (Optional) Test different LLM models with this test case to compare results and determine which model produces the most accurate extraction. 1. Repeat the process of creating and evaluating a test case for your other uploaded sample documents. 1. Once you are ready to validate your data extraction configuration, select the **Validate extraction** tab. :::note - Running an extraction creates a "test" process instance. You can view this in [Operate](/components/operate/operate-introduction.md). - You will achieve different results using different [extraction models](idp-reference.md#extraction-models) and extraction engines. Test different combinations until you find the one that best suits your document type, budget, and accuracy requirements. - You can save and overwrite a test case at any time with your latest results. ::: ## Step 3: Validate extraction {#validate-extraction} On the **Validate extraction** tab, test and validate the configured data extraction against your uploaded documents. This step evaluates the data extraction results produced by the selected extraction engine and LLM extraction model using your extraction fields and prompts. ### Validate extraction To validate the data extraction: 1. Select the **Extraction engine** you want to use for validation. 1. Select the **Project extraction model** you want to use for validation. 1. Click **Test documents** to run the extraction validation against your uploaded sample documents. 1. The extraction validation results are shown in the **Test Case Results** column. - The [validation status](idp-reference.md#status) is shown for each field to indicate the accuracy of the data extracted from each document. For example, if the extracted value matches the expected test case output, it is shown as a “Pass”. - Click on a field to expand the detailed results for each individual document. - A **Field extraction summary** shows a summary percentage value for the overall extraction accuracy to allow you to quickly compare extraction accuracy between different LLM extraction models. 1. Perform any actions required due to the validation results such as saving missing test cases or reviewing documents. If your validation results remain unsatisfactory, try the following before rerunning the validation: - Change the extraction engine to try a different text extraction approach (for example, switch from PDF Parser to an OCR-based engine for scanned documents). - Change the extraction model to try and obtain more accurate results with a different model. - Edit your extraction field prompts. Select the three vertical dots on a field to open the actions menu, and select **Edit**. - Go back to a previous step and edit your data extraction configuration, or upload more sample documents. 1. Once you are satisfied with the extraction accuracy, extraction engine, and extraction model, publish the document extraction template. ### Publish document extraction template {#publish-template} Publish the document extraction template to make it available for [integration into your processes](idp-integrate.md). 1. Click **Publish** and select either: - **Publish to project**: Only users in the Web Modeler project can access the document extraction template. - **Publish to organization**: The document extraction template is made available as a shared resource within your organization. This option is only available for organization owners or users with the Admin role. 1. On the **Publish Extraction Project** dialog, configure the publish settings. - **Extraction engine**: Select the text extraction engine to use for the published document extraction template. - **Version name**: Enter a version for the published document extraction template. - **Version description**: Enter a description for the published document extraction template version. - **Extraction model**: Select the extraction model you want to use for the published document extraction template. 1. Click **Publish** to make the document extraction template available for [integration into your processes](idp-integrate.md). :::note - The most recent **Field extraction summary** results are shown for your chosen **Extraction engine** and **Extraction model** combination. ::: ## Versions {#versions} Click **Versions** to view and manage your published document extraction template versions. ### Compare versions You can compare the change history between two template versions as JSON code in the diff layout. 1. Ensure that the sidebar **Show changes** toggle is turned on. 1. Select the version that you want to compare. The previous version is automatically selected for comparison. The JSON for the previous version is shown on the left, with the currently selected version shown on the right. - Differences between the versions are highlighted in the JSON. - For example, if an extraction field was added, this change is highlighted in green. ### Restore a version You can restore a version to revert to a previous snapshot of a document extraction template. 1. In the sidebar **Versions** list, hover over the version you want to restore. 1. Select the three vertical dots to open the actions menu. 1. Select **Restore as latest**. ### Update a version You can update a version name and description at any time. 1. In the sidebar **Versions** list, hover over the version you want to rename. 1. Select the three vertical dots to open the actions menu. 1. Select **Edit** and enter a new name and/or description for the version. ### Delete a version You can permanently delete a document extraction template version. 1. In the sidebar **Versions** list, hover over the version you want to rename. 1. Select the three vertical dots to open the actions menu. 1. Select **Delete**. 1. You are prompted to confirm the deletion. - Select **Delete version** to permanently delete the version. - Select **Cancel** to cancel the deletion and return to the versions list. :::caution Deleting a document extraction template version is permanent. ::: --- ## Web Modeler Camunda 8 only Web Modeler is a browser-based application for modeling BPMN, DMN, Camunda Forms, and element templates. It enables teams to collaboratively design executable processes as the foundation for scalable IT and business automation. ![Web Modeler Screenshot](./img/context-pad/overview.png) ## Key features | Feature | Description | | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | | [BPMN, DMN, and Forms modeling](../../../modeler/bpmn/bpmn.md) | Create and edit BPMN diagrams, DMN decision tables, and Camunda Forms directly in your browser. | | [Element templates](./element-templates/using-templates.md) | Reuse building blocks to standardize modeling across teams and workspaces. | | [Collaboration](./collaboration/collaboration.md) | Work with team members to speed up the process development cycle. | | [Play mode](validation/play-your-process.md) | Test and validate processes during development. | | [Camunda Marketplace](./modeling/camunda-marketplace.md) | Extend Web Modeler with prebuilt blueprints and connectors. | | [Projects](/components/hub/workspace/manage-projects/manage-projects.md) | Work with sets of files and deploy them in a single bundle. | | [Git synchronization](/components/hub/workspace/manage-projects/git-sync.md) | Connect projects to Git repositories for version control. | | [Process landscape visualization](./process-landscape-visualization.md) | View dependencies and interactions between processes at a glance. | | [Intelligent Document Processing (IDP)](./intelligent-document-processing.md) | Extract and process data from documents to accelerate process automation. | | [AI-powered assistance](modeling/advanced-modeling/camunda-docs-ai.md) | Get intelligent modeling suggestions and automation support. | ## Access Web Modeler Access the modeler in [Camunda Hub](/components/hub/index.md). No installation is required. ## Get started ## Additional resources - [Self-Managed configuration & troubleshooting](../../../../self-managed/components/hub/index.md) --- ## Integrate Web Modeler into CI/CD Intermediate Time estimate: 1 hour [Web Modeler](/components/modeler/about-modeler.md) serves as a powerful tool for the development and deployment of processes and projects. While Web Modeler simplifies one-click deployment for development, professional teams often rely on continuous integration and continuous deployment (CI/CD) pipelines for automated production deployments. The [Web Modeler API](/apis-tools/web-modeler-api/index.md) facilitates integration of Web Modeler into these pipelines, aligning with team practices and organizational process governance. - For low-risk processes, you can use Web Modeler [project development pipeline](/components/hub/workspace/manage-projects/project-pipeline.md) to quickly develop and progress project releases through the stages of a standard development lifecycle. [Version comparison](/components/hub/workspace/modeler/modeling/versions.md#compare-versions) (Visual and XML diffing), built in [review](/components/hub/workspace/manage-projects/project-pipeline.md#review), and [Git Sync](/components/hub/workspace/manage-projects/git-sync.md) provide a powerful combination for collaboration between team members using both Web and Desktop Modeler. - For business-critical and higher-risk processes that require strict governance and/or quality requirements, you can integrate Web Modeler into your CI/CD pipelines. Continuous integration and deployment are pivotal for rapid and reliable software development, testing, and delivery. These practices automate the building, testing, and deployment processes, leading to shorter development cycles, enhanced collaboration, and higher-quality releases. Integrating Web Modeler into your CI/CD pipelines can significantly enhance project development and deployment workflows. By automating project deployment, changes can be promptly and accurately reflected in the production environment. This agility empowers teams to swiftly respond to evolving business needs, fostering a flexible and adaptable process orchestration approach. ## Prerequisites Each pipeline is unique. The Web Modeler API offers flexibility to tailor integrations according to your pipelines. To get started, there are a few prerequisites based on your setup: - A platform to host a version control system (VCS) such as GitHub or GitLab. - An existing pipeline or a plan to set one up using tools like [CircleCI](https://circleci.com/) or [Jenkins](https://www.jenkins.io/), cloud platforms such as [Azure DevOps Pipelines](https://azure.microsoft.com/de-de/products/devops), or built-in solutions of VCS platforms like [GitHub Actions](https://github.com/features/actions) or [GitLab's DevSecOps Lifecycle](https://about.gitlab.com/stages-devops-lifecycle/). - Make yourself familiar with the [Web Modeler API](/apis-tools/web-modeler-api/index.md) through the [OpenAPI documentation](https://modeler.camunda.io/swagger-ui/index.html). - Understand how [clusters](/components/concepts/clusters.md) work in Camunda 8. - Ensure you’ve [created a Camunda 8 account](/components/hub/organization/manage-organization-settings/manage-plan/create-account.md), or installed [Camunda 8 Self-Managed](/self-managed/about-self-managed.md). ## Setup :::tip CI/CD pipeline process blueprint The Camunda Marketplace offers a customizable [process blueprint for CI/CD pipelines](https://marketplace.camunda.com/en-US/apps/439170/cicd-pipeline) to streamline the setup process described below. This blueprint provides a ready-to-use proof of concept for a CI/CD pipeline for Web Modeler, enabling you to synchronize Web Modeler files to GitLab and deploy them across different environments. ::: While a pipeline for project integration and deployment resembles general software CI/CD pipelines, key distinctions exist. Consider the following: - Web Modeler uses [versions](/components/hub/workspace/modeler/modeling/versions.md) to indicate specific process states, such as readiness for developer handover, review, or deployment. - A project comprises diverse resources, such as processes, subprocesses, forms, DMN decision models, connectors, job workers, and orchestrated services. Some applications bundle these resources, while others focus on a single process for deployment. - Process reviews differ from code reviews, occurring on visual diagrams rather than XML. ![Sample CI/CD setup with Web Modeler](img/modeler-ci-cd.png) ### Obtain API clients and tokens Before getting started, obtain API clients and tokens for integrating Web Modeler and accessing the process engine via API: - [Obtain an API token for Web Modeler](/apis-tools/web-modeler-api/authentication.md) - [Obtain an API client for Zeebe](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client) ### Disable manual deployments from Web Modeler To enforce pipeline-driven deployments to your environments, consider disabling manual deployments. Disable manual deployments for any user by configuring environment variables `ZEEBE_BPMN_DEPLOYMENT_ENABLED` and `ZEEBE_DMN_DEPLOYMENT_ENABLED` as documented [here](/self-managed/components/hub/configuration/modeler-configuration.md#general). Users without **Admin** roles in Camunda Hub can deploy only on `dev`, `test`, or `stage` clusters. To restrict their deployment permissions remove the **Developer** role from users in Camunda Hub. :::info Only organization owners or users with the **Admin** role can deploy from Web Modeler to `prod` clusters. ::: Read more in the [user roles documentation](/components/hub/organization/manage-members/manage-users.md). ### Triggering CI/CD You need triggers to initiate the pipeline for files or projects. Choose between manual pipeline start or automatic background triggers based on events. Common approaches include: - Initiating the pipeline manually from your CI/CD tool/platform by uploading the file intended for deployment. - Starting the CI pipeline by creating a pull/merge request in the version control system. - Triggering pipelines by listening to versions with certain characteristics. #### Sync files with version control Synchronize files between Web Modeler and version control systems (VCS) and vice versa. Manage both files and projects by using a complete set of CRUD (create, read, update, delete) operations provided by the Web Modeler API. By syncing files from Web Modeler to your VCS, you benefit from full file ownership and avoid duplicated data housekeeping. For automatic file synchronization, consider maintaining a secondary system of record for mapping Web Modeler projects to VCS repositories. This system also monitors the project-to-repository mapping and update timestamps. To listen to changes in Web Modeler, you currently need to implement a polling approach that compares the update dates with the last sync dates recorded. Use the `POST /api/v1/files/search` [endpoint](https://modeler.camunda.io/swagger-ui/index.html#/Files/searchFiles) with the following payload to identify files updated after the last sync date: ```json title="POST /api/v1/files/search" { "filter": { "projectId": "(PROJECT TO SYNC)", "updated": ">(LAST SYNC DATE)" }, "page": 0, "size": 50 } ``` :::info Pagination is enforced for all listed `search` endpoints. Ensure you obtain all relevant pages. ::: Real-time synchronization isn't always what you need. Consider Web Modeler as a local repository, and update your remote repository only after files are committed and pushed. This aligns with the concept of [versions](/components/hub/workspace/modeler/modeling/versions.md). #### Listening to version creation A version reflects a state of a file in Web Modeler with a certain level of qualification, such as being ready for deployment. You can use this property to trigger deployments when a certain version is created. Currently, you have to poll for versions to listen to new ones created. Use the `POST /api/v1/versions/search` [endpoint](https://modeler.camunda.io/swagger-ui/index.html#/Versions/searchVersions) with the following payload to identify versions created after the last sync date: ```json title="POST /api/v1/versions/search" { "filter": { "created": ">(YOUR LAST SYNC DATE)" }, "page": 0, "size": 50 } ``` You will receive a response similar to this, where the `fileId` indicates the file with the version created: ```json { "items": [ { "id": "string", "name": "string", "fileId": "string", ... }, ... ] } ``` To retrieve the content of this particular version, use the `GET /api/v1/versions/:id` endpoint. To obtain the latest edit state of the file, use the `GET /api/v1/files/:id` endpoint. This endpoint also provides the `projectId` necessary for the `POST /api/v1/projects/search` endpoint if you want to push the full project via the pipeline. Combine these two approaches and listen to versions to sync files to your version control, create a pull/merge request, and trigger pipelines. ## Pipeline stages The following examples illustrate setting up **build**, **test**, **review**, and **publish** stages within a pipeline. ### Build stage While there is no distinct concept for a build package in Camunda 8, artifact structuring depends on your overall software architecture. The build stage should primarily focus on acquiring dependencies and deploying them to a preview environment. #### Set up preview environments Offering an automatically testable and review-ready process preview mandates a dedicated preview cluster. Numerous options exist, varying with software development lifecycle design, preferences, and Camunda 8 deployment type (SaaS, self-managed, or hybrid). This guide proposes a setup with lightweight local self-managed preview clusters (or embedded engines) and full-fledged staging and production clusters (Self-Managed or SaaS). ##### Using fully-featured clusters For local preview environments, you can deploy a comprehensive [Zeebe](https://github.com/camunda/camunda) cluster including Operate and Tasklist. Options include using docker-compose or Kubernetes via Helm. All necessary endpoints and UIs are available for thorough process/application testing. Opt for a cluster version aligned with your production cluster to ensure process compatibility. ##### Using embedded Zeebe engines If you don't need to spawn all apps such as Operate or Tasklist, you can use the lightweight [embedded Zeebe engine](https://github.com/camunda-community-hub/eze), which is a community-maintained project, to set up a cost-effective solution with an in-memory database. Together with the [Zeebe Hazelcast exporter](https://github.com/camunda-community-hub/zeebe-hazelcast-exporter) (community-maintained as well), you can consume data generated from your process for reporting or testing. In the build stage, deploy your process or project to a cluster or embedded engine. Post-pipeline completion, such as deployment to staging or production, preview environments can be discarded. :::tip For GitLab users, consider using [GitLab Review Apps](https://docs.gitlab.com/ee/ci/review_apps/) to provide preview environments. ::: Deploy resources using the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) in this pipeline step, compatible with both SaaS and Self-Managed clusters. Alternately, utilize the [Java](/apis-tools/java-client/getting-started.md) client library or any [community-built alternatives](/apis-tools/community-clients/index.md). :::info Feature branches and Web Modeler installations To maintain a single source of truth, avoid multiple Web Modeler instances for different feature branches. Instead, maintain a single Web Modeler installation for all environments, utilizing versions to signify versioning and pipeline stages. Feature branches can be managed by cloning and merging files or projects, ensuring synchronization using VCS. ::: #### Automate deployment of linked resources/dependencies Pipeline-driven deployment can be executed for a single file or an entire project. A separate system of record, maintained outside Web Modeler, can handle finer-grained dependency management. Fetch the full project for a file using the `GET /api/v1/files/:id` endpoint to acquire the project's `projectId`. Subsequently, use the `POST /api/v1/files/search` endpoint with the following payload to retrieve all project files: ```json title="POST /api/v1/files/search" { "filter": { "projectId": "(PROJECT ID)" }, "page": 0, "size": 50 } ``` :::info Pagination is enforced for all listed `search` endpoints. Ensure you obtain all relevant pages. ::: To retrieve the actual file `content`, iterate over the response and fetch it via `GET /api/v1/files/:id`. Parse the XML of the diagram for the `zeebe:taskDefinition` tag to retrieve job worker types. Utilizing a job worker registry mapping, deploy these workers along with the process if required. If you are running connectors in your process or application, you need to deploy the runtimes as well. Parse the process XML for `zeebe:taskDefinition` bindings to identify the necessary runtimes (in addition to job workers). To learn how to deploy connector runtimes, read more [here](/self-managed/components/connectors/overview.md) for Self-Managed, or [here](/components/connectors/custom-built-connectors/connector-sdk.md#runtime-environments) for SaaS. Deploy resources in this pipeline step using the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md), compatible with both SaaS and Self-Managed clusters. Alternatively, utilize the Java client library or any community-built alternatives. #### Add environment variables via secrets If you are running connectors, you need to provide environment variables, such as service endpoints and API keys, for your preview environment. You can manage these via secrets. Read the [Connectors configuration documentation](/self-managed/components/connectors/connectors-configuration.md) to learn how to set these up in SaaS or Self-Managed. ### Test stage Keep strict quality standards for your processes with automatic testing and reporting. #### Lint your diagrams Add a step to your pipeline for automatic process verification using the [bpmnlint](https://github.com/bpmn-io/bpmnlint) and [dmnlint](https://github.com/bpmn-io/dmnlint) libraries. Maintained by the bpmn-io team at Camunda, these open source libraries provide a default set of verification rules, as well as the option to add custom rules. They provide reporting capabilities to report back when the verification fails. These are the same libraries Web Modeler uses to verify diagrams during modeling. You could even report the wrong diagram patterns together with examples to resolve it using [this extension](https://github.com/bpmn-io/bpmnlint-generate-docs-images). #### Unit and integration tests For unit tests, select a test framework suitable for your environment. If working with Java, the [camunda-process-test](/apis-tools/testing/getting-started.md) library is an excellent option. Alternatively, employ the [Java client](/apis-tools/java-client/getting-started.md) with JUnit for testing your BPMN and DMN diagrams in dev or preview environments. Similar testing can be performed using [community-built clients](/apis-tools/community-clients/index.md). ### Review stage During reviews, use the Modeler API again to [add collaborators](https://modeler.camunda.io/swagger-ui/index.html#/Collaborators/modifyCollaborator), or to [create links to visual diffs of your versions](https://modeler.camunda.io/swagger-ui/index.html#/Versions/compareVersions), and automatically paste them into your GitHub or GitLab pull or merge requests. This provides you the freedom to let reviews happen where you want them, and even include business by sharing the diff links with them in an automated fashion. After review, use the `DELETE /api/v1/projects/{projectId}/collaborators/email` [endpoint](https://modeler.camunda.io/swagger-ui/index.html#/Collaborators/deleteCollaborator) to remove collaborators again. #### Create a link to a visual diff for reviews Use versions to indicate a state for review. Use the `POST /api/v1/versions` endpoint to create a new version, and provide a description to reflect the state of this version using the `name` property. The current content of the file is copied over on version creation. While it is possible to do a diff of your diagrams by comparing the XML in your VCS system, this is often not very convenient, and lacks insight into process flow changes. This approach is also less effective when involving business stakeholders in the review. The Web Modeler API addresses this by providing an endpoint to generate visual diff links for versions. Utilize the `GET /api/v1/versions/compare/{version1Id}...{version2Id}` [endpoint](https://modeler.camunda.io/swagger-ui/index.html#/Versions/compareVersions) to compare two versions. Obtain IDs for the latest versions via the `POST /api/v1/versions/search` [endpoint](https://modeler.camunda.io/swagger-ui/index.html#/Versions/searchVersions), utilizing the `fileId` filter to identify the file to review. The resulting URL leads to a visual diff page similar to this: ![Visual diff of two versions](img/visual-diff.png) ##### Example review flow The following process diagram demonstrates an example flow of how to run a preview using versions and a diff link in GitHub: #### Review a running project If deployed in a review environment, processes/applications can be shared with peers for interactive review. For comprehensive review, full clusters inclusive of Operate and Tasklist can be used for process execution. This closely simulates the final experience. To integrate the preview environment with custom applications, leverage the Operate and Tasklist APIs and deploy them within the review environment. In case you use an embedded Zeebe engine, or want to provide a lightweight, focused review experience, you can use [Zeebe Simple Monitor](https://github.com/camunda-community-hub/zeebe-simple-monitor), which is a community-maintained Web App similar to the [Play mode](/components/hub/workspace/modeler/validation/play-your-process.md) in Web Modeler. Deploying Zeebe SimpleMonitor allows for thorough process testing and review. ### Publish stage Push approved changes to staging or production by deploying them to the respective clusters. You can use the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) to deploy via your pipeline, which works both for a SaaS or Self-Managed cluster. Deployments work slightly different on SaaS and Self-Managed, since there are differences in the cluster connection. Read more about deployments [here](/apis-tools/working-with-apis-tools.md#deploy-processes-start-process-instances-and-more-using-zeebe-client-libraries). #### Define authorizations For clusters with [authorizations](/components/concepts/access-control/authorizations.md) enabled, use the Orchestration Cluster REST API to assign the necessary authorizations through the pipeline. This step ensures appropriate accessibility for process/application stakeholders or updating existing authorizations. ##### Monitoring and error handling As with any CI/CD integration, it's crucial to set up monitoring and error handling mechanisms. These can include: - Monitoring the CI/CD pipeline execution for errors and failures. - Using Operate to catch incidents and send alerts to the pipeline in the test stage. - Sending notifications or alerts in case of deployment issues in both the build and publish stages. - Implementing rollback mechanisms in case a faulty BPMN diagram gets deployed. ## FAQ #### Can I do blue-green deployments on Camunda 8? Blue-green deployments are possible with Camunda 8 with limitations. While switching clusters is quick for new process instances, audit logs and existing process instances remain tied to the previous cluster. Consider exporting audit logs from Elasticsearch or OpenSearch to your own streams if needed. If you don't have to migrate running process instances, keeping them running on the previous cluster alongside new instances on the new cluster is also an option. #### Can I implement blue-green deployments with Camunda 8 SaaS? While blue-green deployments are more straightforward with Self-Managed setups, you can implement similar deployment strategies with Camunda 8 SaaS. Keep in mind the limitations and differences between clusters when planning your deployment approach. #### How can I prevent manual deployments from Web Modeler? To enforce CI/CD pipelines and restrict manual deployments, you can disable manual deployments. For Self-Managed setups, set environment variables `ZEEBE_BPMN_DEPLOYMENT_ENABLED` and `ZEEBE_DMN_DEPLOYMENT_ENABLED`. In Camunda 8 SaaS, only the **Developer** role allows deployments from Web Modeler. Assigning any other role effectively removes deployment privileges. #### How can I sync files between Web Modeler and version control? Use the Web Modeler API's CRUD operations to sync files between Web Modeler and your version control system. Consider maintaining a second system of record to map Web Modeler projects to VCS repositories and track sync/update dates. #### How do I listen to version creation in Web Modeler? Currently, you need to poll for version creations using the `POST /api/v1/versions/search` endpoint of the Web Modeler API. Compare the `created` date of versions with your last sync date to identify newly created versions. #### What is the purpose of the build stage in my pipeline? The build stage focuses on preparing dependencies and deploying them to a preview environment. This environment provides a preview of your process that can be tested and reviewed by team members. #### Can I lint my process diagrams for verification? Yes, you can use the `bpmnlint` and `dmnlint` libraries to automatically verify your process diagrams against predefined rules. These libraries provide reporting capabilities to identify and fix issues during the build stage. #### How can I perform unit and integration tests on my processes? You can use [Camunda Process Test](/apis-tools/testing/getting-started.md) for Java-based unit and integration tests, or community-built clients for other programming languages. These libraries allow you to execute your BPMN and DMN diagrams with assertions in your development or preview environments. #### How do I provide environment variables to connectors in preview environments? You can manage environment variables for connectors using secrets. This can be set up in both Camunda 8 SaaS and Self-Managed. Refer to the [Connectors configuration documentation](/components/connectors/introduction.md) for details. #### How can I monitor and handle errors in my CI/CD pipeline? Implement monitoring mechanisms in your CI/CD pipeline to catch errors and failures during the deployment process. Additionally, consider implementing rollback mechanisms in case a faulty BPMN diagram is deployed. ## Additional resources and next steps - [Camunda 8 overview](https://bit.ly/3TjNEm7) - [Web Modeler API documentation](/apis-tools/web-modeler-api/index.md) --- ## Intelligent document processing (IDP) Use intelligent document processing (IDP) to integrate automated document processing into your end-to-end processes. ## About IDP IDP uses artificial intelligence (AI) and machine learning (ML) to identify, extract, and organize data from your structured and unstructured documents into a structured format you can use in your processes. For example, use IDP to extract and use data from invoices and other document types in your document processing workflow. ## Configure IDP [Configure IDP](idp/idp-configuration.md) for your Camunda 8 setup with access to the required components and credentials. ## Get started with IDP Once initial [configuration](idp/idp-configuration.md#configure-idp) is complete, get started with IDP. ## IDP concepts Learn about key [IDP concepts](idp/idp-key-concepts.md) and terms, such as the difference between structured and unstructured documents, document classification, and how IDP uses LLM Foundation models. - [Structured and unstructured documents](idp/idp-key-concepts.md#documents) - [Document classification](idp/idp-key-concepts.md#classification) - [Preconfigured document types](idp/idp-key-concepts.md#preconfigured-types) - [Extraction model/Large Language Models (LLM)](idp/idp-key-concepts.md#llms) - [Extraction fields](idp/idp-key-concepts.md#fields) ## IDP reference Technical [IDP reference](idp/idp-reference.md) information, including technical architecture and document storage, supported documents and types, and extraction models. - [Technical architecture and document storage](idp/idp-reference.md#architecture) - [Document file formats](idp/idp-reference.md#file-formats) - [Document language support](idp/idp-reference.md#languages) - [Document file formats](idp/idp-reference.md#file-formats) - [Extraction field data types](idp/idp-reference.md#data-types) - [Extraction models](idp/idp-reference.md#extraction-models) - [Validation status](idp/idp-reference.md#status) --- ## Launch Web Modeler Camunda 8 only Design and implement your first diagram using Web Modeler, a component of Camunda. Web Modeler also allows you to create BPMN diagrams, DMN diagrams, and forms. To launch Web Modeler, follow the steps below: 1. After [creating an account and logging in](/components/hub/organization/manage-organization-settings/manage-plan/create-account.md) to Camunda, you are automatically taken to Web Modeler. Navigate between the components by clicking the square-shaped **Camunda components** icon in the top left corner of the page. 2. Select **Create new project** to create a new project and store diagrams. ![web modeler empty home](img/web-modeler-new-user-home.png) 3. Name your diagram. You can go back and change the name any time by clicking on the project name and **Edit name**. 4. Select **Browse blueprints** to open the blueprints dialog and browse blueprints for various use cases as a starting point for your first diagram. ![web modeler blueprint browsing](img/web-modeler-blueprint.png) 5. While browsing blueprints, open the details of a specific blueprint by selecting **More details**. This opens a new tab in [Camunda Marketplace](/components/hub/workspace/modeler/modeling/camunda-marketplace.md). Here, have a closer look at the diagram, and open it in SaaS or Self-Managed. ![Camunda marketplace example](img/camunda-marketplace-example.png) 6. Open a blueprint by selecting **Use blueprint**, which downloads the blueprint into the project and opens it in the diagram screen. Alternatively, click **Create new > BPMN diagram** to create a blank BPMN diagram. --- ## Web Modeler settings Navigate to the Web Modeler settings by clicking on your user icon in the top right corner of the Web Modeler and selecting **Settings**. Here, you can configure email notifications and the project deployment policy. ## Email notifications Configure the projects for which you will receive email notifications when a collaborator mentions you in a comment. To do this, select the top right **Open Settings** user icon in Web Modeler and click **Settings**. Here under **Email notifications**, toggle on or off the options to receive email notifications when you are mentioned in a comment for all projects and new projects. ## Project deployment Organization admins can configure the deployment policy for projects in the Web Modeler settings. By default, only [organization administrators](/components/hub/organization/manage-members/manage-users.md) can deploy projects to clusters marked as [production stages](/components/hub/workspace/manage-projects/project-pipeline.md#deployment-pipeline-stages) from Web Modeler. You can change this in the **Project deployment** settings. To get there, select the top right **Open Settings** user icon in Web Modeler and click **Settings**. Then, select **Project deployment**. Here, you can permit non-admin users with deployment permissions to deploy project versions to production stage clusters after a collaborator has reviewed and approved the project version using the [project review](/components/hub/workspace/manage-projects/project-pipeline.md#review) feature. This setting can only be configured by organization admins and applies to all projects in the organization. By default, only users with the **Web Modeler Admin** role can deploy projects to clusters marked as [production stages](/components/hub/workspace/manage-projects/project-pipeline.md#deployment-pipeline-stages) from Web Modeler. You can change this in the **Project deployment** settings. To get there, select the top right **Open Settings** user icon in Web Modeler and click **Settings**. Then, select **Project deployment**. Here, you can permit non-admin users with deployment permissions to deploy project versions to production stage clusters after a collaborator has reviewed and approved the project version using the [project review](/components/hub/workspace/manage-projects/project-pipeline.md#review) feature. This setting can only be configured by users with the **Web Modeler Admin** role and applies to all projects. If the **Web Modeler Admin** role is not pre-existing, it can be created with the following permissions: - Web Modeler Internal API - `write:*` - Web Modeler Internal API - `admin:*` - Web Modeler Internal API - `admin:*` - Camunda Identity Resource Server - `read:users` Refer to the documentation pages about [assigning roles](../../../../self-managed/components/management-identity/application-user-group-role-management/manage-roles.md) and [adding permissions](/self-managed/components/management-identity/access-management/access-management-overview.md) for detailed instructions. :::info The deployment policy applies only to deployments of **projects** made from Web Modeler. Deployments made from Desktop Modeler and deployments of single BPMN files, for example, are not affected by this setting. ::: --- ## Business rule task linking You can use either of the following approaches to link the DMN decision to be called by a [business rule task](/components/modeler/bpmn/business-rule-tasks/business-rule-tasks.md). ## Using the link button 1. Select a business rule task from the canvas. A link icon appears at the bottom right. 2. Click the link icon and choose any decision from the same project. 3. Click **Link** to complete the linking process. In the properties panel on the right side of the screen, the value **DMN decision** is chosen for the **Implementation** property, and the Decision ID of the decision you chose to link is automatically copied to the **Called decision** section. ![overlay](img/brt_overlay.png) :::note For business rule tasks that are already linked, clicking on the link icon opens a dialog which shows the name of the decision the business rule task is linked to. It is possible to navigate to the linked decision by clicking on it, or you can use the **Unlink** button to remove the link. ::: ![overlay](img/brt_linked.png) ## Using the properties panel You can also enter the Decision ID directly in the **Called decision** section in the properties panel after selecting **DMN decision** for the **Implementation**. - **Binding**: You can also select a different binding for the called decision. See [choosing the resource binding type](/components/best-practices/modeling/choosing-the-resource-binding-type.md). - **Version tag**: If you select **version tag** for the binding, you must enter the actual version tag to use. :::info Deploying a diagram does not automatically deploy linked diagrams. Ensure you deploy linked diagrams separately. You might also consider using a [project](/components/hub/workspace/manage-projects/manage-projects.md) that allows you to deploy all related diagrams together in a single bundle. ::: --- ## Call activity linking You can use one of the following approaches to link the process to be called by a [call activity](/components/modeler/bpmn/call-activities/call-activities.md). ## Using the link button 1. Select a call activity task from the canvas. A link button appears at the bottom right. 2. Click on the button and choose any diagram from the same project. 3. Click the **Link** button to complete the linking process. The process ID of the diagram you chose to link is automatically copied to the **Called element** section in the properties panel on the right side of the screen. ![overlay](img/overlay.png) For call activities that are already linked, clicking on the link button opens a dialog which shows the name of the diagram the call activity is linked to. It is possible to navigate to the linked diagram by clicking on it, or you can use the **Unlink** button to remove the link. ![overlay](img/linked.png) ## Using the properties panel You can also enter the process ID directly in the **Called element** section in the properties panel on the right side of the screen. - **Binding**: You can also select a different binding for the called decision. See [choosing the resource binding type](/components/best-practices/modeling/choosing-the-resource-binding-type.md). - **Version tag**: If you select **version tag** for the binding, you must enter the actual version tag to use. :::info Deploying a diagram does not automatically deploy linked diagrams. Ensure you deploy linked diagrams separately. You might also consider using a [project](/components/hub/workspace/manage-projects/manage-projects.md) that allows you to deploy all related diagrams together in a single bundle. ::: --- ## Camunda Docs AI Camunda 8 only Alpha :::note Camunda Docs AI is an alpha feature. To use this feature, enable the [AI-powered features](https://camunda.com/blog/2024/02/camunda-docs-ai-developer-experience-new-level/) through the [alpha features](/components/hub/organization/manage-organization-settings/enable-alpha-features.md) menu. Learn more about [alpha features](/components/early-access/alpha/alpha-features.md) and [general availability](/reference/announcements-release-notes/release-policy.md#general-availability-ga). ::: Camunda Docs AI provides a smart AI-powered chatbot that helps you find answers to your technical and non-technical questions about Camunda within Web Modeler, rather than navigating and searching across multiple sources of information like documentation, forums, blog posts, etc. Click the **Camunda Docs AI** widget in the bottom right corner of a BPMN/DMN canvas to open the chatbot: ![camunda docs ai trigger](img/camunda-docs-ai-trigger.png) Here, you can ask questions like _How do I design a process?_, _What is BPMN?_, and more. --- ## Form linking You can use one of the following approaches to link a form to a [user task](/components/modeler/bpmn/user-tasks/user-tasks.md) or a [none start event](/components/modeler/bpmn/none-events/none-events.md#none-start-events). :::tip By linking a Camunda Form to a start event, process instances can be started directly [in Tasklist](/components/tasklist/userguide/starting-processes.md) or through your own application built on the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). ::: ## Using the link button 1. Select a user task or none start event from the canvas and a link button will appear at the bottom right. 2. Click on the button and choose any form from the same project. 3. Click the **Link** button to complete the linking process. In the properties panel on the right side of the screen, the value **Camunda Form (linked)** is chosen for the **Type** property, and the form ID of the form you chose to link is automatically copied to the **Form ID** section. For user tasks/start events that are already linked, clicking on the link button opens a dialog which shows a preview of the form the user task is linked to. It is possible to navigate to the linked form by clicking on it, or you can use the **Unlink** button to remove the link. ## Using the properties panel Using the properties panel, you can connect a form to a user task/start event via the **Form** section by choosing between different types. ### Camunda Form (linked) Choosing **Camunda Form (linked)** as the type and entering the form ID directly produces the same result as [using the link button on the modeling canvas](#using-the-link-button). - **Binding**: You can also select a different binding for the called decision. See [choosing the resource binding type](/components/best-practices/modeling/choosing-the-resource-binding-type.md). - **Version tag**: If you select **version tag** for the binding, you must enter the actual version tag to use. Using a linked form is the recommended approach as it allows you to manage form versions independently from the diagram. When deploying a BPMN diagram, Web Modeler will not automatically deploy linked forms. This gives you full control over when and which version of a form is deployed. If you make changes to a form, you must deploy the updated form version separately before or after deploying the diagram, depending on your release process. This allows you to coordinate form and process updates more deliberately and avoid unintentionally deploying in-progress form changes. As a result, you no longer need to re-link forms or [copy and paste JSON configuration](#camunda-form-embedded) when making changes — but you are responsible for ensuring the correct form version is deployed and available to your process. :::warning When deploying a diagram, Web Modeler will not deploy any linked forms automatically. Ensure that all forms linked in your BPMN diagram are available in the cluster and that the chosen binding resolves to the desired version. Diagrams use the latest version of the form unless you change the [binding type for a linked form](#camunda-form-linked). ::: :::info To deploy to a Camunda 8 cluster with a version lower than 8.4, linked forms will be automatically embedded into the BPMN diagram's XML to guarantee backwards compatibility. This conversion will only be applied to the XML deployed to the cluster; the diagram in Web Modeler will not be changed. ::: #### Known issues with linked forms ##### Wrong form used for user task - [zeebe/#16311](https://github.com/camunda/camunda/issues/16311) Some users have encountered an issue that was present in Camunda `8.4.0`, `8.4.1`, and `8.4.2` where linked forms did not correspond correctly to their tasks, leading to discrepancies in workflow execution. We have identified and rectified this issue in the Camunda `8.4.3` release, ensuring that tasks are now generated with the correct forms, as intended in your workflow design. ###### How to fix the issue - Regenerate tasks with correct forms To correct any instances affected by this issue, we recommend the following steps after updating to Camunda `8.4.3`: 1. Navigate to [Operate](/components/operate/operate-introduction.md) to access your workflow instances impacted by the linked form issue. 2. Select the instances where tasks were generated with the incorrect forms by clicking on their process instance key, and you will be navigated to the process instance view. 3. Move the instance to the same task, effectively restarting the task. To do this, click the wrench icon in the top right corner: 4. After this, a popup explaining how process modification works will appear. Click **Continue**, select the active task, and click **Cancel instance**. 5. Click **Add** to add a flow node. 6. Click **Apply notification** to check if the process is correct. 7. Click **Apply**; a new task should be generated with the correct form. ### Camunda Form (embedded) :::info Embedded forms are only supported for job worker-based user tasks. They are not available for the [Camunda user task implementation type](/components/modeler/bpmn/user-tasks/user-tasks.md#user-task-implementation-types). ::: When choosing **Camunda Form (embedded)** as type you have the option to directly paste the form's JSON schema into the **Form JSON configuration** field of the properties panel. The form will be embedded directly into the BPMN diagram's XML representation. This approach is not recommended anymore as it makes it harder to maintain the form and the diagram separately. You will have to manually copy and paste the form's JSON schema into the properties panel every time you make a change to the form instead of benefiting from the advantages of linked forms [described above](#camunda-form-linked). Use this option to ensure that the embedded form does not change when you or someone else makes a change to the source form. ### Custom form key Choose **Custom form key** (only available for user tasks) to create a custom reference to an external form, application, or web page, that you can consume in your custom applications. Read more in the [user task forms reference](/components/modeler/bpmn/user-tasks/user-tasks.md#user-task-forms). --- ## Process documentation with README files Camunda 8 only The README feature brings project documentation directly into the modeling workspaces of business users, developers, and project owners to help capture important context in a way that’s versioned, visible, and maintainable. ![project with readme](img/project-with-readme.png) ## Why use READMEs Process documentation is essential, but often stored outside modeling tools. This makes it hard to find and update, and disconnected from your work. With README support in Web Modeler, you can: - Document your processes **right where you build them**. - Keep information **versioned and automatically synced** with Git. - Make your projects **easier to understand** for everyone involved — from team members to stakeholders. ## What you can document - Workspace goals, scope, and stakeholders - Links to related assets - Go-live dates, status updates, and workspace health - Requirements, process owners, and change logs ## Where READMEs appear - In every workspace, project, and folder (each can have its own README) ![project with readme](img/project-with-readme.png) > Git-connected projects sync README content just like any other file - In the [process landscape view](../../process-landscape-visualization.md) as context for a node or the entire app ![process landscape with readme](img/process-landscape-with-readme.png) :::note Each README is unique to its folder and can’t be moved through the UI. However, you can download or delete associated README files. ::: ## Writing a README README files use standard [Markdown](https://www.markdownguide.org/), a lightweight markup language. A README should provide a snapshot of what your workspace, project, or folder is about. Consider including: - What this project, application, or folder is for - Who owns or maintains it - Key requirements and business rules - Links to related documentation or dashboards :::note Refer to [GitHub’s README guide](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-readmes) for general inspiration. ::: ## README interaction README file changes are fully versioned, you can see previous versions and recent changes by selecting the **Show versions** icon: ![view readme versions ](img/view-readme-versions.png) README files can be downloaded by selecting the **Download** icon: ![download readme](img/download-readme.png) To see the final result of a README file, select the **Preview** tab: ![readme preview](img/readme-preview.png) ## Related resources - [Projects](/components/hub/workspace/manage-projects/manage-projects.md) - [Process landscape visualization](../../process-landscape-visualization.md) --- ## Public start forms were removed Public start forms were removed in Camunda 8.10 together with Tasklist V1. For the release-level summary of this removal, see the [8.10 release announcement](/reference/announcements-release-notes/8100/8100-announcements.md#removal-of-legacy-apis-tasklist-v1-dependent-features-and-zeebe-process-test). ## Current alternatives - For authenticated users inside your organization, link a Camunda Form to a start event and start the process directly [in Tasklist](/components/tasklist/userguide/starting-processes.md). - For public-facing use cases, build your own application with [Camunda Forms](/components/modeler/forms/utilizing-forms.md) and the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). ## If you are migrating from an older version Remove dependencies on public start form links before moving to 8.10. If you used this feature in older versions, replace it with either authenticated Tasklist starts or a custom public application. --- ## Camunda Marketplace Discover the **Camunda Marketplace**, your go-to destination for leveraging various contributions from the Camunda community, trusted partners, and the Camunda team. Follow our intuitive guides to explore and harness connectors and blueprints using Web Modeler. If you prefer to utilize these resources within Desktop Modeler, download them directly from the [Camunda Marketplace website](https://marketplace.camunda.com). If you are a **[Camunda Hub Self-Managed](/self-managed/components/hub/index.md)** user, be aware that your organization may restrict access to marketplace resources. If you are unsure about your organization's access, contact your organization's owner for clarification. ## Browse Marketplace connectors :::note Connectors created by partners or the community are not part of the commercial Camunda product. Camunda does not support these connectors as part of its commercial services to enterprise customers. Please evaluate each client to make sure it meets your requirements before using. ::: To navigate to the Camunda Marketplace, take the following steps: 1. Log in to your Camunda account. 2. Click on an existing project, or create a new project by clicking **New project > Create new > BPMN diagram**. 3. While modeling your BPMN diagram, you can incorporate Marketplace connectors from the append menu. The append menu can be accessed in three ways: - From the canvas, select an element and click the **Change element** icon. - From the properties panel on the right side of the screen, navigate to the **Template** section and click **Select**. - From the side palette, click the **Create element** icon. ![change element](../img/change-element.png) 4. Click the **blue shop icon** next to Change element to open the Camunda Marketplace modal. ![marketplace icon](../img/marketplace-icon.png) 5. Browse [available connectors](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md), tick the boxes on the left side of the modal to implement filters, and search for a specific connector by typing in the **Search for a connector** search bar. ![camunda marketplace](../img/connector-marketplace.png) :::note Want to learn more about a connector before applying it to your diagram? Every connector in the Camunda Marketplace offers additional documentation by clicking the **Documentation** link inside the connector's box. This will open a new tab in your browser of the [Camunda Marketplace](https://marketplace.camunda.com/) and additional details for the connector you selected. ::: ## Download a connector to your diagram Once you find a connector you want to integrate into your BPMN diagram, click **Download to project**. The resource is then downloaded from the Camunda Marketplace into your project. Scroll down in the change type context menu and click on your downloaded connector to change the type of existing task. You can then add the required details in the properties panel on the right side of the screen. After downloading, you may view a modal reading **Connector already exists**: - By clicking **Save as copy**, you are not overwriting the current connector. Instead, you are saving this as a new file you can edit. - By clicking **Replace resource**, you are replacing the current connector. If you are downloading a connector from the Camunda Marketplace, it is read-only and you can view it if you are opening the template using the Camunda template editor. To edit the connector, click **Customize template** to duplicate this template. :::note You can also host custom connectors developed with [Connector SDK](/components/connectors/custom-built-connectors/connector-sdk.md). Instead of viewing **Download to project**, it may read [**Learn more about self-hosted connectors**](/components/connectors/custom-built-connectors/host-custom-connector.md). For the out-of-the-box connectors provided by Camunda, the connectors Bundle project provides a set of all connector templates related to one release version. These are templates that are reusing the built-in connectors via the [Protocol connector Approach](/components/connectors/protocol/rest.md). This means a developer created a template and reused one of the built-in connectors. Only for these templates is direct **Download to project** available. ::: ## Browse Marketplace blueprints 1. Log in to your Camunda account and navigate to Web Modeler by clicking the Camunda components icon in the top left corner of your console, and then select Modeler. 2. Select an existing project or create a new one within the projects tab. 3. If you initiate a project with a pre-defined blueprint, navigate to the Marketplace modal by clicking on **Browse blueprints**. If you wish to incorporate it into an existing project, open the **Create new** dropdown and select **Browse blueprints**. ![Browse-blueprints-ctas](../img/browse-blueprints-ctas.png) 4. Within the modal, you'll discover a variety of blueprints submitted by Camunda, partners, or community members to the **Camunda Marketplace**. These can include BPMN, DMN, and/or Form files. Utilize the sidebar to filter blueprints by use case, or leverage the sub-navigation to search and filter by industry, creator, or supported Camunda version. ![Marketplace-modal-blueprints](../img/marketplace-modal-blueprints.png) 5. Once you've found the desired blueprint, click **Use blueprint** to open it in Web Modeler and start your work. The blueprint will be automatically saved within the project you initiated. 6. If you can't find the right blueprint, you can suggest ideas in our [Idea Portal](https://marketplace.camunda.com/en-US/pages/connectorsIdeaPortal) or contribute your own process to the [Camunda Marketplace](https://marketplace.camunda.com/en-US/pages/submissionMenu). ## Additional resources - Learn more about our available [out-of-the-box connectors](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md). - Understand different [Connector types](/components/connectors/connector-types.md). - Learn how to modify BPMN elements with [Connector templates](/components/connectors/custom-built-connectors/connector-templates.md) to create custom modeling experiences. --- ## File download Camunda 8 only You can download all the files stored in Web Modeler to your local computer for offline editing or integration into version control. You may also export BPMN diagrams in different image formats. ### Download from the project or folder page To download a single file or folder, use the **Download** option in the list item's action menu: A file will be downloaded in form of its XML or JSON definition (depending on the file type). A folder will be downloaded as a zip archive containing all the folder's files and subfolders. To download multiple files or folders together as a zip archive, use the **Download** option in the multi-select action bar: :::note Download limit You can download up to 100 files and 20 folders at the same time. ::: ### Download from the file page #### BPMN and DMN diagrams You can download the diagram's BPMN 2.0/DMN 1.3-compliant XML definition from the action menu. For BPMN, you also find the options to export the diagram as a PNG or SVG image there: #### Forms You can download the form's JSON definition (or copy it to the clipboard) from the action menu: #### Other file types You can download the file's contents by clicking the download button in the toolbar: --- ## Fix problems in your diagram ### Design time errors Based on a set of lint rules, Web Modeler continuously validates implementation properties for a process diagram while the user is modeling. The validation errors are added to the panel at the bottom of Web Modeler. Expand the panel to view the errors by clicking the **Problems** header. The panel is collapsed by default and the latest state (expanded or collapsed) is remembered for the next time you open Web Modeler. ![error panel](../img/diagram-errors/error-panel.png) :::note An error is shown if any process ID, decision ID, or form ID exceeds the supported length for the target environment. To avoid backend-specific deployment problems, keep those IDs short and consistent with the limits of the cluster you plan to deploy to. ::: ### Camunda version selection The version selector at the top right in the problems panel can be used to choose the Camunda version the diagram is validated against. The chosen version should match the version of the cluster where the diagram will be deployed so that the correct set of errors is shown. If you do not know the version, it is shown alongside the cluster name in the deployment dialog, which can be opened by clicking the **Deploy diagram** button. The version selector also provides information about the number of clusters available for each Camunda version within the current organization. ![error panel](../img/diagram-errors/version-selector.png) ### Interactivity The errors are interactive. Clicking on the row highlights the corresponding element in the canvas and points to the specific property in the properties panel where you can resolve the issue. ![error panel](../img/diagram-errors/interactivity.png) ### Deploy time errors If errors are thrown by the engine when deploying a diagram (whether they were caught by design time errors or not), they will be displayed in a modal. ![error panel](../img/diagram-errors/engine-error.png) They will also be kept available in the output panel while you remain on the modeling page. ![error panel](../img/diagram-errors/engine-error-output.png) --- ## Upload files Camunda 8 only Upload a BPMN diagram, DMN diagram, or any other supported file into Web Modeler using one of the following methods: - In a project, click **New > Upload files** and select the files from your computer. ![import diagram](../img/import-diagram/web-modeler-upload-file-menu-item.png) ![import diagram](../img/import-diagram/web-modeler-upload-file-choose.png) ![import diagram](../img/import-diagram/web-modeler-upload-file-completed.png) - In a project, drag one file from your computer and drop it. ![import diagram](../img/import-diagram/web-modeler-project-drag-and-drop.png) :::note For the two options below, the content of the Web Modeler resource is replaced with the content of the file. ::: - In a Web Modeler resource editor, open the breadcrumb menu and choose **Replace via upload**. Then, select a file from your computer. ![import diagram](../img/import-diagram/web-modeler-replace-via-upload-menu-item.png) ![import diagram](../img/import-diagram/web-modeler-replace-via-upload-choose.png) - In a Web Modeler resource editor, drag one file from your computer and drop it onto the canvas. ![import diagram](../img/import-diagram/web-modeler-diagram-replace-via-drag-and-drop.png) ### Undo/redo management limitations You cannot undo or redo any actions taken before an upload as the undo/redo history is cleared when a diagram is uploaded. This prevents inconsistencies between the uploaded diagram and any actions taken before the upload. --- ## Import resources into Web Modeler Learn how to import resources into Web Modeler, how automatic handling works for template conflicts and ignored templates, and how to troubleshoot common import errors. ## Supported import resources Using Web Modeler, you can import the following resource types into a project: - BPMN process models. - Forms. - DMN decision tables. - Element/connector templates. - Markdown files such as `README.md`. - Any other resource type supported by Web Modeler. ## How to add files These are the supported methods you can use to add files to Web Modeler: | Method | Opens from | Import source | Supported resources | | --------------------------------------------------------------------------------- | ------------------------------------------ | -------------------------- | ------------------------------------------------------------------------- | | [**Import**](./preparing-resources-for-import.md) | Web Modeler home page via URL | Any publicly available URL | Any type of resource | | [**Browse blueprints**](./camunda-marketplace.md#browse-marketplace-blueprints) | Web Modeler project or folder page | Camunda Marketplace only | [Blueprints](./camunda-marketplace.md#browse-marketplace-blueprints) only | | [**Discover connectors**](./camunda-marketplace.md#browse-marketplace-connectors) | Append menu inside Web Modeler BPMN editor | Camunda Marketplace only | Element templates only | | [**Upload files**](./import-diagram.md) | Web Modeler project or folder page | Any downloaded file | Any type of resource | To import via a URL hosted on your Web Modeler, see [Prepare resources](./preparing-resources-for-import.md) for more information. :::important - **Import** and **Browse blueprints**: If the imported resources include at least **one BPMN**, Web Modeler treats them as a **project** and groups them accordingly. - **Upload files** and **Discover connectors**: Always treats files as **independent files**, regardless of whether BPMN files are present. ::: ## Template conflicts Element templates, including connector templates, have an associated ID, which is used to find the template when a BPMN process references it. When importing templates, Web Modeler checks for potential conflicts with existing templates already available in your project or organization. A conflict occurs when an imported template has the same ID as an existing one. You can resolve template conflicts using one of these two options: 1. Save as copy: It creates a new file with a new, auto-generated ID. :::note This option is not available when importing projects. ::: 2. [Replace an existing template](#replace-a-template). ### Replace a template You can replace an existing template when: - The template belongs to the project you are importing into. - The imported template has a higher version than the existing one. If an imported template **replaces** an existing template: - The **file contents** of the existing template are overwritten by the imported template. - Due to Web Modeler safeguards, you can only publish **higher versions** of that template in the future. Older or equal versions are blocked from publication to prevent accidentally overwriting already published versions. This behavior ensures consistency for processes that already use the template, but note that historical versions cannot be republished under the same ID and version. ## Ignore templates Web Modeler ignores a template if it detects a **functionally equivalent** template already exists in your project or organization. This ensures that importing the project doesn’t break your existing setup. A template is considered functionally equivalent when, after minifying the JSON, the following fields are equal: - `id` - `version` - `appliesTo` - `elementType` - `groups` - `properties` When a template is ignored: - The imported template is not added as a new resource. - The project uses the existing template definition instead. - There may be small differences in: - Display name. - Documentation URL. - Icon. ## Troubleshoot ### Why your resources might fail to import You might not be able to import certain resources because they fail validation or cannot be processed. These are common reasons: - **Existing template**: A newer or equal version of the same template ID already exists, and the incoming template's contents are not available in your project. - **Invalid file**: The file does not conform to the expected schema. For example, malformed element template JSON. - **Network error**: Web Modeler could not download the file from the given URL. - **Only one README file allowed**: You cannot add additional README files because each project allows only a single README. - **Too large**: The file exceeds Web Modeler’s per‑file size limit. - **Unknown error**: A generic error for unexpected failures. - **Unrecognized file**: The file type is not supported by this version of Web Modeler. ### Manually upgrade a conflicting template If a project depends on a template that is being ignored or differs from your existing template, you can manually upgrade it following these steps: 1. Copy the desired template contents: - Open the template JSON file from the import source. - Copy the entire template definition. 2. Navigate to the conflicting template: - In Web Modeler, open the existing template that shares the same ID and version. - If you do not have access, ask your organization or project admin to open it. 3. Replace the contents of the existing template with the copied JSON. 4. Increase the `version` field to a number higher than the highest published version. 5. Publish the updated template to the relevant project and/or organization. 6. Reopen the BPMN process and click on an element that uses the template. 7. Click **Update available**. Then, click **Update**. 8. The updated fields should now be visible in the properties panel. ### Many resources are ignored or not imported If you see many resources that cannot be imported, check the following: - Confirm that the URLs (for single-file imports) or the `.zip` URL are: - Publicly reachable. - Pointing to the correct files. - For `.zip` imports: - Check that the archive size is at most **10 MB**. - Ensure there are no more than **100** meaningful entries; extra files might be ignored. - For templates: - Verify that IDs and versions are what you expect. - Consider whether equal or higher versions already exist. If the issue persists, try importing a small subset of files or a simpler `.zip` to isolate problematic resources. ### Properties panel fields are missing or show “template not found” You may experience this issue when: - A user task or connector task in BPMN shows a warning such as “template not found”. - Some fields you expect to see in the properties panel are missing. - Some templates could not be imported. #### Why this happens - The process expects a **template version** that was not imported: - A higher version already exists and the imported version was ignored. - An intermediate version (for example, version 2) does not exist, while version 1 and 3 do. - An existing template with the **same ID and version** has different contents. #### How you can solve it - Manually upgrade the template to match the process. See the [manual upgrade steps](#manually-upgrade-a-conflicting-template) section for more details. - If a higher version already exists, you may need to: 1. Unlink the older template version in the BPMN editor. 2. Link the task to the newer template. --- ## Model your first diagram Camunda 8 only After you've created a BPMN diagram, you can start modeling it. A new diagram contains a single start event, the starting point of every process. From here, you can add elements to build out your process. ![Initial diagram](./img/model-your-first-diagram/model-1.png) Use the plus icon in the **context pad** to append your first element, such as a task. ![Append menu](./img/model-your-first-diagram/model-2.png) To change the type of an element, use the replace icon in the **context pad**. For example, you can turn a generic task into a user task. ![Replace menu](./img/model-your-first-diagram/model-3.png) You can also use the **context pad** to link resources, such as forms, to an element. ![Properties panel](./img/model-your-first-diagram/model-4.png) Use the **properties panel** on the right side to inspect and edit the technical properties of each element. ![Properties panel](./img/model-your-first-diagram/model-5.png) To revert or reapply changes, use the **Undo** and **Redo** buttons on the canvas. :::note Undo and redo behavior has limitations when [collaborating](../collaboration/collaboration.md#undoredo-management-limitations) and [importing a diagram](import-diagram.md#undoredo-management-limitations). ::: ## Additional resources - [Camunda Academy: Camunda 8 Web Modeler Overview](https://academy.camunda.com/c8-web-modeler-overview) --- ## Prepare resources for import into Web Modeler Learn how to prepare resources for import into Web Modeler, including guidelines and steps to create import URLs. :::note This guide is intended for repository maintainers, blueprint authors, and solution builders who prepare resources for others to import into Web Modeler. ::: ## Supported methods Prepare resources for import into Web Modeler in two main ways: 1. [**Individual resources**](#prepare-individual-resources): Each resource, such as a BPMN or DMN file, a template, or a README, is accessible via its own public URL and can be imported one by one. 2. [**Packaged resources**](#prepare-packaged-resources): Bundle all resources, such as an entire project, into a single `.zip` file. This allows importing the full package from a single URL. ## Guidelines for importing ### Hosting requirements Each individual resource (or `.zip` file package) must: - Be hosted at a **publicly accessible URL** that does not redirect to another page. - Not require authentication, VPN, or non‑public network dependencies. ### Template naming - Use stable, distinct template IDs to ensure BPMN files can reference them consistently and to avoid conflicts with other templates users might download. - Increment the version number whenever you introduce changes that could affect existing processes. - Element template IDs must be unique within the set of imported files. - BPMN process IDs must be unique within the set of imported files. :::important - If the imported resources include at least one BPMN, Web Modeler treats them as a **project** and groups them accordingly. - If no BPMN file is present, the resources are imported as **independent files** into the chosen project or folder. ::: ## Prepare import resources ### Prepare individual resources Use this approach when: - You have a small set of resources. - You don't expect to add or remove files often. Keep each file within Web Modeler’s per‑resource size limit of **three MB**. ### Prepare packaged resources Use this approach when: - Your solution includes many resources (up to 100), such as multiple BPMN and DMN models, forms, element templates, and documentation. - You want to minimize the risk of missing dependencies during import. #### File and archive limits When preparing the packaged resources into a `.zip` file: - Keep the total `.zip` file size at or below **10 MB**. - Include **at most 100 files** that Web Modeler can support. - Keep each packaged file within Web Modeler’s per‑resource size limit of **three MB**. - Include at most one README file. - Note that the folder structure **will not** be imported into Web Modeler. #### Content and security rules To minimize issues during import: - Do not include files with `..` or leading slashes in their name. - Exclude executables, scripts, and any other files not supported by Web Modeler. - Ensure each resource file is within Web Modeler’s per‑resource size limit. - Use clear, stable file names. ## Create an import URL 1. Get the public URL to the resource. For GitHub-hosted resources, see first [Get public URLs from GitHub](#get-public-urls-from-github). 2. Form the Web Modeler URL like this: ``` /import/resources?source= ``` 3. To add more resources, add a comma after the last URL and paste the next URL: ``` /import/resources?source=, ``` 4. (Optional) Add a title. When the resources are treated as a project, this will be the project's name. ``` /import/resources?title=&source=, ``` ### Get public URLs from GitHub You can host your resources on any public-facing website that allows direct access. See [Hosting requirements](#hosting-requirements) for more details. ### Individual resource URL To get a public URL for a single resource: 1. Open the resource in your public GitHub repository. 2. Click **Raw**. 3. Copy the URL from your browser’s address bar. :::important The URL from step 3 is a direct link that Web Modeler can access without redirects. ::: ### Packaged resources URL To get a public URL for packaged resources into a `.zip` file: 1. Open the `.zip` file in your publicly accessible GitHub repository. 2. In the file view, right‑click the **Raw** button in the top right. 3. Click **Copy link**. 4. In a text editor: - Replace `github.com` with `raw.githubusercontent.com`. - Remove the `/raw` segment that appears once in the URL after the repository name. - Leave everything else unchanged. You can automate this transformation as follows: ```bash GITHUB_RAW_ZIP_URL=your_original_url echo "$GITHUB_RAW_ZIP_URL" \ | sed -E 's#https://github.com/([^/]+)/([^/]+)/raw/(.+)#https://raw.githubusercontent.com/\1/\2/\3#' ``` --- ## Versions Camunda 8 only :::note With 8.7, "milestone" has been renamed to "version". To learn more about this change, see [the related release note](/reference/announcements-release-notes/870/870-release-notes.md#web-modeler-milestones-renamed-to-versions). ::: You can create a version at any time to save a snapshot of your BPMN or DMN diagram. This version is a Web Modeler snapshot version, not a deployed process definition version. See [version](/reference/glossary.md#version). You can restore a version to revert to a previous snapshot of your diagram, for example if you make a mistake while modeling. You can also compare two versions to see the differences between them. ## Versions list You can use the versions list to view, compare, and manage your diagram versions. To view the versions list, select **Versions > Show versions**. ![Versions list showing the show versions button](../img/versions/web-modeler-version-action-show-versions.png) ## Create a version You can create a new version either from your diagram or the versions list. - From your diagram, select **Versions > Create version**. ![versions create via the breadcrumb menu](../img/versions/web-modeler-version-create-via-versions-menu.png) - From the versions list, hover over the draft in the **Versions** panel and select **Create a new version**. ![versions create via icon](../img/versions/web-modeler-version-create-via-icon-highlight.png) ## Compare versions You can compare the change history between two versions, either visually as a diagram or as code in an XML diff layout. 1. Open the versions list for your diagram. 1. Ensure that the sidebar **Show changes** toggle is turned on. 1. Select the version that you want to compare. The previous version is automatically selected for comparison. :::note Turn off the sidebar **Show changes** toggle to view individual versions without comparison to the previous version. ::: ### Compare versions in visual view To view BPMN diagram changes visually, select the **Visual view** tab. ![versions diffing in visual view](../img/versions/web-modeler-version-visual-diffing.png) - Differences between the versions are highlighted visually on the diagram. For example, if an element was added, this change is highlighted in green with a plus symbol. Hover over a change to view more details. - Only differences that affect the execution of the BPMN process are highlighted. - The sidebar **Changes** list shows the details of each change, including the type and identifier. Select a change to highlight it. :::note You can only use the **Code view** to compare changes in a DMN diagram. The **Visual view** only shows a view of the version. ::: ### Compare versions in code view To view BPMN and DMN diagram changes as code in an XML diff layout, select the **Code view** tab. ![versions diffing in code view](../img/versions/web-modeler-version-code-diffing.png) - The XML for the previous version is shown on the left, with the currently selected version shown on the right. - Differences between the versions are highlighted in the XML. For example, if an element was added, this change is highlighted in green. ## Restore a version You can restore a version to revert to a previous snapshot of your diagram. 1. In the sidebar **Versions** list, hover over the version you want to restore. 1. Select the three vertical dots to open the actions menu. 1. Select **Restore as latest**. ![versions restore](../img/versions/web-modeler-version-restore-highlight.png) The diagram reverts to the restored version. A new version is created with "(restored)" appended to the name. ![version restored](../img/versions/web-modeler-version-restore-complete-highlight.png) ## Copy a diagram version You can create a new diagram by copying a specific version. 1. In the sidebar **Versions** list, hover over the diagram version you want to copy. 1. Select the three vertical dots to open the actions menu. 1. Select **Copy to...**. 1. Choose a project/folder and select **Copy here** to create the new diagram in the chosen folder. ## Update a version You can update a version name and description at any time. 1. In the sidebar **Versions** list, hover over the version you want to rename. 1. Select the three vertical dots to open the actions menu. 1. Select **Edit** and enter a new name and/or description for the version. ## Delete a version You can _permanently_ delete a version. 1. In the sidebar **Versions** list, hover over the version you want to rename. 1. Select the three vertical dots to open the actions menu. 1. Select **Delete**. 1. You are prompted to confirm the deletion. - Select **Delete version** to permanently delete the version. - Select **Cancel** to cancel the deletion and return to the versions list. :::caution Deleting a version is permanent. You cannot access a deleted version, and it is removed from the versions list. ::: --- ## Process landscape visualization Camunda 8 only Process landscape view provides a visual map of BPMN files in a project and interfile [call activities](/components/modeler/bpmn/call-activities/call-activities.md) connections between them. - Quickly understand process dependencies, flow relationships, and overall project structure in a single interactive canvas. - The view is automatically generated from all BPMN diagrams within a project, offering insights without the need for manual maintenance or updates. ## Open process landscape view To open the process landscape view, click **View landscape** from any of the following views: - Folder view - [Project](/components/hub/workspace/manage-projects/manage-projects.md) view ![process landscape visualization button](img/process-landscape-view-button.png) ## Landscape view interaction You can interact with the process landscape view in a number of ways. ### View BPMN file information Click on any node to view the information for the selected BPMN file, including the latest version of the process on the sidebar. ![selected node information](img/process-landscape-node-information.png) :::note For projects, version tags represent a unified "versioned" snapshot of all project files rather than separate versions for each file (as with simple BPMN files). ::: ### Search You can search the process landscape for a specific BPMN file. 1. Press `Ctrl+F` or `⌘+F` combination to initialize search. 1. Enter the name or identifier of the BPMN file you want to search for and highlight. ![landscape search](img/process-landscape-search.png) ### Highlight paths Click on a node or connection to highlight the entire hierarchy of related connections. ![landscape selected node connections](img/process-landscape-connection.png) --- ## Run or publish your process Camunda 8 only When you design a process in Camunda Modeler, you have multiple flexible options to either run or publish it on Camunda 8. This page explains the differences between running and publishing a process, and outlines the various options to publish a process into any environment, and to any audience. ## Deploy a process Web Modeler autosaves all your changes on a diagram. If you change a diagram and it is autosaved, this has no effect on deployed or running processes your cluster(s). To make any change live in your cluster(s), you need to deploy it. If you deploy a process, it becomes available on the selected cluster and you can run or publish it. :::info Only organization owners or users with the **Admin** role in Camunda Hub can deploy from Web Modeler to `prod` clusters. Users without **Admin** roles can deploy only on `dev`, `test`, or `stage` clusters. ::: To deploy, click **Deploy** in the upper right corner of the modeling screen: ![The deploy dialog of a BPMN diagram](img/web-modeler-deploy.png) In Self-Managed, you can deploy your diagram to the cluster defined in your Web Modeler [configuration](/self-managed/components/hub/configuration/modeler-configuration.md#clusters). ### Before deploying a process - If the target cluster has [authorizations](/components/admin/authorization.md) enabled, make sure that the deploying users have `CREATE` permission to the `RESOURCE` resource type. - Make sure your process is free of errors, otherwise it can't be deployed. Use the [problems panel to detect and fix errors](modeling/fix-problems-in-your-diagram.md). - Make sure all dependent files are deployed first, such as DMN diagrams, forms, or called processes. You can use the [link tool](modeling/advanced-modeling/call-activity-linking.md) to drill-down into linked resources and deploy them. If you are using [`versionTag` binding](/components/best-practices/modeling/choosing-the-resource-binding-type.md) for a linked resource, make sure it is deployed with the correct version tag. :::tip Consider using a [project](/components/hub/workspace/manage-projects/manage-projects.md) that allows you to deploy a process and all dependent files together in a single bundle. ::: - Implement and run your [job workers](../../../concepts/job-workers.md) if you use tasks such as service or send tasks. - Ensure there are no missing secrets or misconfigured clients required for the process to run. :::info When missing secrets or no client credentials with access to the Orchestration Cluster API are detected, a warning is shown in the **Deploy diagram** dialog. Each warning offers a link to manage the missing secrets or misconfigured clients. ::: :::note To perform any of these actions, make sure to be in **Implement** mode. ::: ## Run a process Running a process means that you execute the process as a process instance on Camunda 8. It allows you to test and debug your process and observe how it performs in a live environment. - [Test run using Play mode](#test-run-using-play-mode) - [Run programmatically](#deploy-to-run-programmatically) - [Run manually from Modeler](#run-manually-from-modeler) - [Schedule via timer](#schedule-via-timer) ### Before running a process - If the target cluster has [authorizations](/components/admin/authorization.md) enabled, make sure that the users running the process are assigned to both the `CREATE_PROCESS_INSTANCE` permission to the `PROCESS_DEFINITION` resource type and `CREATE` permission to the `RESOURCE` resource type. ### Test run using Play mode Before you publish or run a process, you can test it manually using the Play mode. With the Play mode, you can build and test your process iteratively in small steps. To enter the Play mode, click the Play tab in the top left corner of the modeling screen. Refer to the [Play mode documentation](validation/play-your-process.md) for details of how the Play environment works. ### Run manually from Modeler You can also test your process thoroughly on a development cluster to observe how it behaves in Operate and Tasklist, in order to run your job workers, and to access your running process instances [programmatically](#deploy-to-run-programmatically). To start a process instance manually, take the following steps: 1. Click **Run** in the top right corner of the modeling screen. ![Running a process from Web Modeler](img/web-modeler-start-instance.png) 2. Select the target cluster. 3. To test your process with data, you can also specify variables written to the process context at startup. The variables must be formatted in valid JSON. As an example, you can use the following JSON: ```json { "hello": "world" } ``` 4. Click on **Run** to confirm. This will start a process instance on the selected cluster. If required, it (re-)deploys the process beforehand on the cluster. After the process instance has been started, you will receive a notification with a link to the process instance view in [Operate](../../../operate/operate-introduction.md). Follow this link to observe the progress of the process instance and interact with it if required. If the target cluster has [authorizations](/components/admin/authorization.md) enabled, make sure you have the following permissions to be able to view the process instance in Operate: - `READ_PROCESS_DEFINITION` and `READ_PROCESS_INSTANCE` permissions on the `PROCESS_DEFINITION` resource type - `operate` permission to the `COMPONENT` resource type :::info Starting an instance from Web Modeler [deploys](#deploy-a-process) recent changes to the target cluster, which changes future runs of this process definition in case it has already been deployed and used. Existing process instances are not affected. ::: :::tip By [linking a Camunda Form to a start event](/components/hub/workspace/modeler/modeling/advanced-modeling/form-linking.md), process instances can be started with the form's input [via a public form](#publish-via-a-public-form) (SaaS only) or directly [in Tasklist](#publish-to-tasklist). ::: ### Schedule via timer You can also schedule a process to run at a specific time or interval using timers. Timers can be added to one or multiple start events of your process. To schedule a process using a timer, follow these steps: 1. Select the start event. 2. Change the start event type to a timer event by clicking on the element and selecting the **Change element** menu icon. 3. [Configure the timer start event](../../../modeler/bpmn/timer-events/timer-events.md#timer-start-events) using the **properties panel** on the right side of the screen under the **Deploy** button to define when the process should be executed. You can set the timer to trigger at a specific date and time or to repeat at a certain interval. 4. Click on **Deploy** to [deploy](#deploy-a-process) the process. Once the process is deployed, the timer will be activated and the process will be executed at the scheduled time or interval. Read more in the [timers documentation](../../../modeler/bpmn/timer-events/timer-events.md). ### Best practices for running a process - Use the [Play mode](#test-run-using-play-mode) to run a process instance with test data before running it with live data. - Verify that the process is running as expected on a development cluster before running it with live data in your production environment. - Use [Operate](../../../operate/operate-introduction.md) to help you diagnose any problems with the process. :::tip You can also define the success of your processes by setting key performance indicators (KPIs) for your process using [Optimize](/components/optimize/what-is-optimize.md). ::: ## Publishing a process Publishing a process means that you make it available to other users inside and outside of Camunda 8. Once published, other users can access and start instances of the process. You have the following options to publish a process: - [Deploy a process](#deploy-a-process) - [Before deploying a process](#before-deploying-a-process) - [Run a process](#run-a-process) - [Before running a process](#before-running-a-process) - [Test run using Play mode](#test-run-using-play-mode) - [Run manually from Modeler](#run-manually-from-modeler) - [Schedule via timer](#schedule-via-timer) - [Best practices for running a process](#best-practices-for-running-a-process) - [Publishing a process](#publishing-a-process) - [Deploy to run programmatically](#deploy-to-run-programmatically) - [Publish via webhook](#publish-via-webhook) - [Publish to Tasklist](#publish-to-tasklist) - [Publish via a public form](#publish-via-a-public-form) - [Deploy process to the public](#deploy-process-to-the-public) - [Get the public link and share it](#get-the-public-link-and-share-it) - [Listen to message or signal events](#listen-to-message-or-signal-events) - [Best practices for publishing a process](#best-practices-for-publishing-a-process) - [Missing client credentials](#missing-client-credentials) - [Incorrect authorizations](#incorrect-authorizations) ### Deploy to run programmatically To call a process programmatically from or inside another application or service, [deploy](#deploy-a-process) it. Once deployed, you can run a process via our APIs, using an API client, or via one the various community SDKs. Read the [documentation on APIs & clients](../../../../apis-tools/working-with-apis-tools.md) to learn more. ### Publish via webhook You can publish a process via webhook, which allows you to integrate it easily with any system or service that can make an HTTP request. When a webhook is triggered in another system, it sends a HTTP request to a specified URL, which starts a process instance with the payload of the request. Follow these steps to publish a process via a webhook: 1. Select the start event. 2. Switch your start event to a [HTTP webhook connector](/components/connectors/protocol/http-webhook.md) by clicking on the element and selecting the **Change element** menu icon. 3. Define the webhook configuration in the properties panel of the start event. 4. Finally, [deploy the process](#deploy-a-process) to activate the webhook connector. When the process is deployed, the webhook URL can be found in the **Webhook** tab of the **properties panel**, and called from any outside system. You have multiple options to ensure that the webhook connection is safe for use by your target audience only. Please refer to the [full documentation](/components/connectors/protocol/http-webhook.md) for configuration details. ### Publish to Tasklist Publishing a process to Tasklist makes it available to users through the web-based [Tasklist application](../../../tasklist/introduction-to-tasklist.md). To publish a process to Tasklist, you first need to [deploy](#deploy-a-process) it. Once the process is deployed, it will automatically appear in the Tasklist application, where users can start new instances of the process. To learn more about publishing processes to Tasklist, refer to our [documentation on Tasklist](../../../tasklist/userguide/using-tasklist.md#processes). ### Publish via a public form Camunda 8 SaaS only Publishing a process via a public form allows you to share your process with external users who can start instances of the process without requiring access to Camunda 8. This feature is particularly useful when you want to gather data or initiate a process from users who are not part of your organization or do not have direct access to Camunda. It also allows you to rapidly test a process with your peers in a development environment. To publish a process via a public form, you first need to [link a Camunda Form](/components/hub/workspace/modeler/modeling/advanced-modeling/form-linking.md#using-the-link-button) to the process' start event, then you can follow these steps: #### Deploy process to the public 1. Open the **Publication** section in the **properties panel** (not the tab of the same name) and activate the toggle. 2. Click **Deploy** to [deploy](#deploy-a-process) the process and to activate the public form. Once the process is deployed, a public URL for the form is generated on the target cluster. #### Get the public link and share it You can access the URL in the **Publication** tab of the **properties panel**, and share it with any user via email, social media, or any other communication channel. Public form links were removed in Camunda 8.10. For current start options, use authenticated Tasklist starts or build a custom application with the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). ### Listen to message or signal events Camunda 8 supports message and signal events, which can be used to trigger a process instance when a specific event occurs. Everyone on the platform that knows the message or signal correlation keys can call such a process. To listen to a message or signal event, you need to define a [message](../../../modeler/bpmn/message-events/message-events.md#message-start-events) or [signal start event](../../../modeler/bpmn/signal-events/signal-events.md#signal-start-events) in your process model and configure it to listen for the desired event. Follow these steps to configure a message or signal start event: 1. Select the start event. 2. Change the start event type to a message or signal start event by clicking on the element and selecting the **Change element** menu icon. 3. Configure the message or signal start event using the **properties panel** to define the message or signal to listen to. Using messages, you can create a 1:1 relationship between calling processes. With signals, you can create broadcast-like message distributions. 4. Click on **Deploy** to [deploy](#deploy-a-process) the process. As soon as a matching event is received, a process instance will be started. To learn more about message and signal events, refer to our [documentation on events](../../../modeler/bpmn/events.md). ### Best practices for publishing a process - Use the [problems panel](modeling/fix-problems-in-your-diagram.md) to make sure that the process free of errors before publishing it. - Ensure the process works by testing it interactively using the [Play mode](validation/play-your-process.md). - Use meaningful names and descriptions for the process and its elements. - Document the process with clear instructions and details on how it should be used. - Make sure that the process is accessible to the appropriate users only. :::note When working on Camunda 8 Self-Managed, you can define access permissions on a per-process level using [Authorizations](/components/concepts/access-control/authorizations.md). ::: ### Missing client credentials When you deploy a process requiring client credentials, a warning appears in the **Deploy diagram** dialog. The warning offers a link to manage the missing or misconfigured credentials. Client credentials with access to the Orchestration Cluster API are required when at least one of the following elements is used in the process: `service tasks`, `messages`, `signals`, and elements with a `non-connector` task definition. ### Incorrect authorizations When using Modeler to deploy a process model or start a process instance, you may encounter issues related to [resource authorizations](/components/concepts/access-control/authorizations.md). Verify that the credentials you are using have the required authorizations to deploy a process model or start a process instance on the selected cluster and tenant. If you're using Web Modeler, note that deployments and process starts are performed **as your logged-in user**, so the necessary authorizations must be assigned to your user account. --- ## Play mode for rapid validation Camunda 8 only Play is a Zeebe-powered playground environment within Web Modeler for validating a process at any stage of development. Developers can debug their process logic, testers can manually test the process, and process owners can demo to stakeholders - all within Play. ## Opening Play To use Play, open a BPMN diagram and click the **Play** tab. Read the [limitations and availability section](#limitations-and-availability) if this section is missing. In Self-Managed, you are prompted to select from the clusters defined in your Web Modeler [configuration](/self-managed/components/hub/configuration/modeler-configuration.md#clusters). The Camunda 8 Helm and Docker Compose distributions provide one cluster configured by default. A Play environment is then started that utilizes your selected development cluster in SaaS, or the specified cluster in a Self-Managed setup. The current version of the active process and all its dependencies, like called processes or DMN files, are automatically deployed to the Play environment. An error or warning is raised if a file fails to deploy, is missing, or a connector secret isn’t filled out. In SaaS, Play uses connector secrets from your selected cluster. connector secrets are not currently supported in Self-Managed. ## Authorizations If [authorizations](/components/admin/authorization.md) are enabled on the cluster where you will run Play, the following permissions are required for each action: | Resource Type | Permission | Allowed action | | ------------------- | ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- | | Resource | CREATE | Deploy a process | | Process definition | CREATE_PROCESS_INSTANCE | Start a process instance | | Process definition | READ_PROCESS_INSTANCE | View process instance(s) | | Process definition | READ_USER_TASK | Get information about a user task | | Process definition | UPDATE_USER_TASK | Complete a user task | | Process definition | UPDATE_PROCESS_INSTANCE | Complete a service task, Throw error from a service task, Apply modifications, Set variables, Resolve incidents | | Decision definition | READ_DECISION_DEFINITION, READ_DECISION_INSTANCE | View decision instance in Operate (SaaS only) | | Message | CREATE | Publish a message | ### Limitations {#authorizations-limitations} - Fine-grained authorizations are not supported. If the **Resource ID** is not \* when defining authorizations, the user will not have access to any resources. ## Get started with Play ![play process definition view](../img/play-definition.png) The first view in Play is the process definition view. It shows deployment problems, active process instances, and start events. Click a **start event's** play button to begin your process. Open the button's menu to start a process with variables. These variables can also be prefilled from the example data defined for the start event in **Implement** mode. Alternatively, save example data to the BPMN file directly from the modal in Play to reuse it in future sessions or share it with others. Play presents this example data in a readable JSON format, as illustrated below. See [data handling](/components/modeler/data-handling.md) for additional details. :::note Play will only consider the first executable process ID in the BPMN file. ::: ![play example data](../img/play-example-data.png) ## Play a process ![play process instance view](../img/play-instance.png) Click the action icons next to a task or event to play the process. The **Instance History** panel tracks the path taken throughout the diagram. The **Variables** panel tracks the data collected. Global variables are shown by default. To view local variables, select the corresponding task or event. Variables can be edited or added here, and Play supports JSON format to represent complex data. Play executes all logic of the process and its linked files, such as FEEL, forms, DMN tables, and outbound connectors. Actions in Play can be initiated through Operate, Tasklist, or external APIs. For example, you can complete a user task via Tasklist, finish a service task using an external job worker, or cancel/modify your instance through Operate, with all changes reflected in Play. In SaaS, view your process instance in Operate by selecting the **Process Instance Key** in the header. ![play process instance view](../img/play-view-process-instance.png) You have a few options to mock an external system: - In **Implement** mode, hard-code an example payload in the task or event's **Example data** section in the properties panel on the right side of the screen. - When completing a task or event, use the secondary action to complete it with variables. - When filling forms or setting variables from Play, you can also save the variables to the BPMN file as example data to reuse them in future sessions. - Use service task placeholders instead of connectors Play automatically uses example data from the BPMN file for many events and task types. If you want to use different data, you can override the example data by opening the secondary action menu on an element. The new data set will take precedent over the example data from the BPMN file for future Play sessions. Incidents are raised in Play just like in Operate. Use the variables and incident messages to debug the process instance. ## Replay a process To replay a process, rewind to an earlier element by clicking on the **Rewind** button on a previously completed element. :::note You can also return to the definition view by clicking **View all** on the top banner, or start a new process instance by clicking on the **Restart process** button on the start event. ::: ### Rewind a process After completing part of your process, you can **rewind** to a previous element to test a different scenario. Play will start a new instance and replay your actions up to, but not including, the selected previous task. ![rewind process](../img/play-rewind.png) Play's rewind operation currently does not support the following elements: - Call activities - Timer events #### Additional limitations - If you completed an unsupported element before rewinding, you will rewind farther than expected. - Play rewinds to an element, not to an element instance. For example, if you wanted to rewind your process to a sequential multi-instance service task which ran five times, it will rewind your process to the first instance of that service task. - Play rewinds processes by initiating a new instance and executing each element. However, if any element behaves differently from the previous execution, such as a connector returning a different result, the rewind may fail. ## Scenarios {#scenarios} Use scenarios to quickly rerun processes while tracking test coverage. For example, you can validate your process by creating and rerunning scenarios for different paths to check the process works as expected after any diagram changes are made. Scenarios allow you to replay and confirm that a process completes correctly with the predefined actions and variables. :::note Although scenarios are quick to develop and use for non-developers, Camunda [best practices](/components/best-practices/development/testing-process-definitions.md) recommend using specialized test libraries in your CI/CD pipeline. ::: Scenarios are stored in [test scenario files](test-scenario-files.md). You can view and edit these files directly in Web Modeler or in your Git repository using Git sync. Play will use the test scenario file [linked to the first executable process ID](test-scenario-files.md#link-a-process-processid) of the BPMN diagram. If multiple test scenario files are linked to the same process ID, Play will use: - The test scenario file with the earliest name alphabetically - If multiple test scenario files have the same name, the one that was most recently updated ### Save scenario To save a scenario: 1. Execute a path in your process. 1. Click **Save scenario** in the process instance header. 1. A new [test scenario file](test-scenario-files.md) will be saved in the same Web Modeler folder as the process. ![Save a scenario](../img/play-save-scenario.png) :::tip To view your saved scenarios in Play, click **View all** beneath the Scenarios column in the process instance header. ::: ### Scenario coverage Scenario coverage is calculated as the percentage of flow nodes in your process that are covered, including all elements, events, and gateways. For example, the coverage is 80% if eight out of ten flow nodes are covered. - On the process definition page, covered paths are highlighted in blue. Click on individual scenarios to view their specific coverage. - Once a process instance is completed, the process instance header shows how much your process scenario coverage would increase if the path was saved as a scenario. ![Scenario coverage](../img/play-coverage.png) :::warning Scenario coverage will not display as expected if you edit or remove the "metadata" field in the [test scenario file](test-scenario-files.md). ::: ### Run scenario You can run scenarios on the process definition page by clicking either the **Run all scenarios** button or the **Run scenario** button with the play icon for each individual scenario. - Scenario execution results are marked with either a **Completed** or **Failed** status. - You must manually update a failed scenario by clicking **manually complete and update the scenario**, especially if diagram changes are made that require further user input (such as when a new flow node is added to a previously saved scenario path). ![Run a scenario on the process definition page](../img/play-scenario-runs.png) ### Limitations {#scenarios-limitations} Play displays a warning badge on diagram elements with known limitations. Use the **Show problems**/**Hide problems** toggle in the diagram controls to show or hide these badges. ![Play warning badges on diagram elements](../img/play-warning-badges.png) - Call activities are not supported. Scenarios containing call activities cannot be executed successfully. - Ad-hoc sub-processes are not supported. Scenarios containing ad-hoc sub-processes cannot be executed successfully. - Timer events can't be manually triggered. When a scenario reaches a timer event, execution pauses until the timer fires automatically. To skip a timer, use [process instance modification](#modify-a-process-instance) to move the token to the next element. - Scenario paths that include process modifications are not supported. - Similarly to process instances, scenarios do not run in isolation. For example, if two scenario paths are defined for a process and both contain the same message event or signal event, running these scenarios simultaneously might lead to unintended consequences. Publishing a scenario or broadcasting a signal could inadvertently impact the other scenario, resulting in the failure of both. - Play scenarios are compatible with the [CPT JSON instruction format](/apis-tools/testing/json-test-cases.md), but the following [instructions](/apis-tools/testing/json-test-cases.md#reference-instructions) are not supported and will be skipped during execution: - `ASSERT_DECISION` - `ASSERT_ELEMENT_INSTANCE` - `ASSERT_ELEMENT_INSTANCES` - `ASSERT_PROCESS_INSTANCE` - `ASSERT_PROCESS_INSTANCE_MESSAGE_SUBSCRIPTION` - `ASSERT_USER_TASK` - `ASSERT_VARIABLES` - `COMPLETE_JOB_AD_HOC_SUB_PROCESS` - `COMPLETE_JOB_USER_TASK_LISTENER` - `CORRELATE_MESSAGE` - `EVALUATE_CONDITIONAL_START_EVENT` - `EVALUATE_DECISION` - `INCREASE_TIME` - `MOCK_CHILD_PROCESS` - `MOCK_DMN_DECISION` - `MOCK_JOB_WORKER_COMPLETE_JOB` - `MOCK_JOB_WORKER_THROW_BPMN_ERROR` - `SET_TIME` ## Modify a process instance There are two main reasons to modify a process instance in Play: 1. **Skip elements**: If your process is stuck, you can continue testing by skipping over elements. For instance, rather than waiting for a 24-hour timer event to elapse or resolving an incident, you can manually advance the active token from the timer event to the next flow node. 2. **Faster prototyping**: Rather than completing the entire process, you can skip over unnecessary sections of a large diagram to debug the changes you made. There are three ways to modify your process instance: - **Add token**: Select the flow node where you'd like to initiate a new token and select **Add** from the modification dropdown. - **Cancel tokens**: Select the flow node where you'd like to cancel active tokens and select **Cancel** from the modification dropdown. - **Move tokens**: Select the flow node from which you'd like to move active tokens and select **Move** from the modification dropdown. Then, select a target flow node to relocate the tokens. :::note Unlike in [Operate](/components/operate/userguide/process-instance-modification.md), these changes are applied immediately. If you need to change variables while modifying a process, use the **Variables** panel to set them separately. Alternatively, for advanced use cases you can modify the process instance from Operate. ::: ![modify process instance](../img/play-modifications.png) ### Limitations {#modifications-limitations} Rewinding a process instance that has modifications applied to is currently not supported. Additionally, some elements do not support specific modifications: - **Add token**/**Move tokens to** modifications are not possible for the following element types: - Start events - Boundary events - Events attached to event-based gateways - **Move tokens from** modification is not possible for a subprocess itself. - **Add token** modifications are not currently supported for elements with multiple running scopes. However, **Move tokens** modifications are supported for elements inside multi-instance subprocesses. The move operation terminates the specific element instance and activates the target element in the same instance of the multi-instance subprocess. ## Rapid iteration To make changes, switch back to **Implement** mode. When returning to Play, your process is redeployed. Play only shows process instances from the process’s most recent version, so you may not see your previous instances. Play saves your inputs when completing user task forms. It auto-fills your last response if you open the same form later in the session. You can click **Reset** to reset the form to its defaults. ## Details Depending on the BPMN element, there may be a different action: - **User tasks** with an embedded form are displayed on click. However, you cannot track assignment logic. - **Call activities** can be navigated into and performed. - **Manual tasks**, **undefined tasks**, **script tasks**, **business rule tasks**, **gateways**, **outbound connectors** and other BPMN elements that control the process’s path are automatically completed based on their configuration. - **Service tasks**, **inbound connectors**, message-related tasks, or events are simulated on click or triggered from an external client. However, Play attempts message correlation based on the process context but cannot infer keys from FEEL expressions. Therefore, these keys must be manually entered by publishing a message using secondary actions. - Many action icons have secondary actions. For example, **user tasks** can be completed with variables rather than a form, and **service tasks** can trigger an error event. ## Operate vs. Play [Operate](/components/operate/operate-introduction.md) is designed to monitor many production process instances and intervene only as necessary, while Play is designed to drive a single process instance through the process and mock external systems. Both offer monitoring of a single process instance, its variables and path, incidents, and actions to modify or repair a process instance. Operate offers bulk actions and guardrails against breaking production processes, while Play offers a streamlined UX to run through scenarios quickly. ## Limitations and availability This section explains why you might not see the **Play** tab, and any additional limitations. For more information about terms, refer to our [licensing and terms page](https://legal.camunda.com/licensing-and-other-legal-terms#c8-saas-trial-edition-and-free-tier-edition-terms). **Version compatibility:** Although Play is compatible with cluster versions 8.5.1 and above, Camunda fully supports and recommends using versions 8.6.0 or higher. ### Camunda 8 SaaS In Camunda 8 SaaS, Play is available to all Web Modeler users with commenter, editor, or admin permissions within a project. Additionally, within their organization, users need to have a [role](/components/hub/organization/manage-members/manage-users.md#roles-and-permissions) which has deployment privileges. [If authorizations are enabled on the cluster, users need to have specific permissions instead.](#authorizations) ### Camunda 8 Self-Managed In Self-Managed, Play is controlled by the `PLAY_ENABLED` [configuration property](/self-managed/components/hub/configuration/modeler-configuration.md#feature-flags) in Web Modeler. This is `true` by default for the Docker and Kubernetes distributions. Prior to the 8.6 release, Play can be accessed by installing the 8.6.0-alpha [Helm charts](https://github.com/camunda/camunda-platform-helm/blob/camunda-platform-10.4.0/charts/camunda-platform-alpha), or running the 8.6.0-alpha [Docker Compose](https://github.com/camunda/camunda-distributions/tree/main/docker-compose) configuration. ### Features - [Decision table rule](/components/modeler/dmn/decision-table-rule.md) evaluations are not viewable from Play. However, they can be inferred from the output variable, or can be viewed from Operate. - Currently, Play supports displaying up to 100 flow node instances in the instance history panel, 100 variables in the variables panel, and 100 process instances on the process definition page. To access all related data, you can use Operate. - While you can still interact with your process instance in Play (for example, completing jobs or publishing messages), you may be unable to resolve incidents if they occur beyond the 100th flow node instance, as Play does not track them. In this case, incident resolution can be managed in Operate. - Play doesn't support elements defined using [FEEL expressions](/components/modeler/feel/what-is-feel.md), such as job types for service tasks, message correlation keys, and called elements in call activities. - User tasks with a job worker implementation are deprecated and no longer supported in Play from cluster versions 8.8 and above. Please consider migrating to [Camunda user tasks](/components/modeler/bpmn/user-tasks/user-tasks.md#camunda-user-tasks). ## Use Play with Camunda Self-Managed After selecting the **Play** tab in Self-Managed, you are prompted to select from the clusters defined in your Web Modeler [configuration](/self-managed/components/hub/configuration/modeler-configuration.md#clusters). The Camunda 8 Helm and Docker Compose distributions provide one cluster configured by default. ### Limitations {#self-managed-limitations} - The environment variables `CAMUNDA_CUSTOM_CERT_CHAIN_PATH`, `CAMUNDA_CUSTOM_PRIVATE_KEY_PATH`, `CAMUNDA_CUSTOM_ROOT_CERT_PATH`, and `CAMUNDA_CUSTOM_ROOT_CERT_STRING` can be set in Docker or Helm chart setups. However, these configurations have not been tested with Play's behavior, and therefore are not supported when used with Play. - Play cannot check the presence of connector secrets in Self-Managed setups. If a secret is missing, Play will show an incident at runtime. Learn more about [configuring connector secrets](/self-managed/components/connectors/connectors-configuration.md#secrets). ## Play Usage and Billing Considerations The use of Play may result in additional charges depending on your organization's plan and the type of cluster you are using. To avoid extra costs, follow these guidelines based on your plan: - **Enterprise Plans:** Use a [development cluster](/components/concepts/clusters.md#development-clusters-in-the-enterprise-plan) to avoid costs. Alternatively, ensure your organization is designated as a development organization. For further assistance, [contact Camunda support](https://camunda.com/services/support/). - **Professional Plans:** Use a [development cluster](/components/concepts/clusters.md#development-clusters-in-the-starter-plan) to avoid costs. For Professional Plans, you may need to purchase a development cluster. - **Trial Plans:** You can use any cluster. --- ## Task testing You can test a single task directly within Web Modeler to validate its configuration and logic without executing the entire process. Task testing lets you quickly debug mappings, inputs, and outputs without leaving your implementation context. ## Task testing vs. Play While both task testing and Play let you validate your BPMN models, they serve different purposes: | Feature / capability | Task testing (Implement mode) | Play (Play mode) | | -------------------- | --------------------------------- | --------------------------------- | | Test scope | Single task or sub-process | Process segment or full diagram | | Best for | Quick implementation checks | End-to-end test validation | | Data type | Live data only | Live or mocked data | | Saves test scenarios | No | Yes | | Mode required | Runs directly in _Implement_ mode | Requires switching to _Play_ mode | Use task testing during implementation for quick feedback, and use Play for structured testing with mock data or reusable scenarios. ## Prerequisites Before running task testing, ensure you have: - A connection to an active Camunda 8.8 or later orchestration cluster - Permissions to deploy and run processes in the target environment ## Run a task test To test a task in Web Modeler: 1. Select the BPMN task you want to test. 2. Open the **Task testing** tab in the bottom panel. 3. In the left pane, define the process variables in JSON format to simulate the process context. ### Define process variables - Use the **Variables** tab to review available variables in your process. - Confirm that input mappings for your task are configured correctly. - Match variable names and types to those expected by the task. - Provide realistic sample data to reflect actual execution conditions. 4. Click **Test task** to execute the task. Web Modeler automatically deploys the process before running the test. The task executes on the connected cluster using your defined input data. During execution, the log displays each step in real time, including any states where the test is waiting for an external action to complete. ![Task test showing waiting state](./img/task-testing/task-testing-waiting-state.png) ## View results After the test completes, results appear in the output panel: ### Successful execution If the task runs successfully, the output panel displays the resulting process variables and any updates. ![Successful task test showing resulting process variables](./img/task-testing/task-testing-success.png) ### Incident during execution If the task encounters an incident, details are shown along with the relevant variable context to help diagnose the issue. ![Incident details shown for a failed task execution, including incident message and variable context](./img/task-testing/task-testing-incident.png) ### Execution error If the task fails due to an error (for example, a network or configuration issue), the error message and response details are displayed. ![Execution error message shown after a task test fails due to a configuration or network issue](./img/task-testing/task-testing-error.png) ## Related documentation - [Test a task in Desktop Modeler](../../../../modeler/desktop-modeler/task-testing.md) - [Learn about task testing concepts](../../../../modeler/task-testing.md) - [Working with variables](../../../../concepts/variables.md) - [Using Play mode](play-your-process.md) --- ## Test scenario files Camunda 8 only Test scenario files let you define shareable, low-code tests for your BPMN processes. They are stored in JSON format and follow the [Camunda Process Test (CPT) JSON schema](https://camunda.com/json-schema/cpt-test-cases/8.9/schema.json), so you can use the same files in Play and in an automated CPT test suite. You can create, edit, and manage them directly in Web Modeler. You can also download these files or synchronize them with your Git repository using Git Sync. ## Create a test scenario file You can create a new test scenario file by [saving a scenario in Play](play-your-process.md#save-scenario). You can also manage scenarios and update failing scenarios from Play. ## Manual editing ### Test case structure Test scenario files follow the [CPT JSON test cases schema](/apis-tools/testing/json-test-cases.md). Play adds two optional fields to that schema, `processId` and `metadata`, to link the file to a BPMN process and track scenario coverage. ```json { "$schema": "https://camunda.com/json-schema/cpt-test-cases/8.9/schema.json", "processId": "order-fulfillment-process", "testCases": [ { "name": "Happy path order processing", "description": "Customer places an order that is processed successfully.", "instructions": [ // Array of instruction objects ], "metadata": { // Optional - for use in Play only "processInstanceId": 12345, "coveredFlowNodes": [ { "flowNodeId": "startEvent", "elementType": "START_EVENT" }, { "flowNodeId": "processOrder", "elementType": "SERVICE_TASK" } ], "coveredSequenceFlows": ["flow1", "flow2"] } }, { "name": "Error handling scenario", "instructions": [ // Array of instruction objects for error case ] } ] } ``` **Top-level fields** | Field | Required | Description | | ----------- | -------- | -------------------------------------------------------------------------------------------------------- | | `processId` | Yes | Play-specific field. The ID of the BPMN process definition the test cases run against. Required by Play. | | `testCases` | Yes | An array of test case objects. | **Test case fields** | Field | Required | Description | | -------------- | -------- | ---------------------------------------------------------------------------------------------------------- | | `name` | Yes | A descriptive name for the test case scenario. | | `description` | No | A human-readable description of the test case. | | `instructions` | Yes | An array of instruction objects that define the test steps. | | `metadata` | No | Used by Play to show coverage and process instance details. Camunda does not recommend editing this field. | ### Link a process (`processId`) To display the file's scenarios in Play, you must first link the file to a process. Add a `processId` field with the process ID of the BPMN process you want to test: ```json { "processId": "Process_1" } ``` You can find the BPMN process ID in the properties panel, or in the first ` ... more contained elements ... =myCondition ``` --- ## Iso 8601 Date Time A specific point in time defined as ISO 8601 combined date and time representation. It must contain timezone information, either `Z` for UTC or a zone offset. Optionally, it can contain a zone id. - `2019-10-01T12:00:00Z` - UTC time - `2019-10-02T08:09:40+02:00` - UTC plus two hours zone offset - `2019-10-02T08:09:40+02:00[Europe/Berlin]` - UTC plus two hours zone offset at Berlin --- ## Design a process using BPMN Beginner Time estimate: 20 minutes Business Process Model and Notation (BPMN) is the global standard for process modeling. Combining BPMN, an easy-to-adopt visual modeling language, with Camunda, you can automate your business processes. Processes are the algorithms that determine how an organization runs based on independent tasks. Successful businesses grow from proven, effective processes. Therefore, Camunda’s workflow engine executes processes defined in BPMN to ensure these processes can be swiftly orchestrated within a diagram. Take the following example where we've outlined a process in a BPMN diagram to send an email. Don't worry too much about the symbols as we'll get to that shortly. For now, recognize the start and end of the process, comprised of entering a message, and sending the email. ![sending email bmmn diagram](./img/simple-bpmn-process.png) BPMN offers control and visibility over your critical business processes in a way that is understandable for both experienced engineers and business stakeholders. The workflow engine orchestrates processes that span across a wide variety of elements, including APIs, microservices, business decisions and rules, human work, IoT devices, RPA bots, and more. ## Set up Begin by building your BPMN diagrams with [Modeler](/components/modeler/about-modeler.md). To get started, ensure you’ve [created a Camunda 8 account](/components/hub/organization/manage-organization-settings/manage-plan/create-account.md). ## Getting started with BPMN Once logged in to your Camunda 8 account, take the following steps: 1. Within Modeler, click **Create new > BPMN diagram**. 2. Right after creating your diagram, you can name it by replacing the **New BPMN Diagram** text with the name of your choice. In this case, we'll name it "Bake a Cake." ### BPMN elements Before building out the diagram to bake a cake, let's examine the significance of the components on the left side of the screen. You can build out a BPMN diagram for a process using several elements, including the following: - Events: The things that happen. For example, start and end events which begin and terminate the process. - Tasks: For example, user tasks for a particular user to complete, or service tasks to invoke various web services. - Gateways: For example, parallel gateways that move the process along between two tasks at the same time. - Utilize [variables](/components/concepts/variables.md) to reflect the data of process instances. - Leverage [expressions](/components/concepts/expressions.md) to access variables and calculate their value(s). - Subprocesses: For example, a transaction subprocess which can be used to group multiple activities to a transaction. For a complete list of BPMN elements and their capabilities, visit the [BPMN reference material](/components/modeler/bpmn/bpmn.md). ### BPMN in action Using these elements, let's build out a BPMN diagram to examine the process of baking a cake. Take the following steps: 1. On our diagram, we've already been given an element as a start event in the shape of a circle. Click on the element and select the **Change element** menu icon. For now, keep it as a start event. Double click on the circle to add text. 2. Drag and drop an arrow to the first task (the rectangle shape), or click the start event, and then click the task element to automatically attach it. 3. Click on the element and select the **Change element** menu icon to change the type to a user task, which will be named "Purchase Ingredients." Note that each element added has adjustable attributes. Use the properties panel on the right side of the page to adjust these attributes. 4. Click on the user task to connect a gateway to it. By clicking on the element and selecting the **Change element** menu icon and declaring it a parallel gateway, you can connect it to two tasks that can happen at the same time: mixing the ingredients, and preheating the oven. ![baking a cake bpmn sample](./img/bake-cake-bpmn.png) 5. Attach the next gateway once these two tasks have completed to move forward. 6. Add a user task to bake the cake, and finally a user task to ice the cake. 7. Add an end event, represented by a bold circle. 8. No need to save. Web Modeler will autosave every change you make. ![completed bpmn diagram](./img/complete-baking-cake-bpmn.png) :::note You can also import a BPMN diagram with Web Modeler. See how to do that [here](/components/hub/workspace/modeler/modeling/import-diagram.md). ::: ## Execute your process diagram :::note If you change a diagram and it is auto-saved, this has no effect on your cluster(s). When you deploy the diagram, it becomes available on the selected cluster and new instances can start. ::: To execute your completed process diagram, click the blue **Deploy** button. You can now start a new process instance to initiate your process diagram. Click the blue **Run** button. You can now monitor your instances in [Operate](/components/operate/operate-introduction.md). Click the square-shaped **Camunda components** button to move between apps, and view process instances once in Operate. You can also visit an ongoing list of user tasks required in your BPMN diagram. Navigate to [Tasklist](/components/tasklist/introduction-to-tasklist.md) for a closer look. :::note Variables are part of a process instance and represent the data of the instance. To learn more about these values, variable scope, and input/output mappings, visit our documentation on [variables](/components/concepts/variables.md). ::: ## Additional resources and next steps - [Camunda BPMN tutorial](https://camunda.com/bpmn/) - [BPMN implementation reference](https://docs.camunda.org/manual/latest/reference/bpmn20/) - [Zeebe engine](/components/zeebe/zeebe-overview.md) - [BPMN reference](/components/modeler/bpmn/bpmn.md) - [Camunda Academy: BPMN Overview](https://academy.camunda.com/bpmn-overview) - [Operate](/components/operate/operate-introduction.md) - [Tasklist](/components/tasklist/introduction-to-tasklist.md) --- ## BPMN coverage export const Highlight = ({children, color}) => ( {children} ); The following BPMN elements are supported by our modeling tools. Elements highlighted in green are supported for execution by Camunda 8. Click on an element to navigate to the documentation. ## Participants ## Subprocesses ## Tasks ## Gateways ## Markers ## Data :::note `DataObject` and `DataStore`, like other BPMN standard IO mappings, are supported by Camunda for modeling purposes only. ::: ## Artifacts ## Events Type Start Intermediate End Normal Event Subprocess Event Subprocess non-interrupting Catch Boundary Boundary non-interrupting Throw None Message Timer Error Signal Conditional Escalation Compensation Cancel Terminate Link Multiple Multiple Parallel --- ## BPMN primer Business Process Model and Notation 2.0 (BPMN) is an industry standard for process modeling and execution. A BPMN process is an XML document that has a visual representation. For example, here is a BPMN process: ![process](assets/process.png)
The corresponding XML ```xml SequenceFlow_1bq1azi SequenceFlow_0ojoaqz SequenceFlow_1bq1azi SequenceFlow_09hqjpg SequenceFlow_09hqjpg SequenceFlow_1ea1mpb SequenceFlow_1ea1mpb SequenceFlow_0ojoaqz ```
This duality makes BPMN very powerful. The XML document contains all the necessary information to be interpreted by workflow engines and modeling tools like Zeebe. At the same time, the visual representation contains just enough information to be quickly understood by humans, even when they are non-technical people. The BPMN model is source code and documentation in one artifact. The following is an introduction to BPMN 2.0, its elements, and their execution semantics. It tries to briefly provide an intuitive understanding of BPMN's power, but does not cover the entire feature set. For more exhaustive BPMN resources, refer to the [reference links](#additional-resources) at the end of this section. ## Modeling BPMN diagrams The best tool for modeling BPMN diagrams for Zeebe is [Modeler](../about-modeler.md). Learn more by [modeling your first diagram](/components/hub/workspace/modeler/modeling/model-your-first-diagram.md). - [Download page](https://camunda.com/download/modeler/) - [Source code repository](https://github.com/camunda/camunda-modeler) ## BPMN elements ### Sequence flow: Controlling the flow of execution A core concept of BPMN is a **sequence flow** that defines the order in which steps in the process happen. In BPMN's visual representation, a sequence flow is an arrow connecting two elements. The direction of the arrow indicates their order of execution. ![sequence flow](./assets/sequenceflow.png) You can think of process execution as tokens running through the process model. When a process is started, a token is created at the beginning of the model and advances with every completed step. When the token reaches the end of the process, it is consumed and the process instance ends. Zeebe's task is to drive the token and to make sure the job workers are invoked whenever necessary.
### Tasks: Units of work The basic elements of BPMN processes are tasks; these are atomic units of work composed to create a meaningful result. Whenever a token reaches a task, the token stops and Zeebe creates a job and notifies a registered worker to perform work. When that handler signals completion, the token continues on the outgoing sequence flow.
Choosing the granularity of a task is up to the person modeling the process. For example, the activity of processing an order can be modeled as a single _Process Order_ task, or as three individual tasks _Collect Money_, _Fetch Items_, _Ship Parcel_. If you use Zeebe to orchestrate microservices, one task can represent one microservice invocation. Refer to the [tasks](tasks.md) section on which types of tasks are currently supported and how to use them. ### Gateways: Steering flow Gateways are elements that route tokens in more complex patterns than plain sequence flow. BPMN's **exclusive gateway** chooses one sequence flow out of many based on data:
BPMN's **parallel gateway** generates new tokens by activating multiple sequence flows in parallel:
Refer to the [gateways](gateways.md) section on which types of gateways are currently supported and how to use them. ### Events: Waiting for something to happen **Events** in BPMN represent things that _happen_. A process can react to events (_catching_ event) as well as emit events (_throwing_ event). For example:
The circle with the envelope symbol is a catching message event. It makes the token continue as soon as a message is received. The XML representation of the process contains the criteria for which kind of message triggers continuation. Events can be added to the process in various ways. Not only can they be used to make a token wait at a certain point, but also for interrupting a token's progress. Refer to the [events](events.md) section on which types of events are currently supported and how to use them. ### Subprocesses: Grouping elements [**Subprocesses**](/components/modeler/bpmn/subprocesses.md) are element containers that allow defining common functionality. For example, you can attach an event to a subprocess's border. When the event is triggered, the subprocess is interrupted, regardless which of its elements is currently active. Refer to the [subprocesses](subprocesses.md) section on which types of subprocesses are currently supported and how to use them. ## Additional resources - [BPMN specification](http://www.bpmn.org/) - [BPMN tutorial](https://camunda.com/bpmn/) - [Full BPMN reference](https://camunda.com/bpmn/reference/) - [BPMN book](https://www.amazon.com/dp/1086302095/) --- ## BPMN in Modeler **Business Process Model and Notation (BPMN)** was developed as a graphical notation to represent complex processes. It is maintained by the non-profit [The Object Management Group (OMG)](https://www.omg.org/spec/BPMN/) and employed by numerous organizations globally. The visual nature of BPMN enables greater collaboration between different teams, particularly within Modeler. :::note BPMN diagrams must be created for the process engine they intend to be deployed on. You cannot run a BPMN diagram modeled for Camunda 7 in Camunda 8, or vice versa, at this time. ::: ## Start modeling ![Start Modeling](./assets/quickstart-2.png) Web and Desktop Modeler both offer a similar core BPMN 2.0 Modeling experience: - Add BPMN elements from the palette on the left side of the page by dragging and dropping them onto the diagram canvas. - Change the type of element in place by clicking on an element to reveal the context menu. Then, select the **Change element** menu icon to change the type of element to a [service task](./service-tasks/service-tasks.md) or [user task](./user-tasks/user-tasks.md), for example. ## Creating BPMN elements Using BPMN in Modeler, you can create more BPMN 2.0 elements like task types and event definitions. For example, [service tasks](/components/modeler/bpmn/service-tasks/service-tasks.md) within Camunda 8 require you to set a task type and implement [job workers](/components/concepts/job-workers.md) to perform a particular task in a process. Review [the BPMN 2.0 coverage documentation](/components/modeler/bpmn/bpmn-coverage.md) for a complete list of BPMN elements supported by our modeling tools. Additionally, visit [creating readable process models](/components/best-practices/modeling/creating-readable-process-models.md) for general guidelines on modeling with BPMN. ## BPMN 2.0 properties for execution ![Save BPMN Diagram](./assets/quickstart-3.png) In the properties panel on the right side, view and edit attributes that apply to the selected element. --- ## Business rule tasks A business rule task is used to model the evaluation of a business rule; for example, a decision modeled in [Decision Model and Notation](https://www.omg.org/dmn/) (DMN). ![task](assets/business-rule-task.png) :::info Camunda 8 supports alternative task implementations for the business rule task. If you want to use your own implementation for a business rule task, refer to the [job worker implementation](#job-worker-implementation) section below. The sections before this job worker implementation apply to the DMN decision implementation only. ::: :::info If you only want to evaluate a DMN decision, you can use the [`EvaluateDecision`](/apis-tools/zeebe-api/gateway-service.md#evaluatedecision-rpc) API. ::: When the process instance arrives at a business rule task, a decision is evaluated using the internal DMN decision engine. Once the decision is made, the process instance continues. If the decision evaluation is unsuccessful, an [incident](/components/concepts/incidents.md) is raised at the business rule task. When the incident is resolved, the decision is evaluated again. ## Defining a called decision {#defining-a-task} A called decision links the business rule task to a DMN decision, either to a [decision table](/components/modeler/dmn/decision-table.md) or to a [decision literal expression](/components/modeler/dmn/decision-literal-expression.md) . It can be defined using the `zeebe:calledDecision` extension element. A business rule task must define the [DMN decision id](/components/modeler/dmn/decision-table.md#decision-id) of the called decision as `decisionId`. Usually, the `decisionId` is defined as a [static value](/components/concepts/expressions.md#expressions-vs-static-values) (e.g. `shipping_box_size`), but it can also be defined as an [expression](/components/concepts/expressions.md) ( e.g. `= "shipping_box_size_" + countryCode`). The expression is evaluated on activating the business rule task (or when an incident at the business rule task is resolved) after input mappings have been applied. The expression must result in a `string`. The `bindingType` attribute determines which version of the called decision is evaluated: - `latest`: The latest deployed version at the moment the business rule task is activated. - `deployment`: The version that was deployed together with the currently running version of the process. - `versionTag`: The latest deployed version that is annotated with the version tag specified in the `versionTag` attribute. To learn more about choosing binding types, see [choosing the resource binding type](/components/best-practices/modeling/choosing-the-resource-binding-type.md). :::note If the `bindingType` attribute is not specified, `latest` is used as the default. ::: A business rule task must define the process variable name of the decision result as `resultVariable`. The result of the decision is stored in this variable. The `resultVariable` is defined as a static value. ## Variable mappings By default, the variable defined by `resultVariable` is merged into the process instance. This behavior can be customized by defining an output mapping at the business rule task. All variables in scope of the business rule task are available to the decision engine when the decision is evaluated. Input mappings can be used to transform the variables into a format accepted by the decision. :::info Input mappings are applied on activating the business rule task (or when an incident at the business rule task is resolved), before the decision evaluation. When an incident is resolved at the business rule task, the input mappings are applied again before evaluating the decision. This can affect the result of the decision. ::: For more information about this topic, visit the documentation about [input/output variable mappings](/components/concepts/variables.md#inputoutput-variable-mappings). ## Job worker implementation A business rule task does not have to evaluate a decision modeled with DMN. Instead, you can also use [job workers](/components/concepts/job-workers.md) to implement your business rule task. A job worker implementation can be defined using the `zeebe:taskDefinition` extension element. Business rule tasks with a job worker implementation behave exactly like [service tasks](/components/modeler/bpmn/service-tasks/service-tasks.md). The differences between these task types are the visual representation (i.e. the task marker) and the semantics for the model. When a process instance enters a business rule task with alternative task implementation, it creates a corresponding job and waits for its completion. A job worker should request jobs of this job type and process them. When the job is completed, the process instance continues. A business rule task must define a [job type](/components/modeler/bpmn/service-tasks/service-tasks.md#task-definition) the same way as a service task does. This is used as reference to specify which job workers request the respective business rule task job. For example, `order-items`. Note that `type` can be specified as any static value (`myType`) or as a FEEL [expression](../../../concepts/expressions.md) prefixed by `=` that evaluates to any FEEL string; for example, `= "order-" + priorityGroup`. Use [task headers](/components/modeler/bpmn/service-tasks/service-tasks.md#task-headers) to pass static parameters to the job worker (e.g. the key of the decision to evaluate). Define [variable mappings](/components/concepts/variables.md#inputoutput-variable-mappings) the [same way as a service task does](/components/modeler/bpmn/service-tasks/service-tasks.md#variable-mappings) to transform the variables passed to the job worker, or to customize how the variables of the job merge. ## Additional resources ### XML representation A business rule task with a called decision that does not specify the binding type (`latest` is used implicitly): ```xml ``` A business rule task with a called decision that uses the `deployment` binding type: ```xml ``` A business rule task with a called decision that uses the `versionTag` binding type: ```xml ``` A business rule task with a job worker implementation and a custom header: ```xml ``` ### References - [DMN decision](/components/modeler/dmn/dmn.md) - [Job handling](/components/concepts/job-workers.md) - [Variable mappings](/components/concepts/variables.md#inputoutput-variable-mappings) --- ## Call activities A call activity (or reusable subprocess) allows you to call and invoke another process as part of this process. It's similar to an [embedded subprocess](../embedded-subprocesses/embedded-subprocesses.md), but the process is externalized (i.e. stored as separated BPMN) and can be invoked by different processes. ![call-activity](assets/call-activities-example.png) When a call activity is entered, a new process instance of the referenced process is created. The new process instance is activated at the **none start event**. The process can have start events of other types, but they are ignored. When the created process instance is completed, the call activity is left and the outgoing sequence flow is taken. ## Defining the called process A call activity must define the BPMN process ID of the called process as `processId`. Usually, the `processId` is defined as a [static value](/components/concepts/expressions.md#expressions-vs-static-values) (e.g. `shipping-process`), but it can also be defined as [expression](/components/concepts/expressions.md) (e.g. `= "shipping-" + tenantId`). The expression is evaluated on activating the call activity and must result in a `string`. The `bindingType` attribute determines which version of the called process is instantiated: - `latest`: The latest deployed version at the moment the call activity is activated. - `deployment`: The version that was deployed together with the currently running version of the calling process. - `versionTag`: The latest deployed version that is annotated with the version tag specified in the `versionTag` attribute. To learn more about choosing binding types, see [choosing the resource binding type](/components/best-practices/modeling/choosing-the-resource-binding-type.md). :::note If the `bindingType` attribute is not specified, `latest` is used as the default. ::: ## Boundary events ![call-activity-boundary-event](assets/call-activities-boundary-events.png) Interrupting and non-interrupting boundary events can be attached to a call activity. When an interrupting boundary event is triggered, the call activity and the created process instance are terminated. The variables of the created process instance are not propagated to the call activity. When a non-interrupting boundary event is triggered, the created process instance is not affected. The activities at the outgoing path have no access to the variables of the created process instance since they are bound to the other process instance. ## Variable mappings Input mappings can be used to create new local variables in the scope of the call activity. These variables are also copied to the created process instance. If the attribute `propagateAllChildVariables` is set (default: `true`), all variables of the created process instance are propagated to the call activity. This behavior can be customized by defining output mappings at the call activity. The output mappings are applied on completing the call activity and only those variables that are defined in the output mappings are propagated. It's recommended to disable the attribute `propagateAllChildVariables` or define output mappings if the call activity is in a parallel flow (e.g. when it is marked as [parallel multi-instance](../multi-instance/multi-instance.md#variable-mappings)). Otherwise, variables can be accidentally overridden when they are changed in the parallel flow. By default, all variables of the call activity scope are copied to the created process instance. This can be limited to copying only the local variables of the call activity, by setting the attribute `propagateAllParentVariables` to `false`. By disabling this attribute, variables existing at higher scopes are no longer copied. If the attribute `propagateAllParentVariables` is set (default: `true`), all variables are propagated to the child process instance. ## Additional resources ### XML representation A call activity with static process id, propagation of all child variables turned on, and no explicit binding type (`latest` is used implicitly): ```xml ``` A call activity with the `deployment` binding type: ```xml ``` A call activity with the `versionTag` binding type: ```xml ``` A call activity with copying of all variables to the child process turned off: ```xml ``` ### References - [Expressions](/components/concepts/expressions.md) - [Variable scopes](/components/concepts/variables.md#variable-scopes) - [Variable mappings](/components/concepts/variables.md#inputoutput-variable-mappings) --- ## Compensation events Compensation events assist with undoing steps that were already successfully completed in the case that their results are no longer desired and need to be reversed. To revert the effects of an activity, a compensation boundary event is attached to the activity. This activity is called **compensation activity**. The compensation boundary event is associated with the [compensation handler](../compensation-handler/compensation-handler.md), an activity with a compensation marker that is in charge of reverting the effects of the compensation activity. ![Process with compensation throw event](assets/compensation-throw-event.png) The example above shows the execution of compensation events: 1. After the service task `A` is completed, the process reaches the compensation intermediate throw event. 2. This invokes the compensation handler `Undo A` associated with the compensation boundary event. 3. Once the compensation handler `Undo A` is completed, the process completes the compensation intermediate throw event and takes the outgoing sequence flow. ## Triggering compensation When a process instance enters a compensation intermediate throw or end event, it triggers the compensation within its scope and invokes all compensation handlers of completed activities. The compensation handlers of active or terminated activities are not invoked. The compensation throw event remains active until all invoked compensation handlers are completed. :::note The process instance invokes all compensation handlers at once without any specific order. If the order is important, the compensation can be triggered for a specific activity. Read more about this case in [triggering compensation for an activity](#triggering-compensation-for-a-specific-activity). ::: ## Compensating embedded subprocesses If a process instance enters a compensation throw event and there are completed [embedded subprocesses](/components/modeler/bpmn/embedded-subprocesses/embedded-subprocesses.md) in the same scope, it invokes the compensation handlers within these subprocesses and nested subprocesses. The compensation handlers are not invoked if the subprocess is active or terminated. ![Process with embedded subprocesses](assets/compensation-embedded-subprocess.png) If the compensation throw event is inside an embedded subprocess, the process instance invokes only the compensation handlers within the subprocess. It doesn't invoke any compensation handler outside the subprocess. :::info Compensation handlers of child processes are not invoked. The triggering of the compensation stops at the call activity. To revert the effects of a child process, attach a compensation boundary event on the call activity. Read more about this in [call activities as compensation handlers](../compensation-handler/compensation-handler.md#call-activity-as-compensation-handler). ::: ## Compensating multi-instance activities The compensation handler of a multi-instance activity is invoked only once, rather than for each item in the input collection. The compensation handler is responsible for reverting the effects of all instances of the multi-instance activity. ![Process with multi instance activity](assets/compensation-multi-instance-activity.png) To revert the effects of each instance separately, the compensation handler could be marked as multi-instance as well. Read more about this in [multi-instance activities as compensation handlers](../compensation-handler/compensation-handler.md#multi-instance-activity-as-compensation-handler). :::note The process instance invokes the compensation handler only if all instances of the multi-instance activity are completed. ::: ## Triggering compensation for a specific activity By default, a compensation throw event invokes all compensation handlers in its scope. However, it is also possible to trigger the compensation for a specific activity. This can be used to enforce that compensation handlers are invoked synchronously in a given order. ![Trigger compensation for a give activity](assets/compensation-activity-ref.png) On a compensation intermediate throw or end event, it is possible to specify the activity to compensate by using the property `activityRef`. The referenced activity must have a compensation boundary event and must be in the same scope of the compensation throw event. ## Triggering compensation from an event subprocess An interrupting or non-interrupting event subprocess can contain compensation intermediate throw events or a compensation end event. These compensation events can specify an activity or broadcast the compensation within the outer scope of the event subprocess. ![Trigger compensation from an event subprocess](assets/compensation-event-subprocess.png) A common pattern is to use this in combination with an error event subprocess to revert the effects of compensation activities if a failure occurs that can't be recovered from. ## Additional resources ### XML representation An intermediate compensation throw event with a referenced activity: ```xml Flow_0b2blc2 Flow_1goayj7 ``` --- ## Compensation Activities that are associated to a compensation boundary event have a compensation marker. These activities are called **compensation handlers** and are in charge of reverting the effects of the activity with the compensation boundary event, the compensation activity. ![Compensation marker example](assets/compensation-marker-example.png) When a process instance reaches a compensation throw event, it invokes the compensation handlers for all completed activities. If an activity has been completed more than once, the compensation handler is invoked for the same amount. Read more about triggering the compensation in the [compensation events documentation](../compensation-events/compensation-events.md). ## Embedded subprocess as compensation handler ![Process with subprocess as compensation handler](assets/subprocess-compensation-handler.png) The subprocess contains the steps to undo the actions of the compensation activity. Using a subprocess can be useful if a sequence of steps is required to undo the actions of the activity. ## Call activity as compensation handler ![Process with call activity as compensation handler](assets/call-activity-compensation-handler.png) The call activity contains the steps to undo the actions of the compensation activity. Using a call activity as the compensation handler can be useful since the compensation handlers of a child process are not invoked. ## Multi-instance activity as compensation handler ![Process with multi instance activity as compensation handler](assets/multi-instance-compensation-handler.png) The compensation handler for a [multi-instance activity](../multi-instance/multi-instance.md) is invoked only once, rather than for each item in the input collection. To invoke the compensation handler more than once, the handler can be marked as multi-instance too. If the compensation handler should revert the effects of each item in the input collection, it could use the same input collection as the multi-instance activity. ## Interrupting compensation handlers Compensation handlers can be interrupted. If the process instance is canceled, it terminates all compensation handlers. Within a process, the process instance terminates a compensation handler if the compensation throw event that invoked the compensation handler is interrupted. This can happen in the following cases: - If a terminate end event is entered. - If an interrupting event subprocess is triggered. - If the compensation throw event is inside an embedded subprocess and the subprocess is interrupted. ![A compensation handler is interrupted by an event subprocess](assets/interrupt-compensation-handler.png) ## Variable mappings A compensation handler can define input and output [variable mappings](/components/concepts/variables.md#inputoutput-variable-mappings). Input variable mappings are applied before invoking the compensation handler. They can be used to create local variables for the compensation handler. Output variable mappings are applied after completing the compensation handler. They can be used to customize how the result variables of the compensation handler are merged into the process instance. By default, all variables are merged. ## Additional resources ### XML representation A service task with a compensation marker: ```xml ``` --- ## Conditional events Conditional events allow a process to react to changes in process state instead of waiting for a message or signal. For example, when a variable crosses a threshold, a required set of fields becomes complete, a risk score changes, or a business rule flips from false to true. Conditional events are useful when the producer of a change is not a single known sender, when correlation logic would add unnecessary complexity, or when the process logic is naturally expressed as guard conditions over process state. The engine evaluates the FEEL expression and triggers the event when the expression evaluates to `true`. The diagram below shows all four types of conditional events: root-level start, event sub-process start, intermediate catch, and boundary events. ![BPMN diagram showing conditional start, intermediate, and boundary events](assets/all-conditional-event-types.png) In this example, the process starts with a root-level conditional start event. Root-level conditional start events can be triggered via the Orchestration Cluster REST API or Camunda Client SDKs (see [trigger root-level conditional start events via API](../../../concepts/conditionals.md#trigger-root-level-conditional-start-events-via-api) for more details). A new instance is created once the condition `= orderReceived = true` evaluates to `true`. The intermediate conditional catch event acts like a wait-until condition. It continues to “Ship order” only after inventory is successfully reserved. The interrupting conditional boundary event attached to “Review order” handles changes mid-review. If the delivery address is changed, the boundary event triggers and interrupts the user task, routing execution to “Apply changes” before completing the order preparation. Finally, the interrupting event sub-process can cancel the work at any time while the instance is running. If the order is canceled while it is being prepared, the conditional start event inside the event sub-process fires and interrupts the main process, starting the cancellation sub-process to handle the cancellation logic. See [triggering conditional events](../../../concepts/conditionals.md#triggering-conditional-events) for details on how and when conditional events are triggered. ## Conditional start events Conditional start events can be used to start a process instance. Deploying a process with a conditional start event creates a subscription for that event. When the condition of the start event evaluates to `true`, the engine starts a new process instance. When deploying a process with a conditional start event, the following rules apply: - The condition of the conditional start event must be unique across a process definition. If multiple conditional start events have the same condition, the deployment will fail with a validation error. - Upon deployment of a new version, the previous version’s conditional start event subscription is removed and replaced with the new version’s subscription. To start processes via conditional start events from external systems, use the Orchestration Cluster REST API, the Zeebe gRPC API, or a Camunda Client SDK. See [trigger root-level conditional start events via API](../../../concepts/conditionals.md#trigger-root-level-conditional-start-events-via-api) for more details. ## Event sub-process conditional start events Event sub-process conditional start events start an event sub-process within an active process instance when the configured condition evaluates to `true` in the scope of that instance. This allows a running instance to react to state changes (for example, cancellation flags, escalation thresholds, or data corrections) without requiring an external signal or message correlation. An event sub-process conditional start event can be interrupting or non-interrupting: - Interrupting starts the event sub-process and cancels the currently active work in the scope it interrupts, so the instance continues via the event sub-process path. - Non-interrupting starts the event sub-process in parallel while the existing execution continues. A non-interrupting event sub-process conditional start event can trigger more than once while the instance is active. It triggers each time the condition becomes `true`, based on changes to variables referenced in the expression. You can use variable event filters to further restrict which change types (`create`, `update`) trigger evaluation. For runtime evaluation behavior and filter semantics, see [variable filter semantics](../../../concepts/conditionals.md#variable-filter-semantics). ## Intermediate conditional catch events An intermediate conditional catch event waits until its condition becomes `true`. When the process instance reaches the event, it waits until the condition evaluates to `true`, then continues along the outgoing sequence flow. For details on how and when the condition is triggered, see [triggering conditional events](../../../concepts/conditionals.md#triggering-conditional-events). Intermediate conditional catch events are always interrupting, as they represent a waiting point in the process flow. ## Conditional boundary events A conditional boundary event is attached to an activity and monitors data while the activity is active. When the activity is entered, the engine evaluates the boundary event’s condition and triggers immediately if the condition is satisfied. Conditional boundary events can be interrupting or non-interrupting: - Interrupting triggers the boundary event and cancels the attached activity, so execution continues via the boundary event’s outgoing sequence flow. - Non-interrupting triggers the boundary event without canceling the attached activity, starting an additional path via the boundary event’s outgoing sequence flow while the attached activity continues. Like a non-interrupting event sub-process conditional start event, a non-interrupting conditional boundary event can trigger multiple times while the attached activity is active. It triggers each time the condition becomes `true`, based on changes to variables referenced in the expression. You can use variable event filters to further restrict which change types trigger evaluation. See [variable filter semantics](../../../concepts/conditionals.md#variable-filter-semantics) for details. ## Define conditions and variable filters ### Condition expressions (FEEL) Conditional events use a FEEL expression in the `bpmn:condition` element. The expression must evaluate to a boolean value (`true` or `false`). For example: ```xml title="Conditional event definition with FEEL condition" = x > 1 ``` The engine evaluates the FEEL expression using variables available in the event’s scope and derives which variables can trigger the conditional event from the expression. For details, see [expression-based evaluation](../../../concepts/conditionals.md#expression-based-evaluation). ### Variable filters Variable filters restrict when a conditional event is re-evaluated in response to variable changes for those referenced variables. The `variableEvents` attribute applies only to conditional events inside running process instances. It does not apply to root-level conditional start events. Define an event-type filter by adding a `zeebe:conditionalFilter` extension element: ```xml title="Conditional event with Zeebe variable filter" = x > 1 ``` The `zeebe:conditionalFilter` extension element supports: - `variableEvents` specifies which variable events trigger evaluation. Supported values: - `create` - `update` - `create, update` For runtime behavior and limitations of variable filters, see [variable filter semantics](../../../concepts/conditionals.md#variable-filter-semantics). ## Modeling conditional events in Modeler Camunda Modeler supports conditional start events, intermediate conditional catch events, and interrupting or non-interrupting conditional boundary events. To add a conditional event: 1. Select an existing start event, intermediate event, or boundary event, or use the **Create element** popup and search for _conditional_. 2. Change the element type to **Conditional start event**, **Conditional intermediate catch event**, or **Conditional boundary event** as needed. 3. With the conditional event selected, use the properties panel on the right to configure it: - In the **Condition** field, enter a FEEL expression starting with `=` (for example, `= x > 1`). - In the **Variable filters** section, optionally restrict when the condition is re-evaluated by specifying which variable events (`create`, `update`, or `create, update`) should trigger evaluation. ## XML representation ### Conditional start event ```xml = amount > 100 ``` ### Intermediate conditional catch event ```xml = processorAvailable ``` ### Conditional boundary event ```xml = customerCancelled ``` ## Additional resources - [Conditionals concept documentation](../../../concepts/conditionals.md) --- ## Data flow Every BPMN process instance can have one or more variables. Variables are key-value-pairs and hold the contextual data of the process instance required by job workers to do their work, or to decide which sequence flows to take. They can be provided when a process instance is created, when a job is completed, and when a message is correlated. ![data-flow](assets/data-flow.png) ## Job workers By default, a job worker gets all variables of a process instance; it can limit the data by providing a list of required variables as **fetchVariables**. The worker uses the variables to do its work. When the work is done, it completes the job. If the result of the work is needed by follow-up tasks, the worker sets the variables while completing the job. These variables [merge](/components/concepts/variables.md#variable-propagation) into the process instance. ![job-worker](assets/data-flow-job-worker.png) If the job worker expects the variables in a different format or under different names, the variables can be transformed by defining **input mappings** in the process. **Output mappings** can be used to transform the job variables before merging them into the process instance. ## Variable scopes vs. token-based data A process can have concurrent paths; for example, when using a parallel gateway. When the execution reaches the parallel gateway, new tokens are created which execute the following paths concurrently. Since the variables are part of the process instance and not of the token, they can be read globally from any token. If a token adds a variable or modifies the value of a variable, the changes are also visible to concurrent tokens. ![variable-scopes](assets/variable-scopes.png) The visibility of variables is defined by the **variable scopes** of the process. ## Concurrency considerations When multiple active activities exist in a process instance (i.e. there is a form of concurrent execution like usage of a parallel gateway, multiple outgoing sequence flows, or a parallel multi-instance marker), you may need to take extra care in dealing with variables. When variables are altered by one activity, it might also be accessed and altered by another at the same time. Race conditions can occur in such processes. We recommend taking care when writing variables in a parallel flow. Make sure the variables are written to the correct [variable scope](/components/concepts/variables.md#variable-scopes) using variable mappings and make sure to complete jobs and publish messages only with the minimum required variables. These type of problems can be avoided by: - Passing only updated variables - Using output variable mappings to customize the variable propagation - Using an embedded subprocess and input variable mappings to limit the visibility and propagation of variables ## Additional resources - [Job handling](/components/concepts/job-workers.md) - [Variables](/components/concepts/variables.md) - [Input/output variable mappings](/components/concepts/variables.md#inputoutput-variable-mappings) - [Variable scopes](/components/concepts/variables.md#variable-scopes) --- ## Embedded subprocess An embedded subprocess allows you to group elements of the process. ![embedded-subprocess](assets/embedded-subprocess.png) An embedded subprocess must have exactly **one** none start event. Other start events are not allowed. When an embedded subprocess is entered, the start event is activated. The subprocess stays active as long as one containing element is active. When the last element is completed, the subprocess is completed and the outgoing sequence flow is taken. Embedded subprocesses are often used together with **boundary events**. One or more boundary events can be attached to a subprocess. When an interrupting boundary event is triggered, the entire subprocess (including all active elements) is terminated. When adding an embedded subprocess to your model, you can either add a collapsed or expanded subprocess. You cannot collapse an existing expanded subprocess in your model. ## Collapsed subprocesses :::caution Collapsed subprocesses are currently only partially supported by Optimize. While diagrams containing collapsed subprocesses can be imported, it is not possible to drill down into the subprocesses. All other Camunda components fully support collapsed subprocesses. ::: A collapsed subprocess conceals its internal details, thereby hiding complexity within an activity and enabling the nesting of multiple levels of subprocesses. This functionality allows you to simplify the view of a process diagram and facilitates drill-down capabilities to examine details. Collapsed subprocesses serve purely display purposes. For the creation of reusable processes, it is recommended to utilize [call activities](../call-activities/call-activities.md). :::info When you add a **collapsed subprocess**, Modeler shows a link for drill-down. This link only opens the embedded subprocess within the same diagram. You can’t target or reuse a different process from that link. To reference another process you’ve already created, use a [call activity](../call-activities/call-activities.md) instead. ::: ![collapsed-subprocess](assets/collapsed-subprocess.png) ## Variable mappings Input mappings can be used to create new local variables in the scope of the subprocess. These variables are only visible within the subprocess. By default, the local variables of the subprocess are not propagated (i.e. they are removed with the scope.) This behavior can be customized by defining output mappings at the subprocess. The output mappings are applied on completing the subprocess. ## Additional resources ### XML representation An embedded subprocess with a start event: ```xml ... more contained elements ... ``` ### References - [Variable mappings](/components/concepts/variables.md#inputoutput-variable-mappings) --- ## Error events In process automation, you often encounter deviations from the default scenario. One way to resolve these deviations is using a BPMN error event, which allows a process model to react to errors within a task. For example, if an invalid credit card is used in the process below, the process takes a different path than usual and uses the default payment method to collect money. ![process with error event](assets/error-events.png) ## Defining the error In BPMN, **errors** define possible errors that can occur. **Error events** are elements in the process referring to defined errors. An error can be referenced by one or more error events. An error must define an `errorCode`. The value of this `errorCode` is used to determine which catch event can catch the thrown error. For error throw events, it is possible to define the `errorCode` as [an `expression` or a static value](/components/concepts/expressions.md#expressions-vs-static-values). If an `errorCode` expression is configured then it will be evaluated once the event is reached, and used to throw error. For error catch events `errorCode` must be [a static value](/components/concepts/expressions.md#expressions-vs-static-values). Alternatively an error catch event may omit the error reference all together. In this case it catches **all** thrown errors. ## Throwing the error An error can be thrown within the process using an error **end event**. ![process with error throw event](assets/error-throw-events.png) Alternatively, you can inform Zeebe that a business error occurred using a **client command**. This throw error client command can only be used while processing a job. In addition to throwing the error, this also disables the job and stops it from being activated or completed by other job workers. Refer to the [gRPC command](/apis-tools/zeebe-api/gateway-service.md#throwerror-rpc) and [REST request](/apis-tools/orchestration-cluster-api-rest/specifications/throw-job-error.api.mdx) for details. ## Catching the error A thrown error can be caught by an error catch event, specifically using an error **boundary event** or an error **event subprocess**. ![process with error catch event](assets/error-catch-events.png) Starting at the scope where the error was thrown, the error code is matched against the attached error boundary events and error event subprocesses at that level. An error is caught by the first event in the scope hierarchy matching the error code. At each scope, the error is either caught, or propagated to the parent scope. If the process instance is created via call activity, the error can also be caught in the calling parent process instance. It is not possible to define multiple error catch events with the same `errorCode` in a single scope. It is also not permitted to have multiple error catch-all events in a single scope. However, it is possible to define both an error catch event referencing an error with a particular `errorCode` and an error catch-all event within the same scope. When this happens, the error catch event that matches the `errorCode` is prioritized. Error boundary events and error event subprocesses must be interrupting. This means the process instance will not continue along the regular path, but instead follow the path that leads out of the catching error event. If the error is thrown for a job, the associated task is terminated first. To continue the execution, the error boundary event or error event subprocess that caught the error is activated. ## Unhandled errors When an error is thrown and not caught, an [**incident**](/components/concepts/incidents.md) (for example, `Unhandled error event`) is raised to indicate the failure. The incident is attached to the corresponding element where the error was thrown (that is, the task of the processed job or the error end event). When you resolve the incident attached to a task, it ignores the error, re-enables the job, and allows it to be activated and completed by a job worker once again. The incident attached to an error end event cannot be resolved by a user because the failure is in the process itself. The process cannot be changed to catch the error for this process instance. ## Business error vs. technical error In real life, you’ll also have to deal with technical problems that you don't want to treat using error events. Suppose the credit card service becomes temporarily unavailable. You don't want to model the retrying, as you would have to add it to each and every service task. This will bloat the visual model and confuse business personnel. Instead, either retry or fall back to incidents as described above. This is hidden in the visual. In this context, we found the terms **business error** and **technical error** can be confusing, as they emphasize the source of the error too much. This can lead to long discussions about whether a certain problem is technical or not, and if you are allowed to observe technical errors in a business process model. It's much more important to look at how you _react_ to certain errors. Even a technical problem can qualify for a business reaction. For example, you could decide to continue a process in the event that a scoring service is not available, and simply give every customer a good rating instead of blocking progress. The error is clearly technical, but the reaction is a business decision. In general, we recommend talking about business reactions, which are modeled in your process, and technical reactions, which are handled generically using retries or incidents. ## Variable mappings Variables can be passed along into the error catch event with the payload when the error is thrown from the client command. These variables can be merged into the process instance by defining an output mapping at the error catch event. Visit the documentation regarding [variable mappings](../../../concepts/variables/#inputoutput-variable-mappings) for more information. ## Additional resources ### XML representation A boundary error event: ```xml ``` A error boundary catch-all event: ```xml ``` ### References - [Incidents](/components/concepts/incidents.md) --- ## Escalation events Escalation events are events which reference a named escalation, and are used to communicate to a higher flow scope. Unlike an error, an escalation event is non-critical and execution continues at the location of throwing. ![The process reached an escalation event. The escalation gets caught in a higher flow scope. As the escalation throw event is non-critical, the outgoing sequence flow of this event is taken.](assets/escalation-events.png) The example above shows the execution of an escalation event: 1. The process reaches the `Throw` event. 2. This throws an escalation to a higher flow scope. 3. The escalation is caught by the `Catch` event. 4. As escalation events are non-critical, the outgoing sequence flows of `Throw` and `Catch` are both taken. ## Defining an escalation In BPMN, an `escalation event` references an `escalation`. Escalations can be referenced by one or more escalation events. An escalation must define an `escalationCode`. The value of this `escalationCode` is used to determine which catch event can catch the thrown escalation. For escalation throw events, it is possible to define the `escalationCode` as [an `expression` or a static value](/components/concepts/expressions.md#expressions-vs-static-values). If an `escalationCode` expression is configured then it will be evaluated once the event is reached, and used to throw the escalation. For escalation catch events `escalationCode` must be [a static value](/components/concepts/expressions.md#expressions-vs-static-values). Alternatively an escalation catch event may omit the escalation reference all together. In this case it catches **all** thrown escalations. ## Throwing the escalation An escalation can be thrown by an escalation end event, or by an intermediate escalation throw event. Escalation events are non-critical. This means that if the throwing event has any outgoing sequence flows, they will be taken. ## Catching the escalation An escalation can be caught using a boundary event, or using an event subprocess. It is caught by one catch event at most, and this will be the catch event in the nearest parent flow scope. It is not possible to define multiple escalation catch events with the same `escalationCode` in a single scope. It is also not permitted to have multiple escalation catch-all events in a single scope. However, it is possible to define both an escalation catch event referencing an escalation with a particular `escalationCode` and an escalation catch-all event within the same scope. When this happens, the escalation catch event that matches the `escalationCode` is prioritized. If there are no escalation catch events that match the `escalationCode`, the escalation will not be caught. Unlike with [error events](../error-events/error-events.md), no incident is raised. The process will continue without escalating. Even though escalations are non-critical, it is still possible make escalation catch events interrupting. This will behave the same as other interrupting events. The catch event will terminate the scope it is attached to. In this case, the outgoing sequence flows of the throwing escalation event are not taken. ## Additional resources ### XML representation An intermediate escalation throw event with expression: ```xml ``` An escalation boundary catch event: ```xml ``` A escalation boundary catch-all event: ```xml ``` --- ## Event-based gateway An event-based gateway allows you to make a decision based on events. ![process](assets/event-based-gateway.png) An event-based gateway must have at least **two** outgoing sequence flows. Each sequence flow must be connected to an intermediate catch event of type [timer](../timer-events/timer-events.md), [message](../message-events/message-events.md) or [signal](../signal-events/signal-events.md). When an event-based gateway is entered, the process instance waits at the gateway until one of the events is triggered. When the first event is triggered, the outgoing sequence flow of this event is taken. No other events of the gateway can be triggered afterward. ## Additional resources ### XML representation An event-based gateway with two outgoing sequence flows: ```xml PT1H ``` ### References - [Timer events](../timer-events/timer-events.md) - [Message events](../message-events/message-events.md) --- ## Event subprocess An event subprocess is a subprocess triggered by an event. This can be added globally to the process, or locally inside an embedded subprocess. ![event-subprocess](assets/event-subprocess.png) An event subprocess must have exactly **one** start event of one of the following types: - [Timer](../timer-events/timer-events.md) - [Message](../message-events/message-events.md) - [Error](../error-events/error-events.md) - [Signal](../signal-events/signal-events.md) - [Escalation](../escalation-events/escalation-events.md) An event subprocess behaves like a boundary event, but is inside the scope instead of attached to the scope. Like a boundary event, the event subprocess can be interrupting or non-interrupting (indicated in BPMN by a solid or dashed border of the start event). The start event of the event subprocess can be triggered when its containing scope is activated. A non-interrupting event subprocess can be triggered multiple times. An interrupting event subprocess can be triggered only once. When an interrupting event subprocess is triggered, all active instances of its containing scope are terminated, including instances of other non-interrupting event subprocesses. If an event subprocess is triggered, its containing scope is not completed until the triggered instance is completed. ## Variables Unlike a boundary event, an event subprocess is inside the scope. Therefore, it can access and modify all local variables of its containing scope. This is not possible with a boundary event because a boundary event is outside of the scope. Input mappings can be used to create new local variables in the scope of the event subprocess. These variables are only visible within the event subprocess. If no input mappings are defined, the [default behavior](../../../concepts/variables.md#variable-scopes) is applied to the variables alongside the event. By default, the local variables of the event subprocess are not propagated (i.e. removed with the scope). This behavior can be customized by defining output mappings at the event subprocess. The output mappings are applied on completion of the event subprocess. ## Additional resources ### XML representation An event subprocess with an interrupting timer start event: ```xml PT5M ... other elements ``` ### References - [Embedded subprocess](../embedded-subprocesses/embedded-subprocesses.md) - [Variable scopes](/components/concepts/variables.md#variable-scopes) --- ## Overview(Bpmn) **Events** in BPMN represent things that _happen_. A process can react to events (_catching_ event) as well as emit events (_throwing_ event). For example, a catching message event makes the token continue as soon as a message is received. The XML representation of the process contains the criteria for which kind of message triggers continuation. Events can be added to the process in various ways. Not only can they be used to make a token wait at a certain point, but also for interrupting a token's progress. Currently supported events: - [None events](none-events/none-events.md) - [Message events](message-events/message-events.md) - [Timer events](timer-events/timer-events.md) - [Error events](error-events/error-events.md) - [Escalation events](escalation-events/escalation-events.md) - [Terminate events](terminate-events/terminate-events.md) - [Link events](link-events/link-events.md) - [Signal events](signal-events/signal-events.md) - [Compensation events](compensation-events/compensation-events.md) :::note Not all the events are supported yet. For a complete overview of supported events, refer to the [BPMN coverage](../bpmn-coverage#events). ::: ## Events in general Events in BPMN can be **thrown** (i.e. sent), or **caught** (i.e. received), respectively referred to as **throw** or **catch** events (e.g. `message throw event`, `timer catch event`). Additionally, a distinction is made between start, intermediate, and end events: - **Start events** (catch events, as they can only react to something) are used to denote the beginning of a process or subprocess. - **End events** (throw events, as they indicate something has happened) are used to denote the end of a particular sequence flow. - **Intermediate events** can be used to indicate that something has happened (i.e. intermediate throw events), or to wait and react to certain events (i.e. intermediate catch events). Intermediate catch events can be inserted into your process in two different contexts: normal flow, or attached to an activity, and are called boundary events. ## Intermediate events In a typical flow, an intermediate throw event executes its event (e.g. send a message) once the token has reached it. Once complete, the token continues to all outgoing sequence flows (1). An intermediate catch event, however, stops the token and waits until the event it is waiting for occurs, at which point execution resumes and the token moves on (2). ## Boundary events Boundary events provide a way to model what should happen if an event occurs while an activity is active. For example, if a process is waiting on a user task to happen which is taking too long, an intermediate timer catch event can be attached to the task, with an outgoing sequence flow to notification task, allowing the modeler to automate and sending a reminder email to the user. A boundary event must be an intermediate catch event, and can be either interrupting (1)or non-interrupting (2). Interrupting means that once triggered, before taking any outgoing sequence flow the activity the event is attached to is terminated. This allows modeling timeouts where we can prune certain execution paths if something happens (e.g. the process takes too long). --- ## Exclusive gateway An exclusive gateway (or XOR-gateway) selects one outgoing sequence flow based on data such as process variables. ![process](assets/exclusive-gateway.png) For an exclusive gateway with multiple outgoing sequence flows: - All but one sequence flow must have a `conditionExpression`. - The remaining sequence flow can omit the `conditionExpression`, but the gateway must define it as the default flow. - Leaving the `conditionExpression` empty does not automatically make a sequence flow the default flow. In Modeler, set the exclusive gateway's default flow in the gateway properties by selecting the outgoing sequence flow to use as the default. - When a process instance reaches the gateway, the system evaluates the `conditionExpression` values in BPMN XML order and takes the first sequence flow whose condition is fulfilled. - If no condition is fulfilled, the process instance takes the **default flow**. The default flow should not have a condition, so the system does not evaluate it. - If no condition is fulfilled and the gateway has no default flow, an [incident](/components/concepts/incidents.md) is created. For example, if one sequence flow uses the condition `= totalPrice > 100`, you can set another outgoing sequence flow as the gateway's default flow to handle all remaining cases where `totalPrice <= 100`. An exclusive gateway can also join multiple incoming flows to improve BPMN readability. A joining gateway has pass-through semantics and does not merge incoming concurrent flows like a parallel gateway. ## Conditions A `conditionExpression` defines when a flow is taken. It is a [boolean expression](/components/modeler/feel/language-guide/feel-boolean-expressions.md) that can access the process variables and compare them with literals or other variables. The condition is fulfilled when the expression returns `true`. Multiple boolean values or comparisons can be combined as disjunction (`or`) or conjunction (`and`). For example: ```feel = totalPrice > 100 = order.customer = "Paul" = orderCount > 15 or totalPrice > 50 = valid and orderCount > 0 ``` ## Additional resources ### XML representation An exclusive gateway with two outgoing sequence flows: ```xml = totalPrice > 100 ``` ### References - [Expressions](/components/concepts/expressions.md) - [Incidents](/components/concepts/incidents.md) --- ## Overview(3) Gateways are elements that route tokens in more complex patterns than plain sequence flow. BPMN's **exclusive gateway** chooses one sequence flow out of many based on data, whereas BPMN's **parallel gateway** generates new tokens by activating multiple sequence flows in parallel, for example. Currently supported elements: - [Exclusive gateways](exclusive-gateways/exclusive-gateways.md) - [Parallel gateways](parallel-gateways/parallel-gateways.md) - [Event-based gateways](event-based-gateways/event-based-gateways.md) - [Inclusive gateways](inclusive-gateways/inclusive-gateways.md) --- ## Inclusive gateway The inclusive gateway (or OR-gateway) allows for making multiple decisions based on data or process variables. Inclusive gateways can be diverging (a sequence flow is split into multiple paths) or converging (split paths are merged before continuing). ![A process model to prepare lunch at lunchtime can use an inclusive gateway to decide which steps to take to prepare the different lunch components, e.g. cook pasta,stir-fry steak, prepare salad, or any combination of these.](assets/inclusive-gateway.png) If an inclusive gateway has multiple outgoing sequence flows, all sequence flows must have a condition to define when the flow is taken. If the inclusive gateway only has one outgoing sequence flow, then it does not need to have a condition. Optionally, one of the sequence flows can be marked as the default flow. This sequence flow should not have a condition, because its behavior depends on the other conditions. When an inclusive gateway is entered, the conditions are evaluated. The process instance takes all sequence flows where the condition is fulfilled. For example: Courses selected include `pasta` and `salad`. ![An inclusive gateway has decided to take the steps to cook pasta and prepare salad, but not stir-fry steak.](assets/inclusive-gateway-1.png) For example: Courses selected include `steak`, `pasta` and `salad`. ![An inclusive gateway has decided to take the steps to cook pasta, stir-fry steak, and prepare salad.](assets/inclusive-gateway-2.png) If no condition is fulfilled, it takes the **default flow** of the gateway. Note that the default flow is not expected to have a condition, and is therefore not evaluated. If no condition is fulfilled and the gateway has no default flow, an [incident](/components/concepts/incidents.md) is created. For example: No courses selected then the default flow is taken. ![An inclusive gateway has decided to take the step to prepare salad as the default because none of the conditions were fulfilled.](assets/inclusive-gateway-default.png) A converging inclusive gateway (also known as a merging or joining inclusive gateway) merges incoming paths before the sequence flow continues. A converging gateway is completed and merges incoming sequence flows if one of the following conditions is met: - All incoming sequence flows have been taken at least once. - No path exists from any active flow node to the inclusive gateway (excluding incoming paths to the inclusive gateway that have already been taken). For example: Once all selected courses are complete, the table can be cleared. ![An inclusive converging gateway waits until all incoming, executed sequence flows are completed before cleaning the table.](assets/inclusive-gateway-join.png) ## Conditions A `conditionExpression` defines when a flow is taken. It is a [boolean expression](/components/modeler/feel/language-guide/feel-boolean-expressions.md) that can access the process variables and compare them with literals or other variables. The condition is fulfilled when the expression returns `true`. Multiple boolean values or comparisons can be combined as disjunction (`and`) or conjunction (`or`). For example: ```feel = totalPrice > 100 = order.customer = "Paul" = orderCount > 15 or totalPrice > 50 = valid and orderCount > 0 = list contains(courses, "salad") ``` ## Additional resources ### XML representation An inclusive gateway with three outgoing sequence flows and the default sequence flow is `Salad`: ```xml Flow_0mfam08 Flow_0d3xogt Flow_1le3l31 Flow_05d0jjq = list contains(courses, "pasta") = list contains(courses, "steak") ``` ### References - [Conditions](/components/modeler/bpmn/inclusive-gateways/inclusive-gateways.md#conditions) - [Incidents](/components/concepts/incidents.md) --- ## Link events Link events are intermediate events that connect two sections of a process. They have no significance related to content, but facilitate the diagram-creation process. :::tip You can use link events to create loops, to skip sections of a process, or to simplify the sequence flow lines in the diagram. ::: Link events have a throwing link event as the "exit point", and a catching link event as the "re-entrance point". They are linked together by their link name. Multiple throwing link events can link to the same catching link event. A throwing link event cannot link to multiple catching link events. In practice, two paired link events function the same as two [intermediate none events] connected via a sequence flow. ![A pair of link events is equivalent to a pair of intermediate none events connected via a sequence flow](./assets/link-events-example.png) Link events can be very useful if you draw comprehensive process diagrams with many sequence flows. Links help avoid what otherwise might look like a “spaghetti” diagram. In the example below, a retry loop is created using the link events pair `A`. ![A pair of link events is used to form a retry loop](./assets/link-events-example-in-practice.png) :::info Link events are limited to a single scope Link events can only be used to link sections of a process within the same scope. I.e., they can only exist together on the root process level or within the same subprocess. Similarly, a sequence flow cannot be drawn between flow nodes at different scopes. For example, a task in the root process level cannot connect to another task in a subprocess using a sequence flow. Link events have the same limitation. ::: ## Additional resources ### XML representation A manual task: ```xml ``` ### References - [Intermediate none events] [intermediate none events]: ../none-events/none-events.md#intermediate-none-events-throwing --- ## Manual tasks A manual task defines a task that requires human interaction but no external tooling or UI interface. For example, a user reviewing a document or completing a physical task. ![task](assets/manual-task.png) Manual tasks are part of [human task orchestration](/guides/getting-started-orchestrate-human-tasks.md), but differ from [user tasks](/components/modeler/bpmn/user-tasks/user-tasks.md) which define an actionable task assisted by a workflow engine or software application. Within the engine and BPMN model, a manual task is handled as a pass-through activity, automatically continuing the process at the moment the process instance arrives. Manual tasks provide insights into the tasks performed outside the process engine, aiding in modeling a process, though no linked automation process is utilized. ### XML representation A manual task: ```xml ``` --- ## Overview(4) You might want to execute some tasks for every element of a list, like the `for each` construct in programming languages. Refer to [Workflow Pattern 14: Multiple Instances with a priori Run-Time Knowledge](http://www.workflowpatterns.com/patterns/control/new/wcp14.php): "Multiple instances of a task can be created. The required number of instances may depend on a number of runtime factors, but is known before the task instances must be created. Once initiated, these instances are independent of each other and run concurrently. It is necessary to synchronize the instances at completion before any subsequent tasks can be triggered." In BPMN, this is implemented using [multiple instance activities](/components/modeler/bpmn/multi-instance/multi-instance.md): Parallel multiple instance markers define that a subprocess is executed multiple times - once for each element of a given collection (like a `for each` loop in a programming language). Currently supported markers: - [Multi-instance](multi-instance/multi-instance.md) - [Compensation](compensation-handler/compensation-handler.md) - [Ad-hoc](ad-hoc-subprocesses/ad-hoc-subprocesses.md) --- ## Message events Message events are events which reference a message; they are used to wait until a proper message is received. ![process](assets/message-events.png) Message events are used if a single process instance needs to wait for a message from a secondary process or an external system. This is a single sender to a single recipient relationship (1:1), as the message cannot have more than one recipient. These differ from [signal events](/components/modeler/bpmn/signal-events/signal-events.md), which are used if you want to communicate with multiple listeners. For intermediate events, a signal will trigger all process instances with a token waiting at a corresponding catch event, even across different processes. For start events, a signal will start one instance per process that has a corresponding signal start. Unlike message events, signal events form a single sender to several-recipient relationship (1:N). Both event types are methods of collaboration within BPMN. ## Message start events A process can have one or more message start events (besides other types of start events). Each of the message events must have a unique message name. When a process is deployed, it creates a message subscription for each message start event. Message subscriptions of the previous version of the process (based on the BPMN process ID) are closed. ### Message correlation When the message subscription is created, a message can be correlated to the start event if the message name matches. On correlating the message, a new process instance is created and the corresponding message start event is activated. Messages are **not** correlated if they were published before the process was deployed or if a new version of the process is deployed without a proper start event. The `correlationKey` of a published message can be used to control the process instance creation. - If an instance of this process is active (independently from its version) and it was triggered by a message with the same `correlationKey`, the message is **not** correlated and no new instance is created. If the message has a time-to-live (TTL) > 0, it is buffered. - When the active process instance is completed or terminated and a message with the same `correlationKey` and a matching message name is buffered (that is, TTL > 0), this message is correlated and a new instance of the latest version of the process is created. If the `correlationKey` of a message is empty, it creates a new process instance and does not check if an instance is already active. :::note You do not specify a `correlationKey` for a message start event in the BPMN model when designing a process. - When an application sends a message that is caught by a message start event, the application can specify a `correlationKey` in the message. - If a message caught by a start event contains a `correlationKey` value, the `correlationKey` is used to ensure only one process instance is active per key (idempotency). The `correlationKey` is not stored as a tag on the process instance, and the `tags` field remains empty. - Follow-up messages are then checked against this `correlationKey` value (that is, is there an active process instance that was started by a message with the same `correlationKey`?). ::: ## Intermediate message catch events When an intermediate message catch event is entered, a corresponding message subscription is created. The process instance stops at this point and waits until the message is correlated. When a message is correlated, the catch event is completed and the process instance continues. :::note An alternative to intermediate message catch events is a [receive task](../receive-tasks/receive-tasks.md), which behaves the same but can be used together with boundary events. ::: ## Message boundary events An activity can have one or more message boundary events. Each of the message events must have a unique message name. When the activity is entered, it creates a corresponding message subscription for each boundary message event. If a non-interrupting boundary event is triggered, the activity is not terminated and multiple messages can be correlated. ## Message throw events A process can contain intermediate message throw events or message end events to model the publication of a message to an external system; for example, to a Kafka topic. Currently, intermediate message throw events and message end events behave exactly like [service tasks](../service-tasks/service-tasks.md) or [send tasks](../send-tasks/send-tasks.md) , and have the same job-related properties (e.g. job type, custom headers, etc.) The message throw events and the tasks are based on jobs and [job workers](../../../../components/concepts/job-workers.md). The differences between the message throw events and the tasks are the visual representation and the semantics for the model. Read more about the [job properties](../../../../components/concepts/job-workers.md). When a process instance enters a message throw event, it creates a corresponding job and waits for its completion. A job worker should request jobs of this job type and process them. When the job is complete, the process instance continues or completes if it is a message end event. :::note Message throw events are not processed by Zeebe itself (i.e. to correlate a message to a message catch event). Instead, it creates jobs with the defined job type. To process them, provide a job worker. ::: ## Messages A message can be referenced by one or more message events. It must define the name of the message (e.g. `Money collected`) and the `correlationKey` expression (e.g. `= orderId`). If the message is only referenced by message start events, the `correlationKey` is not required. Usually, the name of the message is defined as a [static value](/components/concepts/expressions.md#expressions-vs-static-values) (e.g. `order canceled`), but it can also be defined as [expression](/components/concepts/expressions.md) (e.g. `= "order " + awaitingAction`). If the expression belongs to a message start event of the process, it is evaluated on deploying the process. Otherwise, it is evaluated on activating the message event. The evaluation must result in a `string`. The `correlationKey` is an expression that usually [accesses a variable](/components/modeler/feel/language-guide/feel-variables.md#access-variable) of the process instance that holds the correlation key of the message. The expression is evaluated on activating the message event and must result either in a `string` or in a `number`. To correlate a message to the message event, the message is published with the defined name (e.g. `Money collected`) and the **value** of the `correlationKey` expression. For example, if the process instance has a variable `orderId` with value `"order-123"`, the message must be published with the correlation key `"order-123"`. ## Variable mappings By default, all message variables are merged into the process instance. This behavior can be customized by defining an output mapping at the message catch event. Visit the documentation regarding [variable mappings](/components/concepts/variables.md#inputoutput-variable-mappings) for more information on this topic. ## Additional resources ### XML representation A message start event with message definition: ```xml ``` An intermediate message catch event with message definition: ```xml ``` A boundary message event: ```xml ``` ### References - [Message correlation](/components/concepts/messages.md) - [Expressions](/components/concepts/expressions.md) - [Variable mappings](/components/concepts/variables.md#inputoutput-variable-mappings) - [Incidents](/components/concepts/incidents.md) - [Job handling](/components/concepts/job-workers.md) --- ## Multi-instance A multi-instance activity is executed multiple times - once for each element of a given collection (like a _foreach_ loop in a programming language). We support the multi-instance marker for all [supported activities](/components/modeler/bpmn/bpmn-coverage.md), such as service tasks, receive tasks, embedded subprocceses, and call activities. For example: ![multi-instance](assets/multi-instance-example.png) On the execution level, a multi-instance activity has two parts: a multi-instance body, and an inner activity. The multi-instance body is the container for all instances of the inner activity. When the activity is entered, the multi-instance body is activated and one instance for every element of the `inputCollection` is created (sequentially or in parallel). When all instances are completed, the body is completed and the activity is left. :::note Events with a `JobWorker` implementation, such as intermediate throw events, do not support this marker. ::: ## Sequential vs. parallel A multi-instance activity is executed either sequentially or in parallel (default). In the BPMN, a sequential multi-instance activity is displayed with three horizontal lines at the bottom. A parallel multi-instance activity is represented by three vertical lines. In case of a **sequential** multi-instance activity, the instances are executed one at a time. When one instance is completed, a new instance is created for the next element in the `inputCollection`. ![sequential multi-instance](assets/multi-instance-sequential.png) In case of a **parallel** multi-instance activity, all instances are created when the multi-instance body is activated. The instances are executed concurrently and independently from each other. ![parallel multi-instance](assets/multi-instance-parallel.png) ## Defining the collection to iterate over A multi-instance activity must have an `inputCollection` expression that defines the collection to iterate over (e.g. `= items`). Usually, it [accesses a variable](/components/modeler/feel/language-guide/feel-variables.md#access-variable) of the process instance that holds the collection. The expression is evaluated on activating the multi-instance body. It must result in an `array` of any type (e.g. `["item-1", "item-2"]`). :::tip If you need to iterate `n` times (like with a loop-cardinality), you can use the following expression with a [for-loop](/components/modeler/feel/language-guide/feel-control-flow.md#for-loops): `for i in 1..n return i`. ::: To access the current element of the `inputCollection` value within the instance, the multi-instance activity can define the `inputElement` variable (e.g. `item`). The element is stored as a local variable of the instance under the given name. If the `inputCollection` value is **empty**, the multi-instance body is completed immediately and no instances are created. It behaves like the activity is skipped. ## Collecting the output The output of a multi-instance activity (e.g. the result of a calculation) can be collected from the instances by defining the `outputCollection` and the `outputElement` expression. `outputCollection` defines the name of the variable under which the collected output is stored (e.g. `results`). It is created as a local variable of the multi-instance body and is updated when an instance is completed. When the multi-instance body is completed, the variable is propagated to its parent scope. `outputElement` is an expression that defines the output of the instance (e.g. `= result`). Usually, it [accesses a variable](/components/modeler/feel/language-guide/feel-variables.md#access-variable) of the instance that holds the output value. If the expression only accesses a variable or a nested property, it's created as a **local variable** of the instance. This variable should be updated with the output value; for example, by a job worker providing a variable with the name `result`. Since the variable is defined as a local variable, it is not propagated to its parent scope and is only visible within the instance. When the instance is completed, the `outputElement` expression is evaluated and the result is inserted into the `outputCollection` at the same index as the `inputElement` of the `inputCollection`. Therefore, the order of the `outputCollection` is determined and matches to the `inputCollection`, even for parallel multi-instance activities. If the `outputElement` variable is not updated, `null` is inserted instead. If the `inputCollection` value is empty, an empty array is propagated as `outputCollection`. ## Boundary events ![multi-instance with boundary event](assets/multi-instance-boundary-event.png) Interrupting and non-interrupting boundary events can be attached to a multi-instance activity. When an interrupting boundary event is triggered, the multi-instance body and all active instances are terminated. The `outputCollection` variable is not propagated to the parent scope (i.e. no partial output). When a non-interrupting boundary event is triggered, the instances are not affected. The activities at the outgoing path have no access to the local variables since they are bound to the multi-instance activity. ## Special multi-instance variables Every instance has a local variable `loopCounter`. It holds the index in the `inputCollection` of this instance, starting with `1`. ## Variable mappings Input and output variable mappings can be defined at the multi-instance activity; they are applied on each instance on activating and on completing. The input mappings can be used to create new local variables in the scope of an instance. These variables are only visible within the instance; it is a way to restrict the visibility of variables. By default, new variables (e.g. provided by a job worker) are created in the scope of the process instance and are visible to all instances of the multi-instance activity as well as outside of it. In case of a parallel multi-instance activity, this can lead to variables that are modified by multiple instances and result in race conditions. If a variable is defined as a local variable, it is not propagated to a parent or the process instance scope and can't be modified outside the instance. The input mappings can access the local variables of the instance (e.g. `inputElement`, `loopCounter`); for example, to extract parts of the `inputElement` variable and apply them to separate variables. The output mappings can be used to update the `outputElement` variable; for example, to extract a part of the job variables. **Example:** We have a call activity marked as a parallel multi-instance. When the called process instance completes, its variables are [merged](/components/concepts/variables.md#variable-propagation) into the call activity's process instance. Its result is collected in the output collection variable, but this has become a race condition where each completed child instance again overwrites this same variable. We end up with a corrupted output collection. An output mapping can be used to overcome this, because it restricts which variables are merged. In the case of: - Parallel multi-instance call activity - Multi-instance output element: `=output` - Variable in the child instance that holds the result: `x` The output mapping on the call activity should be: ``` source: =x target: output ``` ## Completion condition A `completionCondition` defines whether the multi-instance body can be completed immediately when the condition is satisfied. It is a [boolean expression](/components/modeler/feel/language-guide/feel-boolean-expressions.md) that will be evaluated each time the instance of the multi-instance body completes. Any instances that are still active are terminated and the multi-instance body is completed when the expression evaluates to `true`. The BPMN 2.0 specification defines the following properties of a multi-instance body: - `numberOfInstances`: The number of instances created. - `numberOfActiveInstances`: The number of instances currently active. - `numberOfCompletedInstances`: The number of instances already completed. - `numberOfTerminatedInstances`: The number of instances already terminated. These properties are available for use in the `completionCondition` expression. For example, using these properties you can express "complete the multi-instance body when 50% or more of the instances already completed" as `= numberOfCompletedInstances / numberOfInstances >= 0.5`. Although these properties are available in this expression, they do not exist as process variables. These properties take precedence over process variables with the same name. Multiple boolean values or comparisons can be combined as disjunction (`and`) or conjunction (`or`). For example: ```feel = result.isSuccessful = count(["a", "b", "c", "d"]) > 3 = orderCount >= 5 and orderCount < 15 = list contains([6,7], today().weekday) = numberOfCompletedInstances = 2 = numberOfCompletedInstances / numberOfInstances >= 0.5 ``` ## Additional resources ### XML representation A sequential multi-instance service task: ```xml = result.isSuccessful ``` ### References - [Variable scopes](/components/concepts/variables.md#variable-scopes) - [Expressions](/components/concepts/expressions.md) - [Variable mappings](/components/concepts/variables.md#inputoutput-variable-mappings) --- ## None events None events are unspecified events, also called "blank" events. ![process](assets/none-events.png) ## None start events At most, a process can have **one** none start event (besides other types of start events). A none start event is where the process instance or a subprocess starts when the process or the subprocess is activated. A none start event is required if you want to [trigger a process via a form](../../../hub/workspace/modeler/run-or-publish-your-process.md#publish-via-a-public-form). ## None end events A process or subprocess can have multiple none end events. When a none end event is entered, the current execution path ends. If the process instance or subprocess has no more active execution paths, it is completed. If an activity has no outgoing sequence flow, it behaves the same as it would be connected to a none end event. When the activity is completed, the current execution path ends. ## Intermediate none events (throwing) Intermediate none events can be used to indicate some state achieved in the process. They are especially useful for monitoring to understand how the process is doing, for example, as milestones or key performance indicators (KPIs). The engine itself doesn't do anything in the event, it just passes through it. ## Variable mappings All none events can have [variable output mappings](../../../../components/concepts/variables.md#output-mappings). For start events, this is often used to initialize process variables. ## Additional resources ### XML representation A none start event: ```xml ``` A none end event: ```xml ``` An intermediate none event: ```xml ``` --- ## Parallel gateway A parallel gateway (or AND-gateway) allows you to split the flow into concurrent paths. ![process](assets/parallel-gateways.png) When a parallel gateway with multiple outgoing sequence flows is entered, all flows are taken. The paths are executed concurrently and independently. The concurrent paths can be joined using a parallel gateway with multiple incoming sequence flows. The process instance waits at the parallel gateway until each incoming sequence is taken. :::note The outgoing paths of the parallel gateway are executed concurrently and not parallel in the sense of parallel threads. All records of a process instance are written to the same partition (single stream processor). ::: ## Additional resources ### XML representation A parallel gateway with two outgoing sequence flows: ```xml ``` --- ## Receive tasks Receive tasks reference a message; these are used to wait until a proper message is received. ![Receive Tasks](assets/receive-tasks.png) When a receive task is entered, a corresponding message subscription is created. The process instance stops at this point and waits until the message is correlated. A message can be published using one of the Zeebe clients. When the message is correlated, the receive task is completed and the process instance continues. :::note An alternative to receive tasks is [a message intermediate catch event](../message-events/message-events.md), which behaves the same way but can be used together with event-based gateways. ::: ## Messages A message can be referenced by one or more receive tasks; it must define the name of the message (e.g. `Money collected`) and the `correlationKey` expression (e.g. `= orderId`). Usually, the name of the message is defined as a [static value](/components/concepts/expressions.md#expressions-vs-static-values) (e.g. `order canceled`), but it can also be defined as [expression](/components/concepts/expressions.md) (e.g. `= "order " + awaitingAction`). The expression is evaluated on activating the receive task and must result in a `string`. The `correlationKey` is an expression that usually [accesses a variable](/components/modeler/feel/language-guide/feel-variables.md#access-variable) of the process instance that holds the correlation key of the message. The expression is evaluated on activating the receive task and must result either in a `string` or `number`. To correlate a message to the receive task, the message is published with the defined name (e.g. `Money collected`) and the value of the `correlationKey` expression. For example, if the process instance has a variable `orderId` with value `"order-123"`, the message is published with the correlation key `"order-123"`. ## Variable mappings Output variable mappings are used to customize how variables are merged into the process instance. These can contain multiple elements that specify which variables should be mapped. The `Process Variable Name` of an output denotes the variable name outside the activity. Visit our documentation on [input and output variable mappings](/components/concepts/variables.md#inputoutput-variable-mappings) for more information on this topic. ## Additional resources ### XML representation A receive task with message definition: ```xml ``` ### References - [Message correlation](/components/concepts/messages.md) - [Expressions](/components/concepts/expressions.md) - [Variable mappings](/components/concepts/variables.md#inputoutput-variable-mappings) - [Incidents](/components/concepts/incidents.md) --- ## Script tasks A script task is used to model the evaluation of a script; for example, a script written in Groovy, JavaScript, or Python. ![task](assets/script-task.png) :::info Camunda 8 supports alternative task implementations for the script task. To use your own implementation for a script task, refer to the [job worker implementation](#job-worker-implementation) section below. The sections before this job worker implementation apply to the [FEEL expression](/components/modeler/feel/language-guide/feel-expressions-introduction.md) implementation only. ::: When the process instance arrives at a script task, the integrated [FEEL Scala](https://github.com/camunda/feel-scala) engine evaluates the script task FEEL expression. Once the FEEL expression is evaluated successfully, the process instance continues. If the FEEL expression evaluation is unsuccessful, an [incident](/components/concepts/incidents.md) is raised at the script task. When the incident is resolved, the script task is evaluated again. ## Defining a script task {#defining-a-task} To define a script task with an inline FEEL expression, use the `zeebe:script` extension element. In the `zeebe:script` extension element, perform the following steps: 1. Define the **FEEL expression** inside the `expression` attribute. 2. Define the name of process variable in the `resultVariable` attribute. This variable will store the result of the FEEL expression evaluation. ### Variable mappings By default, the variable defined by `resultVariable` is merged into the process instance. This behavior can be customized by defining an output mapping at the script task. All variables in scope of the script task are available to the FEEL engine when the FEEL expression in the script task is evaluated. Input mappings can be used to transform the variables into a format accepted by the FEEL expression. :::info Input mappings are applied on activating the script task (or when an incident at the script task is resolved) before the FEEL expression evaluation. When an incident is resolved at the script task, the input mappings are applied again before evaluating the FEEL expression. This can affect the result of the FEEL expression evaluation. ::: For more information about this topic, visit the documentation about [input and output variable mappings](/components/concepts/variables.md#inputoutput-variable-mappings). ## Job worker implementation When the job worker implementation is used, script tasks behave exactly like [service tasks](/components/modeler/bpmn/service-tasks/service-tasks.md). Both task types are based on jobs and [job workers](/components/concepts/job-workers.md). The differences between these task types are the visual representation (i.e. the task marker) and the semantics for the model. When a process instance enters a script task using a job worker implementation, it creates a corresponding job and waits for its completion. A job worker should request jobs of this job type and process them. When the job is complete, the process instance continues. :::note Jobs for script tasks are not processed by Zeebe itself. To process them, provide a job worker. ::: ### Defining a job worker script task A script task must define a [job type](/components/modeler/bpmn/service-tasks/service-tasks.md#task-definition) the same way a service task does. It specifies the type of job workers should subscribe to (e.g. `script`). Use [task headers](/components/modeler/bpmn/service-tasks/service-tasks.md#task-headers) to pass static parameters to the job worker (e.g. the script to evaluate). The community extension [Zeebe Script Worker](https://github.com/camunda-community-hub/zeebe-script-worker) requires certain attributes to be set in the task headers. Define [variable mappings](/components/concepts/variables.md#inputoutput-variable-mappings) the [same way a service task does](/components/modeler/bpmn/service-tasks/service-tasks.md#variable-mappings) to transform the variables passed to the job worker, or to customize how the variables of the job merge. ## Additional resources :::tip Community Extension Review the [Zeebe Script Worker](https://github.com/camunda-community-hub/zeebe-script-worker). This is a community extension that provides a job worker to evaluate scripts. You can run it, or use it as a blueprint for your own job worker. ::: ### XML representation A script task with a custom header: ```xml ``` A script task with an inline FEEL expression: ```xml ``` ### References - [Job handling](/components/concepts/job-workers.md) - [Variable mappings](/components/concepts/variables.md#inputoutput-variable-mappings) --- ## Send tasks A send task is used to model the publication of a message to an external system; for example, to a Kafka topic or a mail server. ![task](assets/send-task.png) Send tasks behave exactly like [service tasks](/components/modeler/bpmn/service-tasks/service-tasks.md). Both task types are based on jobs and [job workers](/components/concepts/job-workers.md). The differences between these task types are the visual representation (i.e. the task marker) and the semantics for the model. When a process instance enters a send task, it creates a corresponding job and waits for its completion. A job worker should request jobs of this job type and process them. When the job is complete, the process instance continues. :::note Jobs for send tasks are not processed by Zeebe itself. To process them, provide a job worker. ::: ## Defining a task A send task must define a [job type](/components/modeler/bpmn/service-tasks/service-tasks.md#task-definition) the same way as a service task does. It specifies the type of job that workers should subscribe to (e.g. `kafka` or `mail`). Use [task headers](/components/modeler/bpmn/service-tasks/service-tasks.md#task-headers) to pass static parameters to the job worker (e.g. the name of the topic to publish the message to). Define [variable mappings](/components/concepts/variables.md#inputoutput-variable-mappings) the [same way as a service task does](/components/modeler/bpmn/service-tasks/service-tasks.md#variable-mappings) to transform the variables passed to the job worker, or to customize how the variables of the job merge. ## Additional resources :::tip Community Extension Review the [Kafka Connect Zeebe](https://github.com/camunda-community-hub/kafka-connect-zeebe). This is a community extension that provides a job worker to publish messages to a Kafka topic. You can run it, or use it as a blueprint for your own job worker. ::: ### XML representation A script task with a custom header: ```xml ``` ### References - [Job handling](/components/concepts/job-workers.md) - [Variable mappings](/components/concepts/variables.md#inputoutput-variable-mappings) --- ## Service tasks A service task represents a work item in the process with a specific type. ![process](../assets/order-process.png) When a service task is entered, a corresponding job is created. The process instance stops here and waits until the job is complete. A [job worker](/components/concepts/job-workers.md) can subscribe to the job type, process the jobs, and complete them using one of the Zeebe clients. When the job is complete, the service task is completed and the process instance continues. ## Task definition A service task must have a `taskDefinition`. The `taskDefinition` is used to specify which [job workers](../../../concepts/job-workers.md) handle the service task work. A `taskDefinition` specifies the following properties: - `type` (required): Used as reference to specify which job workers request the respective service task job. For example, `order-items`. - `type` can be specified as any [static value](/components/concepts/expressions.md#expressions-vs-static-values) (`myType`) or as a FEEL [expression](../../../concepts/expressions.md) prefixed by `=` that evaluates to any FEEL string; for example, `= "order-" + priorityGroup`. - `retries` (optional): Specifies the number of times the job is retried when a worker signals failure. The default is three. The expressions are evaluated on activating the service task and must result in a `string` for the job type and a `number` for the retries. Refer to an example in the form of the [XML representation](#xml-representation) below. ## Task headers A service task can define an arbitrary number of `taskHeaders`; they are static metadata handed to workers along with the job. The headers can be used as configuration parameters for the worker. ## Variable mappings By default, all job variables merge into the process instance. This behavior can be customized by defining an output mapping at the service task. Input mappings can be used to transform the variables into a format accepted by the job worker. For more information about this topic visit the documentation about [Input/output variable mappings](/components/concepts/variables.md#inputoutput-variable-mappings). ## Additional resources ### XML representation A service task with a custom header: ```xml ``` ## Next steps Learn more about the concept of job types and how to set up a job worker via our [manual on job workers](/components/concepts/job-workers.md). ### References - [Job handling](/components/concepts/job-workers.md) - [Expressions](/components/concepts/expressions.md) - [Variable mappings](/components/concepts/variables.md#inputoutput-variable-mappings) - [Incidents](/components/concepts/incidents.md) --- ## Signal events Signal events are events which reference a [signal](../../../concepts/signals.md). Broadcasting a signal will trigger _all_ signal events matching the name of the broadcasted signal. ![Process with multiple different signal events](assets/signal-events.png) Signal events are typically used if you want to communicate with multiple listeners. For intermediate events, a signal will trigger all process instances with a token waiting at a corresponding catch event, even across different processes. For start events, a signal will start one instance per process that has a corresponding signal start. Thus, signals form a single sender to several-recipient relationship. These differ from [message events](/components/modeler/bpmn/message-events/message-events.md), which are used if a single process instance needs to wait for a message from a secondary process or an external system. This is a single sender to a single recipient relationship (1:1), as the message cannot have more than one recipient. Both event types are methods of collaboration within BPMN. ## Signal start events Signal start events can be used to start process instances. Deploying several processes with a signal start event enables creation of multiple process instances by performing a single broadcast. Broadcasting a signal iterates over the available subscriptions. If the name of the broadcasted signal matches the name of the signal start event, the process instance is created. Signal subscriptions only exist for the latest version of a process definition. Deploying a new version of the same process (based on the BPMN process ID) will delete the old signal subscription. A new subscription is opened for the new deployed process definition. When the latest version of a process is deleted, the signal subscription is also deleted. If the previous version of the same process (based on the BPMN process ID) contains a signal start event, a new subscription is opened for it. ## Signal intermediate catch events When a signal intermediate catch event is entered, a signal subscription is created. The process instance stops at this point and waits until it is triggered by a broadcasted signal with the same name. Broadcasting a signal will iterate over the available subscriptions. If the name of the broadcasted signal matches the name of the signal subscription, it triggers the signal subscription. When the subscription is triggered, the corresponding signal catch event is completed and the process instance continues. ## Signal boundary events An activity can have one or more signal boundary events. Each of the signal events must have a unique signal name. When the activity is entered, it creates a signal subscription for each boundary signal event. If a non-interrupting boundary event is triggered, the activity is not terminated and multiple broadcasted signals can trigger the boundary events. ## Signal throw events A process can contain signal intermediate throw events or signal end events to model the broadcasting of a signal. When a signal throw events is entered, it broadcasts a signal that can trigger signal subscriptions. ## Signals In BPMN, a signal event references a `signal`. Signals can be referenced by one or more signal events. A signal must define a `name`. The value is used to determine: - The name of the signal to broadcast for a signal throw event. - The name of the signal to subscribe to for a signal catch event. Usually, the name of the signal is defined as a [static value](../../../concepts/expressions/#expressions-vs-static-values) (e.g. `order canceled`), but it can also be defined as an [expression](../../../concepts/expressions/) (e.g. `= "order " + awaitingAction`). If the expression belongs to a signal start event of the process, it is evaluated on deploying the process. Otherwise, it is evaluated on activating the signal event. The evaluation must result in a `string`. ## Variable mappings When broadcasting a signal you can pass along variables. By default, all signal variables are merged into the process instance. This behavior can be customized by defining an output mapping at the signal catch event. When a signal throw event broadcasts a signal, all local variables are passed along. You can use input mappings to define these local variables. For more information about variable scopes, visit the documentation about [variable scopes](../../../concepts/variables#variable-scopes). ## Additional resources ### XML representation A signal start event with signal definition: ```xml ``` A signal boundary event with signal definition: ```xml ``` A signal intermediate catch event with signal definition: ```xml ``` A signal end event with signal definition: ```xml ``` --- ## Overview(5) **Subprocesses** are element containers that allow defining common functionality. For example, you can attach an event to a subprocess's border. When the event is triggered, the subprocess is interrupted, regardless which of its elements is currently active. Currently supported elements: - [Embedded subprocess](embedded-subprocesses/embedded-subprocesses.md) - [Call activities](call-activities/call-activities.md) - [Event subprocess](event-subprocesses/event-subprocesses.md) - [Ad-hoc sub-process](ad-hoc-subprocesses/ad-hoc-subprocesses.md) --- ## Overview(6) The basic elements of BPMN processes are tasks; these are atomic units of work composed to create a meaningful result. Whenever a token reaches a task, the token stops and Zeebe creates a job and notifies a registered worker to perform work. When that handler signals completion, the token continues on the outgoing sequence flow. Choosing the granularity of a task is up to the person modeling the process. For example, the activity of processing an order can be modeled as a single _Process Order_ task, or as three individual tasks _Collect Money_, _Fetch Items_, _Ship Parcel_. If you use Zeebe to orchestrate microservices, one task can represent one microservice invocation. Currently supported elements: - [Service tasks](service-tasks/service-tasks.md) - [User tasks](user-tasks/user-tasks.md) - [Receive tasks](receive-tasks/receive-tasks.md) - [Business rule tasks](business-rule-tasks/business-rule-tasks.md) - [Script tasks](script-tasks/script-tasks.md) - [Send tasks](send-tasks/send-tasks.md) - [Manual tasks](manual-tasks/manual-tasks.md) - [Undefined tasks](undefined-tasks/undefined-tasks.mdx) --- ## Terminate events Terminate end events are the only kind of terminate events. When a process instance reaches a terminate end event, it terminates all element instances in the same flow scope as the end event. They are often used to terminate a concurrent flow that is not required anymore. Consider the following example. ![The process instance reached the terminate end event and canceled the concurrent flow.](assets/terminate-event-on-process-scope.png) The process has two concurrent tasks `B` and `C`. In the process instance, both tasks are active. We complete the task `C`. The process instance reaches the terminate end event and cancels the task `B`. ## On the process scope A terminate end event on the process scope (i.e. not embedded in a subprocess) terminates all element instances of the process instance. After the termination, the process instance completes. If the process instance was created by a call activity from a parent process then the call activity completes and the parent process instance takes the outgoing sequence flows. ## Inside a subprocess A terminate end event inside an embedded or an event subprocess terminates all element instances of the subprocess. After the termination, the subprocess completes, and the process instance takes the outgoing sequence flows. The terminate end event is limited to its subprocess. It doesn't terminate element instances outside the subprocess. ![The process instance reached the terminate end event in the subprocess and canceled the concurrent task in the subprocess. The process instance took the outgoing sequence flow of the subprocess.](assets/terminate-end-event-inside-subprocess.png) If the subprocess is a multi-instance then the terminate end event terminates only the element instances of the current iteration. It doesn't terminate element instances of other multi-instance iterations. ## Additional resources ### XML representation A terminate end event: ```xml Flow_0zv9prm ``` --- ## Timer events Timer events are events triggered by a defined timer. ![process](assets/timer-events.png) ## Timer start events A process can have one or more timer start events (besides other types of start events). Each of the timer events must have either a time date or time cycle definition. When a process is deployed, it schedules a timer for each timer start event. Scheduled timers of the previous version of the process (based on the BPMN process ID) are canceled. When a timer is triggered, a new process instance is created and the corresponding timer start event is activated. ## Intermediate timer catch events An intermediate timer catch event can either be a time duration, or a time date. When an intermediate timer catch event is entered, a corresponding timer is scheduled. The process instance stops at this point and waits until the timer is triggered. When the timer is triggered, the catch event is completed and the process instance continues. ## Timer boundary events An interrupting timer boundary event must have a time duration, or a time date definition. When the corresponding timer is triggered, the activity is terminated. Interrupting timer boundary events are often used to model timeouts; for example, canceling the processing after five minutes and doing something else. A non-interrupting timer boundary event must have either a time duration, a time cycle definition, or a time date definition. When the activity is entered, it schedules a corresponding timer. If the timer is triggered and defined as time cycle with repetitions greater than zero, it schedules the timer again until the defined number of repetitions is reached. It's important to note that a non-interrupting timer boundary event that's defined with a time duration will only trigger a single time once the date is reached. Non-interrupting timer boundary events are often used to model notifications; for example, contacting support if the processing takes longer than an hour. ## Timers Timers must be defined by providing either a date, a duration, or a cycle. A timer can be defined either as a [static value](/components/concepts/expressions.md#expressions-vs-static-values) (e.g. `P3D`) or as an [expression](/components/concepts/expressions.md). There are two common ways to use an expression: - [Access a variable](/components/modeler/feel/language-guide/feel-variables.md#access-variable) (e.g. `= remainingTime`). - [Use temporal values](/components/concepts/expressions.md#temporal-expressions) (e.g. `= date and time(expirationDate) - date and time(creationDate)`). If the expression belongs to a timer start event of the process, it is evaluated on deploying the process. Otherwise, it is evaluated on activating the timer catch event. The evaluation must result in either a `string` that has the same ISO 8601 format as the static value, or an equivalent temporal value (i.e. a date-time, a duration, or a cycle). :::note Zeebe is an asynchronous system. As a result, there is no guarantee a timer triggers exactly at the configured time. Depending on how much load the system is under, timers could trigger later than their due date. However, timers will never trigger earlier than the due date. ::: ### Time date If the date is in the past at the time of deployment, the timer fires immediately. ### Time duration A duration is defined as a ISO 8601 durations format, which defines the amount of intervening time in a time interval and are represented by the format `P(n)Y(n)M(n)DT(n)H(n)M(n)S`. Note that the `n` is replaced by the value for each of the date and time elements that follow the `n`. The capital letters _P_, _Y_, _M_, _W_, _D_, _T_, _H_, _M_, and _S_ are designators for each of the date and time elements and are not replaced, but can be omitted. - _P_ is the duration designator (for period) placed at the start of the duration representation. - _Y_ is the year designator that follows the value for the number of years. - _M_ is the month designator that follows the value for the number of months. - _W_ is the week designator that follows the value for the number of weeks. - _D_ is the day designator that follows the value for the number of days. - _T_ is the time designator that precedes the time components of the representation. - _H_ is the hour designator that follows the value for the number of hours. - _M_ is the minute designator that follows the value for the number of minutes. - _S_ is the second designator that follows the value for the number of seconds. Examples: - `PT15S` - 15 seconds - `PT1H30M` - 1 hour and 30 minutes - `P14D` - 14 days - `P14DT1H30M` - 14 days, 1 hour and 30 minutes - `P3Y6M4DT12H30M5S` - 3 years, 6 months, 4 days, 12 hours, 30 minutes and 5 seconds If the duration is zero or negative, the timer fires immediately. ### Time cycle A cycle defined as ISO 8601 repeating intervals format; it contains the duration and the number of repetitions. If the repetitions are not defined, the timer repeats infinitely until it is canceled. - `R5/PT10S`: Every 10 seconds, up to five times - `R/P1D`: Every day, infinitely It's possible to define a start time. By doing this, the timer triggers for the first time on the given start time. Afterwards, it will follow the interval as usual. - `R3/2022-04-27T17:20:00Z/P1D`: Every day up to three times, starting from April 27, 2022 at 5:20 p.m. UTC - `R/2022-01-01T10:00:00+02:00[Europe/Berlin]/P1D`: Every day infinitely, starting from January 1, 2022 at 10 a.m. UTC plus 2 hours If the start time is in the past at the time of deployment, the timer fires immediately upon deployment, and then continues with the regular interval from that point on. Additionally, you can specify a time cycle using cron expressions. Refer to the [CronExpression Tutorial](https://spring.io/blog/2020/11/10/new-in-spring-5-3-improved-cron-expressions) for additional information about using cron expressions. - `0 0 9-17 * * MON-FRI`: Every hour on the hour from 9-5 p.m. UTC Monday-Friday ## Additional resources ### XML representation A timer start event with time date: ```xml 2019-10-01T12:00:00Z ``` An intermediate timer catch event with time duration: ```xml PT10M ``` A non-interrupting boundary timer event with time cycle: ```xml R3/PT1H ``` ### References - [Expressions](/components/concepts/expressions.md) - [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) --- ## Undefined tasks An undefined task (also known as abstract task) defines a task for which the type of work is unspecified. This is used to model work done by someone the engine does not need to know of and there is no known system or UI interface. It is also used when modeling a process that is not automated, or while the process automation is in development. For the engine, an undefined task is handled as a pass-through activity, automatically continuing the process at the moment the process instance arrives. Undefined tasks have no real benefit for automating processes, and instead provide insights into the tasks that are performed outside of the process engine. :::tip modeling processes for automation Both Web Modeler and Desktop Modeler default to undefined tasks when adding tasks to your process. You can change the task type by clicking on it and selecting the **Change element** menu icon. ::: ## Additional resources ### XML representation An undefined task: ```xml ``` --- ## User tasks A user task is used to model work that needs to be done by a human and is assisted by a workflow engine or software application. This differs from [manual tasks](/components/modeler/bpmn/manual-tasks/manual-tasks.md), which are not assisted by external tooling. When the process instance arrives at a user task, a new user task instance is created at Zeebe. The process instance stops at this point and waits until the user task instance is completed. When the user task instance is completed, the process instance continues. ![user-task](assets/user-task.png) ## User task implementation types A default user task implementation type is the **Camunda user task** with the `zeebe:userTask` extension element. It is the recommended implementation type that is introduced on Camunda version 8.6. Alternatively, user tasks can be implemented with **Job workers** by removing the `zeebe:userTask` extension element. Refer to the [Job worker implementation](#job-worker-implementation) section for details. ## Camunda user tasks Camunda user tasks support assignments, scheduling, task updates, variable mappings, and a form for a user task as detailed in the following sections. :::note The Camunda user task implementation type was previously referred to as the **Zeebe user task**. ::: ### Assignments User tasks support specifying assignments, using the `zeebe:AssignmentDefinition` extension element. You can use this to define which user the task can be assigned to. You can specify one or all of the following attributes simultaneously: - `assignee`: Specifies the user assigned to the task. [Tasklist](/components/tasklist/introduction-to-tasklist.md) will claim the task for this user. - `candidateUsers`: Specifies the users that the task can be assigned to. - `candidateGroups`: Specifies the groups of users that the task can be assigned to. :::info Usernames and group IDs in the Orchestration Cluster are case-sensitive. When you set `assignee`, `candidateUsers`, or `candidateGroups`, always use the exact value from your identity provider or Identity user record, including case. For example, `abc@example.com` and `Abc@example.com` are treated as different users. Candidate users and candidate groups are not used by current Tasklist releases for task visibility or assignment. Use [user task authorization](/components/tasklist/user-task-authorization.md) and [authorization-based access control](/components/concepts/access-control/authorizations.md) to control who can see and work on a task. ::: Typically, the assignee, candidate users, and candidate groups are defined as [static values](/components/concepts/expressions.md#expressions-vs-static-values) (e.g. `some_username`, `some_username, another_username` and `sales, operations`), but they can also be defined as [expressions](/components/concepts/expressions.md) (e.g. `= book.author` and `= remove(reviewers, book.author)` and `= reviewer_roles`). The expressions are evaluated on activating the user task and must result in a `string` for the assignee and a `list of strings` for the candidate users and a `list of strings` for the candidate groups. For [Tasklist](/components/tasklist/introduction-to-tasklist.md) to claim the task for a known Tasklist user, the value of the `assignee` must be the user's **unique identifier**. The unique identifier depends on the authentication method used to login to Tasklist: - Camunda 8 (login with email, Google, GitHub): `email` - Default Basic authentication (Elasticsearch): `username` - IAM: `username` These assignees are not related to user restrictions, which is related to the visibility of the task in Tasklist for Self-Managed. :::note For example, say you log into Tasklist using Camunda 8 login with email using your email address `foo@bar.com`. Every time a user task activates with `assignee` set to value `foo@bar.com`, Tasklist automatically assigns it to you. You'll be able to find your new task under the task dropdown option `Assigned to me`. ::: ### Scheduling User tasks support specifying a task schedule using the `zeebe:taskSchedule` extension element. You can use this to define when users interact with a given task. You can specify one or both of the following attributes simultaneously: - `dueDate`: Specifies the due date of the user task. - `followUpDate`: Specifies the follow-up date of the user task. :::note For example, you can use the `followUpDate` to define the latest time a user should start working on a task, and then use the `dueDate` to provide a deadline when the user task should be finished. ::: You can define the due date and follow-up date as static values (for example, `2023-02-28T13:13:10+02:00`) or [expressions](/components/concepts/expressions.md) (for example, `= schedule.dueDate` and `= now() + duration("PT15S")`). The expressions are evaluated on activating the user task and must result in a `string` conforming to an ISO 8601 combined date and time representation. :::info ::: ### Define user task priority You can use the `zeebe:priorityDefinition` extension element to specify the priority of a user task. This allows you to prioritize the user task relative to other tasks within the same process, as well as across different processes. To set the priority of a user task, specify the priority in the `priority` attribute. - The priority must be an integer between `0` and `100`. If no value is provided, the default value is `50`. - A higher priority value indicates higher importance. - You can set the priority either as a static integer value or by using an [expression](/components/concepts/expressions.md). Expressions are evaluated when the user task is activated and must result in an integer within the specified range. ### Variable mappings By default, all Camunda user task variables are merged into the process instance. This behavior can be customized by defining an output mapping at the user task. Input mappings can be used to transform the variables into a different format. ### User task forms A user task typically includes a form. A form contains work instructions for the user and captures the resulting information in a structured way. However, user tasks are not limited to forms. User tasks can also be used to refer users to other applications or redirect them to a website. You can use [Camunda Forms](/components/modeler/forms/utilizing-forms.md) that offer visual editing of forms directly in Camunda Modeler, or use your own forms. Forms can either be displayed in [Tasklist](/components/tasklist/introduction-to-tasklist.md), or handled by a custom application. To use a form, a user task requires a form reference. Depending on your use case, two different types of form references can be used: 1. **Camunda Forms** provide a flexible way of linking a user task to a Camunda Form via the form ID. Forms linked this way can be deployed together with the referencing process models. To link a user task to a Camunda Form, you have to specify the ID of the Camunda Form as the `formId` attribute of the task's `zeebe:formDefinition` extension element (see the [XML representation](#camunda-form)). The `bindingType` attribute determines which version of the linked form is used: - `latest`: The latest deployed version at the moment the user task is activated. - `deployment`: The version that was deployed together with the currently running version of the process. - `versionTag`: The latest deployed version that is annotated with the version tag specified in the `versionTag` attribute. To learn more about choosing binding types, see [choosing the resource binding type](/components/best-practices/modeling/choosing-the-resource-binding-type.md). :::note If the `bindingType` attribute is not specified, `latest` is used as the default. ::: You can read more about Camunda Forms in the [Camunda Forms guide](/components/modeler/forms/utilizing-forms.md) or the [Camunda Forms reference](/components/modeler/forms/camunda-forms-reference.md) to explore all configuration options for form elements. 2. A **custom form reference** can specify any custom identifier in the user task using the `externalReference` attribute of the task's `zeebe:formDefinition` extension element (see the [XML representation](#camunda-form-linked)). How the identifier is interpreted depends on your implementation. You can use it to associate a custom form, route to a custom application, or a URL to a web page, for example. A custom form reference will not be shown in Tasklist. :::info For user tasks with a [job worker implementation](#job-worker-implementation), the custom form references are defined on the `formKey` attribute of the `zeebe:formDefinition` extension element instead of the `externalReference` attribute. Furthermore, there is a third form option for job worker-based user tasks: embedded Camunda Forms. You can use them to embed a form's JSON configuration directly into the BPMN process XML as a `zeebe:UserTaskForm` extension element of the process element. The embedded form can then be referenced via the `formKey` attribute (see [XML representation](#camunda-form-embedded)). ::: ### Task headers A user task can define an arbitrary number of `taskHeaders`; they are static metadata stored with the user task in Zeebe. The headers can be used as configuration parameters for Tasklist applications. ### User task listeners User tasks support **user task listeners**, which allow you to react to user task lifecycle events. To define a user task listener, include the `zeebe:taskListeners` extension element within the user task in your BPMN model. This element can contain one or more `zeebe:taskListener` elements, each specifying the following attributes: - The `eventType` (required) that triggers the listener. Possible values are: `creating`, `assigning`, `updating`, `completing`, `canceling`. - The `type` (required) of the listener. Used as a reference to specify which job workers request the respective task listener job. For example, `order-items`. `type` can be specified as any static value (`myType`) or as a FEEL expression prefixed by `=` that evaluates to any FEEL string; for example, `= "order-" + priorityGroup`. - The number of `retries` (optional) for the user task listener job (defaults to 3 if omitted). For more details, see [user task listeners](components/concepts/user-task-listeners.md). ## Job worker implementation A user task does not have to be managed by Zeebe. You can implement custom user task logic using Job workers. To define a Job worker implementation for a user task, simply remove the `zeebe:userTask` extension element from the task. User tasks implemented via Job workers behave similarly to service tasks, with two key differences: - Visual representation: The visual marker distinguishes user tasks from service tasks. - Model semantics: The interpretation and purpose in the process model differ. :::info The job worker implementation for user tasks is deprecated. We recommend using [Camunda user tasks](#camunda-user-tasks) instead for enhanced functionality and adherence to best practices. For a detailed comparison of task implementation types and guidance on migrating to Camunda user tasks, see the [migration guide](/apis-tools/migration-manuals/migrate-to-camunda-user-tasks.md). ::: When a process instance reaches a user task with a Job worker implementation: 1. Zeebe creates a corresponding job and waits for its completion. 2. A Job worker processes jobs of the type io.camunda.zeebe:userTask. 3. Once the job is completed, the process instance resumes execution. Use [task headers](/components/modeler/bpmn/service-tasks/service-tasks.md#task-headers) to pass static parameters to the job worker. Define [variable mappings](/components/concepts/variables.md#inputoutput-variable-mappings) the [same way as a service task does](/components/modeler/bpmn/service-tasks/service-tasks.md#variable-mappings) to transform the variables passed to the job worker, or to customize how the variables of the job merge. ### Limitations User tasks implemented using Job workers come with significant limitations when compared to [Camunda user tasks](#camunda-user-tasks): 1. **API compatibility**: You cannot use the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) to manage user tasks based on job workers. These tasks are restricted to the functionality provided for [service tasks](/components/modeler/bpmn/service-tasks/service-tasks.md). 2. **Lifecycle management**: The Zeebe engine provides no visibility into lifecycle and state management features of Job worker-based user tasks. This means that you must handle these aspects in your custom application, outside the engine. Consider this approach only if your use case requires a highly specific user task implementation that cannot be achieved with Camunda user tasks. 3. **Reduced metrics and reporting**: Metrics and reporting for such user tasks are limited to the capabilities available for service tasks. This means you lose access to user task-specific insights provided by the Zeebe engine. 4. **Task-specific operations**: Assignments, scheduling, and other user task-specific details are included in the job as [task headers](/components/modeler/bpmn/service-tasks/service-tasks.md#task-headers). These must be handled in your custom implementation. Advanced user task features offered by Camunda are not available for Job worker-based user tasks. ## Additional resources ### XML representations #### Camunda Form A user task with a linked Camunda Form that does not specify the binding type (`latest` is used implicitly) as well as an assignment definition and a task schedule: ```xml ``` A user task with a linked Camunda Form that uses the `deployment` binding type: ```xml ``` A user task with a linked Camunda Form that uses the `versionTag` binding type: ```xml ``` #### Custom form reference A user task with an external task form referenced by a custom form reference: ```xml ``` :::info If you choose the [job worker implementation](#job-worker-implementation) for a user task, the custom form reference needs to be set to the `formKey` attribute instead of the `externalReference` attribute. ::: #### Camunda Form (embedded) :::info This is only supported if you choose the [job worker implementation](#job-worker-implementation) for a user task. ::: A job-based user task with an embedded Camunda Form: ```xml ``` #### User task listeners A user task with user task listeners configured: ```xml ``` ### References - [Tasklist](/components/tasklist/introduction-to-tasklist.md) - [Form linking in Modeler](/components/hub/workspace/modeler/modeling/advanced-modeling/form-linking.md) - [Job handling](/components/concepts/job-workers.md) - [Variable mappings](/components/concepts/variables.md#inputoutput-variable-mappings) - [User task listeners](/components/concepts/user-task-listeners.md) --- ## Data handling Process data in Camunda is represented by [variables](/components/concepts/variables.md). A variable has a name and a JSON value, and its visibility is determined by its [variable scope](/components/concepts/variables.md#variable-scopes). BPMN processes do not have an explicit data model or schema. Instead, variables are created implicitly during process execution — for example, when a process instance is started with variables, when a [job worker](/components/concepts/job-workers.md) completes a job, when a message is correlated, or when [input/output variable mappings](/components/concepts/variables.md#inputoutput-variable-mappings) are applied. Because there is no formal schema, the modeler infers the available variables by analyzing what is defined in the process diagram itself. This page explains how that works, and how you can help the modeler provide better editor support. ## How the modeler discovers variables The modeler scans the process diagram and collects variables from the definitions it finds, such as: - Input and output mappings defined on elements (the `target` expressions create variables). - Result variables and result expressions on [service tasks](/components/modeler/bpmn/service-tasks/service-tasks.md), [business rule tasks](/components/modeler/bpmn/business-rule-tasks/business-rule-tasks.md), and [script tasks](/components/modeler/bpmn/script-tasks/script-tasks.md). - Form field bindings on [user tasks](/components/modeler/bpmn/user-tasks/user-tasks.md) linked to [Camunda Forms](/components/modeler/forms/camunda-forms-reference.md). - Example data added to elements (see [Defining example data](#defining-example-data) below). The discovered variables are used in the following places: - FEEL editor suggestions — when writing [expressions](/components/concepts/expressions.md), the FEEL editor suggests variables in scope for the current element. - The **Variables** panel — an overview of all variables found in the diagram (see [Inspecting variables](#inspecting-variables)). :::note The modeler can only discover variables that are explicitly defined in the diagram. Variables created at runtime — for example, by [job workers](/components/concepts/job-workers.md), passed as process start variables, or set through the API — are **not** visible in the modeler unless you define them explicitly using [example data](#defining-example-data). ::: ## Inspecting variables The **Variables** panel helps you explore the variables in your process. Use it to understand which variables are visible in a given scope, where they are written, and what values they hold. ![Variables panel](img/variables-panel.png) ### Selecting elements The list of variables shown in the panel depends on the element or elements you have selected on the canvas. When you select one or more elements, the panel displays all variables in the scope of those elements, including variables from parent scopes (for example, a sub-process scope or the process scope). If no element is selected, the panel shows variables at the process level. ### Filtering variables Use the search bar at the top of the panel to filter the displayed list of variables by name. To show only the variables written by the selected elements, enable the **Written by selection** toggle. ### Variable details Expand a variable to see its details: - **Written by** — lists all elements in the diagram that write to this variable. - **Value** — shows the value of the variable, if it can be determined from the diagram. ## Defining example data Camunda 8 only To help with editor support, you can add example data to an element. Add a JSON return value in the **Data** section of the properties panel. The values are used to derive variable names and types in the FEEL editor. Nested objects are also supported. Providing this data is optional, but it's recommended if you want to take full advantage of the FEEL editor's suggestions. It is especially useful for variables that the modeler cannot discover automatically, such as variables created by [job workers](/components/concepts/job-workers.md) or passed as process start variables. This data will also be used while [playing your process](/components/hub/workspace/modeler/validation/play-your-process.md) to set variables from the respective elements when performing the following actions: - Starting a new instance - Completing a job - Publishing a message with variables :::note The provided example data is only used by the FEEL editor to provide variable suggestions while modeling, and by Play to prefill variables. It is not used during process execution. ::: ![Variable suggestions with example data](img/example-data.png) Data provided this way is added to the scope of the element. To use the data in other parts of your process, you can use [output mappings](/components/concepts/variables.md#output-mappings) to make the variables available in the parent scope. ## Next steps - Learn about [variables](/components/concepts/variables.md), including variable scopes, propagation, and input/output variable mappings. - Explore how to [access variables in expressions](/components/modeler/feel/language-guide/feel-variables.md). --- ## Connect to Camunda 8 To deploy diagrams, start process instances, or test tasks, you must first connect Desktop Modeler to a Camunda 8 Orchestration Cluster. Follow the steps below to connect to **Camunda 8 SaaS**. To connect to a local installation, visit the [Camunda 8 Self-Managed guide](../../../self-managed/components/modeler/desktop-modeler/connect-to-self-managed.md). 1. Click the connection selector. For new installations, this will show **No connection**. If you have previously selected a connection, it will show the name of that connection. ![No connection button in status bar](./img/connection-selector-offline.png) 2. To add a new connection, open the settings. You can either click **Manage connections**, or open the settings directly (Cmd/Ctrl + ,). ![Showing a connection selection popup](./img/connection-selector-offline-open.png) By default, a local c8run connection is already set up. If you have previously used Desktop Modeler to deploy diagrams, those connections will also be available under **Unnamed Connection**. 3. Click **Add connection**. ![Connection manager showing add button](./img/connection-manager-add.png) 4. Enter a name, the cluster URL, and the credentials (client ID and client secret) for your [API client](../../hub/organization/manage-clusters/manage-api-clients.md). ![Connection manager with new connection information filled in](./img/connection-manager-new-connection-loading.png) Desktop Modeler automatically validates the connection. If you have issues connecting to the cluster, see the [troubleshooting page](./troubleshooting.md#debug-zeebe-connection-issues). ![Connection manager showing connection error](./img/connection-manager-new-connection-error.png) If the connection is established successfully, you can go back to the connection selection, where your new connection is now available. ![Connection manager showing connection success](./img/connection-manager-new-connection-success.png) 5. Select the connection you just created to use it for [deployment](./deploy-diagram.md), [starting a new process instance](./start-instance.md), or [task testing](./task-testing.md). ![New connection selected in connection manager](./img/connection-selector-new-connection.png) :::note As a next step, [deploy your diagram](./deploy-diagram.md). ::: --- ## Custom lint rules Through Camunda Modeler plugins, you can add custom lint rules and configure or disable existing rules. [`bpmnlint`](https://github.com/bpmn-io/bpmnlint) is used to validate BPMN diagrams, so the plugins have to be [`bpmnlint` plugins](https://github.com/bpmn-io/bpmnlint#writing--consuming-custom-rules) at the core. ## Getting started Get started with the [custom-linter-rules-plugin template](https://github.com/camunda/camunda-modeler-custom-linter-rules-plugin) and take the following steps: 1. Clone or fork the repository: ``` git clone https://github.com/camunda/camunda-modeler-custom-linter-rules-plugin.git ``` The plugin starter project comes with a client folder referenced in the [plugin entry point](../plugins#plugin-entry-point). It contains the script that adds a `bpmnlint` plugin to the modeler. Since this is a [client plugin](../plugins#extend-the-modeler-and-its-bpmn-and-dmn-components), you must bundle it. 2. Install the dependencies with the following command: ``` npm install ``` 3. Add a custom rule. To do this, add it to the [`bpmnlint-plugin-custom/rules`](https://github.com/camunda/camunda-modeler-custom-linter-rules-plugin/tree/master/bpmnlint-plugin-custom/rules) folder. The example project contains a [`no-manual-task.js`](https://github.com/camunda/camunda-modeler-custom-linter-rules-plugin/blob/master/bpmnlint-plugin-custom/rules/no-manual-task.js) file which implements a custom rule. Every rule must export a function that returns an object with a `check` function: ```javascript /** * Rule that reports manual tasks being used. */ module.exports = function () { function check(node, reporter) { if (is(node, "bpmn:ManualTask")) { reporter.report(node.id, "Element has disallowed type bpmn:ManualTask"); } } return { check: check, }; }; ``` This function is called for every node when `bpmnlint` traverses the [model](https://github.com/bpmn-io/bpmn-moddle/). 4. Change the configuration. Through the configuration in [.bpmnlintrc](https://github.com/camunda/camunda-modeler-custom-linter-rules-plugin/blob/master/.bpmnlintrc), you can add the custom rules you implemented in [`bpmnlint-plugin-custom/rules`](https://github.com/camunda/camunda-modeler-custom-linter-rules-plugin/tree/master/bpmnlint-plugin-custom/rules). ```javascript { "extends": [ "bpmnlint:recommended", "plugin:custom/recommended" ], "rules": { "label-required": "off", "custom/no-manual-task": "warn" } } ``` The example configuration adds all rules specified in the [`recommended`](https://github.com/camunda/camunda-modeler-custom-linter-rules-plugin/blob/master/bpmnlint-plugin-custom/index.js) configuration of the `bpmnlint` plugin. It also adds [all the rules that come with `bpmnlint`](https://github.com/bpmn-io/bpmnlint/tree/master/rules) and configures two rules. 5. Bundle your plugin by running the following command: ``` npm run build ``` The custom lint rules and configuration will be used when validating a BPMN diagram. ![Camunda Modeler with custom lint rule](./img/custom-lint-rule.png) ## Additional resources - [Example plugin](https://github.com/camunda/camunda-modeler-custom-linter-rules-plugin) - [bpmnlint](https://github.com/bpmn-io/bpmnlint) --- ## Deploy a diagram Desktop Modeler can directly deploy diagrams and start process instances in Camunda 8. Follow the steps below to deploy a diagram to a Camunda 8 Orchestration Cluster. 1. Set up and select a connection by following [Connect to Camunda 8](./connect-to-camunda-8.md) guide. ![Dev cluster connected](./img/connection-selector-dev-connection.png) 2. Click the **Deploy** icon: ![deploy icon](./img/deploy-icon.png) 3. Click **Deploy** to perform the actual deployment. ![deploy diagram](./img/deploy-diagram.png) 4. If the deployment is successful, you will see a confirmation message: ![deployment successful](./img/deploy-diagram-success.png) :::note As a next step, [start a new process instance](./start-instance.md). ::: --- ## Configuring templates [Element templates](/components/modeler/element-templates/about-templates.md) are loaded by Desktop Modeler at application startup. Reloading it using `Cmd+R` or `Ctrl+R` reloads all templates. Templates are treated as global or local depending on their location in your file system. ## Global templates For templates to be available for all diagrams store them in the `resources/element-templates` directory containing the Camunda Modeler executable. Alternatively, for element templates to be available across Camunda Modeler installations, you can store them in the `resources/element-templates` directory in the modeler's [user data directory](../../search-paths#user-data-directory). ### Examples ``` └── camunda-modeler-5.10.0-win-x64 ├── Camunda Modeler.exe └── resources └── element-templates └── my-element-templates.json ``` :::note On macOS, the Camunda Modeler is a self-contained `.app` bundle, which makes it difficult to add files to its installation directory. Therefore, we recommend storing [global templates](#global-templates) in the [user data directory](../../search-paths#user-data-directory). ::: ``` └── ~/Library/Application Support └── camunda-modeler └── resources └── element-templates └── my-element-templates.json ``` ``` └── camunda-modeler-5.10.0-linux-x64 ├── camunda-modeler └── resources └── element-templates └── my-element-templates.json ``` ## Local templates For element templates to only be available for specific diagrams, you can store them in a `.camunda/element-templates` directory in the diagrams parent directory or any of their parent directories. ### Example ``` ├── diagram.bpmn └── .camunda └── element-templates └── my-element-templates.json ``` Learn more about search paths [here](../../search-paths). --- ## Using templates(Element-templates) ## Applying templates If an [element template](/components/modeler/element-templates/about-templates.md) matches a selected diagram element, the template catalog button is shown in the properties panel on the right side of the screen. ![Template Chooser](./img/chooser.png) Clicking **Select** opens a modal menu, allowing you to browse and search available templates for the selected element. ![Modal Menu](./img/modal.png) Applying a template stores it via the `modelerTemplate` property and the optional `modelerTemplateVersion` property on the selected element: ```xml ``` It also sets up custom fields on the diagram element and makes these available for inspection and editing. Properties which were not configured in the element template using custom fields will not be available for editing. ## Removing templates To remove an applied template from an element, either the _Unlink_ or _Remove_ function can be used: - **Remove**: Remove the element template from the `modelerTemplate` property and reset all properties of the respective element. - **Unlink**: Remove the element template from the `modelerTemplate` property but keep the properties which were set. ![Unlink or Remove](./img/unlink-remove.png) ## Updating templates If a template is applied and a new version of the template is found you can _update_ the template. ![Update Template](./img/update-template.png) Templates are updated according to the following rules: - If the property is set in the new template, it will override the existing value — unless the value was originally set by the old template and has been manually changed since. - If the property is not defined in the new template, it will unset. - Sub-properties of complex properties (for example, `zeebe:input`, `zeebe:output`) are handled according to these rules if they can be identified. ### Replacing templates If a template is deprecated with a new element template and you want to keep the same input values as in the deprecated template, you can: 1. **Unlink**: Remove the current template that is deprecated from the `modelerTemplate` property, but keep the properties which were set. 2. **Select** and apply the new element template. ## Missing templates If a template is applied to an element but the respective template cannot be found on the system, the editing of the element is disabled. _Unlinking_ or _removing_ the template for the element or adding the element template config enables the editing again. ![Template not Found](./img/template-not-found.png) --- ## Flags Flags allow you to control the availability of certain features within Desktop Modeler. Learn which flags [are available](#available-flags) and how to [configure them](#configuration). ## Configuration You may configure flags in a `flags.json` file or pass them via CLI. ### Configuration via `flags.json` :::note Configuration changes via `flags.json` will only take effect once you restart the application. ::: Place a `flags.json` file inside the `resources` folder of your local [`{USER_DATA}`](../search-paths#user-data-directory) or [`{APP_DATA_DIRECTORY}`](../search-paths#app-data-directory) directory to persist them. ### Configuration via command line Pass flags via the command line when starting the application. ```plain "Camunda Modeler.exe" --disable-plugins ``` ```plain camunda-modeler --disable-plugins ``` ```plain camunda-modeler --disable-plugins ``` Flags passed as command line arguments take precedence over those configured via a configuration file. ## Available flags | flag | default value | | ------------------------------------------------------------- | ----------------------------------- | | ["disable-plugins"](#disable-plug-ins) | false | | "disable-adjust-origin" | false | | "disable-dmn" | false | | "disable-form" | false | | "disable-rpa" | false | | ["disable-httl-hint"](#disable-history-time-to-live-hint) | false | | ["default-httl"](#default-history-time-to-live) | false | | "disable-platform" | false | | "disable-zeebe" | false | | "disable-remote-interaction" | false | | "single-instance" | false | | "user-data-dir" | [Electron default](../search-paths) | | ["display-version"](#custom-display-version-label) | `undefined` | | ["zeebe-ssl-certificate"](#zeebe-ssl-certificate) | `undefined` | | ["c7-engine-version"](#default-execution-platform-version) | `undefined` | | ["c8-engine-version"](#default-execution-platform-version) | `undefined` | | ["enable-new-context-pad"](#enable-new-context-pad) | `false` | | ["disable-connector-templates"](#disable-connector-templates) | `false` | ## Examples ### Disable plug-ins Start the modeler without activating installed plug-ins. This is useful to debug modeler errors. ### BPMN-only mode To disable the DMN, Form, and RPA script editing capabilities of the App, configure your `flags.json` like this: ```js { "disable-dmn": true, "disable-form": true } ``` As a result, the app will only allow users to model BPMN diagrams. ![BPMN only mode](./img/bpmn-only.png) ### Disable `history-time-to-live` hint Camunda 7 only To disable the [history time to live](https://docs.camunda.org/manual/latest/modeler/history-time-to-live/) hint in scenarios where the engine configures HTTL, configure `flags.json`: ```js { "disable-httl-hint": true } ``` ### Default `history-time-to-live` Camunda 7 only To set a default [history time to live](https://docs.camunda.org/manual/latest/modeler/history-time-to-live/) value to be used in newly created models, configure `flags.json`: ```js { "default-httl": 30 } ``` ### Custom `display-version` label To display a custom version information in the status bar of the app, configure `flags.json`: ```js { "display-version": "1.0.0" } ``` ![Custom version info](./img/display-version.png) ### Zeebe SSL certificate Camunda 8 only > :information_source: Modeler will read trusted certificates from your operating system's trust store. Provide additional certificates to validate secured connections to a Camunda 8 installation. Configure your `flags.json`: ```js { "zeebe-ssl-certificate": "C:\\path\\to\\certs\\trusted-custom-roots.pem" } ``` Additional information adapted from the [upstream documentation](https://nodejs.org/docs/latest/api/tls.html#tlscreatesecurecontextoptions): > The peer (Camunda 8) certificate must be chainable to a CA trusted by the app for the connection to be authenticated. When using certificates that are not chainable to a well-known CA, the certificate's CA must be explicitly specified as trusted or the connection will fail to authenticate. If the peer uses a certificate that doesn't match or chain to one of the default CAs, provide a CA certificate that the peer's certificate can match or chain to. For self-signed certificates, the certificate is its own CA, and must be provided. ### Default execution platform version This setting controls the [execution platform version](/reference/glossary.md#execution-platform-version) used by Desktop Modeler. It does not change the version of a deployed cluster or process definition. To change default execution platform version, configure your `flags.json` as follows: ```json { "c7-engine-version": "7.18.0", "c8-engine-version": "8.0.0" } ``` New diagrams created in Desktop Modeler will use the configured version instead of the latest stable version. ### Enable new context pad To use the new context pad, configure your `flags.json` as follows: ```json { "enable-new-context-pad": true } ``` ![New context pad](./img/new-context-pad.png) ### Disable connector templates Camunda 8 only To [disable automatic connector template fetching](../use-connectors.md#automatic-connector-template-fetching), configure your `flags.json` as follows: ```json { "disable-connector-templates": true } ``` --- ## Desktop Modeler Camunda 7 and 8 Desktop Modeler is a desktop application for modeling BPMN, DMN, and Forms. As part of the Camunda implementation toolkit, it helps you create executable diagrams while working alongside your preferred IDE, integrated into your professional software development environment. ![Desktop Modeler Screenshot](./img/new-diagram.png) ## Features - Design [BPMN](../bpmn/bpmn.md), [DMN](../dmn/dmn.md), and [Forms](../forms/camunda-forms-reference.md) - Implement process applications for Camunda 7 and 8 - Deploy and run processes directly from the application - Validate your diagrams using [configurable lint rules](https://github.com/camunda/camunda-modeler-custom-linter-rules-plugin) - [Customize](./flags/flags.md) and [extend](./plugins/plugins.md) the application - Work against the local file system ## Download Download the app for Windows, Linux, or macOS from the [Camunda downloads page](https://camunda.com/download/modeler/). > On Windows and Linux you can carry out [additional steps](./install-the-modeler#wire-file-associations) to register Modeler as the default editor for BPMN, DMN, and Form files. ## Get started Learn how to [develop your first process](./model-your-first-diagram.md) and [deploy it](./connect-to-camunda-8.md) to Camunda 8. ## Resources - [Report an issue](https://github.com/camunda/camunda-modeler/issues) - [Source code](https://github.com/camunda/camunda-modeler) - [Troubleshooting](./troubleshooting.md) --- ## Install Desktop Modeler This document guides you through Desktop Modeler installation, our local modeler. Desktop Modeler is a desktop application for modeling BPMN, DMN, and Forms, and supports you in building executable diagrams with Camunda. ## Installation To install [Desktop Modeler](./index.md) for Windows, macOS, and Linux, visit the [Camunda downloads page](https://camunda.com/download/modeler/). Select your preferred version and take the following steps: 1. Unpack the archive (any platform) or open the `.dmg` file (macOS). 2. For macOS users, move the app to your applications folder (macOS). 3. Start the Camunda Modeler (Windows) or `camunda-modeler` (Linux) executable, or the Camunda Modeler application (macOS). Ensure the installation is owned by `root` and accessible to all users of the machine by following the steps below. 1. Unpack the zip archive using the `tar` command: ```shell cd /usr/bin sudo tar xvfz ~/Downloads/camunda-modeler-5.41.0-linux-x64.tar.gz ``` 2. Ensure the access permissions of the `chrome-sandbox` file are correct and create a link to this version: ```shell sudo chmod 4755 camunda-modeler-5.41.0-linux-x64/chrome-sandbox sudo ln -s camunda-modeler-5.41.0-linux-x64/camunda-modeler camunda-modeler ``` ## Wire file associations On Windows and Linux you can carry out additional steps to register Modeler as the default editor for BPMN, DMN, RPA, and Form files. On macOS, Modeler is automatically registered as the default editor. ### Windows To make Modeler the default editor for `.bpmn`, `.dmn`, `.rpa`, and `.form` files, execute `support/register_fileassoc.bat` in your terminal. ### Linux To register Modeler for `.bpmn`, `.dmn`, `.rpa`, and `.form` files, execute `support/xdg_register.sh` from your terminal. ## Next steps - [Model your first diagram](/components/modeler/desktop-modeler/model-your-first-diagram.md) - [Use connectors](/components/modeler/desktop-modeler/use-connectors.md) --- ## Model your first diagram(Desktop-modeler) After starting [Desktop Modeler](./index.md), you can model your first BPMN diagram. Follow the steps below: 1. Create a [BPMN](../bpmn/bpmn.md) diagram by selecting **BPMN diagram**: ![empty application](./img/empty.png) The BPMN diagram opens with a start event. [Events](/components/modeler/bpmn/events.md) in BPMN represent things that happen. A process can react to events and emit events, for example: ![new diagram](./img/new-diagram.png) 1. The basic elements of BPMN processes are [tasks](/components/modeler/bpmn/tasks.md), or atomic units of work composed to create a meaningful result. Click on the start event and select the rectangular task element. This creates a task directly following the start event, connected by an arrow. Next, double-click the task and type in a name for the element. For example, `My Service Task`. 1. Click on the task and select the dark circle in the top left. This attaches an end event directly to the task. On the left side of the screen you will find the element palette, where you can also drag and drop elements onto the canvas. ![elements](./img/elements.png) Above, you can see that elements that support different types can be reconfigured by clicking on the corresponding icon. In this case, the task can be converted to a [service task](../bpmn/service-tasks/service-tasks.md), for example, by clicking on the element and selecting the **Change element** menu icon. ![task configuration](img/element-configuration.png) You can also [orchestrate human tasks](/guides/getting-started-orchestrate-human-tasks.md). Review the [complete list of supported BPMN elements](/components/modeler/bpmn/bpmn-coverage.md) 1. Open the properties panel by selecting the light gray arrow on the right side of the page halfway down the canvas. Here, you can edit the properties of the currently selected element: ![properties panel](img/properties-panel.png) For example, you might name your element and give it an ID under the **General** section. 1. Save the diagram using **File > Save**, **File > Save As**, or the keyboard shortcut (`Cmd + S` on macOS or `Ctrl + S` on Windows and Linux). Desktop Modeler saves the file to the location you choose on your local file system; it does not store diagrams in a separate internal workspace. To find the file again, reopen it from that folder or from the recent files list on the Desktop Modeler start screen. 1. Once you finish modeling and configuring your diagram, you can deploy it to a [Camunda 8 cluster](./connect-to-camunda-8.md). --- ## Plugins :::note The Camunda Modeler plugins API is not stable and might change in the future. ::: Plugins allow you to change the appearance and behavior of Camunda Modeler and add new features. ## Plugging into Camunda Modeler You can plug into the modeler to change its appearance, add new menu entries, extend the modeling tools for [BPMN](https://github.com/bpmn-io/bpmn-js) and [DMN](https://github.com/bpmn-io/dmn-js), or even slot React.js components into the Camunda Modeler UI. To add a plugin, put it into the `resources/plugins` directory relative to your [`{APP_DATA_DIRECTORY}`](../search-paths#app-data-directory) or [`{USER_DATA_DIRECTORY}`](../search-paths#user-data-directory) directory. Camunda Modeler searches for available plugin entry points via the `resources/plugins/*/index.js` pattern. This means that each plugin must reside in it's own folder which is a direct child of the `plugins` directory. :::note If you download and extract plugins from GitHub, the extracted directory contains the actual plugin, so make sure to copy the plugin, not its parent directory. ::: ## Overview of your possibilities as a plugin developer There are many ways for a developer to extend Camunda Modeler and its modeling tools. The following table shows an overview: | Plugin type | Functionality | Example | | ---------------------- | -------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | Menu Entries | Add new entries to the menu bar - useful to interact with your plugins, link to external pages, add settings, etc. | [Menu Example](https://github.com/camunda/camunda-modeler-plugins/tree/master/menu-plugin-example) | | Custom Styles | Change the look and feel of Camunda Modeler by adding stylesheets. | [Styles Example](https://github.com/camunda/camunda-modeler-plugins/tree/master/style-plugin-example) | | React Components | Embed custom React.js components into specific anchor points of Camunda Modeler. | [React Plugin Example](https://github.com/pinussilvestrus/camunda-modeler-autosave-plugin) | | bpmn-js Modules | Extend our BPMN editor by injecting your own custom [bpmn-js](https://github.com/bpmn-io/bpmn-js) modules. | [bpmn-js Module Example](https://github.com/camunda/camunda-modeler-plugins/tree/master/bpmn-js-plugin-example) | | bpmn-moddle Extensions | Extend the BPMN language model by injecting your own custom [bpmn-moddle](https://github.com/bpmn-io/bpmn-moddle) modules. | [bpmn-moddle Extension Example](https://github.com/camunda/camunda-modeler-plugins/tree/master/bpmn-js-plugin-moddle-extension-example) | | dmn-js Modules | Extend our DMN editor by injecting your own custom [dmn-js](https://github.com/bpmn-io/dmn-js) modules. | [dmn-js Module Example](https://github.com/camunda/camunda-modeler-plugins/tree/master/dmn-js-plugin-example) | | dmn-moddle Extensions | Extend the DMN language model by injecting your own custom [dmn-moddle](https://github.com/bpmn-io/dmn-moddle) modules | n/a | | bpmnlint Plugins | Add custom lint rules through [bpmnlint](https://github.com/bpmn-io/bpmnlint) plugins | [Custom lint rules](../custom-lint-rules) | ## Getting started with development ### Plugin entry point Regardless of the type of your plugin, you have to export a [Node.js module](https://nodejs.org/api/modules.html) named `index.js` that acts as a plugin entry point. The following shows an example of such entry point: ```javascript module.exports = { name: "My Awesome Plugin", // the name of your plugin style: "./style.css", // changing the appearance of the modeler menu: "./menu.js", // adding menu entries to the modeler script: "./script.js", // extending the modeler, and its BPMN and DMN components }; ``` The modeler will automatically load your plugins on startup. ### Changing the appearance of the modeler You can change the appearance of the modeler using CSS. Your stylesheet might look like this: ```css body { background: linear-gradient(0deg, #52b415, #eee); } ``` Plug it into the modeler like this: ```javascript module.exports = { style: "./style.css", }; ``` ### Adding menu entries to the modeler You can add new menu entries to the modeler's menu. Describe your menu entries like this: ```javascript module.exports = function (electronApp, menuState) { return [ { label: "Open BPMN Reference", accelerator: "CommandOrControl+[", enabled: function () { // only enabled for BPMN diagrams return menuState.bpmn; }, action: function () { var shell = require("electron").shell; shell.openExternal("https://camunda.org/bpmn/reference/"); }, }, ]; }; ``` Plug them into the modeler like this: ```javascript module.exports = { menu: "./menu-entries", }; ``` :::note The code within the menu entries executes on [the main process](https://www.electronjs.org/docs/latest/tutorial/process-model) of Electron. This comes with the advantage of allowing you to use [Node.js](https://nodejs.org/en/) modules, but you need to consider that you cannot debug the respective code in Chromium. For more information regarding main process debugging, refer to the [official Electron documentation](https://www.electronjs.org/docs/latest/tutorial/debugging-main-process). ::: For more information on how the modeler's menu works, take a look at its [implementation](https://github.com/camunda/camunda-modeler/blob/master/app/lib/menu/menu-builder.js). ### Extend the modeler and its BPMN and DMN components You can extend the modeling tools for [BPMN](https://github.com/bpmn-io/bpmn-js) and [DMN](https://github.com/bpmn-io/dmn-js) with your own modules, as well as embedding React.js components into certain sections of Camunda Modeler. Since the client of the modeler uses [Chromium](https://www.chromium.org/Home), you can't use Node.js modules to extend the modeling tools. You need to bundle your plugin first. The easiest way to get started with client-side plugins is through [this example project](https://github.com/camunda/camunda-modeler-plugin-example). > In this example, we are building a bpmn-js plugin, but this basic structure applies to all extensions besides menu entries and style. The modules themselves will be different however, so refer to our [examples](https://github.com/camunda/camunda-modeler-plugins) for more information on how to build different kinds. Take the following steps: 1. Clone or fork the repository: ``` git clone https://github.com/camunda/camunda-modeler-plugin-example.git ``` The plugin starter project comes with a menu and style folder which are referenced in the plugin entry point. If you do not need those, you can remove them from the entry point and delete the respective folder. 2. Install the dependencies: ``` npm install ``` 3. Create your module: ```javascript function LoggingPlugin(eventBus) { eventBus.on("shape.added", function () { console.log("A shape was added to the diagram!"); }); } module.exports = { __init__: ["loggingPlugin"], loggingPlugin: ["type", LoggingPlugin], }; ``` 4. Require your file in `client.js` and register it via our [helper functions](https://github.com/camunda/camunda-modeler-plugin-helpers): ```javascript var registerBpmnJSPlugin = require("camunda-modeler-plugin-helpers").registerBpmnJSPlugin; var plugin = require("./LoggingPlugin"); registerBpmnJSPlugin(plugin); ``` 5. You may want to create a plugin which specifically targets Camunda 7 or Camunda 8. To do this, use the appropriate variations of the registration helper function for your plugin type. ```javascript registerPlatformBpmnJSPlugin(plugin); // Register plugin for Camunda 7 BPMN diagrams only registerCloudBpmnJSPlugin(plugin); // Register plugin for Camunda 8 BPMN diagrams only registerBpmnJSPlugin(plugin); // Register plugin for Camunda 7 and 8 BPMN diagrams ``` 6. You can use the globally available functions `getModelerDirectory` and `getPluginsDirectory` to load additional resources: ```javascript function LoggingPlugin(eventBus, canvas) { var img = document.createElement(img); img.src = getPluginsDirectory + "/logging-plugin/image.png"; canvas.getContainer().appendChild(img); } ``` 7. Bundle your plugin: ``` npm run build ``` 8. Put the folder into the `resources/plugins` directory relative to your Camunda Modeler installation directory. You can now use your plugin! ### Development workflow When creating a plugin, you can place the directory containing your plugin in the aforementioned `resources/plugins` directory. Plugins will be loaded on application startup (menu plugins) or reload (style and modeling tool plugins). To reload the application, press `F12` to open the developer tools, and press `Ctrl+R` or `Cmd+R`. This will clear all unsaved diagrams. ## Additional resources - [Example Plugins](https://github.com/camunda/camunda-modeler-plugins) - [Plugin Starter Project](https://github.com/camunda/camunda-modeler-plugin-example) --- ## Process applications Desktop Modeler recognizes [process applications](../../concepts/process-applications.md) you build and offers you advanced editor intelligence, deployment, and execution features within the context of such an application. To identify the boundaries of a process application, Desktop Modeler searches for a `.process-application` file in the root of your project. In professional software development, a typical process application contains resources such as BPMN, DMN, and Form files. These live alongside [job workers](/components/concepts/job-workers.md), implementing process logic, additional application code, and tests. How exactly your project is structured may vary depending on the implementation language, libraries, and frameworks you use. ## Example: Consumer loan application Consider an application implementing consumer loan approval. It may contain: - A main BPMN process (for example, `consumer-loan-application.bpmn`) to define the workflow. - DMN decisions (for example, `interest-rate-calculation.dmn`, `credit-score-calculation.dmn`) for business rules. - Forms (for example, `loan-application-review.form`) for user interactions. - Various [job workers](/components/concepts/job-workers.md) that implement process behavior. - Additional application code and tests In a typical Java/Maven project, the structure of such an application might be as follows: ``` consumer-loan-application/ ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/example/loan/ │ │ │ │ └── workers │ │ │ │ │ ├── UnderwriteLoanWorker.java │ │ │ │ │ └── ... │ │ └── resources/ │ │ ├── consumer-loan-application.bpmn │ │ ├── dmn/ │ │ │ ├── interest-rate-calculation.dmn │ │ │ └── credit-score-calculation.dmn │ │ └── form/ │ │ └── loan-application-review.form │ └── test/ │ └── java/ │ └── ... ├── .process-application ├── pom.xml └── README.md ``` ## Editor support for process applications When you open a file in Modeler, the system implicitly determines whether it belongs to a process application. It does so by checking for the presence of a `.process-application` file in the same folder or a parent folder. Within a process application, Modeler offers improved navigation and assistance. ### Indicating context Process applications are opened and closed "implicitly": A blue item in the status bar indicates whether a diagram belongs to a process application and makes all related diagrams available for navigation. When files from more than one process application are open, they are grouped visually. ### Creating a process application Create a process application by creating a `.process-application` file in the root of your project. Alternatively, create it via Modeler UI by taking the following steps: 1. Click **File > New Process Application...**. 2. Choose a folder. 3. Click **Select folder**. A `.process-application` file will be created in the selected folder, and the folder will now be recognized by Modeler as the applications project root. Any file within the folder or its subfolders will be treated as part of the process application. ### Linking resources Any file within a process application can be linked as a resource. Linking a resource can be achieved in several ways: - Using the append feature - Using the replace feature - Using the create feature - Manually by setting the process, decision, or form ID in the properties panel ### Deploying a process application Process applications can be deployed using the [deploy feature](./deploy-diagram.md). When deploying a process application, all files that are part of the process application will be deployed. ### Starting a process instance :::note Before starting a process instance, all process application files will be deployed to reflect the state of the process application. ::: You can start an instance for any process in a process application using the [start instance feature](./start-instance.md). --- ## Search paths Features like element templates and plugins allow you to add your own resources to Desktop Modeler. For these resources to be found, they have to be in one of two directories depending on how local or global you want them to be. ## App data directory The `resources` directory relative to the directory containing the Camunda Modeler executable file. In our documentation we refer to it as `{APP_DATA_DIRECTORY}`. Resources in the app data directory will be found by any local Camunda Modeler instance. ### Examples ``` └── camunda-modeler-5.10.0-win-x64 ├── Camunda Modeler.exe └── resources ├── element-templates | └── my-element-templates.json └── plugins └── my-plugin └── index.js ``` :::note On macOS, the Camunda Modeler is a self-contained `.app` bundle, which makes it difficult to add files to its installation directory. Therefore, we recommend using the [user data directory](#user-data-directory) instead. ::: ``` └── camunda-modeler-5.10.0-linux-x64 ├── camunda-modeler └── resources ├── element-templates | └── my-element-templates.json └── plugins └── my-plugin └── index.js ``` ## User data directory The `camunda-modeler/resources` directory relative to the per-user application data directory, which by default points to: - `%APPDATA%` on [Windows](https://www.pcworld.com/article/2690709/whats-in-the-hidden-windows-appdata-folder-and-how-to-find-it-if-you-need-it.html) - `$XDG_CONFIG_HOME` or `~/.config` on [Linux](https://wiki.archlinux.org/index.php/XDG_user_directories) - `~/Library/Application Support` on macOS In our documentation we refer to it as `{USER_DATA_DIRECTORY}`. Resources in the user data directory will be found by all Camunda Modeler instances. ### Examples ``` └── %APPDATA% └── Roaming └── camunda-modeler └── resources ├── element-templates | └── my-element-templates.json └── plugins └── my-plugin └── index.js ``` ``` └── ~/Library/Application Support └── camunda-modeler └── resources ├── element-templates | └── my-element-templates.json └── plugins └── my-plugin └── index.js ``` ``` └── ~/.config └── camunda-modeler └── resources ├── element-templates | └── my-element-templates.json └── plugins └── my-plugin └── index.js ``` It is possible to change the user data directory using the `--user-data-dir` option via when starting Camunda Modeler from the command line. Refer to the [flags documentation](../flags) on how to configure the application with a flags file. --- ## Settings(Settings) Configure and manage your Desktop Modeler application and plugin preferences and defaults via the **Settings** window. ## Manage Desktop Modeler settings Open the **Settings** window either from the application menu or by using the `Cmd/Ctrl + ,` keyboard shortcut. - Changes are saved automatically. If a restart is required to apply your changes, a warning is displayed. - Open additional documentation links for a setting by clicking the blue icon next to the setting name. ## Settings overview The following Desktop Modeler settings are available: | Setting | Description | | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Enable new context pad | Enables the alternative context pad interface. | | Disable plugins | Disables all installed plugins when running Desktop Modeler. | | Disable connector templates | If checked, Desktop Modeler will not automatically download or use Camunda connector templates. See [automatic connector template fetching](../use-connectors/#automatic-connector-template-fetching) for details. | | Default Camunda 8 version | Sets the default Camunda 8 [execution platform version](/reference/glossary.md#execution-platform-version) for new diagrams. | | Default Camunda 7 version | Sets the default Camunda 7 [execution platform version](/reference/glossary.md#execution-platform-version) for new diagrams. | :::note Plugins may also provide their own settings in the **Settings** window. ::: ## Settings JSON file Your settings are saved in a `settings.json` file in the [user data directory](../search-paths/search-paths.md#user-data-directory). :::note Only the settings you have changed are stored in this file. ::: --- ## Start a new process instance 1. Ensure you have already set up and selected a connection by following the [Connect to Camunda 8](./connect-to-camunda-8.md) guide. ![dev cluster connected](img/connection-selector-dev-connection.png) 2. Click the play icon to start a new process instance. ![start instance icon](./img/start-instance-icon.png) 3. Enter optional variables for starting the process instance. Click **Start BPMN process instance** to deploy and start the instance. ![start instance overlay](./img/start-instance.png) A confirmation message confirms that the instance started successfully. ![start instance successful](./img/start-instance-successful.png) --- ## Task testing(Desktop-modeler) [Test a single task](../task-testing.md) in Desktop Modeler to quickly verify task logic, variable mappings, and configuration—without running the entire process. Task testing deploys the process and executes the selected task on the engine, as if it would be executed in a process flow. You control the context (process variables) that can verify if the outputs match your expectation. ## Prerequisites Task testing requires a **REST connection** to a Camunda 8.8.0+ cluster. gRPC connections are not supported. You can set up a connection by following the [Connect to Camunda 8](./connect-to-camunda-8.md) guide. ## Testing a single activity 1. Select the task you want to test. 2. Open the **Task testing** tab in the bottom panel. ![Testing tab](./img/task-testing/testing-tab.png) ### Process variables In the left pane, define the [process variables](../../concepts/variables.md), the context the task will be executed in. As you type, the editor provides autocompletion for variables available in scope, including those generated by previous task testing runs. ### Task execution Click **Test task** to execute the selected activity. Your BPMN diagram will be saved, deployed, and a process instance will start automatically. :::note If you're working in a [process application](./process-applications.md), all its resources will be deployed along with the process. ::: Once the process instance starts, a link to view it in Operate is provided. Task testing waits for the task to complete or for an incident to occur. ## Test results A test run can result in any of the following outcomes: ### Successful execution When the task completes successfully, the results panel displays the process variables _after_ execution. This includes variables created by the task, mapped through defined output mappings. ![Testing success](./img/task-testing/testing-success.png) ### Task incident If an incident occurs during execution, you’ll see its details along with the process variables. ![Testing incident](./img/task-testing/testing-incident.png) ### Error If the task execution fails due to an error, the response message is displayed. ## Related - [Test a task in Web Modeler](../../hub/workspace/modeler/validation/task-testing.md) - [Learn about task testing concepts](../task-testing.md) - [Working with variables](../../concepts/variables.md) --- ## Telemetry You can opt out for the collection of telemetry data when using Desktop Modeler. This data is used to better understand how the application is used and to improve it based on data. This page summarizes the data collected. ## General structure of the events The events **Desktop Modeler** sends share a similar payload which usually (but not exclusively) includes information like: - **event name**: The name of the event triggered (e.g. `diagram:opened`) - **application version**: The version of Desktop Modeler being used (e.g. Version 5.0.0) - **editor id**: A randomly generated ID assigned to your Desktop Modeler installation ## Definition of events ### Ping event The `Ping Event` is sent in the following situations: - The modeler is opened (given that `Usage Statistics` option is enabled). - `Usage Statistics` option is enabled for the first time. - Once every 24 hours (given that `Usage Statistics` option is enabled). The `Ping Event` also sends the list of plugins installed and flags defined: ```json "plugins": ["PLUGIN_NAME"], "flags": { "FLAG_NAME": "FLAG_VALUE" } ``` ### Diagram opened/closed event The `Diagram Opened Event` is sent in the following situations: - User created a new BPMN diagram - User created a new DMN diagram - User created a new Form - User opened an existing BPMN diagram - User opened an existing DMN diagram - User opened an existing Form The `Diagram Closed Event` is sent in the following situations: - User closed a BPMN diagram - User closed a DMN diagram - User closed a Form These events include the following properties: - `diagramType`: BPMN, DMN, or Form - Engine profile: - `executionPlatform`: <target platform\> - `executionPlatformVersion`: <target platform version\> In the case of a form, the payload also includes the `formFieldTypes`: ```json "formFieldTypes": { "textfield": 6, "group": 2, "image": 4 } ``` ### Deployment and start instance events The `Deployment Event` is sent in the following situations: - User deploys a BPMN or DMN diagram to Camunda 7 or Camunda 8 - User deploys a Form to Camunda 7 The `Deployment Event` and `Start Instance` have the following properties: - `diagramType`: BPMN, DMN, or Form - Engine profile: - `executionPlatform`: <target platform\> - `executionPlatformVersion`: <target platform version\> In the event of an unsuccessful deployment, an `error` property will be present in the payload containing an error code. If provided, as is the case when deploying to a Zeebe-based platform, the payload also includes the target type of the deployment: ```json "targetType": "[camundaCloud or selfHosted]" ``` If the target engine profile is set in the diagram, the payload will also contain it. ```json "executionPlatform": "" ``` ### Tracked click events The `Tracked Click Events` are sent when a user clicks a link or button contained within a tracked parent 'container'. Currently, these containers are: - Each of the welcome page columns - The version info overlay The event supplies: - The `parent` container ID to locate the application section - The button label or link text (generalized as label) for identification of what was specifically clicked - A type to differentiate buttons, internal links, and external links - The link target (optional for external links) Example event: ```json { "type": "[button or external-link or internal-link]", "parent": "welcome-page-learn-more", "label": "Click here to read more about Camunda", "link": "https://camunda.com/" } ``` :::note `"link"` is only present for `"type": "external-link"`. ::: ### Overlay opened event The `Overlay Opened Event` is sent when an overlay is opened via user interaction. Currently, this event is sent in the following circumstances: - Version Info overlay is opened - Deployment overlay is opened - Start instance overlay is opened - Deployment overlay is closed - Start Instance overlay is closed For the **Version Info** overlay, the event also sends `source` of the click (`"menu"` or `"statusBar"`). For the **Deployment** and **Start Instance** overlays, the event also send the `diagramType` (BPMN, DMN or Form). ### Form editor events The `Form editor events` are sent on different interactions with the form builder: - User opened or collapsed a panel in the form editor. The event includes the current open state for each form preview panel and the interaction that triggered the change. ```json { "layout": { "form-input": { "open": true }, "form-output": { "open": true }, "form-preview": { "open": true } }, "triggeredBy": "keyboardShortcut|previewPanel|statusBar|windowMenu" } ``` - User interacted with the form input data panel. - User interacted with the form preview panel. In all events [the execution platform and version](#diagram-openedclosed-event) are sent as well. ### BPMN editor events BPMN editor events are sent on different interactions with the BPMN editor: - User created, appended, or replaced an element on the canvas. These events include the `oldElement`/`sourceElement` (when applicable) and the new element(s). - User selected, updated, unlinked, or removed an element template from an element via the properties panel on the right side of the screen. These events include the current diagram `selection` and respective element templates. - User interacted with the pop-up menu, context menu, or the palette. These events include the target (`entryId`, `entryGroup`, `entryTitle`) and `triggerType` (`"click"`, `"drag"`, or `"keyboard"`) of the interaction, as well as the current diagram `selection`. Example event: ```json { "entryId": "create.start-event", "entryGroup": "event", "entryTitle": "Start Event", "selection": [ { "id": "StartEvent_1", "type": "bpmn:StartEvent", "templateId": "template-start-event" } ], "triggerType": "keyboard" } ``` ### Task testing events Task testing events are sent when using the [task testing](../task-testing.md) feature: - Task execution is started. This event includes `elementType` and `elementTemplate` if applied. - Task testing deployment. [Deployment event](#deployment-and-start-instance-events) is triggered when the process is deployed during task testing. - Task execution finished. This event includes `elementType`, `elementTemplate`, `success` boolean value, and `incidentType` if task testing resulted in an [incident](../../../concepts/incidents.md). Example task testing finished event: ```json { "elementType": "bpmn:ServiceTask", "elementTemplate": "io.camunda.connectors.HttpJson.v2", "success": false, "incidentType": "JOB_NO_RETRIES" } ``` ### Variables panel events Variables panel events are sent on different interactions with the [Variables](../../data-handling.md#inspecting-variables) panel: - User opened the variables panel. - User closed the variables panel. - User filtered variables using the search input. The search term is not included in the event. - User expanded or collapsed a scope section. - User expanded or collapsed a variable row. - User copied a variable name via the copy button. - User copied a variable path or value via the context menu. These events do not include any additional payload data. ### Connection event The `Connection Event` is sent in the following situations: - Desktop Modeler fails to connect to [a configured cluster](../connect-to-camunda-8.md). - Desktop Modeler connects to a configured cluster. The `Connection Event` includes the following properties: - `success`: `true` or `false` - `targetType`: `SaaS` or `Self-Managed` - `isLocal`: `true` or `false` - `reason`: An error reason, or `null` Example connection event: ```json { "success": true, "targetType": "Self-Managed", "isLocal": true, "reason": null } ``` --- ## Troubleshooting(Desktop-modeler) ## How to start Desktop Modeler without plugins You can start Desktop Modeler with the [`--disable-plugins` flag](./flags/flags.md#disable-plug-ins). ## How to obtain Desktop Modeler logs Depending on your operating system, you can find Desktop Modeler logs in different places: ```plain %APPDATA%\camunda-modeler\logs ``` Example: ```plain C:\Users\Camunda\AppData\Roaming\camunda-modeler\logs ``` ```plain ~/Library/Logs/camunda-modeler ``` ```plain ~/.config/camunda-modeler/logs ``` To produce logging output, you can also run Desktop Modeler from the command line. ## I cannot connect to an orchestration cluster {#i-cannot-connect-to-zeebe} You try to connect (i.e., to deploy) to a remote orchestration cluster, and Desktop Modeler tells you it "Cannot connect to orchestration cluster." :::tip If you run against a Camunda 8 SaaS free-trial cluster, ensure it is [not paused](../../concepts/clusters.md#auto-pause). ::: To resolve this issue, check if you can connect to Zeebe through another client, for example, community-supported [`zbctl`](https://github.com/camunda-community-hub/zeebe-client-go/blob/main/cmd/zbctl/zbctl.md). If that works, [further debug your Zeebe connection](#debug-zeebe-connection-issues). If that does not work, resolve the [general connection issue](#resolve-a-general-zeebe-connection-issue) first. Additionally, if authorizations are enabled, ensure that your [client](/components/admin/client.md) credentials have the required permissions. These differ from [user](/components/admin/user.md) credentials and are evaluated separately. ## I cannot connect to a local orchestration cluster {#i-cannot-connect-to-local-zeebe} You try to connect (i.e., to deploy) to a local orchestration cluster, and Desktop Modeler tells you it "Cannot connect to orchestration cluster." Ensure your local orchestration cluster is running. If you don't have one installed, consider [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md), a lightweight all-in-one distribution. ## Cannot connect to an orchestration cluster in a local network Use this guidance when Desktop Modeler cannot connect to an orchestration cluster running in your local network and shows a "Cannot connect to orchestration cluster" error. Verify that your operating system allows Desktop Modeler to access the local network. Ensure your network is set to **Private** and that apps are allowed to communicate on private networks. See [make a network public or private](https://support.microsoft.com/en-us/windows/essential-network-settings-and-tasks-in-windows-f21a9bbc-c582-55cd-35e0-73431160a1b9#ID0EFF). Ensure **Privacy & Security** settings allow Desktop Modeler to access your local network. See [control access to your local network](https://support.apple.com/en-gb/guide/mac-help/mchla4f49138/mac). ## How to configure a REST connection You try out [task testing](./task-testing.md) and Desktop Modeler tells you "Configure a REST connection to a Camunda 8 cluster." Some features of Desktop Modeler, such as task testing, require a REST connection to a Camunda 8 cluster. Orchestration clusters from version 8.6 support connections with gRPC or the newer [Orchestration Cluster REST API](../../../apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). Depending on the provided URL, the corresponding client will be used. Ensure you use the REST URL in your deployment configuration: - If you are using Camunda 8 SaaS clusters, create an [API client](../../hub/organization/manage-clusters/manage-api-clients.md) and use the value of `Camunda REST API`. - If you are using [Camunda 8 Run](../../../self-managed/quickstart/developer-quickstart/c8run.md), you should use the value of `Orchestration Cluster API`. :::tip Even if the URL starts with `http://`, it may still be a gRPC endpoint. Ensure you use the correct URL provided by your orchestration cluster. ::: ## Resolve a general Zeebe connection issue You try to connect to Zeebe from both Desktop Modeler _and_ community-supported [`zbctl`](https://github.com/camunda-community-hub/zeebe-client-go/blob/main/cmd/zbctl/zbctl.md), and neither of them works. General connection failures can have a couple of reasons: ### The (remote) orchestration cluster is not reachable {#the-remote-zeebe-instance-is-not-reachable} Ensure your computer has access to the (remote) network. :::tip If you run against a Camunda 8 SaaS free-trial cluster, ensure it is [not paused](../../concepts/clusters.md#auto-pause). ::: ### The connection to Zeebe happens through a proxy [Inspect the connection](#how-can-i-get-details-about-a-secure-remote-connection) to understand if it can be established. Secure connections to Zeebe require [HTTP/2 over TLS with protocol negotiation via ALPN](/self-managed/operational-guides/troubleshooting.md#zeebe-ingress-grpc). Ensure your proxy supports these features and does not forcefully downgrade the connection to HTTP/1. ### The connection to Zeebe should not happen through a proxy If you are using a proxy but do not want to connect to Zeebe through it, exclude Zeebe from proxying by adding it to the `NO_PROXY` environment variable: ```plain set NO_PROXY=localhost,127.0.0.1,some.intranet.host && "Camunda Modeler.exe" ``` ```plain NO_PROXY=localhost,127.0.0.1,some.intranet.host camunda-modeler ``` ```plain NO_PROXY=localhost,127.0.0.1,some.intranet.host camunda-modeler ``` ## Debug Zeebe connection issues You can connect to Zeebe via community-supported [`zbctl`](https://github.com/camunda-community-hub/zeebe-client-go/blob/main/cmd/zbctl/zbctl.md) or another API client. However, connecting through Desktop Modeler fails. ### Secure connection to Zeebe fails When connecting securely to Camunda 8 SaaS, Camunda 8 Self-Managed, or a standalone Zeebe instance (via `https` endpoint URL), Desktop Modeler tries to establish a secure connection. In the process, it strictly validates the server certificates presented against well-known certificate authorities. Failure to connect may have several reasons: #### The (remote) endpoint is not configured for secure connections Ensure you properly configured the remote endpoint. #### The (remote) endpoint presents an untrusted certificate [Inspect the connection](#how-can-i-get-details-about-a-secure-remote-connection) to understand which certificates are being returned by the server. Ensure you configure Desktop Modeler for [custom SSL certificates](#how-can-i-provide-a-custom-ssl-certificate). If intermediate signing authorities sign the server certificate, ensure the remote endpoint [serves both server and intermediate certificates](https://nginx.org/en/docs/http/configuring_https_servers.html#chains) to Desktop Modeler. ## How can I provide a custom SSL certificate? You configured a custom SSL certificate in your (remote) Zeebe endpoint and want Desktop Modeler to accept that certificate. The app [strictly validates](./flags/flags.md#zeebe-ssl-certificate) the remote server certificate trust chain. If you use a custom SSL server certificate, you must make the signing CA certificate known to Desktop Modeler, not the server certificate itself. Desktop Modeler reads trusted certificate authorities from your operating systems trust store. Installing custom CA certificates in that trust store is recommended for most users. Alternatively, you may provide custom trusted CA certificates via the [`--zeebe-ssl-certificate` flag](./flags/flags.md#zeebe-ssl-certificate). ## How can I get details about a secure remote connection? You can use the following command to retrieve information about HTTP/2 over TLS support (ALPN) and certificates provided by a remote endpoint: ```sh > openssl s_client -alpn h2 -connect google.com:443 -servername google.com [...] --- Certificate chain 0 s:/CN=*.google.com i:/C=US/O=Google Trust Services LLC/CN=GTS CA 1C3 1 s:/C=US/O=Google Trust Services LLC/CN=GTS CA 1C3 i:/C=US/O=Google Trust Services LLC/CN=GTS Root R1 2 s:/C=US/O=Google Trust Services LLC/CN=GTS Root R1 i:/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA --- [...] --- New, TLSv1/SSLv3, Cipher is AEAD-CHACHA20-POLY1305-SHA256 Server public key is 256 bit Secure Renegotiation IS NOT supported Compression: NONE Expansion: NONE ALPN protocol: h2 SSL-Session: Protocol : TLSv1.3 Cipher : AEAD-CHACHA20-POLY1305-SHA256 Session-ID: Session-ID-ctx: Master-Key: Start Time: 1687516295 Timeout : 7200 (sec) Verify return code: 0 (ok) --- ``` ## How can I debug log gRPC / Zeebe communication? You can also start Desktop Modeler with gRPC logging turned on to get detailed [logging output](#how-to-obtain-desktop-modeler-logs) on communication to Zeebe: ```plain set DEBUG=* && set ZEEBE_NODE_LOG_LEVEL=DEBUG && set GRPC_VERBOSITY=DEBUG && set GRPC_TRACE=all && "Camunda Modeler.exe" ``` ```plain DEBUG=* ZEEBE_NODE_LOG_LEVEL=DEBUG GRPC_VERBOSITY=DEBUG GRPC_TRACE=all camunda-modeler ``` ```plain DEBUG=* ZEEBE_NODE_LOG_LEVEL=DEBUG GRPC_VERBOSITY=DEBUG GRPC_TRACE=all camunda-modeler ``` ## Desktop Modeler does not start on Ubuntu 24 / modern Linux Modern Linux operating systems introduce restrictions on user namespaces, a sandboxing (isolation) mechanism Modeler uses. You may see an error message when you start the application: ```sh $ ./camunda-modeler [46193:1114/170934.837319:FATAL:setuid_sandbox_host.cc(163)] The SUID sandbox helper binary was found, but is not configured correctly. Rather than run without sandboxing I'm aborting now. You need to make sure that [...]/camunda-modeler-[...]-linux-x64/chrome-sandbox is owned by root and has mode 4755. zsh: trace trap (core dumped) ./camunda-modeler ``` To remedy this, configure your system to allow sandboxing by [creating an AppArmor profile](https://github.com/camunda/camunda-modeler/issues/4695#issuecomment-2478458250), or review [this issue](https://github.com/camunda/camunda-modeler/issues/4695#issuecomment-2478581677) for an in-depth explanation of available options. If you don't have the necessary permissions to permit sandboxing, you may choose to disable the sandbox, though this is not recommended. ## Other questions? Head over to the [Modeler category on the forum](https://forum.camunda.io/c/modeler/6) to receive help from the community. --- ## Use connectors Camunda 8 only Use [Camunda 8 connectors](../../connectors/introduction.md) to access a growing range of external services or communication protocols. Desktop Modeler automatically fetches and updates [element templates](/components/modeler/element-templates/about-templates.md) for [pre-built connectors](../../connectors/out-of-the-box-connectors/available-connectors-overview.md) from the [Camunda Marketplace](https://marketplace.camunda.com/en-US/listing?pl=3038&attr=20486&cat=107792&locale=en-US). This happens automatically, in the background, unless explicitly [disabled](#disable-automatic-connector-template-fetching). connector templates can also be provided [manually](#add-connector-templates-manually), offering you full control over the building blocks provided by the modeler. ## Automatic connector template fetching Automatic connector template fetching is enabled by default, and notifies you of any updates or errors. The fetch is triggered whenever you start the application, or every 24 hours if the application is not closed. After an update check has concluded, a notification indicates if the templates are up to date or have been updated: ![Camunda connector templates up to date notification](./img/use-connectors/up-to-date.png) In case of an error you'll see a notification: ![Error updating Camunda connector templates notification](./img/use-connectors/error.png) Once fetched, you can use the templates in the Camunda 8 BPMN editor. ![Using Camunda connector templates in the Camunda 8 BPMN editor](./img/use-connectors/apply.png) ## Disable automatic connector template fetching Disable automatic fetching of connector templates using the `#disable-connector-templates` [feature flag](./flags/flags.md#disable-connector-templates). ## Add connector templates manually For full control over the building blocks offered by Desktop Modeler, download element templates for individual Camunda 8 connectors manually (for example, through the [Camunda Marketplace](https://marketplace.camunda.com/)). Make them available to Desktop Modeler via the element template [search paths](./element-templates/configuring-templates.md). ## Additional resources - [About Camunda 8 connectors](../../connectors/out-of-the-box-connectors/available-connectors-overview.md) - [About element templates](/components/modeler/element-templates/about-templates.md) - [Desktop Modeler flags](./flags/flags.md#disable-connector-templates) --- ## Decision literal expression ![Decision literal expression](assets/decision-literal-expression/decision-literal-expression.png) A decision literal expression represents decision logic which can be depicted as an expression. It consists of a [literal expression](#literal-expression) and a [variable](#variable). A decision literal expression is represented by a `literalExpression` element inside a `decision` XML element. ```xml calendar.getSeason(date) ``` ## Decision name ![Decision Name](assets/decision-literal-expression/decision-name.png) The name describes the decision for which the literal expression provides the decision logic. It is set as the `name` attribute on the `decision` element. ```xml ``` ## Decision id ![Decision Id](assets/decision-literal-expression/decision-id.png) The ID is the technical identifier of the decision. It is set in the `id` attribute on the `decision` element. Each decision should have an unique ID when it is deployed to Camunda. :::caution The decision ID may not contain any special characters or symbols (e.g. whitespace, dashes, etc.). The decision ID can be any alphanumeric string including the `_` symbol. For a combination of words, it's recommended to use the `camelCase` or the `snake_case` format. The `kebab-case` format is not allowed because it contains the operator `-`. If the decision ID contain a special character or symbol then the decision result can't be accessed in a [dependent decision](decision-requirements-graph.md#required-decisions). ::: ```xml ``` ## Literal expression The literal expression specifies how the value of the decision is generated. It can be used to do a complex calculation, or to combine the output values of [required decisions](decision-requirements-graph.md#required-decisions). The expression language of the literal expression is [FEEL](/components/modeler/feel/language-guide/feel-expressions-introduction.md). The expression is set inside a `text` element that is a child of the `literalExpression` XML element. ```xml calendar.getSeason(date) ``` ## Variable A decision literal expression must have a variable which specifies the name and the type of the decision result. A variable is represented by a `variable` element inside a `decision` XML element. ```xml ``` ### Variable name The name of the variable is used to reference the value of the literal expression in the decision result. It is specified by the `name` attribute on the `variable` XML element. :::caution The variable name may not contain any special characters or symbols (e.g. whitespace, dashes, etc.). The variable name can be any alphanumeric string including the `_` symbol. For a combination of words, it's recommended to use the `camelCase` or the `snake_case` format. The `kebab-case` format is not allowed because it contains the operator `-`. If the variable name contain a special character or symbol then the decision result can't be accessed in a [dependent decision](decision-requirements-graph.md#required-decisions). ::: :::tip It is recommended to use the decision ID as the variable name. In contrast to decision tables, the result of a decision literal expression can be accessed in a [dependent decision](decision-requirements-graph.md#required-decisions) by its variable name instead of its decision id. ::: ```xml ``` ### Variable type definition The type of the decision result can be specified by the `typeRef` attribute on the `variable` XML element. After the expression is evaluated it checks if the result converts to the specified type. The type should be one of the supported [data types](dmn-data-types.md). ```xml ``` Note that the type is not required but recommended since it provides a type safety of the expression result. --- ## Decision requirements graph ![Decision Requirements Graph](assets/decision-requirements-graph/drd.png) A Decision Requirements Graph (DRG) models a domain of decision-making, showing the most important elements involved in it and the dependencies between them. The elements modeled are [decisions](#decision), [input data](#input-data), and [knowledge sources](#knowledge-source). The visual representation of a DRG is called Decision Requirements Diagram (DRD). In the XML a DRG is represented by the `definitions` element. ```xml ``` ## Decision requirements graph name ![Decision Requirements Graph Name](assets/decision-requirements-graph/drg-name.png) The name describes the DRG. It is set as the `name` attribute on the `definitions` element. ```xml ``` ## Decision requirements graph id ![Decision Requirements Graph Id](assets/decision-requirements-graph/drg-id.png) The ID is the technical identifier of the DRG. It is set in the `id` attribute on the `definitions` element. Each DRG should have an unique ID when it is deployed to Camunda. ```xml ``` ## Decision ![Decision](assets/decision-requirements-graph/decision.png) A decision requirements graph can have one or more decisions. A decision has a [name](decision-table.md#decision-name) which is shown in the DRD and an [ID](decision-table.md#decision-id). The decision logic inside the decision must be either a [decision table](decision-table.md) or a [decision literal expression](decision-literal-expression.md). A decision is represented by a `decision` element inside the `definitions` XML element. ```xml ``` ## Required decisions ![Required Decision](assets/decision-requirements-graph/required-decision.png) A decision can have one or more required decisions which it depends on. A required decision is represented by a `requiredDecision` element inside an `informationRequirement` XML element. It has a `href` attribute and the value starts with `#` followed by the [decision ID](decision-table.md#decision-id) of the required decision. :::tip The result of a required decision can be accessed in the dependent decision by its decision ID. If the required decision is a decision table and has more than one output then the output values are grouped under the decision ID and can be accessed by their [output names](decision-table-output.md#output-name) ( e.g. `decisionId.outputName`). The structure of the result depends on the decision table [hit policy](decision-table-hit-policy.md). ::: ```xml ``` ## Input data ![Input Data](assets/decision-requirements-graph/input-data.png) An input data denotes information used as an input by one or more decisions. It is represented by an `inputData` element inside the `definitions` element. ```xml ``` Note that an input data has no execution semantics and is ignored on the evaluation. ## Knowledge source ![Knowledge Source](assets/decision-requirements-graph/knowledge-source.png) A knowledge source denotes an authority for a Decision. It is represented by a `knowledgeSource` element inside the `definitions` element. ```xml ``` Note that a knowledge source has no execution semantics and is ignored on the evaluation. --- ## Hit policy ![Hit Policy](assets/decision-table/hit-policy.png) A decision table has a hit policy that specifies what the results of the evaluation of a decision table consist of. The hit policy is set in the `hitPolicy` attribute on the `decisionTable` XML element. If no hit policy is set, then the default hit policy `UNIQUE` is used. ```xml ``` The following hit policies are supported: Hit Policy XML representation Unique UNIQUE Any ANY First FIRST Rule order RULE ORDER Collect COLLECT ## The role of a hit policy A hit policy specifies how many rules of a decision table can be satisfied and which of the satisfied rules are included in the decision result. The hit policies [Unique](#unique), [Any](#any) and [First](#first) will always return a maximum of one satisfied rule. The hit policies [Rule Order](#rule-order) and [Collect](#collect) can return multiple satisfied rules. ## Unique Only a single rule can be satisfied or no rule at all. The decision table result contains the output entries of the satisfied rule. If more than one rule is satisfied, the Unique hit policy is violated. Refer to the following decision table. ![Hit Policy Unique](assets/decision-table/hit-policy-unique.png) Depending on the current season the dish should be chosen. Only one dish can be chosen, since only one season can exist at the same time. ## Any Multiple rules can be satisfied. However, all satisfied rules must generate the same output. The decision table result contains only the output of one of the satisfied rules. If multiple rules are satisfied which generate different outputs, the hit policy is violated. Refer to the following example: ![Hit Policy Any](assets/decision-table/hit-policy-any.png) This is a decision table for the leave application. If the applier has no vacation days left or is currently in the probation period, the application will be refused. Otherwise the application is applied. ## First Multiple rules can be satisfied. The decision table result contains only the output of the first satisfied rule. ![Hit Policy First](assets/decision-table/hit-policy-first.png) Refer to the decision table above for advertisement. Regarding the current age of the user, which advertisement should be shown is decided. For example, the user is 19 years old. All the rules will match, but since the hit policy is set to first only, the advertisement for Cars is used. ## Rule order Multiple rules can be satisfied. The decision table result contains the output of all satisfied rules in the order of the rules in the decision table. ![Hit Policy Rule Order](assets/decision-table/hit-policy-rule-order.png) Again, refer to the advertisement example with the rule order policy. Say we have a user at the age of 19 again. All rules are satisfied so all outputs are given, ordered by the rule ordering. It can perhaps be used to indicate the priority of the displayed advertisements. ## Collect Multiple rules can be satisfied. The decision table result contains the output of all satisfied rules in an arbitrary order as a list. ![Hit Policy Collect](assets/decision-table/hit-policy-collect.png) With this hit policy, the output list has no ordering. So the advertisement will be arbitrary if, for example, the age is 19. Additionally, an aggregator can be specified for the Collect hit policy. If an aggregator is specified, the decision table result will only contain a single output entry. The aggregator will generate the output entry from all satisfied rules. :::info If the Collect hit policy is used with an aggregator, the decision table can only have one output. ::: The aggregator is set as the `aggregation` attribute of the `decisionTable` XML element. ```xml ``` ### Aggregators for collect In the visual representation of the decision table an aggregator can be selected in addition to the `COLLECT` hit policy. The following aggregators are supported: Visual representation XML representation Result of the aggregation Collect (Sum) SUM the sum of all output values Collect (Min) MIN the smallest value of all output values Collect (Max) MAX the largest value of all output values Collect (Count) COUNT the number of output values #### SUM aggregator The SUM aggregator sums up all outputs from the satisfied rules. ![Hit Policy Collect SUM](assets/decision-table/hit-policy-collect-sum.png) The showed decision table can be used to sum up the salary bonus for an employee. For example, the employee has been working in the company for 3.5 years. So the first, second and third rule will match and the result of the decision table is 600, since the output is summed up. #### MIN aggregator The MIN aggregator can be used to return the smallest output value of all satisfied rules. Refer to the following example of a car insurance. After years without a car crash the insurance fee will be reduced. ![Hit Policy Collect MIN](assets/decision-table/hit-policy-collect-min.png) For example, if the input for the decision table is 3.5 years, the result will be 98.83, since the first three rules match but the third rule has the minimal output. #### MAX aggregator The MAX aggregator can be used to return the largest output value of all satisfied rules. ![Hit Policy Collect MAX](assets/decision-table/hit-policy-collect-max.png) This decision table represents the decision for the amount of pocket money for a child. Depending of the age, the amount grows. For example, an input of 9 will satisfy the first and second rules. The output of the second rule is larger then the output of the first rule, so the output will be 5. A child at the age of 9 will get 5 as pocket money. #### COUNT aggregator The COUNT aggregator can be use to return the count of satisfied rules. ![Hit Policy Collect COUNT](assets/decision-table/hit-policy-collect-count.png) For example, refer to the salary bonus decision table again, this time with the COUNT aggregator. With an input of 4, the first three rules will be satisfied. Therefore, the result from the decision table will be 3, which means that after 4 years the result of the decision table is 3 salary bonuses. --- ## Input ![Input](assets/decision-table/input.png) A decision table can have one or more inputs, also called input clauses. An input clause defines the id, label, expression and type of a decision table input. An input can be edited by double-clicking on the respective colum header in the decision table. An input clause is represented by an `input` element inside a `decisionTable` XML element. ```xml season ``` ## Input ID The input ID is a unique identifier of the decision table input. It is used by Camunda to reference the input clause. Therefore, it is required. It is set as the `id` attribute of the `input` XML element. ```xml season ``` ## Input label ![Input Label](assets/decision-table/input-label.png) An input label is a short description of the input. It is set on the `input` XML element in the `label` attribute. Note that the label is not required but recommended, since it helps to understand the decision. ```xml season ``` ## Input expression ![Input Expression](assets/decision-table/input-expression.png) An input expression specifies how the value of the input clause is generated. It is usually simple and references a variable which is available during the evaluation. The expression language of the input expression is [FEEL](/components/modeler/feel/language-guide/feel-expressions-introduction.md). The expression is set inside a `text` element that is a child of the `inputExpression` XML element. ```xml season ``` ## Input type definition ![Input Type Definition](assets/decision-table/input-type-definition.png) The type of the input clause can be specified by the `typeRef` attribute on the `inputExpression` XML element. After the input expression is evaluated, it checks if the result converts to the specified type. The type should be one of the supported [data types](dmn-data-types.md). ```xml season ``` Note that the type is not required but recommended, since it helps to understand the possible input values and provides a type safety to be aware of unexpected input values. --- ## Output ![Output](assets/decision-table/output.png) A decision table can have one or more outputs, also called output clauses. An output clause defines the id, label, name and type of a decision table output. An output clause is represented by an `output` element inside a `decisionTable` XML element. ```xml ``` ## Output ID The output ID is a unique identifier of the decision table output. It is used by Camunda to reference the output clause. Therefore, it is required. It is set as the `id` attribute of the `output` XML element. ```xml ``` ## Output label ![Output Label](assets/decision-table/output-label.png) An output label is a short description of the output. It is set on the `output` XML element in the `label` attribute. Note that the label is not required but recommended, since it helps to understand the decision. ```xml ``` ## Output name ![Output Name](assets/decision-table/output-name.png) The name of the output is used to reference the value of the output. It is specified by the `name` attribute on the `output` XML element. If the decision table has more than one output, then all outputs must have a unique name. :::caution The output name may not contain any special characters or symbols (e.g. whitespace, dashes, etc.). The output name can be any alphanumeric string including the `_` symbol. For a combination of words, it's recommended to use the `camelCase` or the `snake_case` format. The `kebab-case` format is not allowed because it contains the operator `-`. If the output name contain a special character or symbol then the output can't be accessed in a [dependent decision](decision-requirements-graph.md#required-decisions) nor in a calling BPMN process. ::: :::tip If the decision table has only one output then it is recommended that the [decision ID](decision-table.md#decision-id) is used as the output name. The decision result can be accessed in a [dependent decision](decision-requirements-graph.md#required-decisions) by its decision id. Only if the decision table has more than one output then the output values are grouped under the decision id and can be accessed by their output names ( e.g. `decisionId.outputName`). ::: ```xml ``` ## Output type definition ![Output Type Definition](assets/decision-table/output-type-definition.png) The type of the output clause can be specified by the `typeRef` attribute on the `output` XML element. After an output entry is evaluated, it checks if the result converts to the specified type. The type should be one of the supported [data types](dmn-data-types.md). ```xml ``` Note that the type is not required but recommended, since it provides a type safety of the output values. --- ## Rule ![Rule](assets/decision-table/rule.png) A decision table can have one or more rules. Each rule contains input and output entries. The input entries are the condition and the output entries the conclusion of the rule. If each input entry (condition) is satisfied, then the rule is satisfied and the decision result contains the output entries (conclusion) of this rule. A rule is represented by a `rule` element inside a `decisionTable` XML element. ```xml "Winter" "Roastbeef" ``` ## Input entry ![Input Entry](assets/decision-table/input-entry.png) A rule can have one or more input entries, which are the conditions of the rule. Each input entry contains an expression in a `text` element as child of an `inputEntry` XML element. The expression language of the input entry is [FEEL](/components/modeler/feel/language-guide/feel-unary-tests.md) (unary-tests). The input entry is satisfied when the evaluated expression returns `true`. ```xml "Spring" ``` ### Empty input entry In case an input entry is irrelevant for a rule, the expression is empty, which is always satisfied. In FEEL, an empty input entry is represented by a `-`. ```xml ``` ## Output entry ![Output Entry](assets/decision-table/output-entry.png) A rule can have one or more output entries, which are the conclusions of the rule. Each output entry contains an expression in a `text` element as child of an `outputEntry` XML element. The expression language of the output entry is [FEEL](/components/modeler/feel/language-guide/feel-expressions-introduction.md). ```xml "Steak" ``` ## Description ![Description](assets/decision-table/description.png) A rule can be annotated with a description that provides additional information. The description text is set inside the `description` XML element. ```xml Save money ``` --- ## Overview(Dmn) ![Decision Table](assets/decision-table/dish-table.png) A decision table represents decision logic which can be depicted as a table. It consists of [inputs](decision-table-input.md), [outputs](decision-table-output.md) and [rules](decision-table-rule.md). A decision table is represented by a `decisionTable` element inside a `decision` XML element. ```xml ``` ## Decision name ![Decision Name](assets/decision-table/decision-name.png) The name describes the decision for which the decision table provides the decision logic. It is set as the `name` attribute on the `decision` element. It can be changed via the properties panel on the right side of the screen after selecting the respective "Decision" in the Decision Requirements Diagram view. ```xml ``` ## Decision ID ![Decision ID](assets/decision-table/decision-id.png) The ID is the technical identifier of the decision. It is set in the `id` attribute on the `decision` element. Just as the `name`, the `id` can be changed via the Properties Panel after selecting the respective "Decision" in the Decision Requirements Diagram view. Each decision should have an unique ID when it is deployed to Camunda. :::caution The decision ID may not contain any special characters or symbols (e.g. whitespace, dashes, etc.). The decision ID can be any alphanumeric string including the `_` symbol. For a combination of words, it's recommended to use the `camelCase` or the `snake_case` format. The `kebab-case` format is not allowed because it contains the operator `-`. If the decision ID contains a special character or symbol then the decision result can't be accessed in a [dependent decision](decision-requirements-graph.md#required-decisions). ::: ```xml ``` --- ## Data types Camunda supports the following data types for DMN: | Type name | Associated FEEL type | | ----------------- | --------------------------------------------------------------------------------------------------------- | | number | [Number](/components/modeler/feel/language-guide/feel-data-types.md#number) | | string | [String](/components/modeler/feel/language-guide/feel-data-types.md#string) | | boolean | [Boolean](/components/modeler/feel/language-guide/feel-data-types.md#boolean) | | time | [Time](/components/modeler/feel/language-guide/feel-data-types.md#time) | | date | [Date](/components/modeler/feel/language-guide/feel-data-types.md#date) | | dateTime | [Date-Time](/components/modeler/feel/language-guide/feel-data-types.md#date-time) | | dayTimeDuration | [Days-Time-Duration](/components/modeler/feel/language-guide/feel-data-types.md#days-time-duration) | | yearMonthDuration | [Years-Months-Duration](/components/modeler/feel/language-guide/feel-data-types.md#years-months-duration) | | Any | Wildcard for any type | The data types can be used in the type definitions of DMN elements, for example: - [Decision table input](decision-table-input.md#input-type-definition) - [Decision table output](decision-table-output.md#output-type-definition) - [Decision literal expression variable](decision-literal-expression.md#variable-type-definition) --- ## DMN in Modeler Decision Model and Notation (DMN) is a modeling approach owned by an institution called the Object Management Group ([OMG](https://www.omg.org/)), which also operates worldwide standards for [BPMN](/components/modeler/bpmn/automating-a-process-using-bpmn.md). In [DMN](/components/modeler/dmn/dmn.md), decisions are modeled and executed using a language both business analysts and developers can understand. Model a set of rules within a table, and this will yield a decision to rapidly execute a process using a decision engine like Camunda. ## Start modeling ![Start Modeling](assets/desktop-modeler-dmn/main.png) Camunda Desktop and Web Modeler both offer the same Modeling experience for DMN 1.3 models: Modeling starts in the Decision Requirements Diagram (DRD) view. From there, you can add DMN elements from the palette on the left side by dragging and dropping them onto the diagram canvas. Alternatively, you can add new elements by using the context menu that appears when you select an element in the diagram. To change the type of an element, click on the element and select the **Change element** menu icon. Use the properties panel on the right side to change the name or ID of the DMN diagram. After creating a decision and morphing it into a decision table, you can start editing the table by clicking the overlay on the upper left corner of the decision. Using the overview in the decision table view, you can jump between decision tables. ## DMN coverage Modeler supports the following DMN elements: - Decision (tables and literal expressions) - Input data - Knowledge source - Business knowledge model ## Decision tables ![Decision Table](assets/desktop-modeler-dmn/decision-table.png) By clicking the blue icon on a decision table, you can open the decision table view and start to edit it. Add **Input**, **Output**, and **Rule** elements by clicking the plus signs. Edit a table cell by clicking on it. Alternatively, the tabulator and enter keys can be used to walk through the table cells. Delete a rule or a column, copy, or insert a new rule by right clicking in the cell: ![Delete or copy rules](assets/desktop-modeler-dmn/dmn-modeler-right-click.png) Adjust the details of an input or output column (e.g., name, expression, and type) by double clicking in the header row: ![Change input or output column](assets/desktop-modeler-dmn/dmn-modeler-double-click.png) Jump between decision tables or literal expressions in your decision requirement diagram by opening and using the `Overview` on the left side: ![Jump between decision tables](assets/desktop-modeler-dmn/dmn-modeler-toggle-overview.png) ## Literal expressions ![New DMN Literal Expression](assets/desktop-modeler-dmn/literal-expression.png) You can also edit literal expressions. Just as with decision tables, in the decision requirement diagram view, click the blue icon to _drill-down_ into the literal expression view and start editing. ## Business knowledge models :::caution Viewing the result of BKM evaluation is currently not supported in Operate. ::: A _business knowledge model_ (BKM) is a reusable function containing a piece of decision logic. Typically, a BKM instantiates business logic that is required in multiple decisions, such as a common computation. For example, an amortization formula might be used in different loan application processes. You can make BKM elements executable using literal expressions written in FEEL, in almost the same way you would create a decision using a literal expression. A BKM literal expression can optionally accept parameters to be used as inputs to the FEEL expression, and it returns a single result whose name is the same as the BKM element name. Once you’ve created a BKM, it appears in autosuggestions when you’re using literal expressions to create decision logic. Currently, you can only reuse BKMs within the same decision. To create an executable BKM: 1. Add a BKM element to the canvas. 2. Name the BKM element. This name will be used for the result variable. 3. On the context menu, select **Change type > Literal expression**. 4. Click the blue literal expression icon (**{}**) on the BKM element. 5. Add parameters by hovering over the **()** and clicking the edit button. All changes to the BKM are saved automatically. 6. Enter a FEEL expression in the expression box. 7. Select a type for the result. --- ## Element templates in Modeler :::note Element templates are available only in BPMN diagrams. ::: Element templates let you extend [Modeler](https://camunda.org/bpmn/tool/) with domain-specific diagram elements, such as service and user tasks. ## Creating and editing element templates Element templates are defined in template descriptor files using JSON format. Connector templates are a specific type of element template. You can edit element templates in any text editor or in Web Modeler's built-in template editor. Depending on the editor, the [JSON schema](defining-templates.md#json-schema-compatibility) may be recognized to provide additional editing support, such as formatting, code completion, and error highlighting. To learn more about customizing your templates, see [defining templates](./defining-templates.md). ## Next steps Read more about using element templates in Web and Desktop Modeler: - [Using element templates in Web Modeler](/components/hub/workspace/modeler/element-templates/using-templates.md) - [Using element templates in Desktop Modeler](/components/modeler/desktop-modeler/element-templates/using-templates.md) - [Element templates with dependencies](./element-template-with-dependencies.md) --- ## Additional resources Try playing around with custom elements and [our example templates](https://github.com/camunda/camunda-modeler/tree/master/resources/element-templates/cloud-samples.json). If you get stuck, ask for help in our [forums](https://forum.camunda.org/c/modeler). --- ## Defining templates An element template is defined in a template descriptor files as a JSON object. The element template object is divided into required and optional key-value pairs: ## Required keys - [`$schema : String`](./template-metadata.md#validation-schema): URI pointing towards the [JSON schema](https://json-schema.org/) which defines the structure of the element template `.json` file. Element template schemas are maintained in the [element templates JSON schema](https://github.com/camunda/element-templates-json-schema) repository. - [`id : String`](./template-metadata.md#identification-id-and-version): Identifier of the template. - [`name : String`](./template-metadata.md#discoverability-name-description-keywords-icon-documentationref-and-category): Name of the template. Shown in the element template selection modal and in the properties panel on the right side of the screen (after applying an element template). - [`appliesTo : Array`](./template-metadata.md#supported-bpmn-types-appliesto-and-elementtype): List of BPMN element types the template can be applied to. - [`properties : Array`](./template-properties.md): List of properties that the template applies to the BPMN element. Each property object defines the type of input and how its value is bound to a BPMN or Camunda extension property. ## Optional keys - [`version : Integer`](./template-metadata.md#identification-id-and-version): Property to support template versioning and upgrading. If you add a version to a template, it is considered unique based on its ID and version. - [`description : String`](./template-metadata.md#discoverability-name-description-keywords-icon-documentationref-and-category): Description of the template. Shown in the element template selection modal and in the properties panel (after applying an element template). - [`keywords : Array`](./template-metadata.md#discoverability-name-description-keywords-icon-documentationref-and-category): List of keywords. Helps users find the template through search. Keywords are used for search and filtering but are not displayed in the UI. - [`category : Object`](./template-metadata.md#discoverability-name-description-keywords-icon-documentationref-and-category): Category for the template. The template appears under the category name in the element template selection modal. - [`documentationRef : String`](./template-metadata.md#discoverability-name-description-keywords-icon-documentationref-and-category): URL pointing to the template's documentation. Shown in the properties panel (after applying an element template). - [`icon : Object`](./template-metadata.md#discoverability-name-description-keywords-icon-documentationref-and-category): Sets the template's icon. The icon is shown in the element template selection modal and in the properties panel (after applying an element template). - [`engines : Object`](./template-metadata.md#engine-compatibility-engines): Dictionary of environments compatible with the template. The environment version is specified using a semantic version range. - [`elementType : Object`](./template-metadata.md#supported-bpmn-types-appliesto-and-elementtype): Sets the type of the element. The element is replaced with the specified type when a user applies the template. - [`groups : Object`](./template-metadata.md#grouping-properties-groups): Defines groups of property input fields. Groups are sections in the properties panel. Properties can be assigned a group. Some keys and values require other keys to be set. If your editor supports the [JSON schema](https://json-schema.org/), it will flag missing keys as errors. The Web Modeler's editor will also show these errors in the template editor problems panel. Below you find a simple example of an element template with the most commonly used keys: ```json { "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", "name": "Template 1", "id": "sometemplate", "description": "some description", "keywords": ["search alias", "create action"], "version": 1, "engines": { "camunda": "^8.6" }, "appliesTo": ["bpmn:Task"], "elementType": { "value": "bpmn:ServiceTask" }, "properties": [ { "label": "Input Variable", "type": "String", "value": "someProcessVariable", "binding": { "type": "zeebe:input", "name": "anInputVariable" } } ] } ``` ## Creating multiple templates in one file You can define multiple templates in one JSON file by wrapping them in an array. :::warning This is a Desktop Modeler-specific feature. Web Modeler requires each template to be in a separate file. ::: ```json [ { ... "name": "Template 1", "id": "some-template-id", "description": "some description", ... }, { ... "name": "Template 2", "id": "another-template-id", "description": "another description", ... } ] ``` ## Creating and editing templates You can create and edit element templates in the text editor of your choice. Connector templates are a specific type of element template, so the same applies to them. If your editor supports the [JSON schema](https://json-schema.org/), it will recognize the structure of the template and provide additional editing support, such as formatting, code completion, and error highlighting. The [Web Modeler](/components/connectors/manage-connector-templates.md) offers a built-in template editor with validation and error highlighting, as well as a live preview of the properties panel with the applied template. ## Further reading For detailed information about specific aspects of template development, see: - **[Template metadata](./template-metadata.md)** - Learn more about template key-value pairs that help you version your template, make it discoverable, and allow you to define compatability. - **[Template properties](./template-properties.md)** - The properties array is the heart of the element template. Here you can define what properties should be applied to the BPMN element and how these properties should be shown and validated in the properties panel. - **[Example template](./template-example.md)** - A complete example showing how to create an element template. --- ## Element templates with dependencies When creating element templates, you may want to link to a resource like a [form](/components/modeler/forms/camunda-forms-reference.md), or pre-populate a [secret](/components/connectors/use-connectors/index.md#using-secrets) expression. Your template might require a specific [job worker](/components/concepts/job-workers.md) to execute an action. These are all examples of dependencies. ![Element template dependencies](./img/element-template-dependencies.png) Element templates can depend on: - [Camunda forms](/components/modeler/forms/camunda-forms-reference.md): used in user tasks. - [RPA scripts](/components/rpa/overview.md): used in service tasks. - [BPMN process](/components/modeler/bpmn/bpmn.md): used in a call activity. This may introduce nested dependencies (e.g., a called process may depend on other processes and/or resources). - [DMN decisions](/components/modeler/dmn/dmn.md): used in business rule tasks. - [Job workers](/components/concepts/job-workers.md): provide behavior for service tasks such as message send events, send tasks, service tasks, business rule tasks, or custom connector runtime. - Secrets: used in connector elements to access sensitive values (see [secrets in self-managed](/self-managed/components/connectors/connectors-configuration.md#secrets) and [secrets in SaaS](/components/hub/organization/manage-clusters/manage-secrets.md)). To make a template available for use, complete two key steps: 1. **Provision dependencies at runtime**: Make dependencies available in the clusters that need them. - For job workers, ensure the runtime is started and connected to the cluster (see [hosting custom connectors](/components/connectors/custom-built-connectors/host-custom-connector.md)). - Secrets must be configured beforehand. - Other dependency types (e.g., Camunda forms, RPA scripts, DMN decisions) need to be deployed to the cluster. 2. **Make the template available at design time**: Ensure Web Modeler or Desktop Modeler can access the template for use in your projects. ## Next steps - [Defining element templates](./defining-templates.md) - [Publishing an element template in Web Modeler](/components/hub/workspace/modeler/element-templates/manage-element-templates.md#publish-an-element-template) - [Using element templates in Web Modeler](/components/hub/workspace/modeler/element-templates/using-templates.md) - [Configuring element templates in Desktop Modeler](/components/modeler/desktop-modeler/element-templates/configuring-templates.md) - [Using element templates in Desktop Modeler](/components/modeler/desktop-modeler/element-templates/using-templates.md) --- ## Example template This page provides a complete example of an element template that demonstrates how to define user-editable fields and their mapping to BPMN 2.0 XML and Camunda extension elements. ## REST connector template Let us consider the following example that defines a template for invoking a REST API via a service task: ```json { "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", "name": "REST connector", "id": "io.camunda.examples.RestConnector", "description": "A REST API invocation task.", "appliesTo": ["bpmn:ServiceTask"], "properties": [ { "type": "Hidden", "value": "http", "binding": { "type": "zeebe:taskDefinition", "property": "type" } }, { "label": "REST Endpoint URL", "description": "Specify the url of the REST API to talk to.", "type": "String", "binding": { "type": "zeebe:taskHeader", "key": "url" }, "constraints": { "notEmpty": true, "pattern": { "value": "^https?://.*", "message": "Must be http(s) URL." } } }, { "label": "REST Method", "description": "Specify the HTTP method to use.", "type": "Dropdown", "value": "get", "choices": [ { "name": "GET", "value": "get" }, { "name": "POST", "value": "post" }, { "name": "PATCH", "value": "patch" }, { "name": "DELETE", "value": "delete" } ], "binding": { "type": "zeebe:taskHeader", "key": "method" } }, { "label": "Request Body", "description": "Data to send to the endpoint.", "value": "", "type": "String", "optional": true, "binding": { "type": "zeebe:input", "name": "body" } }, { "label": "Result Variable", "description": "Name of variable to store the response data in.", "value": "response", "type": "String", "optional": true, "binding": { "type": "zeebe:output", "source": "= body" } } ] } ``` ## How the example works The example defines five custom fields, each mapped to different technical properties: - **Task type**: The value `http` is mapped to the `type` property of a `zeebe:taskDefinition` element in BPMN 2.0 XML. This field is hidden from users since it's a technical requirement. - **REST Endpoint URL**: Mapped to a `task header` with the key `url`. This field includes validation to ensure it's a valid HTTP(S) URL. - **REST Method**: Mapped to a `task header` with the key `method`. Uses a dropdown to provide predefined HTTP method options. - **Request Body**: Mapped to a local variable via an `input parameter` named `body`. This field is optional, so it won't appear in the XML if left empty. - **Result Variable**: Mapped into a process variable via an `output parameter`. The response data will be stored in the specified variable name. ## Visual result The task type is hidden to the user, as it is a technical implementation detail. The other properties specified in the template can be edited through the properties panel after the user has applied the template as shown in the following screenshot: ![Custom Fields](./img/overview.png) ## Key concepts demonstrated This example showcases several important template features: - **Hidden properties**: Setting technical values that users shouldn't modify. - **Input validation**: Using constraints to ensure valid URLs. - **Dropdown choices**: Providing predefined options for user selection. - **Optional bindings**: Fields that don't persist empty values in the XML. - **Variable mapping**: How to map data between the process and external systems. You can use this example as a starting point for creating your own element templates. --- ## Template metadata The metadata of an element template contains important information about the template itself, such as its name, description, version, compatibility with different Camunda versions, and the schema that is used to validate the template. ## Validation: `$schema` `$schema` is a required key-value pair and must be set. The application uses the `$schema` property to ensure compatibility for a given element template. You can find [the latest supported versions here](https://www.npmjs.com/package/@camunda/zeebe-element-templates-json-schema). The JSON schema versioning is backward-compatible, meaning that all versions including or below the current one are supported. :::info The Web Modeler only supports element templates pointing to the latest schema version: `https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json` ::: The Desktop Modeler ignores element templates defining a higher `$schema` version and logs a warning message. For example, given the following `$schema` definition, the application takes `0.9.1` as the JSON schema version of the element template: ```json { "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema@0.9.1/resources/schema.json", ... } ``` ## Identification: `id` and `version` - `id : String` is a required key and must be set. - `version : Integer` is optional but Camunda strongly recommends setting it. The `id` key defines the identifier of the template. If no `version` is set, templates with the same `id` are regarded as equal, independent of their other key-value pairs. If `version` is set, the modeler treats templates with the same `id ` and `version` as identical, independent of their other key-value pairs. Thus, if you plan to make any changes to your template and want to support [template evolution](https://github.com/bpmn-io/element-templates/blob/main/docs/LIFE_CYCLE.md#overview), maintain a `version` key-value pair on your template. Templates with the same `id` and different `version` values offer an upgrade path. ```json { "id": "sometemplate", "version": 1, ... } ``` Once a template with a new `version` is available to users, the editor tooling suggests an upgrade, [preserving element configurations](https://github.com/bpmn-io/element-templates/blob/main/docs/LIFE_CYCLE.md#upgrade-behavior) on a best-effort basis. :::tip Versioning is an important cornerstone of template evolution. Review the [upstream documentation](https://github.com/bpmn-io/element-templates/blob/main/docs/LIFE_CYCLE.md#overview) to understand the foundations of our upgrade mechanism and of the element template lifecycle. ::: ## Discoverability: `name`, `description`, `keywords`, `icon`, `documentationRef`, and `category` - `name` is a required key and must be set. - `description`, `keywords`, `icon`, `documentationRef`, and `category` are optional key-value pairs. These keys define the user-facing metadata of the template. They help the template users to discover and understand the purpose of the template. They are shown when selecting a template and when the template has been applied to an element. - `name : String` defines the name of the template. - `description : String` provides additional information about the template. - `keywords : Array` list of keywords that can help users find this template. Keywords are used for search and filtering but are not displayed in the UI. - `icon : Object` defines the templates icon. The icon contents must be a valid [data](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) or HTTP(s) URL. We recommend using square icons as they are rendered at 18x18 pixels on the canvas and 32x32 pixels in the properties panel. - `documentationRef : String` URL pointing to the template's documentation. It is shown in the properties panel. - `category : Object` defines a category used to group templates in the element template selection list. If not defined, the template will be displayed in the **Templates** section. - `id : String` required key that defines the unique identifier of the category. - `name : String` required key that defines the name of the category that the template is shown in. It is generally a good idea to provide a proper description of you template. This helps users to understand the purpose of the template and how to use it. In case you require more space to explain the template, you can also provide a `documentationRef` pointing to a more detailed documentation page. This is particularly useful for templates that require external dependencies, such as custom connector implementations. Another good practice is to use a custom icon for your template. This helps users to quickly identify the template in the selection modal and in the properties panel. If you use the Web Modeler's element template editor, you can upload an image and Web Modeler will take care of encoding it as a data URL. ```json { ..., "name": "Template 1", "description": "some description", "keywords": [ "search alias", "create action" ], "icon": { "contents": "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' viewBox='0 0 22 22' fill='none'%3E%3Ccircle cx='11' cy='11' r='9' fill='black'/%3E%3Ctext x='6.9' y='14.9' fill='white' style='font-family: Arial; font-size: 10px;'%3EM%3C/text%3E%3C/svg%3E" }, "documentationRef": "https://example.com/docs/template-1", "category": { "id": "custom-templates", "name": "Custom Templates" }, ... } ``` ## Engine compatibility: `engines` - `engines : Object` is an optional key-value pair. - `camunda : SemanticVersion` is an optional key to define compatibility with Camunda Orchestration Cluster Versions. - `camundaDesktopModeler : SemanticVersion` is an optional key to define compatibility with Desktop Modeler versions. - `camundaWebModeler : SemanticVersion` is an optional key to define compatibility with Web Modeler versions. Define [template compatibility](https://github.com/bpmn-io/element-templates/blob/main/docs/LIFE_CYCLE.md#compatibility) with execution platforms (Camunda Orchestration Cluster versions) and related components (such as Web Modeler) using the `engines` key. This key has a dictionary object as its value, where the execution platform names are the keys, and the [semantic version](https://semver.org/) ranges are the values. For example, the following `engines` definition specifies that the template is compatible with Camunda 8.6 or higher. ```json { ..., "engines": { "camunda": ">8.5" } } ``` Compatibility is only validated if the platform version is provided by both the template and the modeler. In the example below, the template is compatible with the specified versions of both Desktop and Web Modeler, but it requires Camunda version 8.6 or higher for both: ```json { ..., "engines": { "camunda": ">8.5", "camundaDesktopModeler": ">=5.30", "camundaWebModeler": "^8.5.5" } } ``` You can also use this feature to explicitly specify a template's incompatibility with a platform. For instance, the following template is incompatible with all versions of Web Modeler: ```json { ..., "engines": { "camundaWebModeler": "0" } } ``` If no `engines` are specified, a template is considered compatible with any execution platform version. :::tip Review the [upstream documentation](https://github.com/bpmn-io/element-templates/blob/main/docs/LIFE_CYCLE.md#overview) to learn more about template evolution and the template lifecycle. ::: ## Supported BPMN types: `appliesTo` and `elementType` - `appliesTo` is a required key and must be set. - `elementType` is an optional key that can be required under some circumstances, see below for more information. These two key-value pairs define what BPMN types the template can be applied to (`appliesTo`) and whether the element is replaced with a different type when the template is applied (`elementType`). - `appliesTo : Array`: specifies the BPMN types the template can be applied to. The template will only be selectable for these types of elements in the modeler. Currently, element templates may be used on the following BPMN elements: - `bpmn:Activity` (including user tasks, service tasks, call activities, ad-hoc subprocesses, and others) - `bpmn:SequenceFlow` (for maintaining `condition`) - `bpmn:Process` - `bpmn:Event` - `elementType : Object`: If you configure `elementType` on a template, the element is replaced with the specified type when a user applies the template. - `value : String`: Is a required key. The BPMN element is changed to this type the template is applied. - `eventDefinition: String`: This key is used when templating an event. It can be ignored when templating any other element type. Supported values are: - `"bpmn:MessageEventDefinition"` use this value when you template a message event. - `"bpmn:SignalEventDefinition"` use this value when you template a signal event. - `"bpmn:TimerEventDefinition"` use this value when you template a timer event. Some properties require a specific BPMN type, and thus a specific value for `elementType`, to work correctly. For example, if the template sets `zeebe:calledDecision` on an element and `appliesTo` is set to `bpmn:Task`, the `elementType` must be set to `bpmn:BusinessRuleTask`. These constraints are checked based on the [element template schema](./template-metadata.md#validation-schema) by your editor (if it supports JSON schema) and by the modeler when it loads the templates. ```json { ..., "appliesTo": [ "bpmn:Task" ], "elementType": { "value": "bpmn:ServiceTask" } } ``` ## Grouping properties: `groups` - `groups` is an optional key. You can define `groups` to organize custom fields into. The fields will be shown in their assigned group in the properties panel. You can also specify whether a group is expanded or collapsed by default. This helps you to highlight important fields and to reduce visual clutter. Groups can have the following attributes: - `id : String`: Unique identifier of the group - `label : String`: Label of the group - `tooltip : String`: Tooltip for the group (optional) - `openByDefault : Boolean`: Whether the group will be expanded in the properties panel (optional, default: `false`) A property can be assigned to a group by setting the [`group` key](./template-properties.md#grouping-fields-group) to the group's `id` value. ```json { ..., "groups": [ { "id": "definition", "label": "Task definition", "openByDefault": true }, { "id": "request", "label": "Request payload" }, { "id": "result", "label": "Result mapping" }, { "id": "authentication", "label": "Authentication", "tooltip": "Optional authentication settings" } ], "properties": [ ... ] } ``` ## Deprecating a template: `deprecated` - `deprecated` is an optional key. Use `deprecated` to mark a [template as deprecated](https://github.com/bpmn-io/element-templates/blob/main/docs/LIFE_CYCLE.md#deprecation). :::info By deprecating an element template, you prevent its _future use_. Users will no longer see the template when choosing a template, changing an element's type, or creating a new element. Existing uses continue to work, with a deprecation hint shown in the modeler UI. ::: The `deprecated` property can be an object or a boolean. If an object, it has the following attributes: - `message: String`: A message to display to the user - `documentationRef : String`: A link to documentation, i.e., to describe upgrade migration procedures This information allows users to make sense of the deprecation and understand how to migrate to an undeprecated template: ```json { ..., "deprecated": { "message": "Migrate to My Other Custom Task", "documentationRef": "https://localhost/migrate-to-other" }, ... } ``` When `deprecated` is a boolean (`true`), a deprecation hint appears in the properties panel without further help for the user: ```json { ..., "deprecated": true, ... } ``` :::tip Review the [upstream documentation](https://github.com/bpmn-io/element-templates/blob/main/docs/LIFE_CYCLE.md#overview) to learn more about template evolution and the template lifecycle. ::: --- ## Template properties The `properties` array is where you define what properties should be applied to the BPMN element and how the properties panel will present these properties to the user when the template is applied. The underlying concept is very simple: 1. You will create one property object for each property that should be defined by the template. 2. Each property object contains key-value pairs to define how the property is presented and how its value can be changed by the user. 3. Each property object contains one `binding` object that specifies how the property is mapped to BPMN 2.0 XML. Element templates can only set properties that can be described by bindings supported by the element template schema. You can find the full list of supported bindings in the [bindings](#binding-an-input-to-a-bpmn-or-camunda-element-property-binding) section. When the user applies a template, the properties panel hides all BPMN 2.0 XML and Camunda extension element properties that can be defined by bindings in an element template. The template author must explicitly make the properties user-configurable to show them in the properties panel once the user has applied the template. For example, if a template does not contain a property object with the binding type `zeebe:input`, the template user will not be able to define an input mapping for the element once the template is applied. :::info Some properties — such as element documentation and multi-instance configurations — cannot be set by element templates. They are situational and require knowledge of the process context to be used. As they are never part of any element template, users can configure them independently of an applied template. ::: ## What is part of a property? You build your template by adding property objects to the `properties` array. The property object keys are divided into required and optional keys: ### Required keys - [`binding : Object`](#binding-an-input-to-a-bpmn-or-camunda-element-property-binding): An object specifying how the property is mapped to BPMN or Camunda extensions. ### Optional keys - [`type : "String" | "Text" | "Boolean" | "Dropdown" | "Hidden"`](#setting-the-input-type-type): Defines the input type in the properties panel. - [`value : String | Number | Boolean`](#setting-a-default-value-value): The default value used if the bound property is not yet set by the user or if the type is `Hidden`. - [`generatedValue : Object`](#generating-a-value-generatedvalue): Configuration used to generate a value when the property is applied to an element. - [`placeholder : String`](#setting-a-text-placeholder-placeholder): Placeholder text shown in the input field when it is empty. - [`feel : "required" | "optional" | "static"`](#adding-feel-editor-support-feel): Defines whether the property supports FEEL expressions. - `label : String`: Label text shown above the property input. - `tooltip : String`: Tooltip text shown when hovering over the label. - `description : String`: Description text shown below the property input. - [`optional : Boolean`](#preventing-persisting-empty-values-optional): Controls whether properties persist empty values in the underlying BPMN 2.0 XML. - [`constraints : Object`](#validating-user-input-constraints): A list of editing constraints applied to the property value. - [`group : String`](#grouping-fields-group): The group that the property belongs to. - [`condition : Object`](#showing-properties-conditionally-condition): A condition that controls when the property is active and visible. - `id : String`: An identifier used to reference the property in conditional properties. - [`editable : Boolean`](#preventing-edits-editable): Controls whether the property is editable in the properties panel. - [`entriesVisible : Boolean | Object`](#control-visibility-of-default-properties-panel-entries-entriesvisible): Controls whether default properties are shown alongside properties defined in the element template. Not all keys and values are compatible with each other. Some keys or values require other keys to be set to a certain value, even if the key is marked as optional above. For more information, see the documentation below. If your editor (e.g. VS Code) offers validation based on JSON schema, these incompatibilities or missing key-value pairs are highlighted as you edit your template. The Web Modeler's element template editor offers an additional problems panel that shows these errors with additional descriptions to help you better understand what needs to be fixed. The Desktop Modeler shows these errors with additional descriptions in the output tab, when it tries to load an invalid template. For most purposes, `binding`, `label`, `type`, and `value` are sufficient to define a property. All property objects are defined inside the `properties` array: ```json { "schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", ..., "properties": [ { "label": "Some property", "type": "String", "binding": { ... } }, { "label": "Some other property", "type": "Number", "binding": { ... } }, ... ] } ``` For a comprehensive example showing how to create a REST connector template with all the concepts covered in this documentation, see the [complete template example](./template-example.md) page. The key-value pairs of the property object are explained in the following sections. ## Setting the input type: `type` The input types `String`, `Text`, `Number`, `Boolean`, `Dropdown`, and `Hidden` are available. ### String input type The `String` type maps to a single-line input field. Refer to the [FEEL](#adding-feel-editor-support-feel) section to use `Strings` as an expression. ### Text input type The `Text` type maps to a multi-line text area. Refer to the [FEEL](#adding-feel-editor-support-feel) section to use `Text` as an expression. ### Hidden input type The `Hidden` type is not shown in the properties panel. It is used to set [static](#setting-a-default-value-value) or [generated](#generating-a-value-generatedvalue) values that should not be changed by the user. ### Number input type The `Number` type maps to a number input field. Refer to the [FEEL](#adding-feel-editor-support-feel) section to use `Numbers` as expressions. ### Boolean / checkbox input type The `Boolean` type maps to a checkbox that can be toggled by the user. When checked, it maps to `true` in the respective field. Additionally, refer to the [FEEL](#adding-feel-editor-support-feel) section to use `Booleans` as expressions. ### Dropdown input type The `Dropdown` type allows users to select from a number of pre-defined options that are stored in a `choices` array as `{ name : String, value : String }` pairs: ```json { "label": "REST Method", "description": "Specify the HTTP method to use.", "type": "Dropdown", "value": "get", "choices": [ { "name": "GET", "value": "get" }, { "name": "POST", "value": "post" }, { "name": "PATCH", "value": "patch" }, { "name": "DELETE", "value": "delete" } ], "binding": { "type": "zeebe:taskHeader", "key": "method" } } ``` The resulting properties panel control looks like this: ![properties panel drop down](./img/field-dropdown.png) ## Setting a default value: `value` The `value` key defines a static default value for a property. The value is applied to the property defined by the [`binding`](#binding-an-input-to-a-bpmn-or-camunda-element-property-binding) when the template is applied to an element until a user provides their own input value. `value` should be defined whenever the input type is [`Hidden`](#hidden-input-type). The value of `value` must match the `type` of the property and must be a string if the `type` is hidden. ```json { "value": "4", "type": "Hidden", "binding": { "type": "zeebe:taskDefinition", "property": "retries" } } ``` ## Generating a value: `generatedValue` As an alternative to static `value`, you can use a generated value. The value is generated when a property is applied to an element. Currently, the generated value can be a UUID: ```json { "generatedValue": { "type": "uuid" }, "type": "Hidden", "binding": { "type": "zeebe:property", "name": "id" } } ``` ## Setting a text placeholder: `placeholder` The following property types support the `placeholder` attribute: - [`String`](#string-input-type) - [`Text`](#text-input-type) The placeholder is displayed when a field is empty: ```json { "label": "Web service URL", "type": "String", "binding": { ... }, "placeholder": "https://example.com" } ``` ## Adding FEEL editor support: `feel` The following input types support the `feel` property: - `String` - `Text` - `Number` - `Boolean` ### FEEL required The properties panel will display the field as a FEEL editor and will show a visual indication that a FEEL expression is required: ```json { "label": "Required FEEL Expression", "type": "String", "feel": "required", ... } ``` ### FEEL optional The properties panel will show an indicator to switch to a FEEL expression. When activated, the field displays as a FEEL editor: ```json { "label": "Optional FEEL Expression", "type": "String", "feel": "optional", ... } ``` For `Boolean` and `Number` fields, the value will always be persisted as a FEEL expression. This ensures that the value will not be interpreted as a string when evaluated in the engine. ### FEEL static The value of `feel: static` is only valid for `Boolean` and `Number` fields. Similar to [FEEL optional](#feel-optional), the value of the field will be persisted as a FEEL expression. However, there is no toggle to switch to a FEEL editor and only a static value can be entered: ```json { "label": "Static FEEL value", "type": "Number", "feel": "static", ... } ``` For binding types `zeebe:input` and `zeebe:output`, `feel: static` is the default value used in case of missing `feel` property. ## Binding an input to a BPMN or Camunda element property: `binding` The previous sections describe how to display a property to the user in the properties panel and how to configure its value. These inputs need to be mapped to the underlying BPMN 2.0 XML or Camunda extensions by using a `binding` object. The `binding` is an object with a mandatory `type` key and an additional parameter depending on the binding's `type` value. `binding.type` defines what kind of BPMN or Camunda element is targeted by the binding. The additional binding parameter is a key on the binding object--for example, `name`, `key`, or `property`. That key-value pair defines the target for the default [`value`](#setting-a-default-value-value) or user input. Note that adherence to the following bindings s is enforced by design. If the template does not adhere to them, the modeler logs a validation error and ignores the respective element template. To fully grasp the concept of bindings, it is helpful to have a good understanding of BPMN 2.0 XML and Camunda extensions. If you want to learn more about a certain BPMN element and its properties, you can read through the BPMN section on [Tasks](/components/modeler/bpmn/tasks.md), [Events](/components/modeler/bpmn/events.md), and [Subprocesses](/components/modeler/bpmn/subprocesses.md). Each page on an element contains a description of its properties and an example XML representation. :::info If a property cannot be set via any of the bindings described below, it cannot be set by an element template. For example, multi-instance configurations cannot be set by an element template. ::: :::warning If you add multiple properties with equal `binding` objects, the behavior is undefined. ::: The **mapping result** in the following section uses `[userInput]` to indicate where the input provided by the user in the properties panel is set in the BPMN XML. If the user provides no input, the value specified in [`value`](#setting-a-default-value-value) is displayed and used for `[userInput]`. Square brackets, `[]`, are used to indicate what the binding parameters are mapped to in the XML. ### Input mapping: `zeebe:input` | **Binding `type`** | `zeebe:input` | | -------------------------- | -------------------------------------------------------------------------------- | | **Valid property `type`s** | `String` `Text``Hidden``Dropdown``Boolean``Number` | | **Binding parameters** | `name`: The name of the input parameter | | **Mapping result** | `` | Configures a [task header](../../bpmn/service-tasks/#task-headers). ```json { ..., "value": "aHeaderValue", "binding": { "type": "zeebe:taskHeader", "key": "aHeaderKey" } } ``` ### Task definition: `zeebe:taskDefinition` | **Binding `type`** | `zeebe:taskDefinition` | | -------------------------- | --------------------------------------------------------------------------------- | | **Valid property `type`s** | `String` `Text``Hidden``Dropdown` | | **Binding parameters** | `property`: The name of the task definition property. Can be `type` or `retries`. | | **Mapping result** | `` | Configures the [task](../../bpmn/service-tasks/#task-definition) for a service or user task. ```json [ { ..., "value": "aTaskType", "binding": { "type": "zeebe:taskDefinition", "property": "type" } }, { ..., "value": "3", "binding": { "type": "zeebe:taskDefinition", "property": "retries" } }, ... ] ``` ### Task definition: `zeebe:taskDefinition:type` (deprecated) :::danger `zeebe:taskDefinition:type` is a deprecated binding. Instead, use `zeebe:taskDefinition` with `property=type`. ::: | **Binding `type`** | `zeebe:taskDefinition:type` | | -------------------------- | --------------------------------------------------- | | **Valid property `type`s** | `String` `Text``Hidden``Dropdown` | | **Binding parameters** | | | **Mapping result** | `` | Configures the [task type](../../bpmn/service-tasks/#task-definition) for a service or user task. ```json { ..., "value": "aTaskType", "binding": { "type": "zeebe:taskDefinition:type" } } ``` ### Extension properties: `zeebe:property` | **Binding `type`** | `zeebe:property` | | -------------------------- | ----------------------------------------------------- | | **Valid property `type`s** | `String``Text``Hidden``Dropdown` | | **Binding parameters** | `name`: The name of the property | | **Mapping result** | `` | Configures generic BPMN element properties that are text, boolean, and numeric types. Additionally, expression types `completionCondition` and `conditionExpression` are supported. Other properties, such as references and complex property types, are currently NOT supported and will lead to runtime errors when modeling. ```json [ { ..., "value": "= someValue >= 1", "binding": { "type": "property", "name": "completionCondition" } }, { ..., "value": "customPropertyValue", "binding": { "type": "property", "name": "mynamespace:customProperty" } }, ... ] ``` ### Message name: `bpmn:Message#property` | **Binding `type`** | `bpmn:Message#property` | | -------------------------- | -------------------------------------------------- | | **Valid property `type`s** | `String``Text``Hidden``Dropdown` | | **Binding parameters** | `name`: The name of the property | | **Mapping result** | `` | The `bpmn:Message#property` binding allows you to set properties of a `bpmn:Message` referred to by the templated element. This binding is only valid for templates of events with `bpmn:MessageEventDefinition`, receive tasks, and send tasks. ```json { ..., "generatedValue": { "type": "uuid" }, "binding": { "type": "bpmn:Message#property", "name": "name" } } ``` :::note When designing a template for a message receive task or event, it is sufficient to define the binding for the message [name](#message-name-bpmnmessageproperty) and the [correlation key](#message-correlation-key-bpmnmessagezeebesubscriptionproperty). The message ID is automatically generated when the template is applied. The `messageRef` [property](#primitive-bpmn-properties-property) does not have to be defined. Remember that the message [name and correlation key](/components/concepts/messages.md#message-subscriptions) define the correlation characteristics of a message and can be shared by [multiple process definitions](/components/concepts/messages.md#message-cardinality). ::: ### Message correlation key: `bpmn:Message#zeebe:subscription#property` | **Binding `type`** | `bpmn:Message#zeebe:subscription#property` | | -------------------------- | -------------------------------------------------- | | **Valid property `type`s** | `String``Text``Hidden``Dropdown` | | **Binding parameters** | `name`: The name of the property | | **Mapping result** | `` | The `bpmn:Message#zeebe:subscription#property` binding allows you to set properties of a `zeebe:subscription` set within `bpmn:Message` referred to by the templated element. This binding is only valid for templates of events with `bpmn:MessageEventDefinition` and receive tasks. ```json { ..., "value": "=aCorrelationKey", "binding": { "type": "bpmn:Message#zeebe:subscription#property", "name": "correlationKey" } } ``` :::note The binding name of `correlationKey` is not applicable to message start events on a process. In such cases, the property is automatically hidden. ::: ### Signal name: `bpmn:Signal#property` | **Binding `type`** | `bpmn:Signal#property` | | -------------------------- | ---------------------------------------------------------------------- | | **Valid property `type`s** | `String``Text``Hidden``Dropdown` | | **Binding parameters** | `name`: The name of the property. Supported properties: `name`. | | **Mapping result** | `` | The `bpmn:Signal#property` binding allows you to set the name of a `bpmn:Signal` referred to by the templated element. This binding is only valid for templates of events with `bpmn:SignalEventDefinition`. ```json { ..., "value": "aSignalName", "binding": { "type": "bpmn:Signal#property", "name": "name" } } ``` ### Timer event definition property name: `bpmn:TimerEventDefinition#property` | **Binding `type`** | `bpmn:TimerEventDefinition#property` | | -------------------------- | -------------------------------------------------------------------------------------------------------- | | **Valid property `type`s** | `String``Text``Hidden``Dropdown` | | **Binding parameters** | `name`: The name of the property.Supported properties: `timeDate`, `timeCycle`, and `timeDuration`. | | **Mapping result** | `[userInput]` | The `bpmn:TimerEventDefinition#property` binding allows you to configure [timer event definitions](../../bpmn/timer-events/) for timer events. This binding is only valid for templates of events with `bpmn:TimerEventDefinition` set via `elementType.eventDefinition`. Only one of `timeDate`, `timeCycle`, or `timeDuration` can be defined per template. ```json { ..., "value": "0 0 9-17 * * MON-FRI", "binding": { "type": "bpmn:TimerEventDefinition#property", "name": "timeCycle" } } ``` :::note **Property-specific constraints:** - **`timeDuration`**: Cannot be used on process-level start events. Valid on event subprocess start events, intermediate catch events, and boundary events. - **`timeCycle`**: Can only be used on start events and boundary events. When a template with `timeCycle` is applied: - Boundary events are automatically marked as non-interrupting. - Start events in event subprocesses are also marked as non-interrupting. - **`timeDate`**: Can be used on process-level and event subprocess start events, intermediate catch events, and boundary events. ::: ### Conditional event definition property: `bpmn:ConditionalEventDefinition#property` | **Binding `type`** | `bpmn:ConditionalEventDefinition#property` | | -------------------------- | ----------------------------------------------------------------------------------------------------------------- | | **Valid property `type`s** | `String``Text``Hidden` | | **Binding parameters** | `name`: The name of the property.Supported property: `condition`. | | **Mapping result** | `[userInput]` | The `bpmn:ConditionalEventDefinition#property` binding allows you to configure the condition expression for [conditional events](../../bpmn/conditional-events/). This binding is only valid for templates of events with `bpmn:ConditionalEventDefinition` set via `elementType.eventDefinition`. ```json { "label": "Condition Expression", "type": "String", "value": "=orderTotal > 100", "feel": "required", "binding": { "type": "bpmn:ConditionalEventDefinition#property", "name": "condition" } } ``` :::note The `condition` property requires a FEEL expression. When using `String` or `Text` input types, set `feel` to `required`. ::: ### Conditional filter: `bpmn:ConditionalEventDefinition#zeebe:conditionalFilter#property` | **Binding `type`** | `bpmn:ConditionalEventDefinition#zeebe:conditionalFilter#property` | | -------------------------- | ----------------------------------------------------------------------------- | | **Valid property `type`s** | `String``Text``Hidden` | | **Binding parameters** | `name`: The name of the property.Supported properties: `variableEvents`. | | **Mapping result** | `` | The `bpmn:ConditionalEventDefinition#zeebe:conditionalFilter#property` binding allows you to configure the conditional filter for [conditional events](../bpmn/conditional-events/conditional-events.md). The conditional filter controls which variable changes trigger the condition evaluation. ```json [ { "label": "Variable Events", "type": "String", "value": "create,update", "binding": { "type": "bpmn:ConditionalEventDefinition#zeebe:conditionalFilter#property", "name": "variableEvents" } } ] ``` :::note **Property descriptions:** - **`variableEvents`**: A comma-separated list of variable events (`create`, `update`) that trigger condition evaluation. When `bpmn:ConditionalEventDefinition#zeebe:conditionalFilter#property` is used, `bpmn:ConditionalEventDefinition#property` with `condition` should also be set on the same element. ::: ### Called element: `zeebe:calledElement` | **Binding `type`** | `zeebe:calledElement` | | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Valid property `type`s** | `String``Text``Hidden``Dropdown``Boolean` (only for the `propagateAllParentVariables` and `propagateAllChildVariables` properties) | | **Binding parameters** | `property`: The name of the property. Supported properties: `processId`, `bindingType`, `versionTag`, `propagateAllParentVariables`, `propagateAllChildVariables`. | | **Mapping result** | `` | The `zeebe:calledElement` binding allows you to configure a process called by a call activity. You can set the value of the property `bindingType` to control the [resource binding type](../../../best-practices/modeling/choosing-the-resource-binding-type). We recommend setting the property `bindingType` to the value `"versionTag"` and setting the property `versionTag` to the value of the version tag of the process you want to call. ```json [ { ..., "value": "aProcessId", "binding": { "type": "zeebe:calledElement", "property": "processId" } }, { ..., "value": "versionTag", "binding": { "type": "zeebe:calledElement", "property": "bindingType" } }, { ..., "value": "v1", "binding": { "type": "zeebe:calledElement", "property": "versionTag" } }, ... ] ``` #### Variable propagation You can control automatic variable propagation between the parent process and the called process by using the `propagateAllParentVariables` and `propagateAllChildVariables` properties. These properties support only the `Boolean` and `Hidden` types and do not support FEEL expressions. - `propagateAllParentVariables`: When you set this property to `true`, the engine copies all variables from the parent process to the called process. - `propagateAllChildVariables`: When you set this property to `true`, the engine copies all variables from the called process back to the parent process when the called process completes. ```json [ { ..., "type": "Boolean", "value": true, "binding": { "type": "zeebe:calledElement", "property": "propagateAllParentVariables" } }, { ..., "type": "Hidden", "value": "false", "binding": { "type": "zeebe:calledElement", "property": "propagateAllChildVariables" } }, ... ] ``` ### User task implementation: `zeebe:userTask` | **Binding `type`** | `zeebe:userTask` | | -------------------------- | --------------------------------------------------------------------------------------------------------------------- | | **Valid property `type`s** | `Hidden` | | **Binding parameters** | This is a flag-like binding, so it has no parameters and only applies to templates with element type `bpmn:UserTask`. | | **Mapping result** | `` | The `zeebe:userTask` binding allows you to configure the implementation type for a templated `bpmn:UserTask`. When present, it sets the task as a Camunda user task; when omitted, the task defaults to a job worker. ```json { "type": "Hidden", "binding": { "type": "zeebe:userTask" } } ``` ### Form: `zeebe:formDefinition` | **Binding `type`** | `zeebe:formDefinition` | | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | | **Valid property `type`s** | `String``Text``Hidden``Dropdown` | | **Binding parameters** | `property`: The name of the property. Supported properties: `formId`, `externalReference`, `bindingType`, and `versionTag`. | | **Mapping result** | `` | The `zeebe:formDefinition` binding allows you to configure the [user task form](../../bpmn/user-tasks/#user-task-forms) used by a user task. When setting the `formId` property, you can set the value of the property `bindingType` to control the [resource binding type](../../../best-practices/modeling/choosing-the-resource-binding-type). We recommend setting the property `bindingType` to the value `"versionTag"` and setting the property `versionTag` to the value of the version tag of the form you want to link. ```json [ { ..., "value": "aFormId", "binding": { "type": "zeebe:formDefinition", "property": "formId" } }, { ..., "value": "versionTag", "binding": { "type": "zeebe:formDefinition", "property": "bindingType" } }, { ..., "value": "v1", "binding": { "type": "zeebe:formDefinition", "property": "versionTag" } }, ... ] ``` :::note When `zeebe:formDefinition` is used, [`zeebe:userTask`](#user-task-implementation-zeebeusertask) must be set on the same element. Properties `formId` and `externalReference` are mutually exclusive, meaning that only one of them can be set at a time. The property `externalReference` cannot be used together with `bindingType`. ::: ### User task assignment: `zeebe:assignmentDefinition` | **Binding `type`** | `zeebe:assignmentDefinition` | | -------------------------- | ---------------------------------------------------------------------------------------------------------------------- | | **Valid property `type`s** | `String``Text``Hidden``Dropdown` | | **Binding parameters** | `property`: The name of the property. Supported properties: `assignee`, `candidateGroups`, and `candidateUsers`. | | **Mapping result** | `` | The `zeebe:assignmentDefinition` binding allows you to configure the [user task assignment](../../bpmn/user-tasks/#assignments). ```json [ { ..., "value": "=manager", "binding": { "type": "zeebe:assignmentDefinition", "property": "assignee" } }, { ..., "value": "group1,group2", "binding": { "type": "zeebe:assignmentDefinition", "property": "candidateGroups" } }, { ..., "value": "user1,user2,user3", "binding": { "type": "zeebe:assignmentDefinition", "property": "candidateUsers" } }, ... ] ``` :::note When `zeebe:assignmentDefinition` is used, [`zeebe:userTask`](#user-task-implementation-zeebeusertask) must be set on the same element. ::: ### User task schedule: `zeebe:taskSchedule` | **Binding `type`** | `zeebe:taskSchedule` | | -------------------------- | --------------------------------------------------------------------------------------------- | | **Valid property `type`s** | `String``Text``Hidden``Dropdown` | | **Binding parameters** | `property`: The name of the property.Supported properties: `dueDate` and `followUpDate`. | | **Mapping result** | `` | The `zeebe:taskSchedule` binding allows you to configure the [user task scheduling](../../bpmn/user-tasks/#scheduling). ```json [ { ..., "value": "=dueDateVariable", "binding": { "type": "zeebe:taskSchedule", "property": "dueDate" } }, { ..., "value": "2025-10-14T12:00:00Z", "binding": { "type": "zeebe:taskSchedule", "property": "followUpDate" } }, ... ] ``` :::note When `zeebe:taskSchedule` is used, `zeebe:userTask` must be set on the same element. If the template sets a static `value` for `dueDate` or `followUpDate`, it must be defined as an ISO 8601 combined date and time representation. ::: ### User task priority: `zeebe:priorityDefinition` | **Binding `type`** | `zeebe:priorityDefinition` | | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | **Valid property `type`s** | `Number``String` (only with `feel` set to `required`)`Text` (only with `feel` set to `required`)`Hidden``Dropdown` | | **Binding parameters** | `property`: The name of the property.Supported property: `priority`. | | **Mapping result** | `` | The `zeebe:priorityDefinition` binding allows you to configure the [user task priority](../../bpmn/user-tasks/#define-user-task-priority). ```json { ..., "value": 42, "binding": { "type": "zeebe:priorityDefinition", "property": "priority" } } ``` :::note When `zeebe:priorityDefinition` is used, [`zeebe:userTask`](#user-task-implementation-zeebeusertask) must be set on the same element. If the template sets a static `value` for `priority`, it must be between 0 and 100. ::: ### Called decision: `zeebe:calledDecision` | **Binding `type`** | `zeebe:calledDecision` | | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | | **Valid property `type`s** | `String``Text``Hidden``Dropdown` | | **Binding parameters** | `property`: The name of the property. Supported properties: `decisionId`, `resultVariable`, `bindingType`, and `versionTag`. | | **Mapping result** | `` | The `zeebe:calledDecision` binding allows you to configure the [called decision](../../bpmn/business-rule-tasks/#defining-a-task) used by a business rule task. You can set the value of the property `bindingType` to control the [resource binding type](../../../best-practices/modeling/choosing-the-resource-binding-type). We recommend setting the property `bindingType` to the value `"versionTag"` and setting the property `versionTag` to the value of the version tag of the decision you want to call. ```json [ { ..., "value": "aDecisionId", "binding": { "type": "zeebe:calledDecision", "property": "decisionId" } }, { ..., "value": "aResultVariable", "binding": { "type": "zeebe:calledDecision", "property": "resultVariable" } }, { ..., "value": "versionTag", "binding": { "type": "zeebe:calledDecision", "property": "bindingType" } }, { ..., "value": "v1", "binding": { "type": "zeebe:calledDecision", "property": "versionTag" } }, ... ] ``` :::note When `zeebe:calledDecision` is used, `zeebe:taskDefinition` cannot be used on the same element. ::: ### Script: `zeebe:script` | **Binding `type`** | `zeebe:script` | | -------------------------- | ---------------------------------------------------------------------------------------------------- | | **Valid property `type`s** | `String``Text``Hidden``Dropdown` | | **Binding parameters** | `property`: The name of the property. Supported properties: `expression` and `resultVariable`. | | **Mapping result** | `` | The `zeebe:script` binding allows you to configure the [FEEL expression](../../bpmn/script-tasks/#defining-a-task) used by a script task. ```json [ { ..., "value": "= a + b", "binding": { "type": "zeebe:script", "property": "expression" } }, { ..., "value": "result", "binding": { "type": "zeebe:script", "property": "resultVariable" } }, ... ] ``` :::note When `zeebe:script` is used, `zeebe:taskDefinition` cannot be used on the same element. If the input `type` is `String` or `Text`, then [`feel`](#adding-feel-editor-support-feel) must be set to `required`" ::: ### Ad-hoc sub-processes: `zeebe:adHoc` | **Binding `type`** | `zeebe:adHoc` | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | **Valid property `type`s** | `Hidden` | | **Binding parameters** | `property`: The name of the property.Supported properties: `outputCollection`, `outputElement`, and `activeElementsCollection`. | | **Mapping result** | `` | The `zeebe:adHoc` binding marks a sub-process as ad-hoc when deployed to Zeebe. When configured, contained activities can be executed independently without following a predefined sequence flow. #### Example ```json { ..., "appliesTo": [ "bpmn:AdHocSubProcess" ], "elementType": { "value": "bpmn:AdHocSubProcess" }, "properties": [ ..., { "type": "Hidden", "binding": { "type": "zeebe:adHoc", "property": "outputCollection" }, "value": "results" }, { "type": "Hidden", "binding": { "type": "zeebe:adHoc", "property": "outputElement" }, "value": "={ id: results._meta.id, name: results._meta.name, content: results }" } ] } ``` #### Example with `activeElementsCollection` ```json { ..., "appliesTo": [ "bpmn:AdHocSubProcess" ], "elementType": { "value": "bpmn:AdHocSubProcess" }, "properties": [ { "type": "Hidden", "binding": { "type": "property", "name": "cancelRemainingInstances" }, "value": "false" }, { "type": "String", "feel": "required", "binding": { "type": "property", "name": "completionCondition" } }, { "type": "Hidden", "binding": { "type": "zeebe:adHoc", "property": "activeElementsCollection" }, "value": "=anActiveElementsCollection" } ] } ``` :::note The `zeebe:adHoc` binding can only be used with elements of type `bpmn:AdHocSubProcess`. The `outputCollection` property defines where ad-hoc execution results are collected, while `outputElement` specifies the structure of each result item. ::: ### Execution listener: `zeebe:executionListener` | **Binding `type`** | `zeebe:executionListener` | | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | **Valid property `type`s** | `Hidden` | | **Binding parameters** | `eventType`: The event type of the listener. Supported values: `start`, `end`.`retries`_(Optional)_: The number of retries for the listener. | | **Mapping result** | `` | With the `zeebe:executionListener` binding, you can configure [execution listeners](/components/concepts/execution-listeners.md) on any BPMN element that supports them. The `value` (or `generatedValue`) of the property sets the listener job type. ```json { ..., "entriesVisible": { "executionListeners": false }, "properties": [ { "type": "Hidden", "value": "my-start-listener-type", "binding": { "type": "zeebe:executionListener", "eventType": "start" } }, { "type": "Hidden", "value": "my-end-listener-type", "binding": { "type": "zeebe:executionListener", "eventType": "end", "retries": "3" } } ] } ``` :::note This binding only supports property `type` set to `Hidden`. You can't configure listeners through the properties panel; the template must fully define them. When using this binding, set `entriesVisible` with `executionListeners` to `false`. Combining user-defined and template-defined execution listeners isn't supported. ::: ### Task listener: `zeebe:taskListener` | **Binding `type`** | `zeebe:taskListener` | | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Valid property `type`s** | `Hidden` | | **Binding parameters** | `eventType`: The event type of the listener. Supported values: `creating`, `assigning`, `updating`, `completing`, `canceling`.`retries`_(Optional)_: The number of retries for the listener. | | **Mapping result** | `` | With the `zeebe:taskListener` binding, you can configure [task listeners](/components/concepts/user-task-listeners.md) on user tasks. The `value` (or `generatedValue`) of the property sets the listener job type. ```json { ..., "appliesTo": [ "bpmn:UserTask" ], "properties": [ { "type": "Hidden", "value": "my-completing-listener-type", "binding": { "type": "zeebe:taskListener", "eventType": "completing" } }, { "type": "Hidden", "value": "my-creating-listener-type", "binding": { "type": "zeebe:taskListener", "eventType": "creating", "retries": "3" } } ] } ``` :::note This binding only applies to elements of type `bpmn:UserTask`. This binding only supports property `type` set to `Hidden`. You can't configure listeners through the properties panel; the template must fully define them. ::: ## Setting a task implementation The following tasks support multiple implementation types: - User task: [`zeebe:userTask`](#user-task-implementation-zeebeusertask) and [`zeebe:taskDefinition`](#task-definition-zeebetaskdefinition) (most related properties--for example, [Assignment](#user-task-assignment-zeebeassignmentdefinition), [Task schedule](#user-task-schedule-zeebetaskschedule), and [Priority](#user-task-priority-zeebeprioritydefinition)--are only supported when `zeebe:userTask` is set) - Business rule task: [`zeebe:calledDecision`](#called-decision-zeebecalleddecision) and [`zeebe:taskDefinition`](#task-definition-zeebetaskdefinition) - Script task: [`zeebe:script`](#script-zeebescript) and [`zeebe:taskDefinition`](#task-definition-zeebetaskdefinition) You pick an implementation type by adding the respective binding with the respective type to your properties array. ## Setting a resource binding type The task types listed below that can reference external resources also let you define the [resource binding type](/components/best-practices/modeling/choosing-the-resource-binding-type.md): - Call activity: [`zeebe:calledElement`](#called-element-zeebecalledelement) - User task form: [`zeebe:formDefinition`](#form-zeebeformdefinition) - Business rule task: [`zeebe:calledDecision`](#called-decision-zeebecalleddecision) Setting a resource binding type helps you define what version of a resource (process, form, or decision) should be used during process execution. Camunda generally recommends using `versionTag` as the resource binding type. This helps to ensure that only resources with a matching `versionTag` are used during process execution. By default, the binding type is `latest`, meaning that the latest deployed version of a resource is used whenever the task is executed. Using `latest` in combination with element templates bears the risk that a resource can be changed in an incompatible way. This can make invocations with old templates fail, for example, because an input mapping is missing from an old template, but is required by the updated resource. The binding type can be set like so: ```json { "properties": [ ..., { "type": "Hidden", "value": "versionTag", // set binding type to versionTag "binding": { "type": "zeebe:calledElement", // or zeebe:formDefinition or zeebe:calledDecision "property": "bindingType" } }, { "type": "Hidden", "value": "v1", // set the version tag to use "binding": { "type": "zeebe:calledElement", "property": "versionTag" } } ] } ``` As you can see in the example above, the properties `bindingType` and `versionTag` are of type `Hidden`. This is because these properties should generally not be changed by the user, unless the template author has very good reasons to allow this. Should an update to the resource be necessary, you can create a new version of the template that uses a different version tag. For further information, see the section on [element templates with dependencies](./element-template-with-dependencies.md). ## Preventing persisting empty values: `optional` We support optional bindings that do not persist empty values in the underlying BPMN 2.0 XML. If a user removes the value from the input field in the properties panel, it will also remove the mapped element. This works as follows: - `true`: When the user removes the value (or leaves the field empty), the empty values are NOT persisted. - `false`: When the user removes the value, the empty values ARE persisted in the XML. This is the default behavior. The following binding types can be `optional`: - [`zeebe:input`](#input-mapping-zeebeinput) - [`zeebe:output`](#output-mapping-zeebeoutput) - [`zeebe:taskHeader`](#header-zeebetaskheader) - [`zeebe:property`](#extension-properties-zeebeproperty) Example: ```json { "label": "Request", "type": "String", "optional": true, "binding": { "type": "zeebe:input", "name": "request" } } ``` ## Grouping fields: `group` Associate a field with a group (ID) via the field's `group` key: ```json { ..., "groups": [ { "id": "definition", "label": "Task definition", "openByDefault": true } ], "properties": [ { "group": "definition", "label": "Task type", "type": "String", "value": "http-job", "binding": { "type": "zeebe:taskDefinition", "property": "type" } }, ... ] } ``` ## Validating user input: `constraints` Custom fields may have a number of constraints associated with them: - `notEmpty : Boolean`: Input must be non-empty, when set to `true`. - `minLength : Integer`: Minimum length for the input. - `maxLength : Integer`: Maximum length for the input. - `pattern : Object`: Regular expression to match the input against. ### Validating against a regex: `pattern` Set `pattern` to a regular expression to ensure the user's input matches the pattern. Together with the `pattern` constraint, you can define a custom error message: ```json { "label": "Web service URL", "type": "String", "binding": { ... }, "constraints": { "notEmpty": true, "pattern": { "value": "https://.*", "message": "Must be https URL" } } } ``` :::warning When a template exposes a property to a user, the template is responsible for showing all validation errors in the properties panel. That includes non-compliance with BPMN and Zeebe schema constraints. You should therefore use `notEmpty` where necessary. The Modeler's problems panel shows errors for invalid properties, whether or not a template is applied. ::: ## Showing properties conditionally: `condition` Properties may have a condition which determines when they should be active, depending on the value of another property. For example for a HTTP request template, you might not want to show the `Request Body` property when the HTTP method is `GET`. When a property is **active**, it is displayed in the properties panel, and its value is serialized in the XML. If a property is **not active**, it is not displayed, and its value is removed from the XML. For a property value to be used in a condition, the property needs to have an `id` that can be referenced by the conditional property. A property can depend on one or more conditions. If there are multiple conditions, they can be defined using `allMatch`. All the conditions must be met for the property to be active. There are three possible comparison operators: - `equals`: Checks if the value is equal to the value defined in the condition. - `oneOf`: Checks if the value is in the list of values defined in the condition. - `isActive`: Checks if the referenced property is currently active and not hidden by other conditions. ```json [ { "id": "httpMethod", "label": "HTTP Method", "type": "Dropdown", "choices": [ { "name": "get", "value": "GET" }, { "name": "patch", "value": "PATCH" }, { "name": "post", "value": "POST" } ], "binding": { ... } }, { "label": "Request Body", "type": "String", "binding": { ... }, "condition": { "allMatch": [ { "property": "httpMethod", "oneOf": [ "patch", "post" ] }, { "property": "...", "isActive": "true" }, { "property": "...", "equals": "someValue" } ] } }, ... ] ``` ## Preventing edits: `editable` By default, all properties defined in an element template that do not have type `Hidden` are editable. You can prevent edits by setting the `editable` property to `false`. The property will be displayed in the properties panel but cannot be changed. ```json { "label": "Task type", "type": "String", "value": "http-job-type", "editable": false, "binding": { "type": "zeebe:taskDefinition", "property": "type" } } ``` ## Control visibility of default properties panel entries: `entriesVisible` By default, an applied element template causes most properties panel sections to be hidden — element template-defined bindings take precedence over standard groups and entries. This behavior can be customized through the `entriesVisible` property. ### Control specific entry visibility By passing an object to `entriesVisible`, you can override the default display of certain properties panel sections. The key of that object is the ID of a section, the value is a boolean that defines the visibility status. The table below lists what entries may be customized and their default visibility status: | Key | Description | Default visible | | :------------------- | :-------------------------- | :-------------- | | `outputs` | Output mapping section | `false` | | `executionListeners` | Execution listeners section | `true` | | `taskListeners` | Task listeners section | `false` | To show the standard output mapping section, configure your template as shown below: ```json [ { "name": "Template 1", "id": "sometemplate", "entriesVisible": { "outputs": true }, "appliesTo": [ "bpmn:ServiceTask" ], "properties": [ ... ] } ] ``` ### Displaying all entries To show all standard properties panel entries, set `entriesVisible=true`: ```json [ { "name": "Template 1", "id": "sometemplate", "entriesVisible": true, "appliesTo": [ "bpmn:ServiceTask" ], "properties": [ ... ] } ] ``` :::warning As an element template author, you are responsible for ensuring the default sections opened do not conflict with any bindings defined by the element template. ::: --- ## Boolean functions ## not(negand) Returns the logical negation of the given value. **Function signature** ```feel not(negand: boolean): boolean ``` **Examples** ```feel not(true) // false not(null) // null ``` ## is defined(value) Checks if a given value is not `null`. If the value is `null` then the function returns `false`. Otherwise, the function returns `true`. The function requires one argument. Calling `is defined()` without an argument is invalid. **Function signature** ```feel is defined(value: Any): boolean ``` **Examples** ```feel is defined(1) // true is defined(null) // false is defined(x) // false - if no variable "x" exists is defined(x.y) // false - if no variable "x" exists or it doesn't have a property "y" is defined() // error - expected one argument ``` :::caution Breaking change This function worked differently in previous versions. It returned `true` if the value was `null`. Since this version, the function returns `false` if the value is `null`. ::: ## get or else(value, default) Return the provided value parameter if not `null`, otherwise return the default parameter **Function signature** ```feel get or else(value: Any, default: Any): Any ``` **Examples** ```feel get or else("this", "default") // "this" get or else(null, "default") // "default" get or else(null, null) // null ``` ## assert(value, condition) Verify that the given condition is met. If the condition is `true`, the function returns the value. Otherwise, the evaluation fails with an error. **Function signature** ```feel assert(value: Any, condition: Any) ``` **Examples** ```feel assert(x, x != null) // "value" - if x is "value" // error - if x is null or doesn't exist assert(x, x >= 0) // 4 - if x is 4 // error - if x is less than zero ``` ## assert(value, condition, cause) Verify that the given condition is met. If the condition is `true`, the function returns the value. Otherwise, the evaluation fails with an error containing the given message. **Function signature** ```feel assert(value: Any, condition: Any, cause: String) ``` **Examples** ```feel assert(x, x != null, "'x' should not be null") // "value" - if x is "value" // error('x' should not be null) - if x is null or doesn't exist assert(x, x >= 0, "'x' should be positive") // 4 - if x is 4 // error('x' should be positive) - if x is less than zero ``` --- ## Context functions ## get value(context, key) Returns the value of the context entry with the given key. **Function signature** ```feel get value(context: context, key: string): Any ``` **Examples** ```feel get value({foo: 123}, "foo") // 123 get value({a: 1}, "b") // null ``` ## get value(context, keys) Returns the value of the context entry for a context path defined by the given keys. If `keys` contains the keys `[k1, k2]` then it returns the value at the nested entry `k1.k2` of the context. If `keys` are empty or the nested entry defined by the keys doesn't exist in the context, it returns `null`. **Function signature** ```feel get value(context: context, keys: list): Any ``` **Examples** ```feel get value({x:1, y: {z:0}}, ["y", "z"]) // 0 get value({x: {y: {z:0}}}, ["x", "y"]) // {z:0} get value({a: {b: 3}}, ["b"]) // null ``` ## get entries(context) Returns the entries of the context as a list of key-value-pairs. **Function signature** ```feel get entries(context: context): list ``` The return value is a list of contexts. Each context contains two entries for "key" and "value". **Examples** ```feel get entries({foo: 123}) // [{key: "foo", value: 123}] ``` ## context put(context, key, value) Adds a new entry with the given key and value to the context. Returns a new context that includes the entry. If an entry for the same key already exists in the context, it overrides the value. **Function signature** ```feel context put(context: context, key: string, value: Any): context ``` **Examples** ```feel context put({x:1}, "y", 2) // {x:1, y:2} ``` :::info The function `context put()` replaced the previous function `put()` (Camunda Extension). The previous function is deprecated and should not be used anymore. ::: ## context put(context, keys, value) Adds a new entry with the given value to the context. The path of the entry is defined by the keys. Returns a new context that includes the entry. If `keys` contains the keys `[k1, k2]` then it adds the nested entry `k1.k2 = value` to the context. If an entry for the same keys already exists in the context, it overrides the value. If `keys` are empty, it returns `null`. **Function signature** ```feel context put(context: context, keys: list, value: Any): context ``` **Examples** ```feel context put({x:1}, ["y"], 2) // {x:1, y:2} context put({x:1, y: {z:0}}, ["y", "z"], 2) // {x:1, y: {z:2}} context put({x:1}, ["y", "z"], 2) // {x:1, y: {z:2}} ``` ## context merge(contexts) Union the given contexts. Returns a new context that includes all entries of the given contexts. If an entry for the same key already exists in a context, it overrides the value. The entries are overridden in the same order as in the list of contexts. **Function signature** ```feel context merge(contexts: list): context ``` **Examples** ```feel context merge([{x:1}, {y:2}]) // {x:1, y:2} context merge([{x:1, y: 0}, {y:2}]) // {x:1, y:2} ``` :::info The function `context merge()` replaced the previous function `put all()` (Camunda Extension). The previous function is deprecated and should not be used anymore. ::: --- ## Conversion functions Convert a value into a different type. ## string(from) Returns the given value as a string representation. **Function signature** ```feel string(from: Any): string ``` **Examples** ```feel string(1.1) // "1.1" string(date("2012-12-25")) // "2012-12-25" ``` ## number(from) Parses the given string to a number. Returns `null` if the string is not a number. **Function signature** ```feel number(from: string): number ``` **Examples** ```feel number("1500.5") // 1500.5 ``` ## number(from, grouping separator) Parses the given string to a number using the specified grouping separator. Returns `null` if the string is not a number. **Function signature** ```feel number(from: string, grouping separator: string): number ``` **Examples** ```feel number("1,500", ",") // 1500 ``` ## number(from, grouping separator, decimal separator) Parses the given string to a number using the specified grouping and decimal separators. Returns `null` if the string is not a number. **Function signature** ```feel number(from: string, grouping separator: string, decimal separator: string): number ``` **Examples** ```feel number("1 500.5", " ", ".") // 1500.5 ``` ## context(entries) Constructs a context of the given list of key-value pairs. It is the reverse function to [get entries()](feel-built-in-functions-context.md#get-entriescontext). Each key-value pair must be a context with two entries: `key` and `value`. The entry with name `key` must have a value of the type `string`. It might override context entries if the keys are equal. The entries are overridden in the same order as the contexts in the given list. Returns `null` if one of the entries is not a context or if a context doesn't contain the required entries. **Function signature** ```feel context(entries: list): context ``` **Examples** ```feel context([{"key":"a", "value":1}, {"key":"b", "value":2}]) // {a:1, b:2} ``` ## date(from) Returns a date from the given value. Returns `null` if the string is not a valid calendar date. For example, `"2024-06-31"` is invalid because June has only 30 days. **Function signature** ```feel date(from: string): date ``` Parses the given string into a date. ```feel date(from: date and time): date ``` Extracts the date component from the given date and time. **Examples** ```feel date("2018-04-29") // date("2018-04-29") date(date and time("2012-12-25T11:00:00")) // date("2012-12-25") ``` ## date(year, month, day) Returns a date from the given components. Returns `null` if the components don't represent a valid calendar date. For example, `2024,6,31` is invalid because June has only 30 days. **Function signature** ```feel date(year: number, month: number, day: number): date ``` **Examples** ```feel date(2012, 12, 25) // date("2012-12-25") ``` ## time(from) Returns a time from the given value. **Function signature** ```feel time(from: string): time ``` Parses the given string into a time. ```feel time(from: date and time): time ``` Extracts the time component from the given date and time. **Examples** ```feel time("12:00:00") // time("12:00:00") time(date and time("2012-12-25T11:00:00")) // time("11:00:00") ``` ## time(hour, minute, second) Returns a time from the given components. **Function signature** ```feel time(hour: number, minute: number, second: number): time ``` **Examples** ```feel time(23, 59, 0) // time("23:59:00") ``` ## time(hour, minute, second, offset) Returns a time from the given components, including a timezone offset. **Function signature** ```feel time(hour: number, minute: number, second: number, offset: days and time duration): time ``` **Examples** ```feel time(14, 30, 0, duration("PT1H")) // time("14:30:00+01:00") ``` ## date and time(from) Parses the given string into a date and time. The function supports strings in the format `YYYY-MM-DDThh:mm:ss` with optional timezone information either as offset (e.g., `+01:00` or `Z`), as IANA timezone ID (e.g., `@Europe/Berlin`), or as a combination of both (e.g., `+01:00[Europe/Berlin]`). Returns `null` if the string is not a valid calendar date. For example, `"2024-06-31T10:00:00"` is invalid because June has only 30 days. **Function signature** ```feel date and time(from: string): date and time ``` **Examples** ```feel date and time("2018-04-29T09:30:00") // date and time("2018-04-29T09:30:00") date and time("2018-04-29T09:30:00+02:00") // date and time("2018-04-29T09:30:00+02:00") date and time("2018-04-29T09:30:00@Europe/Berlin") // date and time("2018-04-29T09:30:00@Europe/Berlin") date and time("2018-04-29T09:30:00+02:00[Europe/Berlin]") // date and time("2018-04-29T09:30:00@Europe/Berlin") ``` ## date and time(date, time) Returns a date and time from the given components. **Function signature** ```feel date and time(date: date, time: time): date and time ``` ```feel date and time(date: date and time, time: time): date and time ``` Returns a date and time value that consists of the date component of `date` combined with `time`. **Examples** ```feel date and time(date("2012-12-24"),time("T23:59:00")) // date and time("2012-12-24T23:59:00") date and time(date and time("2012-12-25T11:00:00"),time("T23:59:00")) // date and time("2012-12-25T23:59:00") ``` ## date and time(date, timezone) Returns the given date and time value at the given timezone. If `date` has a different timezone than `timezone` then it adjusts the time to match the local time of `timezone`. **Function signature** ```feel date and time(date: date and time, timezone: string): date and time ``` **Examples** ```feel date and time(@"2020-07-31T14:27:30@Europe/Berlin", "America/Los_Angeles") // date and time("2020-07-31T05:27:30@America/Los_Angeles") date and time(@"2020-07-31T14:27:30", "Z") // date and time("2020-07-31T12:27:30Z") ``` ## duration(from) Parses the given string into a duration. The duration is either a days and time duration or a years and months duration. **Function signature** ```feel duration(from: string): days and time duration ``` ```feel duration(from: string): years and months duration ``` **Examples** ```feel duration("P5D") // duration("P5D") duration("P32Y") // duration("P32Y") ``` ## years and months duration(from, to) Returns the years and months duration between `from` and `to`. **Function signature** ```feel years and months duration(from: date, to: date): years and months duration ``` **Examples** ```feel years and months duration(date("2011-12-22"), date("2013-08-24")) // duration("P1Y8M") ``` ## from json(value) Parses a JSON string into a FEEL value. The function converts JSON primitives, objects, and arrays into their corresponding FEEL types. Returns `null` if the string is not a valid JSON value. **Function signature** ```feel from json(value: string): Any ``` **Examples** ```feel from json("{\"a\": 1, \"b\": 2}") // {a: 1, b: 2} from json("true") // true from json("\"2023-06-14\"") // "2023-06-14" ``` ## to json(value) Converts a FEEL value into a JSON string. The function converts FEEL primitives, contexts, and lists into their corresponding JSON types. Temporal values are converted to their ISO 8601 string representation, including timezone information for date and time values (format: `2025-11-24T10:00:00+01:00[Europe/Berlin]`). **Function signature** ```feel to json(value: Any): string ``` **Examples** ```feel to json({a: 1, b: 2}) // "{\"a\":1,\"b\":2}" to json(true) // "true" to json(@"2023-06-14") // "\"2023-06-14\"" to json(@"2025-11-24T10:00:00@Europe/Berlin") // "\"2025-11-24T10:00:00+01:00[Europe/Berlin]\"" to json(@"P3Y") // "\"P3Y\"" ``` --- ## Introduction FEEL includes many built-in functions. These functions can be invoked in [expressions](../language-guide/feel-expressions-introduction.md) and [unary-tests](../language-guide/feel-unary-tests.md). ```feel contains("me@camunda.com", ".com") // invoke function with positional arguments contains(string: "me@camunda.com", match: ".de") // invoke function with named arguments ``` Read more about functions [here](../language-guide/feel-functions.md#invocation). This section is split into functions based on their primary operational data type: - [Boolean](./feel-built-in-functions-boolean.md) - [String](./feel-built-in-functions-string.md) - [Numeric](./feel-built-in-functions-numeric.md) - [List](./feel-built-in-functions-list.md) - [Context](./feel-built-in-functions-context.md) - [Temporal](./feel-built-in-functions-temporal.md) - [Range](./feel-built-in-functions-range.md) Additionally, there are [conversion](./feel-built-in-functions-conversion.md) functions that allow you to construct new values of a data type (factory functions). Functions not matching any of the above categories are listed in the [miscellaneous](./feel-built-in-functions-miscellaneous.md) section. --- ## List functions ## list contains(list, element) Returns `true` if the given list contains the element. Otherwise, returns `false`. **Function signature** ```feel list contains(list: list, element: Any): boolean ``` **Examples** ```feel list contains([1,2,3], 2) // true ``` ## count(list) Returns the number of elements of the given list. **Function signature** ```feel count(list: list): number ``` **Examples** ```feel count([1,2,3]) // 3 ``` ## min(list) Returns the minimum of the given list. **Function signature** ```feel min(list: list): Any ``` All elements in `list` should have the same type and be comparable. The parameter `list` can be passed as a list or as a sequence of elements. **Examples** ```feel min([1,2,3]) // 1 min(1,2,3) // 1 ``` ## max(list) Returns the maximum of the given list. **Function signature** ```feel max(list: list): Any ``` All elements in `list` should have the same type and be comparable. The parameter `list` can be passed as a list or as a sequence of elements. **Examples** ```feel max([1,2,3]) // 3 max(1,2,3) // 3 ``` ## sum(list) Returns the sum of the given list of numbers. **Function signature** ```feel sum(list: list): number ``` The parameter `list` can be passed as a list or as a sequence of elements. **Examples** ```feel sum([1,2,3]) // 6 sum(1,2,3) // 6 ``` ## product(list) Returns the product of the given list of numbers. **Function signature** ```feel product(list: list): number ``` The parameter `list` can be passed as a list or as a sequence of elements. **Examples** ```feel product([2, 3, 4]) // 24 product(2, 3, 4) // 24 ``` ## mean(list) Returns the arithmetic mean (i.e. average) of the given list of numbers. **Function signature** ```feel mean(list: list): number ``` The parameter `list` can be passed as a list or as a sequence of elements. **Examples** ```feel mean([1,2,3]) // 2 mean(1,2,3) // 2 ``` ## median(list) Returns the median element of the given list of numbers. **Function signature** ```feel median(list: list): number ``` The parameter `list` can be passed as a list or as a sequence of elements. **Examples** ```feel median(8, 2, 5, 3, 4) // 4 median([6, 1, 2, 3]) // 2.5 ``` ## stddev(list) Returns the standard deviation of the given list of numbers. **Function signature** ```feel stddev(list: list): number ``` The parameter `list` can be passed as a list or as a sequence of elements. **Examples** ```feel stddev(2, 4, 7, 5) // 2.0816659994661326 stddev([2, 4, 7, 5]) // 2.0816659994661326 ``` ## mode(list) Returns the mode of the given list of numbers. **Function signature** ```feel mode(list: list): number ``` The parameter `list` can be passed as a list or as a sequence of elements. **Examples** ```feel mode(6, 3, 9, 6, 6) // [6] mode([6, 1, 9, 6, 1]) // [1, 6] ``` ## all(list) Returns `false` if any element of the given list is `false`. Otherwise, returns `true`. If the given list is empty, it returns `true`. **Function signature** ```feel all(list: list): boolean ``` The parameter `list` can be passed as a list or as a sequence of elements. **Examples** ```feel all([true,false]) // false all(false,null,true) // false ``` :::info The function `all()` replaced the previous function `and()`. The previous function is deprecated and should not be used anymore. ::: ## any(list) Returns `true` if any element of the given list is `true`. Otherwise, returns `false`. If the given list is empty, it returns `false`. **Function signature** ```feel any(list: list): boolean ``` The parameter `list` can be passed as a list or as a sequence of elements. **Examples** ```feel any([false,true]) // true any(false,null,true) // true ``` :::info The function `any()` replaced the previous function `or()`. The previous function is deprecated and should not be used anymore. ::: ## sublist(list, start position) Returns a partial list of the given value starting at `start position`. **Function signature** ```feel sublist(list: list, start position: number): list ``` The `start position` starts at the index `1`. The last position is `-1`. **Examples** ```feel sublist([1,2,3], 2) // [2,3] ``` ## sublist(list, start position, length) Returns a partial list of the given value starting at `start position`. **Function signature** ```feel sublist(list: list, start position: number, length: number): list ``` The `start position` starts at the index `1`. The last position is `-1`. **Examples** ```feel sublist([1,2,3], 1, 2) // [1,2] ``` ## append(list, items) Returns the given list with all `items` appended. **Function signature** ```feel append(list: list, items: Any): list ``` The parameter `items` can be a single element or a sequence of elements. **Examples** ```feel append([1], 2, 3) // [1,2,3] ``` ## concatenate(lists) Returns a list that includes all elements of the given lists. **Function signature** ```feel concatenate(lists: list): list ``` The parameter `lists` is a sequence of lists. **Examples** ```feel concatenate([1,2],[3]) // [1,2,3] concatenate([1],[2],[3]) // [1,2,3] ``` ## insert before(list, position, newItem) Returns the given list with `newItem` inserted at `position`. **Function signature** ```feel insert before(list: list, position: number, newItem: Any): list ``` The `position` starts at the index `1`. The last position is `-1`. **Examples** ```feel insert before([1,3],1,2) // [2,1,3] ``` ## remove(list, position) Returns the given list without the element at `position`. **Function signature** ```feel remove(list: list, position: number): list ``` The `position` starts at the index `1`. The last position is `-1`. **Examples** ```feel remove([1,2,3], 2) // [1,3] ``` ## reverse(list) Returns the given list in revered order. **Function signature** ```feel reverse(list: list): list ``` **Examples** ```feel reverse([1,2,3]) // [3,2,1] ``` ## index of(list, match) Returns an ascending list of positions containing `match`. **Function signature** ```feel index of(list: list, match: Any): list ``` **Examples** ```feel index of([1,2,3,2],2) // [2,4] ``` ## union(list) Returns a list that includes all elements of the given lists without duplicates. **Function signature** ```feel union(list: list): list ``` The parameter `list` is a sequence of lists. **Examples** ```feel union([1,2],[2,3]) // [1,2,3] ``` ## distinct values(list) Returns the given list without duplicates. **Function signature** ```feel distinct values(list: list): list ``` **Examples** ```feel distinct values([1,2,3,2,1]) // [1,2,3] ``` ## duplicate values(list) Returns all duplicate values of the given list. **Function signature** ```feel duplicate values(list: list): list ``` **Examples** ```feel duplicate values([1,2,3,2,1]) // [1,2] ``` ## flatten(list) Returns a list that includes all elements of the given list without nested lists. **Function signature** ```feel flatten(list: list): list ``` **Examples** ```feel flatten([[1,2],[[3]], 4]) // [1,2,3,4] ``` ## sort(list, precedes) Returns the given list sorted by the `precedes` function. **Function signature** ```feel sort(list: list, precedes: function<(Any, Any) -> boolean>): list ``` **Examples** ```feel sort(list: [3,1,4,5,2], precedes: function(x,y) x < y) // [1,2,3,4,5] ``` ## string join(list) Joins a list of strings into a single string. This is similar to Java's [joining]() function. If an item of the list is `null`, the item is ignored for the result string. If an item is neither a string nor `null`, the function returns `null` instead of a string. **Function signature** ```feel string join(list: list): string ``` **Examples** ```feel string join(["a","b","c"]) // "abc" string join(["a",null,"c"]) // "ac" string join([]) // "" ``` ## string join(list, delimiter) Joins a list of strings into a single string. This is similar to Java's [joining]() function. If an item of the list is `null`, the item is ignored for the result string. If an item is neither a string nor `null`, the function returns `null` instead of a string. The resulting string contains a `delimiter` between each element. **Function signature** ```feel string join(list: list, delimiter: string): string ``` **Examples** ```feel string join(["a"], "X") // "a" string join(["a","b","c"], ", ") // "a, b, c" ``` ## string join(list, delimiter, prefix, suffix) Joins a list of strings into a single string. This is similar to Java's [joining]() function. If an item of the list is `null`, the item is ignored for the result string. If an item is neither a string nor `null`, the function returns `null` instead of a string. The resulting string starts with `prefix`, contains a `delimiter` between each element, and ends with `suffix`. **Function signature** ```feel string join(list: list, delimiter: string, prefix: string, suffix: string): string ``` **Examples** ```feel string join(["a","b","c"], ", ", "[", "]") // "[a, b, c]" ``` ## is empty(list) Returns `true` if the given list is empty. Otherwise, returns `false`. **Function signature** ```feel is empty(list: list): boolean ``` **Examples** ```feel is empty([]) // true is empty([1,2,3]) // false ``` ## partition(list, size) Returns consecutive sublists of a list, each of the same size (the final list may be smaller). If `size` is less than `0`, it returns `null`. **Function signature** ```feel partition(list: list, size: number): list ``` **Examples** ```feel partition([1,2,3,4,5], 2) // [[1,2], [3,4], [5]] partition([], 2) // [] partition([1,2], 0) // null ``` --- ## Miscellaneous functions ## fromAi(value) Returns the unmodified `value` parameter. - The purpose of this function is solely to tag the value as being generated by an AI integration. - The actual handling is not performed by the FEEL engine, but by a custom integration such as a connector or a job worker. The main use case of this function is for [tool definitions](../../../connectors/out-of-the-box-connectors/agentic-ai-aiagent-tool-definitions.md) used by the [AI Agent connector](../../../connectors/out-of-the-box-connectors/agentic-ai-aiagent.md). See the following function overloads for additional function parameters. **Function signature** ```feel fromAi(value: Any): Any ``` **Examples** ```feel fromAi(toolCall.searchQuery) // toolCall.searchQuery contents fromAi(toolCall.userId) // toolCall.userId contents ``` ## fromAi(value, description) Returns the unmodified `value` parameter. In addition to the previous overload, it also accepts an optional `description` parameter to provide a textual description of the value. The description must be `null` or a string constant. **Function signature** ```feel fromAi(value: Any, description: string): Any ``` **Examples** ```feel fromAi(toolCall.searchQuery, "The search query used to find the best match.") // toolCall.searchQuery contents fromAi(toolCall.searchQuery, null) // toolCall.searchQuery contents ``` ## fromAi(value, description, type) Returns the unmodified `value` parameter. In addition to the previous overload, it also accepts an optional `type` parameter to provide type information about the value. The type must be `null` or a string constant. **Function signature** ```feel fromAi(value: Any, description: string, type: string): Any ``` **Examples** ```feel fromAi(toolCall.searchQuery, "The search query used to find the best match.", "string") // toolCall.searchQuery contents fromAi(toolCall.userId, "The user's ID", "number") // toolCall.userId contents fromAi(toolCall.userId, null, "number") // toolCall.userId contents fromAi(value: toolCall.userId, type: "number") // toolCall.userId contents ``` ## fromAi(value, description, type, schema) Returns the unmodified `value` parameter. In addition to the previous overload, it also accepts an optional `schema` parameter to provide a (partial) [JSON schema](https://json-schema.org/) for the value. - The schema must be `null` or a context (map) containing only constant values. For example, function calls within the schema are not supported. - The schema is not validated by the FEEL engine but might be by a custom integration consuming the information. - From the engine side it is possible to specify both a `type` and a `schema`, and it depends on the integration as to which value takes precedence. The [AI Agent connector](../../../connectors/out-of-the-box-connectors/agentic-ai-aiagent.md) will override any type specified in the schema if the `type` parameter is also provided. **Function signature** ```feel fromAi(value: Any, description: string, type: string, schema: context): Any ``` **Examples** ```feel fromAi(toolCall.documentType, "The document type to provide", "string", { enum: ["invoice", "receipt", "contract"] }) // toolCall.documentType contents fromAi(value: toolCall.documentType, description: "The document type to provide", schema: { type: "string", enum: ["invoice", "receipt", "contract"] }) // toolCall.documentType contents fromAi(toolCall.tags, "Tags to apply to the blog post", "array", { items: { type: "string" } }) // toolCall.tags contents ``` ## fromAi(value, description, type, schema, options) Returns the unmodified `value` parameter. In addition to the previous overload, it also accepts an optional `options` parameter to provide additional options for the integration handling the value definition. - The options parameter must be `null` or a context (map) containing only constant values. For example, function calls within options are not supported. **Function signature** ```feel fromAi(value: Any, description: string, type: string, schema: context, options: context): Any ``` **Examples** ```feel fromAi(toolCall.documentType, "The document type to provide", "string", null, { required: false }) // toolCall.documentType contents fromAi(value: toolCall.documentType, options: { required: false }) // toolCall.documentType contents ``` --- ## Numeric functions ## decimal(n, scale) Rounds the given value at the given scale. **Function signature** ```feel decimal(n: number, scale: number): number ``` **Examples** ```feel decimal(1/3, 2) // .33 decimal(1.5, 0) // 2 ``` ## floor(n) Rounds the given value with rounding mode flooring. **Function signature** ```feel floor(n: number): number ``` **Examples** ```feel floor(1.5) // 1 floor(-1.5) // -2 ``` ## floor(n, scale) Rounds the given value with rounding mode flooring at the given scale. **Function signature** ```feel floor(n: number, scale: number): number ``` **Examples** ```feel floor(-1.56, 1) // -1.6 ``` ## ceiling(n) Rounds the given value with rounding mode ceiling. **Function signature** ```feel ceiling(n: number): number ``` **Examples** ```feel ceiling(1.5) // 2 ceiling(-1.5) // -1 ``` ## ceiling(n, scale) Rounds the given value with rounding mode ceiling at the given scale. **Function signature** ```feel ceiling(n: number, scale: number): number ``` **Examples** ```feel ceiling(-1.56, 1) // -1.5 ``` ## round up(n, scale) Rounds the given value with the rounding mode round-up at the given scale. **Function signature** ```feel round up(n: number, scale: number): number ``` **Examples** ```feel round up(5.5) // 6 round up(-5.5) // -6 round up(1.121, 2) // 1.13 round up(-1.126, 2) // -1.13 ``` ## round down(n, scale) Rounds the given value with the rounding mode round-down at the given scale. **Function signature** ```feel round down(n: number, scale: number): number ``` **Examples** ```feel round down(5.5, 0) // 5 round down (-5.5, 0) // -5 round down (1.121, 2) // 1.12 round down (-1.126, 2) // -1.12 ``` ## round half up(n, scale) Rounds the given value with the rounding mode round-half-up at the given scale. **Function signature** ```feel round half up(n: number, scale: number): number ``` **Examples** ```feel round half up(5.5, 0) // 6 round half up(-5.5, 0) // -6 round half up(1.121, 2) // 1.12 round half up(-1.126, 2) // -1.13 ``` ## round half down(n, scale) Rounds the given value with the rounding mode round-half-down at the given scale. **Function signature** ```feel round half down(n: number, scale: number): number ``` **Examples** ```feel round half down (5.5, 0) // 5 round half down (-5.5, 0) // -5 round half down (1.121, 2) // 1.12 round half down (-1.126, 2) // -1.13 ``` ## abs(number) Returns the absolute value of the given numeric value. **Function signature** ```feel abs(number: number): number ``` **Examples** ```feel abs(10) // 10 abs(-10) // 10 ``` ## modulo(dividend, divisor) Returns the remainder of the division of dividend by divisor. **Function signature** ```feel modulo(dividend: number, divisor: number): number ``` **Examples** ```feel modulo(12, 5) // 2 ``` ## sqrt(number) Returns the square root of the given value. **Function signature** ```feel sqrt(number: number): number ``` **Examples** ```feel sqrt(16) // 4 ``` ## log(number) Returns the natural logarithm (base e) of the given value. **Function signature** ```feel log(number: number): number ``` **Examples** ```feel log(10) // 2.302585092994046 ``` ## exp(number) Returns the Euler’s number e raised to the power of the given number . **Function signature** ```feel exp(number: number): number ``` **Examples** ```feel exp(5) // 148.4131591025766 ``` ## odd(number) Returns `true` if the given value is odd. Otherwise, returns `false`. **Function signature** ```feel odd(number: number): boolean ``` **Examples** ```feel odd(5) // true odd(2) // false ``` ## even(number) Returns `true` if the given is even. Otherwise, returns `false`. **Function signature** ```feel even(number: number): boolean ``` **Examples** ```feel even(5) // false even(2) // true ``` ## random number() Returns a random number between `0` and `1`. **Function signature** ```feel random number(): number ``` **Examples** ```feel random number() // 0.9701618132579795 ``` --- ## Range functions A set of functions establish relationships between single scalar values and ranges of such values. All functions take two arguments and return `true` if the relationship between the argument holds, or `false` otherwise. A scalar value must be of the following type: - number - date - time - date-time - days-time-duration - years-months-duration ![range functions overview](../assets/feel-built-in-functions-range-overview.png) ## before(point1, point2) **Function signature** ```feel before(point1: Any, point2: Any): boolean ``` **Examples** ```feel before(1, 10) // true before(10, 1) // false ``` ## before(range, point) **Function signature** ```feel before(range: range, point: Any): boolean ``` **Examples** ```feel before([1..5], 10) // true ``` ## before(point, range) **Function signature** ```feel before(point: Any, range: range): boolean ``` **Examples** ```feel before(1, [2..5]) // true ``` ## before(range1, range2) **Function signature** ```feel before(range1: range, range2: range): boolean ``` **Examples** ```feel before([1..5], [6..10]) // true before([1..5),[5..10]) // true ``` ## after(point1, point2) **Function signature** ```feel after(point1: Any, point2: Any): boolean ``` **Examples** ```feel after(10, 1) // true after(1, 10) // false ``` ## after(range, point) **Function signature** ```feel after(range: range, point: Any): boolean ``` **Examples** ```feel after([1..5], 10) // false ``` ## after(point, range) **Function signature** ```feel after(point: Any, range: range): boolean ``` **Examples** ```feel after(12, [2..5]) // true ``` ## after(range1, range2) **Function signature** ```feel after(range1: range, range2: range): boolean ``` **Examples** ```feel after([6..10], [1..5]) // true after([5..10], [1..5)) // true ``` ## meets(range1, range2) **Function signature** ```feel meets(range1: range, range2: range): boolean ``` **Examples** ```feel meets([1..5], [5..10]) // true meets([1..3], [4..6]) // false meets([1..3], [3..5]) // true meets([1..5], (5..8]) // false ``` ## met by(range1, range2) **Function signature** ```feel met by(range1: range, range2: range): boolean ``` **Examples** ```feel met by([5..10], [1..5]) // true met by([3..4], [1..2]) // false met by([3..5], [1..3]) // true met by((5..8], [1..5)) // false met by([5..10], [1..5)) // false ``` ## overlaps(range1, range2) **Function signature** ```feel overlaps(range1: range, range2: range): boolean ``` **Examples** ```feel overlaps([5..10], [1..6]) // true overlaps((3..7], [1..4]) // true overlaps([1..3], (3..6]) // false overlaps((5..8], [1..5)) // false overlaps([4..10], [1..5)) // true ``` ## overlaps before(range1, range2) **Function signature** ```feel overlaps before(range1: range, range2: range): boolean ``` **Examples** ```feel overlaps before([1..5], [4..10]) // true overlaps before([3..4], [1..2]) // false overlaps before([1..3], (3..5]) // false overlaps before([1..5), (3..8]) // true overlaps before([1..5), [5..10]) // false ``` ## overlaps after(range1, range2) **Function signature** ```feel overlaps after(range1: range, range2: range): boolean ``` **Examples** ```feel overlaps after([4..10], [1..5]) // true overlaps after([3..4], [1..2]) // false overlaps after([3..5], [1..3)) // false overlaps after((5..8], [1..5)) // false overlaps after([4..10], [1..5)) // true ``` ## finishes(point, range) **Function signature** ```feel finishes(point: Any, range: range): boolean ``` **Examples** ```feel finishes(5, [1..5]) // true finishes(10, [1..7]) // false ``` ## finishes(range1, range2) **Function signature** ```feel finishes(range1: range, range2: range): boolean ``` **Examples** ```feel finishes([3..5], [1..5]) // true finishes((1..5], [1..5)) // false finishes([5..10], [1..10)) // false ``` ## finished by(range, point) **Function signature** ```feel finished by(range: range, point: Any): boolean ``` **Examples** ```feel finished by([5..10], 10) // true finished by([3..4], 2) // false ``` ## finished by(range1, range2) **Function signature** ```feel finished by(range1: range, range2: range): boolean ``` **Examples** ```feel finished by([1..5], [3..5]) // true finished by((5..8], [1..5)) // false finished by([5..10], (1..10)) // false ``` ## includes(range, point) **Function signature** ```feel includes(range: range, point: Any): boolean ``` **Examples** ```feel includes([5..10], 6) // true includes([3..4], 5) // false ``` ## includes(range1, range2) **Function signature** ```feel includes(range1: range, range2: range): boolean ``` **Examples** ```feel includes([1..10], [4..6]) // true includes((5..8], [1..5)) // false includes([1..10], [1..5)) // true ``` ## during(point, range) **Function signature** ```feel during(point: Any, range: range): boolean ``` **Examples** ```feel during(5, [1..10]) // true during(12, [1..10]) // false during(1, (1..10]) // false ``` ## during(range1, range2) **Function signature** ```feel during(range1: range, range2: range): boolean ``` **Examples** ```feel during([4..6], [1..10)) // true during((1..5], (1..10]) // true ``` ## starts(point, range) **Function signature** ```feel starts(point: Any, range: range): boolean ``` **Examples** ```feel starts(1, [1..5]) // true starts(1, (1..8]) // false ``` ## starts(range1, range2) **Function signature** ```feel starts(range1: range, range2: range): boolean ``` **Examples** ```feel starts((1..5], [1..5]) // false starts([1..10], [1..5]) // false starts((1..5), (1..10)) // true ``` ## started by(range, point) **Function signature** ```feel started by(range: range, point: Any): boolean ``` **Examples** ```feel started by([1..10], 1) // true started by((1..10], 1) // false ``` ## started by(range1, range2) **Function signature** ```feel started by(range1: range, range2: range): boolean ``` **Examples** ```feel started by([1..10], [1..5]) // true started by((1..10], [1..5)) // false started by([1..10], [1..10)) // true ``` ## coincides(point1, point2) **Function signature** ```feel coincides(point1: Any, point2: Any): boolean ``` **Examples** ```feel coincides(5, 5) // true coincides(3, 4) // false ``` ## coincides(range1, range2) **Function signature** ```feel coincides(range1: range, range2: range): boolean ``` **Examples** ```feel coincides([1..5], [1..5]) // true coincides((1..5], [1..5)) // false coincides([1..5], [2..6]) // false ``` --- ## String functions ## substring(string, start position) Returns a substring of the given value starting at `start position`. **Function signature** ```feel substring(string: string, start position: number): string ``` The `start position` starts at the index `1`. The last position is `-1`. **Examples** ```feel substring("foobar", 3) // "obar" substring("foobar", -2) // "ar" ``` ## substring(string, start position, length) Returns a substring of the given value, starting at `start position` with the given `length`. If `length` is greater than the remaining characters of the value, it returns all characters from `start position` until the end. **Function signature** ```feel substring(string: string, start position: number, length: number): string ``` The `start position` starts at the index `1`. The last position is `-1`. **Examples** ```feel substring("foobar", 3, 3) // "oba" substring("foobar", -3, 2) // "ba" substring("foobar", 3, 10) // "obar" ``` ## string length(string) Returns the number of characters in the given value. **Function signature** ```feel string length(string: string): number ``` **Examples** ```feel string length("foo") // 3 ``` ## upper case(string) Returns the given value with all characters are uppercase. **Function signature** ```feel upper case(string: string): string ``` **Examples** ```feel upper case("aBc4") // "ABC4" ``` ## lower case(string) Returns the given value with all characters are lowercase. **Function signature** ```feel lower case(string: string): string ``` **Examples** ```feel lower case("aBc4") // "abc4" ``` ## substring before(string, match) Returns a substring of the given value that contains all characters before `match`. **Function signature** ```feel substring before(string: string, match: string): string ``` **Examples** ```feel substring before("foobar", "bar") // "foo" ``` ## substring after(string, match) Returns a substring of the given value that contains all characters after `match`. **Function signature** ```feel substring after(string: string, match: string): string ``` **Examples** ```feel substring after("foobar", "ob") // "ar" ``` ## contains(string, match) Returns `true` if the given value contains the substring `match`. Otherwise, returns `false`. **Function signature** ```feel contains(string: string, match: string): boolean ``` **Examples** ```feel contains("foobar", "of") // false ``` ## starts with(string, match) Returns `true` if the given value starts with the substring `match`. Otherwise, returns `false`. **Function signature** ```feel starts with(string: string, match: string): boolean ``` **Examples** ```feel starts with("foobar", "fo") // true ``` ## ends with(string, match) Returns `true` if the given value ends with the substring `match`. Otherwise, returns `false`. **Function signature** ```feel ends with(string: string, match: string): boolean ``` **Examples** ```feel ends with("foobar", "r") // true ``` ## matches(input, pattern) Returns `true` if the given value matches the `pattern`. Otherwise, returns `false`. **Function signature** ```feel matches(input: string, pattern: string): boolean ``` The `pattern` is a string that contains a regular expression. **Examples** ```feel matches("foobar", "^fo*bar") // true ``` ## matches(input, pattern, flags) Returns `true` if the given value matches the `pattern`. Otherwise, returns `false`. **Function signature** ```feel matches(input: string, pattern: string, flags: string): boolean ``` The `pattern` is a string that contains a regular expression. The `flags` can contain one or more of the following characters: - `s` (dot-all) - `m` (multi-line) - `i` (case insensitive) - `x` (comments) **Examples** ```feel matches("FooBar", "foo", "i") // true ``` ## replace(input, pattern, replacement) Returns the resulting string after replacing all occurrences of `pattern` with `replacement`. **Function signature** ```feel replace(input: string, pattern: string, replacement: string): string ``` The `pattern` is a string that contains a regular expression. The `replacement` can access the match groups by using `$` and the number of the group, for example, `$1` to access the first group. **Examples** ```feel replace("abcd", "(ab)|(a)", "[1=$1][2=$2]") // "[1=ab][2=]cd" replace("0123456789", "(\d{3})(\d{3})(\d{4})", "($1) $2-$3") // "(012) 345-6789" ``` ## replace(input, pattern, replacement, flags) Returns the resulting string after replacing all occurrences of `pattern` with `replacement`. **Function signature** ```feel replace(input: string, pattern: string, replacement: string, flags: string): string ``` The `pattern` is a string that contains a regular expression. The `replacement` can access the match groups by using `$` and the number of the group, for example, `$1` to access the first group. The `flags` can contain one or more of the following characters: - `s` (dot-all) - `m` (multi-line) - `i` (case insensitive) - `x` (comments) **Examples** ```feel replace("How do you feel?", "Feel", "FEEL", "i") // "How do you FEEL?" ``` ## split(string, delimiter) Splits the given value into a list of substrings, breaking at each occurrence of the `delimiter` pattern. **Function signature** ```feel split(string: string, delimiter: string): list ``` The `delimiter` is a string that contains a regular expression. **Examples** ```feel split("John Doe", "\s" ) // ["John", "Doe"] split("a;b;c;;", ";") // ["a", "b", "c", "", ""] ``` ## extract(string, pattern) Returns all matches of the pattern in the given string. Returns an empty list if the pattern doesn't match. **Function signature** ```feel extract(string: string, pattern: string): list ``` The `pattern` is a string that contains a regular expression. **Examples** ```feel extract("references are 1234, 1256, 1378", "12[0-9]*") // ["1234","1256"] ``` ## trim(string) Returns the given string without leading and trailing spaces. **Function signature** ```feel trim(string: string): string ``` **Examples** ```feel trim(" hello world ") // "hello world" trim("hello world ") // "hello world" ``` ## uuid() Returns a UUID (Universally Unique Identifier) with 36 characters. **Function signature** ```feel uuid(): string ``` **Examples** ```feel uuid() // "7793aab1-d761-4d38-916b-b7270e309894" ``` ## to base64(value) Returns the given string encoded in Base64 format. **Function signature** ```feel to base64(value: string): string ``` **Examples** ```feel to base64("FEEL") // "RkVFTA==" ``` ## from base64(value) Returns the given Base64 encoded string decoded to a plain string. **Function signature** ```feel from base64(value: string): string ``` **Examples** ```feel from base64("RkVFTA==") // "FEEL" ``` ## is blank(string) Returns `true` if the given string is blank (empty or contains only whitespaces). **Function signature** ```feel is blank(string: string): boolean ``` **Examples** ```feel is blank("") // true is blank(" ") // true is blank("hello world") // false ``` --- ## Temporal functions ## now() Returns the current date and time including the timezone. **Function signature** ```feel now(): date and time ``` **Examples** ```feel now() // date and time("2020-07-31T14:27:30@Europe/Berlin") ``` ## today() Returns the current date. **Function signature** ```feel today(): date ``` **Examples** ```feel today() // date("2020-07-31") ``` ## day of week(date) Returns the day of the week according to the Gregorian calendar. Note that it always returns the English name of the day. **Function signature** ```feel day of week(date: date): string ``` ```feel day of week(date: date and time): string ``` **Examples** ```feel day of week(date("2019-09-17")) // "Tuesday" day of week(date and time("2019-09-17T12:00:00")) // "Tuesday" ``` ## day of year(date) Returns the Gregorian number of the day within the year. **Function signature** ```feel day of year(date: date): number ``` ```feel day of year(date: date and time): number ``` **Examples** ```feel day of year(date("2019-09-17")) // 260 day of year(date and time("2019-09-17T12:00:00")) // 260 ``` ## week of year(date) Returns the Gregorian number of the week within the year, according to ISO 8601. **Function signature** ```feel week of year(date: date): number ``` ```feel week of year(date: date and time): number ``` **Examples** ```feel week of year(date("2019-09-17")) // 38 week of year(date and time("2019-09-17T12:00:00")) // 38 ``` ## month of year(date) Returns the month of the year according to the Gregorian calendar. Note that it always returns the English name of the month. **Function signature** ```feel month of year(date: date): string ``` ```feel month of year(date: date and time): string ``` **Examples** ```feel month of year(date("2019-09-17")) // "September" month of year(date and time("2019-09-17T12:00:00")) // "September" ``` ## abs(n) Returns the absolute value of a given duration. **Function signature** ```feel abs(n: days and time duration): days and time duration ``` ```feel abs(n: years and months duration): years and months duration ``` **Examples** ```feel abs(duration("-PT5H")) // "duration("PT5H")" abs(duration("PT5H")) // "duration("PT5H")" abs(duration("-P2M")) // duration("P2M") ``` ## last day of month(date) Takes the month of the given date or date-time value and returns the last day of this month. **Function signature** ```feel last day of month(date: date): date ``` ```feel last day of month(date: date and time): date ``` **Examples** ```feel last day of month(date("2022-10-01")) // date("2022-10-31")) last day of month(date and time("2022-10-16T12:00:00")) // date("2022-10-31")) ``` --- ## Supported data types Understand the data types supported by cluster variables for different configuration needs. ## Simple values - **String**: Text values for URLs, names, identifiers. - **Number**: Numeric values for thresholds, timeouts, counts. - **Boolean**: True/false values for feature flags and toggles. ## Complex values - **Objects**: Nested structures for grouped configuration. - **Arrays**: Lists of values. :::note Access patterns may vary depending on how the array is used. ::: --- ## Common use cases Explore practical ways to use cluster variables with real-world examples, including environment-specific configurations, SLA rules, and integration endpoints. ## Environment-specific configuration You need different API endpoints and timeouts across development, staging, and production environments. **Setup:** Global scope, e.g., production: ```json { "PAYMENT_API": { "endpoint": "https://api.payment.prod.example.com", "timeout_ms": 5000, "retry_count": 3 } } ``` Tenant scope, e.g., dev environment: ```json { "PAYMENT_API": { "endpoint": "https://api.payment.dev.example.com", "timeout_ms": 30000, "retry_count": 1 } } ``` **Usage in BPMN:** For example, in a service task making a payment API call: ``` URL: = camunda.vars.env.PAYMENT_API.endpoint Timeout: = camunda.vars.env.PAYMENT_API.timeout_ms ``` **Benefit:** The same BPMN file works across all environments without modification. ## Feature flags You want to gradually roll out a new approval workflow to specific tenants. **Setup:** Global scope, e.g., production: ```json { "ENABLE_NEW_APPROVAL_FLOW": false } ``` Tenant scope, e.g., `tenant-a`: ```json { "ENABLE_NEW_APPROVAL_FLOW": true } ``` Tenant scope, e.g., `beta-customer`: ```json { "ENABLE_NEW_APPROVAL_FLOW": true } ``` **Usage in BPMN:** For example, in an exclusive gateway condition: ``` Condition for new flow: camunda.vars.env.ENABLE_NEW_APPROVAL_FLOW = true Condition for old flow: camunda.vars.env.ENABLE_NEW_APPROVAL_FLOW = false ``` **Benefit:** Control feature rollout per tenant without deploying different process versions. ## Multi-tenant SLA configuration Different tenants have different Service Level Agreements (SLAs) with varying approval thresholds and escalation timeouts. **Setup:** Tenant scope, e.g., `tenant-a`: ```json { "SLA_CONFIG": { "approval_threshold": 1000, "escalation_hours": 24, "auto_approve_limit": 100 }, "auto_approve_limit": 100 } ``` Tenant scope, e.g., `tenant-b`: ```json { "SLA_CONFIG": { "approval_threshold": 50000, "escalation_hours": 4, "auto_approve_limit": 5000 }, "auto_approve_limit": 5000 } ``` Tenant scope, e.g., `tenant-c`: ```json { "SLA_CONFIG": { "approval_threshold": 5000, "escalation_hours": 48, "auto_approve_limit": 500 }, "auto_approve_limit": 500 } ``` **Usage in BPMN:** For example, in a gateway condition for auto-approval: ``` amount <= camunda.vars.env.SLA_CONFIG.auto_approve_limit ``` Timer boundary event for escalation: ``` Duration: = duration("PT" + string(camunda.vars.env.SLA_CONFIG.escalation_hours) + "H") ``` **Benefit:** Customize business rules per tenant while maintaining a single process definition. ## Integration credentials and endpoints Your processes integrate with multiple external services that have different configurations per environment. **Setup:** Global scope, e.g., production: ```json { "INTEGRATIONS": { "crm": { "base_url": "https://crm.prod.example.com", "api_version": "v2", "timeout_ms": 10000 }, "erp": { "base_url": "https://erp.prod.example.com", "api_version": "v1", "timeout_ms": 15000 }, "notification": { "base_url": "https://notify.prod.example.com", "api_version": "v1", "timeout_ms": 5000 } } } ``` Tenant scope, e.g., `sandbox`: ```json { "INTEGRATIONS": { "crm": { "base_url": "https://crm.sandbox.example.com", "api_version": "v2", "timeout_ms": 30000 } } } ``` **Usage in BPMN:** For example, a service task for CRM integration: ``` URL: = camunda.vars.env.INTEGRATIONS.crm.base_url + "/" + camunda.vars.env.INTEGRATIONS.crm.api_version + "/customers" ``` **Benefit:** Centralize integration configuration and easily switch between environments. --- ## Get started with cluster variables Get started with cluster variables by creating your first one and using it in a BPMN process. Throughout this tutorial, you'll build a payment processing workflow that calls different payment API endpoints depending on the environment. ## Step 1: Create a global cluster variable First, create a global cluster variable that serves as your production API configuration. This variable is available to all processes across your cluster. Use the Orchestration Cluster API to [create](../../../../apis-tools/orchestration-cluster-api-rest/specifications/create-global-cluster-variable.api.mdx) a global variable: ```bash POST /v2/cluster-variables/global Content-Type: application/json { "name": "PAYMENT_API_CONFIG", "value": { "endpoint": "https://api.payment.prod.example.com", "timeout_ms": 5000, "retry_count": 3 } } ``` The API returns a confirmation response with your variable details. ## Step 2: Override for a specific tenant Now, create a tenant-specific override for your development environment. This allows you to use the same BPMN process in both environments without modifications. Replace `{tenantId}` with your actual tenant ID (for example, `dev-environment`): ```bash POST /v2/cluster-variables/tenant/{tenantId} Content-Type: application/json { "name": "PAYMENT_API_CONFIG", "value": { "endpoint": "https://api.payment.dev.example.com", "timeout_ms": 30000, "retry_count": 1 } } ``` :::note Processes running in the `dev-environment` tenant automatically use the development API configuration, while all other tenants use the production configuration. ::: ## Step 3: Access the variable in Modeler Open Camunda Modeler and create or open a BPMN process. Add a service task to your process that calls the payment API. To use your cluster variable in a service task: 1. Select the service task in your diagram. 2. In the properties panel, navigate to the **Inputs** section. 3. Add an input mapping. 4. For the **Variable assignment value** field, enter the following FEEL expression to access the API endpoint: ``` = camunda.vars.env.PAYMENT_API_CONFIG.endpoint ``` 5. For the **Local variable name** field, enter `apiUrl`. This creates a local variable for your service task. 6. Add another input mapping for the timeout using the following expression: ``` = camunda.vars.env.PAYMENT_API_CONFIG.timeout_ms ``` 7. Set the local variable name to `timeoutMs`. Your service task now has access to both the `apiUrl` and `timeoutMs` variables, which automatically resolve to the correct values based on whether the process runs in your production cluster or the development tenant. ## Step 4: Deploy and test your process 1. Complete your BPMN diagram by adding any additional tasks and an end event. 2. Click **Deploy** to deploy your process to the cluster. 3. Create a process instance by clicking **Run** (or start it via API). 4. Navigate to Operate to view your process instance. 5. Inspect the process variables to see that the cluster variables were resolved correctly based on your tenant context. **What happens during execution** - If the process runs in the `dev-environment` tenant, it uses the development API endpoint (`https://api.payment.dev.example.com`) with a 30-second timeout. - If the process runs in any other context, it uses the production API endpoint (`https://api.payment.prod.example.com`) with a five-second timeout. - The BPMN file remains identical across all environments. ## Next steps Congratulations! You've successfully created and used your first cluster variable in a BPMN process. Now, [learn the fundamentals](./overview.md#learn-the-fundamentals) and [explore further resources](./overview.md#explore-further-resources). --- ## Namespace collisions Understand namespace collisions in cluster variables, how scope priority affects variable resolution, and how to safely handle overrides to avoid unexpected behavior. ## About Namespace collisions occur when the same variable key is defined differently across scopes (process, tenant, global). While scope priority determines which value is used, mismatched data types or structures can cause unexpected results. This guide explains common collision types, shows real examples, and shares best practices to prevent or intentionally manage overrides. ## Collision types The examples below show you common patterns and how to reason about them. ### Process variable shadowing If you create a process variable using the cluster variable namespace, it completely shadows cluster variables. #### Scenario ``` GLOBAL: { API_ENDPOINT: "https://api.global.com" } Process Variable Created: camunda.vars.env.API_ENDPOINT = "https://api.override.com" ``` #### Result ``` camunda.vars.env.API_ENDPOINT → "https://api.override.com" ``` #### Why this happens Process variables [have the highest priority](./scope-and-priority.md) in the resolution hierarchy. When you namespace a process variable under `camunda.vars.env`, it takes precedence over cluster variables. This allows intentional, runtime overrides of cluster configuration when necessary. :::tip Use sparingly and document clearly. Prefer a different namespace for process variables to avoid confusion. ::: ### Structural collisions across scopes It happens when you define the same key with different data types or structures at different scopes. :::note This is the most common source of unexpected behavior. ::: #### Scenario ``` TENANT: { CONFIG_KEY: "simple string value" } GLOBAL: { CONFIG_KEY: { nested: "object", with: "properties" } } ``` #### Result ``` camunda.vars.env.CONFIG_KEY → "simple string value" (TENANT wins) camunda.vars.env.CONFIG_KEY.nested → null (trying to access property on string) ``` #### Why this happens Tenant scope has higher priority and returns a string, so the global object is never evaluated. Accessing properties on a string yields null. ### Detailed collision example #### Scenario ``` GLOBAL scope: { KEY_1: { KEY_2: "hello world", KEY_3: "goodbye world" } } TENANT scope: { KEY_1: "tenant value" } ``` #### Results ``` camunda.vars.env.KEY_1 → "tenant value" ✓ Works as expected, TENANT priority camunda.vars.env.KEY_1.KEY_2 → null ✗ Unexpected! Trying to access property on string camunda.vars.cluster.KEY_1.KEY_2 → "hello world" ✓ Bypasses TENANT scope, accesses GLOBAL directly camunda.vars.tenant.KEY_1 → "tenant value" ✓ Direct TENANT access camunda.vars.tenant.KEY_1.KEY_2 → null ✓ Correctly returns null (KEY_1 in TENANT is string) ``` ## Prevent namespace collisions Avoid collisions by keeping structures consistent, separating fundamentally different data under different keys, and documenting what each key represents at each scope. ### Keep data structures consistent Use the same data type and shape for a given key across all scopes. #### Example ``` GLOBAL: { DATABASE_CONFIG: { host: "global-db.example.com", port: 5432, timeout_ms: 5000 } } TENANT: { DATABASE_CONFIG: { host: "tenant-db.example.com", port: 5432, timeout_ms: 10000 } } ``` Both scopes use the same object structure, with tenant‑specific values. ### Use distinct keys If the structures differ, store them under different keys to avoid type conflicts. #### Example ``` GLOBAL: { DEFAULT_ENDPOINT: "https://api.global.com", DEFAULT_CONFIG: { timeout: 5000, retry: 3 } } TENANT: { TENANT_ENDPOINT: "https://api.tenant.com", TENANT_CONFIG: { timeout: 10000, retry: 5, custom_header: "value" } } ``` ### Detect collisions at runtime Add simple guards when accessing nested properties to avoid nulls if a higher‑priority scope supplies a different type. #### Example ``` if camunda.vars.env.CONFIG.nested_value != null then camunda.vars.env.CONFIG.nested_value else "default" ``` ### Document your schema Maintain a schema registry or documentation that specifies: - Expected keys at each scope. - Data type and structure for each key. - Which keys can be overridden at the tenant scope. - Deprecation notices for keys being phased out. ## Process variable namespace best practices ### Avoid using `camunda.vars` namespace To prevent shadowing cluster variables, avoid creating process variables that use the `camunda.vars` namespace. For example, instead of: ``` Set process variable: camunda.vars.env.TIMEOUT = 5000 ``` Do: ``` Set process variable: processTimeout = 5000 ``` ### When override is intentional If you need to override a cluster variable at the process level, document the intent and scope in the process documentation so consumers understand why the override exists and where it applies. --- ## Cluster variables(Cluster-variable) Manage configuration values centrally across your Camunda cluster with cluster variables. ## About Within your Camunda cluster, you can define [variables](../../../../components/concepts/variables.md) at two levels: globally for the entire cluster, or at the tenant level when multi-tenancy is enabled. Cluster variables allows you to maintain environment-specific configurations, API endpoints, feature flags, and other shared values without hardcoding them into individual process definitions. ### Why use Cluster variables provide **centralized**, **flexible**, and **environment-aware configuration** for your processes. They allow you to: - Define configuration once and reuse it across multiple processes. - Use different values for development, staging, and production without changing BPMN models. - Support multi-tenant setups with tenant-specific overrides on top of global defaults. - Promote processes across environments without modifying process definitions. - Update configuration at runtime without redeploying. - Manage shared settings such as API endpoints and service URLs, feature flags, and integration credentials. ### When not to use Consider alternatives for the following use cases: | Use case | Recommended alternative | | ---------------------------------------------- | -------------------------------- | | Process instance-specific data | Process variables | | Values that change frequently during execution | Process variables | | Sensitive credentials | Secrets management | | Large payloads | External storage with references | ## Get started Get started with cluster variables with the following tutorial. Create your first cluster variable ## Learn the fundamentals Understand the fundamental concepts of cluster variables. ## Explore further resources Dive into common use cases and the API documentation to extend your knowledge. --- ## Scope resolution Learn how cluster variable scope resolution works, the available scopes, and how to access and use them. ## About Cluster variables use a scope priority process to determine which value is used when the same key exists at multiple levels. This guide helps you understand this scope resolution to predict values in your processes and avoid unexpected behavior. ## Scope levels Cluster variables exist at the **global** and **tenant** scope levels. ### Global scope Variables defined at the global scope are available across the entire cluster and accessible by all processes, regardless of tenant context. :::info Global-scope cluster variables are ideal for cluster-wide defaults and shared configuration. ::: They have lowest priority in variable resolution and are managed via the global cluster variables API. ### Tenant scope Variables defined at the tenant scope are specific to a particular tenant and only accessible within that tenant's context. :::note Tenant-scope cluster variables are ideal for tenant-specific customization and overrides. ::: They have higher priority than global-scope ones in variable resolution and are managed via the tenant-specific cluster variables API. :::important They are available only when multi-tenancy is enabled. ::: ## Resolution priority From highest to lowest priority, the cluster variable resolution order is: 1. Process variables. 2. Tenant-scope cluster variables. 3. Global-scope cluster variables. ### Process variables Variables defined on the process instance always take precedence over cluster variables. This includes: - Variables set during instantiation. - Variables created or updated during execution. - Variables passed from parent to child processes. :::note If you create a process variable with a key that matches a cluster variable path (for example, `camunda.vars.env.API_URL`), the process variable will shadow the cluster variable completely. ::: ### Tenant-scope cluster variables When multi-tenancy is enabled, tenant-scoped variables: - Override global scope variables with the same key. - Are visible only to processes in that tenant. - Enable tenant-specific customization without affecting other tenants. ### Global-scope cluster variables Global cluster variables provide cluster-wide defaults and baseline configuration. These variables: - Are accessible to all processes across all tenants. - Serve as fallbacks when no tenant-specific override exists. - Provide consistent defaults for new tenants. ### Resolution examples #### Basic scope override ``` GLOBAL: { API_TIMEOUT: 5000 } TENANT: { API_TIMEOUT: 10000 } PROCESS: (none) Result when accessing camunda.vars.env.API_TIMEOUT: → 10000 (TENANT value) ``` #### Partial override ``` GLOBAL: { API_CONFIG: { timeout: 5000, retry: 3, url: "https://api.global.com" } } TENANT: { API_CONFIG: { timeout: 10000 } } PROCESS: (none) Result when accessing camunda.vars.env.API_CONFIG.timeout: → 10000 (TENANT value) Result when accessing camunda.vars.env.API_CONFIG.retry: → null (TENANT defined API_CONFIG as a different object) ``` :::warning When you override a key at the tenant scope, the entire value at that key is replaced, not merged. Partial object merging does not occur. ::: #### Process variable override ``` GLOBAL: { MAX_AMOUNT: 1000 } TENANT: { MAX_AMOUNT: 5000 } PROCESS: { camunda.vars.env.MAX_AMOUNT: 10000 } Result when accessing camunda.vars.env.MAX_AMOUNT: → 10000 (PROCESS variable) ``` ## Multi-tenant considerations ### Tenant isolation Each tenant's cluster variables are fully isolated. A process running in one tenant cannot access cluster variables defined for another tenant, even with explicit namespace access. ### Global defaults pattern Define sensible defaults globally and override only what you need at the tenant level: ``` GLOBAL: { RATE_LIMITS: { requests_per_minute: 100, burst_limit: 150, concurrent_connections: 10 } } TENANT (premium): { RATE_LIMITS: { requests_per_minute: 1000, burst_limit: 1500, concurrent_connections: 100 } } ``` ## Troubleshoot scope resolution 1. **Check all three scope levels**: Verify values at global, tenant, and process levels. 2. **Use scope-specific namespaces**: Test with `camunda.vars.cluster` and `camunda.vars.tenant` to isolate scope. 3. **Verify tenant context**: Ensure the process is running in the expected tenant. 4. **Look for shadowing**: Check for process variables that might shadow cluster variables. 5. **Validate structure**: Ensure objects have consistent structure across scopes. --- ## How to use cluster variables Learn how to use and access cluster variables, from simple to advanced patterns. ## Use in BPMN ### Service task input mapping Map cluster variables to service task inputs. For example: ``` = camunda.vars.env.API_ENDPOINT ``` For the target variable: ``` apiUrl ``` ### Gateway conditions Use cluster variables in conditional sequence flows. For example: ``` orderAmount > camunda.vars.env.APPROVAL_THRESHOLD camunda.vars.env.FEATURE_EXPRESS_SHIPPING = true ``` ### Script tasks Reference cluster variables in script expressions. For example: ``` var endpoint = camunda.vars.env.API_CONFIG.base_url; var timeout = camunda.vars.env.API_CONFIG.timeout_ms; ``` ### Output mappings Use cluster variables in output parameter expressions. For example: ``` = { "endpoint": camunda.vars.env.SERVICE_URL, "timestamp": now(), "threshold": camunda.vars.env.PROCESSING_THRESHOLD } ``` ### Call activities Pass cluster variables as input to called processes. For example: ``` = { "config": camunda.vars.env.SUBPROCESS_CONFIG, "flags": camunda.vars.env.FEATURE_FLAGS } ``` ### Combining multiple variables Create expressions using multiple cluster variables. For example: ``` camunda.vars.env.API_BASE_URL + "/api/v" + camunda.vars.env.API_VERSION + "/resource" ``` ## Access using FEEL expressions You can reference cluster variables anywhere Camunda Modeler supports FEEL expressions. They are exposed through the following namespaces: - `camunda.vars.cluster` - `camunda.vars.tenant` - `camunda.vars.env` These namespaces correspond to the available scope levels. See [scope resolution](./scope-and-priority.md) for more details. :::tip Camunda recommends using the `camunda.vars.env` namespace for most use cases. ::: #### `camunda.vars.cluster` Provides direct access **only** to global-scope variables. Tenant-scope variables are not accessible through this namespace. **Use when:** - You want to access only global variables. - You need to bypass tenant-level overrides. - You're debugging scope resolution. For example: ``` camunda.vars.cluster.GLOBAL_DEFAULT_TIMEOUT ``` #### `camunda.vars.tenant` Provides direct access **only** to tenant-scope variables. Global-scope variables are not accessible through this namespace. **Use when:** - You want to access only tenant variables. - You need to check tenant-specific values. - You're debugging scope resolution. For example: ``` camunda.vars.tenant.TENANT_SPECIFIC_CONFIG ``` #### `camunda.vars.env` Provides a merged view of both global- and tenant-scope variables, applying automatic priority resolution. This is the recommended namespace for most cases. **Use when:** - You want automatic scope resolution. - You need the most specific value available. - You're writing portable process definitions. For example: ``` camunda.vars.env.API_ENDPOINT camunda.vars.env.CONFIG.timeout ``` ### Simple key access Use dot notation after the namespace for simple key-value pairs. For example: ``` camunda.vars.env.API_URL camunda.vars.env.MAX_RETRIES camunda.vars.env.FEATURE_ENABLED camunda.vars.env.TIMEOUT_SECONDS ``` ### Nested object access For cluster variables with nested object structures, use dot notation to access deeper levels. For example: ``` camunda.vars.env.DATABASE_CONFIG.host camunda.vars.env.DATABASE_CONFIG.port camunda.vars.env.DATABASE_CONFIG.credentials.username camunda.vars.env.API_SETTINGS.retry.max_attempts camunda.vars.env.API_SETTINGS.retry.backoff_ms camunda.vars.env.API_SETTINGS.timeout.connection ``` ### Conditional access Provide fallback values when cluster variables might not exist. For example: ``` if camunda.vars.env.CUSTOM_TIMEOUT != null then camunda.vars.env.CUSTOM_TIMEOUT else 5000 ``` ### Dynamic key access While dynamic key access is limited in FEEL, you can structure your variables to support configuration-driven behavior. For example: ``` camunda.vars.env.COUNTRY_CONFIGS[countryCode].tax_rate ``` ## Usage best practices #### Use meaningful names Choose clear, descriptive variable names that indicate purpose and scope. For example: ``` ✓ camunda.vars.env.PAYMENT_API_ENDPOINT ✗ camunda.vars.env.URL1 ``` #### Group related configuration Use nested objects to organize related values. For example: ``` ✓ camunda.vars.env.PAYMENT_CONFIG.endpoint ✓ camunda.vars.env.PAYMENT_CONFIG.timeout ✓ camunda.vars.env.PAYMENT_CONFIG.retry_count ``` #### Follow naming conventions Establish and follow naming patterns across your organization. For example: - `UPPER_SNAKE_CASE` for top-level keys. - `camelCase` or `snake_case` for nested properties. #### Document variable contracts Maintain documentation of expected cluster variables, their structures, and allowed overrides per process. --- ## FEEL Playground Use the FEEL Playground to validate and troubleshoot your FEEL expressions when modeling process diagrams in Web Modeler. ## About FEEL Playground When using the [FEEL expression language](/components/modeler/feel/what-is-feel.md) in Camunda, your FEEL expressions must be valid. The FEEL Playground allows you to test and validate your FEEL expressions using sample contextual data. The FEEL Playground is integrated into the popup FEEL editor: - **FEEL expression**: Enter and edit the FEEL expression you want to validate. - **Context**: A set of [sample data and variables](/components/modeler/data-handling.md) to use as a context for validating your expression against. You can edit this sample data if required. The data must be correctly formatted as valid JSON. - **Result**: Shows the results of the validation when run against the sample data. For example, if the expression is valid for the sample data, an [Approved validation result](#results) is returned. If there is a validation issue, a warning and description of the issue is shown to help you troubleshoot the expression. - **FEEL Copilot**: (For SaaS only) Open the [FEEL Copilot (alpha feature)](/components/early-access/alpha/alpha-features.md) to chat with the AI FEEL Copilot and get help with generating expressions. :::note The latest version of the [FEEL Scala engine](/components/modeler/feel/what-is-feel.md#feel-engines) is used to validate FEEL expressions in the FEEL Playground. This can be different than the FEEL Scala version used by the linter's Zeebe version and the cluster the diagram will be deployed to. ::: ## Validate your FEEL expression {#validate} To use the FEEL Playground for validation: 1. Open the properties panel of a diagram element containing the FEEL expression you want to validate. 1. To open the popup FEEL editor, click **fx** on the FEEL expression field, and click the **Open popup editor** icon in the field. 1. In the popup FEEL editor, enter and validate your expression using the contextual data. Edit the expression as required until it is valid (returns an Approved status) and no errors are shown. See [validation results](#results). ## Validation results {#results} FEEL Playground validation results are shown as follows for each panel: | Icon | Status | Description | | :--------------------------------------------------------------------------------- | :------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | Valid | The FEEL expression successfully passed validation. | | | Warning | The FEEL expression is invalid. Edit your expression or sample data to pass validation.For example, there might be an invalid type in the contextual data, meaning that values could not be compared as the type does not match as expected (for example, the sample data has a numeric value instead of a boolean value in a key-pair). | | | Error | The validation did not complete due to an error.For example, you might need to check your sample contextual data JSON is formed correctly. | :::tip Hover over a status icon to see more information, such as the reason why the FEEL expression is invalid. ::: ## Validation examples ### Example valid FEEL expression - In this example, both the FEEL expression syntax and the contextual data are valid. - The expression is evaluated, and a valid "Approved" result is returned. ### Example invalid FEEL expression - In this example, the **FEEL expression** shows an error status to indicate it did not pass validation. - The error is caused by an extraneous "else" at the end of the expression, meaning it is not a valid FEEL expression syntax. - Hovering over the icon provides more detail on what is causing the error, for example "Expression evaluation failed: Unrecognized token in Expression". ### Example data warning - In this example, the **Result** shows a warning status to indicate it did not pass validation. - The warning is caused by an invalid type in the contextual data, as the `hasJob` value must be a boolean value for the `hasJob = true` expression to be valid. - The warning text provides an explanation of why the warning occurred, and where to check for an error. ### Example JSON error - In this example, the **Context** shows an error status to indicate it did not pass validation. - The error is caused by an extra comma character after the last key-pair, which is not a valid JSON format. - Hovering over the icon provides more detail on what is causing the error, for example "Invalid JSON: Expected double-quoted property name in JSON at position 98 (line 7 column 1)". ## Prompting in FEEL Playground {#prompting} Use prompting in FEEL Playground to help you write FEEL expressions. To prompt in FEEL Playground: 1. Open the properties panel of a diagram element that contains the FEEL expression you want to validate. 1. In the FEEL expression field, click **fx**, then click **Open popup editor**. 1. In the popup editor, enter your prompt details. 1. Click **Use expression** to use your prompt in your FEEL expression. ### LLM provider-specific prompting Prompting supports prompt creation for specific LLM providers. For example, a prompt like **"Please create a prompt for a classification agent that needs to classify text received by a customer to a list of "sentiment". My model is OpenAI o3."** is treated as an OpenAI-specific prompt. --- ## Boolean expressions ### Literal Creates a new boolean value. ```feel true false ``` ### Comparison Compares two values with one of the following operators. Both values must be of the same type. Otherwise, the result is `null`. Operator Description Supported types = equal to any != not equal to any < less than number, string, date, time, date-time, duration <= less than or equal to number, string, date, time, date-time, duration > greater than number, string, date, time, date-time, duration >= greater than or equal number, string, date, time, date-time, duration between [x] and [y] same as (_ >= x and _ <= y) number, string, date, time, date-time, duration ```feel 5 = 5 // true 5 != 5 // false date("2020-04-05") < date("2020-04-06") // true time("08:00:00") <= time("08:00:00") // true duration("P1D") > duration("P5D") // false duration("P1Y") >= duration("P6M") // true 5 between 3 and 7 // true date("2020-04-06") between date("2020-04-05") and date("2020-04-09") // true ``` :::caution Be Careful! The equals operator has only **one** equals sign (e.g. `x = y`). In other languages, the operator has two equals signs (e.g. `x == y`). ::: ### Null check Any value or variable can be compared with `null` to check if it is equal to `null`, or if it exists. Comparing `null` to a value different from `null` results in `false`. It returns `true` if the value is `null` or the variable doesn't exist. Comparing a context entry with `null` results in `true` if the value of the entry is `null` or if the context doesn't contain an entry with this key. ```feel null = null // true "foo" = null // false {x: null}.x = null // true {}.y = null // true ``` ### Conjunction/and Combines multiple boolean values following the ternary logic. - The result is `true` if all values are `true`. - The result is `false` if one value is `false`. - Otherwise, the result is `null` (i.e. if a value is not a boolean.) ```feel true and true // true true and false // false true and null // null true and "otherwise" // null false and null // false false and "otherwise" // false ``` ### Disjunction/or Combines multiple boolean values following the ternary logic. - The result is `true` if at least one value is `true`. - The result is `false` if all values are `false`. - Otherwise, the result is `null` (i.e. if a value is not a boolean.) ```feel true or false // true false or false // false true or null // true true or "otherwise" // true false or null // null false or "otherwise" // null ``` ### Instance of Checks if the value is of the given type. Available type names: - `boolean` - `number` - `string` - `date` - `time` - `date and time` - `days and time duration` - `years and months duration` - `list` - `context` - `function` - `Any` Use the type `Any` to check if the value is not `null`. ```feel 1 instance of number // true 1 instance of string // false 1 instance of Any // true null instance of Any // false duration("P3M") instance of years and months duration // true duration("PT4H") instance of days and time duration // true ``` ### Unary-tests/in Evaluates a [unary-tests](/components/modeler/feel/language-guide/feel-unary-tests.md) with the given value. The keyword `in` separates the value from the unary-tests. ```feel 5 in (3..7) // true date("2021-06-04") in [date("2021-05-01")..date("2021-05-31")] // false 5 in (3,5,7) // true 5 in [2,4,6,8] // false ``` --- ## Context expressions You can use the following FEEL context expressions. Examples are provided to show common use cases. ### Literal Creates a new context with the given entries. - Each entry has a key and a value. - The key is either a name or a string. - The value can be any type. :::info For valid key names, see [naming conventions](./feel-variables.md#variable-names). ::: ```feel { a: 1, b: 2 } // {a:1, b:2} { "a": 1, "b": 2 } // {a:1, b:2} ``` Inside the context, the previous entries can be accessed. ```feel { a: 2, b: a * 2 } // {a:2, b:4} ``` A context value can embed other context values. ```feel { a: 1, b: { c: 2 } } // {a:1, b:{c:2}} ``` ### Get entry/path ```feel a.b ``` Accesses the entry with the key `b` of the context `a`. The path is separated by a dot `.`. If the value of the entry `b` is also a context, the path can be chained (i.e. `a.b.c`). ```feel {a: 2}.a // 2 {a: {b: 3}}.a // {b: 3} {a: {b: 3}}.a.b // 3 ``` If the context `a` doesn't contain an entry with the key `b`, the expression returns `null`. ```feel {a: 1}.b // null {a: 1}.b.c // null ``` ### Filter ```feel a[c] ``` Filters the list of context elements `a` by the condition `c`. The result of the expression is a list that contains all elements where the condition `c` evaluates to `true`. The other elements are excluded. While filtering, the current element is assigned to the variable `item` and its entries can be accessed by their key. ```feel [ { a: "p1", b: 5 }, { a: "p2", b: 10 } ][b > 7] // [{a: "p2", b: 10}] ``` ### Projection ```feel a.b ``` Extracts the entries with the key `b` of the list of context elements `a` (that is, a projection). It returns a list containing the values of the context elements with the key `b`. ```feel [ { a: "p1", b: 5 }, { a: "p2", b: 10 } ].a // ["p1", "p2"] ``` If an element of the list `a` doesn't contain an entry with the key `b`, the result contains `null` of this element. ```feel [ { a: "p1", b: 5 }, { a: "p2", c: 20 } ].b // [5, null] ``` ## Examples ### Validate data Validate journal entries and return all violations. ```feel { check1: { error: "Document Type invalid for current year posting", violations: collection[documentType = "S2" and glDate > startFiscalYear] }, check2: { error: "Document Type invalid for current year posting", violations: collection[ledgerType = "GP" and foreignAmount != null] }, result: [check1, check2][count(violations) > 0] } ``` ### Structure calculation Calculate the minimum age of a given list of birthdays. ```feel { age: function(birthday) (today() - birthday).years, ages: for birthday in birthdays return age(birthday), minAge: min(ages) }.minAge ``` --- ## Control flow ### If conditions ```feel if c then a else b ``` Executes the expression `a` if the condition `c` evaluates to `true`. Otherwise, it executes the expression `b`. ```feel if 5 < 10 then "low" else "high" // "low" if 12 < 10 then "low" else "high" // "high" ``` :::info good to know If the condition `c` doesn't evaluate to a boolean value (e.g. `null`), it executes the expression `b`. ```feel if null then "low" else "high" // "high" ``` ::: ### For loops ```feel for a in b return c ``` Iterates over the list `b` and executes the expression `c` for each element in the list. The current element is assigned to the variable `a`. The result of the expression is a list. If multiple lists are passed to the `for` loop then it iterates over the cross-product of the elements in the given lists. ```feel for x in [1,2,3] return x * 2 // [2,4,6] for x in [1,2], y in [3,4] return x * y // [3, 4, 6, 8] ``` While iterating over the list, the previous elements are assigned to the variable `partial`. ```feel for i in 1..10 return if (i <= 2) then 1 else partial[-1] + partial[-2] // [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] ``` :::tip The variable `partial` can be used to retrieve the index of the current element. ```feel for x in ["a","b","c"] return {value: x, index: count(partial) + 1} // [{"value":"a","index":1},{"value":"b","index":2},{"value":"c","index":3}] ``` ::: Instead of a list, the `for` loop can also iterate over a given range. ```feel for x in 0..8 return 2 ** x // [1, 2, 4, 8, 16, 32, 64, 128, 256] for x in 3..1 return 2 * x // [6,4,2] ``` --- ## Data types(Language-guide) FEEL defines the following types: ### Null Nothing, null, or nil (i.e. the value is not present). - Java Type: `null` ```feel null ``` ### Number A whole or floating point number. The number can be negative. - not-a-number (NaN), positive/negative infinity are represented as `null` - Java Type: `java.math.BigDecimal` ```feel 1 2.3 .4 -5 ``` ### String A sequence of characters enclosed in double quotes `"`. The sequence can contain escaped characters starting with `\` (e.g. `\'`, `\"`, `\\`, `\n`, `\r`, `\t`, Unicode like `\u269D` or `\U101EF`) and can span multiple lines. - Java Type: `java.lang.String` ```feel "valid" ``` ### Boolean A boolean value. It is either true or false. - Java Type: `java.lang.Boolean` ```feel true false ``` ### Date A date value without a time component. - Format: `yyyy-MM-dd`. - Java Type: `java.time.LocalDate` ```feel date("2017-03-10") @"2017-03-10" ``` ### Time A local or zoned time. The time can have an optional timezone offset. The usage of a time zone ID is deprecated because it can lead to ambiguous results without a date component. - Format: `HH:mm:ss` / `HH:mm:ss+/-HH:mm` - Java Type: `java.time.LocalTime` / `java.time.OffsetTime` ```feel time("11:45:30") time("13:30") time("11:45:30+02:00") @"11:45:30" @"13:30" @"11:45:30+02:00" ``` ### Date-time A date with a local or zoned time component. The time can have an offset or time zone ID. - Formats: - `yyyy-MM-dd'T'HH:mm:ss` - `yyyy-MM-dd'T'HH:mm:ssZ` - `yyyy-MM-dd'T'HH:mm:ss+/-HH:mm` - `yyyy-MM-dd'T'HH:mm:ss@ZoneId` - `yyyy-MM-dd'T'HH:mm:ss+/-HH:mm[ZoneId]` - Java Type: `java.time.LocalDateTime` / `java.time.DateTime` ```feel date and time("2015-09-18T10:31:10") date and time("2015-09-18T10:31:10Z") date and time("2015-09-18T10:31:10+01:00") date and time("2015-09-18T10:31:10@Europe/Paris") date and time("2015-09-18T10:31:10+02:00[Europe/Paris]") @"2015-09-18T10:31:10" @"2015-09-18T10:31:10Z" @"2015-09-18T10:31:10+01:00" @"2015-09-18T10:31:10@Europe/Paris" @"2015-09-18T10:31:10+02:00[Europe/Paris]" ``` ### Days-time-duration A duration based on seconds. It can contain days, hours, minutes, and seconds. - Format: `PxDTxHxMxS` - Java Type: `java.time.Duration` ```feel duration("P4D") duration("PT2H") duration("PT30M") duration("P1DT6H") @"P4D" @"PT2H" @"PT30M" @"P1DT6H" ``` ### Years-months-duration A duration based on the calendar. It can contain years and months. - Format: `PxYxM` - Java Type: `java.time.Period` ```feel duration("P2Y") duration("P6M") duration("P1Y6M") @"P2Y" @"P6M" @"P1Y6M" ``` ### List A list of elements. The elements can be of any type. The list can be empty. - Java Type: `java.util.List` ```feel [] [1,2,3] ["a","b"] [["list"], "of", [["lists"]]] ``` ### Context A list of entries. Each entry has a key and a value. The key is either a name or a string. The value can be any type. The context can be empty. - Java Type: `java.util.Map` ```feel {} {a:1} {b: 2, c: "valid"} {nested: {d: 3}} {"a": 1} {"b": 2, "c": "valid"} ``` --- ## Error handling(Language-guide) FEEL doesn't define any error handling. Instead, it follows one simple rule: if something goes wrong, return `null`. ### Null-friendly If an expression can't be evaluated successfully, it returns `null`. For example, in the following cases: - No variable exists with the given name - No context entry exists with the given key - No function exists with the given name - A function can't be invoked successfully with the given arguments - A value is compared to another value of a different type - An operation is not defined for the given values ### Handle null values Expressions and operators can deal with `null` values. In many cases, they result also in `null`. ```feel a.b > 10 // null [{a: 1}, {b: 2}].a // [1, null] [{a: 1}, {b: 2}][a < 10] // [{a: 1}] ``` To handle `null` values explicitly, use a [null-check](/components/modeler/feel/language-guide/feel-boolean-expressions.md#null-check) or the function [`get or else()`](/components/modeler/feel/builtin-functions/feel-built-in-functions-boolean.md#get-or-elsevalue-default). ```feel a != null and a.b > 10 get or else(a, "prio-99") ``` ### Assertions By default, the evaluation of an expression doesn't fail but returns `null`. If there is a special need to fail the evaluation under a certain condition, use the function [`assert()`](/components/modeler/feel/builtin-functions/feel-built-in-functions-boolean.md#assertvalue-condition). ```feel assert(a, a != null) // returns a if a is not null // fails if a is null assert(b, b >= 0, "'b' should be positive") ``` --- ## Introduction(Language-guide) FEEL expressions are powerful and can be used for various cases. This section is split into expressions based on their operational data type: - [Boolean](./feel-boolean-expressions.md) - [String](./feel-string-expressions.md) - [Numeric](./feel-numeric-expressions.md) - [List](./feel-list-expressions.md) - [Context](./feel-context-expressions.md) - [Temporal](./feel-temporal-expressions.md) The following sections cover more general areas that are not restricted to one data type: - [Variables](./feel-variables.md) - [Control flow](./feel-control-flow.md) - [Functions](./feel-functions.md) - [Error handling](./feel-error-handling.md) ### Comments An expression can contain comments to explain it and give it more context. This can be done using Java-style comments: `//` to the end of line, or `/*.... */` for blocks. ```feel // returns the last item [1,2,3,4][-1] /* returns the last item */ [1,2,3,4][-1] /* * returns the last item */ [1,2,3,4][-1] ``` ### Parentheses Parentheses `( .. )` can be used in expressions to separate different parts of an expression or to influence the precedence of the operators. ```feel (5 - 3) * (4 / 2) x < 5 and (y > 10 or z > 20) if (5 < 10) then "low" else "high" ``` --- ## Functions ### Invocation Invokes a built-in function (e.g. [`contains()`](/components/modeler/feel/builtin-functions/feel-built-in-functions-string.md#containsstring-match)) or a user-defined function by its name. The arguments of the function can be passed positional or named. - Positional: Only the values, in the same order as defined by the function (e.g. `f(1,2)`). - Named: The values with the argument name as prefix, in any order (e.g. `f(a: 1, b: 2)`). ```feel contains("me@camunda.com", ".com") // true contains(string: "me@camunda.com", match: ".de") // false ``` :::info GOOD TO KNOW The invocation returns `null` if no function exists with the given name, or if the argument types don't match the function signature. ::: ### User-defined ```feel function(a,b) e ``` Defines a function with a list of argument names, and an expression (i.e. the function body). When the function is invoked, it assigns the values to the arguments and evaluates the expression. Within an expression, a function can be defined and invoked in a context. ```feel { age: function(birthday) (today() - birthday).years } ``` --- ## List expressions You can use the following FEEL list expressions. Examples are provided to show common use cases. ### Literal Creates a new list of the given elements. The elements can be of any type. ```feel [1,2,3,4] ``` A list value can embed other list values. ```feel [[1,2], [3,4], [5,6]] ``` ### Get element ```feel a[i] ``` Accesses an element of the list `a` at index `i`. The index starts at `1`. If the index is out of the range of the list, it returns `null`. ```feel [1,2,3,4][1] // 1 [1,2,3,4][2] // 2 [1,2,3,4][4] // 4 [1,2,3,4][5] // null [1,2,3,4][0] // null ``` If the index is negative, it starts counting the elements from the end of the list. The last element of the list is at index `-1`. ```feel [1,2,3,4][-1] // 4 [1,2,3,4][-2] // 3 [1,2,3,4][-5] // null ``` :::caution be careful! The index of a list starts at `1`. In other languages, the index starts at `0`. ::: ### Filter ```feel a[c] ``` Filters the list `a` by the condition `c`. The result of the expression is a list that contains all elements where the condition `c` evaluates to `true`. The other elements are excluded. While filtering, the current element is assigned to the variable `item`. ```feel [1,2,3,4][item > 2] // [3,4] [1,2,3,4][item > 10] // [] [1,2,3,4][even(item)] // [2,4] ``` ### Some ```feel some a in b satisfies c ``` Iterates over the list `b` and evaluate the condition `c` for each element in the list. The current element is assigned to the variable `a`. It returns `true` if `c` evaluates to `true` for **one or more** elements of `b`. Otherwise, it returns `false`. ```feel some x in [1,2,3] satisfies x > 2 // true some x in [1,2,3] satisfies x > 5 // false some x in [1,2,3] satisfies even(x) // true some x in [1,2], y in [2,3] satisfies x < y // true ``` ### Every Iterates over the list `b` and evaluate the condition `c` for each element in the list. The current element is assigned to the variable `a`. It returns `true` if `c` evaluates to `true` for **all** elements of `b`. Otherwise, it returns `false`. ```feel every x in [1,2,3] satisfies x >= 1 // true every x in [1,2,3] satisfies x >= 2 // false every x in [1,2,3] satisfies even(x) // false every x in [1,2], y in [2,3] satisfies x < y // false ``` ## Examples ### Filter list and return the first element Return the first packaging element which unit is "Palette". ```feel data.attribute.packaging[unit = "Palette"][1] ``` ### Group list Group the given list of invoices by their person. Each invoice has a person. The persons are extracted from the invoices and are used as a filter for the list. ```feel for p in distinct values(invoices.person) return invoices[person = p] ``` #### Evaluation context ```feel {"invoices":[ {"id":1, "person":"A", "amount": 10}, {"id":2, "person":"A", "amount": 20}, {"id":3, "person":"A", "amount": 30}, {"id":4, "person":"A", "amount": 40}, {"id":5, "person":"B", "amount": 15}, {"id":6, "person":"B", "amount": 25} ]} ``` #### Evaluation result ```feel [ [ { id: 1, person: "A", amount: 10 }, { id: 2, person: "A", amount: 20 }, { id: 3, person: "A", amount: 30 }, { id: 4, person: "A", amount: 40 }, ], [ { id: 5, person: "B", amount: 15 }, { id: 6, person: "B", amount: 25 }, ], ] ``` ### Merge two lists Merge two given lists. Each list contains context values with the same structure. Each context has an `id` entry that identifies the value. The result is a list that contains all context values grouped by the identifier. ```feel { ids: union(x.files.id,y.files.id), getById: function (files,fileId) get or else(files[id=fileId][1], {}), merge: for id in ids return context merge(getById(x.files, id), getById(y.files, id)) }.merge ``` #### Evaluation context ```feel { "x": {"files": [ {"id":1, "content":"a"}, {"id":2, "content":"b"} ]}, "y": {"files": [ {"id":1, "content":"a2"}, {"id":3, "content":"c"} ]} } ``` #### Evaluation result ```feel [ { id: 1, content: "a2" }, { id: 2, content: "b" }, { id: 3, content: "c" }, ] ``` --- ## Numeric expressions ### Literal Creates a new numeric value. Leading zeros are valid. ```feel 1 0.5 .5 -2 01 -0002 ``` ### Addition Adds a value to another value. ```feel 2 + 3 // 5 ``` ### Subtraction Subtracts a value from another value. ```feel 5 - 3 // 2 ``` ### Multiplication Multiplies a value by another value. ```feel 5 * 3 // 15 ``` ### Division Divides a value by another value. ```feel 6 / 2 // 3 ``` ### Exponentiation Raises a value to the power of another value. ```feel 2 ** 3 // 8 ``` --- ## String expressions ### Literal Creates a new string value. ```feel "valid" ``` ### Addition/concatenation An addition concatenates the strings. The result is a string containing the characters of both strings. ```feel "foo" + "bar" // "foobar" ``` :::tip The concatenation is only available for string values. For other types, you can use the [`string()`](/components/modeler/feel/builtin-functions/feel-built-in-functions-conversion.md#stringfrom) function to convert the value into a string first. ```feel "order-" + string(123) ``` ::: --- ## Temporal expressions You can use the following FEEL temporal expressions. Examples are provided to show common use cases. ### Literal Creates a new temporal value. A value can be written in one of the following ways: - using a temporal function (for example, `date("2020-04-06")`) - using the `@` - notation (for example, `@"2020-04-06"`) ```feel date("2020-04-06") @"2020-04-06" time("08:00:00") time("08:00:00+02:00") time("08:00:00@Europe/Berlin") @"08:00:00" @"08:00:00+02:00" @"08:00:00@Europe/Berlin" date and time("2020-04-06T08:00:00") date and time("2020-04-06T08:00:00+02:00") date and time("2020-04-06T08:00:00@Europe/Berlin") @"2020-04-06T08:00:00" @"2020-04-06T08:00:00+02:00" @"2020-04-06T08:00:00@Europe/Berlin" duration("P5D") duration("PT6H") @"P5D" @"PT6H" duration("P1Y6M") duration("P3M") @"P1Y6M" @"P3M" ``` The value is `null` if a date or date-time literal doesn't represent a valid calendar date. For example, `@"2024-06-31"` is invalid because June has only 30 days. ### Addition Adds a value to another value. The operator is defined for the following types. If a value has a different type, the result is `null`. First argument Second argument Result date duration date time days-time-duration time date-time duration date-time duration date date duration time time duration date-time date-time duration duration duration ```feel date("2020-04-06") + duration("P1D") // date("2020-04-07") time("08:00:00") + duration("PT1H") // time("09:00:00") date and time("2020-04-06T08:00:00") + duration("P7D") // date and time("2020-04-13T08:00:00") duration("P2D") + duration("P5D") // duration("P7D") ``` ### Subtraction Subtracts a value from another value. The operator is defined for the following types. If a value has a different type, the result is `null`. If one value has a timezone or time-offset, the other value must have a timezone or time-offset too. Otherwise, the result is `null`. First argument Second argument Result date date days-time-duration date duration date time time days-time-duration time days-time-duration time date-time date-time days-time-duration date-time duration date-time days-time-duration days-time-duration days-time-duration years-months-duration years-months-duration years-months-duration ```feel date("2020-04-06") - date("2020-04-01") // duration("P5D") date("2020-04-06") - duration("P5D") // date("2020-04-01") time("08:00:00") - time("06:00:00") // duration("PT2H") time("08:00:00") - duration("PT2H") // time("06:00:00") duration("P7D") - duration("P2D") // duration("P5D") duration("P1Y") - duration("P3M") // duration("P9M") ``` ### Multiplication Multiplies a value by another value. The operator is defined for the following types. If a value has a different type, the result is `null`. First argument Second argument Result days-time-duration number days-time-duration number days-time-duration days-time-duration years-months-duration number years-months-duration number years-months-duration years-months-duration ```feel duration("P1D") * 5 // duration("P5D") duration("P1M") * 6 // duration("P6M") ``` ### Division Divides a value by another value. The operator is defined for the following types. If a value has a different type, the result is `null`. First argument Second argument Result days-time-duration days-time-duration number days-time-duration number days-time-duration years-months-duration years-months-duration number years-months-duration number years-months-duration ```feel duration("P5D") / duration("P1D") // 5 duration("P5D") / 5 // duration("P1D") duration("P1Y") / duration("P1M") // 12 duration("P1Y") / 12 // duration("P1M") ``` ### Properties A temporal value has multiple properties for its components. The following properties are available for the given types: Property Available for Description year date, date-time the year as number month date, date-time the month as number [1..12], where 1 is January day date, date-time the day of the month as number [1..31] weekday date, date-time the day of the week as number [1..7], where 1 is Monday hour time, date-time the hour of the day as number [0..23] minute time, date-time the minute of the hour as number [0..59] second time, date-time the second of the minute as number [0..59] time offset time, date-time the duration offset corresponding to the timezone or null timezone time, date-time the timezone identifier or null days days-time-duration the normalized days component as number hours days-time-duration the normalized hours component as number [0..23] minutes days-time-duration the normalized minutes component as number [0..59] seconds days-time-duration the normalized seconds component as number [0..59] years years-months-duration the normalized years component as number months years-months-duration the normalized months component as number [0..11] ```feel date("2020-04-06").year // 2020 date("2020-04-06").month // 4 date("2020-04-06").weekday // 1 time("08:00:00").hour // 8 date and time("2020-04-06T08:00:00+02:00").time offset // duration("PT2H") date and time("2020-04-06T08:00:00@Europe/Berlin").timezone // "Europe/Berlin" duration("PT2H30M").hours // 2 duration("PT2H30M").minutes // 30 duration("P6M").months // 6 ``` ## Examples ### Compare date with offset Check if a date is at least 6 months before another date. ```feel date1 < date2 + @"P6M" ``` ### Calculate age Return the current age of a person based on a given birthday. ```feel years and months duration(date(birthday), today()).years ``` ### Check for weekend Check if the current day is on a weekend. ```feel day of week(today()) in ("Saturday","Sunday") ``` ### Calculate duration between dates Return the duration between now and the next Tuesday at 08:00. ```feel (for x in 1..7 return date and time(today(),@"08:00:00Z") + @"P1D" * x )[day of week(item) = "Tuesday"][1] - now() ``` ### Calculate duration between times Return the duration between now and the next time it is 09:00 in the Europe/Berlin timezone. ```feel { time: @"09:00:00@Europe/Berlin", date: if time(now()) < time then today() else today() + @"P1D", duration: date and time(date, time) - now() }.duration ``` ### Calculate next weekday Return the next day that is not a weekend at 00:00. ```feel (for x in 1..3 return date and time(today(),@"00:00:00Z") + @"P1D" * x )[not(day of week(item) in ("Saturday","Sunday"))][1] ``` ### Change format of dates Transform a given list of date-time values into a custom format. ```feel for d in dates return { date: date(date and time(d)), day: string(date.day), month: substring(month of year(date), 1, 3), year: string(date.year), formatted: day + "-" + month + "-" + year }.formatted ``` #### Evaluation context ```feel ["2021-04-21T07:25:06.000Z", "2021-04-22T07:25:06.000Z"] ``` #### Evaluation result ```feel ["21-Apr-2021", "22-Apr-2021"] ``` ### Create a Unix timestamp Return the current point in time as a Unix timestamp. ```feel (now() - @"1970-01-01T00:00Z") / @"PT1S" * 1000 ``` #### Evaluation result ```feel 1618200039000 ``` --- ## Unary-tests A unary-tests expression is a special kind of boolean expression. Unary-tests expressions should be used for the input entries of a decision table (i.e. the conditions of a rule). A unary-tests expression returns `true` if one of the following conditions is fulfilled: - The expression evaluates to `true` when the input value is applied to the unary operators. - The expression evaluates to `true` when the input value is assigned to the special variable `?`. - The expression evaluates to a value, and the input value is equal to that value. - The expression evaluates to a list, and the input value is equal to at least one of the values. - The expression is equal to `-` (a dash). ### Comparison Compares the input value with a given value. The input value is passed implicitly as the first argument of the operator. Both values must be of the same type. Otherwise, the result is `null`. Operator Description Supported types (none) equal to any < less than number, string, date, time, date-time, duration <= less than or equal to number, string, date, time, date-time, duration > greater than number, string, date, time, date-time, duration >= greater than or equal number, string, date, time, date-time, duration ```feel "valid" < 10 <= date("2020-04-06") > time("08:00:00") >= duration("P5D") ``` ### Interval Checks if the input value is within a given interval between `x` and `y`. An interval has two boundaries that can be open `(x..y)` / `]x..y[` or closed `[x..y]`. If a boundary is closed, it includes the given value (i.e. less/greater than or equal). Otherwise, it excludes the value (i.e. less/greater than). The input value is passed implicitly to the operator. ```feel (2..5) // input > 2 and input < 5 ]2..5[ // input > 2 and input < 5 [2..5] // input >= 2 and input <= 5 (2..5] // input > 2 and input <= 5 ``` ### Disjunction/or Combines multiple unary-test expressions following the ternary logic. - Returns `true` if at least one unary-test evaluates to `true`. - Otherwise, it returns `false`. ```feel 2, 3, 4 // input = 2 or input = 3 or input = 4 < 10, > 50 // input < 10 or input > 50 ``` ### Negation/not Negates a given unary-test expression. The expression can be a comparison, an interval, or a disjunction. It returns `true` if the given unary-test evaluates to `false`. ```feel not("valid") // input != "valid" not(2, 3) // input != 2 and input != 3 ``` ### Expressions When a unary operator is not enough to express the condition, any expression that returns a boolean value can be used, such as [invoking a function](/components/modeler/feel/language-guide/feel-functions.md#invocation). In the expression, the input value can be accessed by the special variable `?`. ```feel contains(?, "good") // checks if the input value (string) contains "good" ends with(?, "@camunda.com") // checks if the input value (string) ends with "@camunda.com" ``` --- ## Variables(Language-guide) Learn how to access variables, follow valid variable naming rules, and safely use escaped names when working with FEEL expressions. ## Access variables Access the value of a variable by its [variable name](#variable-names). FEEL doesn't use a separate assignment operator to create or update process variables. Instead, FEEL evaluates variables that are already available in the current context, or you can define local values with [context entries](/components/modeler/feel/language-guide/feel-context-expressions.md). ```feel a + b ``` If the value of the variable is a context, a [context entry can be accessed](/components/modeler/feel/language-guide/feel-context-expressions.md#get-entrypath) by its key. ```feel a.b ``` If no variable exists with the given name, the expression returns `null`. :::tip Use a [null-check](/components/modeler/feel/language-guide/feel-boolean-expressions.md#null-check) if the variable can be `null` or is optional. ```feel a != null and a.b > 10 ``` ::: ## Variable names The name of a variable can be any alphanumeric string including the `_` symbol. For a combination of words, it's recommended to use the `camelCase` or the `snake_case` format. The `kebab-case` format is not allowed because it contains the operator `-`. When accessing a variable in an expression, keep in mind the variable name is case-sensitive. Restrictions of a variable name: - It may not start with a _number_ (e.g. `1stChoice` is not allowed; you can use `firstChoice` instead). - It may not contain _whitespaces_ (e.g. `order number` is not allowed; you can use `orderNumber` instead). - It may not contain an _operator_ (e.g. `+`, `-`, `*`, `/`, `=`, `>`, `<`, `?`, `.`). - It may not be a _literal_ (e.g. `null`, `true`, `false`) or a _keyword_ (e.g. `function`, `if` , `then`, `else`, `for`, `return`, `between`, `instance`, `of`, `not`, `in`, `and`, `or`, `some`, `every`, `satisfies`). #### Escape variable names If a variable name or a context key contains any special character (e.g. whitespace, dash, etc.) then the name can be wrapped into single backquotes/backticks (e.g. `` `foo bar` ``). ```feel `first name` `tracking-id` order.`total price` ``` :::tip Use the [`get value()`](/components/modeler/feel/builtin-functions/feel-built-in-functions-context.md#get-valuecontext-key) function to retrieve the context value of an arbitrary key. ```feel get value(order, "total price") ``` ::: --- ## Introduction(3) Everything begins with [data types](./feel-data-types.md). Read on this subject first to have a better understanding of the other content like [expressions](./feel-expressions-introduction.md) and [built-in functions](./feel-functions.md). If you're writing an expression for an input entry of a decision table, visit our documentation on [unary-tests](./feel-unary-tests.md). Otherwise, have a look at the more general section about [expressions](./feel-expressions-introduction.md). Looking for a function to call? Visit our documentation on [built-in functions](../builtin-functions/feel-built-in-functions-introduction.md). --- ## What is FEEL? **Friendly Enough Expression Language (FEEL)** is designed to write expressions in a way that is easily understood by both business professionals and developers. In Camunda, FEEL is used to define expressions in the context of [BPMN diagrams](/components/modeler/bpmn/bpmn.md), [DMN diagrams](/components/modeler/dmn/dmn.md), and [Forms](/components/modeler/forms/camunda-forms-reference.md). FEEL is specified in the [DMN specification](https://www.omg.org/spec/DMN/) of the Object Management Group (OMG). ## Learning FEEL To understand FEEL better and how it is integrated into Camunda, you can explore the following resources: - [Expressions in Camunda 8](/components/concepts/expressions.md): Learn how expressions are used within our platform. - [FEEL syntax and operators](./language-guide/feel-expressions-introduction.md): Learn how to write basic expression. - [Built-in functions](./builtin-functions/feel-built-in-functions-introduction.md): Read about functions the FEEL engine offers. ## FEEL engines To evaluate your expressions, Camunda employs two distinct FEEL engines depending on the use-case: FEEL Scala (the Java FEEL engine) and feelin (the JavaScript FEEL engine). Both support the basic FEEL syntax and functions. You can try out your expressions in our playgrounds outlined below. ### FEEL Scala (Java FEEL engine) [**FEEL Scala**](https://github.com/camunda/feel-scala) is a Java-based FEEL engine integrated into the backend of our platform. It is primarily responsible for evaluating expressions in BPMN diagrams and DMN tables. :::info info The FEEL Scala engine supports a set of extensions to standard DMN FEEL. The documentation marks them via the following tag: ::: Try out expressions in the [FEEL Scala Playground](https://camunda.github.io/feel-scala/docs/playground/). ### feelin (JavaScript FEEL engine) [**feelin**](https://github.com/nikku/feelin) is a JavaScript-based FEEL engine designed for use in the browser is used for [Camunda Forms](../forms/camunda-forms-reference.md) and [templating](../forms/configuration/forms-config-templating-syntax.md). [Camunda extensions](#camunda-extensions) are **not** currently supported by feelin. Try out expressions in the [feelin Playground](https://nikku.github.io/feel-playground/). --- ## What are Camunda Forms? :::note Support for Camunda Forms The initial release of Camunda Forms includes a debut minimal feature set, which will be expanded with upcoming versions. The Camunda Forms feature was added with the 7.15.0 release of Camunda 7 and the 4.7.0 release of [Camunda Modeler](../about-modeler.md). Therefore, they can be used within BPMN diagrams running on Camunda 7 version 7.15.0 or later. ::: The Camunda Forms feature allows you to design and configure forms. Once configured, they can be connected to a user task or start event to implement a task form in your application. While you can incorporate Camunda Forms solely within Camunda 8, you can also utilize Camunda Forms in Camunda 7. After deploying a diagram with an embedded form, Tasklist imports this form schema and uses it to render the form on every task assigned to it. To learn more about how Camunda Forms are created in Camunda Modeler and embedded in Camunda Tasklist, visit our guide on [user task forms](/components/modeler/forms/utilizing-forms.md). Camunda Forms are powered by the open source [bpmn-io form-js library](https://github.com/bpmn-io/form-js). Visit the [open source repository](https://github.com/bpmn-io/form-js) to find out how to render a form using plain JavaScript in a custom application (note that this also requires you to fetch the form from the respective BPMN 2.0 element and provide data as needed to the form.) ## Additional resources - [Form element library](./form-element-library/forms-element-library.md): An overview of the components supported by Camunda Forms. - [SAP design system supported form features and properties](/components/camunda-integrations/sap/btp-plugin.md#supported-form-features-and-properties) --- ## Data binding ## Binding form fields to process data ### Overview Each **form element** that allows data manipulation is considered a **form field** and has a **Key** attribute. The "Key" serves a dual purpose: 1. To map data to the field during the initial loading of the form. 2. To map the field's data back to the process variable during form submission. When a form is part of a user task or start event and viewed in [Tasklist](../../../tasklist/introduction-to-tasklist.md), the "Key" correlates with a process variable. This variable serves as the initial value of the field and is updated upon form submission. ### Simple key vs path key #### Simple key Traditionally, a key was defined as a simple variable name. This key directly matches a process variable. ``` key: "username" ``` #### Path as key With newer updates, keys can now also be defined as a path, like `user.info.age`. This allows for more complex data structures. ``` key: "user.info.age" ``` Setting this field to value `21` would lead to the following output data: ``` { "user": { "info": { "age": 21 } } } ``` ### Path extension via groups In a form that utilizes **groups**, the group's **path** acts as a prefix to the key of each child field within that group. This effectively extends the key and allows for more intricate data mapping. For example, if the group's path is `user.info`, and a child field has a key of `age`, the complete key would become `user.info.age`. This is particularly useful to deal with nested data structure inputs and outputs. ![Group Example Image](../assets/group-mapping-example.png) If the above group has a path set to `user.info`, and if each field's key matches its label, the resulting output data will look like this: ``` { "user": { "info": { "surname": "Inco", "name": "Gnito", "age": 29 } } } ``` Group paths can be shared by multiple groups, as long as fields themselves do not conflict. However, depending on your use case, it might make sense for your visual and data structure to match. ### Path configuration for dynamic lists While dynamic lists share similarities with groups in form structuring, they differ slightly in terms of path configuration. Since dynamic lists are bound to arrays, they cannot share paths with other dynamic lists or groups. In this instance, consider a dynamic list with the path `contacts`. Each entry in the list might contain fields like `name` and `phone`. The dynamic list directly outputs an array of objects under its own path, and the key of those child variables is used in the creation of the individual objects. The resulting data structure would resemble: ``` { "contacts": [ { "name": "John Doe", "phone": "123-456-7890" }, { "name": "Jane Smith", "phone": "098-765-4321" } // Additional objects for each item in the list ] } ``` This structure is maintained when binding to input data as well. You can programmatically control how many list items are rendered from the number of elements in the array at the binding path, whether individual element data is provided or not. --- ## Options Source ## Configuring form field options sources Certain form fields work from a set of pre-populated options that your user will want to select from, such as the [Radio](../form-element-library/forms-element-library-radio.md) or [Select](../form-element-library/forms-element-library-select.md) fields. This source can be configured several ways, as described below. ### Static configured on the form schema The options will be defined directly on your form schema, the only way to modify them in the future will be to change the form definition itself. The static options group will appear to allow the configuration of the individual options. ![Static Options Group Image](../assets/static-options-example.png) For each of these options, a unique `value` corresponding to the form submitted data must be provided, as well as a `label` which will be displayed to the user filling in the form. You may configure as many options as you want by using the add (+) button in the group header. ### Input data driven by process data The options are mapped from form variables, similarly to how [Form field data binding](./forms-config-data-binding.md) works. Here, the `Input values key` property within the `Dynamic Options` configuration group is used to set which form variable to use as a source. The expected format for the data is an array of options, each defining its label and value in JSON. The below example provides an equivalent configuration to the above statically defined one: ```json { "languageData": [ { "label": "French", "value": "fr" }, { "label": "English (UK)", "value": "en-gb" }, { "label": "German", "value": "de" } ] } ``` ### FEEL expressions The options are populated from a [FEEL expression](../../feel/language-guide/feel-expressions-introduction.md) that returns an array of options. The resulting array must be formatted as described in the [example JSON above](#input-data-driven-by-process-data). #### Shorthand definitions If the value and label are equal, shorthand formats may be used instead: ```json { "languageData": [{ "value": "fr" }, { "value": "en-gb" }, { "value": "de" }] } ``` ```json { "languageData": ["fr", "en-gb", "de"] } ``` #### Supported types The `label` parameter should be a `string`. If a label is provided, the `value` parameter can be any non-null type. Otherwise, it is restricted to types `string`, `number`, and `boolean`. --- ## Table data binding When using the table component, you can bind the table to a context variable to populate the table with data from the process. To do this, set two properties when modeling your form: **Headers source** and **Data source**. ## Headers source For **Headers source**, define the columns of the table. You have two options: - **List of items**: Define the headers statically by adding the headers in the **Headers items** section. From there, you can define a list of columns defined by a **Label** and a **Key**. The **Label** is the text that will be displayed in the header, and the **Key** is the name of the variable that will be used to populate the column. - **Expression**: Define the headers dynamically by using an [expression](../../feel/language-guide/feel-expressions-introduction.md). The [expression](../../feel/language-guide/feel-expressions-introduction.md) must return a list of objects with a **label** and a **key** property. For example, if you have a list of objects with a **name** and a **surname** property, your [expression](../../feel/language-guide/feel-expressions-introduction.md) must evaluate to the following JSON to define the headers: ```json [ { "label": "Name", "key": "name" }, { "label": "Surname", "key": "surname" } ] ``` ## Data source To define the data which will be displayed as table rows, define the **Data source** property. This field accepts only [expressions](../../feel/language-guide/feel-expressions-introduction.md). The [expression](../../feel/language-guide/feel-expressions-introduction.md) must return a list of objects, where each object represents a row in the table. Each object must have a property for each column defined in the **Headers source** property. For example, if you have a list of objects with a **name** and a **surname** property, your [expression](../../feel/language-guide/feel-expressions-introduction.md) must evaluate to the following JSON to define the data: ```json [ { "name": "John", "surname": "Doe" }, { "name": "Jane", "surname": "Doe" } ] ``` --- ## Templating syntax Templated properties configuration allows for dynamic content creation within forms using a templating language called [**feelers**](https://github.com/bpmn-io/feelers). ## Feelers syntax ### Variables/inserts To insert a variable, use double curly braces `{{variable}}`, and the value of this variable will be inserted. You can use **any valid [FEEL expression](../../feel/language-guide/feel-expressions-introduction.md)** within these double braces. ``` Hello {{username}}, you are {{if isAdmin then "an admin" else "a user"}}. ``` ### Iterating through arrays Iterate through arrays using the _loop_ tags. Within the loop, reference each array element with `{{this}}`, or if your array elements are objects, via their properties. To access data outside the scope of the individual items, use the `{{parent}}` accessor. **Data** ```json { "currency": "$", "items": [ { "name": "bananas", "price": 2.5 }, { "name": "mangos", "price": 4 }, { "name": "strawberries", "price": 3 } ] } ``` **Template** ``` {{#loop items}} Item name: {{name}} Item price: {{parent.currency}}{{price}} {{/loop}} ``` ### Conditional sections Conditionally render a section of your template using the `if` tags. This is a quick way to write out large blocks you may or may not want evaluated based on a condition: ``` {{#if user.isCook}} Ingredients list: {{#loop ingredients}} * {{this}} {{/loop}} {{/if}} ``` ## Additional details ### Nest loops If you have an array of users, each with an array of purchases, you may loop over both in a nested manner: **Data** ```json { "users": [ { "name": "jane1995", "purchases": ["mango", "strawberry"] }, { "name": "rob1992", "purchases": ["pineapple", "guava"] } ] } ``` **Template** ``` {{#loop users}} The user '{{name}}' purchased: {{#loop purchases}} * {{this}} {{/loop}} {{/loop}} ``` In this situation, you may need to use the `parent` accessor several times to access data outside the scope. ### More on the `parent` and `this` accessors If the data you are using somehow already makes use of those keywords, there is an alternative syntax which surrounds it with an underscore: `_this_` and `_parent_`. **Data** ```json { "root": "nodes", "nodes": [ { "id": "021321321", "parent": "228321321" }, { "id": "021321321", "parent": "228321321" } ] } ``` **Template** ``` Listing out all node paths: {{#loop nodes}} * http://www.myNodeWebsite/{{_parent_.root}}/{{id}} {{/loop}} ``` In the example above, if you are not surrounding the parent accessor with underscores, you access the parent property of the node, which is not what we're looking for. This also applies to the `this` accessor. --- ## Button A button allowing the user to trigger form actions. ### Configurable properties - **Field label**: Label to be displayed on top of the button. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Action**: The button can either trigger a **Submit** or a **Reset** action. - **Submit**: Submit the form (given there are no validation errors). - **Reset**: Reset the form, all user inputs will be lost. - **Hide if**: [Expression](../../feel/language-guide/feel-expressions-introduction.md) to hide the button. - **Columns**: Space the button will use inside its row. **Auto** means it will automatically adjust to available space in the row. Read more about the underlying grid layout in the [Carbon Grid documentation](https://carbondesignsystem.com/elements/2x-grid/overview/). --- ## Checkbox group A set of checkbox options providing data multi-selection for small datasets. ### Configurable properties - **Field label**: Label displayed on top of the checkbox group. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Field description**: Description provided below the checkbox group. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Key**: Binds the field to a form variable, refer to [data binding docs](../configuration/forms-config-data-binding.md). - **Options source**: Checkbox group components can be configured with an options source defining the individual choices your user can make, refer to [options source docs](../configuration/forms-config-options.md). - **Read only**: Makes the checkbox group read-only, meaning the user can't change but only read its state. Can be dynamically set using an [expression](../../feel/language-guide/feel-expressions-introduction.md). - **Disabled**: Disables the checkbox group, for use during development. - **Hide if**: [Expression](../../feel/language-guide/feel-expressions-introduction.md) to hide the checkbox group. - **Validation**: Given that one of the following properties is set, the form will only submit when the respective condition is fulfilled. Otherwise, a validation error will be displayed. - **Required**: Checkbox group must contain a value. - **Columns**: Space the field will use inside its row. **Auto** means it will automatically adjust to available space in the row. Read more about the underlying grid layout in the [Carbon Grid documentation](https://carbondesignsystem.com/elements/2x-grid/overview/). ### Datatypes Checkbox group components can be bound to data of the `any[]` type, although for most practical cases we recommend `string[]` instead. The checkbox group component correlates the bound data with the values of the different options. The data representation of this checkbox group: ![Checklist Selection Image](../assets/checklist-example.png) Looks like this: ``` { "mailto": [ "regional-manager", "approver" ], } ``` --- ## Checkbox A checkbox allowing the user to read and edit boolean data. ### Configurable properties - **Field label**: Label displayed besides the checkbox. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Field description**: Description provided below the checkbox. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Key**: Binds the field to a form variable, refer to [data binding docs](../configuration/forms-config-data-binding.md). - **Default value**: Provides a default state for the checkbox in case no input data exists for the given key. - **Read only**: Makes the checkbox read-only, meaning the user can't change but only read its state. Can be dynamically set using an [expression](../../feel/language-guide/feel-expressions-introduction.md). - **Disabled**: Disables the checkbox, for use during development. - **Hide if**: [Expression](../../feel/language-guide/feel-expressions-introduction.md) to hide the checkbox. - **Validation**: Given that one of the following properties is set, the form will only submit when the respective condition is fulfilled. Otherwise, a validation error will be displayed. - **Required**: Checkbox must contain a value. - **Columns**: Space the field will use inside its row. **Auto** means it will automatically adjust to available space in the row. Read more about the underlying grid layout in the [Carbon Grid documentation](https://carbondesignsystem.com/elements/2x-grid/overview/). ### Datatypes Checkboxes can be bound to data of the `boolean` type. Any other datatype will be treated as a `false` by default. --- ## Datetime A component allowing the user to read and edit date and time data. ## Configurable properties - **Date label**: Label displayed beside the date input field. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Time label**: Label displayed beside the time input field. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Field description**: Description provided below the datetime component. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Key**: Binds the field to a form variable, refer to [data binding docs](../configuration/forms-config-data-binding.md). - **Subtype**: Selects the type of the datetime component. This can either be **Date**, **Time**, or **Date & Time**. - **Use 24h**: Enables 24-hour time format. - **Read only**: Makes the datetime component read-only, meaning the user can't change but only read its state. Can be dynamically set using an [expression](../../feel/language-guide/feel-expressions-introduction.md). - **Disabled**: Disables the datetime component, for use during development. - **Hide if**: [Expression](../../feel/language-guide/feel-expressions-introduction.md) to hide the datetime component. - **Columns**: Space the field will use inside its row. **Auto** means it will automatically adjust to available space in the row. Read more about the underlying grid layout in the [Carbon Grid documentation](https://carbondesignsystem.com/elements/2x-grid/overview/). - **Time format**: Defines the time data format. This can either be **UTC offset**, **UTC normalized**, or **No timezone**. - **Time interval**: Defines the steps of time that can be selected in the time input field. - **Disallow past dates**: Enables the restriction to not allow past dates. - **Validation**: Given that one of the following properties is set, the form will only submit when the respective condition is fulfilled. Otherwise, a validation error will be displayed. - **Required**: Datetime component must contain a value. ## Datatypes Datetime components can be bound to data of the `string` type. The format of the string depends on the subtype: - **date**: ISO 8601 string of the format `YYYY-MM-DD`. - **datetime**: ISO 8601 string of the format `YYYY-MM-DDTHH:MM`. Note that leading zeroes must be present in the hour and minutes (e.g., 01:30 not 1:30); this is an ISO 8601 requirement. - **time**: String of the format `HH:MM`. Leading zeros can be omitted. --- ## Document preview A form element to preview and download documents. ## Configurable properties #### General - **Title**: Title displayed on top of the element. - Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Document reference**: Data used to render documents. - Accepts an array of objects with document metadata. - Can only be an [expression](../../feel/language-guide/feel-expressions-introduction.md). - The resulting value must have the following structure: ```json [ { "documentId": "u123", "endpoint": "https://api.example.com/documents/u123", "metadata": { "fileName": "Document.pdf", "contentType": "application/pdf" } } ] ``` :::note When previewing documents that were uploaded via [Filepicker](./forms-element-library-filepicker.md) in Tasklist, document references are handled automatically: - Modifying the document’s metadata after upload may prevent the preview from working correctly. - To use Camunda's document service without Filepicker, you must include the `contentHash` and `documentId` values. ::: #### Condition - **Hide if**: [Expression](../../feel/language-guide/feel-expressions-introduction.md) to hide the element. #### Layout - **Columns**: Space the field will use inside its row. - **Auto** means it will automatically adjust to available space in the row. Read more about the underlying grid layout in the [Carbon Grid documentation](https://carbondesignsystem.com/elements/2x-grid/overview/). #### Appearance - **Max height of preview container**: A number which will define the maximum height of each document preview. Any document with a bigger height will display a scroll bar. ## Additional guides Design process applications for document handling with the Document preview component. --- ## Dynamic list The **dynamic list** element is designed to dynamically manage a list of form elements. It enables users to add or remove items from the list and is particularly useful in scenarios where the number of items in a list is not fixed. ## Configurable properties - **Group label**: Label displayed on top of the dynamic list. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Path**: Assigns a path that maps its children into a data object, defined as a variable name or a dot separated variable accessor. See the [data binding docs](../configuration/forms-config-data-binding.md) for more details. - **Default number of items**: Specifies the default number of items rendered when no input data is provided. - **Allow add/delete items**: Enables users to add new items to or delete existing items from the list. - **Disable collapse**: Prevents items in the list from being collapsed. - **Number of non-collapsing items**: Defines the number of items in the list that will not collapse. - **Vertical alignment**: Determines the alignment of items in the list. - **Hide if**: [Expression](../../feel/language-guide/feel-expressions-introduction.md) to hide the dynamic list. ## Usage The dynamic list element facilitates the management of data lists in forms. It is highly adaptable, with features like default item count, add/delete capabilities, and control over item collapse. It's the ideal component for scenarios where users need to input a variable number of items, such as adding multiple contacts or entries in a form. --- ## Expression field An expression field allowing the user to compute new data based on form state. ### Configurable properties - **Key**: Binds the field to a form variable, refer to [data binding docs](../configuration/forms-config-data-binding.md). - **Target value**: Defines an [expression](../../feel/language-guide/feel-expressions-introduction.md) to evaluate. - **Compute on**: Defines when the expression should be evaluated. Either whenever the result changes, or only on form submission. - **Deactivate if**: [Expression](../../feel/language-guide/feel-expressions-introduction.md) to disable the expression. - **Columns**: Space the field will use inside its row. **Auto** means it will automatically adjust to available space in the row. Read more about the underlying grid layout in the [Carbon Grid documentation](https://carbondesignsystem.com/elements/2x-grid/overview/). :::info The expression field is a simple way to create intermediary data which may be re-used within your form, or further down your process. To effectively use this component, a good understanding of [FEEL](../../feel/language-guide/feel-expressions-introduction.md) is required. ::: --- ## Filepicker A form element to select files. ## Configurable properties #### General - **Field label**: Label displayed on top of the Filepicker. - It can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Supported file formats**: [Comma-separated list of supported file formats.](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#unique_file_type_specifiers) - It can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md) or plain text. - **Upload multiple files**: Allows the user to upload multiple files at once. - It can be dynamically set using an [expression](../../feel/language-guide/feel-expressions-introduction.md). - **Key**: Binds the field to a form variable, refer to the [data binding documentation](../configuration/forms-config-data-binding.md). - **Read only**: Makes the Filepicker read-only, meaning the user can't change but only read its state. - It can be dynamically set using an [expression](../../feel/language-guide/feel-expressions-introduction.md). - **Disabled**: Disables the Filepicker, for use during development. #### Condition - **Hide if**: [Expression](../../feel/language-guide/feel-expressions-introduction.md) to hide the element. #### Layout - **Columns**: Space the field will use inside its row. - **Auto** means it will automatically adjust to available space in the row. Read more about the underlying grid layout in the [Carbon Grid documentation](https://carbondesignsystem.com/elements/2x-grid/overview/). #### Validation Given that one of the following properties is set, the form will only submit when the respective condition is fulfilled. Otherwise, a validation error will be displayed. - **Required**: Filepicker must have a selected file. ## Reference uploaded files The Filepicker's output variable is an array of objects with file metadata that represents each uploaded file. It always returns an array of objects, either a user uploads a single file or multiple files. Single file uploads are accessible using `value[1]` (since [FEEL](../../feel/what-is-feel.md) uses 1-based indexing). ## Additional guides Design process applications for document handling with the Filepicker component. --- ## Group The group element serves as a container to group various form elements together. It allows for nesting of fields and assists in organizing complex forms. ### Configurable properties - **Group label**: Label displayed on top of the group. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Path**: Assigns a path that maps its children into a data object, may be left empty, defined as a variable name or a dot separated variable accessor. See the [data binding docs](../configuration/forms-config-data-binding.md) for more details. - **Show outline**: Can be toggled on and off to display a separating outline around the group - **Vertical alignment**: Determines the alignment of items in the list. - **Hide if**: [Expression](../../feel/language-guide/feel-expressions-introduction.md) to hide the group. - **Columns**: Space the field will use inside its row. **Auto** means it will automatically adjust to available space in the row. Read more about the underlying grid layout in the [Carbon Grid documentation](https://carbondesignsystem.com/elements/2x-grid/overview/). ### Usage The group element allows for nesting of various form elements. Children elements inherit the group's path as a prefix to their key, streamlining data mapping. You can nest additional group elements for deeper organization. Groups may also be used to show and hide entire sections of the form. --- ## HTML view A flexible display component designed to quickly render HTML content for the user. ## Configurable properties - **Content**: This property accepts HTML content. Define it using [templating syntax](../configuration/forms-config-templating-syntax.md) or as plaintext HTML. The rendered content is sanitized for security reasons, see below for details. - **Hide if**: [Expression](../../feel/language-guide/feel-expressions-introduction.md) to conditionally hide the HTML content. - **Columns**: Space the field will use inside its row. The **Auto** means it will automatically adjust to available space in the row. Read more about the underlying grid layout in the [Carbon Grid documentation](https://carbondesignsystem.com/elements/2x-grid/overview/). ## Our security and sanitation strategy We prevent all usages of JavaScript inside the HTML, this means that this component is solely meant to be used for presentation of information. Additionally, any text rendered through templating will be escaped. This means that you cannot pass HTML and CSS dynamically, by design, as certain attack vectors could make use of it. You may however inject individual style properties in your CSS. We automatically scope all CSS, templated or otherwise, to the root of the component. This ensures the component does not affect and potentially break the entire structure of the form. :::note While these are all strong security mechanisms, we still advise you to ensure the data coming into your form is trusted, and to use templating only where you really need it. ::: ## HTML configurations Below are various ways of configuring your HTML component, depending on your use case and configuration needs. **Basic HTML**: ``` Welcome to Our Site This is a paragraph with some bold text and italic text. Click here to visit our page. ``` **HTML with inline styling**: ``` Styling Example This paragraph is styled with inline CSS. ``` **HTML with style tags**: ``` Styling Example This paragraph is styled with CSS in a style tag. ``` **List and images**: ``` First Item Second Item ``` **Templating notation** ``` {{pageTitle}} Welcome, {{user.name}}! Your selected color is: {{user.favoriteColor}} Your tasks for today are: {{#loop user.tasks}} {{this}} {{/loop}} ``` --- ## iframe This is an element allowing the user to embed external content via an iframe. :::note Every iframe component is a sandbox. This means that the content of the iframe is not able to access the parent page, cookies, browser storage, and others. [Learn more about sandbox iframes](https://www.w3schools.com/tags/att_iframe_sandbox.asp). ::: ## Configurable properties - **Title**: Label displayed on top of the iframe and as the accessible title. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **URL**: Enter an HTTPS URL to a source. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). Ensure the URL is safe as it might impose security risks. Not all external sources can be displayed in the iframe. Read more about it in [the X-FRAME-OPTIONS documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options). - **Height**: Defines the height of the iframe. Defined as number of pixels. - **Security attributes**: Allow the iframe's sandbox more access to various functionality of the browser, at the cost of security. - **Script execution**: Enables script running, essential for interactive websites. - **Allow same origin**: Controls the same-origin policy for the iframe, impacting access to data like cookies, local storage, and DOM storage. - **Open in fullscreen**: Allows the content of the iframe to request fullscreen mode. - **Geolocation**: Grants or denies access to geolocation services. - **Camera/Microphone access**: Required for functionality which makes use of the camera/microphone. You may need to allow it through a browser prompt as well. - **Forms submission**: Enables the submission of forms within the iframe. - **Open modal windows/popups**: Permits the iframe to open modal windows/popups. - **Top level navigation**: Gives the iframe permission to change the URL of the parent page, navigating away entirely from it. - **Storage access by user**: Controls access of local storage based on user interactions, may be expected in addition to allow same origin on certain browsers for functionality depending on storage. - **Hide if**: [Expression](../../feel/language-guide/feel-expressions-introduction.md) to hide the iframe. - **Columns**: Space the field will use inside its row. **Auto** means it will automatically adjust to available space in the row. Read more about the underlying grid layout in the [Carbon Grid documentation](https://carbondesignsystem.com/elements/2x-grid/overview/). ## Security advisory When configuring iframes, it's essential to understand the security implications, especially if you are not certain of what you'll be rendering ahead of time. - **URL Caution**: Be very careful with the URLs loaded into the iframe. This isn't much of a concern if you just statically define a site, but if you are using links from say, some data a user submitted earlier in your process, it is critical that those are validated prior to being rendered in the iframe. - **Script Execution**: Enabling script execution can expose users to cross-site scripting (XSS) attacks if the content source is not secure. Limit this functionality to trusted, verified sources. - **Allow Same Origin**: This allows the iframe to access cookies, local storage, and DOM storage. Enabling this for an untrusted source can lead to data leaks or other security breaches. - **Camera/Microphone Access and Geolocation**: These features should only be enabled for sites where they are absolutely necessary and trusted. Unauthorized access to these can severely compromise user privacy. - **Top-Level Navigation**: This allows the iframe to redirect the parent page. Be cautious, as malicious sites can abuse this to redirect users to harmful websites. - **Modal Windows/Popups**: While useful, these can be exploited for phishing attacks or unwanted advertising. Only enable for trusted content. You should adopt a allowlisting approach to iframe configuration. This means **only enabling the bare minimum functionality** that you need for your use-case, which ensures the attack surface is kept as low as possible. Additionally, if you are rendering an external link you don't have control over, ensure that you specify what that link should look like and **validate it** somewhere in your process prior to rendering it. If the link could be anything, then you should not render it in this component. --- ## Image view An element allowing the user to display images. ## Configurable properties - **Image source**: Specifies the image source via [expression](../../feel/language-guide/feel-expressions-introduction.md), [templating syntax](../configuration/forms-config-templating-syntax.md) or [static value](/components/concepts/expressions.md#expressions-vs-static-values) (hyperlink or data URI). - **Alternative text**: Provides an alternative text to the image in case it cannot be displayed. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Hide if**: [Expression](../../feel/language-guide/feel-expressions-introduction.md) to hide the image. - **Columns**: Space the field will use inside its row. **Auto** means it will automatically adjust to available space in the row. Read more about the underlying grid layout in the [Carbon Grid documentation](https://carbondesignsystem.com/elements/2x-grid/overview/). --- ## Number A number field allowing the user to read and edit numeric data. ### Configurable properties - **Field label**: Label displayed on top of the number field. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Field description**: Description provided below the number field. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Key**: Binds the field to a form variable, refer to [data binding docs](../configuration/forms-config-data-binding.md). - **Default value**: Provides a default value for the number field in case no input data exists for the given key. - **Decimal digits**: Defines the maximum number of digits after the decimal. - **Increment**: Defines the increment between valid field values. - **Read only**: Makes the number field read-only, meaning the user can't change but only read its state. Can be dynamically set using an [expression](../../feel/language-guide/feel-expressions-introduction.md). - **Disabled**: Disables the number field, for use during development. - **Hide if**: [Expression](../../feel/language-guide/feel-expressions-introduction.md) to hide the number. - **Columns**: Space the field will use inside its row. **Auto** means it will automatically adjust to available space in the row. Read more about the underlying grid layout in the [Carbon Grid documentation](https://carbondesignsystem.com/elements/2x-grid/overview/). - **Serialize to string**: Configures the output format of the datetime value. This enables unlimited precision digits. - **Validation**: Given that one of the following properties is set, the form will only submit when the respective condition is fulfilled. Otherwise, a validation error will be displayed. - **Required**: Number field must contain a value. - **Minimum**: Number field value must be at least `n`. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md) or a number. - **Maximum**: Number field value must be no larger than `n`. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md) or a number. - **Appearance**: Changes the visual appearance of the number field. - **Prefix**: Adds an appendage before the input. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Suffix**: Adds an appendage after the input. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). ### Datatypes Number can be bound to numeric data, or `strings` which can be parsed to numeric data (as per [JavaScript's tryParse](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt)), but will always output strictly `integer` data. --- ## Radio group A radio group allowing the user to select one of multiple data options for small datasets. ### Configurable properties - **Field label**: Label displayed above the radio group. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Field description**: Description provided below the radio group. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Key**: Binds the radio group component to a form variable, refer to [data binding docs](../configuration/forms-config-data-binding.md). - **Default value**: Provides a default selection in case no input data exists for the given key. Only available for _static_ options sources. - **Read only**: Makes the radio group read-only, meaning the user can't change but only read its state. Can be dynamically set using an [expression](../../feel/language-guide/feel-expressions-introduction.md). - **Disabled**: Disables the radio group, for use during development. - **Options source**: Radio group components can be configured with an options source defining the individual choices the component provides, refer to [options source docs](../configuration/forms-config-options.md). - **Hide if**: [Expression](../../feel/language-guide/feel-expressions-introduction.md) to hide the radio group. - **Columns**: Space the field will use inside its row. **Auto** means it will automatically adjust to available space in the row. Read more about the underlying grid layout in the [Carbon Grid documentation](https://carbondesignsystem.com/elements/2x-grid/overview/). - **Validation**: Given that one of the following properties is set, the form will only submit when the respective condition is fulfilled. Otherwise, a validation error will be displayed. - **Required**: One radio option must be selected. ### Datatypes Radio group components can be bound to `any` data, but we recommend working with `strings`. The component will correlate the data value with the appropriate option defined in the options source. If no option is found, the data will simply be ignored. --- ## Select A Select dropdown allowing the user to select one of multiple data option from larger datasets. ### Configurable properties - **Field label**: Label displayed above the select. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Field description**: Description provided below the select. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Key**: Binds the selected value to a form variable, refer to [data binding docs](../configuration/forms-config-data-binding.md). - **Default value**: Provides a default selection in case no input data exists for the given key. Only available for _static_ options sources. - **Searchable**: Allows the select entries to be searched via keyboard. - **Read only**: Makes the select read-only, meaning the user can't change but only read its state. Can be dynamically set using an [expression](../../feel/language-guide/feel-expressions-introduction.md). - **Disabled**: Disables the select, for use during development. - **Hide if**: [Expression](../../feel/language-guide/feel-expressions-introduction.md) to hide the select. - **Columns**: Space the field will use inside its row. **Auto** means it will automatically adjust to available space in the row. Read more about the underlying grid layout in the [Carbon Grid documentation](https://carbondesignsystem.com/elements/2x-grid/overview/). - **Options source**: Selects can be configured with an options source defining the individual choices the select provides, refer to [options source docs](../configuration/forms-config-options.md). - **Validation**: Given that one of the following properties is set, the form will only submit when the respective condition is fulfilled. Otherwise, a validation error will be displayed. - **Required**: One select entry must be selected. ### Datatypes Select components can be bound to `any` data, but we recommend working with `strings`. The component will correlate the data value with the appropriate option defined in the options source. If no option is found, the data will simply be ignored. --- ## Separator A **separator** element is used to create a visual separation between two elements. ## Usage The separator element is typically utilized when you want to visually distinguish between form elements. For example, you could use a separator line to delineate different sections of form fields, create a visual break between a heading and subsequent content, or to clarify the boundaries between various parts of a form. Incorporating separators can aid in generating more organized and easier-to-read layouts, particularly in intricate forms or pages featuring multiple sections. --- ## Spacer A **spacer** element is used to create a defined amount of vertical space between two elements. ## Configurable properties - **Height**: Defines the vertical space the spacer takes up. Defined as number of pixels. ## Usage The spacer element is generally used in scenarios where you need to add separation between elements in the layout. For instance, you might use a spacer to separate two blocks of form fields, to create a gap between a heading and the subsequent content, or to prevent different sections of the page from visually merging together. Using the spacer accordingly can help create cleaner, more readable layouts, especially in complex forms or pages with many different sections. --- ## Table This is an element allowing the user to render tabular data. ## Configurable properties - **Table label**: Label displayed on top of the table and as the accessible label. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Data source**: Data used to render the rows of the table. This can only be an [expression](../../feel/language-guide/feel-expressions-introduction.md). Review [table data binding](../configuration/forms-config-table-data-binding.md) for the required data source structure. - **Pagination**: Enables pagination in the table. If enabled, it splits the rows provided by **Data source** into chunks with the size defined by the field **Number of rows per page**. - **Number of rows per page**: The size of each page. Used only if pagination is enabled. Must be greater than zero. - **Headers source**: Defines which headers will be used in the table. This can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md) or a list of static headers. Review [table data binding](../configuration/forms-config-table-data-binding.md) for the required header structure. - **Hide if**: [Expression](../../feel/language-guide/feel-expressions-introduction.md) to hide the table. - **Columns**: Space the field will use inside its row. **Auto** means it will automatically adjust to available space in the row. Read more about the underlying grid layout in the [Carbon Grid documentation](https://carbondesignsystem.com/elements/2x-grid/overview/). --- ## Taglist A complex and searchable tag based component providing multi-selection for large datasets. ### Configurable properties - **Field label**: Label displayed on top of the taglist. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Field description**: Description provided below the taglist. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Key**: Binds the field to a form variable, refer to [data binding docs](../configuration/forms-config-data-binding.md). - **Hide if**: [Expression](../../feel/language-guide/feel-expressions-introduction.md) to hide the taglist. - **Columns**: Space the field will use inside its row. **Auto** means it will automatically adjust to available space in the row. Read more about the underlying grid layout in the [Carbon Grid documentation](https://carbondesignsystem.com/elements/2x-grid/overview/). - **Validation**: Given that one of the following properties is set, the form will only submit when the respective condition is fulfilled. Otherwise, a validation error will be displayed. - **Required**: Taglist must contain a value. - **Options source**: Taglists can be configured with an options source defining the individual choices your user can make, refer to [options source docs](../configuration/forms-config-options.md). - **Read only**: Makes the taglist read-only, meaning the user can't change but only read its state. Can be dynamically set using an [expression](../../feel/language-guide/feel-expressions-introduction.md). - **Disabled**: Disables the taglist, for use during development. ### Datatypes Taglists can be bound to data of the `any[]` type, although for most practical cases we recommend `string[]` instead. The Taglist component will correlate the bound data with the values of the different options defined for the component. The data representation of this taglist: ![Checklist Selection Image](../assets/taglist-example.png) Would look like this: ``` { "cc_empl": [ "john_doe", "anna_belle" ] } ``` --- ## Text view A Markdown-powered text component allowing to display simple information to the user. ## Configurable properties - **Text**: Either an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). After evaluation, the result is processed using a Markdown renderer that supports basic HTML and [GitHub-flavored Markdown](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet). To ensure safety and prevent cross-site scripting in Camunda Forms, potentially harmful HTML elements will not be rendered. - **Hide if**: [Expression](../../feel/language-guide/feel-expressions-introduction.md) to hide the text. - **Columns**: Space the field will use inside its row. **Auto** means it will automatically adjust to available space in the row. Read more about the underlying grid layout in the [Carbon Grid documentation](https://carbondesignsystem.com/elements/2x-grid/overview/). ## Example text configurations Note that these configurations work in combination with one another. You may use templating syntax to leverage Markdown and HTML. You may also mix Markdown and HTML in a single definition. **Markdown**: ``` # This is a heading This shows an image: ![alternative image text](https://someurl.com/image.png) ## This is a sub-heading Text can be shown for example using **bold**, or *italic* font. * This is an unordered list... * ...with two list items 1. This is an ordered list... 2. ...with two list items ``` **HTML**: ``` This is a heading This shows an image: This is a sub-heading Text can be shown for example using bold, or italic font. This is an unordered list... ...with two list items This is an ordered list... ...with two list items ``` :::note The text component supports a small subset of HTML configurations, and cannot be styled. Please use our [dedicated HTML component](./forms-element-library-html.md) for presentation use-cases relying on HTML heavily. ::: **Template syntax**: ``` {{#if usingTemplating}} Hello {{user.name}}, we are inside a conditional template block. Your hobbies are: {{#loop user.hobbies}} * {{this}} {{/loop}} {{/if}} ``` --- ## Text area A text area allowing the user to read and edit multiline textual data. ## Configurable properties - **Field label**: Label displayed on top of the text area. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Field description**: Description provided below the text area. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Key**: Binds the field to a form variable, refer to [data binding docs](../configuration/forms-config-data-binding.md). - **Default value**: Provides a default value for the text area in case no input data exists for the given key. - **Read only**: Makes the text area read-only, meaning the user can't change but only read its state. Can be dynamically set using an [expression](../../feel/language-guide/feel-expressions-introduction.md). - **Disabled**: Disables the text area; for use during development. - **Hide if**: [Expression](../../feel/language-guide/feel-expressions-introduction.md) to hide the text area. - **Columns**: Space the field will use inside its row. **Auto** means it will automatically adjust to available space in the row. Read more about the underlying grid layout in the [Carbon Grid documentation](https://carbondesignsystem.com/elements/2x-grid/overview/). - **Validation**: Given that one of the following properties is set, the form will only submit when the respective condition is fulfilled. Otherwise, a validation error will be displayed. - **Required**: Text area must contain a value. - **Minimum length**: Text area must have at least `n` characters. - **Maximum length**: Text area must not have more than `n` characters. ## Datatypes Text area can be bound to `boolean`, `string`, and `number` data, but will coerce the data into a string, which will lead the data to be written back to the process as a `string` when the form is submitted. --- ## Textfield A text field allowing the user to read and edit textual data. ### Configurable properties - **Field label**: Label displayed on top of the text field. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Field description**: Description provided below the text field. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Key**: Binds the field to a form variable, refer to [data binding docs](../configuration/forms-config-data-binding.md). - **Default value**: Provides a default value for the text field in case no input data exists for the given key. - **Read only**: Makes the text field read-only, meaning the user can't change but only read its state. Can be dynamically set using an [expression](../../feel/language-guide/feel-expressions-introduction.md). - **Disabled**: Disables the text field, for use during development. - **Hide if**: [Expression](../../feel/language-guide/feel-expressions-introduction.md) to hide the text field. - **Columns**: Space the field will use inside its row. **Auto** means it will automatically adjust to available space in the row. Read more about the underlying grid layout in the [Carbon Grid documentation](https://carbondesignsystem.com/elements/2x-grid/overview/). - **Validation**: Given that one of the following properties is set, the form will only submit when the respective condition is fulfilled. Otherwise, a validation error will be displayed. - **Required**: Text field must contain a value. - **Regular expression validation**: Use predefined validation patterns. Available options are: `Email`, `Phone`, and `Custom`. - **Minimum length**: Text field must have at least `n` characters. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md) or a number. - **Maximum length**: Text field must not have more than `n` characters. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md) or a number. - **Regular expression pattern**: Text field value must match the provided [RegEx](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Cheatsheet) pattern. - **Appearance**: Changes the visual appearance of the text field. - **Prefix**: Adds an appendage before the input. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). - **Suffix**: Adds an appendage after the input. Can either be an [expression](../../feel/language-guide/feel-expressions-introduction.md), plain text, or [templating syntax](../configuration/forms-config-templating-syntax.md). :::info The phone pattern adheres to the international [E.164](https://www.twilio.com/docs/glossary/what-e164) standard, omitting spaces; for example, `+491234567890`. For custom formats, use the `Custom` validation option. ::: ### Datatypes Text Fields can be bound to `boolean`, `string`, and `number` data, but will cohere the data into a string, which will lead the data to be written back to the process as a `string` when the form is submitted. --- ## Overview of Form Elements # Form Elements A form configuration starts off as a composition of **Form Elements** to define the structure. The specific behaviors are then defined as properties on the individual elements via the properties panel on the right side of the screen. Most form elements are intended to be bound to a **Form Variable** for the purpose of data entry, in which case we refer to them as **Form Fields**. Other elements may be used for layout purposes or to provide more specific functionality to the form which doesn't directly affect its state. The following form elements are currently available within Camunda Forms: Symbol Name Description Text view Display simple markdown-powered text HTML Display information in stylable HTML views Text field Read and edit textual data Text area Read and edit multiline textual data Number field Read and edit numeric data Datetime Read and edit date and time data Checkbox Read and edit boolean data Radio Small dataset single data selector Select Large dataset single data selector Checklist Small dataset multi data selector Taglist Large dataset multi data selector Group Group multiple form elements Dynamic list Turn sets of form elements into editable lists iFrame Embed external content Table Render tabular data Expression field Compute intermediary data Filepicker Select files Image view Display images Spacer Display empty space Separator Display a line between elements Button Trigger form actions --- ## Build forms with Modeler Beginner Time estimate: 15 minutes ## Overview The Camunda Forms feature allows you to easily design and configure forms. Once configured, they can be connected to a user task or start event to implement a task form in your application. After deploying a diagram with a linked form, Tasklist imports this form schema and uses it to render the form on every task assigned to it. ## Quickstart ### Create new form To start building a form, log in to your [Camunda 8](https://camunda.io) account or open [Desktop Modeler](/components/modeler/about-modeler.md) and take the following steps: 1. Navigate to Web Modeler or alternatively open the **File** menu in Desktop Modeler. 2. Open any project from your Web Modeler home view. 3. Click **Create new** and choose **Form**. ### Build your form Now you can start to build your form by dragging elements from the palette to the canvas, or by using the AI Form Generator at the bottom of the palette. For the purpose of this guide, we'll build a form from scratch. Right after creating your form, you can name it by replacing the **New Form** text with the name of your choice. In this example, we'll build a form to help with a task in obtaining an email message. ![form email example](./img/form-email-example.png) Add your desired elements from the palette on the left side by dragging and dropping them onto the canvas. ![form palette](./img/form-palette.png) Within Forms, we have the option to add text fields, numerical values, checkboxes, radio elements, selection menus, text components, and buttons. In the properties panel on the right side of the page, view and edit attributes that apply to the selected form element. For example, apply a minimum or maximum length to a text field, or require a minimum or maximum value within a number element. In this case, we have labeled the field, described the field, and required an input for our email message. ![email properties](./img/form-properties-email.png) Refer to the [camunda forms reference](/components/modeler/forms/camunda-forms-reference.md) to explore all form elements and configuration options in detail. ### Save your form To save your form in Camunda 8, you don't have to do anything. Web Modeler will autosave every change you make. ### Link your form to a BPMN diagram {#connect-your-form-to-a-bpmn-diagram} Next, let's implement a task form into a diagram. In tandem, we can link your form to a user task or start event. Navigate to Modeler and open any project from your Web Modeler home view. Take the following steps: 1. Select the diagram where you'd like to apply your form. 2. Select the user task requiring the help of a form. 3. On the right side of the selected user task, select the overlay with the link icon to open the navigation menu. 4. Navigate to the form you want to link and click the **Link** button. 5. When a user task has a linked form, the overlay will always stay visible on the right side of the task. :::note When using Camunda Forms, any submit button present in the form schema is hidden so we can control when a user can complete a task. ::: :::tip Linked Camunda Forms must be explicitly deployed. With Camunda 8.9, linked Camunda Forms are no longer auto-deployed. This change provides greater control over what is deployed and when, enabling more precise management of changes and updates across environments. ::: ## Deploy a linked form To deploy your latest form changes, click the **Deploy** button. ## Deploy your diagram and start an instance To execute your process diagram, click the **Deploy** button. To avoid incidents: - When deploying a process application, if the links between resources are configured with the 'deployment' binding, the BPMN diagrams and their forms must be deployed together. - When deploying a BPMN file separately, and linking resources using the 'latest' binding, ensure the forms are deployed beforehand. Then start a new process instance. Click the **Run** button. You can now monitor your instances in [Operate](/components/operate/operate-introduction.md). :::info When deploying a BPMN diagram, Web Modeler will not automatically deploy linked forms. This gives you full control over when and which version of a form is deployed. As linked forms are resolved to their latest version (unless you change the [binding type](/components/hub/workspace/modeler/modeling/advanced-modeling/form-linking.md#camunda-form-linked)), make sure that the intended form version is available in the target cluster and that the binding resolves to that version. When deploying to a Camunda 8 cluster running a version earlier than 8.4, forms linked to user tasks or none start events will be automatically embedded into the user task to guarantee backwards compatibility. Read more about the different ways to reference Camunda Forms in the [user task forms reference](/components/modeler/bpmn/user-tasks/user-tasks.md#user-task-forms). ::: To [complete a user task](/guides/getting-started-orchestrate-human-tasks.md), navigate to [Tasklist](/components/tasklist/introduction-to-tasklist.md). ## Additional resources - [Desktop and Web Modeler](/components/modeler/about-modeler.md) - [User task reference](/components/modeler/bpmn/user-tasks/user-tasks.md) ## Next steps When building a form for a process, you can also use the [Filepicker form component](/components/modeler/forms/form-element-library/forms-element-library-filepicker.md) to allow users to upload files. Learn more in [building a form for document upload](/components/document-handling/upload-document-to-bpmn-process.md#build-a-form-for-document-upload). You can also use the [document preview component](/components/modeler/forms/form-element-library/forms-element-library-document-preview.md) to display and allow document download with your form. Learn more in [building a form for document preview and download](/components/document-handling/display-and-download-document.md#build-a-form-for-document-preview-and-downloading). Additionally, review the Camunda Academy course on [using the AI-assisted form builder](https://academy.camunda.com/c8-h2-ai-form-builder). --- ## Modeling guidance Modeler checks your BPMN diagram for problems affecting deployment and execution, and provides guidance on how to fix them. This page lists all the rules used to check your diagram and explains how to fix the problems. ## Rules --- ## Called element Call activities must specify the [process ID of the called process](../../../../bpmn/call-activities/#defining-the-called-process). To fix this problem, open the **Called element** group in the properties panel on the right side of the screen and specify the process ID of the called process. ## No process ID specified ![No process ID specified](./img/called-element/wrong.png) ## Process ID specified ![Process ID specified](./img/called-element/right.png) ## References - [Call Activities](../../../../bpmn/call-activities/) - [Rule source](https://github.com/camunda/bpmnlint-plugin-camunda-compat/blob/main/rules/camunda-cloud/called-element.js) --- ## Element type The element you modeled is not [supported for execution](../../../../bpmn/bpmn-coverage/) by the selected Camunda version. It may be supported in a later version. ## Element type not supported by selected version ![Element type not supported by selected version](./img/element-type/wrong.png) ## Element type supported by selected version ![Element type supported by selected version](./img/element-type/right.png) ## References - [BPMN coverage](../../../../bpmn/bpmn-coverage/) - [Rule source](https://github.com/camunda/bpmnlint-plugin-camunda-compat/tree/main/rules/camunda-cloud/element-type) --- ## Error reference An [error event](../../../../bpmn/error-events/) must reference an error defined in the process. The referenced error must have a defined error code. To fix this problem, open the **Error** group in the properties panel on the right side of the screen, select or create an error, and specify its error code. Camunda 8.2 and later support catch-all events and do not require an error reference. ## No error selected ![No error selected](./img/error-reference/wrong-no-error-reference.png) ## No error code specified ![No error code specified](./img/error-reference/wrong-no-error-code.png) ## Error selected and error code specified ![Error selected and error code specified](./img/error-reference/right.png) ## References - [Error events](../../../../bpmn/error-events/) - [Rule source](https://github.com/camunda/bpmnlint-plugin-camunda-compat/blob/main/rules/camunda-cloud/error-reference.js) --- ## Escalation reference An [escalation event](../../../../bpmn/escalation-events/) must reference an escalation defined in the process. The referenced escalation must have a defined escalation code. To fix this problem, open the **Escalation** group in the properties panel on the right side of the screen, select or create an escalation, and specify its escalation code. Camunda 8.2 and later support catch-all events and do not require an escalation reference. ## No escalation selected ![No escalation selected](./img/escalation-reference/wrong-no-escalation-reference.png) ## No escalation code specified ![No escalation code specified](./img/escalation-reference/wrong-no-escalation-code.png) ## Escalation selected and escalation code specified ![Escalation selected and escalation code specified](./img/escalation-reference/right.png) ## Reference - [Escalation events](../../../../bpmn/escalation-events/) - [Rule source](https://github.com/camunda/bpmnlint-plugin-camunda-compat/blob/main/rules/camunda-cloud/escalation-reference.js) --- ## FEEL When using the [FEEL expression language](../../../../feel/what-is-feel/), you must specify a valid expression. ## Invalid FEEL expression ![Invalid FEEL expression](./img/feel/wrong.png) ## Valid FEEL expression ![Valid FEEL expression](./img/feel/right.png) ## References - [FEEL expressions](../../../../feel/what-is-feel/) - [Rule source](https://github.com/camunda/bpmnlint-plugin-camunda-compat/blob/main/rules/camunda-cloud/feel.js) --- ## Message reference A message event or receive task must reference a message defined in the process. To fix this problem, open the **Message** group in the properties panel on the right side of the screen and select or create a message. The referenced message must have a defined correlation key (refer to [message subscriptions](../../../../../concepts/messages/#message-subscriptions)). ## No message selected ![No message selected](./img/message-reference/wrong-no-message-reference.png) ## Message selected ![Message selected](./img/message-reference/right.png) ## References - [Messages](../../../../../concepts/messages/) - [Rule source](https://github.com/camunda/bpmnlint-plugin-camunda-compat/blob/main/rules/camunda-cloud/message-reference.js) --- ## Infinite Loop This error occurs when a BPMN model contains an automated loop (without human interaction or external event triggers). It may cause an endless recursion. This may result in: - Endless execution - [Excessive resource usage](../../../../../components/hub/organization/manage-clusters/cluster-capacity.md) To fix this problem, add a timer, user task, or external event (e.g., message catch) to break the straight-through loop and ensure controlled execution. ## Straight-through processing loop ![Straight-through processing loop](./img/no-loop/wrong.png) ## Controlled Loop ![Controlled Loop](./img/no-loop/right.png) ## References - [Rule source](https://github.com/camunda/bpmnlint-plugin-camunda-compat/blob/main/rules/camunda-cloud/no-loop.js) --- ## Task testing(Modeler) Test a BPMN activity directly from the modeler to verify your implementation without running the entire process. Task testing provides immediate feedback on your implementation, variable mappings, and configuration within the modeler. The selected element runs on the connected Camunda 8 engine, as it does during normal process execution. ![Task testing in Modeler with input variables and result view](./img/task-testing.png) ## How it works When you test an element, the following occurs: 1. The modeler deploys the process to the connected Camunda 8.8+ orchestration cluster. 2. You define the process context by providing input variables. 3. The engine executes the selected element: - Input mappings are applied as configured. - The actual task logic (connector, script, or external task) is executed by the engine. - Output mappings are applied as configured. 4. The modeler displays the **Result** tab containing the execution log, process variables, and local variables, as well as any incidents or errors. :::warning Testing executes elements with live data on the connected cluster. Any configured actions (emails, API calls, database updates, payments, etc.) will run as defined. Do not use a production environment. ::: ## Prerequisites - A connection to an active Camunda 8.8 or later orchestration cluster. - Appropriate credentials and permissions to deploy and run processes. After running a test, you can view the resulting process instance in [Operate](../../components/operate/operate-introduction.md) for additional insights into execution details or incidents. Test instances are deployed as standard process instances and can be viewed, managed, or deleted as usual. For configuration steps, see: - [Test in Web Modeler](../hub/workspace/modeler/validation/task-testing.md) - [Test in Desktop Modeler](./desktop-modeler/task-testing.md) ## Supported elements You can test the following BPMN elements: - **Task elements** — service tasks, script tasks, user tasks, business rule tasks, and send tasks. - **Sub-processes** — embedded sub-processes can be tested directly, executing all contained elements. - **Tasks inside sub-processes** — individual tasks within a sub-process can also be tested. The following elements are not supported: - Call activities - Events (start, end, boundary) - Gateways ## Variable persistence When a test completes: - Input variables are stored locally for reuse in subsequent test runs. - The last test result of an element is persisted, including output variables. - You can rerun tests with the same input set or modify them to test new values. ## Best practices - Use a staging cluster or sandbox environment for testing live integrations. - Mock external API calls and disable production credentials when possible. - Review results in Operate to confirm behavior and variable mappings. --- ## Using Web and Desktop Modeler together [Web Modeler](/components/hub/workspace/modeler/launch-modeler.md) and [Desktop Modeler](/components/modeler/desktop-modeler/index.md) are both tools for designing [BPMN](/components/modeler/bpmn/bpmn.md) diagrams, but they serve different purposes and shine in different scenarios. Web Modeler is great for collaborative, cloud-based process modeling. It allows teams to work together in real-time, manage versions, and store models centrally. It's especially useful when working in distributed teams or when you need tight integration with a remotely hosted Camunda 8 cluster — whether it's Camunda SaaS or your own self-managed environment. Desktop Modeler, on the other hand, is ideal for local development, technical modeling, and full offline control. Among other features, it supports advanced customization, scripting, and deployment to local Camunda 8 runtimes (like Camunda 8 Run), making it a go-to tool for developers working on executable processes. Using both tools together allows you to combine the best of both worlds: - Start collaboratively in Web Modeler, capturing business requirements and designing high-level processes with stakeholders. - Then switch to Desktop Modeler for more technical refinement, such as adding execution details, scripts, or testing locally. This workflow bridges the gap between business users and developers, ensuring smooth handoffs and better alignment across the team. When using [Git sync](/components/hub/workspace/manage-projects/git-sync.md) to keep your project in sync between a Camunda Hub workspace and your local environment, there are a few considerations to ensure both modelers interpret the project (and its `.project` file) consistently. ## Process applications | Desktop Modeler | Web Modeler | | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------- | | A process application is represented as a folder containing a `.process-application` file. | A process application is represented as a [type folder](/components/hub/workspace/manage-projects/manage-projects.md). | :::tip Camunda recommends always structuring your projects as process applications. This ensures consistent behavior across both Web Modeler and Desktop Modeler with minimal adjustments. ::: ## Element templates Element templates are discovered differently in each tool. The following sections outline the key differences. ### Templates per file | Desktop Modeler | Web Modeler | | ----------------------------------------------------- | ------------------------------------ | | Can load multiple templates defined in a single file. | Only supports one template per file. | To maintain compatibility, avoid defining multiple templates in a single file. ### Shared element templates | Desktop Modeler | Web Modeler | | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Templates can be installed as [global templates](/components/modeler/desktop-modeler/element-templates/configuring-templates.md#global-templates). | Templates are [published to the organization](/components/connectors/manage-connector-templates.md#manage-published-connector-templates) for reuse across projects. | Camunda recommends storing shared templates in a separate repository: - **Desktop Modeler**: Copy templates manually into your global directory. - **Web Modeler**: Use a [CI/CD pipeline](/components/best-practices/cicd-guidelines/element-templates-at-scale.md) to sync templates with your repository via the [Web Modeler API](/apis-tools/web-modeler-api/index.md). ### Project element templates | Desktop Modeler | Web Modeler | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | [Local element templates](/components/modeler/desktop-modeler/element-templates/configuring-templates.md#local-templates) are loaded from the `.camunda/element-templates` folder if present. | Loads templates from a single folder. If working with a project (not a process application), templates must first be published. | :::note - If starting in **Desktop Modeler**, use a single folder for your process application. This makes project templates available in both modelers without extra work. - If starting in **Web Modeler**, after cloning the repository manually create an empty JSON object `{}` in a file named `.process-application` in the root directory of your project/repository so Desktop Modeler can correctly recognize the project. ::: ### Handling multiple template versions | Desktop Modeler | Web Modeler | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Detects versions based on separate files. To support multiple versions, maintain different files with distinct names (e.g., `element-template-v1.json`, `element-template-v2.json`). Otherwise, templates may appear as [missing](/components/modeler/desktop-modeler/element-templates/using-templates.md#missing-templates). | Supports evolving a single template file. Simply update the file and [publish](/components/connectors/manage-connector-templates.md#manage-published-connector-templates) new [versions](/components/modeler/element-templates/defining-templates.md#template-versioning). | :::warning - Desktop Modeler can have multiple templates defined in a single file, which is good practice when defining multiple versions of the same template. - Web Modeler only supports defining one template per file, as storing template versions in a file is not needed (Web Modeler automatically tracks version history). ::: ## Frequently asked questions ### Do I really need a `.process-application` file if I’m only using Web Modeler? No. A `.process-application` file is only required if you plan to open the project in Desktop Modeler. Web Modeler does not require it, but adding the file makes the project compatible across both tools. ### Can I use the same element template repository for both modelers? Yes. Camunda recommends maintaining a dedicated version control repository for element templates. Desktop Modeler users can copy templates into their global directory, while Web Modeler users can stay in sync through a CI/CD pipeline and the [Web Modeler API](/apis-tools/web-modeler-api/index.md). ### How should I manage multiple versions of the same element template? In Desktop Modeler, each version of the element template must be present. Versions can be stored in a single file as a list of element templates or in separate files (for example, `element-template-v1.json` and `element-template-v2.json`). Otherwise, the template will appear as [missing](/components/modeler/desktop-modeler/element-templates/using-templates.md#missing-templates). Web Modeler, however, supports [versioning](/components/modeler/element-templates/defining-templates.md#template-versioning) in a single file and allows you to [publish](/components/connectors/manage-connector-templates.md#manage-published-connector-templates) new versions directly. When referencing a dependency such as a form we recommend using a `versionTag` as your [binding type](/components/best-practices/modeling/choosing-the-resource-binding-type.md#supported-binding-types), as this option ensures that the right version of the target resource is always used. --- ## Introduction to Operate Operate is a tool for monitoring and troubleshooting process instances running in [Zeebe](/components/zeebe/zeebe-overview.md). In addition to providing visibility into active and completed process instances, Operate can also: - Carry out key operations such as resolving [incidents](./userguide/resolve-incidents-update-variables.md) and updating process variables. - Retry or cancel [many process instances at once](/components/operate/userguide/selections-operations.md). - [Delete finished instances](/components/operate/userguide/delete-finished-instances.md) and [resources](/components/operate/userguide/delete-resources.md). - [Modify](/components/operate/userguide/process-instance-modification.md) an active process instance to allow execution to continue. - [Batch move](/components/operate/userguide/process-instance-batch-modification.md) active process instances to allow execution to continue. ![operate-introduction](../../images/operate/operate-introduction.png) Learn how to use Operate to monitor process instances and more features in the [Operate user guide](/components/operate/userguide/basic-operate-navigation.md). ## Availability Because Operate can be a helpful tool when getting started with Zeebe and building an initial proof of concept, we make it available under the [Operate trial license](https://camunda.com/legal/terms/cloud-terms-and-conditions/general-terms-and-conditions-for-the-operate-trial-version/). There are no restrictions under this license when it comes to the length of the evaluation period or the available feature set _as long as you use Operate in non-production environments only._ Operate is available for production use (with support) in the Camunda 8 offering. [Try out Operate in Camunda 8](https://signup.camunda.com/accounts?utm_source=docs.camunda.io&utm_medium=referral&utm_content=operate). --- ## Access control(3) If authorization control is enabled for your Orchestration Cluster, users require the following authorizations to work with Operate. :::note You can assign these [in the Admin UI](/components/admin/authorization.md#create-an-authorization). See [the introduction to authorizations](/components/concepts/access-control/authorizations.md#available-resources) for a list of all available authorizations. ::: ## Mandatory authorizations The following mandatory authorizations are required to work with Operate: | Authorization type | Resource type | Resource ID | Permission | | :--------------------------------------------- | :------------------- | :--------------------------------------------------------------------------------- | :------------------------------------------------- | | Component access for Operate | `Component` | `operate` or `*` (for access to all web components). | `ACCESS` | | View process definitions and process instances | `Process Definition` | ID of the respective BPMN process definition or `*` (for all process definitions). | `READ_PROCESS_DEFINITION`, `READ_PROCESS_INSTANCE` | ## Optional authorizations The following optional authorizations can also be defined: | Authorization type | Resource type | Resource ID | Permission | | :-------------------------------------------------------------------------------------------------------- | :--------------------------------- | :-------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------- | | View audit log entries. | `AUDIT_LOG` | An operation category (`DEPLOYED_RESOURCES` or `USER_TASKS`) or `*` for all categories. | `READ` | | View decision definitions and decision instances | `Decision Definition` | ID of the respective DMN decision definition or `*` (for all process definitions). | `READ_DECISION_DEFINITION`, `READ_DECISION_INSTANCE` | | View decision requirements definitions | `Decision Requirements Definition` | ID of the respective DRD or `*` (for all process definitions). | `READ` | | View running and completed batch operations | `Batch` | `*` | `READ` | | Update process instances via batch (cancellation, retries). | `Batch` | `*` | `CREATE` and corresponding permissions for the individual batch operation (for example, `CREATE_BATCH_OPERATION_CANCEL_PROCESS_INSTANCE`). | | Update process instance directly (migrate, add/update variables, resolve incidents) | `Process Definition` | ID of the respective BPMN process definition or `*` (for all process definitions). | `UPDATE_PROCESS_INSTANCE` | | Modify process instances | `Process Definition` | ID of the respective BPMN process definition or `*` (for all process definitions). | `MODIFY_PROCESS_INSTANCE` | | Cancel process instance directly | `Process Definition` | ID of the respective BPMN process definition or `*` (for all process definitions). | `CANCEL_PROCESS_INSTANCE` | | Delete process instances | `Process Definition` | ID of the respective BPMN process definition or `*` (for all process definitions). | `DELETE_PROCESS_INSTANCE` | | View `DEPLOYED_RESOURCES` and `USER_TASKS` operation logs for instances of a specific process definition. | `Process Definition` | A process definition ID or `*` for all process definitions. | `READ_PROCESS_INSTANCE` | | View `USER_TASKS` operation logs for instances of a specific process definition. | `Process Definition` | A process definition ID or `*` for all process definitions. | `READ_USER_TASK` | | Delete process definitions | `Resource` | ID of the respective BPMN process definition or `*` (for all resources). | `DELETE_PROCESS` | | Delete decisions | `Resource` | ID of the respective decision ID or `*` (for all resources). | `DELETE_DECISION_INSTANCE` | --- ## Batch operation details A high-level overview of the batch operation details page in Camunda 8 Operate. ## About the batch operation details page On the batch operation's details page, you'll find: - A summary of the batch operation. - A table with details about the items in the batch operation. - An option to take action on the batch operation. This is only available for some batch operations, depending on the state. ## Summary header The summary header includes the following details about the batch operation: | Detail | Description | | ---------------- | ------------------------------------------------------------------------------------ | | Batch state | The current state of the batch operation. | | Summary of items | The number of successful, failed, and pending items included in the batch operation. | | Start date | The operation's start date and time. | | End date | The operation's end date and time, if applicable. | | Actor | The user or client responsible for triggering the operation. | If the batch operation finishes with the "failed" state, the header also includes error messages. ## Items table In the table, you can review these details about each item in the batch operation: | Column | Description | | -------------------- | --------------------------------------------------------------------------------------- | | Process instance key | The key of the process instance. Clicking this opens the process instance details page. | | Operation state | The state of the batch operation item. | | Date | The date and time when the operation was performed on this item. | ## Next steps - [Learn how to monitor batch operations](../userguide/monitor-batch-operations.md). - [Learn how to manage batch operations](../userguide/manage-batch-operation.md). --- ## Batch Operations(Overview) A high-level overview of the **Batch Operations** page in Camunda 8 Operate. ## About the Batch Operations page Use the **Batch Operations** page to monitor all batch operations performed across any process instance. ## Batch operations table In the table, you can review these details about each batch operation: | Column | Description | | ----------- | ------------------------------------------------------------------------------------ | | Operation | The type of operation performed. | | Batch state | The current state of the batch operation. | | Items | The number of successful, failed, and pending items included in the batch operation. | | Actor | The user or client responsible for initiating the operation. | | Start date | The operation's start date and time. | Learn how to monitor batch operations in the [user guide](../userguide/monitor-batch-operations.md). --- ## Audit operations(Userguide) Audit [operations](../../audit-log/overview.md) in Camunda 8 Operate. ## Prerequisites To follow the steps in this guide, you must be [authorized to view operations in the audit log](../../audit-log/overview/access-control.md). ## Audit all operations In Operate, you can audit all [`DEPLOYED_RESOURCES` and `USER_TASKS` operations](../../audit-log/overview/recorded-operations.md) in the general operations log: 1. In the top navigation, click **Operations > Operations log**. 2. To sort the log, click a column header. 3. To see the details of a particular operation, click the info icon at the end of the row. 4. To filter the audit log, expand the **Filter** panel on the left side of the page. Audit log filters are under the **Operation** header. ## Audit process instance operations You can also review operations at the process instance level: 1. On the **Processes** page, in the **Process Instances** table, click the **Process Instance Key** of the instance you want to audit. 2. Under the process diagram, click **Operations Log**. 3. To sort the log, click a column header. 4. To see the details of a particular operation, click the info icon at the end of the row. 5. To filter the audit log, use the **Operation type** and **Entity type** dropdowns. ## Next steps - [Learn about the operation data structure in the operations log.](../../audit-log/overview/operation-structure.md) - [Use the Audit Log REST API to programmatically access the audit log](../../../apis-tools/orchestration-cluster-api-rest/specifications/search-audit-logs.api.mdx). --- ## Get familiar with Operate Learn how to navigate Camunda 8 Operate. ## Before you begin This section and the next section, [Resolve incidents and update variables](./resolve-incidents-update-variables.md), assume you’ve deployed a process to Zeebe and created at least one process instance, using the [`order-process.bpmn`](/bpmn/operate/order-process.bpmn) process model. If you’re not sure how to deploy processes or create instances, visit our [guides section](/guides/introduction-to-camunda-8.md) to get started with Camunda. ## View a deployed process To view a deployed process, take the following steps: 1. On the **Dashboard** page, in the **Process Instances by Name** panel, note the list of your deployed processes and running instances. The dashboard only displays processes with active instances, so processes without running or incident instances are not shown. 2. When you click on the name of a deployed process in the **Process Instances by Name** panel, you’ll navigate to a view of that process model and all running instances. 3. From this **Processes** page, you can cancel a single running process instance by clicking the cancel icon under the **Operations** column of the **Process Instances** table. ## Inspect a process instance Running process instances appear in the **Process Instances** table below the process model. To inspect a specific instance, click the **Process Instance Key**. Here, observe details about the process instance, including the instance history and the variables attached to the instance. To visualize process instance performance, use [Optimize](/components/optimize/what-is-optimize.md). --- ## Delete finished instances Learn how to delete finished process instances and decision instances in Camunda 8 Operate. ## Overview A finished process instance, meaning a canceled or a completed process instance, can be deleted from the **Processes** page or instance detail page. Evaluated and failed decision instances can be deleted from the **Decisions** page. :::warning You may not access process instances or decision instances after you've deleted them. ::: ## Delete process instances ### Batch delete from the Processes page To delete multiple process instances at once from the **Processes** page, take the following steps: 1. On the **Processes** page, in the **Filter** panel, apply the **Finished Instances** filter by checking the appropriate box. 2. Optionally, narrow down the list using additional filters such as **Process Name**, **Process Version**, or other available filters. 3. Select the process instances you want to delete by checking the checkbox next to each one, or use the select-all checkbox to select all instances. 4. Click the **Delete** button in the toolbar. 5. Confirm the delete operation by clicking **Delete**. ### Delete a single process instance from the Processes page To delete a single process instance from the **Processes** page, take the following steps: 1. On the **Processes** page, in the **Filter** panel, apply the **Finished Instances** filter by checking the appropriate box. 2. Click the **Delete** button on any process instance you want to delete. 3. Confirm the delete operation by clicking **Delete**. ### Delete a process instance from the instance detail page 1. On the **Processes** page, in the **Filter** panel, apply the **Finished Instances** filter by checking the appropriate box. 2. Navigate to the instance detail page by clicking the **Process Instance Key** of the process instance you want to delete. 3. In the page header, click the **Delete** button. 4. Confirm the delete operation by clicking **Delete**. ## Delete decision instances Evaluated and failed decision instances can be batch deleted from the **Decisions** page. ### Batch delete from the Decisions page 1. On the **Decisions** page, in the **Filter** panel, select the decision instances you want to delete using the **Evaluated** or **Failed** filters, or both. 2. Optionally, narrow down the list using additional filters such as **Decision Name**, **Decision Version**, or other available filters. 3. Select the decision instances you want to delete by checking the checkbox next to each one, or use the select-all checkbox to select all visible instances. 4. Click the **Delete** button in the toolbar. 5. Confirm the delete operation by clicking **Delete**. --- ## Delete resources Learn how to delete a specific process or decision definition version in Camunda 8 Operate. ## Delete process definition :::warning Deleting a process definition permanently removes it and has the following effects: - All the deleted process definition's finished process instances will be deleted from the application. - All decision and process instances referenced by the deleted process instances will be deleted. - If a process definition contains user tasks, they will be deleted from [Tasklist](/components/tasklist/introduction-to-tasklist.md). ::: To delete a process definition from the **Processes** page, take the following steps: 1. In the **Filter** panel, select a specific process version by filtering by process name and version. Make sure the selected process definition version has no running instances, otherwise it is not possible to delete a process definition. You can [cancel or resolve running process instances](/components/operate/userguide/basic-operate-navigation.md) from the process instances list or from the process instance detail page. 2. Click the **Delete** button at the top right. 3. Confirm the delete operation by checking the checkbox and clicking **Delete**. ## Delete decision definition :::warning Deleting a decision definition will delete the DRD and will impact the following: - Deleting a decision definition removes the DRD that contains it. All other decision tables and literal expressions that are part of the DRD will also be deleted. - Deleting the only existing version of a decision definition could result in process incidents. ::: 1. On the **Decisions** page, select a specific decision version by filtering by decision name and version. 2. Click the **Delete** button at the top right. 3. Confirm the delete operation by checking the checkbox and clicking **Delete**. --- ## Manage batch operations Learn how to manage [batch operations](../../concepts/batch-operations.md) in Camunda 8 Operate. ## Overview You can suspend, resume, or cancel batch operations when they're in one of the following [states](../../concepts/batch-operations.md#states): | State | Available actions | | --------- | ----------------- | | Active | Suspend, cancel | | Suspended | Resume, cancel | | Created | Cancel | ## Before you begin Before you begin, ensure you're [authorized to update batch operations](../overview/access-control.md#optional-authorizations). ## Manage a batch operation To take an action on a batch operation: 1. On the **Processes** page, above the process diagram, click **View batch operations**. 2. From the **Batch Operations** page, click the operation you want to manage. 3. On the batch operation details page, on the right side of the page header, click an action, if available. --- ## Monitor batch operations Learn how to monitor the [batch operations](../../concepts/batch-operations.md) in Camunda 8 Operate. ## Before you begin To follow the steps in this guide, you must be [authorized to view running and completed batch operations](../overview/access-control.md#optional-authorizations). ## Monitor batch operations 1. In the top navigation, click **Operations > Batch operations**. This opens the [**Batch Operations** page](../overview/batch-operations-overview.md). 2. To see the details of a particular batch operation, click the operation in the table. This opens the [batch operation details page](../overview/batch-operation-details-overview.md). ## View failure reasons On the batch operation details page, expand a failed item row to view the failure reason. ## Next steps - [Learn how to audit process instance operations](./audit-operations.md). - [Learn how to manage a batch operation](./manage-batch-operation.md). --- ## Move a batch of process instances Learn how to move a [batch](../../concepts/batch-operations.md) of process instances from one node to another in Camunda 8 Operate. ## Overview If there was an issue in process execution that caused you to enter the wrong process branch or data was corrupted, you may need to select multiple process instances and move them to the correct element in the process in a single operation. :::note If you only need to modify a single active process instance to allow execution to continue, use [process instance modification](./process-instance-modification.md) instead, which also provides some additional modification options beyond move modification, like activating an element, terminating an element instance, etc. ::: ## Unsupported modifications Some elements do not support specific modifications: - **Move** modifications are not possible for the following types of elements: - Start events - Boundary events - Events attached to event-based gateways - **Move** modification is not possible for a subprocess itself. ## Enter batch modification mode 1. On the **Processes** page, in the **Filter** panel, select the **Name** and **Version** of the process you want to modify. 2. Select the element containing the process instances you intend to move. 3. In the **Process Instances** table, start selecting which instances you want to move. As you select instances, the process instance toolbar will appear and you will now see the **Move** action become available. 4. Once you are ready to continue, click **Move**. An information modal will appear indicating that you are switching to process instance batch move mode. 5. Click **Continue** and the UI will change to indicate that you entered batch modification mode. This is represented by a blue border, including a blue banner at the top and two buttons for applying or exiting modifications at the bottom. 6. You can now continue to select or deselect instances as needed. You can also discard all current selected instances by either clicking the **Discard** option in the process instance toolbar, or by clearing the current selection. Exit the modification mode at any time by clicking **Exit** in the footer. ## Move selected process instances from one element to another 1. Select the element you want to move the selected process instances to. 2. When you have made all your intended selections and you are ready to continue, click **Review Modification** in the footer. A confirmation modal will appear indicating that you are about to apply the selected modifications. 3. Click the **Apply** button from the summary modal to start the batch modification. When moving elements inside multi-instance subprocesses, the move operation terminates only that specific element instance and activates the target element in the same instance of the multi-instance subprocess. ## Next steps - [Monitor the batch operation](./monitor-batch-operations.md). - [Learn how batch operations work](../../zeebe/technical-concepts/batch-operations.md). --- ## Migrate process instances Learn how to migrate process instances from one process definition version to another in Camunda 8 Operate. ## Before you begin Before you try to migrate process instances, learn about the [limitations](/components/concepts/process-instance-migration.md#limitations) of process instance migration. ## Select process instances 1. From the **Processes** page, select a specific process and version from the **Filter** panel. This will be the source process version where instances are migrated from. 2. Select all instances from the **Process Instances** table that should be migrated to another process version. 3. Click **Migrate** to enter the migration view. 4. In the modal, click **Continue**. The migration view features three areas: the source process diagram (top left), the target process diagram (top right) and the element mapping (bottom panel). ## Select a target process version Above the target process diagram, enter a target process into the **Target** box, and select a version from the dropdown. This will be the process version where all selected process instances are migrated to. ## Map source and target nodes In the bottom panel, you can see a list of all service tasks from the source process. 1. Use the dropdowns to select a target element for each source element that should be part of the migration. It is currently only possible to map elements with migration [supported by Zeebe](/components/concepts/process-instance-migration.md#supported-bpmn-elements). 2. (Optional) Click on an element in the diagram or on a source element row in the bottom panel to see how elements are mapped. 3. In the footer, click **Next** for a preview of the migration plan. Now, you can see a preview of how elements are mapped and how many process instances are expected to be migrated. ## Start the migration 1. In the footer, click **Confirm** to review your migration. 2. When ready, enter the word **MIGRATE** into the text box. 3. Click **Confirm** again to start the migration operation. ## Next steps - [Monitor the batch operation](./monitor-batch-operations.md). - [Learn about the limitations of process instance migration](/components/concepts/process-instance-migration.md#limitations). --- ## Modify a process instance Learn how to modify a process instance in Camunda 8 Operate. ## Overview You may need to modify an active process instance to allow execution to continue. The execution may be stuck, and you may want to continue the execution on a different activity (i.e. skip or repeat activities). :::note If an issue in process execution caused you to enter the wrong process branch instead of moving each process instance individually, use [process instance batch modification](./process-instance-batch-modification.md) instead to select multiple process instances and move them to the correct flow node in the process in a single operation. ::: ## Unsupported modifications Some elements do not support specific modifications: - **Add token**/**Move tokens to** modifications are not possible for the following type of elements: - Start events - Boundary events - Events attached to event-based gateways - **Move tokens from** modification is not possible for a subprocess itself. - **Add token** modifications are not currently supported for elements with multiple running scopes. :::note Multi-instance subprocesses While **Add token** modifications are not supported for elements inside multi-instance subprocesses, **Move** modifications are supported. When you move an element instance within a multi-instance subprocess, the operation terminates only that specific element instance and activates the target element in the same instance of the multi-instance subprocess. ::: ## Enter process instance modification mode To enter modification mode: 1. On the **Processes** page, find the process instance you want to modify in the **Process Instances** table. 2. Click the process instance key. This brings you to the instance detail page. 3. In the header, select **Modify**. 4. In the modal, click **Continue**. The UI will change when you enter modification mode, including a blue banner at the top and two buttons for applying or discarding modifications at the bottom. ## Cancel all running tokens on an element To cancel all running tokens on an element, take the following steps: 1. Select the flow node you want to cancel all the running tokens on. 2. Click **Cancel instance**. View the pending modification reflected in the **Instance History** panel. ## Add a new token to an element To add a new token to an element, take the following steps: 1. Select the flow node you want to add the new token to. 2. Click **Add**. View the pending modification reflected in the **Instance History** panel. ## Move all running tokens from one flow node to another The move operation is equivalent to the combination of **Cancel** and **Add** modifications. The modifications described previously can also be achieved with one single move modification. 1. Select the flow node you want to move the running tokens from. 2. Click **Move instance**. 3. Select the flow node you want to move the running tokens to. View the pending modification reflected in the **Instance History** panel. ## Add variable to new scopes During the modification mode, if there are new scopes generated it will be possible to add variables to these new scopes by following these steps: 1. Select the new scope from the **Instance History** panel you want to add a variable to. 2. Click **Add Variable** from the **Variables** panel. 3. Fill out the **Name** and **Value** fields for the variable you want to add. 4. Once you blur out of the field (click anywhere on the screen other than the last edited variable field), assuming the fields have the valid values, the new variable will be added to the [pending modifications](#view-summary-of-pending-modifications). ## Edit variable on existing scopes During modification mode it is not possible to edit existing variables in existing local scopes. Only variables in global scope can be edited by following these steps: 1. Plan at least one **Add** or **Move** modification. 2. Select the global scope from the **Instance History** panel. 3. Edit the variable value from the **Variables** panel. 4. Once you blur out of the field (click anywhere in the screen other than the last edited variable field), assuming the new value is valid, the **Edit Variable** modification will be added to the [pending modifications](#view-summary-of-pending-modifications). ## View summary of pending modifications To display the pending modifications, click **Review Modifications** in the footer. A modal will be displayed where all modifications can be seen. Within this modal, you can: - Delete any modification by clicking **Delete**. - View an added/edited variable in a JSON/Diff Viewer. - Cancel/close the modal, and continue with modification mode. - Apply the modifications and exit modification mode. ## Undo modification Clicking **Undo** from the modification footer will undo the latest modification. ## Apply modifications If you click the **Apply** button from the summary modal as described [here](#view-summary-of-pending-modifications), and modification operation is created successfully, you will observe a success notification and changes will be reflected in a short time. ## Next steps - [Learn how to audit process instance operations](./audit-operations.md). - [Learn how to move a batch of process instances](./process-instance-batch-modification.md) --- ## Resolve incidents and update variables Learn how to resolve incidents and update variables in Camunda 8 Operate. ## Overview Every process instance created for the [`order-process.bpmn`](/bpmn/operate/order-process.bpmn) process model requires an `orderValue` so the XOR gateway evaluation will happen properly. Let’s look at a case where `orderValue` is present and was set as a string, but our `order-process.bpmn` model required an integer to properly evaluate the `orderValue` and route the instance. ## Before you begin To follow this guide, install [`zbctl`](https://github.com/camunda-community-hub/zeebe-client-go/blob/main/cmd/zbctl/zbctl.md), a community-supported command line interface for interacting with Camunda 8. ## Create a process instance Create a process instance with an `orderValue` of `"99"`: ``` ./bin/zbctl --insecure create instance order-process --variables '{"orderId": "1234", "orderValue":"99"}' ``` ``` ./bin/zbctl.darwin --insecure create instance order-process --variables '{"orderId": "1234", "orderValue":"99"}' ``` ``` ./bin/zbctl.exe --insecure create instance order-process --variables '{\"orderId\": \"1234\", \ "orderValue\": \"99\"}' ``` ## Advance an instance to an XOR gateway To advance the instance to our XOR gateway, we’ll create a job worker to complete the `Initiate Payment` task: ``` ./bin/zbctl --insecure create worker initiate-payment --handler cat ``` ``` ./bin/zbctl.darwin --insecure create worker initiate-payment --handler cat ``` ``` ./bin/zbctl.exe --insecure create worker initiate-payment --handler "findstr .*" ``` We’ll publish a message that will be correlated with the instance, so we can advance past the `Payment Received` intermediate message catch event: ``` ./bin/zbctl --insecure publish message "payment-received" --correlationKey="1234" ``` ``` ./bin/zbctl.darwin --insecure publish message "payment-received" --correlationKey="1234" ``` ``` ./bin/zbctl.exe --insecure publish message "payment-received" --correlationKey="1234" ``` In the Operate interface, you should now observe the process instance has an [incident](/components/concepts/incidents.md), which means there’s a problem with process execution that must be fixed before the process instance can progress to the next step. ## Diagnosing and resolving incidents Operate provides tools for diagnosing and resolving incidents. Let’s go through incident diagnosis and resolution step by step. When we inspect the process instance, we can observe exactly what our incident is: 1. Click the **Process Instance Key** for the process stuck on the **Order Value?** node. 2. Navigate to the **Incidents** tab in the bottom panel. 3. Observe the error message: ```text Expected result of the expression 'orderValue >= 100' to be 'BOOLEAN', but was 'NULL'. The evaluation reported the following warnings: [NOT_COMPARABLE] Can't compare '"99"' with '100' ``` To resolve this incident, we must edit the `orderValue` variable so it’s an integer. To do so, take the following steps: 1. Under **Variables**, click the edit icon next to the `orderValue` variable. 2. Edit the variable by removing the quotation marks. 3. Click the checkmark icon to save the change. We were able to solve this particular problem by **editing** a variable, but it’s worth noting you can also **add** a variable if a variable is missing from a process instance altogether. Lastly, initiate a "retry" of the process instance by selecting **Retry** in the top right corner of the page. You should now see the incident has been resolved, and the process instance has progressed to the next step. ## Complete a process instance If you’d like to complete the process instance, create a worker for the `Ship Without Insurance` task: ``` ./bin/zbctl --insecure create worker ship-without-insurance --handler cat ``` ``` ./bin/zbctl.darwin --insecure create worker ship-without-insurance --handler cat ``` ``` ./bin/zbctl.exe --insecure create worker ship-without-insurance --handler "findstr .*" ``` --- ## Initiate a batch operation Learn how to initiate [batch operations](../../concepts/batch-operations.md) in Camunda 8 Operate. ## Overview In some cases, you’ll need to retry or cancel many process instances at once. Operate also supports this type of operation. Imagine a case where many process instances have an incident caused by the same issue. At some point, the underlying problem will have been resolved (for example, maybe a microservice was down for an extended period of time, then was brought back up.) Though the underlying problem was resolved, the affected process instances are stuck until they are “retried." ## Initiate a batch operation Let's create a **selection** in Operate. A selection is a set of process instances on which you can carry out a batch retry or batch cancellation. To create a selection and apply an operation, take the following steps: 1. On the **Processes** page, in the **Process Instances** table, check the box next to the process instances you'd like to include. 2. In the table header, select the operation you want to apply. ## Next steps - [Monitor the batch operation](./monitor-batch-operations.md). - [Learn how batch operations work](../../zeebe/technical-concepts/batch-operations.md). --- ## Getting started(Optimize) Beginner Time estimate: 20 minutes ## Purpose The following document provides a basic end-to-end glance into Optimize and its features for new business users. Optimize offers business intelligence tooling for Camunda customers. By leveraging data collected during process execution, you can access reports, share process intelligence, analyze bottlenecks, and examine areas in business processes for improvement. With Optimize, review heatmap displays for instances which took longer than average to discover long-running flow nodes. As a result, reap actionable insights and rapidly identify the constraints of your system. For an in-depth overview of Optimize’s capabilities, visit our [Optimize documentation](/components/optimize/what-is-optimize.md). ## Set up Within Camunda 8, you can launch Optimize from Camunda Hub — the interface where you can create clusters, and launch both Operate and Tasklist. Therefore, ensure you’ve [created a Camunda 8 account](/components/hub/organization/manage-organization-settings/manage-plan/create-account.md) before getting started with Optimize for SaaS users. :::note So long as you are operating with [Camunda 8 1.2+](https://camunda.com/blog/2021/10/camunda-cloud-1-2-0-released/) when creating a cluster, you can access Optimize. From here, Optimize requires no additional set up. You can immediately obtain process insights as Optimize already continuously collects data for analysis. ::: Once you’ve [created a cluster](/components/hub/organization/manage-clusters/create-cluster.md), click the square-shaped icon in the top left corner of the page and select **Optimize**. You can begin analyzing reports and dashboards with just two process versions. However, the more process versions you work with in Optimize, the more performance attributes and data trends you’ll be able to study. For the purposes of this guide, we’ve preconfigured several processes to demonstrate Optimize’s capabilities. ## Create and analyze dashboards Within Optimize, **reports** are based on a _single_ visualization, similar to a single chart or graph. **Dashboards** are aggregations of these visualizations, similar to a full spreadsheet of data collections, or a combination of several comparative charts and graphs. **Collections** are groups of these data sets, similar to project folders for organizational purposes where we can nest a series of dashboards and/or reports within. Once you open Optimize, you’ll first view the homepage for these collections, dashboards, and reports. To create a collection on the **Home** page, select **Create New > Collection**. Then, you can name your collection and select which data sources and processes will be available. Note that you can select up to 10 processes at once. From within your collection, you can again select **Create New** and draft reports and dashboards. Add users and additional data sources by navigating between the tabs inside the collection. Let’s create a dashboard inside our first collection. Take the following steps: 1. Return to the **Home** page to view a list of existing collections, dashboards, and reports. You’ll be able to view all process instances you’ve already run and retrieve additional data on these instances within the Camunda engine. 2. Select the collection where you’d like to create a dashboard. 3. Click **Create New > New Dashboard**. 4. Optimize offers preconfigured dashboard templates, or you can start from a blank dashboard. In this example, we’ll select a preconfigured template by clicking the **Process performance overview** option. Note that you can also create dashboards with multi-process templates. 5. Under **Select Process**, choose the process you’d like to analyze and the version. 6. Click **Create Dashboard**. 7. Name your dashboard, and add any additional existing reports or create filters. Click **Save**. ![dashboard example](./assets/dashboard.png) In the sample above, Optimize drafted a dashboard filled with reports for review. These reports include objectives like process instance counts, aggregated process duration, active incidents, and heatmaps. Select **Edit > Add a Report** to incorporate additional reports you’ve already created (see [create and access reports](#create-and-access-reports) below). Click and drag the reports on the grid to arrange the dashboard to your liking. :::note Optimize offers collaborative capabilities, too. Click the **Share** tab to share your dashboard. Toggle to **Enable sharing**, and copy or embed the provided link. Colleagues without access to Optimize can still view your report with the shared link. ::: ## Create and access reports To create a custom report based on a key performance indicator (KPI) you’d like to analyze, and to incorporate this report into a dashboard, follow the steps below: 1. On the right side of the **Collections** page, select **Create New > Report**. Here we’ll take a look at a single process, though you can also view data from multiple processes. 2. Click the text box under **Select Process** and select the process you’d like to analyze. 3. Select the type of report you’d like to use on the right side of the **Create new Report** box. As with dashboards, Optimize offers preconfigured templates such as heatmaps and tables. We’ll begin with a heatmap. 4. Click **Create Report**. 5. Set up and customize your report. Begin by naming your report in the text box at the top of the page, pre-filled with **New Report**. 6. In the gray text box to the right, confirm your data source, and select what you’d like to review from the process (in this case, we are viewing flow nodes.) You can also group by topics such as duration or start date. 7. If you’d like, filter the process instance or flow nodes. For example, you can filter by duration, only viewing process instances running for more than seven days. 8. Finally, you have the option to view particular sets of data from the instance, like instance count or absolute value, by selecting the gear icon to the left of your data customization. You can also choose how you’d like to visualize your data in the box beneath **Visualization** (i.e. bar chart, pie chart, etc.). Once you’ve made your selections, click **Save**. ## Alerts You don’t have to log in or view reports and dashboards to be alerted that something may need correction or further analysis in your process. For this purpose, you can create new alerts for reports within your collections. These alerts watch reports for you among collections, and email you an alert if a set outlier occurs in your process flow. To create an alert, take the following steps: 1. Create a report with a number visualization inside a collection for a KPI you want to track. 2. Inside your collection, select the **Alerts** tab. 3. Select the type of alert you would like to receive. For example, you can receive an email notification when the backlog on your bottleneck becomes too high. As you’re notified, you can begin to examine if the process is broken and if additional teams need to be notified. ## Collections Within your collection, you can also access the **Users** and **Data Sources** tabs to further customize your collection. ### Users Within the **Users** tab, review the users with access to your collection. Select **Add** to search for a user to add, who can be assigned as a viewer, editor, or manager. ### Data sources Within the **Data Sources** tab, review and add source(s) of your data to create reports and dashboards inside the collection. ## Additional analysis Now that we’ve created data sets within the **Home** page, let’s shift into the **Analysis** tab. Inside this tab, you’ll notice **Task Analysis** and **Branch Analysis**. ### Task analysis Inside **Task Analysis**, we utilize heatmap displays. Click **Select Process**, choose your process, and choose your version. ![heatmap example](./assets/heatmap.png) Within the example above, we notice increased heat (recognized as red) surrounding our invoice approved gateway. Several instances have taken significantly longer than average, so we may choose to take a closer look at these instances by downloading the instance IDs, or viewing the details for further analysis. Here, you can also find if the outliers have a shared variable. ### Branch analysis Inside the **Branch Analysis** tab, we can select a ​​process and analyze how particular gateway branches impact the probability of reaching an end event. Fill in the process field, click on a gateway, and choose your end event. In the example below, we can further analyze the likelihood of an invoice being processed once it reaches the gateway for approval: ![branch analysis example](./assets/analysis.png) Here, we’ve selected a process flow, gateway, and endpoint for a breakdown of all the instances that went through a particular gateway to a specific endpoint. Hover over the gateway for a breakdown of the process itself. ## Additional resources and next steps We’ve only touched the surface of Optimize. The component is full of additional ways to analyze your data for effective process improvement. We recommend taking a look at several resources to catch up on Optimize’s latest release, new features, and many usage examples: - [Camunda Academy: Measure Value Using Optimize](https://academy.camunda.com/c8-measure-value-using-optimize) - [Camunda Optimize 3.6.0 Release](https://camunda.com/blog/2021/10/camunda-optimize-360-released/) - [The Ultimate Guide to Solving Bottlenecks with Camunda: Part 1](https://camunda.com/blog/2021/10/the-ultimate-guide-to-solving-bottlenecks-with-camunda-part-1/) - [Camunda Optimize examples](https://github.com/camunda/camunda-optimize-examples) - [Process performance made transparent](https://camunda.com/platform/optimize/reports/) --- ## Alerts(Additional-features) Optimize's alerting functionality can be used to notify you when your report hits a predefined critical value. You can create alerts for any number reports that exist within a collection. To configure an alert, take the following steps: 1. Inside a collection, navigate to the **Alerts** tab to create and view all alerts defined for reports in this collection. You can manage an alert by moving the mouse over the alert entry and clicking the **Edit** or **Delete** buttons in the context menu on the right side of the page. ![Alert overview](./img/alerts-overview.png) 2. Click **Create New Alert** to create a new alert. You will then see the following modal: ![Alert modal overview](./img/alert-modal-description.png) 3. To give the alert a name, select the report and define a target webhook or email address of the person who will receive the alert. :::note In Camunda 7 and Camunda 8 Self-Managed, you must configure the email service to receive notifications. See the [technical guide](/self-managed/components/optimize/configuration/system-configuration.md#email) for which properties need to be defined. ::: :::note Only known users in Camunda Hub (SaaS) and Identity (Self-Managed) can receive Optimize alert emails. ::: Note that alerts can only be created for reports which are visualized as a single number and are in the same collection as the alert. Visit the [reports section](../creating-reports.md) on how to define single-number reports. 4. Set a threshold which defines when an alert should be triggered. A notification is sent to the configured email address or webhook as soon as a report value hits the threshold. If reminder notifications are enabled, the alert will continue to send notifications for as long as the value is above (or below, as defined) the threshold. Finally, you'll get a resolve notification as soon as the report value is within a typical range. For example, say you defined an alert which should be triggered when the report value becomes greater than 50. You also enabled reminder notifications to be sent each hour. Here's what that would look like: ![Notifications graph](./img/alert-notifications-graph.png) --- ## Export and import ## Exporting entities Users can export entities by accessing the **Export** option in the entity menu. This downloads a JSON file which includes all relevant information that defines the selected entity. This file can later be used to import the exported entity into a different Optimize installation. ![Exporting a Process Report](./img/export.png) ## Importing entities ### Prerequisites Exported entities can be imported both as private entities and into a selected collection, provided the following prerequisites are met: - All definitions the entity requires exist in the target Optimize. - When importing into a collection, the collection data sources must include all relevant definitions for the entity. - The importing user is authorized to access the relevant definitions. - The entity data structures match. To ensure matching data structures, confirm the Optimize version of the source is the same as the version of the target Optimize. If any of the above conditions are not met, the import will fail. Optimize will display an error message explaining why the import was not successful to enable you to fix the issue and retry. ### Importing private entities To import an entity as a private entity, use the **Import Report/Dashboard** option from the **Create New** menu on the welcome page. The entity will appear in the entity list once the import is finished and can be interacted with as usual. ![Importing a private entity](./img/private-import.png) ### Importing entities into a collection To add the entity to an existing collection, use the same **Import Report/Dashboard** option from the **Create New** menu from within the selected collection. This will import the entity into the collection. Any user that has access to this collection can now also access the imported entity. :::note The collection must have all data sources required by the imported entity or the import will fail. ::: ![Importing an entity into a Collection](./img/collection-import.png) --- ## Machine learning-ready data set The machine learning-ready data set feature allows the export of data into a single data set, easing performance of advanced analysis with Optimize. The data set generated can contain process information and be assembled by generating a raw data report. The data contained in the raw data reports is already organized and pre-processed in such a way that it would allow a trained model to make predictions for future instances based on existing instances for a given definition. In addition to the previously existing columns in the raw data reports, we added columns for improved machine learning capabilities. These columns allow a user to access information such as the total number of [incidents](/components/concepts/incidents.md) per process instance, the number of open incidents, the number of user tasks, and the total duration of an event. For example, this allows you to predict how long an instance will take to complete based on the number of incidents or user tasks. After navigating to a raw data report, note the added columns are now displayed: ![Raw Data Report](../process-analysis/img/raw-data-report-ml-ready-dataset.png) In most cases, when training a machine learning model the data can be fed to common libraries, such as pandas or scikit-learn in CSV or JSON format. To export all data contained in a raw data report and use it as input for model training, export the raw data reports in JSON format. This can be done after saving the report and utilizing the external Optimize endpoint provided to export it to JSON. More information on how to use the JSON export endpoint can be found [here](/apis-tools/optimize-api/report/get-data-export.md). --- ## Process variants comparison When creating reports, it is possible to copy the data source and set different versions and/or tenants for each copy, as well as give it a custom title. This allows you to compare different variants of the process. To use this feature, navigate to the definition edit window from inside a report. Click the copy icon while hovering over the data source. This will create a copy of the data source with all its parameters. Now click the pencil icon while hovering over the data source you want to edit and select a different version and/or tenant. This way there are two variants of the same process which can be now compared. ![Process version selection](./img/process-version-selection.png) Here is an example of a bar chart report comparing the average process instance duration of two versions of the same process. ![Report with two versions of the same process](./img/report-with-process-variants.png) --- ## Variable labeling(Additional-features) The variable labeling functionality allows users to add, update, and delete batches of variable labels so your data is more understandable by business users. This allows Optimize to display a variable's label instead of its original name anywhere the given process definition is being used. Some examples of that would be when viewing and configuring reports, dashboards, or event-based processes. To use this feature, navigate to the definition edit window from inside a report. Click the pencil icon while hovering over the data source and click **Rename Variables** to access the label edit panel. You will then see the following panel: ![Label Edit panel](./img/variable-labeling-panel.png) Delete a label by inputting an empty field for its value. ## Limitations :::note This feature is currently not supported in task analysis and csv export. This means that during task analysis, the original name of a variable is displayed. ::: Keep in mind that when applying variable filters in multi-definition reports and multi-definition dashboards, the filters are applied to all variables across definitions which have the same name and type. This happens even in the case that the variables are labeled differently across definitions. --- ## Collections, dashboards, and reports Optimize uses a single visualization for **reports**, and **[dashboards](./creating-dashboards.md)** combine these visualizations, akin to a spreadsheet or a set of comparative charts. **Collections** organize these datasets, acting as project folders. The Optimize **Collections** page displays all dashboards, reports, and collections, and clicking on a collection reveals associated reports and dashboards. ![home page](./img/home.png) In addition to the name of the dashboard, report, or collection, you can also see modification dates, the number of items they contain, and user access. Optimize enables collaborative sharing through the **Share** tab, allowing the creation of shared links for external viewing. Toggle to **Enable sharing**. :::note Colleagues without access to Optimize can still view your report with the shared link. Learn more about [user permissions](./user-permissions.md). ::: Clicking on a report, dashboard, or collection takes you to its corresponding details page. When moving the mouse over one of these entities, you can access a context menu that allows you to edit, copy, or delete the entity. Multiple entities can be selected and deleted at once using the bulk menu which appears after selecting at least one entity. When copying an entity, you also have the option to move that copy into a collection. ![copy sales dashboard](./img/copy.png) To find a collection, report, or dashboard, use the search field on the top of the page to filter the list by the name of the entity. To [create a dashboard](./creating-dashboards.md) or [report](./creating-reports.md), use the **Create New** button available in the top right corner of the page. ## User permissions For details about roles and access levels in collections (managers, editors, and viewers), see [user permissions](./user-permissions.md). --- ## Create dashboards Efficiently monitor business performance by defining reports in Optimize based on relevant Key Performance Indicators (KPIs). Dashboards, comprising [**Edit mode**](./edit-mode.md) and [**View mode**](./view-mode.md), offer a comprehensive view of system productivity. To create a new dashboard, click the **Create New** button on the homepage or collection page and select the **New dashboard** option. This opens a dialog where you can set the dashboard name and select one of multiple dashboard templates. When not creating a blank dashboard, select a process definition. This process definition is used to create new reports for the dashboard. Creating a dashboard from a template also creates new reports which are saved as soon as the dashboard is saved. ![create new dashboard](./img/dashboardTemplate.png) :::note Click the **Share** tab to share a created dashboard. Toggle to **Enable sharing**, and copy or embed the provided link. Colleagues without access to Optimize can still view your report with the shared link. Learn more about [user permissions](./user-permissions.md). ::: --- ## Creating reports ## Overview Reports offer the ability to view your data from different angles, and thus capture all aspects that influence your processes, show new trends, or depict your current business state. Each report consists of the [edit mode](./process-analysis/report-analysis/edit-mode.md) and [view mode](./process-analysis/report-analysis/view-mode.md) to perform different kinds of actions on it. ## Creating a single report To create a custom report based on a key performance indicator (KPI) you’d like to analyze, and to incorporate this report into a dashboard, follow the steps below: 1. On the right side of the **Collections** page, select **Create New > Report**. Here we’ll take a look at a single process, though you can also view data from multiple processes. 2. Click the text box under **Select Process** and select the process you’d like to analyze. 3. Select the type of report you’d like to use on the right side of the **Create new Report** box. As with dashboards, Optimize offers preconfigured templates such as heatmaps and tables. We’ll begin with a heatmap. 4. Click **Create Report**. ![heatmap example](./img/report-reportEditActions.png) 5. Set up and customize your report. Begin by naming your report in the text box at the top of the page. 6. In the gray text box to the right, confirm your data source, and select what you’d like to review from the process. You can also group by topics such as duration or start date. 7. If you’d like, filter the process instance or flow nodes. For example, you can filter by duration, only viewing process instances running for more than seven days. 8. Additionally, a **Report** can be given a description which is displayed below the **Report** name. Using the **Add/Edit** button, you can add, edit, or remove the description. You are limited to a 400-character plain text description. ![Add description modal](./img/report-descriptionModal.png) 9. You have the option to view particular sets of data from the instance (like instance count or absolute value) by selecting the gear icon to the left of your data customization. You can also choose to visualize your data in the box beneath **Visualization** (i.e. bar chart, pie chart, etc.). Once you’ve made your selections, click **Save**. :::note Click the **Share** tab to share a created report. Toggle to **Enable sharing**, and copy or embed the provided link. Colleagues without access to Optimize can still view your report with the shared link. Learn more about [user permissions](./user-permissions.md). ::: --- ## Data sources If you create a collection, you can add data sources that can be used to create reports. To see the existing data sources or add additional ones, go to the **Data Sources** tab of the collection. ![add source by definition](./img/sources.png) Using the **Add** button, a manager can add one or more sources to the collection by selecting the definitions that need to be added. ![add source by tenant](./img/sourceByTenant.png) The added sources will appear in the process/decision selection list inside the report builder where they can be used to create reports. --- ## Configure dashboards Configure and customize your dashboards in **Edit mode**. It allows you to perform operations such as renaming, modifying or removing descriptions; adding, editing, copying or removing tiles; saving or canceling applied changes; setting dashboard filters; and defining a default auto-refresh rate for periodic updates in [View mode](./view-mode.md). ![edit mode](./img/dashboard-dashboardEditActions.png) ## Adding tiles Assemble reports into a dashboard by clicking "Add a Tile" above the dashboard grid. In the modal, select Optimize reports or external website and text tiles, combining data from various sources as external website tiles and text tiles. ### Optimize reports In the **Add a Tile** modal, use the **Optimize Report** tab to add reports to the dashboard. Use the **Select a Report** field to select one of the previously created reports or click **+ New Report from a template** to create a new report. ![add a report modal](./img/dashboard-addAReportModal.png) :::note If the dashboard is inside a collection, only reports that are in the same collection can be added. If the dashboard is not in a collection, it can only contain reports that are also not in a collection. ::: ### External websites In the **Add a Tile** modal, select **External Website** to input the URL of an external data source to be included in the dashboard. External websites are incorporated as iframes within the dashboard. ![external website editor](./img/dashboard-addAReportModal-externalReport.png) ### Text tiles In the **Add a Tile** modal, choose **Text** to access the text editor. This enables the creation of documents with formatted text, links, and images. ![text editor](./img/dashboard-addAReportModal-textReport.png) ## Editing, copying, and removing tiles Each tile features a set of buttons in its top-right corner, providing you with options to edit and remove the tile. The copy button allows you to duplicate any tile, and the copied version can be conveniently placed anywhere on the dashboard. This duplicated tile is then customizable, enabling you to tailor it to your specific requirements. ## Placing tile on the dashboard Move tiles within the dashboard by dragging them to your preferred location. Upon releasing the tile, it aligns with the nearest grid position. Resize tiles by dragging the handle in the lower right corner. Remove a tile by clicking the button in the top right corner. ![edit actions](./img/dashboard-reportEditActions.png) ## Adding filters in edit mode In dashboard edit mode, click **Add a Filter** to reveal the **Filters** panel. Specify filters for the dashboard, including start and end dates, process instance states (running, completed, or canceled), variable values, assignees, and candidate groups: - Start date: Allows filtering by process instance start date - End date: Allows filtering by process instance end date - Instance state: Allows filtering by process instance state, such as running, completed, or canceled - Variable: Allows filtering by process variable value - Assignee: Allows filtering flow node data by their assignee - Candidate Group: Allows filtering flow node data by their candidate group ![filter edits](./img/filter-editMode.png) For Variable Filters, define the variable and provide a list of values for filtering. Optionally, allow viewers to add their own filter values by checking the **Allow viewer to add filter values** box. Unlike report filters, adding a value here doesn't immediately apply; it only makes the value available for dashboard filtering. For Assignee and Candidate Group filters, specify available options. Similarly, allow viewers to add their values. The list of variable names, values, assignees, and candidate groups is compiled from all reports on the dashboard. ### Setting a default dashboard filter After specifying filters in edit mode, dashboard editors can set a default filter, applied when a user first opens the dashboard. Viewers can remove filter values to see unfiltered reports, but if no manual changes are made, they will view reports with the defined default filter. To set a default filter, use the added filter options in the filter area. The configuration set during dashboard save becomes the default filter. ## Adding or editing description Under the **Dashboard** name, view or modify the description using the **Add/Edit** button. The text editor allows the addition of plain text descriptions, limited to 400 characters. ![Add description modal](./img/dashboad-descriptionModal.png) --- ## Instant process dashboards Optimize serves as a comprehensive process optimization tool, streamlining operations and enhancing efficiency for businesses. Notably, one of its standout features is the automatic generation of dashboards for each process, offering users clear insights into process performance. On first access, Optimize creates an intuitive and user-friendly dashboard on-demand. Users can access these dashboards from the [process dashboards page](./process-dashboards.md) by selecting the desired process. The data presented in the dashboard, along with its corresponding reports, aligns with the user's permissions for that specific process definition. This implies that the dashboard will include data from all authorized tenants. Additionally, both dashboards and reports showcase data from all versions of the relevant process definition. For convenient integration, the dashboard has a consistent URL, allowing embedding into other tools and web pages. The URL format is _https://<OPTIMIZE_URL>/dashboard/instant/<BPMN-PROCESS-ID>/_. Importantly, this URL remains stable across Optimize versions, eliminating the need for updates. :::note Instant process dashboards cannot be shared conventionally. To share, provide the URL, and the recipient will need to sign in to Optimize to access the dashboard. ::: ### Copying the instant process dashboard While the instant preview dashboard offers quick insights, you might want to customize it further to suit your application needs, such as adjusting dashboard filters or configuring reports. To create a copy for customization, use the **Create editable copy** button in the top right corner. This button opens a creation modal with preselected options for an exact copy, but you can also modify the settings to create a different dashboard, choosing from various templates. Additional information on creating a new dashboard can be found [here](./creating-dashboards.md). --- ## Process KPIs Key performance indicators (KPIs) are reports represented by single numerical values with predefined targets. To create a custom KPI, follow the steps below: 1. On the right side of the **Collections** page, select **Create New > Report > Process KPI**. 2. Select one of the predefined KPI templates and the process you’d like to analyze. ![KPI creation step 1](./img/process-kpi-step1.png) 3. Set the target value that you would like to achieve 4. Define the required filters. ![Set creation step 2](./img/process-kpi-step2.png) 5. Click **Create process KPI**. 6. (Optional) Edit the created KPI report and define your own filters/extra configuration. ## Setting existing process reports as KPIs Existing process reports can be configured to act as a KPI. This can be done through the report configuration panel. The checkbox to make the report a KPI is only visible for reports with a number visualization. ![Set KPIs](./img/kpiConfiguration.png) Once a report is set as a KPI, its status is visible on the **Dashboards** page. ![Processes page](./img/processOverview.png) --- ## Branch analysis ## Overview If a process contains more than one end event, it is useful to know which path tokens took to reach a specific end event. Optimize provides you with a statistical analysis for a given end event and a gateway. This analysis includes how tokens were split at the gateway in question, and how many of the tokens of each branch reached the end event. ![branch analysis](./img/analysis-1.png) ## Branch analysis in Optimize Select a process definition using the **Select Process** option in the top left of the page. After selecting a process definition and version, the diagram of the process is displayed on the page. By default, all process instances for the selected process definition are included in the analysis. You can reduce this set of process instances by applying filters. To perform a statistical analysis on the selected process, specify a gateway and an end event. Moving your mouse cursor over the end event and gateway inputs at the top of the screen highlights available elements in the diagram. Likewise, mouse over an element to see whether it is an end event or gateway. Additionally, if you move your mouse over an end event, you see detailed information about this end event, like how many instances reached this end event. Click on an element to select or deselect it. You can also clear the selection using the **x** button in the control panel on top. Changing the process definition also clears the selection. After selecting an end event and gateway, a statistical analysis is shown next to the diagram. The analysis consists of two bar charts and a statistical summary. Both charts contain a bar for every sequence flow leaving the selected gateway. ![branch analysis second example](./img/analysis-2.png) The first chart shows the distribution of process instances over the various sequence flows, showing how often each sequence flow has been executed, independently of whether the process instance then reached the selected end event. The second chart displays the relative amount of process instances that reached the selected end event after taking the respective outgoing sequence flow. Process instances which have taken more than one path (e.g. by looping back to a flow node before the gateway and then taking a different sequence flow) are not considered in the statistical analysis. --- ## Overview(Process-analysis) Locating flaws in your process models can be a huge challenge when you have millions of process instances to sift through. Define filters in Optimize to narrow down your view to only a subset of process instances. Camunda Optimize offers various ways of filtering your data, such as filter by: - [Metadata](./metadata-filters.md) (date, duration, assignee, etc.) - [Instance state](./instance-state-filters.md) (running or canceled instances) - [Flow node](./flow-node-filters.md) (flow node date, flow node duration, etc.) - [Process instance](./process-instance-filters.md) (process instance date, process instance duration) - [Variables](./variable-filters.md) (boolean, string, etc.) ## Filter behavior There are two ways to filter data in Optimize: 1. Instance filters: All filters can be used to filter instances in single reports and during branch analysis. 2. Flow node data filters: These filters can be used if you not only want to filter instances, but you additionally need to filter the content of instances (for example, flow nodes). Since not all filters can be applied on flow nodes, only compatible ones can be used as a flow node data filter. Flow node filters also exclude all instances from the result which do not contain at least one flow node that matches the filter. To summarize, instance filters remove rows, while flow node data filters remove columns. Additionally, if the report contains multiple processes, filters need to specify which definition they apply to. Some filters can apply to multiple definitions at once, while other filters are specific to a certain process definition. For example, because they rely on the flow nodes present in the definition. --- ## Flow node filters ## Flow node filter Retrieve only those process instances that executed certain flow nodes within your process by using the `Flow Node Filter`. Selecting several values at once means that all the selected flow nodes need to have been executed at least once in the process instance lifetime. At the top of the flow node filter modal you can see a preview of the filter you are about to create. You can also filter process instances where certain flow nodes were not executed. ![Flow node filter in Camunda Optimize](./img/flownode-filter.png) ## Flow node selection In flow node and user tasks reports, all flow nodes are included in the result by default. This could result in many table rows or chart entries which makes the visualization hard to read. This filter allows you to specify which flow nodes are included and deselect the ones that are not relevant to the report. ![Specifying which nodes are included in the report](./img/flowNodeSelection.png) ## Flow node status filter Some flow nodes can take a relatively long time to complete (e.g. user tasks or long-running service tasks). By default, a report includes all flow nodes in the calculations, whether they are currently running, canceled, or already completed. You can change this behavior by adding a flow node status filter as a [flow node data filter](./filters.md#filter-behavior). Adding one of the flow node status options will filter both instances and flow nodes according to the selected status: - For instance reports: The filter will only include instances that have at least one flow node matching the filter criteria. This behavior can be seen if you are in variable, incident, or raw data reports. - For flow node reports: Flow nodes that do not match the filter criteria will be excluded from the results. This behavior can be seen if you are in flow nodes or user task reports. Here are the possible options for this filter: - Running flow nodes only: Your report will only collect information from flow nodes that are currently running. - Completed flow nodes only: Considers only successfully completed flow nodes. - Canceled flow nodes only: Considers only canceled flow nodes. - Completed or canceled flow nodes only: Considers all completed flow nodes regardless of whether they were canceled or not. :::note For incident reports, flow node status filters always behave as instance filters and do not filter flow nodes. ::: ## Flow node date filter Similar to process instance date filters, flow node date filters allow you to filter the report based on flow node start or end dates. :::note Reports with a flow node end date filter will only consider data from completed flow nodes. ::: This filter type can be applied either as a [process instance](./filters.md#filter-behavior) or as a [flow node](./filters.md#filter-behavior) filter: - When applied as a process instance filter, you are required to select the flow nodes that are to be relevant to the filter, yielding a report which will only consider those process instances where one or more of the selected flow nodes match the configured filter. ![Flow Node date filter](./img/flowNode-date-filter.png) - When added as a flow node filter, there is no flow node selection. The resulting report automatically only includes data from those flow nodes which match the given filter. ## Flow node duration filter If the **Flow Node Duration Filter** is applied as an instance filter, it will only regard process instances where one or more flow nodes took a certain amount of time for their execution. For instance, you can filter process instances where a flow node took more than three days or less than five seconds. If applied as a flow node filter, it will filter flow nodes and only show the flow nodes that were selected in the filter. ![Flow Node duration filter in Camunda Optimize](./img/flowNode-duration-filter.png) :::note For incident reports, flow node duration filters always behave as instance filters regardless of where they were defined. ::: --- ## Instance state filters ## Running completed instances only filter By default, a report considers all process instances, regardless of whether they are still running. This behavior can be adjusted with the **Running Instances Only** and **Completed Instances Only** filters. Be aware that setting one of those filters (e.g. **Running Instances Only**) while the other one is already set (e.g. **Completed Instances Only**), will show a warning message since these two filters are incompatible with each other and will not show any data. ## Canceled instances only filter If the **Canceled Instances Only Filter** is applied, the report will only consider those instances which were terminated before completion, either internally or externally. Be aware that adding this filter along with the **Running Instances Only** will show a warning message since these filters are incompatible and will not show any data. ## Non canceled instances only filter As opposed to the **Canceled Instances Only Filter**, applying the **Non Canceled Instances Only** filter will make Optimize query only those instances which were _not_ canceled during their execution. This means only active and completed instances are considered. Externally or internally terminated instances are not included in the report. ## Suspended and non suspended instances only filter By default, a report considers all process instances, regardless of whether they are suspended or not. Adding this filter makes it possible to only evaluate process instances that are in the suspension state. Note that if you have enabled history cleanup, this might affect the accuracy of this filer given the suspension state is imported from historic data. --- ## Metadata filters ## Date filters In Optimize, there are two kinds of date filters: the start and the end date filter. Each of these filters can be applied on [process instance](./process-instance-filters.md#process-instance-date-filter) and on [flow node](./flow-node-filters.md#flow-node-date-filter) dates. There are multiple ways in which you can define your date filters: - Set the filter to a current amount of time. For example, today, this week, this month, etc. In such cases, the filter does not remain static, but moves with time to deliver a subset of the data according to the selected time interval. :::note Within date filters, weeks begin on Monday, not Sunday. This is not configurable in Optimize. ::: - Set it to a previous amount of time. For example, yesterday, last week, last month, etc. This filter also moves with time and is automatically adjusted to cover completed periods of time. Take the following example: Today is Wednesday, March 11. If you set a process instance start date filter to `Last... + week`, you get all process instances that were started from Monday, March 2 to Sunday, March 8. A week passes, and we now have Wednesday, March 18. Applying the same filter now filters the process instances which were started from Monday, March 9 to Sunday, March 15. - To cover previous time periods up the current moment of time, you can use the 'Rolling' option. Take the following example: today is March 28. If you set a process instance start date filter to the last three days, you get all process instances that were started from March 26 to March 28. A day passes, and we now have March 29. Applying the same filter now filters the process instances which were started from March 27 to March 29. - If you do not want the filter to be completely dynamic, you can also select `Between`, `Before`, or `After`. - The `Between` option only considers process instances started or ended within a fixed date range (e.g. filter all process instances between 2018-01-01 and 2018-01-26). This range is fixed and does not change. - In the same way, you can select `After` or `Before` options to only consider process instances that started or ended after/before a fixed date. The start and the end date filters are independent and can be applied to a report simultaneously. However, be aware that each of these filters can only exist once. If, for example, you define a new start date filter when another one already exists, the second one will replace the first one. ## Assignee and candidate group filters These filters allow you to include or exclude instances based on the assignee or the candidate group of at least one user task of a particular process instance. ![Assignee/Candidate group filter modal](./img/assignee-filter.png) As shown in the example, it is possible to select one or more assignees or even filter for unassigned instances. This filter has different behavior depending on where it was [defined](./filters.md#filter-behavior): - As a `Flow Node data filter` applied on a user task report: This filter only includes user task instances that satisfy _all_ assignee/candidateGroup filters defined in the report at once. Mutually exclusive filters like having both an inclusive and an exclusive filter on the same assignee do not yield any results in user task reports. - As an `instance filter`: This filter includes all process instances where _at least one_ user task satisfies one particular assignee/candidateGroup criterion. This means multiple mutually exclusive assignee/candidateGroup filter entries might still yield results for these reports (e.g. if the process definition contains multiple user tasks). ## Incident filter This filter has a different behavior depending on where it was [defined](./filters.md#filter-behavior): - As an `instance filter`: This filter will retrieve only those process instances that contain open, resolved, or no incidents (depending on your selection). Here are some examples where this filter can be useful: - Creating reports that contain no incidents since the instances that have incidents have very long durations and are influencing your data. - To monitor all the instances from multiple engines that have open incidents. On the other hand, this filter is not useful for counting the number of incidents because instances with an open or resolved instance filter might still contain instances from the other type. - As a `Flow Node data filter`: This filter will additionally filter the instance incident states to only include incidents of the same type (open or resolved). As an example, This filter can be used to count the number of open or resolved incidents since it considers the incidents of that type exclusively. This filter is currently only useful if you are in an incident view report. :::note The incident filter does not currently filter flow nodes regardless of where it was defined. ::: ## Combined filters All the previously mentioned filters can be combined. Only those process instances which match all the configured filters are considered in the report or analysis. The [duration filter](./process-instance-filters.md#process-instance-duration-filter), [flow node filter](./flow-node-filters.md), and [variable filter](./variable-filters.md) can be defined several times. See the following screenshot for a possible combination of filters: ![Combined filter in Camunda Optimize](./img/combined-filter.png) Everyone who has access to the report can add their own filters. For example, by creating a dashboard that contains that report and using dashboard filters. Note that filters can apply to all processes or a subset of processes. Filters added in such a way are always combined with the filters set in the report edit mode. That way, users can reduce the set of process instances that are considered when evaluating the report, but not increase the number of instances evaluated above the set the report author specified. In essence, if two copies of the same process are present, Optimize combines them with OR logic, and their filters or variables can be combined with the same logic. Therefore, it's possible to compare two differently filtered slices of the same process on the same report (with the group by process feature) or combine them (without group by process). Users can get access to a report via the sharing functionality or if the report is in a shared collection. --- ## Overview(7) The following documentation provides an opportunity to further analyze your reports through several methods: - [Task analysis](./task-analysis.md): Task analysis allows you to easily identify process instances where certain flow node instances took significantly longer than others and subsequently slow down your process. - [Branch analysis](./branch-analysis.md): If a process contains more than one end event, it is useful to know which path tokens took to reach a specific end event. Optimize provides you with a statistical analysis for a given end event and a gateway. --- ## Process instance filters ## Process instance date filter Applying a process instance start or end date filter will result in a report considering only process instances that started or ended within the defined range of dates. :::note Reports with a process instance end date filter applied will only consider completed process instances. ::: As an alternative way to create a process instance start date filter, you can directly select the desired filter interval in the chart itself if your report is visualized as bar or line chart. ## Process instance duration filter The **Process Instance Duration Filter** allows you to only regard process instances whose execution from start to end took a certain amount of time. For instance, you can filter process instances that took more than three days or less than five seconds. :::note This filter shows only completed process instances, since the total duration of running process instances is not yet known. ::: ![Process instance duration filter in Camunda Optimize](./img/duration-filter.png) --- ## Compare target values Based on flow node duration heatmaps, Optimize allows you to specify a target value for every task. For example, if a user task has to be completed within one day, you can set the target value to one day. If the time it takes to complete the user task exceeds this target value, the task is highlighted in the heatmap. To set target values and create a target value comparison heatmap, you need to be in the edit mode of a report which has the following configuration: | View | Flow node duration/user task duration | | ------------ | ------------------------------------- | | Group by | Flow nodes/user tasks | | Visualize as | Heatmap | If your report has this configuration, a target value button is visible. Clicking on the **Target Value** button for the first time opens an overlay containing the process diagram and a table with all flow nodes. You can also see the actual duration value for every flow node. To set a target value for a flow node, use the number and unit fields in the last column. If the target value number field for a flow node is empty, this flow node has no target value set (the selected time unit is ignored in that case). ![Setting Target Values](./img/targetvalue-2.png) If you set a target value for a flow node, this target value is represented as a badge on the flow node in the diagram in the upper part of the overlay. You can click on any flow node in the diagram to jump to the target value input field in the table. If you have a user task report, you can only select user tasks here, as only those are included in the report result. When selecting a target value input field in the table, the corresponding diagram element is highlighted. To save the target value configuration, click **Apply**. After you save the target values, the normal duration heatmap is replaced with a target value visualization. In this new visualization, flow nodes with an average duration larger than the specified target value are highlighted in red. If you mouse over one of the nodes, the tooltip shows the following: - The target duration value - The actual duration - The relative percentage the actual value is of the target value - A button to download a list of process instance IDs that exceed the target value You can also see the target value as a badge on the diagram. ![Target Value Comparison](./img/targetvalue-1.png) After the initial target values for a report are set, you can use the target value button to toggle between the target value and the regular duration view mode. If you want to change target values, use the gear button to open the overlay again. As with any change to a report configuration, to persist target values and show them in the report view mode and on dashboards, you need to save the report using the **Save** button in the upper right corner. --- ## Configure reports The configuration panel groups all the settings that can be applied to a report in one place. To see the panel, click on the cog button available in the edit mode of any report. Every visualization has different settings that can be configured in the panel. When you save changes to the report configuration, they apply to the report view mode and any dashboard this report is displayed on. ## Number Number reports are any reports that are visualized as a single number (e.g. `Process Instance: Count` grouped by `None` or `Process Instance: Duration` Grouped by `None`). In number reports, the following configurations are possible: ## Number precision Number precision can be configured from the panel to limit the most significant units to be shown. For example, we have a report that calculates the total process instances duration. When the precision limit is not set, you will see all possible units, e.g.: `1y 5m 2wk 5d 3h 16min 3s 170ms`. In case you are only interested in certain units - e.g. months - you can omit all insignificant units by limiting the precision as shown in the figure below: ![Number report configurations](./img/NumberConfiguration.png) ## Number goal value (progress bar) Number reports appear as progress bar when the goal option is enabled from the panel as shown. The baseline and the target value of the progress bar can be also set using the panel. ![Progress Bar Visualization](./img/progressbar.png) You can toggle between the progress bar and the single number visualization using the same goal line switch. A red line indicator appears on the progress bar when its value exceeds the goal value. On the right side of the indicator, the bar turns into a darker color to clearly show the exceeded amount. ![Progress Bar Visualization](./img/progressbarExceeded.png) ## Table settings In table reports, the following configurations are possible: ## Show instance count Displays the total instance count on the right side of the visualization. If you save the report while this option is enabled, the number will also be shown on any dashboard this report is added to and when the report is shared. ## Hide, show, and reorder table columns The table settings allow you to hide specific columns using the configuration menu as shown in the figure below: ![raw data configuration](./img/rawdata.png) When working with raw data table reports, you can also re-order the table columns using drag-and-drop on the header of the respective column. ## Sorting by table column To sort a table by a specific column, click on the header of that column. Doing that will show a small caret icon in the header of the column indicating which column the table is currently sorted by and the direction of this sorting (ascending or descending) as shown: ![Sorting a table in Optimize](./img/sorting.png) Clicking again on the same column header will reverse the direction of sorting. Saving the reports will also preserve the applied sorting. The sorting currently works for all table reports except for: - Combined table reports - Reports grouped by integer type variables ## Absolute and relative values When configuring a count report, you have the opportunity to configure which columns are included in the table. You can hide or show the corresponding columns using the switches for absolute and relative value. ## Custom bucket size for date variables When evaluating a report which is grouped by a date variable and displayed as a table, Optimize offers you the option to select your preferred unit specifying the custom result bucket size from the report configuration menu. The available units are year, month, week, day, and automatic. The default unit is automatic, which will create evenly spaced result buckets based on the values of the date variable. This configuration option is also available for charts. ## Custom bucket size and baseline When evaluating a report which is grouped by duration or a number variable, Optimize offers you the option to specify your preferred result bucket size as well as a custom baseline in the report configuration menu. The bucket size determines the width of one bucket, and the baseline specifies the start of the first bucket. For example, say a report contains the variable values 0.3, 6, and 13, and you set a bucket size of 5. By default, Optimize would now return a bucket for the values 0.3 to 5.3, one for 5.3 to 10.3, and one for 10.3 to 15.3. You may prefer your bucket start and end points to be a round number, in which case you should set your baseline to 0. With a baseline of 0 and bucket size 5, the result buckets now span 0 to 5, 5 to 10, and 10 to 15. If these configuration fields are not set, by default Optimize will create evenly spaced result buckets with a range based on the minimum and maximum values of the number variable. This configuration option is also available for charts. ## Charts (line, bar, pie) In bar chart and line chart reports, it is possible to select the color of the graph, add names to the x-axis and y-axis, and edit many other settings as shown in the figure below: ![chart visualization configurations](./img/chartConfiguration.png) In charts, you can hide/show absolute and relative values that appear in the tooltips. ## Show instance count Displays the total instance count on the right side of the visualization. If you save the report while this option is enabled, the number will also be shown on any dashboard this report is added to and when the report is shared. ## Chart goal line Optimize allows you to set a goal line in bar chart and line chart visualizations. Using this feature, it is possible to highlight anything above or below a certain value. A good use case for such functionality is the following example: First, go to the edit mode of a report and choose the following configuration: | View | Count frequency of process instance | | ------------ | ------------------------------------- | | Group by | Start date of process instance: Month | | Visualize as | Bar chart | Let us say that the number of completed process instances should always be above six. A goal line can be used as follows: Set the target value input field to six and select the above button. If the number of process instances is below six, it will be highlighted in red as shown: ![Bar charts goal line](./img/targetValue.png) This feature can be also used with every other bar chart and line chart visualization. Here is another example where the target value is used with line chart visualization: ![Line chart goal line](./img/targetline.png) ## Custom bucket size for date variables When evaluating a report which is grouped by a date variable and displayed as a chart, Optimize offers you the option to select your preferred unit specifying the custom result bucket size in the report configuration menu. The available units are year, month, week, day, and automatic. The default unit is automatic, which will create evenly spaced result buckets based on the values of the date variable. This configuration option is also available for tables. ## Custom bucket size and baseline When evaluating a report which is grouped by duration or a number variable, Optimize offers you the option to specify your preferred result bucket size as well as a custom baseline in the report configuration menu. The bucket size determines the width of one bucket, and the baseline specifies the start of the first bucket. For example, say a report contains the variable values 0.3, 6, and 13 and you set a bucket size of 5. By default, Optimize would now return a bucket for the values 0.3 to 5.3, one for 5.3 to 10.3, and one for 10.3 to 15.3. You may prefer your bucket start and end points to be a round number, in which case you should set your baseline to 0. With a baseline of 0 and bucket size 5, the result buckets now span 0 to 5, 5 to 10, and 10 to 15. If these configuration fields are not set, Optimize will create evenly spaced result buckets with a range based on the minimum and maximum values of the number variable by default. This configuration option is also available for tables. ## Stacked bar chart When evaluating a report which has a second "Group by", Optimize offers you the option to stack the bar chart bars instead of displaying them near each other. Stacking bars would be useful when the focus of the chart is to compare the totals (e.g. flow node count, process instance count, etc.) and one part of the totals (e.g. flow node, variable value, etc.) This configuration option is also available for bar/line charts. ![Stacked bar chart report](./img/stackedBar.png) ## Switch bar chart orientation When evaluating a report, Optimize will automatically set the bar chart orientation according the nature of the data being displayed. You can also switch the orientation manually using the configuration option shown. ![Stacked bar chart report](./img/horizontalBar.png) ## Bar/line chart When evaluating a report which has both count and duration measures, Optimize offers you the option to display one of the measures as bars and the other measure as a line. This would help to differentiate between duration and count values displayed in the visualization. By default, the count measure is displayed as bars and the duration as a line. You can also switch between them by using the configuration option shown. ![Bar/Line chart report](./img/barLine.png) ## Heatmaps When enabling absolute or relative values switches, all tooltips for all flow nodes stay visible. This is also possible when you have defined target values. If you save the report in this state, the tooltips will also be shown on any dashboard this report is added to. ![Heatmap tooltips](./img/heatmap.png) As for charts and table reports, it is possible to display the total instance count on the right-hand side of the visualization. If you save the report while this option is enabled, the number will also be shown on any dashboard this report is added to and when the report is shared. --- ## Define reports In this section of the report builder, you are characterizing the output of the report. Basically, you are defining "I want to view ... grouped by ... visualized as ...". To understand better what "View" and "Group by" mean, you can use the analogy of a graph, where "View" is the y-axis and "Group by" is the x-axis. First, you need to select which part of the data you want to view. Optimize differentiates between the view (e.g. process instance or flow node) and the measure (e.g. count or duration): 1. Raw Data: View just a table with the actual data listed as rows. This can come in handy if you found interesting insights in certain process instances and need detailed information about those instances, or you are exploring a process definition with a limited number of instances. This report type also allows you to inspect raw [object variable values](/self-managed/components/optimize/configuration/object-variables.md). 2. Process instance - Count: View how many process instances were executed. - Duration: View how long the process instances took to complete. 3. Incident - Count: View how many incidents occurred on the process. - Resolution duration: View how long the incident took to get resolved. 4. Flow node - Count: View how often the flow nodes (e.g. tasks) have been executed. - Duration: View how long each flow node took to complete. 5. User task - Count: View how often each user task has been executed. - Duration: View how long each user task took to complete. 6. Variable: View an aggregation of values for a specific numeric variable of the process definition. It is possible to display both count and duration measures for a single view in the same report. Subsequently, you need to define how to group the data. Think of it as applying a metric to your input, where you break up the data by date, flow nodes, variable values, or other properties. For that, you have different options: - **None**: Do not split up the data. - **Flow nodes**: Cluster the data by flow nodes. - **User tasks**: Cluster the data by user tasks. - **Duration**: Cluster the data by duration. Depending on the selected view, this can be the duration of process instances, flow nodes, or user tasks. - **Start date**: Group instances together that were started during the same date period or time, e.g. hour, day or month. Depending on the selected view, this can be the start date of process instances, flow nodes, or user tasks. - **End date**: Group instances together that were finished during the same date period or time, e.g. hour, day or month. Depending on the selected view, this can be the start date of process instances, flow nodes, or user tasks. - **Running date of the process instance**: Group process instances together that were running during the same date period or time, e.g. hour, day, or month. - **Variable**: Process instances with the same value for the selected variable are grouped together. - **Assignee**: Only available for user task views. Tasks are grouped together according to their current assignee. - **Candidate group**: Only available for user task views. Tasks with the same candidate group are grouped together. - **Process**: Only available for process instance reports with multiple definitions. Data from the same process is grouped together. Finally, define how you want the data to be visualized. Examples are heatmap, table, bar, or line chart. Not all the above view, group by, and visualization options can be combined. For instance, if you choose `Flow Node: Count` as view, the data is automatically grouped by flow nodes as no other combination would be valid. All possible combinations can also be found in the following table: | View | Group by | Visualize as | | --------------------------------------------------- | --------------------------------------------------------------- | --------------------- | | Raw Data | None | Table | | Process instance: Count, Process instance: Duration | None | Number | | Process instance: Count | Start Date, End Date, Running Date, Variable, Duration, Process | Table, Chart | | Process instance: Duration | Start Date, End Date, Variable, Process | Table, Chart | | Incident: Count, Incident Duration | None | Number | | Incident: Count, Incident Duration | Flow Nodes | Table, Chart, Heatmap | | Flow Node: Count, Flow Node: Duration | Flow Nodes | Table, Chart, Heatmap | | Flow Node: Count | Start Date, End Date, Duration, Variable | Table, Chart | | Flow Node: Duration | Start Date, End Date, Variable | Table, Chart | | User Task: Count, User Task: Duration | User Tasks | Table, Chart, Heatmap | | User Task: Count, User Task: Duration | Start Date, End Date, Assignee, Candidate Group | Table, Chart | | User Task: Count | Duration | Table, Chart | | Variable | None | Number | :::note You might sometimes see a warning message indicating that the data is limited to a certain number of points. This happens because the available stored data, in this case is very large, and it is not possible to display all the data in the selected visualization. ::: ## Reports with a second "Group by" option Using the second "Group by" option, it is possible to apply another grouping to your data to display extra details such as dates, variable values, or assignees. This option will be shown below the first "Group by" option if the current report combination supports it. Here is an overview of the reports that supports a second "Group by": ## Flow node reports Flow node names can be applied as a second "Group by". If the report contains multiple process definitions, the data can also be grouped by process as a second "Group by". ## User task reports For information about Optimize user task analytics, refer to our [task analysis documentation](../task-analysis.md). ## Process instance reports Refer to the table below for the process instance count and duration reports that support a second "Group by": | View | Group by | Second group by | | -------------------------------- | ---------------------- | ----------------------------------------------------------------- | | Process Instance Count, Duration | Start Date, End Date | Variable, Process (only for multi-definition reports) | | Process Instance Count, Duration | Variable | Start Date, End Date, Process (only for multi-definition reports) | | Process Instance Count | Running Date, Duration | Process (only for multi-definition reports) | The diagram below shows a report grouped by `Start Date` and a boolean variable: ![Distributed process instance report](./img/distributedByVar.png) --- ## Overview(Report-analysis) Edit mode allows you to configure the report and adjust it to your needs. The following operations are possible within edit mode: - Rename your report - Add/edit/remove description - Build a report - Configure your report - Save the current state with your applied changes - Cancel changes you already applied to the report Building a report is the crux of the report edit mode. The building process itself is composed of several steps, which happen in the control panel. :::note In edit mode, you can toggle the automatic preview update of the report by toggling the **Update Preview Automatically** switch. You can also use the **Run** button to run the update manually. By default, the automatic preview update is **disabled**. The toggle setting is stored in the user session and will be applied as a default value next time. ::: In this section, learn how to: - [Select process definitions](./select-process-definitions.md) - [Define reports](./define-reports.md) - [Set durations and variable report aggregation](./measures.md) - [Compare target values](./compare-target-values.md) - [Select process instance parts](./process-instance-parts.md) - [Configure reports](./configure-reports.md) --- ## Measures ## Duration and variable report aggregation For duration and variable views, the default aggregation type is the average. You can add and change different aggregations like minimum, maximum, and median in the report configuration panel. Note that the median is an estimate and the other operations are exact values. ![Duration Aggregation Selection](./img/durationAggregation.png) Reports with multiple aggregations that have a [second "Group by"](./define-reports.md#reports-with-a-second-group-by-option) can only be visualized as table. ## User task duration time For information about Optimize user task analytics, refer to our [task analysis documentation](../task-analysis.md). --- ## Report process analysis ## Edit mode The [edit mode](./edit-mode.md) allows you to configure the report and adjust it to your needs. The following operations are possible within edit mode: - Rename your report - Add/edit/remove description - Build a report - Configure your report - Save the current state with your applied changes - Cancel changes you already applied to the report ## View mode Once you have defined what your report should look like, the [view mode](./view-mode.md) gives you a full view of the report visualization. To see more details about the report, you can interact with it, e.g. by moving your mouse over individual data points in diagrams or zooming in or out of heatmaps. The kind of interaction always depends on the report itself. --- ## Process instance parts In some cases, you may not be interested in the duration of the whole process instance, but only a certain part of it. For that scenario, there is an additional button called **Process Instance Part** available for every process instance duration view that only shows data for a single process definition. Clicking this button opens an overlay letting you select the start and end of the part of the process instance you are interested in. After confirming the selection, the displayed duration refers to the selected part only instead of the whole instance. In some cases it can happen that the same task is executed multiple times in the same process instance, e.g. if the process contains loops or parallel gateways. In such cases, Optimize considers only the part between the start date of the first instance of the start node and the end date of the first instance of the end node. ![Process Instance Part Modal](./img/process-part.png) --- ## Select process definitions Every report relates to one or multiple process definitions, versions, and tenants. You must choose at least one process definition you want to create a report for. To add a process definition to the report, click **Add** at the top of the **Data Source** section of the report control panel. This opens a dialog showing all process definitions you can use in the report. You can select up to 10 definitions to add to the report. If there are many process definitions, you can use the input field to search for the definition you are looking for. ![Process definition selection in the report builder in Camunda Optimize](./img/report-processDefinitionSelection.png) For every added process definition, you can set a display name and a specific version or version range. To do so, click the **Edit** button in the process definition card. There are also buttons to remove the definition from the report or add a copy of the definition. When editing a process definition, using the version dropdown, you can choose between all versions, the latest version, or a specific set of versions. ![Process Version selection in the report builder in Camunda Optimize](./img/report-versionSelection.png) - **All** option: Every process instance across all versions of the process definition will be considered in your report. - **Always display latest** option: Makes your report always refer to the latest version. Keep in mind that if a new version of the process is deployed, the report will automatically consider process instances of this new version only. - **Specific version** option: Specify one or multiple existing versions of the process. Data from older versions is mapped to the most recent version in the selection. Therefore, the report data can seem to be inconsistent, which is due to changes that occurred within the diagram through the different versions. For example, the old versions do not contain newly added tasks or a part of the diagram was removed because it was considered to be obsolete. ![Process definition selection for a multi-tenancy scenario](./img/tenantSelection.png) By default, all process instances for the selected process definitions are included in a report. You can reduce this set of process instances by applying a [filter](../../process-analysis/filters.md). --- ## View mode Once you have defined what your report should look like, the view mode gives you a view of the report visualization and the raw instance data. To see more details about the report, you can interact with it, e.g. by moving your mouse over individual data points in diagrams or zooming in or out of heatmaps. The kind of interaction always depends on the report itself. The view mode also provides you with different kinds of actions, such as: ![report sharing popover in Camunda Optimize](./img/report-sharingPopover.png) - Download CSV: In case you want to download the data of the report, you can click the **Download CSV** button. The downloaded file will include the report information in a table format. - Sharing: In case you want to share the report with other people or want to embed it in a webpage, you can use the sharing feature of the report. Just click on the **Share** button, which opens up a popover. After enabling the **Enable sharing** switch, a link is generated which you can send to people who do not have access to Camunda Optimize and thus enable them to see the report. You can also use the **Embed Link** button if you wish to insert the report into your webpage. Everyone that views the webpage can then see content of the report. The shared versions of the report allow you to view the report itself only. There is no possibility to alter it or interact with any other features of Optimize. You can revoke the sharing any time by disabling the share switch. If you prefer to hide the header of the shared report or specific part of it, you can do that by adding the following parameter to the share URL: ``` header : titleOnly / linkOnly / hidden ``` For example, to completely hide the header from the shared report, you can add `header=hidden` as shown: ``` http://?header=hidden ``` - Alerts: If the created report is inside a collection, you can use the **Alert** dropdown to create and manage Alerts for that report. Since alerts can only be created on reports that have a number visualization, the **Alerts** dropdown will be only be visible for such reports. - Description: If a Report has a description, the description is displayed below the Report name. The **More/Less** button can show or hide the text for descriptions longer than a single line. ![report description](./img/report-showMoreDescription.png) --- ## Task analysis Task analysis allows you to identify process instances that took significantly longer than others to complete a flow node, and subsequently slow down your process. ## Task analysis in action Select a process definition you would like to analyze. Once a definition is selected, a **heatmap** is displayed, highlighting the flow nodes where Optimize identified many duration outliers. In our example, the **Approve Invoice** task has duration outliers. Additionally, in the **Outliers** table, you can see how many instances were identified, how much longer they took than the average duration, and a list of related variables. ![task analysis example 1](./img/outlierExample_1_heatMap.png) Click the node on the **heatmap** or use the **View Details** button in the table to directly see a duration distribution chart for the specific flow node. The duration distribution chart contains information about how long the identified outliers took, and also in comparison to the other flow node instance durations. ![task analysis example 2](./img/outlierExample_2_detailsModal.png) ## Significant variable values When looking at the duration outlier instances, you can analyze the data further to find the root cause of why these instances took so long. Look at the significant variables table that lists significant variable values in the outlier instances. This also allows you to see how many times this variable value occurred in the outlier instances compared to the rest of the process instances. This can give you a good idea of if there is a correlation between a variable value and a flow node taking longer than expected. In our example, we can see that for most of our duration outliers the delay variable was set to `true`. --- ## User task analytics In the realm of automated processes, the efficiency and effectiveness of a system are largely determined by how well it supports the user in performing their tasks. Often, businesses find that, despite automation, there still exist significant bottlenecks affecting overall productivity. These bottlenecks are commonly rooted in user tasks. - **User tasks generating the highest effort:** In an automated environment, certain tasks still demand significant human intervention. These tasks are often complex, error-prone, and time-consuming, leading to a disproportionate allocation of resources. - **Optimizing user tasks as a driver for improvement projects:** As organizations continually seek to improve performance, the optimization of user tasks emerges as a primary driver for new improvement projects. Streamlining these tasks can lead to substantial gains in productivity and cost savings. - **User tasks as a source of operational frictions:** User tasks are frequently the primary source of operational frictions. These frictions can manifest as delays, decreased throughput, and increased error rates, all of which negatively impact the business's bottom line. ![user task heatmap example](./img/userTask_heatMap.png) Optimizing user tasks offers a comprehensive approach to mitigating the challenges associated with user tasks. By leveraging advanced analytical tools, businesses can gain insights into how time and resources are being spent on user tasks and identify opportunities for improvement. Optimize provides the following capabilities to do so: - **Analyze time spent on user tasks for assignment:** By measuring and analyzing the time users spend on specific tasks, organizations can identify patterns and outliers. This analysis allows for a better understanding of task complexity and the allocation of resources. - **Define targets for user tasks:** With concrete data on how long tasks should take, organizations can set realistic performance targets. These targets provide a benchmark for measuring improvements and can drive accountability and motivation among users. ## Use cases ### Analyze task duration on a heatmap The user task heatmap is designed to offer stakeholders a panoramic view of all user tasks within a given process. This visual tool synthesizes complex data into an understandable graphical representation, highlighting areas of high activity and potential bottlenecks within the process flow. Optimize allows different aggregations based on the lifecycle of a task. With filters, it is possible to restrict evaluated process instances to analyze specific cases. ![user task duration heatmap example](./img/userTask_duration_heatMap.png) ### Measure targets on a heatmap To maintain adherence to Service Level Agreements (SLAs), it is essential to establish clear and measurable targets for each user task within a process. These targets serve as performance benchmarks, guiding users and helping maintain the quality and timeliness of task completion. Optimize allows users to define target values for user tasks on the heatmap. ![user task target example](./img/userTask_target_heatMap.png) ### Evaluate task performance For a thorough understanding of task execution within your processes, it is imperative to examine how tasks are performed both by the nature of the user task itself and by the assignee responsible for completing the task. This detailed analysis enables organizations to assess individual and collective task performance, providing insights into workflow efficiency and productivity. Optimize allows breaking down user tasks by user task or assignee for the assigned and unassigned state of the task lifecycle. With filters, it is possible to evaluate only a subset of the process instances, for example, to evaluate the performance by country. ![user filtered report example](./img/userTask_report_filters.png) ## Features ![user features example 1](./img/userTask_features1.png) ![user features example 2](./img/userTask_features2.png) To evaluate user tasks, the following features are available in the report builder: - **View user task** - The `View: User task` option limits the flow nodes evaluated to user tasks only. - **Duration aggregations** - Durations can be aggregated by `Total`, `Assigned` and `Unassigned`. - **Grouping** - Data can be grouped by `User tasks` or `Assignee`. - **Filter** - Data can be filtered by `Assignee`. ## Good to know :::danger Known limitations - Currently, user task analytics can be used only with assigned or unassigned time. We are working on analyzing net-work time. - This will only work with Tasklist and custom task applications implementing the complete [Camunda Tasklist Lifecycle](/apis-tools/frontend-development/01-task-applications/01-introduction-to-task-applications.md). - User task analytics only work correctly if all user tasks in a process are of type `Camunda user task` (formerly Zeebe user task). The `job worker` user type does not contain task lifecycle information and is therefore not displayed in the `User tasks` view. ::: ### How to evaluate task performance per assignee Evaluating performance on assignee level is not allowed in all organizations. All features related to data evaluation on an assignee level can be deactivated via [configuration](/self-managed/components/optimize/configuration/system-configuration-platform-8.md#settings-related-to-camunda-8-zeebe-user-tasks). Evaluation on assignee level is only possible in Camunda 7 and Camunda 8 Self-Managed. ### How the user task duration time is calculated In user task duration reports, you have the opportunity to select which part of the user task's lifecycle you want to see in the report: - **Unassigned:** View how long each user task was considered unassigned (not claimed by an assignee/user) during its execution. - **Assigned:** View how long each user task was considered to be assigned to assignees/users (claimed by an assignee/user) during its execution. - **Total:** View how long each user task took to complete. It is possible to display and compare multiple user task duration times in the same report. Reports with multiple user task duration times that have a [second "Group by"](./report-analysis/define-reports.md#reports-with-a-second-group-by-option) can only be visualized as table. In certain circumstances, user tasks can be completed without being assigned to a user. These user tasks are evaluated as follows: - If the user task was canceled without assignment (for example, by an Operator in Operate), the task duration is considered `Unassigned`. - If the user tasks were completed without assignment (for example, via a custom UI), the time between start and end is considered `Assigned`. - As these user tasks do not have an `assignee` set, they are displayed `Unassigned` in the reports. --- ## Variable filters Use the `Variable Filter` to retrieve only those process instances which hold the specified variable value for the selected variable. :::note Variable filters can only filter for the final value of the variable. For instance, assume you want to analyze only those process instances which have the variable `department` with the value `marketing`. Say you also have some instances where this variable had the value `marketing` at the start of the execution, yet this was later reassigned to the value `sales`. These instances will not be included in the filter. ::: To use complex types like object, use the **Variable Import Customization** feature to transform your object variables into primitive type variables. Start creating a variable filter by searching for and selecting a variable from the suggested list of variable names. ![Searching through the variables in variable filter](./img/variable-filter.png) There are four types of variables that you can filter for: ## Boolean variables They can have the state `true`, `false`, `null`, or `undefined`. ## String variables Two types of operators are available for variables of type `String`. You can either filter by an exact variable value (`is` and `is not`) or filter by a substring (`contains` and `does not contain`). For the operators `is` and `is not`, the first 10 values are loaded and displayed. If the variable has more than 10 values, a `Load More` button is shown to be able to extend the list as much as you need. You can also search through the whole list of values using the search input field. The list only contains variable values that already appeared in one of the instances of the process. To filter by a variable value that is not in the list of available values, click the **+ Value** button and add a custom variable value. In case the `is` option of the toggle button is selected, checking one or more values means that you want to see only those process instances where the variable value equals one of the checked values (this corresponds to the `or` operator in boolean logic.) In case the `is not` option of the toggle button is selected, checking one or more values means that you want to see only those process instances where the variable value does not equal any of the checked values (this corresponds to the `and` operator in the boolean logic.) For the operators `contains` and `does not contain`, you can add one or multiple values that should match a substring of the variable value. For the `contains` operator, adding one or more values means that you want to see only those process instances where the variable value contains one of the entered values (this corresponds to the `or` operator in boolean logic). In case the `does not contain` operator is selected, adding one or more values means that you want to see only those process instances where the variable value does not contain any of the entered values (this corresponds to the `and` operator in boolean logic.) There is an option to include the null or undefined values of the selected variable in the result. By using the same option, it is also possible to show all the values except the null or undefined by selecting the `is not` option of the toggle button. ## Numeric variables Here you have an input field to define whether the variable value in the process instance should be equal, not equal, less than, or greater than a certain value. You can even add more input fields and apply the same operation several times at once. If the `is` option of the toggle button is selected, adding one or more values means that you want to see only those process instances where the variable value equals one of the checked values (this corresponds to the `or` operator in boolean logic.) If the `is not` option of the toggle button is selected, adding one or more values means that you want to see only those process instances where the variable value does not equal any of the checked values (this corresponds to the `and` operator in boolean logic.) In case the `is less than` or `is greater than` option is selected, only one value can be entered. Null or undefined options can be included or excluded from the results in a way similar to string variables. ## Date variables This filters all instances where the selected date variable has a value within a specified date range. All the options that are available to configure [date filters](./metadata-filters.md#date-filters) are also available for date variables. Similar to the other variables, there are two input switches that allow you to exclude or include process instances where a particular date variable is either `null` or `undefined`. ## List variable filters To filter based on the value of a [list variable](/self-managed/components/optimize/configuration/object-variables.md#list-variables), the applied filter will depend on the primitive type of items within the list. For example, you will be creating a numeric variable filter for a variable which is a list of numbers, a string variable filter for a list of strings, and so on. It is important to note here that filters are applied on each individual item within the list variable and not the list itself. For example, an "is" filter on a list of string values filters for those instances where any individual list item is equal to the given term. For example, instances whose list variable "contains" the selected value. Similarly, the "contains" filter matches process instances whose list variable contains at least one value which in turn contains the given substring. ## Combine multiple variables filters with OR logic Additionally, to use variable filters individually, there is also the option of combining all the previously mentioned variable filters with OR logic. This means that variables which fulfill the condition specified in at least one filter will be displayed. --- ## Process dashboards ## Overview The **Process Default Dashboards and KPIs** section gives you an overview of all the processes and their status on a single page. This section also allows you to set a process owner and take responsibility of a process, viewing time, and quality KPIs to track process performance. Additionally, an **Adoption Dashboard** can be found at the top of the page, which integrates data from all processes in one view. ![Processes page](./img/processOverview.png) ## Set time and quality KPIs KPIs, or Key Performance Indicators, are reports represented by single numerical values with predefined targets. The process of creating KPI reports is explained in [process KPIs](./process-KPIs.md). The classification into a time or quality KPI occurs internally in Optimize based on the chosen measure. Once a report is set as a KPI, its status is visible on the **Dashboards** page. Hovering over the status provides a preview of specific information related to the KPI, including the set target, the current actual value of the single-number report, and a link to it. Users accessing the report via this link without proper authorization won't be able to view any data in the report view. ![Set time and quality KPIs](./img/kpiConfiguration.png) ## Configuring process owner and digests The process can be configured by clicking the **Configure** option selected from the three dots menu displayed on the right side when hovering over the process. From this modal, you can change the owner of the process, and enable/disable the process digest. The process digest is a scheduled email report summarizing the current and previous state of the KPI reports for that process. It will be emailed to the owner of that process at [globally configurable regular intervals](/self-managed/components/optimize/configuration/system-configuration.md#digest). Note that process digests are an alpha feature. ![Configure Process](./img/configureProcess.png) ## KPI import scheduler Since users might be dealing with hundreds or even thousands of KPIs, a scheduler has been developed which updates the KPI values on a given interval. The default interval in which the KPIs get updates is 10 minutes. To change this interval, modify the configuration value for **entity.kpiRefreshInterval**. For more information, visit the relevant [configuration section](/self-managed/components/optimize/configuration/system-configuration.md). ## Limitations Since the updates on the KPIs will appear on the process overview page after the given KPI import scheduler interval has passed, changes such as creation, update and deletion of KPIs will show with a delay. In case you wish to make these changes apparent more promptly, you can set the KPI scheduler interval to a lower value as described above. Additionally, it is worth mentioning that for the evaluation of the KPI reports, the default timezone of the machine on which Optimize is being run on will be used. --- ## User permissions By default, if you create a collection, only you can access the collection and the contents within. To share a collection with other users, add them to the collection. You are automatically assigned the manager role when creating a new collection. There can be multiple managers for a collection. However, there must be at least one manager for every collection. ## Roles in collections User permissions in collections involve managers, editors, and viewers. ### Managers Managers can: - Add, edit, and remove dashboards and reports in the collection. - Edit the collection name and delete the collection using the context menu in the header. - Add, edit, and remove other users in the collection via the **Users** tab. - Manage collection data sources. A manager can add a new user to the collection using the **Add** button. Use the ID of the user to add them. ### Editors Editors can: - Create, edit, and delete dashboards or reports in the collection. Editors cannot: - Edit the name of the collection. - Delete the collection. - Change anything in the **Users** tab. - Manage collection data sources. ### Viewers Viewers have read-only access. They can: - View the components contained within the collection. - Copy components. Viewers cannot: - Create, edit, or delete components in a collection. - Rename or delete the collection itself. - Change anything in the **Users** tab. - Manage collection data sources. Every user has a role assigned to them that specifies their access rights to the collection. Add users to collections via the **Users** tab, assigning roles that determine access rights. --- ## Monitor dashboards Monitor your dashboards and visualize your processes data with the **View mode**. It provides interactive charts, raw data tables, and sharing features. ## Supported features View mode provides the following features for monitoring your processes: - **Full-screen**: Display the dashboard in full-screen mode to focus solely on the reports, hiding the header, control panel, and footer. While in full-screen mode, toggle between the default light theme and a dark theme using the Toggle Theme button. - **Auto-refresh**: Periodically updates the dashboard with the latest data. You can customize the update frequency from one to 60 minutes. An animation indicates the timing of the next update. You can disable this feature if you no longer wish to use it. :::note The refresh rate will not be saved unless it is selected in the [edit mode](./edit-mode.md) of the dashboard. If it was selected in the view mode, the refresh rate will not be saved when refreshing the dashboard page manually or switching to another page in between. ::: - **Alerts**: For dashboards within a collection, create and manage alerts for reports inside the dashboard. ![process performance overview](./img/dashboard-viewMode-monitorFeatures.png) - **Description**: Displayed beneath the dashboard name, the description can be expanded or collapsed using the **More/Less** button for longer texts. ![dashboard description](./img/dashboard-showMoreDescription.png) - **Sharing**: To share or embed the dashboard, use the **Share** button. After turning the **Enable sharing** switch on, a link is generated for those without Optimize access. Include applied filters in the shared version by enabling the **Share with current filters applied** checkbox. If the checkbox is not checked, the shared dashboard will include the default filters if any have been set. ![sharing](./img/dashboard-sharingPopover.png) :::important Dashboard shared versions only allow you to view the dashboard. You cannot alter it or use any other Optimize features. To revoke the sharing, disable the share switch. ::: - **Embedding**: Click the **Embed Link** button to copy a code to paste into your webpage. Everyone that views the webpage can then see the content of the dashboard. To hide the header of the shared dashboard or specific part of it, add the following parameter to the share URL: `header : titleOnly / linkOnly / hidden` For example, to completely hide the header from the shared dashboard, add `header=hidden` as shown: `http://?header=hidden` ## Interacting with reports To see more details about the report on the dashboard, interact with the reports. The kind of interaction always depends on the report itself. If the interactions do not suffice to get the desired information, or you want to edit the report, directly access the report by clicking on its title. ## Adding filters in view mode In the dashboard view mode, there is a **Filters** button which opens a panel that shows all filters available for this dashboard. More filters can be made available in the dashboard edit mode. If the dashboard editor checked the **Allow viewer to add filter values** box for assignee, candidate group, or variable filters, dashboard viewers can add their own values to filter by. ![filters in view mode](./img/filter-viewMode.png) Filters apply to all process reports on the dashboard. If a report already has filters set, they will be combined with the dashboard filter. For example, if a report has a filter to only show running instances and a dashboard filter for suspended instances is set, the report will only show instances that are both running and suspended. Dashboard filters are not applied to decision reports, external websites, or text tiles. Variable filters are only applied to reports whose process definition includes the variable. Otherwise, the filter is ignored for that report. Other dashboard filters and filters defined directly on the report are still applied. --- ## What is Optimize? :::note New to Optimize? Visit our introductory guide to [Optimize](/components/optimize/improve-processes-with-optimize.md) to get started. ::: Camunda 8 is built to handle three key aspects of process automation: - Design - Automate - Improve Users can design process flows through our [Modeler](/components/modeler/about-modeler.md). In a production scenario, users can deploy through Desktop Modeler, Web Modeler, or programmatically. A user can use [Tasklist](/components/tasklist/introduction-to-tasklist.md) to review and complete tasks, and [Operate](/components/operate/operate-introduction.md) to view and analyze process instances. Beyond these design and automate cornerstones lies an important component to leverage our process data and analyze areas for improvement: Optimize. Geared toward business stakeholders, Optimize offers business intelligence tooling for Camunda enterprise customers. By leveraging data collected during process execution, users can collaboratively access reports, share process intelligence, analyze bottlenecks, and examine areas in business processes for improvement. ![process performance dashboard](./img/dashboard-sharingPopover.png) As process instances run through the server, Optimize makes REST API calls to the Camunda server, collecting new historical data and storing it in its Elasticsearch database. This enables users to independently analyze reports and dashboards, gaining actionable insights without affecting runtime. Optimize goes beyond traditional business intelligence tools, guiding users toward continuous process improvement by understanding their goals. It facilitates the rapid identification of constraints within an individual's or organization's system. Explore Optimize for a closer look at process performance dashboards, review heatmaps for instance durations, and visualize activity instances compared to total process instances. In the following sections, we'll guide you through using and analyzing Optimize. --- ## About the Orchestration Cluster The Orchestration Cluster is the core component of Camunda 8, powering the automation and orchestration of processes. It includes: - Zeebe as the workflow engine. - Operate for monitoring and troubleshooting process instances running in Zeebe. - Tasklist for interacting with user tasks (assigning, completing, and so on). - Admin for managing the integrated authentication and authorization. - APIs for interacting with the Orchestration Cluster programmatically. ## Zeebe Zeebe is the process automation engine powering Camunda 8. - [Learn more about Zeebe](/components/zeebe/zeebe-overview.md) ## Operate Operate is a tool for monitoring and troubleshooting process instances running in Zeebe. - [Learn more about Operate](/components/operate/operate-introduction.md) ## Tasklist Tasklist is a ready-to-use application to rapidly implement business processes alongside user tasks. - [Learn more about Tasklist](/components/tasklist/introduction-to-tasklist.md) ## Admin Admin is responsible for managing authentication, authorization, and access-related entities within the Orchestration Cluster. - [Learn more about Admin](/components/admin/admin-introduction.md) --- ## Backpressure Warning :::note We recommend reducing the rate of requests. When backpressure is active, the broker may reject any request except _CompleteJob_ RPC and _FailJob_ RPC. These requests are allowed during backpressure and are always accepted by the broker even if it is receiving requests above its limits. ::: --- ## Connector Task You can apply a connector to a task or event via the append menu. For example: - **From the canvas**: Select an element and click the **Change element** icon to change an existing element, or use the append feature to add a new element to the diagram. - **From the properties panel**: Navigate to the **Template** section and click **Select**. - **From the side palette**: Click the **Create element** icon. ![change element](./img/change-element.png) After you have applied a connector to your element, follow the configuration steps or see [using connectors](/components/connectors/use-connectors/index.md) to learn more. --- ## Create Api Credentials To interact with your Camunda 8 cluster, you'll use the Camunda client. First, you'll need to create credentials. 1. The main page for Camunda Hub should be open on another tab. Use Camunda Hub to navigate to your clusters either through the navigation **Clusters** or by using the section under **View all** on the **Clusters** section of the main dashboard. Click on your existing cluster. This will open the **Overview** for your cluster, where you can find your **region Id** and **cluster Id** (in your client credentials under the **API** tab within your cluster). You will need this information later when creating a worker in the next section. :::note If your account is new, you should have a cluster already available. If no cluster is available, or you’d like to create a new one, click **Create New Cluster**. ::: 2. Navigate to the **API** tab. Click **Create**. 3. Provide a descriptive name for your client like `microservice-worker`. For this tutorial, the scope must be the Orchestration Cluster scope. Click **Create**. 4. Your client credentials can be copied or downloaded at this point. You will need your client ID and your client secret when creating a worker in the next section, so keep this window open. Once you close or navigate away from this screen, you will not be able to see them again. --- ## Create Cluster To deploy and run your process, you must create a cluster in Camunda 8. 1. To create a cluster, navigate to **Camunda Hub** by clicking the square-shaped icon labeled **Camunda components** in the top left corner, and click **Camunda Hub**. 2. Click the **Clusters** tab, and click **Create new cluster**. 3. Name your cluster. For the purpose of this guide, we recommend using the **Stable** channel and the latest generation. Additionally, select your region. Click **Create cluster**. 4. Your cluster will take a few moments to create. Check the status on the **Clusters** page or by clicking into the cluster itself and looking at the **Applications** section. Even while the cluster shows a status **Creating**, you can still proceed to begin modeling. :::note Zeebe must show a status of **Healthy** to properly deploy your model. ::: ## Troubleshooting If **Create new cluster** is disabled, consider these explanations: - Your organization is on a trial plan and you have already created a cluster. In this case, you cannot create another cluster, because only one cluster is included in the trial plan. - Your billing reservations do not allow any more clusters. You must increase the [reservations](/components/hub/organization/manage-organization-settings/manage-plan/update-billing-reservations.md) to create more clusters. If you do not have the necessary rights, contact an admin or the owner of the organization. --- ## Get started with RPA Use the RPA worker and Camunda Modeler to create, test, and automate RPA scripts. ## About the RPA worker The RPA worker is available on all major platforms (Windows, Linux, and macOS). This lets you automate applications on their native platforms, which is typically Windows. For console applications or browser automation, you can use a lightweight distribution such as this [Docker image](../../../self-managed/deployment/docker/). ## Create your first script Get started with RPA by creating your first RPA script. [Camunda Modeler](/components/modeler/about-modeler.md) offers an interface for editing and testing your scripts: 1. **Download Camunda Modeler**: [Download the latest version of Camunda Modeler](https://camunda.com/download/modeler/). Because RPA scripts run locally, we recommend testing using [Desktop Modeler](../../modeler/desktop-modeler/). 2. **Open the RPA script editor**: Open Desktop Modeler and navigate to the RPA script editor under **Testing**. 3. **Write your RPA script using Robot Framework**: Use the editor to create your first RPA script. Scripts use the [Robot Framework](https://robotframework.org/) syntax. ## Test your script Once you have written your script, you can test it on a local RPA worker. 1. **Start the RPA worker**: 1. Download the latest version of the [RPA worker](https://github.com/camunda/rpa-worker/releases). 2. Unpack the `rpa-worker_*.zip` file. The zip archive contains the worker executable and an example configuration file. 3. Start the worker by running the executable. 2. **Check Desktop Modeler**: Ensure the RPA worker is connected to Desktop Modeler. The worker should automatically connect. If not, click on the connection status to display additional configuration options. 3. **Test the script**: 1. Click the test tube (🧪) icon in the footer of Desktop Modeler to open the run dialog. Add any variables required by the process in JSON format. Once you start the execution, the execution tab will open. 2. Review the execution log and the variables created during the script execution within Modeler. ## Automate execution Once you are happy with your script and have tested it locally, you can start automating it with Camunda. ### Link RPA task to BPMN 1. **Deploy the RPA file**: 1. If you have not already, [set up client connection credentials](../../hub/organization/manage-clusters/manage-api-clients/#create-a-client) for your Modeler. 2. Assign the **RPA role** to the client in the [Orchestration Cluster Admin (formerly Orchestration Cluster Identity)](../../admin/role/#assign-client-to-a-role). 3. Deploy your RPA script file by clicking on the rocket (🚀) icon in Modeler. 4. Note the ID of your RPA script. You will need this in the next step. 2. **Add RPA to your process**: 1. In Camunda Modeler, create a new BPMN file or open an existing one. 2. Add a new task and change the type to an RPA connector. 3. Configure the task with the script ID from the previous step. Add any input mappings required for your script to work. 3. **Deploy and run the process**: 1. Deploy the BPMN model with the configured RPA task by clicking on the rocket (🚀) icon in Modeler. 2. Start an instance of your process. ### Connect worker to Zeebe The last step is to configure the RPA worker to pick up the jobs from Camunda. 1. **Create credentials for the worker**: 1. Create the necessary worker credentials in Camunda Hub. You can follow the same steps as for the Modeler credentials. Give your new client the `Zeebe` and `Secrets` scopes. 2. Add the generated credentials to your `application.properties` in the same directory as your RPA worker executable. 2. **Restart the worker**: If your worker is still running, restart it to apply the new credentials. The RPA worker should now be connected and ready to execute scripts from Zeebe. ## Interact with the process Now that you have integrated your first script, it can be part of a larger BPMN process. The main interaction between the script and your process will be the variables and documents. ### Variables Process variables will be mapped to robot variables automatically. Use the `Camunda` library and the `Set Output Variable` keyword to set return variables. In this example, the input would be the following: ```Robot *** Settings *** Library Camunda *** Tasks *** Log X Log Process variable 'x' is set to ${x} Set Output Variable result We logged x ``` ### Documents :::note Multiple Camunda components can create documents. Visit our [concepts page](/components/document-handling/getting-started.md) to learn how Camunda handles binary data. ::: Documents managed by Camunda can be consumed or created by an RPA script. Use `Download Documents` to resolve a document descriptor to a file and `Upload Documents` to create a document descriptor from a file. The script below downloads a file, appends a line, and uploads the document with the same variable name: ```Robot *** Settings *** Library Camunda Library Camunda.FileSystem *** Tasks *** Log Operation ${path}= Download Documents ${operationLog} Append To File ${path} new Line, appended by RPA script Upload Documents ${path} operationLog ``` ### Handling exceptions You can handle problems in your tasks in two ways: exceptions and errors. See Camunda [best practices](/components/best-practices/development/dealing-with-problems-and-exceptions.md) to understand which strategy is best for your case. #### Incidents If your RPA script runs into an unexpected error during execution, this error (alongside the output) will be reported to Zeebe. If the job retries are exceeded, an [incident](/components/concepts/incidents.md) will be created in [Operate](/components/operate/operate-introduction.md). To ensure your environment is always clean and all open applications are closed, create a cleanup step and tag it as `[Teardown]`. See the [Robot Framework documentation](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#user-keyword-setup-and-teardown) for details on setup and teardown. ``` *** Settings *** Library Camunda Library Camunda.Browser.Selenium *** Tasks *** Main Perform Work [Teardown] Cleanup *** Keywords *** Perform Work Open Browser about:blank Fail Cleanup # Close your application, even when encountering errors Close All Browsers ``` #### BPMN errors If you encounter an error that should be handled as a BPMN error, you can use the `Throw BPMN Error` keyword. Instead of creating an incident, this will create a [BPMN error](/components/best-practices/development/dealing-with-problems-and-exceptions.md#handling-errors-on-the-process-level). :::note A BPMN error cannot be caught in the script. It always stops the script execution and initiates the teardown procedure. ::: ```robot *** Settings *** Library Camunda *** Tasks *** Log Operation Throw BPMN Error MY_ERROR_CODE We encountered a business error [Teardown] Log Teardown is still executed ``` ### Shared script resources Multiple script files are not supported. Each task should be contained within a single script. You can use [pre-run and post-run scripts](/components/rpa/production.md#pre--and-post-run-scripts) for environment setup and cleanup. --- ## Robotic process automation (RPA) Use Robotic process automation (RPA) to integrate legacy systems without APIs into your Camunda processes. ## About RPA RPA allows you to automate manual, repetitive tasks by interacting with legacy mainframe applications, desktop applications such as ERP systems, or websites that do not expose APIs. Camunda uses RPA to integrate legacy applications without accessible APIs into your process orchestration. ## Get started with RPA Camunda RPA uses a standalone RPA worker and Robot Framework–based scripts. This setup lets you build and run RPA scripts that integrate with your existing Camunda components. :::tip Before you begin, review the [RPA known issues](https://github.com/camunda/rpa-worker/discussions/categories/known-issues) to check environment compatibility. ::: To start building RPA scripts, see [Get started with RPA](./getting-started.md). ## Further resources - Learn how to configure your RPA workers for [production use cases](./production.md). - For more details or to leave feedback, see the [RPA product roadmap](https://roadmap.camunda.com/c/212-rpa-1-0). --- ## RPA production setup Use the RPA worker’s production configuration options to run RPA scripts reliably at scale. ## Configuration options ### Transition from a development setup When moving from development to production, consider the following: - **Disable the local sandbox**: If the worker should only accept scripts from Zeebe and not from Desktop Modeler, disable local execution by setting `camunda.rpa.sandbox.enabled=false`. - **Install required third-party tools**: Install any external tools your scripts rely on so the worker can access them. - **Add tags to your workers and scripts**: Tag workers based on their capabilities, such as operating systems or installed applications. See [Labels](#labels) for details. ### Use secrets When running an RPA worker with Camunda SaaS, you can access [connector secrets](/components/connectors/use-connectors/index.md#using-secrets). To do this: 1. [Create client credentials](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client) with both the `Orchestration Cluster API` and `Administration API - Resource: Secrets` scopes. 2. Use them in the worker config by adding the secrets endpoint to your `rpa-worker.properties` file: ```properties camunda.rpa.zeebe.secrets.secrets-endpoint=https://cluster-api.cloud.camunda.io ``` In the RPA script, your secrets are stored in the `${secrets}` variable. You can reference a secret like `MY_API_KEY` with `${secrets.MY_API_KEY}`. ### Labels Use tags and labels to differentiate worker capabilities. 1. In the `rpa-worker.properties`, add: ```properties camunda.rpa.zeebe.worker-tags=accounting-system ``` 2. If you also want the worker to work on unlabeled tasks, use: ```properties camunda.rpa.zeebe.worker-tags=default,accounting-system ``` 3. Add a label to your script when configuring the RPA task in your diagram. Labels describe capabilities. If you want your worker to only pick up a specific script, use a unique label on both the worker and the RPA task. If no label is defined, both the task and worker will use the label `default`. ### Pre- and post-run scripts Some scripts require environment setup before they run. To use pre- or post-run scripts: 1. Create and deploy separate RPA scripts. 2. Reference them in the properties panel of the RPA task. :::note The worker removes the job’s working directory after the job completes. ::: ### Timeouts To set timeouts: - **On the RPA task (recommended)**: Set the timeout when configuring the RPA task in your diagram. - **In the worker**: Use the default timeout in `rpa-worker.properties` as a fallback. ### Concurrent jobs To enable concurrent jobs, set `camunda.rpa.zeebe.max-concurrent-jobs` in the worker config. Enable this only if your scripts, such as browser automation, can run safely in parallel. ### Additional libraries To install additional dependencies: 1. Create a `requirements.txt` file listing required packages. 2. Set `camunda.rpa.python.extra-requirements=extra-requirements.txt` in the properties file. 3. Restart the worker to install them. Use [labels](#labels) to ensure scripts run only on compatible workers. For example, to use Playwright: ```txt ## requirements.txt robotframework-browser ``` ```properties ## application.properties camunda.rpa.python.extra-requirements=extra-requirements.txt camunda.rpa.zeebe.worker-tags=default,playwright ``` ## Installation and setup guide An RPA worker is a specialized job worker that runs outside the main Camunda Orchestration Cluster. ### Prerequisites RPA workers are supported on Windows, Linux, macOS, and in Docker for headless automation. #### Hardware requirements The RPA worker runs on bare metal, virtualized, or containerized systems. Use hardware suitable for your automated applications. #### Software requirements The RPA worker is a standalone binary. | Operating system | Required software | Optional software | | ---------------- | ----------------- | ----------------- | | Windows | RPA worker | - | | Linux and macOS | RPA worker | Python 3.12 + pip | #### Network configurations - Ensure connectivity to your Camunda cluster. - Allow internet access if downloading external libraries. ### Installation and configuration This section describes how to set up the RPA host machine. #### Scaling and operation Each machine hosts one RPA worker. For scalable workloads: - Use VMs to quickly spin up new workers. - Use [max-concurrent-jobs](https://github.com/camunda/rpa-worker/?tab=readme-ov-file#configuration-reference) if safe for your use case. #### Setting up a VM Use virtualization to create scalable and repeatable worker deployments. ##### Create a template VM 1. Start with a clean Windows VM. 2. Install and configure the RPA worker. 3. Install all required third-party applications. 4. Test connectivity by running a process in Camunda. 5. Add the RPA worker to run **automatically on startup**. 6. **Configure Windows autologon**. 7. Disable screen saver, sleep, and lock. 8. Set the VM's time zone to match business requirements. 9. Save the configured VM as a **template**. 10. Keep a separate local administrator account and password in escrow for emergency access, and audit all RDP and console logons. ##### Monitoring and scaling To scale using your VM template: 1. **Provision new VMs** from the template. 2. Start the VMs to allow them to connect to Zeebe and begin executing tasks. 3. Use **Operate** and **Optimize** to monitor task execution, wait times, and incidents. If jobs are waiting too long, create additional VMs. Operational tips: 1. **Script handling and versioning**: RPA workers fetch the latest script version automatically. 2. **Labels**: Use them to route tasks to the right worker types. 3. **Maintenance and monitoring**: - Enable OS updates during a maintenance window. - Take regular snapshots. - Monitor health, disk space, and RPA service status. - Surface alerts into your monitoring tools. ## FAQ **My RPA task is never picked up.** Ensure your RPA worker is connected to the correct Zeebe instance and has the correct [label](#labels) configured for the task. **My first script run succeeds, but any subsequent runs fail. I always need to restart the machine.** Your script might not clean up properly. Use [teardown scripts](./getting-started.md#incidents) to close apps after execution. **How do I handle errors and exceptions within RPA scripts?** Use setup and teardown steps in the Robot Framework. Optionally, use `Throw BPMN Error` for BPMN-specific handling. --- ## Auto-updates Camunda 8 SaaS customers can enable auto-updates. When enabled, the cluster is updated once a new patch release is available. Auto-updates can be enabled during [cluster creation](/components/hub/organization/manage-clusters/create-cluster.md) or in the **Settings** tab. Depending on your [role](/components/hub/organization/manage-members/manage-users.md), this may appear grayed out in the **Settings** tab. For Camunda 8 SaaS, auto-updates are only for patch releases (x.y.**z**, where **z** is a patch release). Auto-updates are only applied when a cluster is running. If a cluster is sleeping during the update cadence, the auto-update is not applied automatically. However, the update is still available for a manual update in Camunda Hub. Minor updates (x.**y**.z, where **y** is a minor release) are not eligible for auto-updates and require manual steps to initiate. Depending on your [role](/components/hub/organization/manage-members/manage-users.md), you may see that an update is available, but no **Update cluster** button. Contact your organization owner or admin to update your cluster. --- ## Backups(Saas) Camunda Enterprise You can use the backup feature of Camunda 8 SaaS to regularly back up the state of all of its components (Zeebe, Operate, Tasklist, and Optimize) with _zero downtime_. In case of failures that lead to data loss, you can request to restore the backup. A Camunda 8 SaaS backup consists of a data backup of Zeebe, Operate, Tasklist, Optimize, and the backup of exported Zeebe records in Elasticsearch. Since the data of these applications depend on each other, the backup must be consistent across all components. Therefore, the backup of a Camunda 8 cluster is taken as a whole. With backups, you can capture snapshots of your data and applications while they are actively in use, resulting in zero downtime or disruption to your operations. Backups are designed specifically for disaster recovery purposes, and should not be used for archival of process data. :::caution Backups are created and managed on a per-cluster basis. It is important to be aware that deleting a cluster will also delete all associated backups. Exercise caution when deleting clusters to avoid unintended loss of backups. ::: > Your cluster generation needs to be greater or equal to `8.2.4` to support backups. ## Backup location When you create a cluster in Camunda 8 SaaS, you must specify a region for that cluster. You also need to specify where the backups for that cluster will be located: - By default, the backups will be located in the same region as the cluster. - For disaster recovery reasons, you can select a "dual-region" backup location. Backups will be automatically replicated in the secondary region to give you better protection in case the primary region experiences disruption. Dual-region backup is offered at no additional cost. ## Manual backup Manual backups refer to the user-initiated process of creating a consistent snapshot of the state of all system components, including Zeebe, Operate, Tasklist, and Optimize. These backups are managed on a per-cluster basis and are primarily designed for disaster recovery purposes. ### Retention and rate limits To ensure system stability, backup operations are subject to rate limits. Specifically, you can perform a backup operation every 15 minutes. However, users can delete an existing backup to create a new one before the rate limit period ends. The system retains the three most recent completed backups per cluster. Failed backup attempts do not count towards the retention count. When a new backup is successful and the retention count is reached, the oldest backup is automatically deleted. ## Scheduled backups Scheduled backups are created periodically (e.g daily, weekly). They are configured to run automatically on the scheduled time. ### Retention A backup schedule retains the last three successful and failed backups. Failed backups are retained to allow further root-causing why the backup failed. If a backup fails, it is not retried immediately as the failure can lead to instability. :::note If you require more retained backups or more frequent backups, [contact Camunda support](https://camunda.com/services/support/) to discuss your specific needs. ::: ## Programmatic access The backup operations can be performed programmatically using the Administration API. This provides the flexibility to seamlessly integrate backup-related tasks with your existing systems and automation workflows. For detailed information on using the API, refer to the [Administration API reference](/apis-tools/administration-api/administration-api-reference.md). ## Restore To restore your Camunda 8 cluster from a backup (and for any further assistance in general), [contact Camunda support](https://camunda.com/services/support/) to request a restore for your backup. Our support team will assist you with the restoration process and guide you through the necessary steps to recover your cluster from the backup. --- ## Encryption at rest using external encryption keys Learn how to configure encryption at rest for your Camunda 8 SaaS Orchestration cluster using AWS KMS. ## Prerequisites | Requirement | Description | | --------------------- | --------------------------------------------------------------------------- | | AWS account | Access to an AWS account with AWS KMS permissions. | | AWS KMS permissions | Ability to create and manage AWS KMS keys and attach key policies. | | Cluster region | The AWS KMS key must reside in the same AWS Region as your Camunda cluster. | | Technical familiarity | Some experience with the AWS Management Console, IAM roles, and AWS KMS. | :::warning Important - Deleting or disabling your AWS KMS key will make your cluster and data inaccessible. To understand how Camunda behaves if a key is disabled, deleted, or its policy is changed, see [key state behavior](/components/saas/byok/key-state-behavior.md). - Key management is fully customer-side in AWS KMS. Camunda cannot rotate keys. ::: ## Step 1: Create a Camunda 8 SaaS Orchestration cluster 1. Sign in to the [Camunda Hub](https://console.camunda.io/). 2. Navigate to the **Cluster** section and click **Create new cluster**. 3. Select an AWS Region for your cluster. 4. Choose **Single region** or **Dual region backup**. - Dual region requires one key per region; keys can be separate. 5. Under **Encryption at rest**, choose **External**. ![external option encryption at rest](./img/external-encryption.png) 6. Click **Create cluster**. After creation, note the **AWS Role ARN** displayed in the Console for your cluster. The ARN uses the following format: ``` arn:aws:iam:::role/c8-cluster/c8-apps- ``` ## Step 2: Create and configure an AWS KMS key You can create the key either via AWS CLI or manually in the AWS Management Console. ### Option A: Create the key using AWS CLI ![create key using AWS CLI](./img/create-key-cli.png) We provide automated scripts to create the necessary AWS KMS key(s) with the correct policy and permissions. Choose the option that matches your backup configuration. #### Single-region backup Use this script to create a single AWS KMS key in the same Region as the cluster. **What the script does:** - Creates an AWS KMS key with the required policy for Camunda access. - Sets up an alias for easier key management. - Outputs the key ARN to provide to Camunda. **Instructions:** 1. Download [create-byok-kms-key-single-region.sh](https://raw.githubusercontent.com/camunda/camunda-docs/refs/heads/main/docs/components/saas/byok/downloads/create-byok-kms-key-single-region.sh). 2. Modify the following values at the top of the script: - `AWS_ACCESS_KEY_ID` - `AWS_SECRET_ACCESS_KEY` - `AWS_SESSION_TOKEN` (if using temporary credentials) - `YOUR_ACCOUNT_ID` - `ALIAS_NAME` (optional) 3. Make the script executable and run it. 4. Copy the outputted key ARN and provide it to Camunda. #### Dual-region backup Use this script to create a multi-Region primary key in the cluster’s Region and a replica key in the backup Region. **What the script does:** - Creates a multi-Region primary key and replica key. - Applies the correct policies to both keys. - Outputs both key ARNs to provide to Camunda. **Instructions:** 1. Download [create-byok-kms-key-multi-region.sh](https://raw.githubusercontent.com/camunda/camunda-docs/refs/heads/main/docs/components/saas/byok/downloads/create-byok-kms-key-multi-region.sh). 2. Modify the same variables as above. 3. Make the script executable and run it. 4. Copy the two outputted key ARNs and provide them to Camunda. :::note Alternative For dual-region setups, you can also run the single-region script twice—once in the cluster’s Region and once in the backup Region. Make sure to modify the `REGION` variable before creating the second key. ::: ### Option B: Manual key creation in AWS Console ![manual key creation in AWS Console](./img/manual-key-creation.png) #### Single-region backup 1. **Sign in to the AWS Management Console** - Navigate to the AWS KMS service and select the correct Region. 2. **Create a customer managed key** - Click **Create key**. - Choose **Symmetric** and **Encrypt and decrypt** usage. 3. **Add labels** - Add an alias (for example, `camunda-saas-byok`). - Add a description (for example, `AWS KMS key for Camunda SaaS BYOK`). 4. **Define key administrators** - Select IAM users or roles that will administer the key. 5. **Define key usage permissions** - Skip this step; permissions are configured in the next step. 6. **Edit key policy** - Switch to policy view and replace the existing policy with the [provided key policy](https://github.com/camunda/camunda-docs/tree/main/docs/components/saas/byok/downloads/aws-kms-key-policy.json). - Replace `` and `` with your values. 7. **Finish and copy the ARN** - Click **Finish** and copy the key ARN to use in Camunda Hub. #### Dual-region backup You can either create a multi-Region key and replica or create two single-Region keys. ##### Method A: Multi-Region key (recommended) 1. Follow the single-region steps, selecting **Multi-Region key** under **Advanced options**. 2. After creating the primary key in the cluster’s Region, go to **Regional replicas** and click **Create replica key**. 3. Select the Region for the replica and confirm. The Region should be the same as the backup Region. 4. Copy both key ARNs and provide them to Camunda. ##### Method B: Two single-Region keys 1. Create a key in the cluster’s Region using the single-region steps. 2. Repeat the process in the backup Region using a different alias (for example, `camunda-saas-byok-replica`). 3. Provide both key ARNs to Camunda. ### Sample key policy Replace `` with the **AWS Role ARN** from Step 1, and `` with your AWS account ID.
View sample key policy JSON ```json { "Version": "2012-10-17", "Statement": [ { "Sid": "Enable IAM user permissions", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam:::root" }, "Action": "kms:*", "Resource": "*" }, { "Sid": "Allow Camunda tenant IAM role basic key access", "Effect": "Allow", "Principal": { "AWS": "" }, "Action": [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:DescribeKey", "kms:GenerateDataKey*" ], "Resource": "*" }, { "Sid": "Allow Camunda tenant IAM role to create grants for provisioning encrypted EBS volumes", "Effect": "Allow", "Principal": { "AWS": "" }, "Action": ["kms:CreateGrant", "kms:ListGrants", "kms:RevokeGrant"], "Resource": "*" } ] } ```
:::warning Key policy guidance - Don’t restrict the Camunda cluster **role** from required AWS KMS actions. - Key rotation is managed in AWS KMS; Camunda cannot rotate keys. - Revoking access immediately breaks the cluster. ::: ## Step 3: Associate the AWS KMS key with your Camunda cluster 1. Return to **Camunda Hub**, and locate the **AWS KMS key ARN** input field. ![KMS AWS key ARN input field](./img/aws-key-arn.png) - For dual region, two fields will be available—enter the correct key for each Region. 2. Paste your AWS KMS key ARN(s) from Step 2. 3. Confirm and apply. Camunda provisions storage using your key for: - Document handling storage - Backup storage - Orchestration cluster persistent disks - Elasticsearch persistent disks :::note Once a key is applied, it cannot be edited or replaced. To change keys, you must create a new cluster. ::: ## Step 4: Verify encryption and logging - In **Camunda Hub**, check the cluster details **Encryption at rest** tab to confirm the **AWS KMS key ARN** is applied correctly. ![cluster details page](./img/cluster-details-page.png) - In AWS, verify key usage: 1. Navigate to **Customer managed keys**. 2. Select your key and view **Key policy** and **Key usage** tabs. 3. Review **Recent activity** to confirm operations (Encrypt, Decrypt, GenerateDataKey). ### Monitor AWS KMS usage - **AWS CloudTrail** logs all AWS KMS operations. - **Amazon CloudWatch** can trigger alarms for: - Key deletion or disabling - Unauthorized access attempts - Policy or grant modifications - Regularly review logs to detect unauthorized activity. - Optionally, integrate with **Amazon EventBridge** for event-based monitoring and automation. :::warning Monitoring reminder You are responsible for monitoring key usage and access logs within your AWS account. Use AWS CloudTrail, Amazon CloudWatch, and Amazon EventBridge to detect misconfigurations or unauthorized access. ::: ## Additional considerations - **Key rotation**: Enable [automatic rotation](https://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html) or rotate manually in AWS KMS. - **Cost**: Using AWS KMS keys incurs storage and management charges in your AWS account. See the [BYOK cost implications](/components/saas/byok/index.md#cost-implications). - **Failure scenarios**: Deleting keys or revoking permissions makes cluster data inaccessible. See [troubleshooting steps](/components/saas/byok/faq-and-troubleshooting.md#troubleshooting-external-encryption-keys). :::note Reference For more information, see the [AWS KMS documentation](https://docs.aws.amazon.com/kms/latest/developerguide/overview.html). ::: --- ## FAQ & troubleshooting Frequently asked questions and troubleshooting guidance for encryption at rest, encryption key types, and external AWS KMS encryption keys in Camunda 8 SaaS. ## General questions ### What is encryption at rest? Encryption at rest protects data on storage media (disks or backups) from unauthorized access. :::note Applies to both Orchestration clusters and Web Modeler in Camunda 8 SaaS. ::: ### Which encryption options are available? - Provider-managed (default): Cloud provider keys. - Camunda-managed software key: Uses Google KMS at software protection level (FIPS 140-2 Level 1). - Camunda-managed hardware key: Uses Google KMS HSM (FIPS 140-2 Level 3). - External key: Customer-supplied AWS KMS key (AWS only currently). Full comparison: [encryption at rest](/components/saas/encryption-at-rest.md) ### When can I choose the encryption type? Only during cluster creation. It cannot be changed later. ### Is encryption at rest enabled by default? Yes. All clusters use provider-managed encryption by default. ## Camunda-managed keys - Software vs. hardware: Software uses FIPS 140-2 Level 1, hardware uses FIPS 140-2 Level 3. Both support zero downtime rotation. - Backups always use provider-managed keys. ## External encryption keys - Use your own AWS KMS key to encrypt cluster data. You control rotation and revocation and are responsible for monitoring via AWS CloudTrail and Amazon CloudWatch. - Supported on enterprise plans only. - Revoking access immediately blocks cluster access; a new key or restored access is required. - Camunda does not store your key. Setup instructions: [external encryption setup guide](/components/saas/byok/aws-kms-setup.md) ## Other questions - Performance: Minimal impact; handled by AWS KMS. - Per-cluster keys: Supported. - Encryption in transit: TLS enforced. - Cost: AWS KMS key storage and management charges apply in your AWS account. See [cost implications](/components/saas/byok/index.md#cost-implications). ## Troubleshooting external encryption keys | Issue | Possible cause | Resolution | | --------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Cluster cannot access AWS KMS key | Key policy does not grant the Camunda cluster AWS Role access | Update the AWS KMS key policy with the correct AWS Role ARN from Camunda Hub. | | Encryption/decryption errors | Key disabled, deleted, or in wrong Region | Re-enable, restore, or create a new key in the correct AWS Region. | | CloudTrail does not show activity | AWS CloudTrail not enabled or retention too short | Enable AWS CloudTrail in the cluster Region and store logs beyond 90 days. [View CloudTrail events](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/view-cloudtrail-events.html) | | Key rotation issues | Cluster encryption update not supported | Create a new key and associate it with a new cluster. Verify encryption settings before use. | :::note For details on how Camunda responds when an external KMS key becomes disabled, deleted, or misconfigured, see [key state behavior](/components/saas/byok/key-state-behavior.md). ::: :::note Support For persistent issues with key policies, Region, or key status, contact [AWS support](https://docs.aws.amazon.com/awssupport/latest/user/case-management.html). For Camunda-specific cluster provisioning issues, contact [Camunda support](https://camunda.com/services/support-guide/). ::: --- ## Encryption Camunda 8 SaaS encrypts all cluster data at rest. By default, encryption uses cloud provider–managed keys. For stricter compliance or control, you can configure **Bring Your Own Key (BYOK)** with **AWS KMS**, available for clusters hosted in AWS Regions. ## Encryption overview With BYOK, Camunda 8 SaaS uses your customer-managed key stored in your AWS account. You control the key’s lifecycle—creation, access, rotation, and logging—while Camunda handles encryption and decryption operations. | Category | Details | | ----------------- | ------------------------------------------------ | | Availability | AWS-hosted clusters only | | Encrypted storage | Document, backup, Zeebe, and Elasticsearch disks | | Setup | Configure the key during cluster creation | | Rotation | Managed in AWS KMS (not through Camunda) | | Logging | Key usage visible in AWS CloudTrail |
Encryption details for beginners **Encryption at rest** protects stored data from unauthorized access. Camunda 8 SaaS supports three encryption models: | Type | Managed by | Description | | ----------------------- | ---------- | ---------------------------------------------------------- | | Camunda-managed | Camunda | Default encryption, handled automatically | | AWS managed | AWS | Encryption in your account, but AWS controls key lifecycle | | Customer-managed (BYOK) | You | You create, own, and manage the key in your AWS account | Industries such as finance, healthcare, and government often require this level of control for compliance reasons. With BYOK, you maintain visibility through **AWS CloudTrail** and **Amazon CloudWatch**, apply your own rotation policies, and centralize audit logs in your AWS account.
## Responsibilities | Owner | Responsibility | | -------- | -------------------------------------------------------- | | Customer | Create and manage the AWS KMS key | | Customer | Ensure the key and cluster are in the same AWS Region | | Customer | Configure key policies granting Camunda access | | Camunda | Encrypt and decrypt customer data using the provided key | | Camunda | Surface any key-related errors in Camunda Hub | :::warning Key management If your AWS KMS key is disabled, deleted, or permissions are revoked, your cluster and its data become inaccessible. For details on how Camunda responds when an external AWS KMS key becomes disabled, deleted, or misconfigured, see [key state behavior](/components/saas/byok/key-state-behavior.md). ::: ## Cost implications Using external encryption keys with **AWS KMS** incurs costs directly in your AWS account. Camunda does not charge for the feature itself, but you are responsible for AWS KMS key storage, management, and persistence of logs. | Cost type | Description | Notes | | --------------- | ------------------------------------------------------- | ---------------------------------------------------------- | | KMS key storage | Monthly charge for each AWS KMS key | Depends on AWS Region and key type | | CloudTrail logs | Charges for storing and accessing AWS CloudTrail events | Includes encryption/decryption activity by Camunda cluster | :::note Customers are not charged for key usage operations (for example, encrypting or decrypting) as per [AWS KMS pricing](https://aws.amazon.com/kms/pricing/). ::: :::warning Cost responsibility You are responsible for monitoring AWS KMS key storage, management, and log persistence costs. ::: ### Cost optimization tips - Use separate keys only when necessary to avoid extra storage fees. - Review AWS CloudTrail retention settings to balance compliance and storage cost. --- ## Key rotation and audit logging Learn more about key rotation and audit logging when using AWS BYOK with Camunda 8 SaaS. :::note Disclaimer References to Amazon Web Services (AWS) operations may change over time. Camunda does not control AWS features, APIs, or logs. This documentation may become outdated if AWS updates their services. ::: ## Key rotation With BYOK, you manage your own encryption keys in AWS KMS. Camunda cannot rotate customer-managed keys. Only you can rotate keys in AWS KMS. ### Manual key rotation - Rotating a key in AWS KMS does not change the Key ID. - New key material is generated for future encryption; previously encrypted data remains accessible. - Camunda clusters continue using the same Key ID and do not need reconfiguration. - Camunda does not re-encrypt existing data; you are responsible for key management. To use a new AWS KMS key instead of rotating, contact [Camunda support](https://camunda.com/services/support-guide/) to update cluster settings. :::warning Key rotation caution - Do not delete or disable an old key until the cluster uses a replacement. For details on how Camunda responds when an external KMS key becomes disabled, deleted, or misconfigured, see [key state behavior](/components/saas/byok/key-state-behavior.md). - Improper key management may block data access. - Ensure backup storage and persistent volumes remain accessible. - See [KMS key rotation](https://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html) and [S3 server-side encryption](https://docs.aws.amazon.com/AmazonS3/latest/userguide/serv-side-encryption.html). ::: ### Best practices | Practice | Description | | ----------------------------- | ------------------------------------------------------------------------- | | Separate keys per environment | Use different keys for production, staging, and development clusters. | | Regular auditing | Periodically review AWS KMS key policies and access logs. | | Monitor rotation | Use Amazon CloudWatch or Amazon EventBridge to track key rotation events. | ## Audit logging AWS KMS integrates with AWS CloudTrail to log all key usage. You are responsible for monitoring and persisting these logs. ### What is logged - Encrypt, Decrypt, GenerateDataKey, and CreateGrant operations - Failed attempts due to denied access :::note Log visibility All AWS KMS operations performed by Camunda appear in AWS CloudTrail in your AWS account. ::: ### Audit best practices 1. Enable **AWS CloudTrail** in the cluster Region and persist logs. 2. Set up **Amazon CloudWatch** or **Amazon EventBridge** alerts for key deletion, disabled keys, or access denied events. 3. Review logs regularly for compliance. 4. Use tools like [CloudTrail Lake](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-lake.html) or [Access Analyzer for KMS](https://docs.aws.amazon.com/kms/latest/developerguide/grants.html) to simplify auditing. 5. Export logs to a centralized SIEM if required. :::warning Audit responsibility You are responsible for monitoring and persisting AWS KMS activity and logs. Camunda does not have access to AWS CloudTrail logs in your account. ::: --- ## Key state behavior Learn how Camunda 8 SaaS responds when your external Amazon KMS encryption key (BYOK) becomes unavailable during cluster startup or runtime. This page applies only to **external customer-managed keys** (AWS KMS). For encryption fundamentals, see the [encryption overview](/components/saas/byok/index.md). :::warning If your external encryption key is disabled, deleted, or its permissions are revoked, your cluster becomes inaccessible and may enter a frozen state. Camunda cannot recover encrypted data if the key is permanently deleted. ::: ## Key state summary The table below provides a high-level overview of how different KMS key states affect cluster startup, operation, and data availability. | Key state | Cluster startup | Cluster runtime | Data accessible? | Recovery possible? | | -------------------------- | ----------------- | --------------------------------------------- | ---------------- | ----------------------------- | | **Enabled** | ✔ Starts normally | ✔ Operates normally | Yes | Not needed | | **Disabled** | ❌ Cannot start | ❌ Freezes: no reads/writes, operations hang | No | ✔ Re-enable key | | **Scheduled for deletion** | ❌ Cannot start | ❌ Same as disabled | No | ✔ Cancel deletion + re-enable | | **Permanently deleted** | ❌ Cannot start | ❌ Cluster remains non-functional permanently | No | ❌ No — encrypted data lost | | **Incorrect key policy** | ❌ Cannot start | ❌ Behaves like disabled key | No | ✔ Fix policy | ## What happens when a key becomes unavailable? When a cluster loses access to its KMS key: - All encryption/decryption requests fail immediately. - Zeebe, Elasticsearch, and backup operations **freeze**. - Camunda Hub may still show the cluster as **Healthy**, even though no work can proceed. - Within ~15 minutes, the **Encryption at rest** panel displays: > **External encryption key is not ready** ### Timeline of effects 1. **Immediate (0–1s):** Key becomes inaccessible. 2. **Seconds:** Storage reads/writes hang. 3. **Backup jobs:** Become stuck “In progress” indefinitely. 4. **Suspend/Resume:** Requests appear accepted but never execute. 5. **Console status:** May incorrectly continue to show “Healthy.” 6. **After re-enabling:** Automatic recovery occurs, but timing depends on reconciliation and exponential backoff. ## Component-level impact | Component/feature | Requires key for | Behavior when key unavailable | | ---------------------- | ------------------------------- | ----------------------------------------- | | **Zeebe brokers** | State storage (encrypted disks) | Execution freezes; no read/write activity | | **Elasticsearch** | Persistent disk encryption | Indexing and queries freeze | | **Backups** | Encrypting/decrypting snapshots | Backup requests hang forever | | **Restore operations** | Decrypting snapshots | Restore cannot proceed | | **Document storage** | Encrypting stored files | Document reads/writes freeze | | **Suspend / resume** | Changing cluster state | Request is logged but not executed | ## Behavior by key lifecycle state ### Disabled key - Cluster cannot start. - If cluster was running: - All operations freeze. - Suspend/resume does not complete. - Backup operations get stuck. - Camunda Hub eventually shows: **External encryption key is not ready** **Recovery:** Re-enable the KMS key. Cluster resumes automatically, but recovery time increases the longer the key was disabled. ### Key scheduled for deletion Scheduling deletion automatically **disables** the key. Behavior is identical to a disabled key. **Recovery:** Cancel deletion → Re-enable the key. ### Permanently deleted key Once the AWS deletion waiting period passes: - The key is irrecoverable. - The cluster becomes permanently unusable. - No encrypted data can be recovered. **Recovery:** Not possible. Create a new cluster. ### Incorrect or missing key policy If the KMS policy does not grant Camunda's AWS Role the required permissions: - Cluster cannot start or becomes frozen. - Behavior mirrors a disabled key. **Recovery:** Update the KMS key policy using the Tenant Role ARN displayed in Console. ## Error handling and user-visible messages Currently, Camunda displays a single unified message: ```yaml External encryption key is not ready ``` More granular messaging is planned for future iterations. ## Operator responsibilities and best practices ### Customer responsibilities (BYOK model) - Maintain and monitor the key lifecycle in AWS. - Monitor for disable/delete events (CloudWatch & EventBridge). - Ensure policies remain correct. - Understand that deleting or disabling the key freezes the cluster. ### Recommended monitoring Activate: - **CloudWatch alerts** for key disabled, scheduled deletion, access denied - **EventBridge** for policy changes - **CloudTrail** for Encrypt/Decrypt failures and audit logs ## Related documentation - [Encryption overview](/components/saas/byok/index.md) - [External encryption setup guide](/components/saas/byok/aws-kms-setup.md) - [FAQ & troubleshooting](/components/saas/byok/faq-and-troubleshooting.md) - [Key rotation and audit logging](/components/saas/byok/key-rotation-audit-logging.md) --- ## Data locations Learn more about where your Camunda 8 SaaS data is located and how data is handled. ## About Camunda 8 SaaS data locations - Camunda Hub is hosted in the EU. - You can create Camunda Orchestration Clusters on AWS (Amazon Web Services) or GCP (Google Cloud Platform) in the [region](regions.md) of your choice. :::caution Processing personal data Unless specifically mentioned, Camunda does not process personal data on behalf of its customers. It is the responsibility of you as the customer to decide whether to use Camunda for processing personal data. ::: ## Alerts Camunda 8 [Alerts](/components/hub/organization/manage-clusters/manage-alerts.md) can notify you when process instances stop with an error. | Host location | Data handled | Personal data processing | | :---------------- | :---------------------------------------------------- | :----------------------- | | Belgium, EU (GCP) | Route alerts containing administrative metadata only. | N/A | :::note optional Camunda 8 Alerts are optional. This information only applies if you use Alerts. ::: ## Connector secrets (credentials) This only applies if you want to create [connector secrets](/components/hub/organization/manage-clusters/manage-secrets.md) and are using the Camunda-hosted connector version. Connector secrets are configured and referenced via Camunda Hub. - If you want to control the location where the secrets are stored, you can also [host your own connector runtime](/components/connectors/custom-built-connectors/host-custom-connector.md). - If you want to use your own secret management solution, see [Secrets (Self‑Managed)](/self-managed/components/connectors/connectors-configuration.md). | Host location | Data handled | Personal data processing | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | GCP Secret Manager, [replicated globally](https://cloud.google.com/secret-manager/docs/secret-manager-secrets-comparison) for high availability.From December 2025: Connector secrets for Camunda Orchestration Clusters >= 8.7 created in an AWS region will be stored inside AWS Secret Manager, in the same AWS region as the Camunda Orchestration Cluster only. | Stores credentials required by connectors (API keys, tokens, passwords), not business process data. | Not intended for personal data processing.If you embed personal data in connector secrets, note the global replication of data. You should review if your company has specific data residency requirements, and use connector secrets accordingly. | :::note optional Connector secrets are optional. This information only applies if you use connector secrets. ::: ## Camunda Hub [Camunda Hub](/components/hub/index.md) is used for cluster and organization management as well as hosting the browser-based modeler. | Host location | Data handled | Personal data processing | | :----------------------- | :------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Germany, EU (AWS) | Stores administrative metadata and settings. | Limited to account/authentication data to access Camunda Platform SaaS. It does not include personal data in scope of [Data Processing Agreements](https://legal.camunda.com/). | | Belgium, EU (GCP) | Stores process models (diagrams/designs). | Not intended for personal data processing. | You can use Camunda Hub and/or the [Desktop Modeler](/components/modeler/desktop-modeler/index.md) with Camunda Self‑Managed if you want to control the hosting location. ## Admin [Admin](/components/admin/admin-introduction.md) is managed by Camunda for SaaS. Single Sign-on (SSO) is supported. | Host location | Data handled | Personal data processing | | :---------------------------------- | :------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Germany, EU (Auth0 as part of Okta) | User accounts and authentication metadata. Separate from process payloads. | Limited to account/authentication data by design to access the Camunda Platform SaaS. It does not include personal data in scope of [Data Processing Agreements](https://legal.camunda.com/). | :::info Learn more - [Admin](/components/admin/admin-introduction.md) - [Connect to an identity provider](/components/hub/organization/manage-organization-settings/external-sso.md) ::: ## Orchestration Clusters and backups You can choose a [region](regions.md) in **GCP** or **AWS**. Each [Orchestration Cluster](/components/orchestration-cluster.md) uses a dedicated infrastructure. | Host location | Data handled | Personal data processing | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------- | | Orchestration Clusters are created on AWS or GCP, in one of the available [regions](regions.md).Backups are single‑region by default, in the same region as the Orchestration Cluster.Optionally, you can replicate backups in a [secondary region](regions.md), depending on the chosen primary region. | All data uploaded to Camunda in Orchestration Clusters during customers’ process orchestration (used in Zeebe, Operate, Tasklist, Optimize and Connectors). | Dependent on the data you sent to Camunda in the Orchestration Clusters. Camunda does not process personal data by default. | :::info Learn More - [Backups](/components/saas/backups.md) - [Cluster backups](/components/hub/organization/manage-clusters/cluster-backups.md) ::: ## REST connector (traffic routing) For security reasons, REST API requests made by the [REST connector](/components/connectors/protocol/rest.md) are all routed through a dedicated HTTPS proxy hosted by Camunda in the EU. The REST connector uses either an AWS or a GCP-hosted HTTPS proxy, depending on your chosen Orchestration Cluster cloud provider. As a customer, you can either use the Camunda‑hosted REST connector, or [host your own connector runtime](/components/connectors/custom-built-connectors/host-custom-connector.md) (hybrid mode) to keep traffic in your chosen location. | Host location | Data handled | Personal data processing | | :------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------ | | Belgium, EU (GCP clusters)Germany, EU (AWS clusters) | All data uploaded to Camunda in an Orchestration Cluster and processed specifically by the REST API Connector.Use of the REST API Connector is optional, and depends on the customers’ workflows. | Dependent on the data you send to Camunda in an Orchestration Cluster. Camunda does not process personal data by default. | :::note optional Use of the REST connector is optional. This information only applies if you use the REST connector. ::: --- ## Data retention In Camunda 8 SaaS, the following data retention strategies are implemented. This is necessary as the amount of data can grow significantly overtime. These settings are a balance between performance and usability. ## Default retention time of each application The following time-to-live settings are configured in SaaS for each application. These are the defaults for our production clusters in the Starter and Enterprise plans. - **Orchestration Cluster**: 30 days - **Optimize**: 180 days - **Zeebe**: 7 days - **Usage metrics**: 730 days (2 years) If there are specific requirements for your use-case, [reach out to us](/reference/contact.md) to discuss your data retention needs under an Enterprise plan. For more information on development clusters in the Starter or Professional plans, refer to our [fair usage limits of those plans](https://camunda.com/legal/fair-usage-limits-for-starter-plan/). ## Additional information Visit the [Self-Managed documentation](/self-managed/components/orchestration-cluster/core-settings/concepts/data-retention.md), which describes these data retention concepts in more detail. --- ## Encryption at rest Encryption at rest protects stored data by making it unreadable without the appropriate decryption keys. By default, Camunda 8 SaaS uses a provider-managed encryption key with [Google Cloud Platform (GCP) encryption](https://cloud.google.com/docs/security/encryption/default-encryption). Enterprise customers can choose: - Camunda-managed software or hardware keys (Google KMS) - Bring Your Own Key (BYOK) on AWS for full control Key points: - Encryption type is selected only when [creating a cluster](/components/hub/organization/manage-clusters/create-cluster.md) - Each cluster can have its own key - The key applies to all workloads and persists across updates - View encryption details on the cluster's **Overview** tab under **Cluster Details** :::note Backups use default provider GCP encryption. ::: ## Encryption types | Type | Managed by | Notes | | ------------------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------ | | Provider (default) | Google | FIPS 140-2 validated encryption module (certificate 4407) | | Software key | Camunda | Google KMS software protection; operations in software; FIPS 140-2 Level 1; zero downtime rotation | | Hardware key | Camunda | Google KMS hardware (HSM) protection; FIPS 140-2 Level 3; operations in HSM; zero downtime rotation | | BYOK | Customer | AWS KMS key; FIPS 140-3 Security Level 3 certification; full control over lifecycle, rotation, and revocation; enterprise only | ## Provider encryption key Default option, managed by Google. Uses FIPS 140-2 validated module. :::info Learn more about [Google default encryption](https://cloud.google.com/docs/security/encryption/default-encryption) ::: ## Camunda-managed keys ### Software key - Managed by Camunda using Google KMS - FIPS 140-2 Level 1 - Operations in software - Zero downtime rotation ### Hardware key - Managed by Camunda using Google KMS - FIPS 140-2 Level 3 - Operations in HSM - Zero downtime rotation ## Bring Your Own Key (BYOK) Enterprise customers on AWS can use their own AWS KMS key. - You manage the key lifecycle, including rotation and revocation - Camunda never stores the key; access occurs via standard AWS KMS integrations - Zero downtime rotation supported See the [BYOK setup guide](/components/saas/byok/aws-kms-setup.md) for configuration. --- ## Help Center Camunda 8 SaaS only Camunda 8 SaaS offers a Help Center to all users, where you can access additional documentation, step through various use cases in Camunda, share your feedback, and more. ## Visiting the Help Center You can visit the Help Center directly from your Camunda 8 account. From Camunda Hub, click the **Open Help Center** icon in the top right corner of the navigation bar: ![question mark icon to open the help center](./img/open-help-center.png) This launches a modal for the Help Center, where you are taken to the **Your recommendations** tab. ## New user survey At the top of **Your recommendations**, you may note an info box prompting you to complete the new user survey for a list of custom recommendations for additional learning. Click **Resume survey** if you would like custom recommendations. Here, you may add additional details about your profile, goals, use case, and experience level. Fill out these details and click **Next**. Your recommendations will automatically update based on your responses. Click **Retake the survey** at any time for a new list of recommendations. ## Using the Help Center ### Your recommendations As mentioned, the **Your recommendations** tab populates a list of recommended resources and next steps based on your sign-up or in-app onboarding survey. These resources may link to additional material in Camunda Hub, documentation, Camunda Academy, or blog. ### Automation project guide The **Automation project guide** tab provides a set of recommended documentation pages for each phase of an automation project with Camunda SaaS. For example, a set of pages for getting started, for designing and implementing, for testing, and for going live or improving your process. ![automation project guide](./img/automation-project-guide.png) ### Use case guide The **Use case guide** tab provides a few getting started templates, such as process modeling or human task orchestration. These tutorials link to tutorials in Web Modeler, which may take 10-15 minutes each. ### Camunda Academy The **Camunda Academy** tab introduces the Camunda Academy, where you can step through free on-demand and instructor-led courses for additional learning. For example, explore bite-sized videos for tasks like creating clusters, gain practical skills to BPMN, DMN, and Camunda training, or experience hands-on learning and personalized guidance in a virtual classroom. ### Share your feedback The **Share your feedback** tab offers a quick way to share product feedback, suggestions, or bug reports. This information travels directly to Camunda's Product Management team. You may also click the **Documentation** or **Support forum** links in the bottom left corner of the modal if you would like to be taken to either of these resources at any time. :::note Worried about losing your place in Camunda Hub? Clicking any links in the Help Center will automatically open a new tab for additional learning, and your current tab will remain as-is within the product. ::: --- ## Hostnames and IP addresses for Camunda connections Camunda 8 SaaS only Camunda 8 SaaS hostnames and IP addresses for inbound and outbound connections. ## Static outbound IP addresses Camunda SaaS uses static IP addresses for some of its services. These addresses can be retrieved using the [Camunda Management API `/meta/ip-ranges` endpoint](https://console.cloud.camunda.io/customer-api/openapi/docs/#/default/GetMeta). Although changes to these IP addresses are infrequent, they may occur from time to time. Any change will be published through the API at least 24 hours in advance. If you rely on these IP addresses for your network configuration, it is strongly recommended fetching the latest version at least once every 24 hours. :::note Typically, any changes to these IP addresses are communicated in advance. However, in exceptional cases, it may not be possible to provide prior notice. ::: ## Inbound connections When you [create a cluster](/components/hub/organization/manage-clusters/create-cluster.md) in Camunda 8 SaaS, you will receive a set of hostnames for connecting to the different cluster components. The public IP addresses exposed for connecting to the cluster depends on the cloud provider and [region](/components/saas/regions.md) the cluster was created in. - **Amazon Web Services (AWS)**: Each endpoint is served by multiple IP addresses. - **Google Cloud Platform (GCP)**: IP addresses are AnyCast IP addresses and are globally available. ## Outbound connections If you use a [Camunda connector](/components/connectors/introduction.md), your cluster sends requests from the Camunda SaaS infrastructure to the external services you configure in your processes. Depending on the cloud provider, [region](/components/saas/regions.md), and type of configured connector, connections are made from different IP addresses. To ensure the security of incoming connector connections, you can: - Authenticate the requests made by the Camunda connector(s). For example, see [REST connector authentication](/components/connectors/protocol/rest.md#authentication). - Run the connectors into your own infrastructure and remove incoming calls from the Camunda infrastructure to your own services. For example, see [Self-Managed connectors](/self-managed/components/connectors/overview.md). --- ## Configure monitoring systems to scrape metrics Configure your monitoring systems to scrape metrics from a Camunda 8 SaaS cluster using the Cluster Metrics endpoint. ## Before you begin Before configuring metric scraping, ensure that: - The Cluster Metrics endpoint is enabled for your Camunda 8 SaaS cluster. - You have the metrics endpoint URL and authentication credentials. - Your monitoring system can reach the endpoint from an allowlisted IP address. For information about the Cluster Metrics endpoint monitoring model and limitations, see [Cluster Metrics endpoint](/components/saas/monitoring/cluster-metrics-endpoint/index.md). ## Metrics endpoint details The metrics endpoint: - Uses HTTPS - Requires Basic Authentication - Returns metrics in Prometheus format ### Endpoint format The full metrics endpoint follows this format: `https:///` - ``: The domain hosting the metrics endpoint. - ``: The identifier of the Camunda 8 SaaS cluster. ### Verify endpoint access Before configuring your monitoring system, use the following command to verify that the Cluster Metrics endpoint is reachable: ```bash curl -v -u ":" https:/// ``` ## Configure Prometheus scraping Prometheus can scrape the Cluster Metrics endpoint directly. ### Example scrape configuration ```yaml scrape_configs: - job_name: "c8-" scheme: https metrics_path: / static_configs: - targets: - basic_auth: username: password: scrape_timeout: 5s scrape_interval: 30s ``` Configuration notes: - Use HTTPS. - Configure Basic authentication using the credentials provided when the Cluster Metrics endpoint was enabled. - A scrape timeout of less than 10 seconds is recommended. - A scrape interval of at least 15 seconds is recommended. Metrics are collected every 15–30 seconds, so shorter intervals do not produce new data. ## Scrape interval and retention The Cluster Metrics endpoint exposes metrics from the most recent scrape only and does not retain historical data. Configure your monitoring system to store and retain metrics as needed. ## Verify metric collection After configuring scraping: - Confirm that the scrape target reports a healthy state. - Check that metrics correspond to the expected Camunda 8 cluster. If metrics do not appear, review authentication, network access, and scrape configuration. ## Example dashboards Camunda provides example Grafana dashboards in a public [GitHub repository](https://github.com/camunda/camunda/tree/main/monitor/grafana) that you can use to explore and visualize Cluster Metrics. These dashboards serve as reference examples and may rely on additional metric sources, such as [kube-state-metrics](https://github.com/kubernetes/kube-state-metrics) or [node-exporter](https://github.com/prometheus/node_exporter). You can adapt them to match your monitoring conventions, alerting rules, and operational requirements. Available metrics can vary depending on the Camunda version running in your cluster. ## Integrate non-Prometheus monitoring systems The Cluster Metrics endpoint exposes metrics in Prometheus-compatible formats. Some monitoring systems require additional components to ingest these metrics. In these cases, you can deploy a self-managed OpenTelemetry Collector to adapt the metrics to your monitoring system. For more information, see the [OpenTelemetry Collector documentation](https://opentelemetry.io/docs/collector/). ![Integrate non-Prometheus monitoring systems](./img/cluster-metrics-endpoint-non-prometheus-architecture.png) Using an OpenTelemetry Collector allows you to normalize, enrich, and control the flow of metrics scraped from the Cluster Metrics endpoint. For example, you can: - Transform metrics to match internal naming conventions - Filter metrics to reduce noise or control ingestion costs. - Enrich metrics with standard labels such as environment or region. - Forward metrics to one or more monitoring backends. - Manage scrape behavior, buffering, retries, and backpressure without changing how Camunda exposes metrics. ### Push-only monitoring systems If your monitoring system only supports push-based ingestion, use the following approach: 1. Deploy a self-managed OpenTelemetry Collector. 1. Configure the collector to scrape the Cluster Metrics endpoint. 1. Configure the collector to push metrics to your monitoring system. Camunda provides the metrics endpoint only. You are responsible for deploying, configuring, and operating the collector. ### Non-Prometheus metric formats If your monitoring system requires a format other than Prometheus, use an OpenTelemetry Collector with the appropriate exporter. The OpenTelemetry Collector supports a wide range of exporters, allowing you to forward metrics to different monitoring backends. For more information, see [OpenTelemetry Collector exporters](https://opentelemetry.io/docs/collector/components/exporter/). ## Troubleshoot common issues ### Authentication errors - Verify the configured username and password. - Check that the monitoring system’s IP address is allowlisted. ### Scrape timeouts - Increase the configured scrape timeout. - Verify network connectivity to the metrics endpoint. ### Missing or incomplete metrics - Confirm that the Cluster Metrics endpoint for the cluster is enabled and healthy. - Verify that the cluster is running a supported Camunda version. - Review scrape interval and retention behavior. --- ## Cluster Metrics endpoint The Cluster Metrics endpoint lets you expose metrics from a Camunda 8 SaaS Orchestration cluster and consume them in your own monitoring system. ## About Use this endpoint to monitor cluster performance, set alerts, and correlate Camunda metrics with the rest of your infrastructure using your existing observability tools. This endpoint is a dedicated, customer-facing metrics service that exposes aggregated component-level metrics. The endpoint is separate from Camunda’s internal monitoring and operational systems and provides access only to metrics intended for customer consumption. ## Before you begin Before using the Cluster Metrics endpoint, ensure that: - You have an external monitoring system capable of collecting prometheus metrics. - You understand your organization’s network access and IP allowlisting requirements. ## Supported environments - The Cluster Metrics endpoint is available for all Camunda 8 SaaS Orchestration clusters. - The endpoint is configured per Orchestration cluster and can be enabled without requiring an upgrade or downtime. ## Metrics exposure model The Cluster Metrics endpoint exposes metrics using a pull-based model and Prometheus-compatible format ([Prometheus](https://github.com/prometheus/docs/blob/main/docs/instrumenting/exposition_formats.md#text-based-format) and [OpenMetrics](https://github.com/prometheus/docs/blob/main/docs/instrumenting/exposition_formats.md#openmetrics-text-format) text exposition formats). ![Cluster Metrics endpoint architecture](./img/cluster-metrics-endpoint-prometheus-architecture.png) When the Cluster Metrics endpoint is enabled for a cluster: - Camunda exposes a cluster-scoped metrics endpoint that aggregates metrics from all Orchestration cluster components. - Metrics are exposed in Prometheus-compatible format. - Your monitoring system initiates metric collection by scraping the endpoint. The Cluster Metrics endpoint does not push metrics to customer systems. ## Monitoring endpoint constraints The Cluster Metrics endpoint exposes the application-level metrics produced by the Camunda version running in your cluster. The following constraints apply: - The Cluster Metrics endpoint uses Basic authentication only. - Metric names and labels depend on the Camunda version running in your cluster. - Metric and dashboard compatibility between Camunda versions is not guaranteed. If your monitoring system does not support Prometheus scraping, you can adapt the metrics using a self-managed OpenTelemetry Collector. For more information, see [Integrate non-Prometheus monitoring systems](/components/saas/monitoring/cluster-metrics-endpoint/configure-monitoring-systems-to-scrape-metrics.md#integrate-non-prometheus-monitoring-systems). ## Next steps - To enable the Cluster Metrics endpoint and obtain connection details, see [Set up the Cluster Metrics endpoint](/components/saas/monitoring/cluster-metrics-endpoint/set-up-cluster-metrics-endpoint.md). - To configure Prometheus to scrape metrics, see [Configure monitoring systems to scrape metrics](/components/saas/monitoring/cluster-metrics-endpoint/configure-monitoring-systems-to-scrape-metrics.md). - If your monitoring system does not support Prometheus scraping, see [Integrate non-Prometheus monitoring systems](/components/saas/monitoring/cluster-metrics-endpoint/configure-monitoring-systems-to-scrape-metrics.md#integrate-non-prometheus-monitoring-systems). --- ## Set up the Cluster Metrics endpoint Set up and manage the Cluster Metrics endpoint for a Camunda 8 SaaS Orchestration cluster, including how to activate and deactivate the endpoint and obtain the connection details required to integrate it with an external monitoring system. ## Before you begin Before enabling the Cluster Metrics endpoint, ensure that: - You are using Camunda 8 SaaS. - You have access to Camunda Hub and have permission to manage cluster-level settings. - If allowlisting is configured for your cluster, the monitoring system’s source IP addresses are added to the cluster IP allowlist. ## Enable Cluster Metrics endpoint Enable the Cluster Metrics endpoint per Orchestration cluster via either Camunda Hub or the API. When the endpoint is enabled, Camunda provisions a secure, cluster-scoped metrics endpoint for external scraping. To activate the endpoint: 1. Sign in to Camunda Hub. 1. Navigate to **Clusters**. 1. Select an existing cluster, or create a new one. 1. Open the **Monitoring** tab for the cluster. 1. Click **Activate monitoring endpoint**. 1. Enter a **username** for the monitoring credentials. 1. Click **Activate**. ### Capture connection details When the Cluster Metrics endpoint is activated, Camunda Hub displays a dialog containing the authentication credentials. 1. Copy and store the password securely. 1. Click **Got it** to close the dialog. After closing the dialog, you can find the metrics endpoint URL in the **Monitoring** tab for the cluster. :::warning Copy and safely store the password when it is displayed. The password is not shown again after you close the dialog. If you lose it, generate a new password. ::: The following information is required to connect your monitoring system: - **Metrics endpoint URL**: HTTPS endpoint used by your monitoring system to scrape metrics. - **Username**: Used for Basic authentication. - **Password**: Used for Basic authentication. ## Manage authentication credentials Authentication credentials are created and managed in Camunda Hub. ### Create additional credentials You can create up to 20 credentials per cluster. To create additional credentials: 1. On the **Monitoring** tab, click **Create new credentials**. 1. Enter a username. 1. Generate and copy the password when it is displayed. ### Rotate credentials Ƭo rotate a password: 1. On the **Monitoring** tab, locate the credential. 1. Click the **Generate password** icon next to the username. 1. Generate and copy the new password when prompted. When credentials are removed or rotated, previously issued credentials may continue to work briefly. Access may persist for up to five minutes before the credentials are fully invalidated. To avoid interruptions during credential rotation, you can create multiple credentials for the same cluster and update your monitoring system to switch between credentials, rather than rotating a single credential in place. ## Authentication and IP allowlisting The Cluster Metrics endpoint enforces both authentication and network restrictions. | Restriction | Description | | :-------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Authentication | The endpoint uses Basic authentication. | | IP allowlisting | The endpoint enforces the cluster-level IP allowlist. Requests from non-allowlisted IP addresses are rejected.If an IP allowlist is configured for the cluster, you must add the source IP addresses of your monitoring system to the allowlist to access the endpoint. | ### Error responses The Cluster Metrics endpoint returns standard HTTP status codes to indicate access and availability issues: | Scenario | HTTP status code | | :--------------------------------------------- | :------------------------ | | Request from a non-allowlisted IP address. | `403 Forbidden` | | Invalid or missing authentication credentials. | `401 Unauthorized` | | Request rate exceeds allowed limits. | `429 Too Many Requests` | | Metrics endpoint is temporarily unavailable. | `503 Service Unavailable` | | Request times out due to high load. | `504 Gateway Timeout` | ## Disable the Cluster Metrics endpoint If you no longer want to expose cluster metrics externally, you can disable the Cluster Metrics endpoint: - Click **Deactivate** in the **Monitoring** tab, or - Delete all credentials associated with the endpoint When the Cluster Metrics endpoint is disabled: - The monitoring endpoint is shut down almost immediately (typically within a few seconds). - All existing credentials are deleted and are not retained if the endpoint is reactivated. - Monitoring systems can no longer scrape metrics from the cluster. To use the endpoint again, you must reactivate it and create new credentials. Disabling the Cluster Metrics endpoint does not affect cluster operation or workload execution. ## Next steps - To configure Prometheus to scrape metrics from the Cluster Metrics endpoint, see [Configure monitoring systems to scrape metrics](/components/saas/monitoring/cluster-metrics-endpoint/configure-monitoring-systems-to-scrape-metrics.md). - If your monitoring system does not support Prometheus scraping, see [Integrate non-Prometheus monitoring systems](/components/saas/monitoring/cluster-metrics-endpoint/configure-monitoring-systems-to-scrape-metrics.md#integrate-non-prometheus-monitoring-systems). --- ## Monitoring Use your own monitoring and observability tools to observe the health and performance of your Camunda 8 SaaS Orchestration Clusters. ## Cluster Metrics endpoint Camunda provides a secure, customer-facing metrics endpoint that exposes aggregated cluster metrics for external consumption. [Cluster Metrics endpoint](cluster-metrics-endpoint/index.md) --- ## Regions When you [create a cluster](/components/hub/organization/manage-clusters/create-cluster.md) in Camunda 8 SaaS, you must specify a region for that cluster. The following regions are available for customers on Trial, Starter, and Enterprise Plans. Enterprise customers can also [reach out to Camunda](https://camunda.com/contact-us/) to discuss custom regions. :::info See [Data locations](data-locations.md) for more information about where your Camunda 8 SaaS data is located and how data is handled. ::: ## Google Cloud Platform (GCP) regions The following GCP regions are currently supported in Camunda 8 SaaS. | GCP region | Secondary backups region | | :----------------------------------------------- | :------------------------------------------------ | | Belgium, Europe (europe-west1) | Germany, Europe (europe-west3) | | Iowa, North America (us-central1) | Salt Lake City, North America (us-west1) | | London, Europe (europe-west2) | Germany, Europe (europe-west3) | | Singapore, Asia (asia-southeast1) | Changhua County, Taiwan (asia-east1) | | South Carolina, North America (us-east1) | Iowa, North America (us-central1) | | Sydney, Australia (australia-southeast1) | Melbourne, Australia (australia-southeast2) | | Toronto, North America (northamerica-northeast2) | Montréal, North America (northamerica-northeast1) | To learn more about each region code/location, refer to [Google Cloud locations](https://cloud.google.com/about/locations). ## Amazon Web Services (AWS) regions The following AWS regions are currently supported in Camunda 8 SaaS. | AWS region | Secondary backups region | | :---------------------------------- | :---------------------------------- | | Frankfurt, Europe (eu-central-1) | Ireland, Europe (eu-west-1) | | Paris, Europe (eu-west-3) | Ireland, Europe (eu-west-1) | | North America, Ohio (us-east-2) | Oregon, North America (us-west-2) | | North America, Virginia (us-east-1) | Oregon, North America (us-west-2) | | Singapore, Asia (ap-southeast-1) | Jakarta, Indonesia (ap-southeast-3) | To learn more about each region code/location, refer to [AWS regions and availability zones](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/). :::note Secure connectivity (AWS PrivateLink) is available only for AWS-hosted Orchestration Clusters. To learn more, see [secure connectivity (AWS PrivateLink)](/components/saas/secure-connectivity/index.md). ::: --- ## Camunda 8 SaaS Run Camunda 8 as a fully managed, cloud-based service. No technical setup or installation is needed, and maintenance and scaling is handled by Camunda. :::warning This documentation is a work in progress and may contain incomplete, placeholder, or evolving content. ::: :::tip Use [Camunda 8 Self-Managed](/self-managed/about-self-managed.md) if you want to deploy and manage Camunda on your own infrastructure, with responsibility for updates, security, and scaling. ::: ## Sign up Sign up and start your developer journey with Camunda 8 SaaS. 1. Visit [accounts.cloud.camunda.io/signup](https://accounts.cloud.camunda.io/signup) to sign up. 1. Fill out the signup form and click **Create account**. 1. Click on the link in your confirmation email to verify your email address. 1. Log in to Camunda 8 SaaS using either the email address and password you signed up with or the social login buttons. You can also log in to Camunda 8 SaaS directly at [camunda.io](https://weblogin.cloud.camunda.io/). ## Architecture The Camunda 8 SaaS platform is built on Google Cloud Platform (GCP) and based on a microservices architecture. ### Clusters There are two types of [cluster](/components/concepts/clusters.md) used when running Camunda 8 SaaS: - Camunda Hub is hosted in GCP in the _europe-west1_ [region](/components/saas/regions.md). - Orchestration cluster components such as Zeebe, Tasklist, Operate, Optimize, and Connectors, are hosted in GCP or Amazon Web Services (AWS) regions. An Orchestration Cluster is a provided group of production-ready nodes that run Camunda 8. Camunda 8 SaaS uses single-tenant clusters, with all data contained in a single tenant for easier administration and simpler security. A cell-based architecture means that each cluster runs as dedicated processes in a separate cell isolated from all other clusters, allowing secure fault and workload separation. Scaling is achieved by deploying additional clusters for new use cases and/or teams. :::note Camunda Self-Managed also supports [multi-tenant](/components/concepts/multi-tenancy.md) clusters, where multiple tenants share the same underlying infrastructure, but with their data logically isolated. Each data entry (for example, process definition, process instance, job) is appended with a tenant ID to ensure separation. ::: ### Zeebe The [Zeebe](/components/zeebe/zeebe-overview.md) core process automation engine that powers Camunda 8 is fully managed by Camunda in SaaS, and is already pre-integrated with other Camunda 8 components such as Operate, Optimize, and Tasklist. You can interact with Zeebe in SaaS using both gRPC and REST APIs. See [working with APIs and tools](/apis-tools/working-with-apis-tools.md). ### Deployment You can configure a number of deployment options to meet your specific business and hosting requirements. For example, you can choose where to host your data and what level of data encryption to use. | Deployment option | Description | | :------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [Cluster](/components/concepts/clusters.md) | Configure the cluster [type](/components/concepts/clusters.md#cluster-type) and [size](/components/concepts/clusters.md#cluster-size) to meet your organization's availability and scalability needs, and to provide control over cluster performance, [availability and uptime](/components/concepts/clusters.md#cluster-availability-and-uptime), and disaster recovery guarantees.To learn more about choosing your cluster size, see [sizing your runtime environment](/components/best-practices/architecture/sizing-your-environment.md#sizing-your-runtime-environment). | | [Region](/components/saas/regions.md) | Choose the region and type of hosting you want to use for your clusters. GCP and AWS region hosting options are available. | | [Encryption at rest](/components/saas/encryption-at-rest.md) | Cluster data is encrypted at rest to provide data security and protection.By default, Camunda 8 SaaS GCP and AWS cluster data at rest is protected with a provider-managed encryption key using GCP encryption. The encryption key is owned and managed by GCP.Enterprise customers requiring a higher level of protection can select a dedicated Camunda-managed software or hardware (HSM) encryption key when creating a new GCP cluster. The encryption key is managed by Camunda using Google Cloud Key Management Service (KMS).Camunda-managed encryption is not currently supported for AWS region clusters. | | [Backups](/components/saas/backups.md) | Back up the state of all Camunda 8 components (Zeebe, Operate, Tasklist, and Optimize) on a regular basis and with zero downtime. In case of failures that lead to data loss, you can request to restore the backup. | | [Auto-updates](/components/saas/auto-updates.md) | Camunda 8 SaaS customers can enable auto-updates. When enabled, the cluster is updated once a new patch release is available. | | [Secure connectivity (AWS PrivateLink)](/components/saas/secure-connectivity/index.md) | Enable private inbound connectivity from your VPC to AWS-hosted Orchestration Clusters using AWS PrivateLink. Available for Enterprise plans. | ### Monitoring Camunda 8 SaaS offers a number of monitoring options to help you keep track of your processes and system health. | Monitoring option | Description | | :------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [Operate](/components/operate/operate-introduction.md) | Operate allows you to monitor, manage, and troubleshoot process instances in Camunda 8. It allows you to monitor, search, and resolve [incidents](/components/operate/userguide/resolve-incidents-update-variables.md) across your tenants. | | [Optimize alerts](/components/optimize/improve-processes-with-optimize.md#alerts) | Optimize allows you to create alerts for reports within a collection. These alerts can notify you when a report hits a predefined critical value. For SaaS users, alerts can be sent to the email addresses of Camunda Hub users. | | [Usage alerts](/components/hub/organization/manage-organization-settings/usage-alerts.md) | For Starter and Enterprise organizations, Camunda 8 SaaS provides usage alerts for production clusters. Organization owners and admins can set up alerts for process instances, decision instances, and task users.These alerts are triggered when usage reaches a defined threshold, and notifications are sent via email and in-app notifications. | | [Flow control](/self-managed/operational-guides/configure-flow-control/configure-flow-control.md) | Flow control is enabled by default to protect SaaS clusters from excessive load and to maintain a stable state. This feature helps in monitoring and managing cluster performance. | | [Usage metrics](/reference/data-collection/usage-metrics.md) | There are three main usage metrics that have an impact on Camunda 8 pricing. It is important to understand these definitions, their impact on billing, and how to retrieve them. | | [Camunda 8 SaaS status](/components/saas/status.md) | Camunda provides a status page where you can check the current and past service availability of Camunda 8 SaaS. You can also subscribe to updates via Atom and RSS feeds to receive notifications about service status changes. | ## Security and compliance ### Compliance At Camunda, we're committed to Information Security, Privacy and Compliance. Our mission is to establish trust through transparency. - Visit the [Camunda Trust Center](https://camunda.com/trust-center/) to learn more about our standards and certifications, including SOC 2 compliance, ISO/IEC 27001 certification, and GDPR Compliance. - Camunda is a member of the [Cloud Security Alliance](https://cloudsecurityalliance.org/star/registry/camunda/services/camunda). ### Data retention In Camunda 8 SaaS, [data retention](/components/saas/data-retention.md) strategies are implemented. This is necessary as the amount of data can grow significantly overtime. These settings are a balance between performance and usability. ### Data locations See [data locations](data-locations.md) to learn more about where your Camunda 8 SaaS data is located and how data is handled. ### Access controls Camunda 8 SaaS supports the following access controls. | Access control type | Description | | :------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [Single sign-on (SSO)](/components/hub/organization/manage-organization-settings/external-sso.md) | SSO is available for both Starter and Enterprise plans, using Identity as a bridge between an OpenID Connect (OIDC) provider and the Camunda platform. | | [OAuth](/components/hub/organization/manage-clusters/manage-api-clients.md) | The OAuth service is used to allow client applications to interact with Zeebe in SaaS from the outside. Every client application must authenticate itself using an OAuth Flow. | | [Role based access (RBAC)](/components/hub/organization/manage-members/manage-users.md) | Camunda 8 SaaS supports RBAC through a system of roles and permissions.Each role provides a different level of access to Camunda 8 components, allowing organizations to control user permissions based on their responsibilities. | | [Resource-based authorization](/components/hub/organization/manage-members/manage-users.md#resource-based-authorizations) | Resource authorizations allow you to control the level of access a user has to a particular resource in the system. | :::note In Enterprise plans, the hostname section of the email address for invites can be restricted to meet your internal security policies. To learn more, [contact Camunda support](https://camunda.com/services/support/). ::: --- ## Enable secure connectivity This guide explains how to enable secure connectivity (AWS PrivateLink) for an AWS-hosted Camunda 8 SaaS Orchestration Cluster. Secure connectivity must be enabled per cluster. For a conceptual overview, see [secure connectivity (AWS PrivateLink)](./index.md). ## Prerequisites Before enabling secure connectivity: - The cluster must be hosted in AWS. - The cluster must be version 8.8.0+. - You must have sufficient permissions to manage clusters in Camunda Hub. - You must know the AWS account IDs or ARNs that should be allowed to connect. - Your organization must be on an Enterprise plan. On the AWS side, you must have: - An existing AWS VPC. - Permission to create VPC interface endpoints. - Appropriate security group configuration. For instructions on creating a VPC interface endpoint, see the [AWS documentation on configuring an interface VPC endpoint](https://docs.aws.amazon.com/vpc/latest/privatelink/create-interface-endpoint.html). ## Enable secure connectivity for a cluster 1. Navigate to **Camunda Hub > Clusters**. 2. Select the cluster. 3. Open the **Private networking** tab. The **Private networking** tab is available only for clusters hosted in AWS. It is not displayed for clusters hosted in other cloud providers. 4. Select **Activate PrivateLink endpoint service**. ### Allowed principals 1. In **Principal ARN**, enter the ARN of an AWS principal that should be allowed to connect. For supported principal types, see the [AWS documentation on configuring endpoint service permissions](https://docs.aws.amazon.com/vpc/latest/privatelink/configure-endpoint-service.html#add-remove-permissions). 2. Select **Add principal**. 3. Repeat for additional principals as needed. 4. Select **Next**. Validation requirements for principal ARNs are described in [validation and activation requirements](#validation-and-activation-requirements). ### Supported regions The AWS region where the Orchestration Cluster is located is always supported and is preselected by default. You can add additional AWS regions to allow cross-region endpoint connections. Cross-region connectivity may increase network latency and incur additional AWS charges. 1. Review the cluster's AWS region (preselected). 2. Optionally add additional regions to allow cross-region endpoint connections. 3. Select **Activate service**. After activation, Console provisions a VPC endpoint service for the cluster and displays the connection details. ## Validation and activation requirements When configuring and activating the PrivateLink endpoint service, Console validates the provided values during each step: - At least one valid AWS principal ARN must be provided. - Principal ARNs must follow a valid AWS ARN format. - At least one supported region must be configured. - The cluster’s AWS region is preselected by default. You cannot activate the service until all required fields are completed. ## Activation behavior After selecting **Activate service**, Console provisions the VPC endpoint service for the cluster. The service status is displayed in the **Service details** section. Provisioning may take up to 10 minutes. During provisioning, the endpoint service is not available for new VPC endpoint connections. ## View connection details After activation, the **Private networking** tab displays the **Service details** section. This section shows: - **Status** (for example, Ready). - **Service name** (VPC endpoint service name). - **Service region**. - **Service type** (Interface). - **Private DNS name** (generated by Camunda for the endpoint service). - **Allowed principals**. - **Supported regions**. To connect from AWS, select **Create interface VPC endpoint connections in AWS**. Endpoint connections are created in your AWS account. When creating a VPC interface endpoint, use the **Service name** shown in the **Service details** section. For detailed instructions, see the [AWS documentation on creating an interface endpoint](https://docs.aws.amazon.com/vpc/latest/privatelink/create-interface-endpoint.html). The **Endpoint connections** section lists VPC interface endpoint connections created in AWS that target this cluster’s VPC endpoint service. For each connection, Console displays: - The VPC endpoint identifier. - The connection status (for example, Pending or Available). New endpoint connections appear in this section after they are created in AWS. ## Manage allowed principals and regions After activation, you can modify the configuration from the **Private networking** tab. In the **Service details** section: - Select the edit icon next to **Allowed principals** to add or remove AWS principal ARNs. - Select the edit icon next to **Supported regions** to add or remove regions. Changes apply to new VPC endpoint connection attempts. Removing a previously allowed principal does not invalidate existing VPC endpoint connections. ### Endpoint connection approval VPC endpoint connections are automatically approved when the AWS principal creating the interface endpoint is included in the **Allowed principals** list. You don’t need to manually approve endpoint connections. ### Removing supported regions Removing a supported region does not affect existing VPC endpoint connections that were created using that region. Existing endpoint connections remain available. ## Create a VPC interface endpoint in AWS After activating the PrivateLink endpoint service in Console: 1. Copy the **Service name** from the **Service details** section. 2. In your AWS account, create a VPC interface endpoint that connects to this service. 3. In the Amazon VPC console, open the **Create endpoint** wizard, then: - **Select a category** > **PrivateLink Ready partner services**. - Do not select **AWS services**. Otherwise, the Camunda service will not appear. 4. Configure subnets, security groups, and optional private DNS according to your AWS requirements. AWS-side provisioning must follow the standard AWS PrivateLink process. For detailed instructions, see the [AWS documentation on creating an interface endpoint](https://docs.aws.amazon.com/vpc/latest/privatelink/create-interface-endpoint.html), which also covers endpoint configuration and validation. ## Deactivate secure connectivity In the **Private networking** tab, select **Deactivate service** to remove the VPC endpoint service for the cluster. Public connectivity remains available. ## View-only access If you do not have permission to manage private networking for a cluster: - The **Activate PrivateLink endpoint service** button is not displayed. - The **Deactivate service** option is not displayed. - Edit options for **Allowed principals** and **Supported regions** are hidden. You can still view the **Service details** and **Endpoint connections** sections. ## Limits You can create up to 10 VPC endpoint connections per cluster. For organization-wide limits and adjustments, see [secure connectivity (AWS PrivateLink)](./index.md). --- ## Secure connectivity (AWS PrivateLink) Secure connectivity allows you to connect to Camunda 8 SaaS Orchestration Clusters from your AWS Virtual Private Cloud (VPC) using AWS PrivateLink. When enabled, traffic from your AWS VPC to an Orchestration Cluster is routed over private AWS networking rather than the public internet. Secure connectivity: - Applies per cluster. - Is available only for AWS-hosted Orchestration Clusters. - Supports inbound connectivity only — it enables private access from your AWS VPC to Camunda, but does not provide outbound private connectivity from Camunda to your services. - Adds a private connectivity path. Public endpoints remain enabled. - Is available to Enterprise customers. ## How it works ![AWS PrivateLink architecture](./img/aws-privatelink-diagram.jpg) Secure connectivity uses AWS PrivateLink to establish a private network path between your AWS VPC and the Camunda-managed cluster infrastructure. When you enable secure connectivity for a cluster: - Camunda provisions a VPC endpoint service for that cluster in the cluster’s AWS region. - You create one or more VPC interface endpoints in your AWS account that connect to the endpoint service. - Traffic from resources in your VPC (for example, job workers or inbound connectors) is routed privately to the cluster. Each cluster has its own VPC endpoint service and dedicated networking components. Access to the Orchestration Cluster is handled through dedicated load balancing and API gateway components. Secure connectivity relies on standard AWS PrivateLink functionality. For an overview of AWS PrivateLink concepts and terminology, see [the AWS documentation](https://docs.aws.amazon.com/vpc/latest/privatelink/what-is-privatelink.html). ## Connect from your AWS VPC via PrivateLink At a high level: 1. Enable secure connectivity for a cluster in Console. 2. Review the VPC endpoint service details provided by Camunda (for example, service name, service type, region, and private DNS name). 3. Create one or more VPC interface endpoints in your AWS account and configure the required security groups. 4. Optionally configure private DNS for the endpoint connection in AWS. Enabling private DNS provides a seamless HTTPS experience. 5. Test connectivity from resources inside your VPC. Camunda owns and operates the VPC endpoint service and the associated cluster-side infrastructure. You own and manage resources in your AWS VPC, including: - VPC interface endpoints. - Security groups. - Routing and DNS configuration. - AWS permissions and quotas. For step-by-step Camunda Hub instructions, see [Enable secure connectivity for a cluster](./console-setup.md). ## Security and isolation Secure connectivity restricts private access to your cluster using AWS PrivateLink and an allowlist of AWS principals. When enabling secure connectivity, you define one or more allowed AWS principals (AWS account IDs or ARNs). Only those principals can create VPC endpoints that connect to your cluster’s endpoint service. Requests from AWS accounts that are not explicitly allowed cannot establish a PrivateLink connection. For each cluster: - A separate VPC endpoint service is provisioned. - Cluster-specific networking components are provisioned. Private connectivity does not share entry components across clusters. - Access to the Orchestration Cluster is handled through the cluster's API gateway layer. Traffic between your VPC and the cluster’s API gateway layer is encrypted in transit using TLS. TLS terminates at the cluster’s API gateway layer. Traffic within the cluster follows the same model as public connectivity. ## Limits The following limits apply: - Up to 10 VPC endpoint connections per organization (adjustable on request). - Up to 10 VPC endpoint connections per cluster. Contact Camunda support if you require higher limits. ## Supported connectivity modes The following combinations are supported: | Private connectivity | Public connectivity | Supported | | -------------------- | ------------------- | --------- | | Disabled | Enabled | Yes | | Enabled | Enabled | Yes | | Enabled | Disabled | No | Private-only connectivity is not currently supported. Public connectivity remains enabled even when secure connectivity is configured. ## Public and private connectivity When secure connectivity is enabled, public connectivity remains available. - Orchestration Cluster components like Operate, Tasklist, and Admin can still be accessed using their public URLs. - When creating client credentials for a cluster, you can choose which connectivity type to use: - Public connectivity, which uses the public hostnames shown for the credentials. - Private connectivity, which uses the private DNS hostname of your VPC interface endpoint instead of the public cluster hostname. ### Private DNS hostname When you create a VPC interface endpoint in AWS, the endpoint is assigned a private DNS hostname. This hostname resolves to the private network address of the endpoint within your VPC and is used to route traffic through AWS PrivateLink. When connecting to your Camunda cluster using secure connectivity, replace the `{PRIVATE_DNS}` placeholder in the cluster endpoint URL with the private DNS hostname of your VPC endpoint. You can find this hostname in the details of your VPC interface endpoint in AWS. For more information, see the AWS documentation on VPC interface endpoints. ## What secure connectivity does not change Secure connectivity: - Does not change data location or backup regions. - Does not affect encryption at rest or key management. - Does not provide outbound private connectivity from Camunda to your services. - Does not replace IP allowlists or other access control features. If an IP allowlist is configured, it continues to apply to connections made through private connectivity. For more information, see [Manage IP allowlists](../../hub/organization/manage-clusters/manage-ip-allowlists). Secure connectivity changes only the network path used for inbound connections from your AWS VPC to the Orchestration Cluster. --- ## Camunda 8 SaaS status Camunda 8 SaaS is a hosted service for the Camunda 8 stack that runs on the Google Cloud Platform (GCP). Like any service, it might occasionally undergo availability changes. When availability changes, Camunda makes sure to provide you with a current service status. To see current and past service availability, visit [Camunda 8 SaaS Status](https://status.camunda.io). ## Subscribe to updates Don’t want to check the service status page manually? Get notified about changes to the service status automatically. To receive service status updates: 1. Go to the [Camunda 8 SaaS Status](https://status.camunda.io) page and click **SUBSCRIBE TO UPDATES**. 1. Select **Atom and RSS feeds**. 1. Add the feed URL to your favourite Atom/RSS reader. 1. After you subscribe to updates, you are notified whenever a service status update is posted. ## Support Support can be requested by subscription or enterprise customers via [JIRA](https://jira.camunda.com/projects/SUPPORT/). Otherwise, [contact us](/reference/contact.md). For more information about Enterprise support and additional support resources, see [Enterprise Support](https://camunda.com/services/support/). --- ## Tasklist API changes Tasklist in Camunda 8.10 and later uses only the [Orchestration Cluster REST API](../../apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). The legacy Tasklist V1 API and the Tasklist V1 UI mode were removed in 8.10. For the release-level summary of these removals, see the [8.10 release announcement](/reference/announcements-release-notes/8100/8100-announcements.md#removal-of-legacy-apis-tasklist-v1-dependent-features-and-zeebe-process-test). ## What changed in 8.10 - Tasklist always uses the Orchestration Cluster REST API. - The Tasklist V1 mode toggle is no longer available in SaaS or Self-Managed clusters. - Features that depended on Tasklist V1 are no longer available in the current version. ## Removed legacy behavior The following behavior was tied to Tasklist V1 and is no longer available in 8.10 and later: - Job worker-based user tasks - Draft variables - User task access restrictions - Public start forms - Advanced process filtering that depended on Tasklist V1 - Task context description and context variables - The Tasklist-specific permission and visibility model from V1 ## Recommended replacements - Use the [Orchestration Cluster REST API](../../apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) for task, form, and process interactions. - Use [user task authorization](./user-task-authorization.md) for current Tasklist access control. - Use the [migration guide for Camunda user tasks](../../apis-tools/migration-manuals/migrate-to-camunda-user-tasks.md) if you still have job worker-based user tasks. - Use [migrating to the Orchestration Cluster REST API](../../apis-tools/migration-manuals/migrate-to-camunda-api.md) if you still have integrations that call the removed Tasklist API. ## Historical timeline | Version | Status | | :----------- | :---------------------------------------------------------------------------------------------------------- | | Camunda 8.8 | V2 API is the default and recommended option. V1 API is deprecated but remains available via configuration. | | Camunda 8.9 | V1 API remains deprecated and is not recommended for new implementations. | | Camunda 8.10 | V1 API is removed. V2 API is the only available option. | --- ## Introduction to Tasklist Tasklist is a ready-to-use application to rapidly implement business processes alongside [user tasks](/components/modeler/bpmn/user-tasks/user-tasks.md). With Tasklist you can orchestrate human workflows critical to your business and reduce time-to-value for your process orchestration projects with an out-of-the-box interface. As you model and run business processes using BPMN, users are notified in Tasklist when they're assigned a task, and run the work that's required for the specific task. You are not limited to use the [Tasklist user interface](/components/tasklist/userguide/using-tasklist.md). You can extend your case or build a fully custom application using the [Orchestration Cluster API](../../apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). Learn more in the [frontend developer documentation](/apis-tools/frontend-development/01-task-applications/01-introduction-to-task-applications.md). --- ## User task access restrictions were removed User task access restrictions were removed in Camunda 8.10 together with Tasklist V1. For the release-level summary of this removal, see the [8.10 release announcement](/reference/announcements-release-notes/8100/8100-announcements.md#removal-of-legacy-apis-tasklist-v1-dependent-features-and-zeebe-process-test). Use [user task authorization](./user-task-authorization.md) and [authorization-based access control](/components/concepts/access-control/authorizations.md) for current Tasklist access control. If you are migrating from an older installation, review [Tasklist API changes](./api-versions.md) to understand which legacy V1 features were removed. --- ## User task authorization This page explains the recommended way to configure authorization for user tasks in Tasklist V2 when authorization is enabled. It describes how authorization affects Tasklist behavior and which permissions are required to perform common user task operations. ## When you need to configure user task authorization Configure user task authorization if all of the following apply: - Authorization is enabled in your cluster - You use Tasklist V2 - You want to control which users can view, claim, update, or complete user tasks If authorization is disabled, Tasklist does not enforce permission checks for user task operations. ## Recommended authorization model For Tasklist, the recommended authorization model combines: - Process-level permissions to grant broad access to user tasks that belong to a process - Task-level permissions for fine-grained access control on individual user tasks - The default task worker role for common user task operations With this approach, you can grant general access where appropriate while restricting access to specific tasks based on task properties, such as assignee or candidate groups. ## Authorization resources used by Tasklist Tasklist evaluates user task authorization using the following authorization resources: - `PROCESS_DEFINITION`: Controls access to user tasks that belong to a specific process definition (for example: `READ_USER_TASK`, `UPDATE_USER_TASK`, `CLAIM_USER_TASK`, `COMPLETE_USER_TASK`). - `USER_TASK`: Controls access to individual user tasks using property-based access control. For details about authorization resources, permission evaluation, and configuration, see [Authorization concepts](../concepts/access-control/authorizations.md). ## Default task worker role On new installations and upgrades, Camunda Identity automatically creates a default `task-worker` role. This role grants property-based `USER_TASK` permissions: - Permissions: - `READ` - `CLAIM` - `COMPLETE` - Scoped by: - `assignee` - `candidateUsers` - `candidateGroups` This lets typical task workers see, claim, and complete only the tasks they're responsible for. You can use this role as-is or create custom roles with similar property-based authorizations. :::note Upgrade behavior When you upgrade from Camunda 8.8 to 8.9, Identity creates only the default `task-worker` role automatically. Other roles are not created during upgrade. If needed, create additional roles in Identity and assign the appropriate permissions. ::: ## User task operations and required permissions The following table shows which permissions are required to perform common user task operations in Tasklist. The table reflects currently-implemented permissions that are enforced by Tasklist. | Operation | `USER_TASK` permission | `PROCESS_DEFINITION` permission | | ----------------------------------------- | ---------------------- | ------------------------------------------ | | Get user task (by key) | `READ` | `READ_USER_TASK` | | Search user tasks | `READ` | `READ_USER_TASK` | | Get user task form | `READ` | `READ_USER_TASK` | | Claim task | `CLAIM` | `CLAIM_USER_TASK` or `UPDATE_USER_TASK` | | Unassign task | `UPDATE` | `UPDATE_USER_TASK` | | Assign task (override assignee) | `UPDATE` | `UPDATE_USER_TASK` | | Complete task (with or without variables) | `COMPLETE` | `COMPLETE_USER_TASK` or `UPDATE_USER_TASK` | | Update user task | `UPDATE` | `UPDATE_USER_TASK` | :::note Some operations listed in the table (for example, task reassignment) may not yet be available in the Tasklist UI. ::: ## How Tasklist evaluates permissions Tasklist relies on the Orchestration Cluster authorization model to control access to user tasks. Permissions are evaluated in two layers: 1. Process-level permissions on the `Process Definition` resource (`READ_USER_TASK`, `CLAIM_USER_TASK`, `COMPLETE_USER_TASK`, and `UPDATE_USER_TASK`). 2. Task-level permissions on the `USER_TASK` resource (`READ`, `UPDATE`, `CLAIM`, and `COMPLETE`), often combined with property-based access control on `assignee`, `candidateUsers`, and `candidateGroups`. When both layers are configured, process-level permissions take precedence: - If a user already has the required process-level permission on `Process Definition` for an operation (for example, `READ_USER_TASK`, `CLAIM_USER_TASK`, `COMPLETE_USER_TASK`, or `UPDATE_USER_TASK`), Tasklist does not require or evaluate additional `USER_TASK` permissions for that operation. - If the user does not have sufficient process-level permissions, Tasklist evaluates `USER_TASK` permissions instead, including property‑based authorizations based on task properties. ## Example: claim and complete group tasks To allow users to work on tasks that are assigned to their group: 1. Grant the `READ_USER_TASK` permission on the `PROCESS_DEFINITION` resource for the relevant process. 2. Grant the `CLAIM` and `COMPLETE` permissions on the `USER_TASK` resource using the `candidateGroups` property. This configuration allows users to see tasks for the process and claim or complete only those tasks for which they are listed as a candidate group member. ## Learn more - [Authorization concepts](../concepts/access-control/authorizations.md) --- ## Access control(Userguide) If authorization control is enabled for your Orchestration Cluster, users require the following authorizations to work with Tasklist. :::note You can assign these [in the Admin UI](/components/admin/authorization.md#create-an-authorization). See [the introduction to authorizations](/components/concepts/access-control/authorizations.md#available-resources) for a list of all available authorizations. ::: ## Mandatory authorizations The following mandatory authorizations are required to work with Tasklist: | Authorization type | Resource type | Resource ID | Permission | | :----------------------------- | :------------------- | :--------------------------------------------------------------------------------- | :--------------- | | Component access for Tasklist. | `Component` | `tasklist` or `*` (for access to all web components). | `ACCESS` | | Read user tasks. | `Process Definition` | ID of the respective BPMN process definition or `*` (for all process definitions). | `READ_USER_TASK` | ## Optional authorizations The following optional authorizations can also be defined: | Authorization type | Resource type | Resource ID | Permission | | :---------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------- | :--------------------------------------------------------------------------------- | :------------------------------------------------------------- | | View task history records. | `AUDIT_LOG` | `USER_TASKS` or `*` for all categories. | `READ` | | Assign and complete user tasks | Process Definition | ID of the respective BPMN process definition or `*` (for all process definitions). | `CLAIM_USER_TASK`, `COMPLETE_USER_TASK`, or `UPDATE_USER_TASK` | | View BPMN diagrams | Process Definition | ID of the respective BPMN process definition or `*` (for all process definitions). | `READ_PROCESS_DEFINITION` | | View [`USER_TASKS` operations](../../audit-log/overview/recorded-operations.md#user_tasks-operations) for instances of a specific process definition. | `PROCESS_DEFINITION` | A process definition ID or `*` for all process definitions. | `READ_USER_TASK` | | View task history records related to specific tasks the user has access to based on task properties. | `USER_TASK` | A user task property (`assignee`, `candidateUsers`, `candidateGroups`). | `READ` | | Authorization type | Resource type | Resource ID | Permission | --- ## Audit task history Audit [task history](../../audit-log/overview.md) in Camunda 8 Tasklist. ## Prerequisites To follow the steps in this guide, you must be [authorized to view task history records](../../audit-log/overview/access-control.md). ## Audit task history 1. On the **Tasks** page, click a task to audit. 2. In the tasks detail view, click the **History** tab. 3. To sort the log, click a column header. 4. To see the details of a particular operation, click the info icon at the end of the row. ## Next steps - [Learn about `USER_TASKS` operations](../../audit-log/overview/recorded-operations.md#user_tasks-operations). - [Learn about the operation data structure in the history table.](../../audit-log/overview/operation-structure.md) --- ## Defining task priorities You can add prioritization to [user task elements](/components/modeler/bpmn/user-tasks/user-tasks.md) by specifying a priority value for a user task. This determines the task's importance in relation to other tasks within processes. - The task priority is an **integer** value ranging from 0 to 100, with a default value of 50. - A higher priority value indicates higher importance. When displayed in Tasklist, priority values are mapped to the following default labels: | Priority value | Default label | | :------------- | :------------ | | 0-25 | Low | | 26-50 | Medium | | 51-75 | High | | 76-100 | Critical | These labels give Tasklist users a clear view of task priority, making it easier to assess a task's urgency. This also makes sorting and filtering simple, helping users prioritize the most important tasks. ## Step-by-step guide This step-by-step guide shows you how to define task priorities for Tasklist users. ### 1. Model a BPMN process Start by modeling your [BPMN process in Modeler](/components/modeler/bpmn/automating-a-process-using-bpmn.md), ensuring that the required user tasks are defined within the process. ### 2. Set a priority for user tasks During user task configuration you can specify a priority value. You can also define the value using an [expression](/components/concepts/expressions.md). The priority value determines the task's importance relative to other tasks. ![set-user-task-priority-in-modeler](img/modeler-user-task-priority.jpg) ### 3. Deploy and start the process After the process is fully defined and all configurations are complete, the process can be deployed and started. The priority values are now associated with each user task within the process. ### 4. View task priority in Tasklist Tasklist users can view the tasks assigned to them within their task list. Each task card displays the assigned priority label, ensuring users have a clear understanding of the task's importance and priority. ![set-user-task-priority-in-modeler](img/tasklist–tasks-with-priority.jpg) ### 5. Sort tasks by priority Task users can sort tasks by priority. This helps users organize their workload by focusing on urgent items first. ![set-user-task-priority-in-modeler](img/tasklist-tasks-with-priority-sorting.jpg) --- ## Localization When creating processes for audiences in non-English speaking regions, you can localize the Tasklist interface to the appropriate language. To do this, design [user task forms](/components/modeler/forms/utilizing-forms.md) in the local language, and set the Tasklist interface to use the same language. Tasklist supports the following four languages by default: - English - French - German - Spanish The interface language is automatically determined by the device language settings of the user. If the device's language is not supported, the interface will default to English. ## Change Tasklist language settings ![tasklist-language-settings](img/tasklist-language-settings.jpg) The language used in Tasklist can be changed through the following steps: 1. Open your user profile, and navigate to **Language**. 2. Select your preferred language from the list. 3. Once selected, the Tasklist interface will update to your chosen language. --- ## Managing tasks When you've successfully logged in, you'll see the **Tasks** page: ![tasklist-start-screen](../img/tasklist-start-screen_light.png) ## Assign tasks To find relevant tasks you can use [filters](./using-filters.md). Select the **Unassigned** filter list and assign a task to yourself using the **Assign to me** button on the top panel: ![tasklist-claim](img/tasklist-claim_light.png) ## Work on assigned tasks Select the **Assigned to me** filter list to see the tasks that are assigned to you. Select a task to work on it. ![tasklist-claimed-by-me-list](img/tasklist-claimed-by-me-list_light.png) ### Get notified about new assignments Tasklist users can receive a browser notification when new tasks are assigned to them: ![Tasklist notifications banner](./img/tasklist-notifications.png) The **Don't miss new assignments** banner at the top of the page appears when the user either assigns a new task to themselves, or opens a task that is already assigned to them. To turn on browser notifications, click **Turn on notifications**. To turn off notifications, disable notifications for this site in your browser settings. :::note This requires Tasklist to run in the background, so if Tasklist is closed, users will not receive notifications. We recommend keeping Tasklist open in your browser for consistent use. ::: ## Complete a task When a task is assigned to you, you can complete the task by filling out the given form, and clicking on the **Complete Task** button. ![tasklist-completing-task](img/tasklist-completing-task_light.png) There are also cases where no form is available. In these cases, you have to add and/or update variables directly. ![tasklist-with-variables-claimed-by-me](img/tasklist-with-variables-claimed-by-me_light.png) Always choose a list of tasks with a specified status. Then, select the task you want to work on. Change variables as needed and begin completion with the **Complete Task** button. Completed tasks will be shown in the [**Completed** task list](#completed-tasks). ### Add and update variables Update variables in the **Variables** section by adjusting their text field. To add a new variable, click **Add Variable**. ![tasklist-add-new-variable](img/tasklist-add-new-variable.png) ## View completed tasks You will now see the completed task by selecting the **Completed** task list: ![tasklist-task-completed](img/tasklist-task-completed_light.png) ## Options ### Auto-select first available task If this is enabled, whenever you open tasks, change filter options, or complete a task, Tasklist will automatically select the first task in the list. --- ## Starting processes You can start processes on demand using Tasklist. To do this, click **Processes** in the navigation menu. All the processes you have access to start are listed on the **Processes** page. ![tasklist-processes](img/tasklist-processes.png) In the **Search** box, search for the process definition ID of the process you want to start. ![tasklist-processes-search](img/tasklist-processes-search.png) Tasklist no longer supports the legacy V1 process filters that were available during the migration period before 8.10. To start a process, click **Start process** on the process you want to start. ![tasklist-processes-start](img/tasklist-processes-start.png) If the start event of this process contains a [linked or embedded Camunda Form](/components/hub/workspace/modeler/modeling/advanced-modeling/form-linking.md), a modal window containing that form will automatically open. ![tasklist-processes-start-with-form](img/tasklist-processes-start-with-form.png) Tasklist will then wait for the process to be executed. If the process generates a task, you will be redirected to the generated task. :::info To share a process with users inside your organization, but not with external users, you can click on the share icon ![share icon](img/tasklist-processes-share-button.png) to copy a link to the process. This link will only be accessible to users that already have access to Tasklist. ::: ## Not seeing a process There could be multiple reasons why you are not seeing any process in the **Processes** tab: - There is no process deployed to your environment. - Permissions to start a process are managed via [Authorizations](/components/concepts/access-control/authorizations.md). It is likely your user does not yet have privileges to create process instances. For all the above scenarios, contact your administrator to understand why no processes are displayed. ## Public start forms Public start forms were removed in Camunda 8.10 together with Tasklist V1. To start processes with forms in the current version, use authenticated Tasklist starts, or build your own public-facing application with [Camunda Forms](/components/modeler/forms/utilizing-forms.md) and the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). --- ## Using filters When you open the **Tasks** page, you will see the list of all tasks. This is a starting point for managing your workload. ![tasklist-all-tasks](img/task-filters/tasklist-all-tasks.jpg "All open tasks") ## Apply a filter As you browse through the list, you can utilize filters to locate specific tasks and organize your workload. ### Select a filter You can select one of the default filters or [create a new filter](#create-new-filter). Default filters: - All open tasks - Assigned to me - Unassigned - Completed tasks ![tasklist-default-filters](img/task-filters/tasklist-default-filters.png "List of the default filters") ## Create new filter To create a new filter, click to apply a new filter. This action will open a filter dialog. In the dialog, you can define various rules based on task attributes. The supported attributes are: - Assignees and candidate groups - Status - Processes tasks belong to - Dates (due date and follow up date) - Task ID - Task variables ![tasklist-filter-dialog-with-advanced-options](img/task-filters/tasklist-filter-dialog-with-advanced-options.jpg "Available filter attributes") After defining the rules, click **Apply** to apply the filter. The system will then filter the tasks according to your criteria, displaying only the relevant tasks. ## Save filter for future use If you need to use the filter again, you can save it: 1. Click **Save**. 2. You will be prompted to enter a name for the filter. Choose a descriptive name that will help you recognize it later. 3. Click **Save and apply**. ![tasklist-save-filter-dialog](img/task-filters/tasklist-save-filter-dialog.jpg "Add a descriptive name for the filter") The next time you need this filter, select it from your saved filters. ![tasklist-applied-filter-tasks](img/task-filters/tasklist-applied-filter-tasks.jpg "List of tasks for the applied filter") :::note Filters you create are saved locally to your device. Therefore, while you can access and reuse them on the same device, these filters will not be available if you switch to a different device. ::: ## Update saved filter You can edit or [delete](#delete-a-filter) filters that you saved. ![tasklist-saved-filter-options](img/task-filters/tasklist-saved-filter-options.jpg "Saved filter options") To change the criteria of an existing filter, take the following steps: 1. Choose the filter you want to update from your list of saved filters, and click the three vertical dots next to its name. 2. Click **Edit**. This action will open the filter dialog with the current settings of the filter. 3. Update the filter criteria as needed. 4. Confirm the changes by clicking **Save and apply**. ![tasklist-edit-filter-dialog](img/task-filters/tasklist-edit-filter-dialog.jpg "Edit filter details") ## Delete a filter To delete a filter, take the following steps: 1. Choose the filter you want to delete from your saved filters, and click the three vertical dots next to its name. 2. Click **Delete**. 3. Click **Confirm deletion** in the dialog. ![tasklist-delete-filter-dialog](img/task-filters/tasklist-delete-filter-dialog.jpg "Confirm filter deletion") After deletion, the default filter will be applied, showing the full list of tasks again. --- ## Overview(Userguide) The user interaction with a task may involve making updates, adding variables, filling out a [Camunda Form](/components/modeler/forms/utilizing-forms.md), or simply reviewing and completing the task. User tasks can be automatically assigned to users and groups in the BPMN process, or they must be self-assigned from Tasklist. Once assigned to a user, the task can be completed. The user can unassign the task if they do not intend to work on it. Tasklist has two main pages: - [Tasks page](#tasks-overview) to manage tasks. - [Processes page](./starting-processes.md) to start processes. ## Tasks queue The **Tasks** page lists all tasks available to a user or user group and allows users to assign themselves a task from the list to work on. On the left side of the page you can see task filters and the queue of tasks. On the right side, details of the selected task are displayed. See an overview of the page structure below: The queue shows the preview of available tasks with the following information: - Task name - Name of the process the task belongs to - Task context description ([it can be optionally configured](/components/concepts/variables.md#context-variable)) - Assignee - Priority - Creation date - Due date - Follow up date ## Task details Select a task from the list to view its details. The task includes a form that has to be filled out and submitted to complete a task. ![tasklist-task-details-form](./img/tasklist-task-details-form.png "Task completion form") If the task doesn’t have a form, it will display task variables. ![tasklist-with-variables-claimed-by-me](img/tasklist-with-variables-claimed-by-me_light.png "Task variables") ### View process diagram From the task detail page you can switch to the **Process** tab. This provides a visual representation of the BPMN diagram the task is part of, and may help you understand how an individual task fits into the larger workflow, what activities happened earlier, and what’s coming next. ![tasklist-process-diagram](./img/tasklist-task-details-process-diagram.png "Process diagram preview") ## Filtering To group tasks and quickly find relevant assignments, use [task filters](./using-filters.md). [![tasklist-default-filters](img/task-filters/tasklist-default-filters.png "Task filters")](./using-filters.md) ## Ordering Click the order icon ![order-icon](img/order-icon.png) to order the tasks. You can arrange them by the date of creation, due date, follow-up date, or priority. The follow-up date defines the latest time you should start working on a task. The due date provides a deadline for when the task should be finished. The priority defines urgency of a task in relation to others. ![tasklist-task-ordering](img/tasklist-task-ordering.png "Order tasks by dates") ## Document handling Tasklist users may view and download files displayed in the task's form. See additional details and limitations in [document handling](/components/document-handling/getting-started.md). --- ## Architecture There are four main components in Zeebe's architecture: - Clients - Gateways - Brokers - Exporters ![zeebe-architecture](assets/zeebe-architecture.png) In Camunda 8, you work exclusively with clients. Gateways, brokers, and exporters are pre-configured to provide the service, but are not accessible. In local or private cloud deployments, all components are relevant. ## Clients Clients send commands to Zeebe to: - Deploy processes - Carry out business logic - Start process instances - Publish messages - Activate jobs - Complete jobs - Fail jobs - Handle operational issues - Update process variables - Resolve incidents Client applications can be scaled up and down separately from Zeebe. The Zeebe brokers do not execute any business logic. Clients are libraries you embed in an application (e.g. a microservice that executes your business logic) to connect to a Zeebe cluster. Clients connect to the Zeebe Gateway via a mix of REST and [gRPC](https://grpc.io). While REST can be served over any HTTP version, the gRPC part of the API requires an HTTP/2-based transport. To learn more about how REST is used in Zeebe, review the [Orchestration Cluster API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). To learn more about gRPC in Zeebe, review the [Zeebe API (gRPC)](/apis-tools/zeebe-api/grpc.md). Check out the [API & tools section](/apis-tools/working-with-apis-tools.md) to find clients in your programming language of choice or create your own. ### Job workers A job worker is a Zeebe client that uses the client API to first activate jobs, and upon completion, either complete or fail the job. ## Gateways A gateway serves as a single entry point to a Zeebe cluster and forwards requests to brokers. The gateway is stateless and sessionless, and gateways can be added as necessary for load balancing and high availability. ## Brokers The Zeebe Broker is the distributed workflow engine that tracks the state of active process instances. Brokers can be partitioned for horizontal scalability and replicated for fault tolerance. A Zeebe deployment often consists of more than one broker. It's important to note that no application business logic lives in the broker. Its only responsibilities are: - Processing commands sent by clients - Storing and managing the state of active process instances - Assigning jobs to job workers Brokers form a peer-to-peer network in which there is no single point of failure. This is possible because all brokers perform the same kind of tasks and the responsibilities of an unavailable broker are transparently reassigned in the network. ## Exporters The exporter system provides an event stream of state changes within Zeebe. This data has many potential uses, including but not limited to: - Monitoring the current state of running process instances - Analysis of historic process data for auditing, business intelligence, etc. - Tracking [incidents](/components/concepts/incidents.md) created by Zeebe The exporter includes an API you can use to stream data into a storage system of your choice. Zeebe includes an out-of-the-box [Elasticsearch exporter](https://github.com/camunda/camunda/tree/main/zeebe/exporters/elasticsearch-exporter), and other [community-contributed exporters](https://github.com/camunda-community-hub/awesome-camunda-platform-8) are also available. --- ## Batch operations(Technical-concepts) When you create a [batch operation](../../concepts/batch-operations.md), you provide filter criteria (such as process definition, version, or state) rather than a specific list of instances. The system then: 1. Distributes the operation across all partitions in your cluster 2. Each partition queries the secondary database to find matching instances 3. Executes operations in parallel across partitions 4. Tracks progress and aggregates results This distributed approach enables efficient processing of large numbers of instances while maintaining cluster performance. Batch operations execute concurrently with regular partition processing—they do not block it. Just as multiple process instances can run in parallel on the same partition, batch operations process their target instances alongside all other partition activity without interruption. ## Prerequisites To use batch operations, you need: - A Zeebe cluster with secondary storage configured (Elasticsearch or OpenSearch) - Appropriate [authorization permissions](../../concepts/batch-operations.md#authorization) for the operations you want to perform - Access to one of the following: - [Operate UI](/components/operate/operate-introduction.md) (for basic operations) - [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) (for full lifecycle management) - [Camunda Java client](/apis-tools/java-client/getting-started.md) (for programmatic access) ## Batch operation components A Zeebe batch operation always consists of two parts: - **The command:** The batch operation type determines the action performed on the process instances. For example, the type `MIGRATE_PROCESS_INSTANCE` means a request to migrate process instances to a new process definition version. Some batch types require more details, such as a migration plan for process instance migration or a modification plan for process instance modifications. - **The batch operation items:** The items are not directly defined at creation. Instead, a filter describes them. This filter is applied to the configured secondary database (for example, Elasticsearch) to identify matching process instances. ## Batch operation lifecycle Batch operations follow a structured lifecycle with distinct phases. Understanding these phases helps you monitor progress and troubleshoot issues. ### 1. Creation phase **What happens:** - User submits a batch operation request with filter criteria - System performs validation checks - The operation is distributed to all partitions in the cluster - A **leader partition** is designated (the partition that first processes the creation command) - All other partitions become **follower partitions** - A unique batch operation key is generated and assigned **Duration:** Milliseconds to seconds **What can go wrong:** See [Creation request validation](#creation-request-validation) below. ### 2. Initialization phase **What happens:** - Each partition queries the secondary database using the provided filter - Results are paginated and split into manageable chunks to avoid overwhelming exporters (each item in a chunk triggers a separate export operation) - The total number of items to process is determined and fixed at this point - Signals are sent to start the execution phase once initialization is complete **Duration:** Seconds to minutes, depending on the number of matching instances, cluster size, and whether other batch operations are already queued for initialization. If multiple batch operations are created, they are initialized sequentially. Each operation must finish its initialization phase before the next one begins. **API visibility:** In [Get Batch Operation](../../../apis-tools/orchestration-cluster-api-rest/specifications/get-batch-operation.api.mdx) API responses: - The `operationsTotalCount` becomes eventually accurate once initialization completes on all partitions - The batch operation `state` transitions from `CREATED` to `ACTIVE` once initialization finishes and execution is triggered **What can go wrong:** See [Initialization failures](#initialization-failures) below. #### Dependency on secondary storage Although the broker's internal state stores process instance data, the secondary storage is queried because: - RocksDB (our internal state database) is a key-value store, not optimized for complex queries. - The secondary database (for example, Elasticsearch) efficiently supports complex filtering. - The user interface enables filter-based batch creation, which RocksDB cannot support. - Pagination and large result set handling is better supported by secondary databases. ### 3. Execution phase **What happens:** - Each partition processes its assigned items independently - Individual commands (for example, `CANCEL`, `MIGRATE`) are executed on each process instance - Progress is tracked and can be monitored - Failed items are recorded with error details **Duration:** Minutes to hours (depending on the number of instances and operation type) **Key characteristics:** - **Fire-and-forget execution**: Individual operation commands are dispatched immediately without blocking. While the system does not wait for each command to complete before dispatching the next one, you can still monitor overall batch progress and view failed items through the monitoring APIs. - **Independent processing**: Each partition works independently, enabling parallel execution across the cluster **What can go wrong:** See [Execution failures](#execution-failures) below. ### 4. Completion phase **What happens:** - Follower partitions report completion or failure to the leader partition - Leader partition aggregates results from all partitions - Final status is determined (`COMPLETED` if at least one partition succeeded, `FAILED` if all failed) **Duration:** Seconds ## Error handling and recovery ### Creation request validation Before processing begins, the system performs several validation checks: - **Empty filter validation**: Filters cannot be empty or null - **Authorization validation**: User permissions are checked before operation creation - **Command-specific validation**: Each command type has specific requirements (for example, migration plan validity) Failed validations result in immediate rejection with specific error codes and messages (for example, `INVALID_ARGUMENT`, `NOT_AUTHORIZED`). ### Initialization failures Initialization can fail for several reasons: - **Network issues**: Connection problems with the secondary database - **Permission errors**: Insufficient authorization to query the database - **Configuration issues**: Secondary database not properly configured - **Query errors**: Invalid or malformed filter criteria **Error handling behavior:** The system automatically handles initialization failures based on the error type: - **Retryable errors**: Network issues, temporary database unavailability - Uses exponential backoff with configurable retry limits - Maximum delay and retry count can be configured - **Non-retryable errors**: Permission issues, configuration problems - Fail immediately without retries - **Adaptive sizing**: If record size limits are exceeded, page sizes are automatically reduced ### Execution failures Individual items may fail during execution for various reasons: - Process instance was completed or canceled after batch initialization - Incident was already resolved by another operation - Migration or modification plan is invalid for the specific instance - Authorization insufficient for the specific process instance The system marks failed items as `FAILED` and does not retry them within the same batch operation. ### Recovery strategies - **Retry failed batches**: Create a new batch operation with the same filter - **Partial recovery**: Use more specific filters to target only failed items - **Monitoring**: Use provided APIs to track which items failed and why ## Lifecycle management Batch operations support several lifecycle management operations: ### Suspend and resume - **Suspend**: Temporarily stops execution of a running batch operation - **Resume**: Restarts execution of a suspended batch operation - Operations can be suspended during initialization or execution phases ### Cancel - **Cancel**: Permanently stops a batch operation - Cannot be resumed once canceled - Partially processed items remain in their modified state ## Batch operations in distributed clusters ![distributed-batch-operation](assets/batch-operation.png) Zeebe clusters are distributed systems with multiple brokers and partitions. Each partition manages a subset of process instances. When a batch operation starts: 1. The batch operation (command + filter) is created and distributed to all partitions. 2. Each partition processes the batch independently: - It uses the filter to query relevant process instances from the secondary database. - It applies the batch command to each matched instance. 3. After all partitions finish, the final statuses are collected, and the batch is marked `COMPLETED`. This distributed, asynchronous approach allows parallel processing of many process instances. ### Important considerations #### Partition independence Each partition fetches and processes matching instances independently. At the start of very large batches, the total number of known items may vary until all partitions finish fetching. This is due to multiple paged queries to the secondary database. #### Leader-follower coordination The leader partition coordinates the overall operation status while follower partitions focus on execution. Inter-partition communication ensures consistent final status reporting. ## How to create and monitor batch operations You can create batch operations using: - **[Operate UI](/components/operate/operate-introduction.md)**: Start batch operations directly from the Operate interface for process instances and incidents - **[Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md)**: REST endpoints to create, manage, and monitor batches programmatically - **[Camunda Java client](/apis-tools/java-client/getting-started.md)**: APIs for batch operations in Java applications :::warning Important for Operate users When you start a batch operation from Operate UI, the actual instances processed may differ from those displayed in the filter preview. The batch operation uses filter criteria to query the secondary database at execution time, and results may have changed since the preview was generated. The count is fixed when the batch starts and will not include instances created afterward. ::: :::note Lifecycle management (suspend, resume, cancel) is only available via the REST API and Java client, not through Operate UI. More features may be added in future Operate versions. ::: ### Technical monitoring Batch operation performance can be tracked via [Grafana dashboards](/self-managed/operational-guides/monitoring/metrics.md#grafana). **Key metrics to monitor:** - Initialization time and success rate - Execution throughput and latency - Error rates by type and partition - Resource utilization during batch processing - Secondary database query performance ## Performance impact Batch operations share cluster resources with regular process instances, affecting each other's performance. Commands created by batch operations run with the same priority as user-generated commands. ### Performance considerations **During batch creation:** - RocksDB storage impact from batch metadata - Memory usage for operation state management **During initialization:** - Heavy querying of the secondary database - Network bandwidth usage for query results - Partition coordination overhead **During execution:** - Command processing load shared with regular operations - Potential backpressure on high-throughput scenarios - Resource contention with live process instances #### Best practices - **Timing**: Schedule large batch operations during low-traffic periods - **Sizing**: Break very large operations into smaller batches - **Monitoring**: Watch cluster performance metrics during batch execution - **Filtering**: Use precise filters to minimize unnecessary processing :::warning Large batch operations can temporarily impact cluster performance, especially during initialization when partitions query the secondary database individually and in parallel. ::: :::warning Heavy querying of the secondary database can notably affect its performance, especially for large batches with broad filters. ::: ### Configuration Batch operation behavior can be configured through broker settings under `camunda.processing.engine.batch-operations.*`. All settings are validated at startup, and invalid values will cause the broker to fail with a descriptive error message. :::note Default values are optimized for typical workloads. Only adjust these settings if you experience performance issues or have specific requirements. ::: #### Scheduler settings Controls how frequently the batch operation scheduler checks for work: | Parameter | Type | Default | Description | | ------------------- | -------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `schedulerInterval` | Duration | **`PT1S`** | How often the background scheduler runs to progress initialization and continuation of batch operations.**Impact:** Lower values provide faster responsiveness but increase CPU usage. | #### Chunking and pagination settings Controls how batch operations split large result sets into manageable pieces: | Parameter | Type | Default | Description | | ------------------- | ---- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `chunkSize` | int | **`100`** | Maximum number of items per chunk. This controls both the number of items written per chunk record during initialization and the number of items stored per chunk in RocksDB state.**Note:** The default value of 100 is a reasonable tradeoff between the number of records and exporter performance. Values greater than 3000 are not recommended, as the broker logs a warning. Higher values may lead to performance issues in the exporters and can exceed the 4 MB record size limit.**RocksDB optimization:** A chunk size of 3000 item keys results in approximately 24 KB (31 KB with overhead), which aligns well with RocksDB's 32 KB block size for efficient read/write performance. | | `queryPageSize` | int | **`10000`** | Page size when querying the secondary database during initialization.**For Elasticsearch/OpenSearch:** This interacts with the default 10,000 result window limit. | | `queryInClauseSize` | int | **`1000`** | Maximum number of keys in a single IN clause when querying by key list.**Use case:** Primarily for RDBMS-based secondary databases. | #### Retry and error handling settings Controls how the system handles transient failures when querying the secondary database: | Parameter | Type | Default | Description | | ------------------------- | -------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `queryRetryMax` | int | **`0`** | Maximum number of retry attempts for transient query failures (for example, network timeouts, temporary database unavailability).**Default behavior:** Retries are disabled by default (set to `0`). Set to a higher value to enable retries. | | `queryRetryInitialDelay` | Duration | **`PT1S`** | Initial delay before the first retry attempt.**Behavior:** Each subsequent retry uses exponential backoff. | | `queryRetryMaxDelay` | Duration | **`PT60S`** | Maximum delay between retry attempts.**Constraint:** Must be greater than or equal to `queryRetryInitialDelay`.**Purpose:** Prevents excessive wait times. | | `queryRetryBackoffFactor` | double | **`2.0`** | Multiplier applied to the delay between consecutive retries.**Example:** With factor `2.0` and initial delay `PT1S`, retries occur at 1s, 2s, 4s, 8s, and so on (capped by `queryRetryMaxDelay`). | #### Configuration example ```yaml # In your broker configuration file (for example, broker.yaml) camunda: processing: engine: batch-operations: # Scheduler scheduler-interval: PT1S # Chunking and pagination chunk-size: 100 query-page-size: 10000 query-in-clause-size: 1000 # Retry and error handling query-retry-max: 0 query-retry-initial-delay: PT1S query-retry-max-delay: PT60S query-retry-backoff-factor: 2.0 ``` #### Tuning recommendations **For large batch operations (100,000+ instances):** - Consider reducing `chunkSize` to `500` or lower to reduce memory pressure - Reduce `queryPageSize` if you encounter Elasticsearch/OpenSearch query timeouts **For high-throughput environments:** - Increase `schedulerInterval` to `PT5S` or `PT10S` to reduce scheduler overhead - Monitor partition CPU usage and adjust accordingly **For unreliable network connections:** - Increase `queryRetryMax` to `5` or higher - Increase `queryRetryMaxDelay` to `PT120S` for longer retry windows :::warning The engine enforces a 4MB per-record limit. If initialization queries return very large result sets, the scheduler automatically splits them across multiple chunk records. Prefer tuning `chunkSize` and `queryPageSize` rather than increasing global message size limits. ::: :::tip For backward compatibility, the legacy configuration path `zeebe.broker.experimental.engine.batchOperations.*` is still supported. However, the unified configuration format shown above (`camunda.processing.engine.batch-operations.*`) is recommended for new deployments. ::: #### Exporter configuration If you use the Camunda Exporter (Self-Managed with Elasticsearch/OpenSearch), you can control whether pending batch items are exported immediately at batch creation. ##### Camunda exporter exportItemsOnCreation | Parameter | Type | Default | Description | | -------------------------------------- | ------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `batchOperation.exportItemsOnCreation` | boolean | **`true`** | Controls whether pending batch items are exported to Elasticsearch/OpenSearch immediately when batch initialization starts.**When enabled:** Operate can display a loading spinner indicating pending batch operations. | **When to disable:** For very large batches (100,000+ items), enabling this option causes a temporary spike in write load due to the high volume of document insertions into the secondary database. If you experience performance issues during batch creation, consider setting this to `false`. **Trade-offs:** - **Enabled (`true`)**: Operate UI shows real-time progress indicators, but may cause indexing pressure - **Disabled (`false`)**: Reduces initial indexing load, but Operate UI won't show the progress spinner. Use the REST API batch status endpoints and Grafana metrics for monitoring instead. **Configuration example:** ```yaml # In your broker configuration file (for example, broker.yaml) zeebe: broker: ... exporters: camundaexporter: args: batchOperation: exportItemsOnCreation: false ``` --- ## Clustering Zeebe can operate as a cluster of brokers, forming a peer-to-peer network. In this network, all brokers have the same responsibilities and there is no single point of failure. ![cluster](assets/cluster.png) ## Gossip membership protocol Zeebe implements the [gossip protocol](https://en.wikipedia.org/wiki/Gossip_protocol) to know which brokers are currently part of the cluster. The cluster is bootstrapped using a set of well-known bootstrap brokers, to which the others can connect. To achieve this, each broker must have at least one bootstrap broker as its initial contact point in their configuration: ```yaml --- cluster: initialContactPoints: [node1.mycluster.loc:26502] ``` When a broker is connected to the cluster for the first time, it fetches the topology from the initial contact points and starts gossiping with the other brokers. Brokers keep cluster topology locally across restarts. ## Raft consensus and replication protocol To ensure fault tolerance, Zeebe replicates data across servers using the [raft protocol](). Data is divided into partitions (shards). Each partition has a number of replicas. Among the replica set, a **leader** is determined by the Raft protocol, which takes in requests and performs all the processing. All other brokers are passive **followers**. When the leader becomes unavailable, the followers transparently select a new leader. Each broker in the cluster may be both leader and follower at the same time for different partitions. In an ideal world, this leads to client traffic distributed evenly across all brokers. ![cluster](assets/data-distribution.png) :::note There is no active load balancing across partitions. Each leader election for any partition is autonomous and independent of leader elections for other partitions. This may lead to one node becoming the leader for all partitions. This is not a problem for fault tolerance as the guarantees of replication remain. However, this may negatively impact throughput as all traffic hits one node. To reach a well-distributed leadership again, the [Rebalancing API](/self-managed/components/orchestration-cluster/zeebe/operations/rebalancing.md) can be used in Self-Managed environments. Be aware that this is on a best-effort basis. ::: ## Commit Before a new record on a partition can be processed, it must be replicated to a quorum of brokers, and this majority of followers has to confirm the received record. When the leader received these confirmations from half or more of its followers, the leader **commits** the record. Committing ensures a record is durable, even in case of complete data loss on an individual broker. The exact semantics of committing are defined by the raft protocol. ![cluster](assets/commit.png) A well-balanced replication ensures records can be committed even when one or more brokers will become unavailable, but the majority of brokers are still available. **Odd replication factors** [are recommended](partitions.md#replication). Examples for common replication factors and their quorum: | Replication factor | Description | Quorum | Use case | | :----------------: | --------------------- | ----------------------------------------------------------- | -------------------------------------------------------------------------------- | | 3 | 1 leader, 2 followers | Half or more of 2 followers is 1 follower that confirmed. | Single region, 3 availability zones. One broker can go down without losing data. | | 5 | 1 leader, 4 followers | Half or more of 4 followers are 2 followers that confirmed. | Allows a higher tolerance against the loss of 2 brokers. | The only exception to have **even replication factors** is the [dual region setup](/self-managed/concepts/multi-region/dual-region.md). In this setup, an even replication factor ensures records are always replicated to both regions. In the case of losing a whole region, every new request will be denied, as no replication can get a quorum anymore. All partitions will become unhealthy, and operators start their [failover procedure](/self-managed/deployment/helm/operational-tasks/dual-region-ops.md). No data is lost. Using an odd replication factor in a dual region setup would favor some partitions, where the leader and the majority of followers live in the surviving region, against the partitions that have only a minority of followers survived. This may slow down to detect a region loss, as some process instances still continue while others are stuck. Below is one example for replication and quorum used in the [dual region setup guide](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/dual-region.md#camunda-8-helm-chart-prerequisites): | Replication factor | Description | Quorum | Use case | | :----------------: | --------------------- | ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 4 | 1 leader, 3 followers | Half or more of 3 followers are 2 followers that confirmed. | Exception for dual-region with minimal replication, records always replicated to both regions [following the recommended setup](/self-managed/concepts/multi-region/dual-region.md#zeebe-cluster-configuration). | --- ## Health Health in Zeebe is not a binary status, but can have three different states: - **Healthy**: everything is working as expected. - **Unhealthy**: at least parts of the system are in a degraded state. - **Dead**: one or more parts of the cluster experienced non-recoverable failure. ## Unhealthy When a node in a Zeebe cluster is unhealthy, it means parts of the system may not work. This is often a transient state. For example, when a node is starting, it starts in an unhealthy state, as several components, such as a partition and the workflow engine, are not yet installed or ready for operation. When this is temporary, and the cluster overall converges to a steady healthy state, you can safely ignore these temporary signals. However, if things persist, or if you see the cluster alternate between healthy and unhealthy, it can be an indicator of an underlying issue that requires investigation. :::note Note that even if parts of the cluster are unhealthy, it can be that other parts are still working fine. ::: ## Dead When something is marked as dead in a cluster, it means it failed in a non-recoverable way. This means it **will** require human intervention to recover. This is a rare status, but it can happen if data corruption is detected, for example. You should promptly investigate any components with a dead status. :::note Note that it's possible that only parts of the system has failed, and the rest is still working fine. For example, a dead broker can be due to a single partition which has data corruption; other partitions on the same broker may still be processing and working as expected. ::: ## Cluster The health of a cluster depends on the deployment topology. More specifically, the health of a cluster is based on the expected number of brokers, gateways, partitions, and the replication factor. :::note If using the single application, or embedded gateways - that is, where brokers act as gateways - then you can ignore the gateway part. ::: :::note Starting with 8.8, we consider a `gateway` to be anything which exposes the Camunda 8 REST API, and optionally, the Camunda 8 gRPC API. While this is often the Zeebe Gateway, it can be a single application combining Operate, Tasklist, and the Zeebe Gateway. ::: Informally, a healthy cluster is one where: - The expected number of [brokers and gateways](../../../self-managed/components/orchestration-cluster/zeebe/operations/health.md) report a healthy status. - Every partition has exactly one leader. - There are `N-1` followers per partition (where `N` is the replication factor). More formally, given `G` the number of expected gateways, `B` the number of expected brokers, `P` the partition count, and `R` the replication factor, we can define the health of a cluster as: - There are `G` gateway nodes that report a healthy status via their health check. - There are `B` broker nodes that report a healthy status via their health check. - For every partition from `1..P`: - There is exactly one leader broker. - There are `R-1` follower brokers. For example, given we expect 3 brokers, 3 partitions, and a replication factor of 3, a healthy cluster would show the following topology: ``` Cluster size: 3 Partitions count: 3 Replication factor: 3 Gateway version: 8.7.1 Brokers: Broker 0 - zeebe-0.internal.local Version: 8.7.1 Partition 1 : Leader, Healthy Partition 2 : Follower, Healthy Partition 3 : Follower, Healthy Broker 1 - zeebe-1.internal.local Version: 8.7.1 Partition 1 : Follower, Healthy Partition 2 : Leader, Healthy Partition 3 : Follower, Healthy Broker 2 - zeebe-2.internal.local Version: 8.7.1 Partition 1 : Follower, Healthy Partition 2 : Follower, Healthy Partition 3 : Leader, Healthy ``` We can see that the topology reports the expected number of brokers, and that for every partition there is exactly one leader, two followers, and all partitions are healthy. ## Partition [Partitions](./partitions.md) are distributed across multiple nodes. In a cluster, all partitions have the same replication factor, which defines on how many nodes a partition's data will live. For example, given a replication factor of three, exactly three brokers in the cluster will store the partition's data. In other words, that partition is a distributed system across those three nodes: it is made of discrete parts that exist on different nodes, separated by a network, but it is acts conceptually as a single system. A partition can have the following health status: `HEALTHY`, `UNHEALTHY`, or `DEAD`. A partition is considered healthy if: - It has exactly one healthy leader in the cluster. A healthy leader is one who can: - Replicate to [a quorum of followers](./clustering.md#raft-consensus-and-replication-protocol). - [Process data](./internal-processing.md). - It has at least `floor(N/2)` healthy followers in the cluster. With this amount of followers and the leader, data can be [committed](./clustering.md#commit). Anything short of this represents an unhealthy partition: - Having multiple leaders in a cluster would prevent Zeebe from guaranteeing processing consistency. - Having fewer than `floor(N/2)` healthy followers would prevent the leader from committing. - This would block processing, which would cause all requests to time out, and make the partition functionally unavailable. - Having **no** leader would be similar: nothing would be committed, and as such, nothing processed. A dead partition represents one that has failed in a non-recoverable way. This is exceptional and always requires human intervention. Examples of dead partitions are: - Data corruption was detected. Since the engine is a stream processing application, we cannot skip any entries, meaning everything is stopped. - This can include detection of gaps or missing data. ## Broker The health of a broker is computed from the health of its component tree. While there are many components in the broker, it's sufficient to understand that a broker is considered: - **Healthy** if and only if all components in the tree are healthy. - **Unhealthy** if at least one component is unhealthy. - **Dead** if at least one component is dead. There are many components in a broker, but most importantly, each partition in a broker is a separate, independent component. As such, you can roughly define the health of a broker as the sum of the health of its partitions. The health of a broker is reported via its [status health check](../../../self-managed/components/orchestration-cluster/zeebe/operations/health.md#broker). --- ## Internal processing Internally, Zeebe is implemented as a collection of **stream processors** working on record streams \(partitions\). The stream processing model is used since it is a unified approach to provide: - Command protocol \(request-response\), - Record export \(streaming\), - Process evaluation \(asynchronous background tasks\) Record export solves the history problem and the stream provides the kind of exhaustive audit log a workflow engine needs to produce. ## State machines Zeebe manages stateful entities like jobs and processes. Internally, these entities are implemented as **state machines** managed by a stream processor. An instance of a state machine is always in one of several logical states. From each state, a set of transitions defines the next possible states. Transitioning into a new state may produce outputs/side effects. Let's look at the state machine for jobs: ![partition](assets/internal-processing-job.png) Every oval is a state. Every arrow is a state transition. Note how each state transition is only applicable in a specific state. For example, it is not possible to complete a job when it is in state `CREATED`. ## Events and commands Every state change in a state machine is called an **event**. Zeebe publishes every event as a record on the stream. State changes can be requested by submitting a **command**. A Zeebe Broker receives commands from two sources: - Clients send commands remotely. For example, deploying processes, starting process instances, creating and completing jobs, etc. - The broker itself generates commands. For example, locking a job for exclusive processing by a worker. Once received, a command is published as a record on the addressed stream. ## Stateful stream processing A stream processor reads the record stream sequentially and interprets the commands with respect to the addressed entity's lifecycle. More specifically, a stream processor repeatedly performs the following steps: 1. Consume the next command from the stream. 2. Determine if the command is applicable based on the state lifecycle and the entity's current state. 3. If the command is applicable, apply it to the state machine. If the command was sent by a client, send a reply/response. 4. If the command is not applicable, reject it. If it was sent by a client, send an error reply/response. 5. Publish an event reporting the entity's new state. For example, processing the **Create Job** command produces the event **Job Created**. ## Driving the engine As a workflow engine, Zeebe must continuously drive the execution of its processes. Zeebe achieves this by also writing follow-up commands to the stream as part of the processing of other commands. For example, when the **Complete Job** command is processed, it does not just complete the job; it also writes the **Complete Activity** command for the corresponding service task. This command can in turn be processed, completing the service task and driving the execution of the process instance to the next step. ## Unexpected error handling When the workflow engine encounters an unexpected error while processing a command, it rejects the command. If the command is related to a process instance, it additionally publishes an error event, applying it to the state machine. As a result, the process instance is banned from the workflow engine, while it's data remains accessible. ### Banned process instance Banning the process instance is a safety mechanism to safeguard against incorrect execution of the process and to prevent a single error from jamming the entire partition's stream processor. - A banned process instance will not continue to execute. - A banned process instance cannot be recovered. - You can still cancel a banned process instance. Internally, the workflow engine skips commands that are applicable to executing a banned process instance. Commands related to canceling a banned process instance are not skipped. ## Handling backpressure When a broker receives a client request, it is written to the **event stream** first, and processed later by the stream processor. If the processing is slow or if there are many client requests in the stream, it might take too long for the processor to start processing the command. If the broker keeps accepting new requests from the client, the backlog increases and the processing latency can grow beyond an acceptable time. To avoid such problems, Zeebe employs [flow control](/self-managed/operational-guides/configure-flow-control/configure-flow-control.md) strategies that apply write rate limits and [backpressure](/self-managed/components/orchestration-cluster/zeebe/operations/backpressure.md) to user requests. In the case of backpressure when the broker receives more requests than it can process with an acceptable latency, it rejects some requests. For flow control, it can be used with static write rate limits or throttling which prevents the partition from building an excessive backlog of records not exported. Backpressure is indicated to the client by throwing a **resource exhausted** exception. If a client sees this exception, it can retry the requests with an appropriate retry strategy. If the rejection rate is high, it indicates the broker is constantly under high load and you need to reduce the rate of requests. Alternatively, you can also increase broker resources to adjust to your needs. In high-load scenarios, it is recommended to [benchmark](https://camunda.com/blog/2022/05/how-to-benchmark-your-camunda-platform-8-cluster/) your Zeebe Broker up front to size it correctly. The maximum rate of requests that can be processed by a broker depends on the processing capacity of the machine, the network latency, current load of the system, etc. There is no fixed limit configured in Zeebe for the maximum rate of requests it accepts. Instead, Zeebe uses an adaptive algorithm to dynamically determine the limit of the number of in-flight requests (the requests that are accepted by the broker, but not yet processed). The in-flight request count is incremented when a request is accepted, and decremented when a response is sent back to the client. The broker rejects requests when the in-flight request count reaches the limit. This is not a single static threshold for the whole broker. Zeebe calculates the in-flight count and the limit per partition, and the current limit changes over time based on the configured backpressure algorithm. To observe the current limit, monitor the `zeebe_backpressure_requests_limit` metric. For more details, see [backpressure](/self-managed/components/orchestration-cluster/zeebe/operations/backpressure.md) and [metrics](/self-managed/operational-guides/monitoring/metrics.md#performance-metrics). --- ## Partitions In Zeebe, all data is organized into **partitions**. A **partition** is a persistent stream of process-related events. In a cluster of brokers, partitions are distributed among the nodes so it can be thought of as a **shard**. When you bootstrap a Zeebe cluster, you can configure how many partitions you need. :::note If you've worked with the [Apache Kafka System](https://kafka.apache.org/) before, the concepts presented on this page will sound very familiar to you. ::: ## Usage examples Whenever you deploy a process, you deploy it to the first partition. The process is then distributed to all partitions. On all partitions, this process receives the same key and version such that it can be consistently identified. :::note To learn more about aligning partitions with the same version of deployment and why it is built this way, visit our [Zeebe Chaos blog](https://camunda.github.io/zeebe-chaos/2021/01/26/deployments/#deployments). ::: When you start an instance of a process, the client library then routes the request to one partition in which the process instance is published. All subsequent processing of the process instance happens in that partition. ## Distribution over partitions When a process instance is created in a partition, its state is stored and managed by the same partition until its execution is terminated. The partition in which it is created is determined by various factors. - When a client sends a command `CreateProcessInstance` or `CreateProcessInstanceWithResult`, the gateway chooses a partition in a round-robin manner and forwards the requests to that partition. The process instance is created in that partition. - When a client publishes a message to trigger a **message start event**, the message is forwarded to a partition based on the correlation key of the message. The process instance is created on the same partition where the message is published. - Process instances created by **timer start events** are always created on partition 1. ## Scalability Use partitions to scale your process processing. Partitions are dynamically distributed in a Zeebe cluster and for each partition there is one leading broker at a time. This **leader** accepts requests and performs event processing for the partition. Let's assume you want to distribute process processing load over five machines; you can achieve that by bootstraping five or more partitions. :::note While each partition has one leading broker, _not all brokers are guaranteed to lead a partition_. A broker can lead more than one partition, and, at times, a broker in a cluster may act only as a replication back-up for partitions. This broker will not be doing any active work on processes until a partition fail-over happens and the broker gets elected as the new leader for that partition. ::: ## Partition data layout A partition is a persistent append-only event stream. Initially, a partition is empty. As the first entry is inserted, it takes the place of the first entry. As the second entry comes in and is inserted, it takes the place as the second entry, and so on and so forth. Each entry has a position in the partition which uniquely identifies it. ![partition](assets/partition.png) ## Replication For fault tolerance, data in a partition is replicated from the **leader** of the partition to its **followers**. Followers are other Zeebe Broker nodes that maintain a copy of the partition without performing event processing. We recommend an **odd replication factor**, as it ensures high fault-tolerance and availability. **Even replication factors** have no benefit over the previous odd value and are weaker than the next. For example, a replication factor of four has no benefit over a replication factor of three. A replication factor for four would be weaker than a replication factor of five. ### Roles A replica which takes part in a partition is assigned a role, which can be one of: `leader`, `follower`, or `inactive`. :::note There are other [Raft specific roles](./clustering.md#raft-consensus-and-replication-protocol) which are used as part of the replication process, but are generally not relevant for the operation of a Zeebe cluster. ::: As mentioned, replication of a partition is from the **leader** to its **followers**. This means _there can only be one leader for a partition at any given time_. More generally, a node will a partition role of: - `leader`: a node which is appending new commands to the stream, and replicating them to the **followers**. - `follower`: a node which is actively taking part in replication, and which could become a `leader`. - `inactive`: a node which is still starting, or which has encounted a non-recoverable error. It can happen that nodes will encounter non-recoverable errors, and will explicitly stop a partition - aka take on an `inactive` role. Non-recoverable here simply means that it cannot automatically recover by itself - human intervention, for example, may allow it to recover. An example would be during a rolling update, it could be that a newer version of a node writes data that is not readable by the older version, which would cause it to stall. Recovery in this case would consist of restarting the older node so that it can update, which will let it recover. ## Partition distribution If no other configuration is specified, partitions are distributed in a guaranteed round-robin fashion across all brokers in the cluster, considering the number of nodes, number of partitions, and the replication factor. For example, the first partition will always be hosted by the first node, plus the following nodes based on the replication factor. The second partition will be hosted on the second node and the following to fulfill the replication factor. As an example, the following partition schemes are guaranteed: ### Example 1 #### Context - Number of nodes: 4 - Number of partitions: 7 - Replication factor: 3 #### Partition layout | | Node 1 | Node 2 | Node 3 | Node 4 | | ----------: | :----: | :----: | :----: | :----: | | Partition 1 | X | X | X | | | Partition 2 | | X | X | X | | Partition 3 | X | | X | X | | Partition 4 | X | X | | X | | Partition 5 | X | X | X | | | Partition 6 | | X | X | X | | Partition 7 | X | | X | X | ### Example 2 #### Context - Number of nodes: 5 - Number of partitions: 3 - Replication factor: 3 #### Partition layout | | Node 1 | Node 2 | Node 3 | Node 4 | Node 5 | | ----------: | :----: | :----: | :----: | :----: | :----: | | Partition 1 | X | X | X | | | | Partition 2 | | X | X | X | | | Partition 3 | | | X | X | X | ## Recommendations Choosing the number of partitions depends on the use case, workload, and cluster setup. Here are some rules of thumb: - For testing and early development, start with a single partition. Note that Zeebe's process processing is highly optimized for efficiency, so a single partition can already handle high event loads. - With a single Zeebe Broker, a single partition is usually enough. However, if the node has many cores and the broker is configured to use them, more partitions can increase the total throughput (around two threads per partition). - Base your decisions on data. Simulate the expected workload, measure, and compare the performance of different partition setups. --- ## Process lifecycles In Zeebe, the process execution is represented internally by events of type `ProcessInstance`. The events are written to the log stream and can be observed by an exporter. Each event is one step in a process instance lifecycle. All events of one process instance have the same `processInstanceKey`. Events which belong to the same element instance (e.g. a task) have the same `key`. The element instances have different lifecycles depending on the type of element. ## (Sub-)Process/Activity/Gateway lifecycle ![activity lifecycle](assets/activity-lifecycle.png) ## Event lifecycle ![event lifecycle](assets/event-lifecycle.png) ## Sequence flow lifecycle ![sequence flow lifecycle](assets/pass-through-lifecycle.png) ## Example ![order process](assets/process.png) Given the above process, a successful execution yields the following records in the commit log: Intent Element ID Element type ELEMENT_ACTIVATING order-process process ELEMENT_ACTIVATED order-process process ELEMENT_ACTIVATING order-placed start event ELEMENT_ACTIVATED order-placed start event ELEMENT_COMPLETING order-placed start event ELEMENT_COMPLETED order-placed start event SEQUENCE_FLOW_TAKEN to-collect-money sequence flow ELEMENT_ACTIVATING collect-money task ELEMENT_ACTIVATED collect-money task ELEMENT_COMPLETING collect-money task ELEMENT_COMPLETED collect-money task SEQUENCE_FLOW_TAKEN to-fetch-items sequence flow ... ... ... SEQUENCE_FLOW_TAKEN to-order-delivered sequence flow ELEMENT_ACTIVATING order-delivered end event ELEMENT_ACTIVATED order-delivered end event ELEMENT_COMPLETING order-delivered end event ELEMENT_COMPLETED order-delivered end event ELEMENT_COMPLETING order-process process ELEMENT_COMPLETED order-process process --- ## Protocols Zeebe clients connect to brokers via a stateless gateway. For the communication between client and gateway, a mix of REST and [gRPC](https://grpc.io/) is used. The gRPC protocol is defined using Protocol Buffers v3 ([proto3](https://developers.google.com/protocol-buffers/docs/proto3)), and you can find it in the [Zeebe repository](https://github.com/camunda/camunda/tree/main/zeebe/gateway-protocol). There, you will also find the OpenAPI v3 spec for the REST portion of the gateway API. Note that while gRPC requires HTTP/2, the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) can work with either HTTP/1.1 or HTTP/2. ## What is gRPC? gRPC was first developed by Google and is now an open source project and part of the Cloud Native Computing Foundation. If you’re new to gRPC, see [What is gRPC](https://grpc.io/docs/guides/index.html) on the project website for an introduction. ## Why gRPC? gRPC has many beneficial features that make it a good fit for Zeebe, including: - Supports bi-directional streaming for opening a persistent connection and sending or receiving a stream of messages between client and server - Uses the common HTTP/2 protocol by default - Uses Protocol Buffers as an interface definition and data serialization mechanism–specifically, Zeebe uses proto3, which supports client generation in ten different programming languages. ## Supported clients Currently, Zeebe officially supports a gRPC client in [Java](/apis-tools/java-client/getting-started.md). [Community clients](/apis-tools/community-clients/index.md) have been created in other languages, including C#, Ruby, and JavaScript. If there is no client in your target language yet, you can [build your own client](/apis-tools/build-your-own-client.md) in a range of different programming languages. ## Intercepting calls Zeebe supports loading arbitrary [gRPC server interceptors](self-managed/components/orchestration-cluster/zeebe/zeebe-gateway/interceptors.md) and [Jakarta servlet filters](self-managed/components/orchestration-cluster/zeebe/zeebe-gateway/filters.md) to intercept incoming calls. --- ## Technical concepts This section gives an overview of Zeebe's underlying technical concepts. - [Architecture](architecture.md) - Introduces you to the internal pillars of Zeebe, as well as interfaces for external systems to interact with Zeebe. - [Batch operations](batch-operations.md) - How distributed batch operations work in the Zeebe engine. - [Clustering](clustering.md) - Discusses the internal structure and properties of a Zeebe cluster. - [Partitions](partitions.md) - Sheds light on how Zeebe achieves horizontal scalability. - [Internal processing](internal-processing.md) - Explains the basics of Zeebe's event processing. - [Process lifecycles](process-lifecycles.md) - Expands on the event processing concept and goes into more detail regarding the lifecycles of selected process elements. - [Protocols](protocols.md) - Explains how external clients communicate with Zeebe. In addition to these sections, you may also be interested in our [Best Practices](/components/best-practices/best-practices-overview.md). --- ## Introduction to Zeebe Zeebe is the process automation engine powering Camunda 8. While written in Java, you do not need to be a Java developer to use Zeebe. A workflow engine is an essential part of any process automation tool. We call it an “engine” because it drives business processes from start to finish, no matter how complex the process and decision logic need to be. ## Why Zeebe? Zeebe doesn’t rely on a central database, so there’s no performance bottleneck as process volumes increase. Deliver high throughput by distributing processing across nodes, or add cluster nodes to execute an unlimited number of processes at consistently low latency. Zeebe distributes data across all brokers in a cluster with storage directly on the server filesystem. If one broker goes down, another can replace it with no data loss. This built-in replication mechanism ensures that Camunda can recover from machine or software failure with no human interaction, no data loss, and minimal downtime. For documentation on deploying Zeebe as part of Camunda 8 Self-Managed, refer to the [deployment guide](../../self-managed/components/orchestration-cluster/zeebe/overview.md). ## Get started New to Zeebe? Learn about clustering, partitions, internal processing, and more. --- ## AI usage guidelines Learn how to use Camunda AI features responsibly with guidance on data handling, human oversight, prohibited uses, and AI agents. ## About Camunda AI features Camunda AI features and capabilities currently include: - **Agentic orchestration**: Embedding AI agents into BPMN processes so that an AI model can, within boundaries you define, decide which tools to call to reach a goal. - **Camunda Copilot**: AI-assisted process, form, and decision modeling. Describe what you need in natural language and Camunda generates BPMN diagrams, Camunda Forms, and FEEL expressions. - **Intelligent Document Processing (IDP)**: Automated document analysis and structured data extraction, connecting to your chosen document AI provider. Camunda provides an **application and orchestration layer** that integrates AI models via interfaces. In most cases, the underlying AI models are not developed or operated by Camunda itself. Depending on your setup, AI models may be: - Provided by a third-party provider that Camunda has selected and licensed (for example, in SaaS deployments). - Provided by a third-party provider that you select and contract directly. - Brought and operated by you under a bring-your-own-model approach. The specific models and providers active in your environment are described in the applicable service description and documentation. ## How to use AI services responsibly The following principles apply across all AI service use cases. They reflect both good engineering practice and regulatory expectations. | Principle | What it means in practice | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Security | Use as little data, as few permissions, and as few interfaces as necessary for your use case. Before using any data or system, confirm you are authorized to use it in the way you intend. | | Legality | Comply with all applicable laws, industry regulations, and contractual obligations, including the EU AI Act where it applies to your use case. | | Fairness | Review AI-generated outputs to assess whether they could have a discriminatory effect on individuals or groups. | | Transparency | Document data sources, AI models, versions, prompts, and key decisions. Where applicable law requires it, label AI-generated content as such. | | Monitoring | Monitor AI service usage at intervals appropriate to the risk level of your use case, and keep records of your monitoring activities. | ## What these features are designed for Camunda's AI services are designed and intended for use in non-high-risk scenarios. If your use case qualifies as high-risk, you are responsible for compliance with the applicable legal requirements. [Reach out to Camunda](https://www.camunda.com/services/support-guide/) before proceeding so we can discuss what that means for your specific scenario. ### Use cases that may qualify as high-risk If your use case involves any of the following, take a closer look before deploying: - Recruitment or selection of individuals. For example, filtering job applications or evaluating candidates. - Decisions affecting employment relationships. For example, promotion, termination, or performance monitoring. - Creditworthiness assessment or credit scoring (except systems used solely for fraud detection). - Risk assessment or pricing for life and health insurance. If you're unsure, [contact Camunda](https://www.camunda.com/services/support-guide/). ## Evaluating AI outputs AI-generated outputs may be incorrect, incomplete, biased, or outdated. Always review outputs critically before acting on them, particularly in business-critical, legally relevant, or financially significant contexts. In those contexts, consider implementing a [Human-in-the-Loop (HITL)](/reference/glossary.md#human-in-the-loop-hitl) mechanism: a human review step before the output is acted upon. ## The AI models behind these features Camunda's AI services integrate models from various sources. Here is what you need to know depending on your setup. ### Camunda-provided models If Camunda offers its own AI model as part of an AI service and that model qualifies as a general-purpose AI (GPAI) model under the AI Act, Camunda provides it as a GPAI model without systemic risk unless Camunda notifies you otherwise. ### Fine-tuning and training Where Camunda enables you to refine AI models through fine-tuning or other training methods, be aware that Camunda does not assume responsibility for the resulting model being classified as a GPAI model with systemic risk. Any obligations that arise from such a classification are yours to manage. ### Third-party models If you train or modify a third-party model, you may become a "provider" of that model under the AI Act, with the compliance obligations that role entails. ## Knowing your AI tools In practice, AI literacy means the people in your organization who configure, operate, or make decisions based on Camunda's AI services should have a working understanding of: - What the AI services can and cannot do. - The risks associated with AI-generated outputs, including the possibility of inaccurate, biased, or incomplete results. - The requirements set out in these usage guidelines. - How to correctly interpret and apply AI outputs in your specific context. ### Practical steps How you meet this standard is largely up to you. The AI Act does not prescribe a specific training format. As a starting point, we suggest: - Providing some form of orientation before people in your organization first use Camunda's AI services. - Revisiting that orientation when the AI services change significantly or when use cases evolve. - Keeping a record of what training you have provided, in whatever format suits your organization's existing compliance processes. ## Handling data with care When using Camunda's AI services, you are submitting data, potentially including personal data, to AI models. The following requirements apply. ### Data minimization Use as little data as necessary for your intended purpose. Where possible, use anonymized or pseudonymized data. If you need to use special categories of personal data (such as health data, biometric data, or data revealing racial or ethnic origin) or data relating to minors, ensure a lawful basis exists and, where required, complete a data protection impact assessment (DPIA) first. ### Retention and compliance Follow applicable retention periods and deletion obligations. Ensure compliance with data subject rights and document data provenance, legal bases, and recipients as required by applicable data protection law. ### Confidential information Do not include secrets, credentials, or confidential information in prompts, configurations, or any other input to AI services. This includes: - API keys and passwords. - Internal pricing or financial data. - Product roadmaps. - Confidential contracts or legal documents. Only use data from approved, lawful sources, and comply with all applicable license, usage, and purpose limitation requirements. ### Security Implement appropriate measures against prompt injection, data exfiltration, and service misuse. These risks are specific to AI systems and are worth addressing separately from your general application security practices. ## Using content in AI features ### Input content Before submitting content as input to Camunda's AI services, make sure that: - You have the right to use it in the way you intend, including for AI processing. - It does not infringe third-party intellectual property rights, including copyright, trademark, database rights, and open-source license obligations. - Where the content belongs to a third party, your license covers AI training and the specific use you have in mind. ### Output content Outputs generated by AI services may be subject to copyright or related rights, the legal position varies by jurisdiction and continues to evolve. Before relying on AI-generated outputs commercially or legally, verify that your intended use is permissible. ## What you must not do The following uses of Camunda's AI services are prohibited. If you are unsure if a planned use case falls into any of these categories, [contact Camunda](https://www.camunda.com/services/support-guide/) before proceeding. These uses are prohibited regardless of technical feasibility: - **Manipulation**: Using AI to materially distort people's behavior through covert or manipulative techniques in a way likely to cause significant harm. - **Exploitation of vulnerable groups**: Deliberately exploiting the vulnerabilities of specific groups, such as children or persons with disabilities, to materially distort their behavior. - **Social scoring**: Evaluating or ranking individuals using AI in a way that leads to detrimental or unfair treatment. - **Predictive policing**: Predicting criminal offenses based on profiling or personal characteristics. - **Biometric database scraping**: Collecting facial or biometric data from the internet or camera systems without targeting specific individuals, to build or expand facial recognition databases. - **Biometric categorization**: Inferring sensitive personal characteristics from biometric data, such as racial or ethnic origin, political opinion, religious belief, health data, or sexual orientation. - **Emotion recognition at work or school**: Monitoring the emotional state of employees or learners in workplace or educational contexts. - **Real-time biometric identification in public spaces**: Using AI to identify individuals in real time in publicly accessible spaces for law enforcement purposes. ## Working with AI agents AI agents are AI-powered components that can, with a degree of autonomy, plan and execute multi-step actions to reach a goal, including calling tools, triggering actions in connected systems, and orchestrating sub-processes. Because of this autonomy, they carry higher operational and legal risk than assistive AI features like the Copilot. The requirements below are not about limiting what you can build: Camunda's AI agent framework is designed to be flexible and powerful. They are about helping you deploy agents safely, so that the autonomy you enable stays within the boundaries you intend. ### Before you deploy: planning A small amount of upfront planning makes AI agent deployments significantly safer and easier to manage. Before deploying an agent into production, document: - What the agent is supposed to do, and what it is not supposed to do. - How much autonomy it has. For example, whether it can act immediately or only propose actions for human approval. - What "success" looks like, and under what conditions the agent should be stopped. - Which systems, processes, and interfaces are in scope. ### Governance For any agent running in production, define clear answers to: who owns this use case, who is responsible for the technical implementation, who handles incidents, and how changes are approved. The AI agent has no legal capacity, and everything it does is legally attributed to your organization, so knowing who is accountable matters. ### Pilot before you scale Running a limited pilot before full deployment gives you the opportunity to catch unexpected behaviors in a controlled environment. A good pilot has defined success criteria and a clear process for incorporating what you learn before going broader. ### Human oversight Maintain effective human oversight over any AI agent you deploy. This is also a sound operational principle; autonomous agents acting in production systems can have real-world consequences that are difficult to reverse. At a minimum, human oversight means: - Someone with the authority and technical ability to stop the agent at any time using a [kill switch](/reference/glossary.md#kill-switch) or equivalent mechanism. - Defined limits on what the agent can do. For example, caps on transaction amounts, action frequency, or which systems it can write to. - A human approval step for any action with significant legal, financial, or safety implications. - A regular review process so you can catch drift or unexpected behavior before it becomes a problem. How you implement these controls is up to you. The right approach depends on your use case, your risk tolerance, and your existing operational processes. These guidelines do not prescribe a specific technical implementation. ### Technical readiness Before going to production with an AI agent, ask: - Have we tested for edge cases and unexpected inputs, including adversarial ones like prompt injection? - Do we have monitoring in place to detect errors or anomalous behavior? - Can we roll back if something goes wrong? These are standard questions for any production system and they matter more for AI agents because the failure modes can be less predictable. ### Data and tool access Agents with broad access to data and tools are harder to control and audit. As a general principle, give agents access to only what they need to do their job, the least-privilege principle. In practice this means: - Knowing what data the agent can reach and how sensitive it is. - Restricting the tools and actions available to the agent to those actually needed for the use case. - Keeping production data access as narrow and time-limited as possible. ### Transparency and communication Users interacting with AI-driven processes should know they are doing so. Make sure that: - AI-driven interactions are clearly identified. - Users know how to escalate or get help. - There is a plan for communicating disruptions or incidents, both internally and, where required, externally. ### Bias and fairness Where an AI agent makes or influences decisions in sensitive areas, such as HR, financial access, or similar, build in a periodic fairness review. This does not need to be a formal audit process; it can be as simple as periodically reviewing a sample of agent decisions to check that outcomes are consistent and not systematically skewed. ### Documentation Keep a record of the key decisions you made when setting up and operating your AI agent, including what it does, how it is configured, what controls are in place, and how those have evolved over time. Good documentation makes incidents easier to investigate and changes easier to manage. It also supports your compliance obligations under the AI Act if your use case is ever scrutinized. --- ## Build with AI Build Camunda solutions with AI-ready workflows. Build Camunda solutions with agentic orchestration and MCP integrations. Connect your AI tools to a running Camunda cluster, embed AI agents in BPMN processes, and design faster with Camunda Copilot. Set up your AI development environment ## Orchestrate AI agents Orchestrate AI agents within your BPMN-based workflows, enabling human tasks, deterministic rule sets, and AI-driven decisions to collaborate in a robust, end-to-end process. Set up and build any enterprise automation pattern, from simple task agents to complex multi-agent systems. ## Design with AI Explore these selected AI features to design workflows and processes more easily and faster. ## Integrate via MCP Use MCP servers to let your AI agents interact with Camunda. ## Use AI responsibly Review our [AI usage guidelines](./ai-usage-guidelines.md) for requirements on security, legality, data handling, human oversight, and prohibited use cases. --- ## Create a cluster(Guides) --- ## Build your first AI agent Beginner Time estimate: 45 minutes Get started with Camunda [agentic orchestration](/components/agentic-orchestration/agentic-orchestration-overview.md) by building and running your first [AI agent](/components/agentic-orchestration/ai-agents.md). ## About AI agents represent the practical implementation of agentic process orchestration within Camunda, combining the flexibility of AI with the reliability of traditional process automation. In Camunda, an AI agent refers to an automation solution that uses [ad-hoc sub-processes](/components/modeler/bpmn/ad-hoc-subprocesses/ad-hoc-subprocesses.md) to perform tasks with non-deterministic behavior. In this guide, you will: - Run your AI agent using [Camunda 8 SaaS](https://accounts.cloud.camunda.io/signup) or locally with [Camunda 8 Self-Managed](/self-managed/about-self-managed.md). - Use an [AI Agent connector](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md) to provide interaction and reasoning capabilities to the AI agent. - Use an [ad-hoc sub-process](/components/modeler/bpmn/ad-hoc-subprocesses/ad-hoc-subprocesses.md) to define the tools the AI agent should use. - Integrate a [Large Language Model (LLM)](/reference/glossary.md#large-language-model-llm) into your AI agent. After completing it, you will have an example AI agent running in Camunda 8. ## Prerequisites To build your first AI agent, see the prerequisites below depending on: - Your working environment. - Your chosen model. ### Camunda 8 environment To run your agent, you must have Camunda 8 (version 8.8 or newer) running, using either: - [Camunda 8 SaaS](/components/saas/saas.md). For example, [sign up for a free SaaS trial account](https://accounts.cloud.camunda.io/signup). - [Camunda 8 Self-Managed](/self-managed/about-self-managed.md). For example, follow [Run your first local project](../getting-started-example). ### Supported models The AI Agent connector makes it easy to integrate LLMs into your process workflows, with out-of-the-box support for popular model providers such as Anthropic and Amazon Bedrock. It can also connect to any additional LLM that exposes an OpenAI-compatible API. See [supported model providers](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-subprocess.md#model-provider) for more details. In this guide, you can try the following use cases: | Setup | Model provider | Model used | Prerequisites | | :----------------------- | :-------------------------------------------------------------------------------- | :----------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | SaaS | [Camunda-provided LLM](/components/agentic-orchestration/camunda-provided-llm.md) | Camunda-managed model (for example, Claude Sonnet 4.6) | Camunda 8 SaaS trial or enterprise organization. Camunda-provided LLM available in your organization. No additional LLM provider credentials are required to run this guide. | | Cloud (customer-managed) | AWS Bedrock | Claude Sonnet 4 | An AWS account with permissions for the [Bedrock Converse API](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_Converse.html). Anthropic Claude foundation models using the AWS console. See [AWS documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access-modify.html) for details. | | Local | Ollama | GPT-OSS:20b | [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) running locally. Ollama and GPT-OSS:20b installed. See [Set up Ollama](#set-up-ollama) for details. | :::important Running LLMs locally requires substantial disk space and memory. GPT-OSS:20b requires more than 20GB of RAM to function and 14GB of free disk space to download. ::: ## Step 1: Install the model blueprint To start building your first AI agent, you can use a Camunda model blueprint from [Camunda Marketplace](https://marketplace.camunda.com/en-US/home). In this guide, you will use the [AI Agent Chat Quick Start](https://marketplace.camunda.com/en-US/apps/587865) model blueprint. Depending on your working environment, follow the corresponding steps below. 1. In the [blueprint page](https://marketplace.camunda.com/en-US/apps/587865), click **For SAAS** and select or create a project to save the blueprint. 1. The blueprint BPMN diagram opens in Web Modeler. 1. In the [blueprint page](https://marketplace.camunda.com/en-US/apps/587865), click **For SM** and download the blueprint files from the repository. :::note If you’re using Camunda 8 Run and installed it using the [starter package](./getting-started-example.md#download-the-camunda-8-starter-package), the blueprint was already downloaded as part of it. ::: 2. Open the blueprint BPMN diagram in Desktop Modeler. ### About the example AI agent process The example AI agent process is a chatbot that you can interact with via a [user task form](/components/modeler/forms/camunda-forms-reference.md). This process showcases how an AI agent can: - **Make autonomous decisions** about which tasks to execute based on your input. - **Adapt its behavior** dynamically using the context provided. - **Handle complex scenarios** by selecting and combining different tools. - **Integrate seamlessly** with other process components. The example includes a form linked to the start event, allowing you to submit requests ranging from simple questions to more complex tasks, such as document uploads. :::tip Understand the decision model behind this example To make this agent reliable, treat each activity in the ad-hoc sub-process as a documented tool. Learn why this matters in [AI agents: Why tool documentation in ad-hoc sub-processes matters](/components/agentic-orchestration/ai-agents.md#why-tool-documentation-in-ad-hoc-sub-processes-matters). For a runtime view of what the LLM decides vs. what Camunda orchestrates, see [Design and architecture: How execution works in an AI agent](/components/agentic-orchestration/design-architecture.md#how-execution-works-in-an-ai-agent). For prompt configuration details, see [AI Agent connector: System prompt, user prompt, and tool descriptions](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md#system-prompt-user-prompt-and-tool-descriptions). ::: ## Step 2: Configure the AI Agent connector Depending on your model choice, configure the AI Agent connector accordingly. :::info With Camunda-provided LLM, you do not need additional LLM setup. Camunda-provided LLM is only available in SaaS. It is not available in Self-Managed environments. ::: 1. Verify your organization has **AI features enabled**. Camunda-provided LLM is available automatically when AI features are enabled. 1. Keep the AI Agent connector's default settings from the blueprint. Most AI blueprints default to use Camunda-provided LLM in SaaS. You only need to configure a customer-managed provider if you want custom billing, quotas, or provider control. See [Camunda-provided LLM](/components/agentic-orchestration/camunda-provided-llm.md) for more details. Configure the connector's authentication and template for AWS Bedrock. #### Configure authentication The example blueprint downloaded in step one is preconfigured to use AWS Bedrock. For authentication, it uses the following connector secrets: - `AWS_BEDROCK_ACCESS_KEY`: The AWS Access Key ID for your AWS account able to call the Bedrock Converse API. - `AWS_BEDROCK_SECRET_KEY`: The AWS Secret Access Key for your AWS account. You will configure these secrets differently depending on your working environment. Configure the secrets using the [Console](../components/hub/organization/manage-clusters/manage-secrets.md). Export the secrets as environment variables before starting the distribution. See [Connector secrets](/self-managed/components/connectors/connectors-configuration.md#secrets) for details. See [Amazon Bedrock model provider](../components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-subprocess.md#amazon-bedrock) for more information about other available authentication methods. #### Configure properties In the blueprint BPMN diagram, the AI agent is implemented using the [AI Agent Sub-process connector](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-subprocess.md). You can keep the default configuration or adjust it to test other setups. To do so, use the properties panel: :::tip When configuring connectors, use [FEEL expressions](/components/modeler/feel/language-guide/feel-expressions-introduction.md), by clicking the `fx` icon, to reference [process variables](/reference/glossary.md#process-variable) and create dynamic prompts based on runtime data. ::: Configure your local LLM with Ollama. #### Set up Ollama 1. **Download and install**: Follow [Ollama's documentation](https://docs.ollama.com/quickstart) for details. 1. **Confirm installation**: Check the installed version in a terminal or command prompt by running `ollama --version`. 1. **Start the local server**: Start it using the application, or run `ollama serve` in a terminal or command prompt. 1. **Pull the GPT-OSS:20b model**: If it isn't installed by default, run `ollama pull gpt-oss:20b` in a terminal or command prompt to download the model. 1. **Test**: Ollama serves an API at `http://localhost:11434` by default. To test it, open that URL in a browser or run this command in your terminal: ``` curl -X POST http://localhost:11434/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"model":"gpt-oss:20b","messages":[{"role":"user","content":"Hello!"}]}' ``` #### Configure properties The example blueprint downloaded in step one is preconfigured to use AWS Bedrock. Update the connector's configuration using the Model provider and Model sections to use Ollama instead. **Model provider** 1. Select **OpenAI Compatible** from the **Provider** dropdown. 1. Enter `http://localhost:11434/v1` in the **API endpoint** field. This is Ollama's default API URL. 1. No authentication or additional headers are required for the local Ollama API, so leave the remaining fields blank. **Model** 1. Enter `gpt-oss:20b` in the **Model** field. This field is case-sensitive, so be sure to enter it in all lowercase. ## Step 3: Test your AI agent Deploy and run your AI agent in your Camunda cluster. :::important Whether you are testing your agent in Camunda 8 SaaS or locally with Camunda 8 Self-Managed, make sure you’re running a cluster with version 8.8 or higher. ::: Depending on your working environment, test your agent by following the corresponding steps below. 1. Open [Web Modeler](/components/hub/workspace/modeler/index.md). 1. Select the [**Play**](/components/hub/workspace/modeler/validation/play-your-process.md) tab. 1. Select the cluster you want to deploy and play the process on. 1. Open the Start form and add a prompt for the AI agent. For example, enter "Tell me a joke" in the **How can I help you today?** field, and click **Start instance**. 1. The AI agent analyzes your prompt, decides what tools to use, and responds with an answer. Open the **Task form** to view the result. 1. You can monitor the process execution in [Operate](/components/operate/operate-introduction.md). 1. You can follow up with more prompts to continue testing the AI agent. Select the **Are you satisfied with the result?** checkbox when you want to finish your testing and complete the process. :::tip Instead of using **Play**, you can also test the process within the **Implement** tab using **Deploy & Run**, and use [Tasklist](/components/tasklist/introduction-to-tasklist.md) to complete the form. ::: 1. Deploy the process model to your local Camunda 8 environment using [Desktop Modeler](/components/modeler/desktop-modeler/index.md). 1. Open Tasklist in your browser at http://localhost:8080/tasklist. 1. On the **Processes** tab, find the `AI Agent Chat With Tools` process and click **Start process**. 1. In the start form, add a prompt for the AI agent. For example, enter "Tell me a joke" in the **How can I help you today?** field, and click **Start process**. 1. The AI agent analyzes your prompt, decides what tools to use, and responds with an answer. 1. Select the **Tasks** tab in Tasklist. When the AI agent finishes processing, you should see a `User Feedback` task waiting for you to complete. 1. You can monitor the process execution in [Operate](/components/operate/operate-introduction.md). Open it in your browser at http://localhost:8080/operate. 1. You can follow up with more prompts to continue testing the AI agent. Select the **Are you satisfied with the result?** checkbox when you want to finish the process. ### What to expect during execution When you run the AI agent process: 1. The AI agent receives your prompt and analyzes it together with the configured system prompt and tool descriptions. 1. The LLM determines which tools from the ad-hoc subprocess should be activated. 1. Camunda executes the selected BPMN activities. 1. Tasks can execute in parallel or sequentially, depending on the agent's decisions and process state. 1. Process variables are updated as each tool completes its execution. 1. The agent may iterate through multiple tool calls to handle complex requests. You can observe this dynamic behavior in real-time through Operate, where you'll see which tasks were activated and in what order. ## Step 4: Add your first tool You can customize your AI agent by adding tools. In this section, you will add a tool that fetches weather conditions for a given location using the [Open-Meteo API](https://open-meteo.com/). ### Add a REST connector task 1. Inside the AI agent sub-process, add a new task element. 1. Change the task type to [**REST Outbound Connector**](/components/connectors/protocol/rest.md) using the **Change element** menu. 1. Name the task. For example, `Get current weather`. This name is visible to the LLM as the tool name. ### Write a tool description The LLM selects tools based on their description. Open the **Documentation** field in the properties panel and add a clear description of what the tool does and when to use it. For example: ``` Fetches current weather conditions for a given location. Use this tool when the user asks about weather, temperature, wind, or climate conditions for a city or place. Returns temperature in Celsius, wind speed, and a weather description. ``` :::tip Provide as much context as possible in tool descriptions to help the LLM select the right tool and generate proper inputs. ::: ### Configure the REST connector Set up the HTTP request in the properties panel: 1. In the **Authentication** section, select **None**. 1. In the **HTTP Endpoint** section: - Set **Method** to **GET**. - Set **URL** to the following [FEEL expression](/components/modeler/feel/language-guide/feel-expressions-introduction.md) by clicking the `fx` icon: ```feel "https://api.open-meteo.com/v1/forecast" ``` - Set **Query parameters** to: ```feel { latitude: fromAi(toolCall.latitude, "Latitude of the location to check weather for", "string"), longitude: fromAi(toolCall.longitude, "Longitude of the location to check weather for", "string"), current: "temperature_2m,wind_speed_10m,weather_code" } ``` The [`fromAi()`](../components/modeler/feel/builtin-functions/feel-built-in-functions-miscellaneous.md#fromaivalue) calls tell the AI Agent connector which parameters the LLM must provide. At runtime, the LLM generates the latitude and longitude values based on the user's request, while the `current` parameter is a fixed value that selects which weather fields to return. ### Map the response to `toolCallResult` Each tool within the AI agent sub-process must return its result in a `toolCallResult` variable so the AI Agent connector can pass it back to the LLM. In the **Output Mapping** section, set **Result Expression** to: ```feel { toolCallResult: { latitude: response.body.latitude, longitude: response.body.longitude, temperature_celsius: response.body.current.temperature_2m, wind_speed_kmh: response.body.current.wind_speed_10m, weather_code: response.body.current.weather_code } } ``` This extracts the relevant fields from the Open-Meteo API response and returns them in a structure the LLM can interpret and summarize for the user. ### Test the new tool Deploy the updated process and start a new instance. Try prompts like: - _"What's the weather in Paris right now?"_ - _"Is it windy in Tokyo?"_ - _"Tell me the temperature in New York"_ The LLM will recognize these as weather requests, select the **Get current weather** tool, provide the appropriate latitude and longitude values, and summarize the response in natural language. ### Add your own tools To add more tools to your agent, follow the same pattern: 1. Add a task inside the ad-hoc sub-process and apply a [connector](/components/connectors/introduction.md) or configure a [job worker](/components/concepts/job-workers.md). 1. Write a clear tool name and **Documentation** description so the LLM knows when to use it. 1. Use [`fromAi()`](../components/modeler/feel/builtin-functions/feel-built-in-functions-miscellaneous.md#fromaivalue) in input mappings to define the parameters the LLM must provide. 1. Return `toolCallResult` in the result expression or output mapping. At runtime, each tool call produces one `toolCallResult`, and the ad-hoc multi-instance output collection aggregates them into `toolCallResults` for the AI Agent connector. :::tip For more examples, review the tasks already available in this blueprint and the [AI Agent tool definitions](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-tool-definitions.md) documentation. ::: ## Next steps Now that you’ve built your first Camunda AI agent, you can tailor it further. For example: - Add and configure more tools. - Update the system prompt to adjust the AI agent's behavior. - Experiment with different model providers and configurations in the AI Agent connector. Learn more about [Camunda agentic orchestration](/components/agentic-orchestration/agentic-orchestration-overview.md) and the [AI Agent connector](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md). :::info Camunda Academy Register for the free [Camunda 8 - Agentic Orchestration](https://academy.camunda.com/path/c8-lp-agentic) course to learn how to model, deploy, and manage AI agents in your end-to-end processes. ::: --- ## Run your first Spring Boot or Node.js project with Camunda 8 Beginner 1 hour This guide is tailored for developers who want to implement process automation solutions using Java (Spring) or JavaScript (Node.js). You'll work with a local, self-managed, lightweight Camunda 8 environment. ## Get started with an example project You will: - Run Camunda 8 in a local development environment using **Camunda 8 Run**. - Use the **Camunda Modeler** to deploy and start a business process. - Run workers written in either: - The **Spring (Java) SDK**, or - The **Node.js (JavaScript) SDK** to handle tasks in the process. ## Prerequisites [JDK 21-25](https://www.oracle.com/de/java/technologies/downloads/) [Maven 3](https://maven.apache.org/index.html) [JDK 21-25](https://www.oracle.com/de/java/technologies/downloads/) [Node.js](https://nodejs.org/en) 20+ NPM 11+ ## Download the Camunda 8 starter package Download the Camunda 8 starter package from the following website: [https://developers.camunda.com/install-camunda-8/](https://developers.camunda.com/install-camunda-8/) The starter package includes the following components: - [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) – A simplified, single-application Camunda configuration for a local development environment. - [Camunda Modeler](/components/modeler/about-modeler.md) – An application for modeling BPMN, DMN, and Forms. - [Getting started project](https://github.com/camunda/camunda-8-get-started) – An example project with a simple BPMN process and workers in Java or JavaScript. All of these components are included in the starter package. You do not need to download them separately. The links above are provided for additional information. ## Example project The example project, located in the `camunda-8-get-started/2-order-process-with-service-workers` directory, contains a BPMN process model that represents a simple e-commerce flow with three service tasks. ![Example business process](./img/getting-started-guide-example-process.png) The service tasks in the process are executed by [job workers](/reference/glossary.md#job-worker). The `java` and `nodejs` directories inside `2-order-process-with-service-workers` contain code for job workers that correspond to this process model. ## Instructions Unzip the Camunda 8 starter package. Start Camunda 8 Run by changing into the directory and running the command: ```bash ./camunda-start.sh ``` ```bash .\camunda-start.bat ``` Open the Camunda Modeler application from the starter package. In Camunda Modeler, use:
File > Open File...
to open the file:
camunda-8-get-started/2-order-process-with-service-workers/bpmn/order-process.bpmn
Click the "Rocket" icon to connect to your Camunda 8 Run instance and deploy the model. You can use the pre-configured `c8run (local)` connection. ![Connect to Cluster and deploy model](./img/get-started-example-deploy.png) Click the “Play” icon on the bottom toolbar of Modeler to deploy and start an instance of the process model. You do not need to set any variables for the process. Optionally, you can set a value for the item variable by pasting in:
{`{"item": "special widget"}`}
![Start a new process instance in Camunda Modeler](./img/get-started-example-start-process.png) ## View the process instance and start job workers A [process instance](/reference/glossary.md#process-instance) is now running in the engine. You can view the process instance in **Operate**, the visual operations tool, by navigating to: [http://localhost:8080/operate](http://localhost:8080/operate) Login with the credentials: `demo` / `demo`. There you will see an active process instance. :::note Data needs to sync to Operate, so the process instance may not be visible immediately. Additionally, when the workers are running, process instances will be completed immediately and further process instances will not appear as active. ::: ![Active process instance visible in Operate](./img/get-started-operate-screenshot.png) Next, start the job workers to allow them to perform the work for the service tasks. The workers are configured to connect to the locally-running engine and retrieve available work for the process instance. Change into the Spring SDK directory: ```bash cd camunda-8-get-started/2-order-process-with-service-workers/java ``` Start the workers with the command: ```bash mvn spring-boot:run ``` You can stop the application via Ctrl+C. Change into the Node.js SDK directory: ```bash cd camunda-8-get-started/2-order-process-with-service-workers/nodejs ``` Install dependencies with the command: ```bash npm i ``` Start the workers with the command: ```bash npm start ``` You can stop the application via Ctrl+C. The workers start, connect to the engine, and request work. You will see the workers processing the jobs for the process instance. ![Worker output](./img/get-started-example-output-java.png) ![Worker output](./img/getting-started-example-output.png) ## Complete! Navigate back to Operate and verify that your active process instance in Operate has transitioned to completed. ## Summary You have completed your first process instance by: 1. Running Camunda 8 Run locally. 2. Deploying a process model. 3. Starting a process instance. 4. Completing the service tasks in the process instance using job workers. ## Next steps For further information about the Node.js SDK, refer to [the Camunda 8 JavaScript SDK API documentation](https://camunda.github.io/camunda-8-js-sdk/). Find more examples on the [Developer Portal](https://camunda.com/developers/). ## Teardown You can stop Camunda 8 Run by executing: ```bash ./camunda-stop.sh ``` ```bash ./camunda-stop.bat ```` --- ## Run your first BPMN process with Camunda 8 Beginner Run your first fully-automated BPMN process locally with Camunda 8. ## About In this guide, you’ll deploy and run a **Rocket Launch** BPMN process where you play mission control: you provide a `fuelLevel` variable, and the Camunda 8 engine runs the launch sequence end-to-end: 1. Start the launch sequence. 2. Run a pre-flight check. 3. Decide whether systems are **GO** or **NO-GO**. 4. If **GO**: plot the destination via a DMN decision. 5. Generate a mission report and complete the mission. 6. If **NO-GO**: cancel the mission and end the process. After completing this guide, you will have a BPMN process running locally in Camunda 8 and understand how basic concepts, such as variables, DMN, and FEEL expressions, work together in an automated BPMN workflow. ## Prerequisites - [JDK 21-25](https://www.oracle.com/de/java/technologies/downloads/): Required by Camunda 8 Run to start the engine locally. ## Step 1: Download the Camunda 8 starter package Download the Camunda 8 starter package from the following website: [https://developers.camunda.com/install-camunda-8/](https://developers.camunda.com/install-camunda-8/) The starter package includes the following components: - [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md): A simplified, single-application Camunda configuration for a local development environment. - [Camunda Modeler](/components/modeler/about-modeler.md): An application for modeling BPMN, DMN, and Forms. - [Getting started project](https://github.com/camunda/camunda-8-get-started): Example projects including the Rocket Launch process used in this guide. ## Step 2: Deploy and run your model 1. Unzip the Camunda 8 starter package. 2. Start Camunda 8 Run by changing to its directory and running the following command based on your OS: ```bash ./camunda-start.sh ``` ```bash .\camunda-start.bat ``` 3. Open Camunda Modeler from the starter package. 4. Click **File**, then **Open File** to open the process model `camunda-8-get-started/1-rocket-launch/rocket-launch.bpmn`. 5. Deploy your model by clicking the rocket icon in the bottom toolbar. You can use the pre-configured **c8run (local)** connection and click **Deploy**. This automatically deploys all resources in the project, including the DMN decision table. 6. Click **Play** in the bottom toolbar to start a new [process instance](/reference/glossary.md#process-instance). 7. Run your first model instance by setting the input mission variables. For example: ```json { "missionName": "Odyssey", "fuelLevel": 95 } ``` ## Step 3: Explore your process in Operate 1. Navigate to Operate at [http://localhost:8080/operate](http://localhost:8080/operate) and log in using the `demo` / `demo` credentials. 1. Find the **Rocket Launch** process and click your running instance. :::note Data needs to sync to Operate, so your process instance may not be visible immediately. ::: ### Watch the timer Your process instance pauses at the **Countdown T-10** task for 10 seconds. Open the instance in Operate and watch the token move through the countdown in real time. ### Inspect variables Click a completed instance in Operate and open the **Variables** panel. You can see the variables the process created, such as `destination`, `fuelAfterBurn`, and `missionResult`. Compare the variables between a successful launch and a canceled mission to see how the process logic sets different values. ## Step 4: Try different scenarios Start additional process instances in Modeler with different `fuelLevel` values. Here’s what happens based on your `fuelLevel` input: | `fuelLevel` | What happens | | ----------- | ---------------------------------------------------------------- | | `>= 90` | Launch proceeds — destination set to **Venus** (5 experiments) | | `76 – 89` | Launch proceeds — destination set to **Mars** (5 experiments) | | `75` | Launch proceeds — destination set to **Mars** (3 experiments) | | `50 – 74` | Launch proceeds — destination set to **Moon** (3 experiments) | | `< 50` | **Mission canceled** — the process ends on the cancellation path | :::tip Go to Operate to compare how each process instance takes a different path. ::: ## Step 5: Understand how it works The process uses no external code. All logic is expressed using [FEEL expressions](/components/modeler/feel/what-is-feel.md) in script tasks and a [DMN decision table](/components/modeler/dmn/decision-table.md). | Task | FEEL expression | Output variable | | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | | Pre-flight check | `if fuelLevel >= 50 then "ALL SYSTEMS GO" else ...` (sets status to cancel) | `systemStatus` | | Cancel mission | `"Mission " + missionName + " scrubbed — not enough fuel (" + string(fuelLevel) + "%)"` | `missionResult` | | Plot destination | DMN table: `>= 90` → Venus, `>= 75` → Mars, `>= 50` → Moon | `destination` | | Burn stage 1 | `fuelLevel - 25` | `fuelAfterBurn` | | Run experiments | `if fuelLevel > 75 then 5 else 3` | `experimentsRun` | | Mission report | `"Crew " + missionName + " reached " + destination + "! Fuel: " + string(fuelAfterBurn) + "%. Experiments: " + string(experimentsRun)` | `missionResult` | ## Step 6: Clean up Navigate back to Operate and verify that your process instances have completed successfully (or were canceled, depending on the fuel level). You can now stop your Camunda 8 Run local environment by executing the following command based on your OS: ```bash ./camunda-stop.sh ``` ```bash .\camunda-stop.bat ``` ## Next steps Now that you've run your first BPMN process in Camunda 8, explore more of the platform: - [Build your first AI agent](./getting-started-agentic-orchestration.md). - [Run your first Spring Boot or Node.js project with service workers](./getting-started-example.md). - [Learn more about the BPMN, DMN, and FEEL elements supported in Camunda](/components/concepts/bpmn-dmn-feel.md). --- ## Get started with API orchestration Beginner Camunda 8 SaaS only Time estimate: 15 minutes This guide is designed for users who prefer a low-code approach to process automation. You can follow this tutorial using either a local, Self-Managed lightweight setup, or Camunda 8 SaaS. This guide will walk you through working with a REST connector task as a first time Camunda 8 SaaS user. The REST connector is a [protocol connector](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md#protocol-connectors), where you can make a request to a REST API and use the response in the next steps of your process. :::note New to connectors? Review our [introduction to connectors](/components/connectors/introduction.md) to get familiar with their capabilities, and have a closer look at all of the available [out-of-the-box connectors](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md). :::
Have you signed up for Camunda yet?
The concept of a [connector](/reference/glossary.md#connector) consists of two parts: the business logic is implemented as a [job worker](/reference/glossary.md#job-worker), and the user interface during modeling is provided using an element template. In this guide, you will create a REST connector task in your process, handle the HTTP response, and deploy your process. New to creating a process? Get started by [modeling your first diagram](/components/hub/workspace/modeler/collaboration/design-your-process.md). ## Step 1: Create a REST connector task To use a **REST connector** in your process, follow the steps below: 1. Create a BPMN diagram. To do this, click **New project** within Modeler. 2. Name your project and select **Create new > BPMN diagram**. 3. Give your model a descriptive name and ID. On the right side of the page, expand the **General** section of the properties panel to find the name and ID fields. For this guide, we'll use `API Orchestration Tutorial` for the name and `api-orchestration-tutorial` for the ID. 4. Use Web Modeler to design a BPMN flow with a connector. Create a connector by dragging the rectangular task element from the palette, or click the existing start event and the displayed task element to the right of the start event. 5. Change the task type by clicking on the element and selecting the **Change element** menu icon. Select **REST Outbound connector** in the **Connectors** section. Alternatively, you can directly choose a **REST Outbound connector** by using the context pad. ![Blank task on Web Modeler canvas with properties panel open](img/connectors-blank-task.png) 6. Add a descriptive name using the **General** section in the properties panel. For this guide, we'll use `Make a request`. ## Step 2: Make your REST connector executable ![Connector on Web Modeler canvas with properties panel open](img/connectors-rest-red-properties.png) To make the **REST connector** executable, fill out the mandatory **URL** field in the HTTP Endpoint section (highlighted in red) in the properties panel with `https://catfact.ninja/fact` so we can get a random cat fact from the [Cat Fact API](https://catfact.ninja/) for this example. ## Step 3: Handle your response The HTTP response will be available in a temporary local response variable. This variable can be mapped to the process by specifying **Result Variable**. In the **Response Mapping** section use `={"body" : body}` as the **Result Expression** so you can see the entire JSON object returned if it's successful. ## Step 4: Deploy your process To deploy your process, take the following steps: 1. Drag the bolded circular end event element from the palette and onto the canvas, or by clicking on the final service task, and then the end event element alongside it. Ensure there is an arrow connecting the service task to the end event. 2. In the top right corner click the blue **Deploy** button. Your diagram is now deployed to your cluster. :::note If you have not yet created a cluster, clicking **Deploy** will take you to the console to create a cluster. Once you make your cluster creation request, you will automatically be redirected back to Modeler. The creation of a cluster can take 1 to 5 minutes. To read more about creating clusters, visit our documentation on [creating a cluster](create-cluster.md). ::: 3. Start a new [process instance](/reference/glossary.md#process-instance) by clicking on the blue **Run** button. 4. In the top left corner of the screen, click the square-shaped **Camunda components** button. Navigate to Operate to see your process instance with a token waiting at the service task by clicking **View process instances**. ## Wrap up Congratulations! You successfully built your first API orchestration solution with Camunda 8. Camunda 8 empowers users to automate processes faster. connectors are reusable components that allow you to access APIs without writing code. Don't want to build the process yourself? Click this button to create it from a template in Camunda 8 SaaS, or sign up first. Open model in Camunda 8 Sign up ## Additional resources and next steps - Learn more about Camunda 8 and what it can do by reading [What is Camunda 8](/components/components-overview.md) or watching our [Overview video](https://bit.ly/3TjNEm7) in Camunda Academy. - [Learn about types of connectors](/components/connectors/connector-types.md) - [Use connectors in your BPMN process](/components/connectors/use-connectors/index.md) - [Camunda Academy: Generate a Connector Template from an API Specification](https://academy.camunda.com/c8-h2-generate-connector-from-api/) --- ## Get started with human task orchestration Beginner Time estimate: 15 minutes This guide is designed for users who prefer a low-code approach to process automation. You can follow this tutorial using either a local, Self-Managed lightweight setup, or Camunda 8 SaaS. Camunda 8 allows you to orchestrate processes with human tasks of any complexity. Utilizing [user tasks](/reference/glossary.md#user-task), you can create and assign tasks to users. Then, users can perform their work and enter the necessary data to drive the business process. :::note If you prefer a video-based learning experience or a more complex example, visit [this Camunda Academy course](https://bit.ly/3PJJocB). ::: This guide introduces you to the basics of human task orchestration. You will create a simple process to decide on dinner, and drive the process flow according to that decision.
Have you installed Camunda yet?
Have you signed up for Camunda yet?
Take the following five steps to create and run your first process with a human in the loop: ## Step 1: Create a new process In this step, you will design a process that demonstrates how to route the process flow based on a user decision. In this example, you will create a process to decide what is for dinner. ### Create a new file 1. Every file in Web Modeler requires a project. Within Modeler, click **New project**. 2. Name your project and select **Create new > BPMN diagram**. 3. Give your file a descriptive name. In this case, name it `Decide for Dinner`. 4. Make sure to name the process itself as well. Click the empty canvas, and specify the process name and technical ID in the properties panel on the right side of the screen. This specifies how the process will appear in other tools of Camunda 8. Within Desktop Modeler, select **BPMN diagram** under **Create a new file**. ### Design the process :::note To run this guide, make sure to be in **Implement** mode to specify the technical details of the process. ::: 1. A **start event** is automatically added to the canvas. Click it to display configuration and append options. 2. Click the rectangular **Append Task** icon to append a task. 3. Enter a descriptive name for the task, such as `Decide what's for dinner`. 4. Change the task type by clicking on the element and selecting the **Change element** menu icon. Select **User Task**. 5. Select the user task and click on the diamond-shaped icon to append an exclusive gateway. The gateway allows to route the process flow differently, depending on conditions. 6. Select the gateway and append a task by clicking the task icon. Repeat it to create a second process flow. Name the tasks based on what the user decides to eat: in this case, we've named ours `Prepare chicken` and `Prepare salad`. 7. To route the user to the right task, add [expressions](/components/concepts/expressions.md) to the **sequence flows**. Sequence flows are represented by arrows connecting the gateway to the tasks. To add an expression, click on a sequence flow to view the **properties panel**, and open the **Condition** section. 8. Verify the sequence flows have the following expressions: `meal = "Salad"` on one side, and `meal = "Chicken"` on the other. You will define the variable `meal` later when designing a form for the user task. 9. Connect the split process flows again. Append another exclusive gateway to one of the tasks. Select the other task and drag the arrow-shaped sequence flow tool to connect it to the gateway. 10. Select the gateway and add an **end event** to your process, denoted by the circle with the thick outline. :::note New to BPMN or want to learn more? Visit our [BPMN cheat sheet](https://page.camunda.com/wp-bpmn-2-0-business-process-model-and-notation-en) for an overview of all BPMN symbols. Variables are part of a process instance and represent the data of the instance. To learn more about these values, variable scope, and input/output mappings, visit our documentation on [variables](/components/concepts/variables.md). ::: ## Step 2: Design a form You have now designed the process. To allow the user to make the decision, you will now design a [form](../components/modeler/forms/camunda-forms-reference.md). Forms can be added to user tasks and start events to capture user input, and the user input can be used to route the process flow, to make calls to APIs, or to orchestrate your services. 1. Select the user task you created in **[Step 1](#step-1-create-a-new-process)**. 2. Click the blue **link icon** in the lower right corner. A menu expands that allows you to create a new form. 3. Click **Create new form**. A form will be created and opened in the form editor. The form is automatically named. :::note Don't worry about saving your process diagram. Modeler automatically saves every change you make. ::: 5. Click and drag the **Text view** component (found under Presentation) to the empty form. 6. Open the **General** section in the properties panel and enter a text, such as `What's for dinner?`. 7. Click and drag the **Radio** component to the form to create a radio group. Give it a descriptive name within the properties panel. 8. Additionally, set a **key** which maps to a process variable. The value of the component will be stored in this variable, and it can be read by the process that uses this form. As already defined by the conditions in the process earlier, use the variable `meal`. 9. Scroll down to the **Static options** section of the properties panel to add radio options. Since there are two options for the dinner, add an extra value by clicking on the plus sign. Enter the value `Chicken` with the same label as `Chicken` and enter the value `Salad` with the label as `Salad` in the other value. 1. Create a new Form in Desktop Modeler by navigating to **File -> New File -> Form (Camunda 8)**. 2. Click and drag the **Text view** component (found under Presentation) to the empty form. 3. 4. Open the **General** section in the properties panel and enter a text, such as `What's for dinner?`. 5. Click and drag the **Radio** component to the form to create a radio group. Give it a descriptive name within the properties panel. 6. Additionally, set a **key** which maps to a process variable. The value of the component will be stored in this variable, and it can be read by the process that uses this form. As already defined by the conditions in the process earlier, use the variable `meal`. 7. Scroll down to the **Static options** section of the properties panel to add radio options. Since there are two options for the dinner, add an extra value by clicking on the plus sign. Enter the value `Chicken` with the same label as `Chicken` and enter the value `Salad` with the label as `Salad` in the other value. 8. In your form's properties panel, copy the Form ID for use in your process. :::note If the properties panel for your form doesn't open automatically, navigate to **Window -> Toggle Properties Panel** to open it manually. ::: ## Step 3: Link the form to your process Once the form is designed, you must link it to your process. 1. Click on the project name in the navigation history in the top bar to navigate back, and open the process you created in **[Step 1](#step-1-create-a-new-process)**. 2. Select the user task. Click the blue **form link icon** to open the form menu. 3. Select the form you just created, and click **link** to confirm. 4. You can check if you linked the right form by clicking the form linking icon again. A preview of the form will appear. 1. Open the process you created in **[Step 1](#step-1-create-a-new-process)** by clicking on the process file's name in the top bar. 2. Select the user task, and open the **Form** menu in the properties panel. :::note If the properties panel for your task doesn't open automatically, navigate to **Window -> Toggle Properties Panel** to open it manually. ::: 3. In the Form menu, enter the **Form ID** for the form you created in **[Step 2](#step-2-design-a-form)**. :::note Forms linked in the user task are deployed together with the process. If you make changes to a form, you have to deploy the referencing process again to make the changes appear. ::: ## Step 4: Run your process Your process is now ready to run. Given its human-centric nature, it is well suited to be run in Tasklist. In order to make it accessible from Tasklist, the process must be deployed first. :::tip Human-centric processes involving user tasks seamlessly unfold within Tasklist, offering a cost-effective orchestration solution for human work with forms. However, the versatility of these processes extends beyond Tasklist, encompassing various alternative methods and applications. For instance, users can be redirected to external applications to fulfill tasks, bespoke task applications can be developed for any domain, or interactions with the physical world can be captured through event signals from sensors and IoT devices. ::: ### Deploy and test run 1. Click **Deploy** to deploy the process to your cluster. If you have not yet created a cluster, clicking **Deploy** will take you to Console to [create a cluster](create-cluster.md) first. 2. After you deploy your process, it can be executed on the cluster. There are multiple ways to run a process. This time, click **Run** in Modeler for a test run. :::tip Other options to run a process are to start it via Tasklist, test it in the Play mode, or call it via the API or an inbound trigger. Read more about [run options](/components/hub/workspace/modeler/run-or-publish-your-process.md). ::: :::note Ensure your installation of [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) is running prior to deploying your process. ::: 1. Click the rocket-shaped **Deploy** icon to begin deploying your process, and provide the following configuration: - **Target:** Self-Managed - **Cluster endpoint:** `http://localhost:8080/v2` - **Authentication:** None 2. Click **Deploy** to deploy your process. 3. To run your new process, click the arrow-shaped **Run** icon, and provide your form input as JSON (for example, `{"meal": "chicken"}`). 4. Click **Run** to run your process with the provided variables. ### Check successful start in Operate 1. The process start will be confirmed via a notification message on the screen. Click the **chevron icon** next to **Run** to open more options. Click **View process instances** to see the running process in Operate. 2. In Operate, you will see a visualization of the running process instance. Notice that a green **token** is waiting at the user task. This means that a task is waiting to be worked on in Tasklist. :::tip In production, Operate is used to monitor both long-running and straight-through, high-throughput processes. In development environments, use Operate to confirm if the process flow works as expected. For faster in-place validation during development, use the [Play mode](/components/hub/workspace/modeler/validation/play-your-process.md). ::: 1. Open Operate at `http://localhost:8080/operate`, and select **Processes** from the top bar. 2. In the **Process** panel, use the **Name** drop-down to select your process. 3. A visualization of your running process instance now displays in Operate, and your user task is marked with a green **token** icon. This means that a task is waiting to be worked on in Tasklist. ## Step 5: Complete a user task When the process instance arrives at the user task, a new user task instance is created at Zeebe. The process instance stops at this point and waits until the user task is completed. Applications like [Tasklist](/components/tasklist/introduction-to-tasklist.md) can be used by humans to complete these tasks. In this last step, you will open Tasklist to run the user task you created. :::tip While it may originally seem like the goal of automating a process is to remove humans entirely, efficiently allocating work through user tasks can be even more beneficial. Within this example, we've included a form to demonstrate the completion of a user task. Using the Zeebe or Tasklist API, many other ways to complete a user task are possible, such as redirecting to another application to complete the task, or even listening to IoT devices to capture human interaction with the real world via job workers. ::: 1. Click the **navigation menu icon** next to the Camunda logo in the top bar to open the global navigation, and click **Tasklist** to open the Tasklist application. 1. Open Tasklist at `http://localhost:8080/tasklist`. 2. On the left, you will notice a list of **tasks**. There should be one open task `Decide what's for dinner`. Click this task to open it in the detail view. 3. In the detail view, the form you created in **[Step 2](#step-2-design-a-form)** appears. It is read only since this task is currently unassigned. You have to claim the task to work on it. Next to **Assignee**, click **Assign to me** to claim the task. 4. Select one of the radio options. 5. Click **Complete Task** to submit the form. ![complete a human task in Tasklist](./img/user-task-tasklist.png) 6. To verify your task completion, you can filter by **Completed** tasks in the left task list panel. You can now navigate back to Operate and notice the process instance has continued as the token has moved forward to the selected option. The token moves through the exclusive gateway (also called the XOR gateway), and is used to model the decision in the process. When the execution arrives at this gateway, all outgoing sequence flows are evaluated in the order in which they have been defined. The sequence flow which condition evaluates to ‘true’ is selected for continuing the process. In this case, the token will move through the gateway and (according to the conditional expressions we outlined earlier) to the selected dinner based on the **Decide what's for dinner** user task we completed. If we select **Chicken**, the token moves forward to **Prepare chicken**. If we select **Salad**, the token moves forward to **Prepare salad**. ## Wrap up At this point, you've successfully crafted a human-centered process that routes the process flow based on a decision made by a user. A core value of Camunda 8 lies in the combination of automation and human interaction. Continue with the following resources to learn about intelligent task assignments, flexible forms to capture data and decisions, operational insights to refine task efficiency, and pathways to publish your processes to users via Tasklist or even publicly. Don't want to build the process yourself? Click this button to create it from a template in Camunda 8 SaaS, or sign up first. Open model in Camunda 8 Sign up ## Additional resources and next steps - Watch the [video-based Human Task Orchestration Course](https://bit.ly/3PJJocB). - Learn how to use [BPMN user tasks](/components/modeler/bpmn/user-tasks/user-tasks.md) to route tasks to the right users. - Learn how to [build more complex forms](/components/modeler/forms/utilizing-forms.md) using the form editor. - Learn how to write powerful [expressions](/components/concepts/expressions.md) and utilize [variables](/components/concepts/variables.md) to route complex process flows. - Get an [introduction to Operate](/components/operate/operate-introduction.md). - Learn how to [set up Tasklist](/components/tasklist/introduction-to-tasklist.md) for efficient task management. - Explore start forms and attach the form directly to the start event for authenticated starts in Tasklist. --- ## Get started with Camunda Ready to start? Run your first local Camunda 8 project. Get hands-on with [Camunda 8](https://camunda.io) with our getting started guides. Start by running your first BPMN process, building your first AI agent, and orchestrating human tasks and APIs using [connectors](/reference/glossary.md#connector). Run your first BPMN process with Camunda 8 ## Run your first BPMN process with Camunda 8 Launch a fully-automated Rocket Launch process on your local machine with no code required. Provide a fuel level, and let Camunda 8 run pre-flight checks, plot a destination with DMN, and generate a mission report using only FEEL expressions. Run your first BPMN process ## Run your first Spring Boot or Node.js project with Camunda 8 This guide is for developers who want to implement process automation solutions using Spring Boot or Node.js. You'll work with a local, self-managed, lightweight Camunda 8 environment. Run your first local project ## Build your first AI agent Get started with Camunda [agentic orchestration](/components/agentic-orchestration/agentic-orchestration-overview.md) by building and running your first [AI agent](/components/agentic-orchestration/ai-agents.md). Build your first AI agent ## Orchestrate human tasks This guide is for low-code developers using Camunda 8 SaaS to efficiently allocate work through [user tasks](/reference/glossary.md#user-task). Get started with human task orchestration ## Orchestrate APIs using connectors This guide is for users who prefer a low-code approach to process automation, walking you through working with a REST connector task as a first time Camunda 8 SaaS user. You can follow this tutorial using either a local, Self-Managed lightweight setup, or Camunda 8 SaaS. Get started with API orchestration --- ## Conceptual differences When thinking about migration it is important to understand conceptual differences between Camunda 7 and Camunda 8. ## Architectural differences There are a number of key architectural differences between Camunda 7 and Camunda 8. ### No embedded engine in Camunda 8 Camunda 7 allows embedding the workflow engine as a library in your application. This means both run in the same JVM, share thread pools, and can even use the same data source and transaction manager. In contrast, **the workflow engine** in Camunda 8, Zeebe, is always **a remote resource** for your application, while the embedded engine mode is not supported. If you are interested in the reasons **why** we switched our recommendation from embedded to remote workflow engines, refer to the blog post on [moving from embedded to remote workflow engines](https://blog.bernd-ruecker.com/moving-from-embedded-to-remote-workflow-engines-8472992cc371). The implications for your process solution and the programming model are described below. Conceptually, the only big difference is that with a remote engine, **you cannot share technical [ACID transactions](https://en.wikipedia.org/wiki/ACID)** between your code and the workflow engine. You can read more about it in the blog post on [achieving consistency without transaction managers](https://blog.bernd-ruecker.com/achieving-consistency-without-transaction-managers-7cb480bd08c). ### Different data types In Camunda 7, you can store different data types, including serialized Java objects. Camunda 8 only allows storage of **primary data types or JSON** as process variables. This might require some additional data mapping in your code when you set or get process variables. Camunda 7 provides [Camunda Spin](https://docs.camunda.org/manual/latest/reference/spin/) to ease XML and JSON handling. This is not available with Camunda 8, and ideally you migrate to an own data transformation logic you can fully control (for example, using Jackson). To migrate existing process solutions that use Camunda Spin heavily, you can still add the Camunda Spin library to your application itself and use its API to do the same data transformations as before in your application code. ### Expression language Camunda 7 uses [Java Unified Expression Language (JUEL)](https://docs.camunda.org/manual/latest/user-guide/process-engine/expression-language/) as the expression language. In the embedded engine scenario, expressions can even read into beans (Java object instances) in the application. Camunda 8 uses [Friendly-Enough Expression Language (FEEL)](/components/modeler/feel/what-is-feel.md) and expressions can only access the process instance data and variables. Most expressions can be converted (see [this code in the diagram converter](https://github.com/camunda/camunda-7-to-8-migration-tooling/blob/5e66012c2ef5f301ab6e61b6a3120b13c9c26459/diagram-converter/core/src/main/java/io/camunda/migration/diagram/converter/expression/ExpressionTransformer.java#L21) as a starting point), but you may need to completely rewrite others. Some expressions might even require an additional service task to prepare necessary data that may have been calculated on the fly in Camunda 7. You can also use the [FEEL Copilot](/components/early-access/alpha/feel-copilot/feel-copilot.md) to rewrite complex expressions for you. ### Different connector infrastructure Through Camunda Connect, Camunda 7 provides an HTTP and a SOAP HTTP [Connector](https://docs.camunda.org/manual/latest/reference/connect/). Camunda 8 offers multiple [Connectors](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md) out-of-the-box on a completely different codebase. To migrate existing connectors, consider the following options: - Use the [REST protocol connector](components/connectors/protocol/rest.md) to leverage an out-of-the-box connector. - Create a small bridging layer via custom [job workers](/components/concepts/job-workers.md). ### Multi-tenancy There are several differences between how [multi-tenancy](/components/concepts/multi-tenancy.md) works in Camunda 7 and Camunda 8: 1. The [one engine per tenant approach from Camunda 7](https://docs.camunda.org/manual/develop/user-guide/process-engine/multi-tenancy/#one-process-engine-per-tenant) isn't possible with Camunda 8. Camunda 8 only provides multi-tenancy through a tenant identifier. 2. In Camunda 7, users can deploy shared resources (processes, decisions, and forms) available to all tenants. In Camunda 8, there are no shared resources. This will be added in the future. 3. In Camunda 7, data is mapped to a `null` tenant identifier, meaning resources are shared by default. In Camunda 8, data is mapped to the `` tenant identifier when multi-tenancy is disabled. 4. [Tenant checks in Camunda 7](https://docs.camunda.org/manual/develop/user-guide/process-engine/multi-tenancy/#disable-the-transparent-access-restrictions) can be disabled to perform admin/maintenance operations. This can't be done in Camunda 8, but an admin user can be authorized to all tenants, which would result in the same thing. 5. If a user tries to trigger a command on a resource mapped to multiple tenants in Camunda 7, an exception is thrown, and [the `tenantId` must be explicitly provided](https://docs.camunda.org/manual/develop/user-guide/process-engine/multi-tenancy/#run-commands-for-a-tenant). However, the Camunda 7 engine will try to infer the correct `tenantId` as much as possible. Users in Camunda 7 that are authorized for multiple tenants may perform a lot more operations without providing a `tenantId`. This inference in the Zeebe Broker doesn't happen in Camunda 8, and Zeebe asks users to provide the `tenantId` explicitly. ## Process solutions using Spring Boot With Camunda 7, a frequented architecture to build a process solution (also known as process applications) is composed out of: - Java - Spring Boot - Camunda Spring Boot Starter with embedded engine - Glue code implemented in Java delegates (being Spring beans) This is visualized on the left-hand side of the following image. With Camunda 8, a comparable process solution would look like the right-hand side of the picture and leverage: - Java - Spring Boot - [Camunda Spring Boot Starter](../../apis-tools/camunda-spring-boot-starter/getting-started.md) (embedding the Zeebe client) - Glue code implemented as workers (being Spring beans) ![Diagram showing the spring boot architecture](../img/architecture-spring-boot.png) The difference is that the engine is no longer embedded. If you are interested in the reasons why Camunda switched our recommendation from embedded to remote workflow engines, refer to this blog post on [moving from embedded to remote workflow engines](https://blog.bernd-ruecker.com/moving-from-embedded-to-remote-workflow-engines-8472992cc371). The packaging of a process solution is the same with Camunda 7 and Camunda 8. Your process solution is one Java application that consists of your BPMN and DMN models, as well as all glue code needed for connectivity or data transformation. The big difference is that the configuration of the workflow engine itself is not part of the Spring Boot application anymore. ![Process Solution Packaging](../img/process-solution-packaging.png) :::note Process solution definition is taken from [Practical Process Automation](https://processautomationbook.com/). ::: You can find a complete Java Spring Boot example, showing the Camunda 7 process solution alongside the comparable Camunda 8 process solution in the [Camunda 7 to Camunda 8 migration example](https://github.com/camunda-community-hub/camunda-7-to-8-migration). ## Programming model The programming models of Camunda 7 and Camunda 8 are very similar if you program in Java and use Spring. For example, a worker in Camunda 8 can be implemented as follows (using the [Camunda Spring Boot Starter](../../apis-tools/camunda-spring-boot-starter/getting-started.md)): ```java @JobWorker(type = "payment") public void retrievePayment(ActivatedJob job) { // Do whatever you need to, for example invoke a remote service: String orderId = job.getVariablesMap().get("orderId"); paymentRestClient.invoke(...); } ``` :::info - You can find more information on the programming model in Camunda 8 in this blog post on [how to write glue code without Java Delegates in Camunda Cloud](https://blog.bernd-ruecker.com/how-to-write-glue-code-without-java-delegates-in-camunda-cloud-9ec0495d2ba5). - Check out [code conversion patterns](../migration-tooling/code-conversion/) for more details. ::: ## Other process solution architectures Besides Spring Boot, there are other environments used to build process solutions. ### Container-managed engine (Tomcat, WildFly, WebSphere & co) Camunda 8 doesn't provide integration into Jakarta EE application servers like Camunda 7 does. Instead, Jakarta EE applications need to manually add the Zeebe client library. The implications are comparable to what is described for Spring Boot applications in this guide. ![A diagram showing a container-managed engine](../img/architecture-container-managed.png) ### CDI or OSGI Due to limited adoption, there is no support for CDI or OSGI in Camunda 8. A lightweight integration layer comparable to the [Camunda Spring Boot Starter](../../apis-tools/camunda-spring-boot-starter/getting-started.md) may be provided in the future. ### Polyglot applications (C#, Node.js) When you run your application in Node.js or C#, for example, you exchange one remote engine (Camunda 7) with another (Camunda 8). As Zeebe comes with a different API, you need to adjust your source code. ![A diagram showing a polygot application architecture](../img/architecture-polyglot.png) ## Plugins [**Process engine plugins**](https://docs.camunda.org/manual/latest/user-guide/process-engine/process-engine-plugins/) are not available in Camunda 8, as such plugins can massively change the behavior or even harm the stability of the engine. Some use cases might be implemented using [exporters](/self-managed/concepts/exporters.md) or [interceptors](self-managed/components/orchestration-cluster/zeebe/zeebe-gateway/interceptors.md#implementing-an-interceptor). :::note Exporters are only available for Self-Managed Zeebe clusters and are not available in Camunda 8 SaaS. ::: Migrating **Desktop Modeler Plugins** is generally possible, as the same modeler infrastructure is used. **Cockpit or Tasklist plugins** _cannot_ be migrated. --- ## Camunda 7 to Camunda 8 migration guide Learn how to migrate process solutions developed for Camunda 7 to run on Camunda 8. ## About this guide This guide covers the typical migration journey from Camunda 7 to Camunda 8, describing the decisions to be made along the way, as well as linking to more in-depth material or tooling you might find useful. It is important to understand that **Camunda 8 is not a drop-in replacement for Camunda 7**. It is not sufficient to exchange a library; you might have to adjust your BPMN models, refactor code, and probably also re-architect your solution. This effort greatly depends on how your existing Camunda 7 solution is built. This guide dives deeper into all the aspects involved. ## What you will learn This guide covers the following main aspects involved in migrating from Camunda 7 to Camunda 8. ## Help and further resources Get help with your migration journey from the following additional resources. - The [Camunda forum](https://forum.camunda.io/c/c7-to-c8/) - Camunda Consulting - [Migration workshops](https://camunda.com/wp-content/uploads/2024/03/Camunda_ConsultingWorkshops_5-Migration-Evaluation_2024.pdf) - Professional advisory services - [Camunda Partners](https://camunda.com/de/partners/): - Professional advisory services - Implementation services - [Camunda Academy: Migration Overview](https://academy.camunda.com/c8-migration-overview/) :::info Still need more help? [Reach out to Camunda](https://camunda.com/contact-us/). ::: --- ## Migration journey Although migration projects differ in complexity, there is a common theme in how to approach the migration journey. ## Migration journeys The complexity of Camunda-7-based solutions varies significantly. Solutions range from small implementations with [clean delegates](./migration-tooling/code-conversion.md) to large solutions with hundreds of processes using internal API hacks. This guide covers two typical journeys - a simplified migration journey and an advanced migration journey. ### Simplified migration journey Suitable for solutions that are easy to migrate. If your solution fits into this category, migrating to Camunda 8 is straightforward. ![Diagram showing a simplified migration journey](../img/simple-journey.png) ### Advanced migration journey For more complex solutions that require tackling additional complexities. ![Diagram showing an advanced migration journey](../img/advanced-journey.png) Maybe the truth will be somewhere in the middle for you. As a rule of thumb, Camunda advises keeping things as simple as possible (but of course not simpler). Let's explore the various steps in more detail. You might skip some steps if a simplified journey is sufficient. ## 1Orient yourself First, understand what needs to be done to migrate and what effort to expect. This allows you to plan and budget a migration project properly. Important steps include: - Understand [conceptual differences between Camunda 7 and Camunda 8](./conceptual-differences.md). - [Analyze your solution](#analyze-your-solution) to understand required changes based on your models. - Explore [code conversion patterns](./migration-tooling/code-conversion.md). - [Estimate migration effort](#estimate-migration-effort-and-budget-project) and budget the project. - [Define your target Camunda version](#define-your-target-camunda-version-for-migration) and derive a migration timeline. - [Leverage advisory services](#leverage-guidance-advisory-and-tooling) from Camunda consulting or certified partners. After orientation, you can plan your migration project and start two independent work streams: - [Set up Camunda 8](#2set-up-camunda-8). - [Migrate your solution(s)](#4migrate-solutions). But first, let's understand orientation in more detail. ### Analyze your solution Use the [**Migration Analyzer**](./migration-tooling/index.md#migration-analyzer) to understand migration tasks. ![Sample results from the Migration Analyzer tool](../img/analyzer-result-excel.png) To add some real-world flavor: In a customer scenario, our consultants ran 400 BPMN files through the tool, resulting in roughly 12,000 lines of findings. Analyzing those lines using the spreadsheet reduced that number to roughly 30 different types of tasks, making the migration project manageable. This analysis will help you understand what needs to be done to migrate. ### Estimate migration effort and budget project This analysis is the basis for estimating migration efforts. Our consultants use rule-of-thumb numbers on how much effort typical tasks require. For example, converting a Camunda 7 Java Delegate to a Camunda 8 Job Worker might take one hour if it just involves refactoring the JavaDelegate. It might take more effort if internal Camunda API is used in the delegate. Looking at [code conversion patterns](./migration-tooling/code-conversion.md) allows you to come up with your own estimate, which you can multiply by the number of tasks from the analyzer. In past migration projects, this gave us a good ballpark estimation to plan and budget the project. ### When to migrate? Any new projects should already be started using Camunda 8. For Camunda 7 solutions, understand the support timeline for the Camunda 7 product: - **Camunda 7 CE** (Community Edition) will EOL (end of life) in **October 2025** with a final release (v7.24) on Oct 14, 2025. - There will be no more Camunda 7 CE releases after that date, and the GitHub repo will be archived. The code will still be available, but issues and pull requests will be closed, and the README will reflect the EOL status. - **Camunda 7 EE** (Enterprise Edition) customers will continue to get **patch releases** (security patches & bug fixes) on a rolling basis **till at least 2030**. - Camunda 7 CE users could switch to Camunda 7 EE to benefit from this long-term support to have enough time for migration. While there is some urgency to start migration efforts, you are not yet under hard pressure. Migrating to Camunda 8 gives you additional advantages, which might raise priority for your solution if: - You want to leverage a SaaS offering (for example, to reduce the effort for hardware or infrastructure setup and maintenance). - You need performance at scale and/or improved resilience. - You need certain features that can only be found in Camunda 8 (for example, BPMN message buffering, improved multi-instance handling, the new connectors framework, RPA, IDP, or the improved collaboration features in Web Modeler). ### When not to migrate? You might wonder if there are cases where migration doesn't make sense? Camunda basically sees two scenarios here: - Your solution is in legacy mode and will approach its own end of life before Camunda ends support. - Your solution relies on an architecture that is not possible with Camunda 8. For example, software vendors that embedded Camunda 7 as a Java library into their own build, relying on shipping exactly one self-contained Java application. Refer to [conceptual differences](./conceptual-differences.md) for technical details. Most often, you could still migrate those scenarios if you rearchitect the solution. ### Define your target Camunda version for migration Because of the improved core architecture of Camunda 8, features need to be re-added to Camunda 8 step-by-step. That means the current Camunda 8 version might not yet have sufficient feature parity for your scenario to migrate. Prominent examples are [task listeners](https://roadmap.camunda.com/c/147-user-task-listeners-for-assigning-and-completing-events), which will be introduced with 8.8. If your solution requires this feature, it might make sense to wait for the Camunda 8 version that will provide it. You can **[check the public feature roadmap](https://roadmap.camunda.com/) to understand timelines**. The Camunda version to target might differ per process solution. For example, Process A might not need additional features and can migrate right away, but Process B might use the task listener planned for 8.8. That means you wait for rolling out migration for Process B till that version. As Camunda is currently running [an architecture streamlining initiative](https://camunda.com/blog/2024/04/simplified-deployment-options-accelerated-getting-started-experience/) to improve the core architecture, which [will be released with Camunda 8.8](https://camunda.com/blog/2025/01/camunda-87-88-release-update/). Unless you have time pressure or momentum to lose, Camunda generally recommends waiting for this to happen and targeting a Camunda version \>= 8.8 for migration. ![A diagram showing the targeted version](../img/target-version.png) For example, targeting the 8.9 release doesn't mean you wait for it to happen before thinking about migration. Typically, you can already perform analysis (which you might need to do anyway to understand the right target version), which is also important to plan your project and apply for the required budget on time. Doing the migration tasks can also happen before the actual release, either independently or based on early alpha versions. ![A diagram showing the on-time targeted version](../img/starting-ontime.png) ### Leverage guidance, advisory, and tooling This guide is the main resource walking you through migration. As part of your migration journey, you might also want to consider engaging professional services to help you. The main starting points are: - [Migration evaluation workshop (Camunda)](https://camunda.com/wp-content/uploads/2025/06/Camunda_ConsultingWorkshops_6-Migration-Evaluation_2025_EN.pdf) - Scoping Workshop (Camunda) - Professional advisory services (Camunda, Partners) - Implementation services (Partners) Furthermore, you can use the [migration tooling](./migration-tooling/index.md) and related resources. ## 2Set Up Camunda 8 To run any solution on Camunda 8, you must have a running Camunda 8 installation. If you used an embedded engine with Camunda 7 in the past, this model is no longer possible (see [conceptual differences between Camunda 7 and Camunda 8](./conceptual-differences.md)). This might be new to your organization to operate Camunda in addition to your solution itself. The most successful operating model is to have a central team in the organization caring about Camunda, offering it as a self-service platform to others. This is also described in our [process automation Center of Excellence playbook](https://camunda.com/process-orchestration/automation-center-of-excellence/). :::note We want to unmask some typical misconceptions with Camunda 8: - Camunda 8 does **not** need to be consumed as SaaS! But you can use SaaS if you want. - Camunda 8 does **not** mean there needs to be one huge cluster to rule them all! But you can run big workloads on one cluster. - Camunda 8 does **not** need to be set up for horizontal scalability! But you can set this up if you want. You can run small Camunda 8 installations, one per solution, if you like. They can all be Self-Managed, meaning they run in your own datacenter. With the [architecture streamlining and the RDBMS initiative](https://camunda.com/blog/2024/04/simplified-deployment-options-accelerated-getting-started-experience/), Camunda provides a very simple Java installation (single JAR) that removes installation complexity and is sufficient for many use cases (RDBMS support for a secondary data store is available since Camunda 8.9). ::: There are multiple ways to set up Camunda 8: - Use **Camunda's SaaS** offering: You don't need to install or operate the platform yourself. This is the most convenient and generally recommended option. If you face legal challenges around information security, privacy, and compliance, check the [Camunda Trust Center](https://camunda.com/trust-center/). However, be aware of the following limitations: - You cannot [migrate historical audit data from Camunda 7](./migration-tooling/index.md). - [Multi-tenancy](/components/concepts/multi-tenancy.md) is not currently supported. - Run the platform **Self-Managed**. You might want to look at the [Camunda 8 Run distribution](/self-managed/quickstart/developer-quickstart/c8run.md). RDBMS support is available since Camunda 8.9, removing the need for Elastic Search and allowing a relatively simple setup that Camunda 7 users often like. Still, you can go for more scalable options (see also the [architecture streamlining blog post](https://camunda.com/blog/2024/04/simplified-deployment-options-accelerated-getting-started-experience/)). Refer to [installation guides](/self-managed/deployment/index.md) for details. While setting up Camunda 8 is not part of the core migration journey, it is a prerequisite and should be tackled early in the migration journey to avoid blockers. ## 3Making Core Decisions There are a small number of core decisions that will influence your overall migration journey. Although you might make them later in your journey once you have a better understanding of the consequences, Camunda presents them here so that you have them top of mind for the remainder of this guide. ### Drain out vs big bang There are two possible migration scenarios: Drain out and big bang. ![A diagram showing drain out vs big-bang migration scenarios](../img/drain-out-vs-big-bang.png) Both are valid approaches. Let's briefly look at the differences. - **Drain out**: Keep running the C7-based solution, but run the C8-based (migrated) solution in parallel. New process instances are started in the new one. After some time, no processes are active in Camunda 7 any more, and the C7-based solution can be decommissioned. - **Big bang**: The solution is migrated from Camunda 7 to Camunda 8, including data migration scripts. At one moment in time, the C7-based solution is stopped, the data is migrated, and the new C8-based solution is started. This Camunda 7 solution can be decommissioned right after. Note that a big bang relates to **one process solution only**. So if you run multiple processes, you typically migrate them one by one in multiple big bangs, not in one super big bang. For any of those processes, when you migrate, you flip the switch and are on Camunda 8 for those processes. Let's look at the pros and cons of each approach. **Drain out** ![A diagram showing the drain out scenario](../img/drain-out.png) Pros: - No downtime. - No data migration required. - Easy fallback to old solution in case of problems. Cons: - Requires code switch (for example, forwarding messages to either Camunda 7 or Camunda 8 depending on where the corresponding process was started). - Duplicate tooling (for example, one Tasklist for Camunda 7 and one for Camunda 8 processes; same for operators with Cockpit and Optimize). - Need to operate two solutions at the same time. **Big bang** ![A diagram showing the big-bang scenario](../img/big-bang.png) Pros: - Only one solution is running. - No code switches necessary. - No need to support the legacy codebase. Cons: - Requires data migration, at least runtime instances, which has some limitations (see [migration tooling](./migration-tooling/index.md)). - Complexity of the necessary data migration might drive effort. - Might require downtime. There are some scenarios where process instances are short-lived, and the big bang approach can simply drain out existing instances and then restart without the need for data migration, making migration simpler. **Recommendation** There is no general recommendation for which strategy to use. You could consider the **big bang approach less complex** in many scenarios. However, there are some **indicators to use drain out** instead: - **Short-lived processes**: If processes finish quickly, the drain out happens fast, and it might even be possible to delay new process starts until after the drain out has happened. - **Latency-sensitive processes**: Some use cases can't stand the outage time required for data migration. Maybe you can delay data migration and do it after you have already switched to the C8-based solution. Otherwise, big bang might simply not be feasible or require a more sophisticated data migration strategy. - **Risk**: If your use case carries a lot of risk, you might not feel comfortable with the big bang. Most often, this can be mitigated by properly testing your migration. - **No switching logic required**: Maybe running the C8-based solution in parallel requires almost no effort on your end (as you don't have user tasks or message receive events), then it might be the simpler choice. - **Complexity of data migration**: If data migration turns out to be complex in your solution, draining out might be the better choice. **Migrating your solution landscape step-by-step** If you run multiple process solutions, you best migrate them one by one. You can also drive this idea one step further. If you have complex solutions with multiple BPMN models, call activities, and further dependencies, even migrating those solutions in one go might be overwhelming. In this case, you could apply a microservices mindset and adjust your call activities to be service calls to other components. This way, you could migrate that solution process by process. This is not a general recommendation, just illustrating possibilities to reduce the migration scope. ### Adapt existing code vs refactoring You need to adjust the code of your solution during migration. There are two general possibilities: 1. Keep existing code written for Camunda 7 and add an **adapter** to run it with Camunda 8. The [Camunda 7 Adapter](https://github.com/camunda-community-hub/camunda-7-to-8-migration/tree/main/camunda-7-adapter) is a starting point for doing this. 2. **Refactor** your code to work with Camunda 8. The [code conversion part of this guide](./migration-tooling/code-conversion.md) will focus on this approach. We generally **recommend refactoring your code.** Let's briefly dive into both options. **Adapt existing code** ![A diagram showing the adapt existing code approach](../img/adapt-code.png) While this approach sounds easy at first glance, it typically works only with very cleanly implemented Java Delegates (which could also be simply refactored to Job Workers). Even if using an adapter, you still need to understand architectural implications (such as transactional boundaries) and might need to rewrite some code. It also does not adapt all assets (for example, the Camunda 7 service API or test cases are not adapted). In general, the adapter approach is rarely used. **Refactor your code** ![A diagram showing the code refactoring approach](../img/refactor-code.png) Rewrite your code. This follows typical patterns and might even be automated to some extent using OpenRewrite recipes. See the [code conversion guide](./migration-tooling/code-conversion.md) for details. This approach has the big advantage that the resulting solution will comply with best practices on how Camunda 8 solutions should be written. Furthermore, architectural differences can be better understood while refactoring the solution. Some projects are also happy to clean up a codebase that has grown over time, reducing technical debt as part of the migration effort. The downside is the effort required to refactor. The best strategy is to not over-engineer the approach for small code bases, but to automate migration of big code bases as much as possible. ## 4Migrate solution(s) The main tasks to migrate your solutions to run on Camunda 8 include: - Prepare your Camunda 7 solution (optional) - Convert models - Convert expressions - Refactor code - Improve Camunda 8 Solution (optional) Let's dive into the details of these tasks. ### Prepare your Camunda 7 solution (optional) It can make sense to do a first refactoring step in your Camunda 7 codebase to prepare for easier migration. The advantage of this approach is that you can make changes in a codebase and environment you understand well, while you can still run existing test cases without changes, derisking any changes you make. Typical preparation steps include: - **Extract complex code from your JavaDelegates** into Camunda-independent classes (for example, Spring beans) that are then invoked from the original JavaDelegate. This way, you do a step towards [Clean Delegates](./migration-readiness.md) that will be easy to refactor. - **Remove the usage of internal APIs** or calls that depend on the transactionally integrated architecture of Camunda 7 (for example, querying the HistoryService within a JavaDelgate and expect it to know current changes already). - **Increase test coverage** of the solution to increase confidence that code conversions during migration do not break anything. This is especially important if your C7-based solutions lack good test coverage. To avoid migration effort on test cases, ideally **abstract the test cases from the Camunda version** used, either by using a small own abstraction layer, or by using a test framework such as [Cucumber](https://cucumber.io/) or [Sentinel](https://developer.hashicorp.com/sentinel/docs/intro). - **Structural changes in your BPMN model** to make them runnable on Camunda 8. One trigger could be that you are using constructs that are not supported in Camunda 8 (for example, execution listeners on sequence flows). Or you want to get your models into a state that allows runtime data migration (see [data migration](#migrate-data-optional)), for example by adding artificial wait states before multi-instance tasks (as multiple instance is not supported for runtime data migration). - **Replace JUEL expressions with FEEL**. JUEL is not supported in Camunda 8 (see [conceptual differences](./conceptual-differences.md)). We [plan to release a FEEL plugin for the Camunda 7 platform](https://github.com/camunda/camunda-bpm-platform/issues/2384), allowing you to already rewrite your expressions within your Camunda 7 environment. This change goes beyond simply converting JUEL to FEEL, but also removing any calls to Spring beans that are not possible in FEEL. So you might, for example, add execution listeners invoking the code you need, writing a process variable that then can be evaluated in FEEL. This step is optional and can also be skipped, either because the codebase is already in a clean state or you are feeling confident about directly converting your codebase to Camunda 8. ### Convert models Your BPMN and DMN models need to be adjusted. The [Migration Analyzer](./migration-tooling/index.md#migration-analyzer) can do most changes for you. Depending on how you refactor your code and what elements of Camunda 7 you have used, you can extend or customize the diagram convertion to suit your needs. You can dive into the [technical details of model differences](./migration-tooling/index.md#extending-the-conversion-logic) if you are interested in more detail. ### Convert expressions Your models might contain JUEL expressions, which are not supported in Camunda 8. Simple expressions are directly converted by the [Migration Analyzer](./migration-tooling/index.md#expression-conversion). You can also use the [FEEL Copilot](/components/early-access/alpha/feel-copilot/feel-copilot.md) to rewrite more complex expressions for you. Check the [code conversion patterns section](./migration-tooling/code-conversion.md) for more complicated scenarios. ### Refactor code You need to refactor your code to use Orchestration Cluster APIs only. Most prominently you need to **convert any API calls to Camunda** (for example, RuntimeService) and the glue code attached to process models (for example, JavaDelegates). The [code conversion patterns](./migration-tooling/code-conversion.md) goes into more details how to approach this. Some of those changes might be automated using [OpenRewrite recipes](https://docs.openrewrite.org/). Depending on your architecture you might also have to **re-architect** core parts of your solution. This is especially true if you rely on transaction integration, threading, internal API (such as calling the HistoryService from within a JavaDelegate), or features that are deprecated and thus are not planned for Camunda 8 (such as CMMN). See [conceptual differences between Camunda 7 and Camunda 8](./conceptual-differences.md) for more details on this. As part of this effort you also have to **adjust your test cases**. If you used [camunda-bpm-assert](https://github.com/camunda/camunda-bpm-platform/tree/master/test-utils/assert), a natural choice is to migrate to [Camunda Process Test](https://github.com/camunda/camunda/tree/main/testing/camunda-process-test-java). You can also leverage [code conversion patterns](./migration-tooling/code-conversion.md) or [OpenRewrite recipes](https://docs.openrewrite.org/). If you use other means of testing (such as Cucumber, camunda-bpm-scenario, and so on) you must adjust accordingly. The [Code Migration Detector](https://github.com/camunda-community-hub/camunda-7-to-8-migration/tree/main/code-migration-detector) (based on ArchUnit) can check how much Camunda 7 API is used in your codebase to allow you refactor to reduce the footprint step-by-step. Ideally, you can remove any Camunda 7 dependency at the end of your refactoring. ### Improve Camunda 8 solution (optional) One optional step many customers are doing is to adjust their solution to the latest Camunda best practices after migration. This allows you to reduce technical debt or remove workarounds that either crept into the solution over time or during the migration project for going live quickly. This step does not have to block the rollout of your migrated solution, and is often done after the fact to remove time pressure and incorporate any learnings from operating the Camunda 8 solution. While this step can happen during code conversion already, it might also make sense to do it in a separate later step to reduce the number of changes during conversion to reduce the risk of breaking things. While technically it is a great thing to improve your solution, many migration projects are under pressure to run efficiently (read: minimal effort). Therefore it is advisable to at least separate two different goals: - Migrate the solution - Improve the solution and reduce further technical debt While Camunda sees a lot of value in doing both tasks at the same time - as you touch a lot of the code anyway and will probably also retest your solution thoroughly - if the budget is tight it might be better to focus on migration instead of not getting budgeted for the increased scope. ## 5Migrate data (optional) With your solution code migrated, you also need to look at your production data. Camunda provides the **[Data Migrator](./migration-tooling/data-migrator/index.md)** to be used for this. You might need to customize the data migrator, especially if you used complex data formats in Camunda 7 (for example, Java objects) that need to be converted to something Camunda 8 can handle (for example, JSON). As part of this step you might also need to extract big payloads and binaries (such as documents) into an external data store and reference it from the process (using for example upcoming document handling possibilities). Data to be migrated includes **runtime instances, audit data, and optimize data**. Let's look at it one by one. ![A diagram showing data migration](../img/data-migration.png) **Runtime instances** Currently running process instances. Running means that these process instances in Camunda 7 are not yet ended and currently wait in some [wait-state](https://docs.camunda.org/manual/latest/user-guide/process-engine/transactions-in-processes/#wait-states). This state is persisted in the database and a corresponding data entry needs to be created in Camunda 8, so that the process instance can continue from that state in the new solution. Runtime instance migration has limitations, check [migration tooling](./migration-tooling/index.md) for details. As a result you might need to adjust your process models before migration. You can use [process version migration](https://docs.camunda.org/manual/7.22/user-guide/process-engine/process-instance-migration/) in the Camunda 7 environment to migrate process instances to the version that is migratable to Camunda 8. An interesting strategy can be to define dedicated migration states you want your process instances to pile up in. Another common strategy is to use [process instance modification](https://docs.camunda.org/manual/7.22/user-guide/process-engine/process-instance-modification/) in the Camunda 7 environment to move out of states that are not migratable (for example, process instances within a multiple instance task). Migrating runtime instances is only necessary if you target a big bang migration for your process solution (keep in mind that "big bang" in this context means to switch one process solution from Camunda 7 to Camunda 8 on a defined point in time - it doesn't mean that you have to migrate all your processes at once). If you drain out your Camunda 7 processes, or if they are typically very short-lived, you do not need runtime instance migration. The Data Migrator needs to access the Camunda 7 database, but just uses Orchestration Cluster APIs, which means you can also use this tool when you run on SaaS. **Audit data (aka History)** Process instances left traces, often referred to as "history data". These are audit logs when a process instance was started, what path it took, and so on. It is important to note, that audit data can exist for ended processes from the past, but is also available for currently still running process instances, as those process instances also left traces up to the current wait state. If you need to preserve audit data and want to transfer it to Camunda 8, you can also use the [Data Migrator](./migration-tooling/index.md). Migrating audit **data comes with limitations** (most prominently that you need to run [Camunda 8 with **RDBMS**](/self-managed/concepts/secondary-storage/index.md), a feature introduced with 8.9). Migrating audit data is optional. If you need to do it, consider that audit data migration might need to look at a huge amount of data, which can take time to migrate. But you can run audit data migration beside the normal operations after a successful big bang migration over a period of time, which helps you to keep downtimes low. **Optimize data** The process intelligence tool Optimize keeps a lot of audit data to allow various analysis. This data might also want to be migrated when switching to Camunda 8. Optimize data migration **is currently not yet possible**, but this is planned on the roadmap. ## 6Roll out After you migrated the solution and prepared and tested the data migration (if necessary) you need to roll out your changes. - **Drain out**: In a drain out scenario you need to deploy your new solution next to the existing Camunda 7 solution. Then you need to add switching logic in your code, API gateway, or load balancer logic, which redirects traffic to your new solution. However, you still need to correlate callbacks to waiting process instances to the old solution, and might also present user tasks of both solutions to your users. - **Big bang**: In a big bang scenario, you will typically shutdown the old solution (or at least parts of it for the process under migration), then run the runtime instance migration to make sure all waiting process instances are transferred to the Camunda 8 solution. After this step succeeded, you can start up the new solution and route traffic to it. This approach assumes there can be a downtime of the application. If that is not an option, another alternative is to startup the Camunda 8 solution in parallel and just switch the traffic routing without downtime, and then start to migrate runtime instances afterwards. Audit data migration can run after the switch has happened, after a successful drain out (in parallel to normal operations) or of course during the downtime of a big bang. The best approach depends on the amount of data and the possibility for downtimes. ## 7Decommission Camunda 7 solution Once you have successfully big banged to Camunda 8, or all running processes are drained out, you can decommission your Camunda 7 solution. If this was the last Camunda 7 solution, you can completely decommission the Camunda Platform 7 and related practices. --- ## Migration-ready solutions To implement Camunda 7 process solutions that can be easily migrated, follow these rules and development practices. ## Overview These practices might also inform a refactoring step to prepare your existing Camunda 7 solution for migration: - Implement what we call **Clean Delegates** - concentrate on reading and writing process variables, plus business logic delegation. Data transformations will be mostly done as part of your delegate (and especially not as listeners, as mentioned below). Separate your actual business logic from the delegates and all Camunda APIs. Avoid accessing the BPMN model and invoking Camunda APIs within your delegates. - Use **primitive variable types or JSON** payloads only (no XML or serialized Java objects). - Use **simple expressions** or plug-in **FEEL**. FEEL is the only supported expression language in Camunda 8. JSONPath is also relatively easy to translate to FEEL. Avoid using special variables in expressions, for example `execution` or `task`. - Use your own user interface for task forms or Camunda Forms; the other form mechanisms are not supported out of the box in Camunda 8. - **Don’t** rely on an **ACID transaction manager** spanning multiple steps or resources. - **Don’t expose Camunda APIs** (REST or Java) to other services or frontend applications. - **Don’t call Spring beans in expressions** (for example to leverage Java code to do data transformations). - **Avoid** using any **implementation classes** from Camunda; generally, those with `\*.impl.\*` in their package name. - **Avoid** using **process engine plugins**. - **Avoid** using **Cockpit plugins**. ## Clean delegates Given that Java delegates and the workflow engine are embedded as a library, projects can do dirty hacks in their code. Casting to implementation classes? No problem. Using a ThreadLocal or trusting a specific transaction manager implementation? Yeah, possible. Calling complex Spring beans hidden behind a simple Java Unified Expression Language (JUEL) expression? Well, you guessed it — doable! Those hacks are the real showstoppers for migration, as they cannot be migrated to Camunda 8. In fact, [Camunda 8 increased isolation intentionally](https://blog.bernd-ruecker.com/moving-from-embedded-to-remote-workflow-engines-8472992cc371). Concentrate on what a Java delegate is intended to do: 1. Read variables from the process and potentially manipulate or transform that data to be used by your business logic. 2. Delegate to business logic — this is where Java delegates got their name from. In a perfect world, you would simply issue a call to your business code in another Spring bean or remote service. 3. Transform the results of that business logic into variables you write into the process. The following is an example of a good Java delegate: ```java @Component public class CreateCustomerInCrmJavaDelegate implements JavaDelegate { @Autowired private CrmFacade crmFacade; public void execute(DelegateExecution execution) throws Exception { // Data Input Mapping CustomerData customerData = (CustomerData) execution.getVariable("customerData"); // Delegate to business logic String customerId = crmFacade.createCustomer(customerData); // Data Output Mapping execution.setVariable("customerId", customerId); } } ``` Never cast to Camunda implementation classes, use any ThreadLocal object, or influence the transaction manager in any way. Java delegates should always be stateless and not store any data in their fields. You can migrate such delegates according to our [code conversion patterns](./migration-tooling/code-conversion.md#code-conversion-patterns), for example using [OpenRewrite recipes](./migration-tooling/code-conversion.md#refactoring-recipes-using-openrewrite). ## No transaction managers You should not trust ACID [transaction managers](https://blog.bernd-ruecker.com/achieving-consistency-without-transaction-managers-7cb480bd08c) to glue together the workflow engine with your business code. - Instead, embrace eventual consistency and make every service task its own transactional step. If you are familiar with Camunda 7 lingo, this means that all BPMN elements will be `async=true`. - A process solution that relies on five service tasks to be executed within one ACID transaction, probably rolling back in case of an error, will make migration challenging. ## Don’t expose Camunda API You should apply the [information hiding principle](https://en.wikipedia.org/wiki/Information_hiding) and not expose too much of the Camunda API to other parts of your application. In the following example, you should not hand over an execution context to your `CrmFacade`: ```java // DO NOT DO THIS! crmFacade.createCustomer(execution); ``` The same holds true when a new order is placed, and your order fulfillment process should be started. Instead of the frontend calling the Camunda API to start a process instance, provide your own endpoint to translate between the inbound REST call and Camunda. For example: ```java @RestController public class OrderFulfillmentRestController { @Autowired private ProcessEngine camunda; @RequestMapping(path = "/order", method = POST) public ResponseEntity placeOrder(@RequestBody OrderDto orderPayload) throws Exception { // TODO: Somehow extract data from orderPayload OrderData orderData = OrderData.from(orderPayload); ProcessInstance pi = camunda.getRuntimeService() .startProcessInstanceByKey("orderFulfillment", Variables.putValue("order", orderData)); response.setStatus(HttpServletResponse.SC_ACCEPTED); return ResponseEntity.accepted().body(StatusDto.of("pending")); } } ``` ## Use primitive variable types or JSON Camunda 7 provides flexible ways to add data to your process. For example, you could add Java objects that would be serialized as byte code. Java byte code is brittle and also tied to the Java runtime environment. Another possibility is transforming those objects on the fly to JSON or XML using Camunda Spin. It turned out this was black magic and led to regular problems, which is why Camunda 8 does not offer this any more. Instead, you should perform any transformation within your code before communicating with the Camunda API. Camunda 8 only takes JSON as a payload, which automatically includes primitive values. In the following Java delegate example, you can see Spin and Jackson were used in the delegate for JSON to Java mapping: ```java @Component public class CreateCustomerInCrmJavaDelegate implements JavaDelegate { @Autowired private ObjectMapper objectMapper; //... public void execute(DelegateExecution execution) throws Exception { // Data Input Mapping JsonNode customerDataJson = ((JacksonJsonNode) execution.getVariable("customerData")).unwrap(); CustomerData customerData = objectMapper.treeToValue(customerDataJson, CustomerData.class); // ... } } ``` This way, you have full control over what is happening, and such code is also easily migratable. The overall complexity is even lower, as Jackson is quite well known to Java people — a kind of de-facto standard with a lot of best practices and recipes available. ## Simple expressions and FEEL [Camunda 8 uses FEEL as its expression language](/components/modeler/feel/what-is-feel.md). There are big advantages to this decision. Not only are the expression languages between BPMN and DMN harmonized, but also the language is really powerful for typical expressions. One of my favorite examples is the following onboarding demo we regularly show. A decision table will hand back a list of possible risks, whereas every risk has a severity indicator (yellow, red) and a description. ![An onboarding demo DMN diagram](https://camunda.com/wp-content/uploads/2022/05/Migrating-to-Camunda-Platform-8-image-1-1024x367.png) The result of this decision will be used in the process to make a routing decision: ![A BPMN diagram showing the routing decision](https://camunda.com/wp-content/uploads/2022/05/Migrate-to-Camunda-Platform-8-25052022-image-2-1024x481.png) To unwrap the DMN result in Camunda 7, you could write some Java code and attach that to a listener when leaving the DMN task (this is already an anti-pattern for migration as you will read next). The code is not very readable: ```java @Component public class MapDmnResult implements ExecutionListener { @Override public void notify(DelegateExecution execution) throws Exception { List risks = new ArrayList(); Set riskLevels = new HashSet(); Object oDMNresult = execution.getVariable("riskDMNresult"); for (Object oResult : (List) oDMNresult) { Map result = (Map) oResult; risks.add(result.containsKey("risk") ? (String) result.get("risk") : ""); if (result.get("riskLevel") != null) { riskLevels.add(((String) result.get("riskLevel")).toLowerCase()); } } String accumulatedRiskLevel = "green"; if (riskLevels.contains("rot") || riskLevels.contains("red")) { accumulatedRiskLevel = "red"; } else if (riskLevels.contains("gelb") || riskLevels.contains("yellow")) { accumulatedRiskLevel = "yellow"; } execution.setVariable("risks", Variables.objectValue(risks).serializationDataFormat(SerializationDataFormats.JSON).create()); execution.setVariable("riskLevel", accumulatedRiskLevel); } } ``` With FEEL, you can evaluate that data structure directly and have an expression on the "red" path: ``` = some risk in riskLevels satisfies risk = "red" ``` Additionally, you can even hook in FEEL as the scripting language in Camunda 7 (as explained in [Scripting with DMN inside BPMN](https://camunda.com/blog/2018/07/dmn-scripting/) or [User Task Assignment based on a DMN Decision Table](https://camunda.com/blog/2020/05/camunda-bpm-user-task-assignment-based-on-a-dmn-decision-table/)). However, more commonly you will keep using JUEL in Camunda 7. If you write simple expressions, they can be migrated automatically, as you can see in [the test case](https://github.com/camunda-community-hub/camunda-7-to-8-migration-analyzer/blob/main/core/src/test/java/org/camunda/community/migration/converter/ExpressionTransformerTest.java) of the migration community extension. You should avoid more complex expressions if possible. Very often, a good workaround to achieve this is to adjust the output mapping of your Java delegate to prepare data in a form that allows for easy expressions. Avoid hooking in Java code during an expression evaluation. The above listener to process the DMN result was one example of this, but a more diabolic example could be the following expression in Camunda 7: ```java // DON'T DO THIS: #{ dmnResultChecker.check( riskDMNresult ) } ``` Now, the `dmnResultChecker` is a Spring bean that can contain arbitrary Java logic, possibly even querying some remote service to query whether we currently accept yellow risks or not. Such code cannot be executed within Camunda 8 FEEL expressions, and the logic needs to be moved elsewhere. ## Camunda Forms Finally, while Camunda 7 supports [different types of task forms](https://docs.camunda.org/manual/latest/user-guide/task-forms/), Camunda 8 only supports [Camunda Forms](/components/modeler/forms/utilizing-forms.md) (and will actually be extended over time). If you rely on other form types, you either need to make Camunda Forms out of them or use a bespoke tasklist where you still support those forms. --- ## Code Conversion As Camunda 8 is a complete rewrite of Camunda 7, you must convert your models (BPMN and DMN) and some of your code to work with the Orchestration Cluster REST API. :::tip Easiest path: use the Camunda migration agent skill If you use an [Agent Skills](https://agentskills.io/)-compatible AI coding agent (such as Claude Code), you can run an interactive end-to-end migration with the [Camunda migration agent skill](#camunda-migration-agent-skill). See [Leverage AI for code migration](#leverage-ai-for-code-migration) for details. ::: ## Overview You must especially rewrite code that does the following: - Uses the Client API: Starting process instances, correlating messages, managing tasks, etc. - Implements service tasks, including: - [External tasks](https://docs.camunda.org/manual/latest/user-guide/process-engine/external-tasks/#the-external-task-pattern) where workers subscribe to the engine - [Java code attached to service tasks](https://docs.camunda.org/manual/latest/user-guide/process-engine/delegation-code/) called directly by the engine (in-VM) ### Tools and resources This guide covers tools and approaches to help with code conversion: 1. [API Mapping Guide](#api-mapping-guide): Understand how Camunda 7 REST API endpoints map to Camunda 8 2. [OpenRewrite Recipes](#refactoring-recipes-using-openrewrite): Automatically refactor Java code with configurable recipes 3. [Code Conversion Patterns](#code-conversion-patterns): Detailed technical reference for manual migration 4. [AI-Assisted Code Migration](#leverage-ai-for-code-migration): Use AI coding agents for interactive and agentic migration, including the [Camunda migration agent skill](#camunda-migration-agent-skill) for an end-to-end interactive workflow Additionally, you will find information about: - [Diagram Converter](#diagram-converter) for BPMN and DMN model conversion - [Complete migration example](#example-adjusting-a-spring-boot-application) showing all tools in action ### Choose your migration approach You can combine these tools depending on your codebase complexity: | Approach | Best for | How it works | | -------------------------- | ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | | **OpenRewrite only** | Standard patterns, large codebases with many similar files | Run batch recipes, review diffs, manually fix remaining TODOs | | **AI agent only** | Small codebases, complex custom code, exploratory migration | Give an AI agent your code and migration patterns, iterate on results | | **Combined (recommended)** | Most real-world projects | Run OpenRewrite first for deterministic bulk changes, then use AI for TODOs, edge cases, tests, and configuration | ## API mapping guide The Camunda 7 and Camunda 8 Orchestration Cluster APIs share many similarities, but several aspects have been modernized in Camunda 8. ### Key structural changes Streamlined search endpoints: - **Camunda 7**: Separate endpoints like `GET /resource` and `GET /resource/count` - **Camunda 8**: Single `POST /search` endpoint with filtering capabilities Tenant handling: - **Camunda 7**: `tenantId` passed as path parameter with multiple endpoint variants - **Camunda 8**: `tenantId` passed in request body, simplifying the API surface History data: - **Camunda 7**: Separate endpoints for historic data (for example, HistoryService) - **Camunda 8**: No separate historic endpoints; history is managed through Operate ### Using the interactive mapping tool To help you understand the differences between the two APIs, we provide an interactive web application that maps the complete Camunda 7 REST API to its Camunda 8 counterparts. The tool shows: - Direct mappings: Camunda 7 endpoints that map one-to-one to Camunda 8 - Conceptual mappings: Functionality that exists in Camunda 8 but works differently - Roadmap items: Features planned for future Camunda 8 releases - Discontinued features: Camunda 7 endpoints that are no longer available and why [Open the API Mapping Guide](https://camunda.github.io/camunda-7-to-8-migration-tooling/). :::tip When to use this tool Use the API mapping guide to: - Quickly find Camunda 8 equivalents for Camunda 7 API calls - Understand why certain parameters or endpoints changed - Check if a planned feature is on the roadmap - Plan your migration strategy based on API availability ::: ## Code conversion patterns Due to the flexibility of Camunda 7, there are many ways to write code and therefore many possible conversion patterns. We maintain a collaborative catalog of these patterns to serve as technical reference material for manual migration and recipe development. ### What are code conversion patterns? Code conversion patterns are detailed, technical examples showing how specific Camunda 7 code constructs translate to Camunda 8. Each pattern includes: - Side-by-side code comparisons between Camunda 7 and Camunda 8 - Explanations of conceptual differences - Parameter mappings and method equivalents - Notes on edge cases and limitations ### When to use the patterns Use the code conversion patterns when: - Manual migration is needed: The OpenRewrite recipes cannot handle your specific code structure - Understanding changes: You want to understand what the recipes are doing under the hood - Extending recipes: You're developing custom recipes for your organization's specific patterns - Complex scenarios: Your code uses advanced features that require careful manual conversion ### Pattern categories The catalog covers the following types of code: General patterns: - Maven dependencies and configuration - Handling process variables Client code (code that calls the Camunda API): - Starting process instances - Correlating messages - Handling user tasks - Managing process variables - Searching process definitions - Broadcasting signals - Canceling process instances - Raising incidents - Handling resources - Class-level changes Glue code (code executed by the process engine): - Converting JavaDelegates to Job Workers - Converting ExecutionListeners to Job Workers - Converting External Task Workers to Job Workers - Converting expressions Test code: - Complete test cases - Process instance assertions - Process variable assertions - User task assertions - Message correlation - Job execution ### Accessing the patterns The complete pattern catalog with code examples is maintained on GitHub. Browse the complete pattern catalog with code examples in the [Migration Tooling repository](https://github.com/camunda/camunda-7-to-8-migration-tooling/tree/main/code-conversion/patterns). :::tip The pattern catalog is actively maintained by Camunda consultants, partners, and community members. You can contribute your own patterns or request additions via GitHub issues and pull requests. ::: ### Using patterns with OpenRewrite The patterns inform the OpenRewrite recipe development. If you find a pattern that's not yet covered by the recipes, you can: 1. Use the pattern for manual migration 2. Reference the pattern when [extending the recipes](https://github.com/camunda/camunda-7-to-8-migration-tooling/blob/main/code-conversion/recipes/developer_guide.md) 3. Contribute a new recipe based on the pattern ## Refactoring recipes (using OpenRewrite) [OpenRewrite](https://docs.openrewrite.org/) is an open-source framework that can automate refactorings by so-called recipes. It is provided with an Apache License, making it easy to adopt in any context. The Camunda 7 to 8 OpenRewrite recipes help you automatically refactor: - Client code using the Camunda 7 Java API - Java delegates and execution listeners (glue code) - External task workers - Unit tests (work in progress) :::note The recipes are still under development. Expect recipes to work out-of-the-box only in simple scenarios. For complex codebases, you may need to extend or customize them to suit your needs. ::: ### How the recipes work The code transformation is performed in three phases to ensure your code remains compilable throughout the migration: 1. **Prepare**: Prepares the Camunda 7 code with minimal changes (e.g., converting TypedValue API to Java Object API, adding Maven dependencies). 2. **Migrate**: Replaces Camunda 7 methods with Camunda 8 equivalents. Comments are added where parameters were modified or removed. 3. **Cleanup**: Removes unnecessary dependencies and imports. ### Available recipes The recipes are organized by code type and transformation phase: | Type of change | Client code | Java delegate | External worker | | -------------- | ----------------------- | ------------------------- | ------------------------------- | | **Prepare** | AllClientPrepareRecipes | AllDelegatePrepareRecipes | AllExternalWorkerPrepareRecipes | | **Migrate** | AllClientMigrateRecipes | AllDelegateMigrateRecipes | AllExternalWorkerMigrateRecipes | | **Cleanup** | AllClientCleanupRecipes | AllDelegateCleanupRecipes | AllExternalWorkerCleanupRecipes | | **Combined** | AllClientRecipes | AllDelegateRecipes | AllExternalWorkerRecipes | You can apply recipes individually by phase, or use the _combined_ recipes to run all three phases at once. ### Using the recipes #### Prerequisites - Maven-based Java project (Gradle is also supported via [OpenRewrite's documentation](https://docs.openrewrite.org/running-recipes/getting-started)) - Project under version control (to easily review refactorings) #### Step 1: Add the OpenRewrite Maven plugin Add the following to your `pom.xml`: ```xml org.openrewrite.maven rewrite-maven-plugin 6.29.0 io.camunda.migration.code.recipes.AllClientRecipes io.camunda.migration.code.recipes.AllDelegateRecipes io.camunda.migration.code.recipes.AllExternalWorkerRecipes false io.camunda camunda-7-to-8-code-conversion-recipes 0.2.0 ``` :::warning Important Always back up your code or use version control before running recipes. This ensures you can review and rollback changes if needed. ::: :::note The use of `camunda-7-to-8-code-conversion-recipes` artifact requires access to the Camunda Enterprise Maven repository. See the [Camunda 7 documentation](https://docs.camunda.org/get-started/apache-maven/#camunda-artifact-storage) for instructions on setting up the repository in your Maven configuration. ::: Choose the recipes that match your codebase: - Include `AllClientRecipes` if you have code that calls the Camunda API (starting processes, correlating messages, etc.) - Include `AllDelegateRecipes` if you have Java delegates or execution listeners - Include `AllExternalWorkerRecipes` if you have external task workers #### Step 2: Run the recipes Execute the following command: ```shell mvn rewrite:run ``` #### Step 3: Review the changes Carefully examine all changes using your version control system's diff tool. The recipes add comments where manual review is needed: - Parameters that were removed or have different semantics in Camunda 8 - Methods with no direct one-to-one replacement (for example, executionId-based operations) - Dummy literal strings that need to be replaced with actual values :::warning Important Always review the transformed code. Some concepts from Camunda 7 (like executionId) don't exist in Camunda 8, and recipes cannot automatically determine the correct replacement in all cases. ::: ### Recipe completeness and limitations The recipes cover: - Class structure and annotations - Dependencies and imports - Basic types and commonly used methods However, they are incomplete in two aspects: - Some Camunda 7 methods could be transformed but are not yet included - Some Camunda 7 methods have no equivalent in Camunda 8 If Camunda 7 code remains after applying recipes: 1. Refer to the [code conversion patterns](#code-conversion-patterns) for manual migration guidance 2. Extend the recipes for your specific use case (see the [developer guide](https://github.com/camunda/camunda-7-to-8-migration-tooling/blob/main/code-conversion/recipes/developer_guide.md)) 3. Remove or refactor the code if the functionality is no longer available ### Additional resources - [Recipe source code and developer guide](https://github.com/camunda/camunda-7-to-8-migration-tooling/tree/main/code-conversion/recipes) - [OpenRewrite documentation](https://docs.openrewrite.org/) - [Complete migration example](https://github.com/camunda-community-hub/camunda-7-to-8-migration-example) ## Diagram converter Your BPMN and DMN models need to be adjusted to work with Camunda 8. The [Diagram Converter](https://github.com/camunda/camunda-7-to-8-migration-tooling/tree/main/diagram-converter) handles most common changes automatically. Depending on how you refactor your code and what elements of Camunda 7 you have used, you can extend or customize the Diagram Converter to suit your needs. Find the diagram conversion tooling and its documentation in the [Migration Tooling – Diagram Converter](https://github.com/camunda/camunda-7-to-8-migration-tooling/tree/main/diagram-converter). ## Leverage AI for code migration AI can accelerate code migration by applying the [code conversion patterns](#code-conversion-patterns) interactively. This is especially valuable for code that OpenRewrite recipes cannot handle automatically, such as custom superclasses, complex test cases, or configuration files. You can use AI in three ways: 1. **Standalone**: Give the AI agent your Camunda 7 code and the conversion patterns, and let it produce Camunda 8 equivalents. 2. **Post-OpenRewrite cleanup**: Run OpenRewrite first, then use AI to handle the remaining tasks and compilation errors. 3. **Full agentic migration**: Let an AI agent assess your codebase, run OpenRewrite, and handle all remaining migration tasks. :::tip The fastest path is the [Camunda migration agent skill](#camunda-migration-agent-skill): it packages all three approaches into an interactive workflow inside your AI coding agent. ::: The rest of this section describes the underlying pattern catalog and prompts the Camunda migration agent skill uses, which you can also apply manually with any AI coding agent or chat assistant. ### Camunda migration agent skill Use the official Camunda migration [Agent Skill](https://agentskills.io/). It packages everything, including assessment, OpenRewrite, AI cleanup, and validation, into an interactive workflow that runs inside your AI coding agent. 1. Install it. You can use Claude Code: ```bash claude plugin marketplace add camunda/camunda-7-to-8-migration-tooling claude plugin install camunda-migration ``` :::note Install with other agents The skill follows the open [Agent Skills](https://agentskills.io/) format and works with any compatible agent. See the [Agentic Migration Skills README](https://github.com/camunda/camunda-7-to-8-migration-tooling/tree/main/agentic-migration-skills) for manual installation. ::: 2. Run from your Camunda 7 project directory: ``` /camunda-migration:migrate-c7-to-c8-code ``` The skill asks for your project path and migration approach, then guides you through: | Approach | What it does | | ------------------------------------ | ------------------------------------------------------------------------------------------------------------ | | **OpenRewrite + AI** _(recommended)_ | Runs OpenRewrite recipes for bulk transforms, then AI resolves remaining TODOs, configuration, and test code | | **AI only** | AI migrates everything directly — for non-Maven/Gradle builds or when you want to review every change | | **Assessment only** | Scans the codebase and reports files, complexity, and effort estimate — no code changes | The skill fetches the latest [pattern catalog](https://github.com/camunda/camunda-7-to-8-migration-tooling/blob/main/code-conversion/patterns/ALL_IN_ONE.md) at runtime, so it always reflects current migration guidance. ### Recommended combined workflow For most real-world projects, the combined approach gives the best results: ``` Step 1: Assess → AI agent scans the codebase, classifies files, creates plan Step 2: OpenRewrite → Run batch recipes for deterministic bulk transformations Step 3: AI cleanup → AI handles TODOs, edge cases, tests, and configuration Step 4: Validate → Compile, run tests, and search for remaining C7 references ``` ### Set up an AI agent for migration When using an AI coding agent, provide it with the migration patterns as context so the agent can apply these patterns across your entire codebase. :::tip Provide context to AI agents Point the agent to the pattern catalog for best results: - **URL reference**: `https://github.com/camunda/camunda-7-to-8-migration-tooling/blob/main/code-conversion/patterns/ALL_IN_ONE.md`. - **Local file**: If you have the migration tooling repository cloned, reference the local `ALL_IN_ONE.md` file directly. - **Inline patterns**: For specific migration tasks, include the relevant patterns directly in your prompt. Agents with access to browse URLs or read local files (like Claude Code or Copilot Agent Mode) can fetch the patterns automatically. For chat-based tools, paste the relevant code examples from the pattern catalog into your prompt. ::: ### Step 1: Assess your codebase Before migrating, use AI to create an inventory of all Camunda 7 code in your project. This helps you plan the migration order and identify which tools to use for each file. **Prompt: Codebase assessment** ``` Analyze this Camunda 7 codebase and create a migration inventory. For each Java file that imports from org.camunda.bpm.*, classify it as one of: 1. Client code — Uses ProcessEngine, RuntimeService, TaskService, RepositoryService 2. JavaDelegate — Implements org.camunda.bpm.engine.delegate.JavaDelegate 3. ExecutionListener — Implements org.camunda.bpm.engine.delegate.ExecutionListener 4. External Task Worker — Implements ExternalTaskHandler or uses @ExternalTaskSubscription 5. JUEL Expression — Referenced in BPMN as camunda:expression 6. Test code — Uses BpmnAwareTests, ProcessEngineRule, or camunda-bpm-assert 7. Configuration — Spring config, application.properties/yml, processes.xml Output a markdown table with columns: File, Type, Complexity (Low/Medium/High), Notes. Complexity guidelines: - Low: Standard patterns (simple delegate, basic startProcessInstance, simple test) - Medium: Uses builder patterns, correlates messages, typed value API, multiple services - High: Custom superclasses, JUEL expressions, complex queries, history service ``` ### Step 2: Migrate client code Client code is any code that calls the Camunda API from your application, for example, code that starts process instances, correlates messages, or manages tasks. **Prompt: Client code migration** ``` Migrate the following Camunda 7 client code to Camunda 8 using the official migration patterns from https://github.com/camunda/camunda-7-to-8-migration-tooling/blob/main/code-conversion/patterns/ALL_IN_ONE.md Apply these rules: Class-level: - Replace @Autowired ProcessEngine engine with @Autowired CamundaClient camundaClient - Remove any @Autowired RuntimeService, TaskService, RepositoryService fields Start Process Instance: - engine.getRuntimeService().startProcessInstanceByKey(key, vars) → camundaClient.newCreateInstanceCommand().bpmnProcessId(key).latestVersion() .variables(vars).send().join() - Return type: ProcessInstance → ProcessInstanceEvent Cancel Process Instance: - engine.getRuntimeService().deleteProcessInstance(id, reason) → camundaClient.newCancelInstanceCommand(processInstanceKey).send().join() Message Correlation: - engine.getRuntimeService().correlateMessage(name, businessKey, vars) → camundaClient.newCorrelateMessageCommand().messageName(name) .correlationKey(key).variables(vars).send().join() Signal Broadcasting: - engine.getRuntimeService().signalEventReceived(name, vars) → camundaClient.newBroadcastSignalCommand().signalName(name) .variables(vars).send().join() User Tasks: - engine.getTaskService().complete(taskId, vars) → camundaClient.newUserTaskCompleteCommand(userTaskKey).variables(vars).send().join() Variables: - engine.getRuntimeService().getVariable(execId, name) → camundaClient.newVariableSearchRequest() .filter(f -> f.processInstanceKey(key).name(name)).send().join().items().get(0) - engine.getRuntimeService().setVariable(execId, name, value) → camundaClient.newSetVariablesCommand(elementInstanceKey) .variable(name, value).send().join() - VariableMap / TypedValue API → Map Type changes: - String processInstanceId → Long processInstanceKey - ProcessInstance → ProcessInstanceEvent - VariableMap → Map - Task → UserTask Important: Parameter names are swapped in C8: - "processDefinitionKey" (C7, the BPMN ID) → "bpmnProcessId" (C8) - "processDefinitionId" (C7, deployment key) → "processDefinitionKey" (C8) Here is the Camunda 7 client code to migrate: [... paste code ...] ``` ### Step 3: Migrate JavaDelegates to Job Workers JavaDelegates are glue code that runs within the Camunda 7 engine. In Camunda 8, they become Job Workers, that is, Spring beans with `@JobWorker`-annotated methods. **Prompt: JavaDelegate migration** ``` Migrate the following Camunda 7 JavaDelegate to a Camunda 8 Job Worker using the official migration patterns from https://github.com/camunda/camunda-7-to-8-migration-tooling/blob/main/code-conversion/patterns/ALL_IN_ONE.md Apply these rules: Class-level: - Remove "implements JavaDelegate" - Keep @Component annotation - Add a @JobWorker annotated method: @JobWorker(type = "") public Map handleJob(JobClient client, ActivatedJob job) { } The "type" should match the bean name (for delegateExpression) or camelCase of the class name (for camunda:class) - Remove the old execute(DelegateExecution) method after migrating its body Variable handling: - execution.getVariable("name") → job.getVariablesAsMap().get("name") - execution.setVariable("name", value) → return from the method: return Map.of("name", value) - Multiple setVariable calls → collect into one Map and return it - TypedValue API (Variables.integerValue(x), IntegerValue, etc.) → plain Java types BPMN Error: - throw new BpmnError(code, msg) → throw CamundaError.bpmnError(code, msg, variables) Failure handling: - throw new ProcessEngineException(msg) → throw CamundaError.jobError(msg, vars, job.getRetries() - 1, Duration.ofSeconds(30)) Incident (retries=0): - execution.createIncident(type, config, msg) → throw CamundaError.jobError(msg, vars, 0, null, exception) Here is the Camunda 7 JavaDelegate to migrate: [... paste code ...] ``` ### Step 4: Migrate external task workers to Job Workers External task workers have a similar architecture to Camunda 8 Job Workers, which makes this migration relatively straightforward. **Prompt: External task worker migration** ``` Migrate the following Camunda 7 External Task Worker to a Camunda 8 Job Worker using the official migration patterns from https://github.com/camunda/camunda-7-to-8-migration-tooling/blob/main/code-conversion/patterns/ALL_IN_ONE.md Apply these rules: Class-level: - Change @Configuration to @Component - Remove "implements ExternalTaskHandler" - Remove @ExternalTaskSubscription("topicName") - Add: @JobWorker(type = "topicName") public Map handleJob(JobClient client, ActivatedJob job) { } If using lambda-style (multiple workers per class): - Convert each @Bean @ExternalTaskSubscription("topic") method to a separate @JobWorker(type = "topic") method Variable handling: - externalTask.getVariable("name") → job.getVariablesAsMap().get("name") - externalTaskService.complete(id, vars, null) → return vars from method BPMN Error: - externalTaskService.handleBpmnError(task, code, msg, vars) → throw CamundaError.bpmnError(code, msg, vars) Failure/Incident: - externalTaskService.handleFailure(id, msg, details, retries, timeout, vars, null) → throw CamundaError.jobError(msg, vars, job.getRetries() - 1, Duration.ofSeconds(30)) - With retries=0 → throw CamundaError.jobError(msg, vars, 0, null, exception) Here is the Camunda 7 External Task Worker to migrate: [... paste code ...] ``` ### Step 5: Migrate test code Test code migration requires updating the test framework, assertions, and the way processes are started and validated. AI agents are especially effective here because test patterns vary widely. **Prompt: Test code migration** ``` Refactor the following Camunda 7 JUnit test case to Camunda 8 using the official migration patterns from https://github.com/camunda/camunda-7-to-8-migration-tooling/blob/main/code-conversion/patterns/ALL_IN_ONE.md The refactored test must: Test class setup: - Use @SpringBootTest and @CamundaSpringProcessTest annotations - Inject @Autowired CamundaClient client - Inject @Autowired CamundaProcessTestContext processTestContext - Remove ProcessEngine, RuntimeService, TaskService injections - Remove @Deployment, ProcessEngineRule, ProcessEngineTestRule Starting process instances: - runtimeService().startProcessInstanceByKey("process-id", vars) → client.newCreateInstanceCommand().bpmnProcessId("process-id").latestVersion() .variables(vars).send().join() - Return type: ProcessInstance → ProcessInstanceEvent - Variables.createVariables().putValue("x", 7) → Map.of("x", 7) or new HashMap<>() Process instance assertions: - assertThat(pi).isNotEnded() → assertThat(pi).isActive() - assertThat(pi).isEnded() → assertThat(pi).isCompleted() - assertThat(pi).isWaitingAt("TaskId") → assertThat(pi).hasActiveElements("TaskId") - assertThat(pi).isWaitingAt(findId("Name")) → assertThat(pi).hasActiveElements(byName("Name")) - assertThat(pi).hasPassed("ElementId") → assertThat(pi).hasCompletedElements("ElementId") Variable assertions: - assertThat(pi).variables().containsEntry("key", value) → assertThat(pi).hasVariable("key", value) User task assertions and completion: - assertThat(task()).hasName("x").isAssignedTo("u") → assertThat(UserTaskSelectors.byTaskName("x")).isCreated().hasName("x").hasAssignee("u") - complete(task()) → processTestContext.completeUserTask("TaskName") Timer handling: - Do NOT manually query and execute timer jobs - managementService().createJobQuery()... + managementService().executeJob(jobId) → processTestContext.increaseTime(Duration.ofMinutes(6)) Message correlation in tests: - runtimeService().correlateMessage("MsgName", correlationKeys) → client.newPublishMessageCommand().messageName("MsgName") .correlationKey("key").send().join() Job workers in tests (optional): - Disable workers: @SpringBootTest(properties = {"camunda.client.worker.defaults.enabled=false"}) - Complete manually: processTestContext.completeJob("jobType") - Mock a worker: processTestContext.mockJobWorker("jobType").thenComplete(variables) Static imports: - Remove: import static org.camunda.bpm.engine.test.assertions.bpmn.BpmnAwareTests.* - Add: import static io.camunda.process.test.api.CamundaAssert.assertThat - Add: import static io.camunda.process.test.api.assertions.ElementSelectors.byName - Add: import io.camunda.process.test.api.assertions.UserTaskSelectors Here is the Camunda 7 test case: [... paste full test case code ...] ``` ### Step 6: Migrate dependencies and configuration **Prompt: Dependencies and configuration migration** ``` Migrate the Maven dependencies and Spring Boot configuration from Camunda 7 to Camunda 8. pom.xml changes: - Remove all Camunda 7 dependencies (camunda-bpm-spring-boot-starter, camunda-engine, camunda-bpm-assert, camunda-spin-*, etc.) - Add the Camunda 8 Spring SDK: io.camunda camunda-spring-boot-starter {camunda8Version} - For testing: io.camunda camunda-process-test-spring {camunda8Version} test Application class: - @EnableProcessApplication → @Deployment(resources = "classpath*:/bpmn/**/*.bpmn") - Remove META-INF/processes.xml application.yml / application.properties: - Remove all camunda.bpm.* properties - Add Camunda 8 connection configuration (see Spring SDK docs) Here is my current pom.xml: [... paste pom.xml ...] Here is my current application.yml: [... paste config ...] ``` ### Step 7: Validate the migration After migration, verify all Camunda 7 references have been removed and the code compiles and passes tests. **Prompt: Post-migration validation** ``` Validate the Camunda 7 to 8 migration: 1. Compile: mvn compile — fix any errors 2. Find remaining Camunda 7 references: search for org.camunda.bpm.* imports 3. Find TODO comments left by OpenRewrite or manual migration 4. Check for common issues: - String process instance IDs that should now be Long keys - VariableMap usage that should be Map - History service or management service usage (no direct C8 equivalent) - Batch operations (C8 operates on single instances) 5. Run tests: mvn test — fix any failures 6. Review @JobWorker methods: verify type matches BPMN, check return types ``` ### Full agentic migration prompt For AI agents that can browse files and execute commands, you can use a single comprehensive prompt to migrate an entire project: :::tip The full agentic prompt works best with AI coding agents that have terminal access and can read/write files directly (for example, Claude Code, Copilot Agent Mode, Cursor). For chat-based tools, use the individual prompts from Steps 2–7 above, and provide the relevant code in each prompt. ::: ``` You are migrating a Camunda 7 Spring Boot application to Camunda 8. Use the official migration patterns from https://github.com/camunda/camunda-7-to-8-migration-tooling/blob/main/code-conversion/patterns/ALL_IN_ONE.md STEP 1 — ASSESS: Scan the codebase and classify every Java file that imports from org.camunda.bpm.* into: client code, JavaDelegate, ExecutionListener, External Task Worker, test code, or configuration. Create a migration plan as a numbered task list. STEP 2 — DEPENDENCIES: Update pom.xml: remove all org.camunda.bpm dependencies, add io.camunda:camunda-spring-boot-starter and io.camunda:camunda-process-test-spring (test). STEP 3 — CONFIGURATION: Replace @EnableProcessApplication with @Deployment(resources = "classpath*:/bpmn/**/*.bpmn"). Remove camunda.bpm.* properties, add camunda.client.* config. Delete META-INF/processes.xml. STEP 4 — MIGRATE CLIENT CODE: For each file using ProcessEngine/RuntimeService/TaskService/RepositoryService: - Replace autowired engine services with @Autowired CamundaClient camundaClient - startProcessInstanceByKey(key, vars) → camundaClient.newCreateInstanceCommand().bpmnProcessId(key).latestVersion() .variables(vars).send().join() - deleteProcessInstance(id, reason) → camundaClient.newCancelInstanceCommand(key).send().join() - correlateMessage(name, businessKey, vars) → camundaClient.newCorrelateMessageCommand().messageName(name) .correlationKey(key).variables(vars).send().join() - signalEventReceived(name, vars) → camundaClient.newBroadcastSignalCommand().signalName(name).variables(vars).send().join() - taskService.complete(taskId, vars) → camundaClient.newUserTaskCompleteCommand(taskKey).variables(vars).send().join() - Type changes: ProcessInstance→ProcessInstanceEvent, String id→Long key, VariableMap→Map STEP 5 — MIGRATE JAVADELEGATES: For each class implementing JavaDelegate: - Remove implements JavaDelegate, keep @Component - Replace execute(DelegateExecution) with @JobWorker(type="beanName") method - execution.getVariable("x") → job.getVariablesAsMap().get("x") - execution.setVariable("x", v) → return Map.of("x", v) from method - BpmnError → CamundaError.bpmnError(code, msg, vars) - ProcessEngineException → CamundaError.jobError(msg, vars, retries-1, backoff) - Remove all TypedValue API usage, use plain Java types STEP 6 — MIGRATE EXTERNAL TASK WORKERS: For each class implementing ExternalTaskHandler: - @Configuration→@Component, remove implements ExternalTaskHandler - Replace execute(ExternalTask, ExternalTaskService) with @JobWorker method - externalTask.getVariable → job.getVariablesAsMap().get - externalTaskService.complete → return vars from method - handleBpmnError → CamundaError.bpmnError - handleFailure → CamundaError.jobError STEP 7 — MIGRATE TESTS: For each test class: - Add @CamundaSpringProcessTest, inject CamundaClient and CamundaProcessTestContext - Replace runtimeService().startProcessInstanceByKey with client.newCreateInstanceCommand - isWaitingAt→hasActiveElements, hasPassed→hasCompletedElements, isEnded→isCompleted - complete(task()) → processTestContext.completeUserTask("TaskName") - Timer: managementService job query → processTestContext.increaseTime(Duration) - Variables: .variables().containsEntry(k,v) → .hasVariable(k, v) STEP 8 — VALIDATE: - Compile: mvn compile - Search for remaining org.camunda.bpm imports - Run tests: mvn test - List any remaining TODO items After each step, report what was changed and any issues encountered. ``` ### Key API mapping reference These tables summarize the most important mappings between Camunda 7 and Camunda 8. They are useful as quick references when writing migration prompts or reviewing AI-generated code. #### Type mappings | Camunda 7 | Camunda 8 | | ---------------------------------------------- | ------------------------------------------------- | | `ProcessEngine` | `CamundaClient` | | `RuntimeService` | `CamundaClient` (methods directly on client) | | `TaskService` | `CamundaClient` (user task methods) | | `RepositoryService` | `CamundaClient` (deployment/definition methods) | | `ProcessInstance` | `ProcessInstanceEvent` | | `Task` | `UserTask` | | `Deployment` | `DeploymentEvent` | | `Batch` | No direct equivalent (single instance operations) | | `VariableMap` | `Map` | | `TypedValue` (IntegerValue, StringValue, etc.) | Plain Java types | | `DelegateExecution` | `ActivatedJob` | | `ExternalTask` + `ExternalTaskService` | `JobClient` + `ActivatedJob` | | `BpmnError` | `CamundaError.bpmnError(...)` | | `ProcessEngineException` | `CamundaError.jobError(...)` | #### Parameter name changes :::important The terms `processDefinitionKey` and `processDefinitionId` have **swapped meanings** between Camunda 7 and Camunda 8. Review these carefully during migration. ::: | Description | Camunda 7 | Camunda 8 | | -------------------------------- | -------------------------- | --------------------------------------- | | BPMN model identifier (from XML) | `processDefinitionKey` | `bpmnProcessId` / `processDefinitionId` | | Unique key from deployment | `processDefinitionId` | `processDefinitionKey` | | Process instance identifier | `String processInstanceId` | `Long processInstanceKey` | #### Test assertion mappings | Camunda 7 (BpmnAwareTests) | Camunda 8 (CamundaAssert) | | ----------------------------------------------- | --------------------------------------------------------------------------------------- | | `assertThat(pi).isNotEnded()` | `assertThat(pi).isActive()` | | `assertThat(pi).isEnded()` | `assertThat(pi).isCompleted()` | | `assertThat(pi).isWaitingAt("id")` | `assertThat(pi).hasActiveElements("id")` | | `assertThat(pi).isWaitingAt(findId("name"))` | `assertThat(pi).hasActiveElements(byName("name"))` | | `assertThat(pi).hasPassed("id")` | `assertThat(pi).hasCompletedElements("id")` | | `assertThat(pi).variables().containsEntry(k,v)` | `assertThat(pi).hasVariable(k, v)` | | `assertThat(task()).hasName("x")` | `assertThat(UserTaskSelectors.byTaskName("x")).hasName("x")` | | `assertThat(task()).isAssignedTo("u")` | `assertThat(UserTaskSelectors.byTaskName("x")).hasAssignee("u")` | | `complete(task())` | `processTestContext.completeUserTask("name")` | | `managementService().executeJob(id)` | `processTestContext.increaseTime(Duration)` or `processTestContext.completeJob("type")` | #### Import replacements | Remove | Add | | ------------------------------------------------------------------------- | ---------------------------------------------------------------- | | `org.camunda.bpm.engine.*` | `io.camunda.client.*` | | `org.camunda.bpm.engine.delegate.*` | `io.camunda.client.api.worker.JobHandler` | | `org.camunda.bpm.engine.variable.*` | (plain Java collections) | | `org.camunda.bpm.engine.test.assertions.bpmn.BpmnAwareTests.*` | `io.camunda.process.test.api.CamundaAssert.*` | | — | `io.camunda.process.test.api.assertions.ElementSelectors.byName` | | — | `io.camunda.process.test.api.assertions.UserTaskSelectors` | | — | `io.camunda.process.test.api.CamundaProcessTestContext` | | — | `io.camunda.process.test.api.CamundaSpringProcessTest` | | `org.camunda.bpm.spring.boot.starter.annotation.EnableProcessApplication` | `io.camunda.client.annotation.Deployment` | ## Example: Adjust a Spring Boot application See the [end-to-end migration example](https://github.com/camunda-community-hub/camunda-7-to-8-migration-example) on GitHub. --- ## Cockpit plugin :::warning Experimental feature - The Cockpit plugin is an **experimental** feature and we don't recommend using it in production environments. - Read more about this plugin's [limitations](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/limitations.md#cockpit-plugin). ::: The Cockpit plugin provides a web-based interface for viewing information about skipped and migrated runtime and history data. It integrates with Camunda 7 Cockpit to give you visibility into which runtime process instances or history data (like variables, flow nodes, etc.) were successfully migrated or skipped during migration and the reasons why. For more information on Camunda 7 plugins, see the [Camunda 7 documentation](https://docs.camunda.org/manual/latest/webapps/cockpit/extend/plugins/). ## Prerequisites - **Database Configuration**: The plugin can only access databases that Camunda 7 is connected to, so the migration schema needs to be created in the Camunda 7 database (default behavior), or both Camunda 7 and Camunda 8 databases must point to the same database instance. For an example, see the [Data Migrator setup example](config-examples.md#data-migrator). - Camunda 7 Webapps are deployed, running, and accessible. - Migration schema is available in Camunda 7 database. - The Cockpit plugin requires running the migrator with `save-skip-reason` enabled. - The plugin doesn't show skip reasons without this setting because they are not stored. - To use the Cockpit plugin, run the migrator with the following setting: ```yaml camunda.migrator: save-skip-reason: true ``` :::warning Heads-Up - Saving the skip reason could result in a large amount of data being stored additionally in your database, depending on the order of magnitude of the data to be migrated or potentially skipped. - We recommend testing your migration in a QA environment before running the Data Migrator against your production database. ::: ## Installation 1. **Download the latest release** from the [releases page](https://github.com/camunda/camunda-7-to-8-migration-tooling/releases). 2. **Deploy the plugin** to your Camunda 7 installation by copying the generated JAR file into the Camunda 7 plugins directory. For example the paths are: - For Tomcat: `./camunda-bpm-ee-tomcat--ee/server/apache-tomcat-/webapps/camunda/WEB-INF/lib/`. - For Run: `./camunda-bpm-run-ee--ee/configuration/userlib/`. 3. **Inspect skipped and migrated data in Cockpit** once the plugin has been deployed. ## Configuration ### Operate URL When viewing migrated process instances, the plugin shows the Camunda 8 process instance key. You can optionally configure the Camunda 8 Operate URL so that these keys link directly to the corresponding process instance in Operate. To configure this, create or edit the Cockpit configuration file (`config.js`) and add the `operateUrl` property: ```javascript export default { operateUrl: "https:///operate", }; ``` Deploy this file to your Camunda 7 installation at `/app/cockpit/scripts/config.js`. If `operateUrl` is not configured, the Camunda 8 key is displayed as plain text without a link. ## Using the Cockpit plugin After installation and configuration, the Cockpit plugin provides: - **Skipped entity overview**: View all entities that were skipped during migration. - **Detailed skip reasons**: Understand why specific entities were not migrated. - **Migration status tracking**: See data that has been migrated successfully. ## Screenshots The following screenshots demonstrate the Cockpit plugin interface and functionality: ### Migrated process instances view Shows a table of successfully migrated process instances from Camunda 7 to Camunda 8, including the process instance ID, process definition key, and the corresponding Camunda 8 key. ![Runtime Migrated Instances](img/runtime-migrated.png) ### Skipped process instances overview Displays process instances that were skipped during migration, allowing users to identify which instances failed and need further attention. ![Runtime Skipped Instances](img/runtime-skipped.png) ### Entity type selection Allows filtering historic entities by type (process instances, variables, tasks, etc.) to simplify analysis of migration issues. ![Skipped Entity Type Selection](img/skipped-select-type.png) ### Variable-specific skip analysis When viewing historic variable data, the type and value of primitives are retrieved to provide additional context. ![Skipped Variables](img/skipped-variables.png) --- ## Configuration examples Configuration examples for the Data Migrator's `configuration/application.yml`. ## Camunda 8 Client ```yaml camunda.client: mode: self-managed # Operation mode: 'self-managed' or 'cloud' grpc-address: http://localhost:26500 # The gRPC API endpoint rest-address: http://localhost:8080 # The REST API endpoint ``` ## Data Migrator ```yaml camunda.migrator: page-size: 500 # Number of records to process in each page job-type: migrator # Job type for actual job activation (used for validation and activation unless validation-job-type is defined) # validation-job-type: '=if legacyId != null then "migrator" else "noop"' # Optional: job type for validation (falls back to job-type if not defined) auto-ddl: true # Automatically create/update database schema table-prefix: MY_PREFIX_ # Optional table prefix for migrator schema interceptors: - class-name: com.example.MyCustomInterceptor # Custom interceptor class - class-name: com.example.AnotherInterceptor # Another custom interceptor class history: partition-count: 3 # Optional: Number of partitions when skipping Camunda 8 REST/topology connectivity (Camunda 8 database access is still required) auto-cancel: cleanup: enabled: true # Populate cleanup dates for auto-canceled entities ttl: P6M # Time-to-live for auto-canceled history (6 months) ``` ## Datasource Configure the Camunda 7 and Camunda 8 datasources. You can use the same or different databases for Camunda 7 and Camunda 8. ### Camunda 7 (runtime and history) ```yaml camunda.migrator.c7.data-source: table-prefix: MY_PREFIX_ # Optional prefix for Camunda 7 database tables auto-ddl: true # Automatically create/update Camunda 7 database schema jdbc-url: jdbc:h2:./h2/data-migrator-source.db username: sa # Database username password: sa # Database password driver-class-name: org.h2.Driver ``` You can apply any HikariCP property (for example, pool size) under `camunda.migrator.c7.data-source`. ### Camunda 8 RDBMS (history) ```yaml camunda.migrator.c8.data-source: table-prefix: MY_PREFIX_ # Optional prefix for Camunda 8 RDBMS database tables auto-ddl: true # Automatically create/update Camunda 8 RDBMS database schema jdbc-url: jdbc:h2:./h2/data-migrator-target.db username: sa # Database username password: sa # Database password driver-class-name: org.h2.Driver ``` You can apply any HikariCP property (for example, pool size) under `camunda.migrator.c8.data-source`. ## Logging ```yaml logging: level: root: INFO # Root logger level io.camunda.migration.data: INFO # Migrator logging io.camunda.migration.data.RuntimeMigrator: DEBUG # Runtime migration logging io.camunda.migration.data.persistence.IdKeyMapper: DEBUG # ID mapping logging file: name: logs/camunda-7-to-8-data-migrator.log ``` --- ## Configuration property reference Reference for all Data Migrator configuration properties, set in the `configuration/application.yml` file. ## `camunda.client` Prefix: `camunda.client` :::info Read more about Camunda Client [configuration options](/apis-tools/camunda-spring-boot-starter/configuration.md). ::: | Property | Type | Description | | :-------------- | :------- | :------------------------------------------------------------------------------------------------- | | `.mode` | `string` | Operation mode of the Camunda 8 client. Options: `self-managed` or `saas`. Default: `self-managed` | | `.grpc-address` | `string` | The gRPC API endpoint for Camunda 8 Platform. Default: `http://localhost:26500` | | `.rest-address` | `string` | The REST API endpoint for Camunda 8 Platform. Default: `http://localhost:8080` | ## `camunda.migrator` Prefix: `camunda.migrator` | Property | Type | Description | | :----------------------------- | :-------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `.page-size` | `number` | Number of records processed per page. Default: `100`. | | `.job-type` | `string` | Job type used for job activation. Default: `migrator`. | | `.validation-job-type` | `string` | Job type for validation purposes. Optional: falls back to `job-type` if not defined. Set to `DISABLED` to disable job type execution listener validation entirely. | | `.auto-ddl` | `boolean` | Automatically create/update migrator database schema. Default: `false`. | | `.table-prefix` | `string` | Optional prefix for migrator database tables. Default: _(empty)_. | | `.tenant-ids` | `string` | Comma-separated list of tenant ids for which process instances should be migrated during runtime migration. For more information, read [tenant in runtime](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/runtime.md#tenants). Default: _(empty)_ (migrate only process instances without assigned tenant id). | | `.database-vendor` | `string` | Database vendor for migrator schema. Options: `h2`, `postgresql`, `oracle`. Default: Automatically detected. | | `.interceptors` | `array` | List of variable interceptors (built-in and custom) to configure during migration. | | `.save-skip-reason` | `boolean` | Whether to persist skip reasons for entities that could not be migrated. Required when using the Cockpit plugin. Default: `false`. | | `.history.partition-count` | `number` | Number of partitions to use for distributing history data. When set, this value takes precedence over querying the topology from the Camunda REST API, so the migrator does not require REST connectivity to a running Camunda 8 cluster (but still requires access to the Camunda 8 database). If not set, the partition count is automatically fetched from the Camunda REST API. | | `.history.auto-cancel.cleanup` | `object` | Configuration for history auto-cancel cleanup behavior. See [camunda.migrator.history.auto-cancel.cleanup](#camundamigratorhistoryauto-cancelcleanup) for details. | | `.identity.skip-users` | `boolean` | Skip the migration of users (enable when IdP is configured for users). Default: `false`. | | `.identity.skip-groups` | `boolean` | Skip the migration of groups (enable when IdP is configured for groups). Default: `false`. | | `.identity.sync.timeout` | `number` | Timeout for identity sync operations in millis. Default: `3000`. | | `.identity.sync.poll-interval` | `number` | Polling interval for identity sync operations in millis. Default: `250`. | ## `camunda.migrator.history.auto-cancel.cleanup` Prefix: `camunda.migrator.history.auto-cancel.cleanup` Configuration for history cleanup of auto-canceled instances. | Property | Type | Description | | :--------- | :-------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `.enabled` | `boolean` | Whether to populate cleanup dates for auto-canceled entities. When `false`, history cleanup dates are set to `null` for all auto-canceled instances. Default: `true`. | | `.ttl` | `period` | Time-to-live for auto-canceled history records. The cleanup date is calculated as `end_date + ttl`. Accepts ISO-8601 duration format (e.g., `P6M` for 6 months, `P1Y` for 1 year, `P90D` for 90 days). Default: `P6M` (6 months). | ## `camunda.migrator.interceptors.[n]` Prefix: `camunda.migrator.interceptors.[n]` There are two types of interceptors: - **VariableInterceptors** for [runtime migration](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/variables.md#transformation) - **EntityInterceptors** for [history migration](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/history.md#entity-transformation) The configuration is the same for both types. | Property | Type | Description | | :----------- | :-------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | | `class-name` | `string` | **Required.** Fully qualified class name of the interceptor (built-in or custom). | | `enabled` | `boolean` | Whether the interceptor is enabled. Default: `true` for all interceptors. | | `properties` | `map` | Custom properties (key:value pairs) to configure the interceptor. Properties call setter methods on the interceptor class and pass the value. | ### Built-in interceptors The following built-in interceptors are available and can be disabled: **Validators (reject unsupported types):** - `io.camunda.migration.data.impl.interceptor.ByteArrayVariableValidator` - `io.camunda.migration.data.impl.interceptor.FileVariableValidator` - `io.camunda.migration.data.impl.interceptor.ObjectJavaVariableValidator` **Transformers (convert supported types):** - `io.camunda.migration.data.impl.interceptor.PrimitiveVariableTransformer` - `io.camunda.migration.data.impl.interceptor.StringVariableTransformer` - `io.camunda.migration.data.impl.interceptor.NullVariableTransformer` - `io.camunda.migration.data.impl.interceptor.DateVariableTransformer` - `io.camunda.migration.data.impl.interceptor.ObjectJsonVariableTransformer` - `io.camunda.migration.data.impl.interceptor.ObjectXmlVariableTransformer` - `io.camunda.migration.data.impl.interceptor.SpinJsonVariableTransformer` - `io.camunda.migration.data.impl.interceptor.SpinXmlVariableTransformer` **Entity transformers:** - `io.camunda.migration.data.impl.interceptor.history.entity.ProcessInstanceTransformer` - `io.camunda.migration.data.impl.interceptor.history.entity.ProcessDefinitionTransformer` - `io.camunda.migration.data.impl.interceptor.history.entity.FlowNodeTransformer` - `io.camunda.migration.data.impl.interceptor.history.entity.UserTaskTransformer` - `io.camunda.migration.data.impl.interceptor.history.entity.IncidentTransformer` - `io.camunda.migration.data.impl.interceptor.history.entity.VariableTransformer` - `io.camunda.migration.data.impl.interceptor.history.entity.DecisionInstanceTransformer` - `io.camunda.migration.data.impl.interceptor.history.entity.DecisionDefinitionTransformer` - `io.camunda.migration.data.impl.interceptor.history.entity.DecisionRequirementsDefinitionTransformer` ## `camunda.migrator.c7.data-source` Prefix: `camunda.migrator.c7.data-source` | Property | Type | Description | | :------------------- | :-------- | :------------------------------------------------------------------------------------------------------------------------------------------- | | `.table-prefix` | `string` | Optional prefix for Camunda 7 database tables. Default: _(empty)_ | | `.auto-ddl` | `boolean` | Automatically create/update Camunda 7 database schema. Default: `false` | | `.database-vendor` | `string` | The database vendor is automatically detected and can currently not be overridden. | | `.*` | | You can apply all [`HikariConfig` properties](https://github.com/brettwooldridge/HikariCP?tab=readme-ov-file#gear-configuration-knobs-baby). | | `.jdbc-url` | `string` | JDBC connection URL for the source Camunda 7 database. Default: `jdbc:h2:mem:migrator` | | `.username` | `string` | Username for Camunda 7 database connection. Default: `sa` | | `.password` | `string` | Password for Camunda 7 database connection. Default: `sa` | | `.driver-class-name` | `string` | JDBC driver class for Camunda 7 database. Default: `org.h2.Driver` | ## `camunda.migrator.c8` Prefix: `camunda.migrator.c8` | Property | Type | Description | | :---------------- | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | `.deployment-dir` | `string` | Define directory which resources like BPMN processes are automatically deployed to C8. In case multi-tenancy is enabled, please perform a manual deployment. | ## `camunda.migrator.c8.data-source` Prefix: `camunda.migrator.c8.data-source` If the `c8.data-source` configuration is absent, the RDBMS history data migrator is disabled. | Property | Type | Description | | :------------------- | :-------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- | | `.table-prefix` | `string` | Optional prefix for Camunda 8 RDBMS database tables. Default: _(empty)_ | | `.auto-ddl` | `boolean` | Automatically create/update Camunda 8 RDBMS database schema. Default: `false` | | `.database-vendor` | `string` | Database vendor for Camunda 8 schema. Options: `h2`, `postgresql`, `oracle`. Default: Automatically detected. | | `.*` | | You can apply all [`HikariConfig` properties](https://github.com/brettwooldridge/HikariCP?tab=readme-ov-file#gear-configuration-knobs-baby). For example: | | `.jdbc-url` | `string` | JDBC connection URL for the target Camunda 8 RDBMS database. Default: `jdbc:h2:mem:migrator` | | `.username` | `string` | Username for Camunda 8 database connection. Default: `sa` | | `.password` | `string` | Password for Camunda 8 database connection. Default: `sa` | | `.driver-class-name` | `string` | JDBC driver class for Camunda 8 database. Default: `org.h2.Driver` | ## `logging` Prefix: `logging` | Property | Type | Description | | :--------------------------------- | :------- | :-------------------------------------------------------------------------------------------------------------------- | | `.level.root` | `string` | Root logger level. Default: `INFO` | | `.level.io.camunda.migration.data` | `string` | Migrator logging level. Default: `INFO` | | `.file.name` | `string` | Log file location. Set to: `logs/camunda-7-to-8-data-migrator.log`. If not specified, logs are output to the console. | --- ## Database Database configuration is required for both Camunda 7 and Camunda 8 (RDBMS history) data sources. The Data Migrator uses JDBC to connect to these databases. ## Setup 1. Include the appropriate JDBC driver in the classpath by dropping the JAR into `configuration/userlib`. 2. Configure connection details in `configuration/application.yml`. 3. Set table prefixes if your installation uses them. 4. Verify connectivity before starting migration. 5. Ensure sufficient disk space for migration data. :::info The database vendor is automatically detected but can be overridden using the `database-vendor` property. ::: ## Transaction isolation level The required isolation level to run the Data Migrator with is `READ COMMITTED`. Other transaction isolation levels are not supported and might lead to unexpected behavior. ## History migration atomicity Read more about [history migration atomicity](../data-migrator/history.md#atomicity). ## Compatibility The migrator supports the following SQL databases: | Database | Version | JDBC Driver | Notes | | ------------------------ | -------------- | ---------------------------------------------- | -------------------------- | | **PostgreSQL** | 15, 16, 17, 18 | `org.postgresql.Driver` | Recommended for production | | **Oracle** | 19c, 23ai | `oracle.jdbc.OracleDriver` | Recommended for production | | **Microsoft SQL Server** | 2022 | `com.microsoft.sqlserver.jdbc.SQLServerDriver` | Recommended for production | | **MariaDB** | 11.8 | `org.mariadb.jdbc.Driver` | Recommended for production | The migrator supports migration only within the same database vendor: | Migration Path | Status | | ----------------------- | ---------------- | | PostgreSQL → PostgreSQL | ✅ Supported | | PostgreSQL → Oracle | ❌ Not supported | ## Dropping the migration mapping schema The migrator uses the `{prefix}MIGRATION_MAPPING` table to keep track of instances. To drop this table after the migration is complete, use `--drop-schema` when starting the migrator. This will drop the migration mapping schema on shutdown if the migration was successful (no entities were skipped): ```bash # Migrate and drop the migration mapping schema on shutdown if migration was successful ./start.sh --runtime --drop-schema ``` To drop the table regardless of the migration status, use `--force` in combination with `--drop-schema`. This will perform the drop in all cases: ```bash # Migrate and force drop the migration mapping schema on shutdown ./start.sh --runtime --drop-schema --force ``` ```bash # Migrate and drop the migration mapping schema on shutdown if migration was successful start.bat --runtime --drop-schema ``` To drop the table regardless of the migration status, use `--force` in combination with `--drop-schema`. This will perform the drop in all cases: ```bash # Migrate and force drop the migration mapping schema on shutdown start.bat --runtime --drop-schema --force ``` :::warning Using `--force` can lead to data loss. Use with caution. ::: --- ## Coverage The following tables show which Camunda 8 entities and properties are migrated by the History Data Migrator. ## Unsupported entities The following entities are not supported for migration (no properties are migrated): - Batch operation - Batch operation item - Cluster variable - Correlated message subscription - Exporter position - History deletion - Job metrics batch - Message subscription - Sequence flow - Usage metric - Usage metric (TU) - Web session ## Supported entities The following limitations apply to Audit log entries: - Audit log entries are migrated only for user tasks, process definitions, process instances, variables, jobs, external tasks, users, groups, and authorizations. - Audit log entries are not migrated for batch operations, identity links, attachments, decisions, job definitions, metrics, operation logs, filters, comments, and properties. | Entity Type | Property | Migration Supported | Notes | | -------------------------- | ----------------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Audit log** | | | | | | `auditLogKey` | Yes | | | | `entityKey` | Partially | Migrated only for entities related to user tasks, process definitions, and process instances | | | `entityType` | Yes | | | | `operationType` | Yes | | | | `entityVersion` | Yes | | | | `entityValueType` | No | | | | `entityOperationIntent` | No | | | | `batchOperationKey` | No | | | | `batchOperationType` | No | | | | `timestamp` | Yes | | | | `actorType` | Yes | | | | `actorId` | Yes | | | | `agentElementId` | No | | | | `tenantId` | Yes | | | | `tenantScope` | Yes | | | | `result` | Yes | | | | `category` | Yes | | | | `processDefinitionId` | Yes | | | | `decisionRequirementsId` | No | | | | `decisionDefinitionId` | No | | | | `processDefinitionKey` | Yes | | | | `processInstanceKey` | Yes | | | | `rootProcessInstanceKey` | Yes | | | | `elementInstanceKey` | Partially | Migrated only for entities related to user tasks | | | `jobKey` | Partially | Migrated only for entities related to jobs | | | `userTaskKey` | Yes | | | | `decisionRequirementsKey` | No | | | | `decisionDefinitionKey` | No | | | | `decisionEvaluationKey` | No | | | | `deploymentKey` | No | | | | `formKey` | No | | | | `resourceKey` | No | | | | `relatedEntityType` | No | | | | `relatedEntityKey` | No | | | | `entityDescription` | Partially | Migrated only for user, group, tenant, and delete variable operations | | | `partitionId` | Yes | Assigned based on the root process instance, or randomly if not related to a process instance. See [partition distribution](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/history.md#partition-distribution). | | | `historyCleanupDate` | Yes | | | **Decision definition** | | | | | | `decisionDefinitionKey` | Yes | | | | `name` | Yes | | | | `decisionDefinitionId` | Yes | | | | `tenantId` | Yes | | | | `version` | Yes | | | | `decisionRequirementsId` | Yes | | | | `decisionRequirementsKey` | Yes | | | | `decisionRequirementsName` | Yes | | | | `decisionRequirementsVersion` | Yes | | | **Decision instance** | | | | | | `decisionInstanceId` | Yes | | | | `decisionInstanceKey` | Yes | | | | `state` | Yes | | | | `evaluationDate` | Yes | | | | `evaluationFailure` | No | | | | `evaluationFailureMessage` | No | | | | `result` | Yes | | | | `flowNodeInstanceKey` | Yes | | | | `flowNodeId` | Yes | | | | `processInstanceKey` | Yes | | | | `processDefinitionKey` | Yes | | | | `processDefinitionId` | Yes | | | | `rootProcessInstanceKey` | Yes | | | | `decisionDefinitionKey` | Yes | | | | `decisionDefinitionId` | Yes | | | | `decisionRequirementsKey` | Yes | | | | `decisionRequirementsId` | Yes | | | | `rootDecisionDefinitionKey` | Yes | | | | `decisionType` | Yes | | | | `tenantId` | Yes | | | | `partitionId` | Yes | Assigned based on the root process instance, or randomly for standalone decisions. See [partition distribution](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/history.md#partition-distribution). | | | `evaluatedInputs` | Yes | | | | `evaluatedOutputs` | Yes | | | | `historyCleanupDate` | Yes | | | **Decision requirements** | | | | | | `decisionRequirementsKey` | Yes | | | | `decisionRequirementsId` | Yes | | | | `name` | Yes | | | | `resourceName` | Yes | | | | `version` | Yes | | | | `xml` | Yes | | | | `tenantId` | Yes | | | **Flow node instance** | | | | | | `flowNodeInstanceKey` | Yes | | | | `processInstanceKey` | Yes | | | | `processDefinitionKey` | Yes | | | | `processDefinitionId` | Yes | | | | `flowNodeScopeKey` | Yes | | | | `startDate` | Yes | | | | `endDate` | Yes | | | | `flowNodeId` | Yes | | | | `flowNodeName` | Yes | | | | `treePath` | Yes | | | | `type` | Yes | | | | `state` | Yes | | | | `incidentKey` | No | | | | `numSubprocessIncidents` | No | | | | `tenantId` | Yes | | | | `partitionId` | Yes | Assigned based on the root process instance. See [partition distribution](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/history.md#partition-distribution). | | | `rootProcessInstanceKey` | Yes | | | **Form** | | | | | | `formKey` | Yes | | | | `tenantId` | Yes | | | | `formId` | Yes | | | | `schema` | Yes | | | | `version` | Yes | | | | `isDeleted` | Yes | Always `false`. | | **Incident** | | | | | | `incidentKey` | Yes | | | | `processDefinitionKey` | Yes | | | | `processDefinitionId` | Yes | | | | `processInstanceKey` | Yes | | | | `rootProcessInstanceKey` | Yes | | | | `flowNodeInstanceKey` | Yes | Will not be populated when an incident occurs in flow node in waiting state with asyncBefore configuration | | | `flowNodeId` | Yes | | | | `jobKey` | Partially | Populated only for entities related to jobs of type asynchronous continuation. | | | `errorType` | Yes | Populated when an equivalent mapping is possible. In all other cases, it's set to `UNKNOWN` | | | `errorMessage` | Yes | | | | `errorMessageHash` | No | | | | `creationDate` | Yes | | | | `state` | Yes | Always `Resolved` | | | `treePath` | Yes | | | | `tenantId` | Yes | | | | `partitionId` | Yes | Assigned based on the root process instance. See [partition distribution](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/history.md#partition-distribution). | | **Job** | | | | | | jobKey | Yes | | | | type | No | | | | worker | Yes | Set to the hostname of the machine where Camunda 7 has executed the job. | | | state | Yes | Always `COMPLETED`. | | | kind | Yes | Always `BPMN_ELEMENT`. | | | listenerEventType | Yes | Always `UNSPECIFIED`. | | | retries | Yes | Always `0`. | | | isDenied | No | | | | deniedReason | No | | | | hasFailedWithRetriesLeft | No | | | | errorCode | No | | | | errorMessage | No | | | | serializedCustomHeaders | No | | | | customHeaders | No | | | | deadline | No | | | | endTime | No | | | | processDefinitionId | Yes | | | | processDefinitionKey | Yes | | | | processInstanceKey | Yes | | | | rootProcessInstanceKey | Yes | | | | elementId | Yes | | | | elementInstanceKey | Partially | It is not populated for jobs related to flow nodes in waiting state with `asyncBefore` configuration | | | tenantId | Yes | | | | partitionId | Yes | Assigned based on the root process instance. See [partition distribution](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/history.md#partition-distribution). | | | creationTime | Yes | | | | lastUpdateTime | No | | | **External Task as a Job** | | | | | | jobKey | Yes | | | | type | Yes | Topic name | | | worker | No | | | | state | Yes | Always `COMPLETED`. | | | kind | Yes | Always `BPMN_ELEMENT`. | | | listenerEventType | Yes | Always `UNSPECIFIED`. | | | retries | Yes | Always `0`. | | | isDenied | No | | | | deniedReason | No | | | | hasFailedWithRetriesLeft | No | | | | errorCode | No | | | | errorMessage | No | | | | serializedCustomHeaders | No | | | | customHeaders | No | | | | deadline | No | | | | endTime | No | | | | processDefinitionId | Yes | | | | processDefinitionKey | Yes | | | | processInstanceKey | Yes | | | | rootProcessInstanceKey | Yes | | | | elementId | Yes | | | | elementInstanceKey | Yes | | | | tenantId | Yes | | | | partitionId | Yes | Assigned based on the root process instance. See [partition distribution](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/history.md#partition-distribution). | | | creationTime | Yes | | | | lastUpdateTime | No | | | **Process definition** | | | | | | `processDefinitionKey` | Yes | | | | `processDefinitionId` | Yes | | | | `resourceName` | Yes | | | | `name` | Yes | | | | `tenantId` | Yes | | | | `versionTag` | Yes | | | | `version` | Yes | | | | `bpmnXml` | Yes | | | | `formId` | Yes | | | **Process instance** | | | | | | `processInstanceKey` | Yes | | | | `rootProcessInstanceKey` | Yes | | | | `processDefinitionId` | Yes | | | | `processDefinitionKey` | Yes | | | | `state` | Yes | | | | `startDate` | Yes | | | | `endDate` | Yes | | | | `tenantId` | Yes | | | | `parentProcessInstanceKey` | Yes | | | | `parentElementInstanceKey` | Yes | | | | `numIncidents` | Yes | Always `0`. | | | `version` | Yes | | | | `partitionId` | Yes | Assigned based on the root process instance, or randomly for root process instances. See [partition distribution](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/history.md#partition-distribution). | | | `treePath` | Yes | | | | `historyCleanupDate` | Yes | | | | `tags` | Yes | Tags do not exist in Camunda 7, but we allow to set them during migration via [interceptors](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/history.md#entity-transformation) and they will be visible in Camunda 8 after migration. Default tags: `legacy-id` | | | `businessId` | Yes | | | **User task** | | | | | | `userTaskKey` | Yes | | | | `elementId` | Yes | | | | `name` | Yes | | | | `processDefinitionId` | Yes | | | | `creationDate` | Yes | | | | `completionDate` | Yes | | | | `assignee` | Yes | | | | `state` | Yes | | | | `formKey` | Yes | | | | `processDefinitionKey` | Yes | | | | `processInstanceKey` | Yes | | | | `rootProcessInstanceKey` | Yes | | | | `elementInstanceKey` | Yes | | | | `tenantId` | Yes | | | | `dueDate` | Yes | | | | `followUpDate` | Yes | | | | `candidateGroups` | Yes | | | | `candidateUsers` | Yes | | | | `externalFormReference` | No | | | | `processDefinitionVersion` | Yes | | | | `customHeaders` | No | | | | `priority` | Yes | | | | `tags` | Yes | Tags do not exist in Camunda 7, but we allow to set them during migration via [interceptors](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/history.md#entity-transformation) and they will be visible in Camunda 8 after migration | | | `partitionId` | Yes | Assigned based on the root process instance. See [partition distribution](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/history.md#partition-distribution). | | **Variable** | | | | | | `variableKey` | Yes | | | | `name` | Yes | | | | `type` | Yes | | | | `doubleValue` | Yes | | | | `longValue` | Yes | | | | `value` | Yes | | | | `fullValue` | Yes | | | | `isPreview` | Yes | | | | `scopeKey` | Yes | | | | `processInstanceKey` | Yes | | | | `rootProcessInstanceKey` | Yes | | | | `processDefinitionId` | Yes | | | | `tenantId` | Yes | | | | `partitionId` | Yes | Assigned based on the root process instance. See [partition distribution](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/history.md#partition-distribution). | | | `elementInstanceKey` | Yes | | --- ## History Use the History Data Migrator to copy process instance audit data to Camunda 8. ## About history migration Process instances leave traces, referred to as [history in Camunda 7](https://docs.camunda.org/manual/latest/user-guide/process-engine/history/). These are audit logs of when a process instance was started, what path it took, and so on. It is important to note that audit data can exist for ended processes from the past, but is also available for currently still running process instances, as those process instances also left traces up to the current wait state. The History Data Migrator can copy this audit data to Camunda 8. For process instances that were still active or suspended in Camunda 7, the migrated history data will be marked as canceled in Camunda 8. This ensures a clear audit trail while preventing confusion with actively running instances in Camunda 8. Audit data migration might need to look at a huge amount of data, which can take time to migrate. You can run audit data migration alongside normal operations (for example, after the successful big bang migration of runtime process instances) so that it doesn't require downtime and as such, the performance might not be as critical as for runtime instance migration. During migration, the History Data Migrator sets a `legacyId` variable in the process instances to link them to their original Camunda 7 process instances. ## Requirements and limitations The following requirements and limitations apply: - Camunda 7 must be stopped, and the Camunda 8 database must be reachable. Zeebe can be stopped during history migration. - The History Data Migrator must be able to access the Camunda 7 database. - The History Data Migrator can migrate data to Camunda 8 only when a relational database (RDBMS) is used. - The History Data Migrator must be able to access the Camunda 8 database. As a result, you can run this tool only in a self-managed environment. - If you manipulate Camunda 7 data between History Data Migrator runs, data consistency might be affected. See [auto-cancellation of active instances](#auto-cancellation-of-active-instances) for details. - **History cleanup during migration**: If you keep Camunda 8 running and history cleanup is enabled during history migration, and cleanup dates are due (for example, past removal times from Camunda 7 or negative auto-cancel TTL values), Camunda 8 history cleanup will run concurrently with migration. This may result in parent entities being cleaned up before their children are migrated, causing child entities to be skipped. See [history cleanup](#history-cleanup) for mitigation strategies. - If you migrate runtime and history data for an active Camunda 7 process instance, two separate records will appear in Operate: 1. **Fresh runtime instance**: The migrated active process instance running on Zeebe. This instance continues execution from the last wait state before migration and produces new history going forward. It does not include historical data from before the migration. 2. **Auditable instance**: A canceled historic process instance that preserves the audit trail (history data) up to the last wait state pre-migration. This instance appears as canceled and serves only as an audit record of what happened in Camunda 7. These two instances are separate entities and are not automatically linked in the UI, although they share the same process variables from the migration point. ## Usage examples ```bash # Run history migration ./start.sh --history # List all skipped history entities ./start.sh --history --list-skipped # List skipped entities for specific types ./start.sh --history --list-skipped HISTORY_PROCESS_INSTANCE HISTORY_USER_TASK # List the Camunda 7 ID and Camunda 8 key for each migrated entity ./start.sh --history --list-migrated # List the Camunda 7 ID and Camunda 8 key for specific entity types ./start.sh --history --list-migrated HISTORY_PROCESS_INSTANCE # Retry skipped history entities ./start.sh --history --retry-skipped ``` ```bash # Run history migration start.bat --history # List all skipped history entities start.bat --history --list-skipped # List skipped entities for specific types start.bat --history --list-skipped HISTORY_PROCESS_INSTANCE HISTORY_USER_TASK # List the Camunda 7 ID and Camunda 8 key for each migrated entity start.bat --history --list-migrated # List mappings for specific entity types start.bat --history --list-migrated HISTORY_PROCESS_INSTANCE # Retry skipped history entities start.bat --history --retry-skipped ``` ## Entity types | Entity type | Description | | :---------------------------- | :------------------- | | `HISTORY_FORM_DEFINITION` | Form definitions | | `HISTORY_PROCESS_DEFINITION` | Process definitions | | `HISTORY_PROCESS_INSTANCE` | Process instances | | `HISTORY_JOB` | Jobs | | `HISTORY_INCIDENT` | Process incidents | | `HISTORY_VARIABLE` | Process variables | | `HISTORY_USER_TASK` | User tasks | | `HISTORY_FLOW_NODE` | Flow node instances | | `HISTORY_DECISION_INSTANCE` | Decision instances | | `HISTORY_DECISION_DEFINITION` | Decision definitions | ## Tenants - Camunda 7's `null` tenant is migrated to Camunda 8's `` tenant. - All other `tenantId`s will be migrated as-is. - For details, see [multi-tenancy](/components/concepts/multi-tenancy.md#tenant-identifier) in Camunda 8. ## Auto-cancellation of active instances When migrating history data, the Data Migrator automatically handles **active or suspended** process instances from Camunda 7 by marking them as **canceled** in Camunda 8. This applies to: - Process instances - Flow nodes - User tasks - Incidents Auto-canceled entities are assigned the migration timestamp as their end date. By default, auto-canceled instances receive a cleanup date calculated as: ```text cleanup_date = end_date + 6 months ``` This keeps auto-canceled instances eligible for history cleanup after six months and helps prevent unbounded history growth. See [configuration for history auto-cancellation](../data-migrator/config-properties.md#camundamigratorhistoryauto-cancelcleanup) for more details. :::warning Negative TTL values If you configure a negative auto-cancel TTL value, calculated cleanup dates are in the past. If Camunda 8 is running during migration, history cleanup can immediately clean up these entities, potentially before their child entities are migrated. See [history cleanup](#history-cleanup) for mitigation strategies. ::: ## History Cleanup Instances that were already completed in Camunda 7 retain their original cleanup dates: - If a `removalTime` exists in Camunda 7, it is migrated as-is. Child entities (user tasks, variables, flow nodes) inherit the root instance's cleanup date. - If no `removalTime` exists, no cleanup date is set. - Auto-cancel cleanup configuration **only applies to instances that were active or suspended** in Camunda 7. If Camunda 8 is running during migration with cleanup dates in the past, history cleanup may delete parent entities before their children are migrated, causing children to be skipped. **Common scenarios:** - Past `removalTime` values from Camunda 7 - Negative auto-cancel TTL (for example, `P-1D`) - Long migrations where dates become due during execution As a result, you will see skipped child entities with messages referencing the deleted parent. These cannot be recovered unless the parent is re-migrated. Choose one approach to prevent cleanup interference: **Stop Camunda 8 during migration** - Stop Camunda 8 cluster before migration - Run migration while offline - Start Camunda 8 when complete ✅ Guarantees no cleanup interference **Set cleanup dates in the future** - Update Camunda 7 removal times before migration (if feasible) - Configure positive TTL for auto-canceled instances (for example, `P6M`, `P1Y`) ✅ Prevents immediate cleanup **Allow concurrent cleanup** - Run Camunda 8 during migration - Accept some entities may be cleaned up immediately - Accept child entity skips ⚠️ Suitable only when historical data loss is acceptable ## Partition distribution The History Data Migrator assigns migrated history data to Zeebe partitions. **Partition assignment is critical for history cleanup to work correctly.** ### How partitions are assigned - **Root process instances** or **standalone decisions**: Randomly assigned to available partitions - **Child entities** (sub-processes, call activities, flow nodes, variables, user tasks, incidents, decision instances, etc.): Inherit partition from root process instance - **Audit logs**: Inherit the root process instance partition, or are randomly assigned when not related to a process instance This ensures all entities in a process hierarchy share the same partition, enabling the RDBMS exporter on that partition to perform cleanup. :::warning Partition configuration must match Zeebe topology Camunda 7 migrated history data can **only be deleted via history cleanup**, which requires: - The partition ID exists in your Camunda 8 Zeebe cluster - That partition has an RDBMS exporter configured If partition IDs assigned during migration don't exist or lack RDBMS exporters, **that data cannot be cleaned up** and will persist indefinitely. ::: ### Partition discovery By default, the migrator queries Zeebe topology through the Camunda REST API at migration start to discover available partitions. ### Offline mode To migrate without Camunda 8 REST API connectivity (no topology query), configure the partition count manually: ```yaml camunda.migrator: history: partition-count: 3 # Must match your Camunda 8 cluster ``` When configured: - Topology is not queried from REST API - Partition IDs generated as sequence: 1, 2, 3, ... - No Camunda 8 REST API connectivity is required at migration start (database connectivity is still required) The configured value must exactly match your Camunda 8 Zeebe cluster's partition count. If the configured value does not match the cluster, then: - Data assigned to non-existent partitions cannot be cleaned up - Data may persist indefinitely with no automatic cleanup path Always verify cluster partition configuration before migration. :::caution Don't change partitions after migration Changing cluster partition count after migration can leave data on removed partitions that cannot be cleaned up. Complete history migration before scaling partitions. ::: ## Forms The History Data Migrator automatically migrates [Camunda Forms](https://docs.camunda.org/manual/latest/user-guide/task-forms/#camunda-forms) from Camunda 7 to Camunda 8. This includes forms linked to process definitions (start forms) and user tasks. The form schema (JSON definition) is extracted from the Camunda 7 deployment resources and migrated to Camunda 8. The form structure, fields, and validation rules are preserved during migration. User tasks that reference non-existent forms will be migrated as well. ### Form linking The migrator automatically detects and links forms configured using `camunda:formRef` with a `camunda:formKey`. This applies to both start events and user tasks. For each form reference, the migrator: 1. Resolves the form definition based on the form key and binding (deployment, latest, or version). 2. Links the element (process definition or user task) to the migrated form in Camunda 8. Example BPMN configurations that will be migrated: ```xml ``` ## Atomicity The History Data Migrator uses the configured Camunda 8 datasource for both the migration mapping schema and the migrated data. This ensures single-transaction atomicity for each entity migration. ### What is migrated atomically Each entity migration writes multiple rows in a single transaction: - Camunda 8 data: The migrated entity (for example, a user task, process instance, or variable) - Child entities, when applicable (for example, decision instances and their related decisions, inputs, and outputs) - Tracking information: A mapping from the Camunda 7 ID to the Camunda 8 key, used for resuming migrations and preventing duplicates If an error occurs, the transaction is rolled back and no partial data is persisted. This prevents inconsistent states such as Camunda 8 data without tracking information (which can cause duplicates on retry) or orphaned child entities. ## Entity transformation Entity transformations are handled by built-in interceptors that transform Camunda 7 historic entities into Camunda 8 database models during migration. The History Data Migrator uses the `EntityInterceptor` interface to allow customization of this conversion process. ### Built-in interceptors The following built-in transformers convert Camunda 7 historic entities: | Interceptor | Camunda 7 entity type | Camunda 8 Model | | ------------------------------------------- | ---------------------------------------- | ----------------------------- | | `AuditLogTransformer` | `UserOperationLogEntry` | `AuditLogDbModel` | | `FormTransformer` | `CamundaFormDefinitionEntity` | `FormDbModel` | | `ProcessInstanceTransformer` | `HistoricProcessInstance` | `ProcessInstanceDbModel` | | `ProcessDefinitionTransformer` | `ProcessDefinition` | `ProcessDefinitionDbModel` | | `FlowNodeTransformer` | `HistoricActivityInstance` | `FlowNodeInstanceDbModel` | | `UserTaskTransformer` | `HistoricTaskInstance` | `UserTaskDbModel` | | `JobTransformer` | `HistoricJobLog` | `JobDbModel` | | `IncidentTransformer` | `HistoricIncident` | `IncidentDbModel` | | `VariableTransformer` | `HistoricVariableInstance` | `VariableDbModel` | | `DecisionInstanceTransformer` | `HistoricDecisionInstance` | `DecisionInstanceDbModel` | | `DecisionDefinitionTransformer` | `HistoricDecisionDefinition` | `DecisionDefinitionDbModel` | | `DecisionRequirementsDefinitionTransformer` | `HistoricDecisionRequirementsDefinition` | `DecisionRequirementsDbModel` | ## Custom transformation The `EntityInterceptor` interface allows you to define custom logic that executes when a Camunda 7 historic entity is being converted to a Camunda 8 database model during migration. This is useful for enriching, auditing, or customizing entity conversion. Custom interceptors are enabled by default and can be restricted to specific entity types. ### Type-safe API The `EntityInterceptor` interface uses Java generics to provide compile-time type safety: ```java public interface EntityInterceptor { void execute(C7 entity, C8 builder); // ... } ``` ### How to implement an `EntityInterceptor` 1. Create a new Maven project with the provided `pom.xml` structure 2. Add a dependency on `camunda-7-to-8-data-migrator-core` (scope: `provided`) 3. Implement the `EntityInterceptor` interface 4. Add setter methods for any configurable properties 5. Package as JAR and deploy to the `configuration/userlib` folder 6. Configure in `configuration/application.yml` ### Create a custom entity interceptor Here's an example of a custom entity interceptor which is only called for process instances: ```java public class ProcessInstanceEnricher implements EntityInterceptor { /** * Restrict this interceptor to only handle process instances. */ @Override public Set> getTypes() { return Set.of(HistoricProcessInstance.class); } @Override public void execute(HistoricProcessInstance entity, ProcessInstanceDbModel.ProcessInstanceDbModelBuilder builder) { // Custom conversion logic // For example, add custom metadata or modify the conversion builder.processDefinitionId(entity.getProcessDefinitionKey()); } } ``` #### Access Camunda 7 process engine To retrieve information from Camunda 7 entities, use the `EntityConversionContext` parameter which provides access to the `processEngine`. Use it to access services such as `RepositoryService` and `RuntimeService`. Fetch additional data as needed from other Camunda 7 entities. ```java public class ProcessInstanceEnricher implements EntityInterceptor { @Override public Set> getTypes() { return Set.of(HistoricProcessInstance.class); } /** * Alternative execute signature with EntityConversionContext access. * This signature gives you access to the process engine and other context. */ @Override public void execute(EntityConversionContext context) { // Access the entity and builder from context HistoricProcessInstance entity = context.getC7Entity(); ProcessInstanceDbModel.ProcessInstanceDbModelBuilder builder = context.getC8DbModelBuilder(); // Use ProcessEngine to retrieve deployment information from Camunda 7 process engine ProcessEngine processEngine = context.getProcessEngine(); // Example: Retrieve the deployment ID from the process definition String deploymentId = processEngine.getRepositoryService() .createProcessDefinitionQuery() .processDefinitionKey(entity.getProcessDefinitionKey()) .singleResult() .getDeploymentId(); // Custom conversion logic using the retrieved data // ... } } ``` **Note:** The `EntityInterceptor` interface provides two `execute` method signatures: - `execute(C7 entity, C8 builder)` - Simple type-safe signature for basic transformations - `execute(EntityConversionContext context)` - Full signature with access to process engine and other context ### Limit interceptors by entity type Entity interceptors can be restricted to specific entity types using the `getTypes()` method. Use Camunda 7 historic entity classes: ```java // Example 1: Handle multiple specific types public class MultiEntityInterceptor implements EntityInterceptor { @Override public Set> getTypes() { // Handle only specific types return Set.of( ProcessDefinition.class, // Process definitions HistoricProcessInstance.class, // Process instances HistoricActivityInstance.class, // Flow nodes/activities HistoricTaskInstance.class, // User tasks HistoricVariableInstance.class, // Variables HistoricIncident.class, // Incidents HistoricDecisionInstance.class // Decision instances ); } @Override public void execute(Object entity, Object builder) { // Handle different entity types if (entity instanceof HistoricProcessInstance) { // Process instance logic } else if (entity instanceof HistoricActivityInstance) { // Flow node logic } // etc. } } // Example 2: Universal interceptor (handles all entity types) public class EntityLogger implements EntityInterceptor { @Override public Set> getTypes() { return Set.of(); // Empty set = handle all types } @Override public void execute(Object entity, Object builder) { // This will be called for all entity types System.out.println("Converting entity: " + entity.getClass().getSimpleName()); } } ``` ### Configure custom interceptors Configure your custom interceptors in `application.yml`: ```yaml # Entity interceptor plugins configuration # These plugins can be packaged in JARs and dropped in the userlib folder camunda: migrator: interceptors: - class-name: com.example.migrator.ProcessInstanceEnricher enabled: true properties: customProperty: "value" enableAudit: true ``` ### Deployment 1. Package your custom interceptor as a JAR file 2. Place the JAR in the `configuration/userlib/` folder 3. Configure the interceptor in `configuration/application.yml` 4. Restart the Data Migrator The `enabled` property is supported for all interceptors (both built-in and custom) and defaults to `true`. See [example interceptor](https://github.com/camunda/camunda-7-to-8-migration-tooling/tree/main/data-migrator/examples/entity-interceptor). ### Execution order - Custom interceptors configured in the `application.yml` are executed in their order of appearance from top to bottom - Built-in transformers run first, followed by custom interceptors ### Error handling When an entity transformation fails: 1. The entity is marked as _skipped_ in the migration database. 2. After the initial migration completes, the migrator automatically retries all skipped entities. 3. Retries run in multiple passes until no further progress can be made (that is, no additional entities are successfully migrated). 4. Any entities that remain skipped after all automatic retries are logged as warnings, along with their skip reasons. 5. Use `--history --list-skipped` to view entities that remain skipped after automatic retries. 6. After resolving the underlying issue (for example, unsupported variable types), use `--history --retry-skipped` to manually retry the migration. This automatic retry mechanism is particularly useful for resolving cross-entity dependencies, such as: - Flow node instances that depend on their parent flow node (scope) - Child process instances that depend on parent call activities - Variables or user tasks that depend on their parent process instance --- ## Identity Use the Identity Data Migrator to copy authorizations and tenants to Camunda 8. ## About identity migration Identity data in Camunda includes: - **Identities**: Users, groups, tenants, and their related memberships - **Authorizations**: Permission rules that control access to resources For more information about limitations, refer to the [limitations](./limitations.md#identity) of the Identity Data Migrator. ### Users, groups, and group memberships - The Identity Data Migrator does **not** migrate Camunda 7 internally managed users, groups, and group memberships to Camunda 8. - We recommend integrating Camunda 8 with your organization's external Identity Provider (IdP) instead. - Most organizations use an IdP to manage users, groups, and group memberships centrally, making manual migration unnecessary. ## Authorizations Not all authorizations can be migrated from Camunda 7 to Camunda 8 due to differences in the authorization models of both systems. When identity migration is executed, authorizations that are not supported are skipped and the reason for incompatibility is logged by the migrator. If an authorization contains at least one unsupported permission, the whole authorization is skipped. The following tables provide an overview of the supported authorizations. #### By Authorization Type Camunda 8 only supports `GRANT` authorizations. `REVOKE` and `GLOBAL` authorizations from Camunda 7 are not supported for migration. Because in Camunda 7 `GRANT` authorizations take precedence over `REVOKE` authorizations, skipping (and therefore ignoring) `REVOKE` authorizations does not lead to a loss of effective permissions when migrating to Camunda 8. | Authorization Type | Migration supported | | ------------------ | ------------------- | | `GRANT` | Yes | | `REVOKE` | No | | `GLOBAL` | No | #### By Resource Type | C7 Resource Type | Migration supported | C8 Resource Type equivalent | | ---------------------------------- | ----------------------------------------------- | ---------------------------------- | | `Application` | Yes | `Component` | | `Authorization` | [Partial\*](#authorization-compatibility) | `Authorization` | | `Batch` | [Partial\*](#batch-compatibility) | `Batch` | | `Dashboard` | No | - | | `Decision Definition` | [Partial\*](#decision-definition-compatibility) | `Decision Definition` | | `Decision Requirements Definition` | Yes | `Decision Requirements Definition` | | `Deployment` | [Yes\*](#deployment-compatibility) | - | | `Filter` | No | - | | `Group` | Yes | `Group` | | `Group Membership` | [Partial\*](#group-membership-compatibility) | `Group` | | `Historic Process Instance` | No | - | | `Historic Process Instance` | No | - | | `Historic Task` | No | - | | `Process Definition` | [Partial\*](#process-definition-compatibility) | `Process Definition` | | `Process Instance` | No | - | | `Report` | No | - | | `System` | [Partial\*](#system-compatibility) | `System` | | `Task` | No | - | | `Tenant` | Yes | `Tenant` | | `Tenant Membership` | [Partial\*](#tenant-membership-compatibility) | `Tenant` | | `User` | Yes | `User` | | `User Operation Log Category` | No | - | Details for partial migration can be found below. ## Partial migration Some resource types only support partial migration due to differences in the Camunda 7 and Camunda 8 authorization models, which means some permissions (or some combination of permissions) are not supported for migration. The following sections provide details on the supported permissions for these resource types. ### `Authorization` compatibility In Camunda 7, authorizations for `Authorization` resources can be fine-grained to specific resource IDs (`authorizationId`). Camunda 8 only supports the wildcard (`*`) resource ID for the `Authorization` type. Therefore, only authorizations with the wildcard resource ID are migrated. Migration support for individual permissions: | C7 Permission | Migration supported | C8 Permission equivalent | | ------------- | ------------------- | ------------------------------------ | | `READ` | Yes | `READ` | | `UPDATE` | Yes | `UPDATE` | | `CREATE` | Yes | `CREATE` | | `DELETE` | Yes | `DELETE` | | `ALL` | Yes | `READ`, `UPDATE`, `CREATE`, `DELETE` | ### `Batch` compatibility In Camunda 7, authorizations for `Batch` resources can be fine-grained to specific resource IDs (`batchId`). Camunda 8 only supports the wildcard (`*`) resource ID for the `Batch` type. Therefore, only authorizations with the wildcard resource ID are migrated. | C7 Permission | Migration supported | C8 Permission equivalent | | ------------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `READ` | Yes | `READ` | | `UPDATE` | Yes | `UPDATE` | | `CREATE` | Yes | `CREATE` | | `DELETE` | No | - | | `READ_HISTORY` | No | - | | `DELETE_HISTORY` | No | - | | `CREATE_BATCH_MIGRATE_PROCESS_INSTANCES` | Yes | `CREATE_BATCH_OPERATION_MIGRATE_PROCESS_INSTANCE` | | `CREATE_BATCH_MODIFY _PROCESS_INSTANCES` | Yes | `CREATE_BATCH_OPERATION_MODIFY_PROCESS_INSTANCE` | | `CREATE_BATCH_RESTART_PROCESS_INSTANCES` | No | - | | `CREATE_BATCH_DELETE_RUNNING_PROCESS _INSTANCES` | Yes | `CREATE_BATCH_OPERATION_CANCEL_PROCESS_INSTANCE`, `CREATE_BATCH_OPERATION_DELETE_PROCESS_INSTANCE` | | `CREATE_BATCH_DELETE_FINISHED_PROCESS _INSTANCES` | Yes | `CREATE_BATCH_OPERATION_DELETE_PROCESS_INSTANCE` | | `CREATE_BATCH_DELETE_DECISION_INSTANCES` | Yes | `CREATE_BATCH_OPERATION_DELETE_DECISION_INSTANCE` | | `CREATE_BATCH_SET_JOB_RETRIES` | No | - | | `CREATE_BATCH_SET_REMOVAL_TIME` | No | - | | `CREATE_BATCH_SET_EXTERNAL_TASK_RETRIES` | No | - | | `CREATE_BATCH_UPDATE_PROCESS_INSTANCES _SUSPEND` | No | - | | `CREATE_BATCH_SET_VARIABLES` | No | - | | `ALL` | Yes | `CREATE`, `READ`, `UPDATE`, `CREATE_BATCH_OPERATION_CANCEL_PROCESS_INSTANCE`, `CREATE_BATCH_OPERATION_DELETE_PROCESS_INSTANCE`, `CREATE_BATCH_OPERATION_MIGRATE_PROCESS_INSTANCE`, `CREATE_BATCH_OPERATION_MODIFY_PROCESS_INSTANCE`, `CREATE_BATCH_OPERATION_RESOLVE_INCIDENT`, `CREATE_BATCH_OPERATION_DELETE_DECISION_INSTANCE`, `CREATE_BATCH_OPERATION_DELETE_DECISION_DEFINITION`, `CREATE_BATCH_OPERATION_DELETE_PROCESS_DEFINITION`, | ### `Group Membership` compatibility The `Group Membership` resource type does not exist in Camunda 8, but its functionality is covered by the `Group` resource type in combination with the `UPDATE` permission. However, only the `ALL` permission from Camunda 7 is supported for migration, as individual `CREATE` and `DELETE` permissions would result in a more permissive authorization. | C7 Permission | Migration supported | C8 Resource Type equivalent | C8 Permission equivalent | | ------------- | ------------------- | --------------------------- | ------------------------ | | `CREATE` | No | - | - | | `DELETE` | No | - | - | | `ALL` | Yes | `GROUP` | `UPDATE` | ### `Tenant Membership` compatibility The `Tenant Membership` resource type does not exist in Camunda 8, but its functionality is covered by the `Tenant` resource type in combination with the `UPDATE` permission. However, only the `ALL` permission from Camunda 7 is supported for migration, as individual `CREATE` and `DELETE` permissions would result in a more permissive authorization. | C7 Permission | Migration supported | C8 Resource Type equivalent | C8 Permission equivalent | | ------------- | ------------------- | --------------------------- | ------------------------ | | `CREATE` | No | - | - | | `DELETE` | No | - | - | | `ALL` | Yes | `TENANT` | `UPDATE` | ### `System` compatibility | C7 Permission | Migration supported | C8 Permission equivalent | | ------------- | ------------------- | -------------------------------------------------------- | | `READ` | Yes | `READ`, `READ_USAGE_METRIC` | | `SET` | No | - | | `DELETE` | No | - | | `ALL` | Yes | `READ`, `READ_USAGE_METRIC`, `UPDATE`, `READ_JOB_METRIC` | ### `Decision Definition` compatibility As historic definitions are migrated with a prefixed ID to Camunda 8 to avoid collisions between native and migrated definitions ([ref](../data-migrator/limitations.md#history)), authorizations for `Decision Definition` resources are migrated twice: once for the original ID and once for the prefixed ID. This guarantees that authorizations remain effective after migration. | C7 Permission | Migration supported | C8 Permission equivalent | | ----------------- | ------------------- | ------------------------------------------------------------------------------------------------------------ | | `READ` | Yes | `READ_DECISION_DEFINITION`, `READ_DECISION_INSTANCE` | | `UPDATE` | No | - | | `CREATE_INSTANCE` | Yes | `CREATE_DECISION_INSTANCE` | | `READ_HISTORY` | No | - | | `ALL` | Yes | `CREATE_DECISION_INSTANCE`, `READ_DECISION_DEFINITION`, `READ_DECISION_INSTANCE`, `DELETE_DECISION_INSTANCE` | ### `Process Definition` compatibility As historic definitions are migrated with a prefixed ID to Camunda 8 to avoid collisions between native and migrated definitions ([ref](../data-migrator/limitations.md#history)), authorizations for `Process Definition` resources are migrated twice: once for the original ID and once for the prefixed ID. This guarantees that authorizations remain effective after migration. | C7 Permission | Migration supported | C8 Permission equivalent | | -------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `READ` | Yes | `READ_PROCESS_DEFINITION` | | `UPDATE` | No | - | | `DELETE` | No | - | | `SUSPEND` | No | - | | `CREATE_INSTANCE` | Yes | `CREATE_PROCESS_INSTANCE` | | `READ_INSTANCE` | Yes | `READ_PROCESS_INSTANCE` | | `UPDATE_INSTANCE` | Yes | `UPDATE_PROCESS_INSTANCE` | | `RETRY_JOB` | No | - | | `SUSPEND_INSTANCE` | No | - | | `DELETE_INSTANCE` | Yes | `DELETE_PROCESS_INSTANCE` | | `MIGRATE_INSTANCE` | No | - | | `READ_TASK` | Yes | `READ_USER_TASK` | | `UPDATE_TASK` | Yes | `UPDATE_USER_TASK` | | `TASK_ASSIGN` | No | - | | `TASK_WORK` | No | - | | `READ_TASK_VARIABLE` | No | - | | `READ_HISTORY` | No | - | | `READ_HISTORY_VARIABLE` | No | - | | `DELETE_HISTORY` | No | - | | `READ_INSTANCE_VARIABLE` | No | - | | `UPDATE_INSTANCE_VARIABLE` | No | - | | `UPDATE_TASK_VARIABLE` | No | - | | `UPDATE_HISTORY` | No | - | | `ALL` | Yes | `CREATE_PROCESS_INSTANCE`, `READ_PROCESS_DEFINITION`, `READ_PROCESS_INSTANCE`, `READ_USER_TASK`, `UPDATE_PROCESS_INSTANCE`, `UPDATE_USER_TASK`, `MODIFY_PROCESS_INSTANCE`, `CANCEL_PROCESS_INSTANCE`, `DELETE_PROCESS_INSTANCE` | ### `Deployment` compatibility As `Deployment` maps to `Resource` in Camunda 8, when migrating a `Deployment` authorization to Camunda 8, one authorization per resource contained in the deployment is created in Camunda 8, with the same permissions as the original `Deployment` authorization. This operates in combination with the prefix principle described for [Process Definition](#process-definition-compatibility) and [Decision Definition](#decision-definition-compatibility), which also applies for forms. In practice, this means that, if a deployment contains process definitions, decision definitions, and forms, for each of these resources two authorizations are created in Camunda 8: one for the original ID and one for the prefixed ID. | C7 Permission | Migration supported | C8 Permission equivalent | | ------------- | ------------------- | ---------------------------------------------------------------------------------- | | `READ` | Yes | `READ` | | `CREATE` | Yes | `CREATE` | | `DELETE` | Yes | `DELETE_RESOURCE`, `DELETE_FORM`, `DELETE_PROCESS`, `DELETE_DRD` | | `ALL` | Yes | `CREATE`, `READ`, `DELETE_DRD`, `DELETE_FORM`, `DELETE_PROCESS`, `DELETE_RESOURCE` | ### `ALL` permissions compatibility :::info As illustrated in the tables above, the `ALL` permission in Camunda 7 maps to **all the existing permissions in Camunda 8** for the given resource type, independently of whether these exist in Camunda 7 or not. This is true for all resource types. ::: ## Executing identity migration ```bash # Run identity migration ./start.sh --identity # List all skipped identity entities ./start.sh --identity --list-skipped # List the Camunda 7 ID and Camunda 8 key for each migrated entity ./start.sh --identity --list-migrated # Retry skipped identity entities ./start.sh --identity --retry-skipped ``` ```bash # Run identity migration start.bat --identity # List all skipped identity entities start.bat --identity --list-skipped # List the Camunda 7 ID and Camunda 8 key for each migrated entity start.bat --identity --list-migrated # Retry skipped identity entities start.bat --identity --retry-skipped ``` :::warning After migration has been completed, it is strongly recommended to verify the results in Camunda 8 before using the system in production. ::: --- ## Data Migrator Use the Data Migrator to copy runtime and audit data from Camunda 7 to Camunda 8. ![data-migration](../../../img/data-migration.png) ## Modes of operation The Data Migrator offers three modes of operation: - [Runtime migration](runtime.md): Migrate running process instances and continue execution in C8. Production-ready with Camunda 8.8. - [History migration](history.md): Copy audit (history) data to Camunda 8. - [Identity migration](identity.md): Migrate identity data to Camunda 8. Migration details are summarized as follows: | What is migrated | What is NOT migrated | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Running process instances (state-preserving).Process variables and their values.Execution history.Identity data. | BPMN process models (use the [Diagram Converter](/guides/migrating-from-camunda-7/migration-tooling/diagram-converter.md)).Custom code or integrations (use [Code Conversion Utilities](../code-conversion)).Users, groups (use IdP), and authorizations (use [Identity Migration](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/identity.md)).Runtime task assignments and states (due date, priority, etc.). | ## Key features - Preserves the execution state of running process instances during migration. - Converts and migrates process variables, decision inputs, and decision outputs with proper type handling. - Supports customizable variable interceptors for both runtime and history migration contexts. - Validates data before migration to help ensure a successful run. - Allows you to skip or retry problematic instances during migration. - Provides detailed logging and reporting to monitor migration progress. - Supports multiple database vendors, including H2, PostgreSQL, and Oracle. ## Typical choreography of runtime and history migration As described in [the roll-out phase of the migration journey](../../migration-journey.md), you will typically use the following sequence of tasks when applying both data migrations (while keeping downtimes to a minimum): :::warning Configure the Camunda 8 datasource before your first migration If you plan to migrate both runtime and history data, configure the Camunda 8 datasource before running your first migration. If the Camunda 8 datasource is not configured, the migrator creates the migration schema on the Camunda 7 datasource as a fallback. If you add the Camunda 8 datasource later, the migrator creates a new migration schema on Camunda 8, which resets migration tracking and can result in duplicate migrations. ```yaml # Configure this before your first migration camunda.migrator.c8.data-source: jdbc-url: jdbc:postgresql://localhost:5432/camunda8 username: camunda password: camunda ``` ::: 1. Stop the Camunda 7 solution (normally shut down your application). 2. Start the Data Migrator in "running instance migration mode". 3. Wait until running instance migration is completed. 4. Start the new Camunda 8 solution immediately so migrated process instances can continue right away. 5. Start the Data Migrator in "history migration mode". 6. The migrator runs until all history data is migrated while Camunda 8 process execution continues in parallel. With this approach, the duration of history migration doesn't block big bang migrations. ## Customization You might need to customize the data migration, especially if you used complex data formats in Camunda 7 (for example, Java objects) that need to be converted to something Camunda 8 can handle (for example, JSON). As part of this step, you might also need to extract big payloads and binaries (like documents) into an external data store and reference it from the process (using, for example, upcoming document handling possibilities). ## Repository You can track progress and releases in the [migration tooling repository](https://github.com/camunda/camunda-7-to-8-migration-tooling). ## Cockpit plugin (experimental) The [Cockpit plugin](cockpit-plugin.md) provides a web-based interface to view information about skipped and migrated data during the migration process (experimental). --- ## Install and quick start Install the Data Migrator and run your first data migration. ## Prerequisites - Java 21+ - Running Camunda 8 (self-managed, or SaaS for runtime migration only) - Camunda 7 has been stopped - Access to Camunda 7 database - Models migrated and deployed to C8 ## Installation 1. Download the latest release from the GitHub releases page: - https://github.com/camunda/camunda-7-to-8-migration-tooling/releases 2. Extract the archive to your preferred directory. 3. Navigate to the extracted directory. ## Quick start The Data Migrator supports three main modes of operation: - [Runtime migration](runtime.md): Migrate running process instances - [History migration](history.md): Copy audit trail data - [Identity migration](identity.md): Copy authorizations and tenants Below is a quick start guide to get you started. For detailed information on each mode, see their respective documentation pages. ### Quick start steps 1. Make sure Camunda 8 is up and running, all process models to migrate are deployed, and Camunda 7 has been stopped. For runtime migration, every process model requires: - A blank start event (you must add one if the process model doesn't have one already). - An execution listener at the end of your blank start event with the job type `migrator`. You have to add this manually or let the [Diagram Converter](/guides/migrating-from-camunda-7/migration-tooling/diagram-converter.md) add it. ```xml ``` :::note For automatic resource deployment, you can also drop your BPMN files into the `configuration/resources` folder. ::: 2. Drop your JDBC driver into the `configuration/userlib` folder (for example, `postgresql-$VERSION.jar` for PostgreSQL). 3. Prepare your configuration file (`configuration/application.yml`): ```yaml camunda.client: mode: self-managed grpc-address: http://localhost:26500 rest-address: http://localhost:8080 camunda.migrator: auto-ddl: true # Automatically create migration tracking schema camunda.migrator.c7.data-source: jdbc-url: jdbc:postgresql://localhost:5432/camunda7 username: your-username password: your-password camunda.migrator.c8.data-source: jdbc-url: jdbc:postgresql://localhost:5432/camunda8 username: your-username password: your-password ``` 4. Run the Data Migrator: ```bash # Runtime migration (default mode) ./start.sh --runtime # History migration ./start.sh --history # Identity migration ./start.sh --identity # View all available options ./start.sh --help ``` ```bash # Runtime migration (default mode) start.bat --runtime # History migration start.bat --history # Identity migration start.bat --identity # View all available options start.bat --help ``` 5. Monitor the migration progress in the console output and log files. --- ## Limitations An overview of the current limitations of the Camunda 7 to Camunda 8 Data Migrator, covering general limitations as well as specific limitations related to variables and BPMN elements. ## Identity The following requirements and limitations apply: - Identity migration includes the migration of: - Users, groups, tenants and their associated memberships. - Supported authorizations (detailed in the [Authorizations](identity.md#authorizations) section). - Once migration has been triggered, it's strongly recommended not to create new identity data on Camunda 7. Even if migration is attempted again, the new data might not be migrated. - In order for authorizations to work correctly after migration, process definitions, forms, DRD and decision definitions need to have the same IDs in Camunda 8 as in Camunda 7. This should be the case if you have already migrated runtime and history data. - Tenant memberships are migrated as part of their respective tenants and are not tracked individually. - If a tenant is migrated, all its memberships are migrated as well. If a tenant is skipped, its memberships are also skipped. - If the migration of an individual tenant membership fails (for example, due to a missing user), it cannot be retried. - The `--list-skipped` and `--list-migrated` options do not list individual tenant memberships. - This is also true for groups and group memberships. - For security reasons, passwords for users cannot be migrated. For this reason, users are migrated with a generated secure password that needs to be reset by an administrator after migration. ### Supported entities | Identity type | Migration supported | | ------------------ | ------------------- | | Users | Yes | | Groups | Yes | | Group Memberships | Yes | | Tenants | Yes | | Tenant Memberships | Yes | ## Runtime The runtime migration has the following limitations. ### General limitations - To migrate running process instances, the historic process instance must exist. - You cannot migrate running instances when you have configured history level to `NONE` or a custom history level that doesn't create historic process instances. - The minimum supported history level is `ACTIVITY`. - You must add an execution listener of type `migrator` to all your start events. - Migration of users, groups, or tenants as well as authorizations is currently not supported. - You must ensure that the users, groups, and authorizations are already migrated to Camunda 8 before migrating process instances. - See https://github.com/camunda/camunda-bpm-platform/issues/5175 - Data changed via user operations - Data set via user operations like setting a due date to a user task cannot be migrated currently. - See https://github.com/camunda/camunda-bpm-platform/issues/5182 ### Variables - [Unsupported Camunda 7 types](../variables#unsupported-types). - [Camunda 8 variable name restrictions](/components/concepts/variables.md#variable-values). - Variables that do not follow the restrictions will cause issues in FEEL expression evaluation. - Variables set into the scope of embedded sub-processes are not supported yet and will be ignored. - See https://github.com/camunda/camunda-bpm-platform/issues/5235 :::info To learn more about variable migration, see [variables](../variables). ::: ### Incidents Due to the [limitation regarding async before/after wait states](#async-beforeafter-wait-states), incident data from instances currently waiting due to failed jobs causing active incidents will not be migrated during runtime migration. We recommend to resolve incidents prior to runtime migration. ### BPMN elements Some BPMN elements and configurations supported in Camunda 7 are not supported in Camunda 8 or have specific limitations during migration. Below is an overview of these limitations and recommendations to address them. #### Elements supported in Camunda 7 but not supported in Camunda 8 Some BPMN elements available in Camunda 7 are not supported in Camunda 8. Before migration, compare the BPMN coverage of both platforms and adjust your models accordingly: - [Camunda 7 BPMN coverage](https://docs.camunda.org/manual/7.24/reference/bpmn20/) - [Camunda 8 BPMN coverage](/components/modeler/bpmn/bpmn-coverage.md) When converting diagrams, the Diagram Converter also helps identify unsupported elements interactively. #### Start events - It is required that a process instance contains a single process level None Start Event to run the data migrator. - If a process definition only has event-based start events (for example, Message, Timer), it is required to add a temporary None Start Event. This change must be reverted after the data migration is completed. - Example adding a None Start Event: ```diff Flow_FromEventStartEvent + + Flow_FromNoneStartEvent + + Flow_FromEventStartEvent Flow_FromNoneStartEvent Flow_1o2i34a Flow_1o2i34a ``` #### Async before/after wait states Camunda 8 does not support [asynchronous continuation before or after](https://docs.camunda.org/manual/latest/user-guide/process-engine/transactions-in-processes/#asynchronous-continuations) any kind of wait state. Service-task-like activities are executed asynchronously by default in Camunda 8 - so for example a service task waiting for asynchronous continuation before will be correctly migrated. But if you need to migrate an instance currently waiting asynchronously at other elements in a Camunda 7 model, such as a gateway for example, this instance would just continue without waiting in the equivalent Camunda 8 model. You might need to adjust your model's logic accordingly prior to migration. #### Message events - Only message catch and throw events are supported for migration. - Depending on your implementation, you may need to add [a correlation variable](/components/modeler/bpmn/message-events/message-events.md#messages) to the instance pre-migration. #### Message and signal start events - If your process starts with a message/signal start event, no token exists until the message/signal is received and hence no migration is possible until that moment. - Once the message/signal is received, the token is created and moved down the execution flow and may be waiting at a migratable element inside the process. However, due to how the migration logic is implemented, at the moment the data migrator only supports processes that start with a normal start event. #### Triggered boundary events - Camunda 7 boundary events do not have a natural wait state. - If the process instance to be migrated is currently at a triggered boundary event in Camunda 7, there may still be a job associated with that event, either waiting to be executed or currently running. In this state, the token is considered to be at the element where the job is created: typically the first activity of the boundary event’s handler flow, or technically the point after the boundary event if asyncAfter is used. - During migration to Camunda 8, the token will be mapped to the corresponding target element. However, if that element expects input data that is normally produced by the boundary event’s job (for example, setting variables), this data may be missing in the migrated instance. - Recommendation: To ensure a consistent migration, allow boundary event executions to complete before initiating the migration. #### Call activity To migrate a subprocess that is started from a call activity, the migrator must set the `legacyId` variable for the subprocess. This requires propagating the parent variables. This can be achieved by updating the Camunda 8 call activity in one of the following ways: - Set `propagateAllParentVariables` to `true` (this is the default) in the `zeebe:calledElement` extension element. - Or, if `propagateAllParentVariables` is set to `false`, provide an explicit input mapping: ```xml ``` #### Multi-instance Processes with active multi-instance elements can currently not be migrated. We recommend to finish the execution of any multi-instance elements prior to migration. #### Parallel gateways Process instances with active joining parallel gateways cannot currently be migrated. The migrator will skip these instances during migration. - This limitation occurs when some execution paths have completed and reached the joining parallel gateway, but other paths are still waiting at activities before the gateway. - Recommendation: Ensure no token waits in a joining parallel gateway. - See https://github.com/camunda/camunda-bpm-platform/issues/5461 #### Timer events - Timer start events: prior to migration, you must ensure that your process has at least one [none start event](/components/modeler/bpmn/none-events/none-events.md#none-start-events). Processes that only have a timer start event cannot be migrated. - If your model contains timer events (start and other), you must ensure that no timers fire during the migration process. - Timers with [date](/components/modeler/bpmn/timer-events/timer-events.md#time-date): ensure the date lies outside the migration time frame. - Timers with [durations](/components/modeler/bpmn/timer-events/timer-events.md#time-duration): ensure the duration is significantly longer than the migration time frame. - Timers with [cycles](/components/modeler/bpmn/timer-events/timer-events.md#time-cycle)): ensure the cycle is significantly longer than the migration time frame and/or use a start time that lies outside the migration time frame. - Note that during deployment and/or migration, the timers may be restarted. If business logic requires you to avoid resetting timer cycles/duration, you need to apply a workaround: - Timers with cycles: - Add a start time to your cycle definition that is equal to the moment in time when the currently running Camunda 7 timer is next due. - You must still ensure that the start time lies outside the migration time frame. - Timers with durations: - Non-interrupting timer boundary events: - Switch to cycle definition with a start time that is equal to the moment in time when the currently running Camunda 7 timer is next due and add a "repeat once" configuration. - This way, for the first post migration run, the timer will trigger at the start time. - For all subsequent runs, the defined cycle duration will trigger the timer. The "repeat once" instruction ensures it only fires once, similar to a duration timer. - You must still ensure that the start time lies outside the migration time frame. - Interrupting boundary and intermediate catching events: - Add a variable to your Camunda 7 instance that contains the leftover duration until the next timer is due. - In your Camunda 8 model, adjust the timer duration definition to use an expression: if the variable is set, the value of this variable should be used for the duration. If the variable is not set or does not exist, you may configure a default duration. - This way, for the first post migration run the variable will exist and the timer will set its duration accordingly. - For all subsequent runs, the variable will not exist and the default duration will be used. - Again, you must ensure the leftover duration for the first post migration run lies outside the migration time frame. #### Event subprocesses - Event subprocesses with interrupting start events can cause unexpected behavior during migration if triggered at the wrong moment. This includes timer, message, and signal start events. - What can go wrong: - A task that already ran in Camunda 7 might run again in Camunda 8. - The process might end up in the wrong state after migration — for example, being one step behind what you see in Camunda 7. - When could it happen: - This can occur when a process instance is already inside an event subprocess in Camunda 7, and the start event of that same subprocess is accidentally triggered again in Camunda 8 during migration. - How to prevent it: - Don't correlate messages or send signals during migration. - Temporarily adjust timer start events in event subprocesses to ensure they do not trigger during migration (see the section on timer events for more details). - If above suggestions are not feasible in your use case make sure service tasks are idempotent — so repeating them does not cause issues. ## History The history migration has the following limitations. ### General - All migrated data is assigned `partitionId=1` so that the RDBMS exporter on partition 1 can perform history cleanup for migrated data. - Migrated data has partition ID 4095 encoded in their keys to avoid key collisions with Zeebe produced data. - Due to this, migrated process instances cannot be deleted via C8 API or in Operate since Zeebe cannot delegate the operation to a partition. - See https://github.com/camunda/camunda/issues/47927 - Please use [RDBMS History Cleanup](/self-managed/concepts/databases/relational-db/configuration.md#history-cleanup-1) to delete the migrated data. - The minimum required history level in Camunda 7 is `FULL` to ensure that sufficient data is available for migration. - To avoid collisions between definitions (process/decision/form), each definition migrated from Camunda 7 to 8 has its ID prefixed with `c7-legacy-`. - Do not deploy new definitions in Camunda 8 with IDs starting with this prefix to avoid conflicts. - These migrated definitions are visible in Camunda 8 Tasklist but cannot be started. To start new instances, you need to use the [Diagram Converter](../diagram-converter.md) to migrate your legacy processes to Camunda 8 compatible versions and deploy them to Camunda 8 as described in the [preparation step for runtime migration](runtime.md#1-preparation). - See https://github.com/camunda/camunda-7-to-8-migration-tooling/issues/1000 - Avoid manipulating Camunda 7 data in between History Data Migrator runs to ensure data consistency unless there is a specific migration issue to fix (e.g. moving instances out of states that are not migratable). See [Auto-cancellation of active instances](history.md#auto-cancellation-of-active-instances) for details. - During migration, some entities may be skipped due to unresolved dependencies (for example, when a parent entity has not yet been migrated). - After the initial migration completes, the migrator automatically retries skipped entities to resolve cross-entity dependencies. - Automatic retries run in multiple passes until no further progress can be made. - Entities that remain skipped after all automatic retries are logged as warnings, along with their skip reasons. - After fixing underlying issues, you can manually retry remaining skipped entities using the `--retry-skipped` flag. - Examples of temporary skip reasons include: - Flow node instances whose parent flow node (scope) has not yet been migrated. - Child process instances called from parent call activities, where the parent flow node has not yet been migrated. - The History Data Migrator does not support the following Camunda 7 entity types: - **CMMN entities**: CMMN user tasks and CMMN variables are not supported and are skipped during migration. - **Standalone user tasks**: User tasks that are not associated with a process instance are not supported and are skipped during migration. - Camunda 7 does not store audit data of asyncBefore wait state for flow nodes. Migration of flow nodes is executed in all other cases. - The History Data Migrator does not support the following Camunda 8 entities or properties: - Sequence flow: Sequence flows cannot be highlighted in Operate. - Message subscription and correlated message subscription: These entities are not available in Camunda 7. - Batch operation entity and batch operation item: Camunda 7 does not retain sufficient information about processed instances. - User metrics: Not available in Camunda 7. - Exporter position: This entity does not exist in Camunda 7. - Please note that if any Camunda 7 process instances progress in their state in between multiple runs of the History Data Migrator, data consistency might be affected: for example, if a process instance is completed in Camunda 7 after the first run but before the second run, the History Data Migrator would migrate it as canceled in the first and as completed in the second run. As a result, in Operate you may see that a process instance was canceled in a Flow Node that chronologically precedes the end event in your model, where the instance will be marked as completed. To avoid such situations, ensure that Camunda 7 data remains unchanged between History Data Migrator runs. ### DMN The History Data Migrator supports migration of DMN entities, but with the following limitations: - DMN models version 1.1 are not supported by Operate, decision definition data will be migrated but the decision model itself will not be visible in Operate. ### Forms The History Data Migrator supports migration of Camunda Forms, but with the following limitations: - Only [Camunda Forms](https://docs.camunda.org/manual/latest/user-guide/task-forms/#camunda-forms) are migrated. Other form types are not supported: - Embedded forms (HTML/JSF) - External forms (URL-based forms) - Generated forms (from form data definitions) - Supported form bindings: - `deployment` - Form version deployed together with the process definition - `latest` - Latest version of the form definition - `version` - Specific version of the form definition - Unsupported form bindings: - Expression-based bindings (for example, `${formKey}`) ### Jobs The History Data Migrator migrates only jobs of type [asynchronous continuation](https://docs.camunda.org/manual/7.24/user-guide/process-engine/transactions-in-processes/#configure-asynchronous-continuations). - The jobs associated with multi-instance activities will be skipped. (https://github.com/camunda/camunda-7-to-8-migration-tooling/issues/1103) ### Incidents The History Data Migrator supports migration of DMN entities, but with the following limitations: - The incidents are migrated in `resolved` state. Operate does not visualize resolved incidents, therefore incidents of migrated process instances will not be visible in Operate. Audit data related to incidents can be observed by querying APIs. - When there's a failing start timer in Camunda 7, the incident cannot be migrated (as there's no process instance history) and will be skipped. - The incidents associated with multi-instance activities will be skipped. (https://github.com/camunda/camunda-7-to-8-migration-tooling/issues/1103) ### Audit logs The following limitations apply: - Audit log entries are migrated only for user tasks, process definitions, process instances, variables, decisions, users, groups, and authorizations. - Audit log entries are not migrated for batch operations, identity links, attachments, job definitions, jobs, external tasks, metrics, operation logs, filters, comments, and properties. ### Data coverage For a detailed overview of which Camunda 8 entities and properties are migrated by the History Data Migrator, see the [History migration coverage](history-coverage.md) page. ## Cockpit plugin The [Cockpit plugin](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/cockpit-plugin.md) has the following limitations: - The migration schema has no authorization mechanism. Anyone with authenticated access to the Camunda 7 Cockpit can see the Cockpit Plugin and read the migration schema. - If the migration of a process instance or any other entity is skipped for multiple reasons, only one reason is stored and displayed. - See https://github.com/camunda/camunda-bpm-platform/issues/5389 - For historic data migration the skip reason is currently only stored for the initial migration attempt. If migration fails again after retry, the skip reason is not updated. - See https://github.com/camunda/camunda-bpm-platform/issues/5390 - There are currently some UI inconsistencies. See: - https://github.com/camunda/camunda-bpm-platform/issues/5422 - https://github.com/camunda/camunda-bpm-platform/issues/5423 - https://github.com/camunda/camunda-bpm-platform/issues/5424 - The Cockpit plugin doesn't have extensive test coverage yet so we cannot guarantee a high level of stability and therefore don't claim it to be production-ready. - See https://github.com/camunda/camunda-bpm-platform/issues/5404 --- ## Runtime(Data-migrator) Migrate currently running process instances. ## About runtime migration Running refers to process instances in Camunda 7 that are not yet ended and are currently waiting in a [wait-state](https://docs.camunda.org/manual/latest/user-guide/process-engine/transactions-in-processes/#wait-states). This state is persisted in the database, and a corresponding data entry must be created in Camunda 8 so the process instance can continue from that state in the new solution. ## Requirements and limitations The following requirements and limitations apply: - The Runtime Data Migrator needs to access the Camunda 7 database. - The Runtime Data Migrator needs to access Orchestration Cluster APIs (which means you can also use it when running SaaS). - You must be familiar with the Data Migrator [limitations](limitations.md). If you need to adjust your process models before migration, you can use [process version migration](https://docs.camunda.org/manual/latest/user-guide/process-engine/process-instance-migration/) in the Camunda 7 environment to migrate process instances to versions that are migratable to Camunda 8. One useful strategy is to define dedicated migration states where your process instances can accumulate. Another common strategy is to use [process instance modification](https://docs.camunda.org/manual/latest/user-guide/process-engine/process-instance-modification/) in the Camunda 7 environment to move out of states that are not migratable (for example, process instances within a multiple instance task). To use the Runtime Data Migrator, every Camunda 8 process model must have: - A process-level **None Start Event**, and - An execution listener on that **None Start Event** with the type `migrator` (or a validated job type, see below). Example: ```xml ``` ## Choreography The runtime migration typically follows these phases: ### 1. Preparation - Stop Camunda 7 process execution to avoid starting new instances during migration. - Migrate BPMN models using the [Diagram Converter](/guides/migrating-from-camunda-7/migration-tooling/diagram-converter.md). The Diagram Converter automatically adds the required `migrator` execution listener to None Start Events when the **Add Data Migration Execution Listener** option is enabled. - Adjust Camunda 8 models to comply with migration limitations. - Test migrated models in a Camunda 8 environment. - Back up your Camunda 7 database before migration. ### 2. Migration - Deploy Camunda 8 process models and resources to the target environment. - **Do not start new Camunda 8 process instances** on models that still have the `migrator` execution listener. See [Externally Started Process Instances](#externally-started-process-instances) for details. - Configure the migrator with proper database connections and settings. - Start the migrator and monitor progress through logs. - Verify results in Camunda 8 Operate. - Handle skipped instances by reviewing and addressing validation failures. - After successful migration, clean up models: - Remove `migrator` execution listeners from Camunda 8 models. - Revert temporary model changes. - Redeploy the updated models. - Migrate instances to the latest version of Camunda 8 models if appropriate. ### 3. Validation - Check migrated instances in Camunda 8 Operate. - Verify variable data and migrated state. - Test process continuation by completing some migrated instances. - Monitor system performance and resource usage. - Validate business logic continues to work as expected. ## Validation and skip reasons The migrator validates each process instance before migration and will skip instances that fail validation for the following reasons: | Skip reason | Condition (why it is skipped) | | :------------------------------------ | :--------------------------------------------------------------------------------------------------------------- | | Missing Camunda 8 process definition | No corresponding Camunda 8 process definition is found for the Camunda 7 process ID. | | Multi-instance activities | The process instance has active multi-instance activities. | | Missing flow node elements | The Camunda 7 instance is at a flow node that does not exist in the deployed Camunda 8 model. | | Missing None Start Event | The Camunda 8 process definition does not have a process-level None Start Event. | | Missing `migrator` execution listener | The Camunda 8 process definition does not have an execution listener of type `migrator` on the None Start Event. | | Multi-tenancy | The tenant ids are not configured in the Data Migrator. | When a process instance is skipped: - The skipped process instance is logged. - The instance is marked as skipped in the migration database. - You can list skipped instances. - You can retry migration of skipped instances after fixing the underlying issues. ### Common resolution steps 1. Deploy the missing Camunda 8 process definition 1. Wait for multi-instance activities to complete 1. Ensure all active flow nodes in the Camunda 7 process have corresponding elements in the Camunda 8 process 1. Modify process instance to a supported state ## Usage examples ```bash # Run runtime migration ./start.sh --runtime # List all skipped process instances ./start.sh --runtime --list-skipped # List the Camunda 7 ID and Camunda 8 key for each migrated entity ./start.sh --runtime --list-migrated # Retry skipped process instances ./start.sh --runtime --retry-skipped ``` ```bash # Run runtime migration start.bat --runtime # List all skipped process instances start.bat --runtime --list-skipped # List the Camunda 7 ID and Camunda 8 key for each migrated entity start.bat --runtime --list-migrated # Retry skipped process instances start.bat --runtime --retry-skipped ``` ## Job type configuration By default, the job type is configured as `migrator`. The Diagram Converter adds an execution listener with this type to None Start Events, and the Data Migrator activates jobs matching this type. This works out of the box, and no additional configuration is needed. The job type is relevant because during migration, the Data Migrator creates new Camunda 8 process instances with a `legacyId` variable linking them to their original Camunda 7 instances. It then activates all jobs with the execution listener type `migrator`. These jobs are only processed if `legacyId` is present. Process instances started directly on Camunda 8 are skipped. See [Externally Started Process Instances](#externally-started-process-instances) for details. The migrator supports two job type configurations with fallback behavior: - **`job-type`**: Used for actual job activation (default: `migrator`). - It is used for activating jobs in Camunda 8 and is required for the migrator to function correctly. - It must match the execution listener type defined on the start event in the BPMN model. If the BPMN execution listener is an expression that resolves to a type, then `validation-job-type` needs to be configured as well. - **`validation-job-type`**: Used for validation purposes (optional). - Before starting a process instance in Camunda 8, the Data Migrator verifies that the job type is present in the BPMN. This ensures the process instance execution waits for the Data Migrator at the start event. - When `validation-job-type` is not defined, `job-type` is used for both validation and activation. - You can define a FEEL expression that provides different job types based on the process instance context. - It must match the execution listener type defined on the start event in the BPMN model. - Set to `DISABLED` to disable job type validation entirely. This is not recommended. **Basic Configuration (default):** ```yaml camunda.migrator: job-type: migrator # Used for both validation and activation ``` **Advanced: Separate Validation and Activation:** If you need to use a FEEL expression for the execution listener type (for example, to differentiate between migrated and externally started instances), you can configure `validation-job-type` separately: ```yaml camunda.migrator: job-type: migrator # Used for activation validation-job-type: '=if legacyId != null then "migrator" else "noop"' # Used for validation with FEEL expression ``` When using a FEEL expression in `validation-job-type`, you must also specify the same expression in the execution listener of your BPMN process start events: ```xml ``` :::note Use FEEL expressions only for validation, not for job activation, since during job activation, the FEEL expression is already evaluated to a static value. ::: ### Externally Started Process Instances New Camunda 8 process instances should not be started on models that still have the `migrator` execution listener. Follow the recommended [choreography](#choreography): complete the migration first, then remove the execution listener and redeploy before starting new process instances. If a process instance is started externally (not by the Data Migrator) on a model with the `migrator` execution listener, the Data Migrator will activate the job but skip it because the `legacyId` variable is not present. The process instance will remain at the start event. After the job lock times out, the job becomes available for activation again. This does not cause errors or data corruption, but an externally started process instance will not progress until the execution listener is removed and the model is redeployed. When using the advanced FEEL expression configuration `=if legacyId != null then "migrator" else "noop"`, externally started process instances will generate jobs with the type `noop` instead of `migrator`, so the Data Migrator will not activate them at all. However, to allow these process instances to proceed past the start event, you must implement a **noop job worker** that completes these jobs: ```java @JobWorker(type = "noop") public void handleNoopJobs(ActivatedJob job) { // Simply complete the job without any processing // This allows externally started process instances to continue normally } ``` This approach ensures that: - Process instances started by the Data Migrator are handled by the `migrator` job worker. - Externally started process instances continue their normal execution flow through the `noop` job worker. - Both types of instances can coexist in the same Camunda 8 environment. ## Tenants - Camunda 7 process instances assigned to no tenant (`tenantId=null`) are migrated to Camunda 8 with `` tenant. - The default behavior is to migrate only process instances without assigned tenant. - When migrating process instances, the migrator can be configured to handle specific tenants throughout the migration process. Defining tenant IDs ensures that only process instances associated with those tenants are migrated. - Make sure to create the tenants in Camunda 8 before starting the migration. - Add `Authentication configuration for the client` and assign the user to the all of the tenants ### How Multi-Tenancy Works 1. **Validation**: Pre-migration validation ensures target tenant deployments exist in Camunda 8 2. **Tenant Preservation**: Process instances maintain their original tenant association during migration 3. **Job Activation**: The migrator fetches jobs only for the configured tenants and the default tenant when activating jobs ### Example Use the `camunda.migrator.tenant-ids` [property](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/config-properties.md#camundamigrator) to specify which tenants should be included in the migration process. This property accepts a comma-separated list of tenant identifiers. ```yaml camunda: migrator: tenant-ids: tenant-1, tenant-2, tenant-3 ``` With this configuration, only process instances associated with `tenant-1`, `tenant-2`, `tenant-3`, and the default tenant will be created and migrated. Instances associated with other tenants will be skipped. --- ## Troubleshooting(Data-migrator) Troubleshooting information for common issues when running the Data Migrator. ## Migration fails to start - Verify Java 21+: `java -version`. - Check database connectivity and credentials. - Ensure Camunda 8 is running and accessible. - Review your `configuration/application.yml` configuration. - Check that JDBC driver jar is added to the `configuration/userlib/` directory. ## Process instances are skipped - Ensure Camunda 8 process definitions are deployed. - Verify `migrator` execution listeners are added to None Start Events. - Ensure flow nodes exist in both Camunda 7 and Camunda 8 models. - Review skipped instance logs for exact reasons. List and retry skipped instances: ```bash ./start.sh --runtime --list-skipped ./start.sh --runtime --retry-skipped ``` ```bash start.bat --runtime --list-skipped start.bat --runtime --retry-skipped ``` ## Performance issues - Adjust `camunda.migrator.page-size`. - Ensure database resources are sufficient. - Check network latency between components. - Monitor CPU/memory/disk usage. ## Variable migration errors - Check Camunda 8 variable name restrictions. - Verify variable types are supported. - Implement a custom `VariableInterceptor` if needed. ## Debug logging Increase logging levels to get more detail: ```yaml logging: level: root: INFO io.camunda.migration.data: DEBUG io.camunda.migration.data.RuntimeMigrator: TRACE file: name: logs/camunda-7-to-8-data-migrator.log ``` ## Cockpit plugin ### Plugin not visible in Cockpit - Ensure the plugin JAR is placed in the correct Camunda 7 plugins directory. - Check Camunda 7 logs for any plugin loading errors. - Restart Camunda 7 after deploying the plugin. ### No skip data displayed - Confirm `save-skip-reason: true` is set in the migrator configuration. - Verify migration has been run with this setting enabled. - Check database connectivity between the plugin and the migrator database. ## Getting help - Use the [Camunda forum](https://forum.camunda.io/c/c7-to-c8/). - Search existing issues: https://github.com/camunda/camunda-bpm-platform/issues. - When creating a new issue, include: logs, config, environment, steps to reproduce. --- ## Variables(Data-migrator) The Data Migrator automatically handles the transformation of Camunda 7 variables to Camunda 8 compatible formats during migration. Variable transformation is supported for the following migration types: - Runtime migration: process instance variables. - History migration: process variables, decision inputs, and decision outputs. ## About variables This section documents which variable types are supported and how they are transformed. For complete details on Camunda 7 variable types, see the [official Camunda 7 documentation](https://docs.camunda.org/manual/latest/user-guide/process-engine/variables/#supported-variable-values). ## Supported types The following table shows how the migrator handles different Camunda 7 variable types: | Camunda 7 Type | Example Value | Migration Behavior | Camunda 8 Result | Interceptor Type | | ------------------------------ | --------------------- | ----------------------- | ----------------- | ---------------------------------- | | String | `"hello world"` | Direct migration | String value | `StringValue`, `PrimitiveValue` | | Boolean | `true`, `false` | Direct migration | Boolean value | `BooleanValue`, `PrimitiveValue` | | Integer | `42`, `1234` | Direct migration | Number value | `IntegerValue`, `PrimitiveValue` | | Long | `123456789L` | Direct migration | Number value | `LongValue`, `PrimitiveValue` | | Double | `3.14159` | Direct migration | Number value | `DoubleValue`, `PrimitiveValue` | | Short | `(short) 1` | Direct migration | Number value | `ShortValue`, `PrimitiveValue` | | Null | `null` | Direct migration | Null value | `NullValueImpl` | | Date | `new Date()` | Converted to ISO format | String (ISO 8601) | `DateValue`, `PrimitiveValue` | | Java Object serialized as JSON | Serialized JSON | Converted to Map | JSON object | `ObjectValue`, `SerializableValue` | | Spin JSON | `SpinJsonNode` | Converted to Map | JSON object | `SpinValue`, `SerializableValue` | | Spin XML | `SpinXmlElement` | Converted to String | String (raw XML) | `SpinValue`, `SerializableValue` | | Java Object serialized as XML | XML serialized object | Converted to String | String (raw XML) | `ObjectValue`, `SerializableValue` | ## Unsupported types When a process instance contains unsupported variable types, the migrator will: - Skip the entire process instance - Log a detailed error message indicating the variable type that caused the skip - Mark the instance as skipped for potential retry after manual intervention The following Camunda 7 variable types are **not supported** and will cause the process instance migration to be skipped: | Camunda 7 Type | Example | Interceptor Type | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------- | | Byte Array | `"hello".getBytes()` | `BytesValue`, `PrimitiveValue` | | File | `FileValue` objects | `FileValue` | | Java Serialized Objects | Java objects serialized as `application/x-java-serialized-object` like `List`, `Set`, `Map`, `float`, `byte`, `char` or custom types | `ObjectValue`, `SerializableValue` | ## Transformation Variable transformations are handled by built-in transformers that run in a specific execution order. Validators run first (Order: 1-3) to reject unsupported types, followed by transformers (Order: 10-20) that convert supported types. ### Date - Input: Java `Date` objects from Camunda 7 - Output: ISO 8601 formatted strings (`yyyy-MM-dd'T'HH:mm:ss.SSSZ`) - Example: `2024-07-25T14:30:45.123+0200` - Timezone: Uses the JVM's default timezone setting ### JSON JSON variables are handled differently depending on their origin and migration mode: Spin JSON Variables and JSON Object Variables (serialized with `application/json`): - Runtime migration: - Deserializes JSON into Map structures for Camunda 8. - Preserves the nested object structure. - Example: `{"name": "John", "age": 30}` becomes a Map object. - History migration: - Preserves JSON values as raw strings. - Example: `{"name": "John", "age": 30}` is stored as-is. **Invalid JSON**: If JSON cannot be parsed during runtime migration, the migrator skips the process instance. ### XML Spin XML Variables and XML Object Variables (serialized with `application/xml`): - Raw XML string content is preserved - No parsing or transformation applied ### Name compatibility The migrator handles variable names that are invalid in FEEL expressions: - Names starting with numbers (e.g., `1stVariable`) - Names with spaces (e.g., `my variable`) - Names with special characters (e.g., `var/name`, `var-name`) - Reserved keywords (e.g., `null`) These variables are migrated as-is, but may require special handling in FEEL expressions using bracket notation. ## Disabling built-in interceptors You can disable any built-in transformer or validator using the `enabled` configuration property: ```yaml camunda: migrator: # Variable interceptor plugins configuration interceptors: # Disable date transformation - class-name: io.camunda.migration.data.impl.interceptor.DateVariableTransformer enabled: false ``` You can find a complete list of built-in interceptors in the [property reference](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/config-properties.md#built-in-interceptors). ## Custom transformation The `VariableInterceptor` interface allows you to define custom logic that executes whenever a variable is accessed or modified during migration. This is useful for auditing, transforming, or validating variable values. Custom interceptors are enabled by default and can be restricted to specific variable types. ### How to implement a `VariableInterceptor` 1. Create a new Maven project with the provided `pom.xml` structure 2. Add a dependency on `camunda-7-to-8-data-migrator-core` (scope: `provided`) 3. Implement the `VariableInterceptor` interface 4. Add setter methods for any configurable properties 5. Package as JAR and deploy to the `configuration/userlib` folder 6. Configure in `configuration/application.yml` ### Creating a custom variable interceptor Here's an example of a custom variable interceptor which is only called for string variables: ```java public class MyVariableInterceptor implements VariableInterceptor { /** * Restrict this interceptor to only handle string variables. */ @Override public Set> getTypes() { return Set.of(StringValue.class); } @Override public void execute(VariableContext context) { // Access the variable name String name = context.getName(); // Get the Camunda 7 value Object c7Value = context.getC7Value(); // Get the Camunda 7 entity VariableInstanceEntity variableInstance = context.getEntity(); // Transform and set the Camunda 8 value context.setC8Value(transformedValue); } } ``` ### Type restrictions Variable interceptors can be restricted to specific variable types using the `getTypes()` method. You can find a complete list of available variable types for restriction as subinterfaces of the `TypedValue` interface in the [JavaDoc](https://docs.camunda.org/javadoc/camunda-bpm-platform/7.24/org/camunda/bpm/engine/variable/value/TypedValue.html#:~:text=All%20Known%20Subinterfaces%3A). ```java @Override public Set> getTypes() { // Handle only specific types return Set.of( BooleanValue.class, // Boolean values IntegerValue.class, // Integer values LongValue.class, // Long values DoubleValue.class, // Double values ShortValue.class, // Short values StringValue.class, // String values PrimitiveValue.class, // String, Integer, Boolean, etc. DateValue.class, // Date variables ObjectValue.class // JSON, XML, Java serialized objects ); } ``` ```java // Or handle all types (default behavior) @Override public Set> getTypes() { return Set.of(); // Empty set = handle all types } ``` ### Entity type filtering In addition to filtering by variable type, interceptors can also filter by the source entity type. This is useful when you want different behavior for history/runtime process variables and decision inputs/outputs. #### Available entity types - `VariableInstanceEntity.class`: runtime process variables - `HistoricVariableInstanceEntity.class`: historic process variables - `HistoricDecisionInputInstanceEntity.class`: decision input variables - `HistoricDecisionOutputInstanceEntity.class`: decision output variables #### Example: process variables only ```java public class ProcessVariableInterceptor implements VariableInterceptor { @Override public Set> getEntityTypes() { // Only handle process variables (runtime and history) return Set.of( VariableInstanceEntity.class, HistoricVariableInstanceEntity.class ); } @Override public void execute(VariableContext context) { // This is only called for process variables // not for decision inputs/outputs } } ``` #### Example: decision variables only ```java public class DecisionVariableInterceptor implements VariableInterceptor { @Override public Set> getEntityTypes() { // Only handle decision inputs and outputs return Set.of( HistoricDecisionInputInstanceEntity.class, HistoricDecisionOutputInstanceEntity.class ); } @Override public void execute(VariableContext context) { // This is only called for decision variables } } ``` #### Default behavior ```java // Empty set = handle all entity types @Override public Set> getEntityTypes() { return Set.of(); } ``` ### Detecting migration context Variable interceptors can detect whether they are running in a runtime or history migration context: ```java @Override public void execute(VariableContext context) { if (context.isRuntime()) { // Handle runtime migration // Example: Deserialize JSON to Map } else if (context.isHistory()) { // Handle history migration // Example: Keep JSON as string } } ``` ### Configuring custom interceptors Configure your custom interceptors in `application.yml`: ```yaml # Variable interceptor plugins configuration # These plugins can be packaged in JARs and dropped in the userlib folder camunda: migrator: interceptors: - class-name: com.example.migrator.AuditVariableInterceptor enabled: true properties: prefix: "CUSTOM_PREFIX_" enableLogging: true ``` ### Deployment 1. Package your custom interceptor as a JAR file 2. Place the JAR in the `configuration/userlib/` folder 3. Configure the interceptor in `configuration/application.yml` 4. Restart the Data Migrator The `enabled` property is supported for all interceptors (both built-in and custom) and defaults to `true`. See [example interceptor](https://github.com/camunda/camunda-7-to-8-migration-tooling/tree/main/data-migrator/examples/variable-interceptor). ### Execution order - Custom interceptors configured in the `application.yml` are executed in their order of appearance from top to bottom - Built-in interceptors run first, followed by custom interceptors - In a Spring Boot environment, you can register interceptors as beans and change their execution order with the `@Order` annotation (lower values run first) ### Error handling When variable transformation fails: #### Runtime migration - The migrator skips the entire process instance. - Logs detailed error messages with the variable name and cause. - Marks the instance for potential retry after you fix the underlying issue. #### History migration - Skips only the affected variables, decision inputs, or outputs. - Continues migrating the parent entity. - Logs skipped items with detailed error information. #### Example commands ```bash # List all skipped variables ./start.sh --history --list-skipped HISTORY_VARIABLE # List all skipped decision instances ./start.sh --history --list-skipped HISTORY_DECISION_INSTANCE # List the Camunda 7 ID and Camunda 8 key for each migrated variable ./start.sh --history --list-migrated HISTORY_VARIABLE # Retry skipped entities after fixing issues ./start.sh --history --retry-skipped ``` ```bash # List all skipped variables start.bat --history --list-skipped HISTORY_VARIABLE # List all skipped decision instances start.bat --history --list-skipped HISTORY_DECISION_INSTANCE # Retry skipped entities after fixing issues start.bat --history --retry-skipped ``` --- ## Diagram Converter With **Diagram Converter**, you'll get an initial understanding of the migration tasks you'll need to perform when moving from Camunda 7 to Camunda 8. It analyzes Camunda 7 diagram files (BPMN or DMN) and generates a list of tasks required for the migration. In a second step, it can also convert these files from the Camunda 7 format to the Camunda 8 format. For example, it updates namespaces and renames XML properties, if needed. All BPMN elements supported by Camunda 8 can be transformed. For the full list see the [BPMN coverage page](../../../components/modeler/bpmn/bpmn-coverage.md). You can use the Diagram Converter in the following ways: - **Web Interface**: A wizard-like UI built with Java (Spring Boot) and React. Available versions: - Java JAR - Docker - Free, hosted SaaS - **CLI**: A command-line interface implemented in Java. The results are available as: - **XLSX**: A Microsoft Excel file, including pre-built pivot tables for data exploration. - **CSV**: A plain-text comma-separated file, compatible with any spreadsheet tool. In the following sections, you'll learn how to: - [Install the Diagram Converter](#install-the-diagram-converter) - [Analyze your diagrams using the web interface](#analyze-your-diagrams-using-the-web-interface) - [Use the CLI](#use-the-cli) - [Convert your diagrams](#convert-your-diagrams) ## Install the Diagram Converter ### Hosted Diagram Converter Use the hosted Diagram Converter at [https://diagram-converter.camunda.io/](https://diagram-converter.camunda.io/). This option requires no local setup and is suitable for quick evaluations or one-off migrations. Your models are not stored on this platform, and all processing happens in-memory. Your data is transmitted securely over HTTPS. :::note The hosted version has a limit on the number of files that can be processed in a single batch request. If you need to convert a larger number of files, use the [local web application](#local-web-application) or the [CLI](#use-the-cli). ::: ### Local web application #### Prerequisites - Java 21 or later #### Steps 1. Download the latest `camunda-7-to-8-diagram-converter-webapp-{version}.jar` from [GitHub Releases](https://github.com/camunda/camunda-7-to-8-migration-tooling/releases). 2. Run the application: ```shell java -jar camunda-7-to-8-diagram-converter-webapp-{version}.jar ``` 3. Access the web application at [http://localhost:8080/](http://localhost:8080/). To run the application on a different port, for example `8090`: ```shell java -jar camunda-7-to-8-diagram-converter-webapp-{version}.jar --server.port=8090 ``` To increase the maximum number of files allowed per batch request (default is 100), configure `server.tomcat.max-part-count`: ```shell java -jar camunda-7-to-8-diagram-converter-webapp-{version}.jar --server.tomcat.max-part-count=200 ``` ### CLI installation #### Prerequisites - Java 21 or later #### Steps 1. Download the latest `camunda-7-to-8-diagram-converter-cli-{version}.jar` from [GitHub Releases](https://github.com/camunda/camunda-7-to-8-migration-tooling/releases). 2. Verify the installation: ```shell java -jar camunda-7-to-8-diagram-converter-cli-{version}.jar --help ``` ## Analyze your diagrams using the web interface Open the Diagram Converter: - For a local installation, open [http://localhost:8080/](http://localhost:8080/). - For the hosted SaaS version, open [https://diagram-converter.camunda.io/](https://diagram-converter.camunda.io/). Upload one or more diagrams: ![Upload your diagrams](../../img/analyzer-screenshot-1.png) Click **Analyze and convert**: ![See results](../../img/analyzer-screenshot-2.png) On this screen you can: - Download the analyzer results as a Microsoft Excel file (XLSX) - Download the analyzer results as a CSV file - Download the converted diagrams (individually or as ZIP) Analysis results contain a list of items where each row represents an action item required for migrating your solution to Camunda 8. These items are grouped by severity: - **INFO**: No action needed. Diagram conversion can successfully map attributes to the Camunda 8 implementation. - **REVIEW**: The conversion will modify some expressions or attributes. Please verify that the intended functionality remains unchanged. - **WARNING**: A Camunda 7 concept can not be directly mapped to a Camunda 8 equivalent. Consider reviewing the Camunda 8 roadmap or exploring possible workarounds. - **TASK**: Manual changes are required to make the diagram work in Camunda 8. This allows you to focus on the most important findings. Tasks can also be grouped by type. For example, changing a `JavaDelegate` to a `JobWorker` might appear 100 times in your codebase, but still represents just one recurring pattern. Pivot tables can help you identify tasks that appear multiple times across different files, providing a comprehensive overview of migration efforts. Next, you'll learn how to use those results. ### Analyze results in Microsoft Excel ![The MS Excel result](../../img/analyzer-result-excel.png) The XLSX file includes three tabs: - **AnalysisSummary**: Pivot tables and charts that summarize typical migration tasks. - **PivotTable**: A large pivot table for dynamic data exploration. - **AnalysisResults**: The raw data from the analysis, which you can copy, import, or process further. You can open the file using Microsoft Excel (desktop or Office 365). ### Analyze results in Google Sheets or LibreOffice You can also open the XLSX file in Google Sheets, LibreOffice, OpenOffice, or similar tools. The raw data will be imported correctly, but pivot tables won't be preserved. Alternatively, download the results as a CSV file, and import them directly into your preferred tool. In this case, either: - Create your own pivot table in the tool. - Copy the contents of the **AnalysisResults** tab into your own spreadsheet. For Google Sheets, consider using this [Google Spreadsheet template](https://docs.google.com/spreadsheets/d/1ZUxGhj1twgTnXadbopw1CvZg_ZvDnB2VXRQDSrKtmcM/edit?gid=6013418#gid=6013418) created by Camunda consultants. ![The Google Sheet](../../img/analyzer-screenshot.png) ## Use the CLI If you prefer the command line, use the CLI for batch processing or automation. The CLI supports two modes: - **local**: Analyze and convert diagrams from your file system - **engine**: Analyze and convert diagrams directly from a running Camunda 7 process engine ### Local mode ```shell java -jar camunda-7-to-8-diagram-converter-cli-{version}.jar local myDiagram.bpmn --xlsx ``` To process all diagrams in a directory (including subdirectories): ```shell java -jar camunda-7-to-8-diagram-converter-cli-{version}.jar local ./my-processes/ ``` ```shell java -jar camunda-7-to-8-diagram-converter-cli-{version}.jar local .\my-processes\ ``` Key options for `local` mode: | Option | Description | | -------------------- | ------------------------------------------------------------- | | `--platform-version` | Semantic version of the target platform (defaults to latest) | | `--csv` | Create a CSV file with analysis results | | `--xlsx` | Create an XLSX file with analysis results | | `--prefix` | Prefix for the generated file name (default: `converted-c8-`) | | `-o, --override` | Override existing files | To see all available options: ```shell java -jar camunda-7-to-8-diagram-converter-cli-{version}.jar local --help ``` ### Engine mode Use engine mode to process diagrams directly from a running Camunda 7 engine via its REST API: ```shell java -jar camunda-7-to-8-diagram-converter-cli-{version}.jar engine http://localhost:8080/engine-rest ``` Key options for `engine` mode: | Option | Description | | ------------------------ | -------------------------------------------------------------- | | `--platform-version` | Semantic version of the target platform (defaults to latest) | | `-u, --username` | Username for Basic authentication | | `-p, --password` | Password for Basic authentication | | `-t, --target-directory` | Directory to save the .bpmn files (default: current directory) | | `--csv` | Create a CSV file with analysis results | | `--xlsx` | Create an XLSX file with analysis results | To see all available options: ```shell java -jar camunda-7-to-8-diagram-converter-cli-{version}.jar engine --help ``` ## Convert your diagrams As mentioned, the Diagram Converter can also convert BPMN and DMN diagrams for use with Camunda 8. This includes: - Updating namespaces - Adjusting XML structure and properties - Transforming expressions Converted files can be downloaded via the web interface or generated via the CLI. ## Extend the conversion logic You can extend the conversion logic by implementing custom visitors and conversions using the Java Service Provider Interface (SPI). This lets you: - Add custom conversion rules for proprietary extensions - Modify how specific BPMN elements are transformed - Add custom analysis messages For implementation details and examples, see the [extension example on GitHub](https://github.com/camunda/camunda-7-to-8-migration-tooling/tree/main/diagram-converter/extension-example). ## Convert expressions JUEL expressions used in Camunda 7 aren't supported in Camunda 8. The Diagram Converter tries to [convert simple expressions, automatically](https://github.com/camunda/camunda-7-to-8-migration-tooling/blob/8a9a37/diagram-converter/core/src/main/java/io/camunda/migration/diagram/converter/expression/ExpressionTransformer.java). For an overview of what’s supported, see the [ExpressionTransformer test case](https://github.com/camunda/camunda-7-to-8-migration-tooling/blob/8a9a37/diagram-converter/core/src/test/java/io/camunda/migration/diagram/converter/ExpressionTransformerTest.java). You may have to manually rewrite more complex expressions. The [FEEL Copilot](https://feel-copilot.camunda.com/) can help with this. You can also customize or extend the transformer logic as needed. --- ## Migration tools Camunda is invested in supporting and easing your migration from Camunda 7 to Camunda 8 with migration tools. All migration tools are available as **ready-to-use builds** from the [GitHub releases page](https://github.com/camunda/camunda-7-to-8-migration-tooling/releases). You can download different versions as needed for your migration. ## Migration tools Camunda provides the following migration tools: | Migration tool | Description | GitHub link | | :---------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- | | **[Diagram Converter](./diagram-converter.md)** | Gain an initial understanding of migration tasks. Available for local installation (Java or Docker) or [hosted as a free SaaS offering](https://diagram-converter.camunda.io/). | [Migration Tooling – Diagram Converter](https://github.com/camunda/camunda-7-to-8-migration-tooling/tree/main/diagram-converter) | | **[Data Migrator](./data-migrator/)** | Copies Camunda 7 runtime instances and history (audit log) to Camunda 8. | [Migration Tooling – Data Migrator](https://github.com/camunda/camunda-7-to-8-migration-tooling/tree/main/data-migrator) | | **[Code Conversion Utilities](./code-conversion.md)** | Mixture of code mapping tables, code conversion patterns, and automatable refactoring recipes. | [Migration Tooling – Code Conversion](https://github.com/camunda/camunda-7-to-8-migration-tooling/tree/main/code-conversion) | ## Examples | Example | Description | GitHub link | | :--------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | | **[Simple end-to-end example](https://github.com/camunda-community-hub/camunda-7-to-8-migration-example)** | Shows all tools in action for a simple Spring Boot Java solution. | [Camunda 7 to 8 migration example](https://github.com/camunda-community-hub/camunda-7-to-8-migration-example) | --- ## Update guide In this guide, you'll learn about the breaking changes and migration steps to consider when you upgrade Migration Tooling. :::note Migration Tooling follows [semantic versioning](https://semver.org/). ::: ## Prerequisites Before you update: 1. Check the [version compatibility matrix](version-compatibility.md) to confirm your Migration Tooling version is compatible with your Camunda 7 and Camunda 8 versions. 2. Apply database schema updates for the new version, if required. Check the release notes for details. 3. Review the [Migration Tooling release notes](https://github.com/camunda/camunda-7-to-8-migration-tooling/releases) for your target version. ## Breaking changes by version ### Version 0.2.x to 0.3.0 **Release date:** 14th of April 2026 \ **Camunda 8 compatibility:** 8.9.x #### Data Migrator: Database schema update Run the Data Migrator with `camunda.migrator.auto-ddl: true` configuration to update the schema. #### Data Migrator: Entity Interceptor API changes The `EntityInterceptor` interface now provides compile-time type safety using Java generics, eliminating the need for manual casting. ##### What changed ```java // 0.2.x public class ProcessInstanceEnricher implements EntityInterceptor { @Override public void execute(EntityConversionContext context) { // Manual casting required HistoricProcessInstance entity = (HistoricProcessInstance) context.getC7Entity(); ProcessInstanceDbModel.ProcessInstanceDbModelBuilder builder = (ProcessInstanceDbModel.ProcessInstanceDbModelBuilder) context.getC8DbModelBuilder(); // Custom logic builder.processDefinitionId(entity.getProcessDefinitionKey()); } } // 0.3.x public class ProcessInstanceEnricher implements EntityInterceptor { @Override public void execute(HistoricProcessInstance entity, ProcessInstanceDbModel.ProcessInstanceDbModelBuilder builder) { // No casting needed! Type-safe access builder.processDefinitionId(entity.getProcessDefinitionKey()); } } ``` #### Data Migrator: Automatic Camunda 8 datasource selection When the Camunda 8 datasource is configured, the migrator now creates and uses the migration schema on the Camunda 8 database automatically. Manual selection is no longer required. ##### What changed - Removed: `camunda.migrator.data-source` - New behavior: The migration schema is created on the Camunda 8 database when `camunda.migrator.c8.data-source` is configured ##### Impact on existing migrations :::warning Risk of duplicate migrations If you ran a migration in 0.2.x without configuring `camunda.migrator.data-source: C8`, upgrading to 0.3.0 creates a new migration schema on the Camunda 8 database. This resets migration tracking and can result in duplicate data migration. ::: ##### Migration steps 1. **If your Camunda 7 and Camunda 8 datasources point to the same database in 0.2.x:** - Remove `camunda.migrator.data-source` from your configuration. - No further action is required. The migration schema is already accessible from both datasources. 2. **If you explicitly configured `camunda.migrator.data-source: C8` in 0.2.x:** - Remove `camunda.migrator.data-source` from your configuration. - No further action is required. The migration schema is already on the Camunda 8 database. 3. **If you used the default configuration in 0.2.x (migration schema on the Camunda 7 database):** - Option A (recommended): Copy the migration schema from the Camunda 7 database to the Camunda 8 database before upgrading. - Option B: Start over without existing migration tracking. - This can result in duplicate migrations. - Before upgrading, you can use `--drop-schema` to remove the migration schema from the Camunda 7 database (for example, to reclaim disk space). 4. **If you have not started a migration yet:** - Configure the Camunda 8 datasource: ```yaml camunda.migrator.c8.data-source: jdbc-url: jdbc:postgresql://localhost:5432/camunda8 username: camunda password: camunda ``` :::note See [history atomicity](data-migrator/history.md#atomicity) for more details. ::: #### Data Migrator: New `StringVariableTransformer` - **New transformer**: `StringVariableTransformer` now handles string variables specifically - **Modified transformer**: `PrimitiveVariableTransformer` no longer handles string variables, only: - `BooleanValue` - `IntegerValue` - `LongValue` - `DoubleValue` - `ShortValue` **If you need to disable the new string transformer:** ```yaml camunda: migrator: interceptors: - class-name: io.camunda.migration.data.impl.interceptor.StringVariableTransformer enabled: false ``` :::note Further reading See the [Variables documentation](data-migrator/variables.md#supported-types) for the complete list of variable types and their interceptors. ::: ### Version 0.1.x to 0.2.0 **Release date:** 17/12/2025 \ **Camunda 8 compatibility:** 8.8.x #### Data Migrator: Package name changes The package name of the Data Migrator has changed from `io.camunda.migrator` to `io.camunda.migration.data`. Make sure you update your custom interceptors and `application.yml` configuration. **Migration steps** Change your import statements: ```java // 0.1.x // 0.2.0 ``` Change your `application.yml` configuration for built-in interceptors: ```yaml # 0.1.x camunda.migrator.interceptors: - class-name: io.camunda.migrator.impl.interceptor.DateVariableTransformer enabled: false # 0.2.0 camunda.migrator.interceptors: - class-name: io.camunda.migration.data.impl.interceptor.DateVariableTransformer enabled: false ``` #### Data Migrator: Variable interceptor API changes This release updates the variable interceptor API to support history migration and improve context awareness. `VariableInvocation` was renamed to `VariableContext`: ```java // 0.1.x public void execute(VariableInvocation invocation) { VariableInstanceEntity variable = invocation.getC7Variable(); String processInstanceId = variable.getProcessInstanceId(); invocation.setVariableValue(transformedValue); } // 0.2.0 public void execute(VariableContext context) { VariableInstanceEntity variable = (VariableInstanceEntity) context.getEntity(); String processInstanceId = variable.getProcessInstanceId(); String name = context.getName(); Object value = context.getC7Value(); context.setC8Value(transformedValue); } ``` `MigrationVariableDto` class was removed: - Use `VariableContext` methods directly - `getName()` and `getC8Value()`/`setC8Value()` replace DTO access **Migration steps** 1. Update method signatures from `VariableInvocation` to `VariableContext`. 2. Replace `invocation.getC7Variable()` with `context.getC7Value()` or `context.getC7TypedValue()`. 3. Replace `invocation.getMigrationVariable().getName()` with `context.getName()`. 4. Replace `invocation.setVariableValue()` with `context.setC8Value()`. 5. (Optional) Add entity type filtering, using `getEntityTypes()`. 6. (Optional) Add runtime/history context detection, using `context.isRuntime()` or `context.isHistory()`. :::note Further reading See the [Variables documentation](data-migrator/variables.md) for more information. ::: #### Code Conversion: Repository and package changes Code Conversion is now officially supported by Camunda. The code repository and package names have changed. - Repository location - **Old**: `camunda-community-hub/camunda-7-to-8-code-conversion` - **New**: `camunda/camunda-7-to-8-migration-tooling` - Package names - **Old**: `org.camunda.migration.rewrite.*` - **New**: `io.camunda.migration.code.*` - Maven module names - **Old**: `org.camunda.community:camunda-7-to-8-rewrite-recipes` - **New**: `io.camunda:camunda-7-to-8-code-conversion-recipes` - Documentation location - **Old**: `https://camunda-community-hub.github.io/camunda-7-to-8-code-conversion/` - **New**: `https://camunda.github.io/camunda-7-to-8-migration-tooling/` All artifacts now use the `io.camunda` groupId instead of `org.camunda.community`. **Migration steps** If you're using OpenRewrite recipes in your project, update your dependencies: ```xml org.camunda.community camunda-7-to-8-rewrite-recipes io.camunda camunda-7-to-8-code-conversion-recipes 0.2.0 ``` Change all imports: ```java // OLD // NEW ``` #### Diagram Converter: Repository and module changes Diagram Converter is now officially supported by Camunda. The code repository location and module names have changed. - Repository location - **Old**: `camunda-community-hub/camunda-7-to-8-migration-analyzer` - **New**: `camunda/camunda-7-to-8-migration-tooling` - Maven module names - **Core Module**: `io.camunda:camunda-7-to-8-diagram-converter-core` - **Web Application**: `io.camunda:camunda-7-to-8-diagram-converter-webapp` - **CLI**: `io.camunda:camunda-7-to-8-diagram-converter-cli` All artifacts now use the `io.camunda` groupId. **Migration steps** If you're embedding the Diagram Converter as a library, update your dependencies: ```xml org.camunda.community.migration camunda-7-to-8-migration-analyzer-core io.camunda camunda-7-to-8-diagram-converter-core 0.2.0 ``` For the web application or CLI, download the [latest releases](https://github.com/camunda/camunda-7-to-8-migration-tooling/releases). --- ## Version compatibility Each version of the Migration Tooling is bound to a specific version of Camunda 7 and 8. Camunda only recommends and supports these combinations: | Migration Tooling Version | Camunda 7 Version | Camunda 8 Version | Support Status | | :------------------------ | :---------------- | :---------------- | :----------------------- | | `0.1.x` \* | `7.24.x` | `8.8.x` | 🔴 **End of Life (EOL)** | | `0.2.x` | `7.24.x` | `8.8.x` | ✅ **Supported** | | `0.3.x` | `7.24.x` | `8.9.x` | ✅ **Supported** | \* Data Migrator only. Code Conversion and Diagram Converter were added to the Migration Tooling in version 0.2.0. --- ## 8.10 Release announcements | Minor release date | Scheduled end of maintenance | Release notes | Upgrade guides | | ------------------ | ---------------------------- | --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | | 13 October 2026 | 11 April 2028 | [8.10 release notes](/reference/announcements-release-notes/8100/8100-release-notes.md) | [8.10 upgrade guides](/reference/announcements-release-notes/8100/whats-new-in-810.md#upgrade-guides) | :::info 8.10 resources - See [release notes](/reference/announcements-release-notes/8100/8100-release-notes.md) to learn more about new features and enhancements. - Refer to the [quality board](https://github.com/orgs/camunda/projects/187/views/23) for an overview of known bugs by component and severity. ::: Change #### PostgreSQL 14 no longer supported Camunda 8.10 drops support for PostgreSQL 14. Supported versions are now 15, 16, 17, and 18. - PostgreSQL 14 reached the end of its standard support window. - Upgrade your PostgreSQL instance to a supported version before moving to Camunda 8.10. [RDBMS version support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) Change #### Amazon Aurora PostgreSQL 14 no longer supported Camunda 8.10 drops support for Amazon Aurora PostgreSQL 14. Supported versions are now 15, 16, and 17. - Aurora PostgreSQL 14 has reached the end of standard support on AWS. - Migrate your Aurora cluster to a supported version before moving to Camunda 8.10. [RDBMS version support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) Change #### Microsoft SQL Server 2019 no longer supported Camunda 8.10 drops support for Microsoft SQL Server 2019. Supported versions are now 2022 and 2025. - SQL Server 2019 has reached the end of mainstream support from Microsoft. - Upgrade your SQL Server instance to a supported version before moving to Camunda 8.10. [RDBMS version support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) Change #### Oracle 23ai rebranded as Oracle 26ai Oracle has rebranded Oracle Database 23ai as Oracle AI Database 26ai, effective with the October 2025 Release Update (RU 23.26). The internal version continues to use the 23.x code line; the transition requires no database upgrade or application recertification. Camunda 8.10's supported Oracle versions are 19c and 26ai. [RDBMS version support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) Change #### H2 2.3 no longer supported Camunda 8.10 drops support for H2 2.3. Only H2 2.4 is now supported. - The bundled H2 driver in Camunda images is on the 2.4 line. - H2 remains supported for development, testing, and evaluation only. Production use is not recommended. [RDBMS version support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) ## Agentic orchestration Breaking change #### AI Agent connector: Conversation storage SPI redesign [Camunda 8.10.0-alpha1](/reference/announcements-release-notes/8100/8100-release-notes.md#8100-alpha1) redesigns the conversation storage SPI used by [custom AI Agent storage backends](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-customization.md#custom-conversation-storage). Built-in stores (in-process, Camunda Document, AWS AgentCore) are migrated transparently; only custom `ConversationStore` implementations are affected. **Action:** If you maintain a custom `ConversationStore`, migrate to the new SPI. See the updated [AI Agent connector customization guide](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-customization.md#custom-conversation-storage) for the new shape, and the [migration guide on GitHub](https://github.com/camunda/connectors/blob/main/connectors/agentic-ai/docs/breaking-changes.md) for a step-by-step walkthrough. ## APIs & tools :::note Changes for 8.10 will be added here as the 8.10 documentation is updated. ::: Breaking change #### Removal of legacy APIs, Tasklist V1-dependent features, and Zeebe Process Test Starting with Camunda 8.10.0-alpha2, Camunda removes the legacy component APIs and related features that were deprecated in 8.8. The following items are removed: - The [Operate API](/apis-tools/operate-api/overview.md) - The [Tasklist API](/apis-tools/tasklist-api-rest/tasklist-api-rest-overview.md) and Tasklist V1 mode - Tasklist V1-dependent features such as [user task access restrictions](/components/tasklist/user-task-access-restrictions.md) and [public start forms](/components/hub/workspace/modeler/modeling/advanced-modeling/publish-public-processes.md) - [Zeebe Process Test](/apis-tools/testing/zeebe-process-test.md) **Action:** Migrate integrations and testing workflows to the current replacements: - Use the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) instead of the removed Operate API and Tasklist API. - Use [user task authorization](/components/tasklist/user-task-authorization.md) and [authorization-based access control](/components/concepts/access-control/authorizations.md) instead of user task access restrictions. - Use authenticated Tasklist starts or build your own application with [Camunda Forms](/components/modeler/forms/utilizing-forms.md) and the Orchestration Cluster REST API instead of public start forms. - Use [Camunda Process Test](/apis-tools/testing/getting-started.md) instead of Zeebe Process Test. [Migrate to the Orchestration Cluster REST API](/apis-tools/migration-manuals/migrate-to-camunda-api.md) [Migrate from Zeebe Process Test](/apis-tools/migration-manuals/migrate-to-camunda-process-test.md) [Migrate to Camunda user tasks](/apis-tools/migration-manuals/migrate-to-camunda-user-tasks.md) Breaking change #### `GET /decision-instances/{decisionEvaluationInstanceKey}` now validates the key format The [Get decision instance](/apis-tools/orchestration-cluster-api-rest/specifications/get-decision-instance.api.mdx) endpoint previously returned `404 Not Found` when the `decisionEvaluationInstanceKey` path parameter contained invalid characters that did not match the required pattern `^[0-9]+-[0-9]+$`. The endpoint now correctly returns `400 Bad Request` in this case, while `404 Not Found` is reserved for well-formed keys that do not exist. **Action:** Update any client code or error handling that relied on receiving `404 Not Found` for malformed keys to also handle `400 Bad Request`. Breaking change #### `JobIntent.COMPLETED` follow-up event no longer carries variables by default Starting with 8.10, the `JobIntent.COMPLETED` follow-up event is emitted without variables by default. This prevents `ExceededBatchRecordSizeException` when a job completes with very large variables. Without this setting, the `JobIntent.COMPLETE` command could be rejected and the job could time out. **Action:** If your exporter or integration reads completion variables from the `JobIntent.COMPLETED` event, read them instead from the `JobIntent.COMPLETE` command record or the follow-up `ProcessEvent.TRIGGERING` event, both of which always carry the variables. To restore the pre-8.10 behavior where `JobIntent.COMPLETED` events carry variables, set `camunda.processing.engine.job.include-variables-in-job-completed-event` to `true`. ## Connectors :::note Changes for 8.10 will be added here as the 8.10 documentation is updated. ::: ## Data :::note Changes for 8.10 will be added here as the 8.10 documentation is updated. ::: ## Deployment Breaking change #### Helm v4 required for Camunda 8.10 Camunda 8.10 (chart 15.x) supports the Helm CLI v4 only. Camunda 8.9 (chart 14.x) is the last minor that supports the Helm v3 CLI. The Helm chart adds a CLI version check and fails fast if Helm v3 is used to install or upgrade chart 15.x. **Action:** Install the Helm v4 CLI before you upgrade to 8.10. No release-state migration is required; Helm is client-side only and both CLIs read and write the same release-storage format. See [Move from the Helm v3 CLI to v4](/self-managed/deployment/helm/operational-tasks/moving-helm-v3-to-v4.md) and [Helm 4](/self-managed/deployment/helm/operational-tasks/helm-v4.md). ## Identity :::note Changes for 8.10 will be added here as the 8.10 documentation is updated. ::: ## Modeler :::note Changes for 8.10 will be added here as the 8.10 documentation is updated. ::: --- ## 8.10 Release notes | Minor release date | Scheduled end of maintenance | Changelog(s) | Upgrade guides | | :----------------- | :--------------------------- | :--------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------- | | 13 October 2026 | 11 April 2028 | [Patch Releases and Changelogs](#technical-changelogs-for-all-810x-releases) | [8.10 upgrade guides](/reference/announcements-release-notes/8100/whats-new-in-810.md#upgrade-guides) | :::info 8.10 resources - See [What's new in Camunda 8.10](/reference/announcements-release-notes/8100/whats-new-in-810.md) for important changes to consider when planning your upgrade from Camunda 8.8. - See [release announcements](/reference/announcements-release-notes/8100/8100-announcements.md) to learn more about supported environment changes, breaking changes, and deprecations. - Refer to the [quality board](https://github.com/orgs/camunda/projects/187/views/23) for an overview of known bugs by component and severity. ::: ### Technical Changelogs for all 8.10.x releases
Overview of all patch releases and their Changelogs in GitHub
## 8.10.0-alpha1 | Release date | Changelog(s) | Blog | | :----------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--- | | 13 May 2026 | [ Camunda 8 core ](https://github.com/camunda/camunda/releases/tag/8.10.0-alpha1)[ Connectors ](https://github.com/camunda/connectors/releases/tag/8.10.0-alpha1) | - | ### Agentic orchestration #### AI Agent connector: Conversation storage SPI redesign AI agentsAgentic orchestrationConnectors The conversation storage SPI used by [custom AI Agent storage backends](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-customization.md#custom-conversation-storage) has been redesigned. Built-in stores are migrated transparently; custom `ConversationStore` implementations must be updated. See the [release announcement](/reference/announcements-release-notes/8100/8100-announcements.md#ai-agent-connector-conversation-storage-spi-redesign) for more details. #### Camunda-provided LLM for SaaS AI agentsAgentic orchestrationSaaS You can now run any AI Agent on Camunda 8 SaaS in minutes using the [Camunda-provided LLM](/components/agentic-orchestration/camunda-provided-llm.md), without wiring your own LLM credentials. Whether you start from a Camunda-provided agentic blueprint or build your own agent from scratch, the required credentials are populated automatically as cluster secrets, so there is little to no extra setup needed to get started. The included budget is sufficient for hundreds or thousands of agent runs even on a trial account, depending on the model used. For enterprise organizations, AI features must be enabled first; after that, Camunda-provided LLM is enabled automatically. This dramatically reduces time-to-first-running-agent by removing the need for external LLM infrastructure or credential setup on day one. #### MCP start event element template Self-ManagedSaaSWeb ModelerDesktop Modeler The **MCP start event** element template is now available in Web Modeler and Desktop Modeler. Apply it to a BPMN message start event to configure the process as an MCP tool with name, purpose, inputs, and usage guidance for LLMs. See [MCP start event](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-start-event.md) for the full property reference. #### Processes MCP Server Self-ManagedSaaSOrchestration Cluster Camunda 8.10 introduces the [Processes MCP Server](/apis-tools/processes-mcp/processes-mcp-overview.md), which enables AI agents to discover and call your deployed BPMN processes as [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) tools. Deploy a process with an MCP start event and it is automatically registered as a callable tool. MCP clients connect to the `/mcp/processes` endpoint and can invoke any registered process, with the Orchestration Cluster starting a new process instance and returning the process instance key immediately. The server also exposes [static tools](/apis-tools/processes-mcp/processes-mcp-static-tools.md) for inspecting running process instances, so agents can check variables, state, and incidents without switching servers. #### Standalone evaluation assertions for judge and semantic similarity AI agentsAgentic orchestration Camunda Process Test now exposes **judge-based evaluation** and **semantic similarity evaluation** as standalone AssertJ assertions for arbitrary string values, without requiring process-variable assertions. Semantic similarity checks support configurable embedding models and thresholds, and both assertion types reuse the existing CamundaAssert configuration with optional local overrides. ### Camunda Hub #### Usage & billing metrics for 2025 enterprise license model SaaSCamunda Hub Camunda Hub and Accounts now support the 2025 enterprise license model. - A new `licensing_model` attribute on `OrganizationMetaData` identifies if an enterprise organization is using the **2025** or **legacy** license model. If unset, it is treated as **legacy**. - If you are an organization with `licensing_model = 2025`, your Usage and Billing views only show **Process Instance (PI)** metrics. **Decision Instance (DI)** and **Unique Task User (TU)** information is no longer shown. Legacy organizations continue to see the existing metric set. - For enterprise (`salesplantype = enterprise`) organizations, the licensing model is shown in the organization details. Admins can edit this by selecting either **legacy** or **2025** via a modal action. - The enterprise onboarding wizard now includes a license selection step (defaults to **2025**). The `ExternalOnboardingRouter` accepts an optional licensing model parameter (defaulting to **2025** if not provided). #### Cluster version selection for SaaS orchestration clusters SaaSCamunda HubOrchestration Cluster You can now create new SaaS Orchestration Clusters on specific supported Camunda 8 minor and patch versions, including: - The latest recommended versions (latest patch of each active minor) - Other still-supported versions that you already run on existing clusters in the same organization. ### Intelligent document processing (IDP) #### Support for ABBYY as an IDP Provider Self-ManagedSaaSIDP Camunda IDP now supports [ABBYY](https://www.abbyy.com/) as a document extraction provider. ### Modeler #### Support for configurable headers for execution listeners Self-ManagedSaaSWeb ModelerDesktop Modeler Execution listeners now support configurable headers, aligned with service task job headers. - In BPMN, execution listeners can define ``. The headers are passed to the listener’s job worker alongside any base-element headers, with listener headers overriding on key conflicts. - In Modeler, you can configure execution listener headers visually (name/value pairs) without editing BPMN XML. - Listener workers can consume these headers as metadata and configuration parameters using the same patterns as service task job workers. ### Integrations #### Microsoft Teams routing and permission-aware task actions Self-ManagedSaaSIntegrations Camunda for Microsoft Teams now supports routing incident and task collaboration to private channels, shared channels, and group chats. Notifications and task actions in Teams now align with Camunda assignment and access rules, ensuring that only eligible users are notified and allowed to act. ### Operate #### JSON display in Operate SaaSOperate Camunda 8.10 introduces an update to the JSON display functionality in Operate (SaaS). You can now: - Open JSON variables in a dedicated JSON viewer directly from the variables panel, without entering editing mode. - View JSON values with consistent, easier to understand formatting. - Copy full JSON variable values to the clipboard. - Use the improved in-line variables display. This change helps navigate more complex data during operations and troubleshooting. ### Orchestration Cluster #### Cancel execution listener Self-ManagedSaaSOrchestration Cluster Execution listeners now support a `cancel` event type on the process element. Cancel listeners run when a process instance is terminated — useful for cleanup, audit logging, or notifying external systems. For details, see [`cancel` listeners](/components/concepts/execution-listeners.md#cancel-listeners). ### Helm chart deployment Helm chartsSelf-Managed #### Helm v4 required Camunda 8.10 (chart 15.x) supports the Helm CLI v4 only. Earlier Camunda versions are the last to support the Helm v3 CLI. Switching CLIs does not require a release-state migration; Helm is client-side only. Before you run `helm upgrade` to 8.10, install the Helm v4 CLI. [Move from the Helm v3 CLI to v4](/self-managed/deployment/helm/operational-tasks/moving-helm-v3-to-v4.md) [Helm 4](/self-managed/deployment/helm/operational-tasks/helm-v4.md) #### Host network support for orchestration cluster pods The 8.10 Helm chart adds `orchestration.hostNetwork` (default: `false`), which lets orchestration cluster pods share the host node's network namespace. This is useful in bare-metal or restricted network environments where pods must be reachable directly via the node IP rather than a cluster overlay network. When `orchestration.hostNetwork` is set to `true` and `orchestration.dnsPolicy` is not set, the chart automatically uses `dnsPolicy: ClusterFirstWithHostNet` to preserve in-cluster DNS resolution. You can override this by setting `orchestration.dnsPolicy` explicitly. ```yaml orchestration: hostNetwork: true ``` For details, see [configure pod networking](/self-managed/deployment/helm/configure/pod-networking.md). --- ## What's new in Camunda 8.10 ## Why upgrade to Camunda 8.10? Upgrading to Camunda 8.10 delivers significant benefits and keeps your installation aligned and ready for future releases. ## Summary of important changes Important changes in Camunda 8.10 are summarized as follows: :::note Additional changes for 8.10 will be added here as the 8.10 documentation is updated. ::: :::info learn more and upgrade - See [release announcements](/reference/announcements-release-notes/8100/8100-announcements.md) and [release notes](/reference/announcements-release-notes/8100/8100-release-notes.md) for a full summary of what's included in Camunda 8.10, including all breaking changes and deprecations, and supported environment changes. - For removed legacy APIs, Tasklist V1-dependent features, and Zeebe Process Test, see the [8.10 release announcement](/reference/announcements-release-notes/8100/8100-announcements.md#removal-of-legacy-apis-tasklist-v1-dependent-features-and-zeebe-process-test). - Ready to upgrade? See the [upgrade guides](#upgrade-guides) to learn more about upgrading from Camunda 8.9 to 8.10. ::: ## Helm chart deployment Important changes to Helm chart deployment in 8.10 are as follows: ### Helm v4 required :::warning Breaking change Camunda 8.10 (chart 15.x) supports the Helm CLI v4 only. Earlier Camunda versions are the last to support the Helm v3 CLI. ::: Switching CLIs does not require a release-state migration; Helm is client-side only. Before you run `helm upgrade` to 8.10, install the Helm v4 CLI. [Move from the Helm v3 CLI to v4](/self-managed/deployment/helm/operational-tasks/moving-helm-v3-to-v4.md) [Helm 4](/self-managed/deployment/helm/operational-tasks/helm-v4.md) ### Host network support for orchestration cluster pods The 8.10 Helm chart adds `orchestration.hostNetwork` (default: `false`), which lets orchestration cluster pods share the host node's network namespace. This is useful in bare-metal or restricted network environments where pods must be reachable directly via the node IP rather than a cluster overlay network. [Configure pod networking](/self-managed/deployment/helm/configure/pod-networking.md) ## Upgrade guides {#upgrade-guides} The following guides offer detailed information on how to upgrade to Camunda 8.10. **Guide** **Description** **Who is this guide for?** [Self-Managed upgrade guide](/self-managed/upgrade/index.md) Evaluate your infrastructure, understand operational changes, and choose the best update strategy for your environment. Operations and platform administrators of Self-Managed installations. [APIs & tools upgrade guide](/) Plan and execute an upgrade from Camunda 8.9 to 8.10, focusing on API and tools transitions. Application developers maintaining Camunda-based solutions in Self-Managed Kubernetes or VM environments.Developers using Camunda APIs and tools. --- ## Release announcements Supported environment changes and breaking changes or deprecations for the Camunda 8.7 release are summarized below. This release focuses primarily on consolidation and deprecation work to simplify APIs, align clients and SDKs, and prepare for upcoming features in 8.8 and later releases. While there are fewer net-new features in this release, these changes reduce long-term maintenance and improve consistency across Camunda components. | Scheduled release date | Scheduled end of maintenance | Release notes | Blog | | :--------------------- | :--------------------------- | :----------------------------------------------------------------------------------- | :------------------------------------------------------------------------------ | | 8 April 2025 | 13 October 2026 | [8.7 release notes](/reference/announcements-release-notes/870/870-release-notes.md) | [Announcing Camunda 8.7](https://camunda.com/blog/2025/04/camunda-8-7-release/) | :::tip Release notes and quality board - See [release notes](/reference/announcements-release-notes/870/870-release-notes.md) to learn more about new features and enhancements. - Refer to the [quality board](https://github.com/orgs/camunda/projects/187/views/16) for an overview of known bugs by component and severity. ::: ## Changes in supported environments ### Identity Keycloak now requires v25 or v26 Self-Managed Identity 8.7 now requires Keycloak v25 or v26, and Keycloak versions must be updated to match. This update also includes changes to the Camunda Helm chart. For more information on configuration changes, see the Self-Managed [update guide](/versioned_docs/version-8.7/self-managed/operational-guides/update-guide/860-to-870.md#identity). ### Spring Zeebe SDK now requires Spring Boot 3.4.x SaaSSelf-Managed The Spring Zeebe SDK 8.7 now requires Spring Boot 3.4.x. For more information on compatibility, see the Spring Zeebe SDK [version compatibility matrix](/apis-tools/camunda-spring-boot-starter/getting-started.md#version-compatibility). ### Desktop Modeler no longer supports macOS 12 Following the end-of-life of macOS 12, support for Desktop Modeler on macOS 12 has been removed. ## Key changes Collectively, these changes consolidate overlapping functionality, align configuration and client behavior across components, and establish clearer upgrade paths for upcoming releases. ### Deprecation of Self-Managed AWS Marketplace offering As of **October 2025**, the **Self-Managed AWS Marketplace** offering will be **deprecated** and no longer publicly available. Existing customers may continue to use the product until their contracts expire. For future use, refer to our [new AWS Marketplace listing](https://aws.amazon.com/marketplace/pp/prodview-6y664fcnydiqg?sr=0-1&ref_=beagle&applicationId=AWSMPContessa) for more information. ### Deploy diagram change Self-Managed {#web-modeler-deploy-diagram-change} With this version, we ship a breaking change to how Web Modeler **Deploy diagram** modals work. Clusters must now be proactively [configured](/self-managed/components/hub/configuration/modeler-configuration.md#clusters) to be able to deploy from Web Modeler. - In 8.6, you could still configure cluster details on the **Deploy diagram** modal when deploying. - In 8.7, you can no longer configure cluster details on the **Deploy diagram** modal. You must [configure the cluster](/self-managed/components/hub/configuration/modeler-configuration.md#clusters) to be able to deploy from this modal. - Note that you must also be assigned the `Zeebe` [Identity role](/self-managed/components/management-identity/application-user-group-role-management/manage-roles.md) to be able to deploy (if `BEARER_TOKEN` is used as authentication). ### Deprecated: Web Modeler cluster authentication `OAUTH` and `CLIENT_CREDENTIALS` Self-Managed The following authentication methods for a [configured cluster in Web Modeler](/self-managed/components/hub/configuration/modeler-configuration.md#clusters) are now being deprecated and will no longer be supported in version 8.8: - `OAUTH`: This method was replaced by `BEARER_TOKEN`. - `CLIENT_CREDENTIALS`: This method was introduced as a temporary solution to support deployments from Web Modeler when using [Microsoft Entra ID or a generic OIDC provider](/self-managed/deployment/helm/configure/authentication-and-authorization/external-oidc-provider.md). It is marked for removal in 8.8 as the `BEARER_TOKEN` authentication will be supported for Entra ID and generic providers as well. ### Breaking changes in Camunda Process Test In version 8.6, the element assertions use names to identify the BPMN elements. Starting with version 8.7, the [element assertions](/apis-tools/testing/assertions.md#with-bpmn-element-id) use BPMN element IDs instead of names. This change eases the migration of previous testing frameworks and aligns with other APIs. If you prefer to stay with element names, you can use the new [element selector](/apis-tools/testing/assertions.md#with-element-selector) `io.camunda.process.test.api.assertions.ElementSelectors#byName`. To migrate all your test cases at once, change the [default element selector](/apis-tools/testing/assertions.md#element-selector) in the configuration. ### Deprecated: OpenAPI entities with `integer (int64)` key attributes OpenAPI entities containing keys of type `integer (int64)` are now being deprecated. This is part of a transition where API entity keys change from type `integer (int64)` to `string`. See the [overview about API Key Attributes][camunda8-api-overview] for more details. [camunda8-api-overview]: /versioned_docs/version-8.7/apis-tools/camunda-api-rest/camunda-api-rest-overview.md#api-key-attributes ### Zeebe Java client Starting with 8.8, the Zeebe Java client will become the new Camunda Java client. This transition brings a new Java client structure designed to enhance the user experience and introduce new features while maintaining compatibility with existing codebases. The primary goal of those changes is to enable users to interact with Camunda clusters with one consolidated client rather than multiple. The `CamundaClient` will replace the `ZeebeClient`, offering the same functionality and adding new capabilities. If you need to continue using the old `ZeebeClient`, you can use the new version 8.8 `CamundaClient` artifact without issues as it still contains the related `ZeebeClient` classes. Those classes are marked as deprecated, so you can easily spot code you need to adjust to the `CamundaClient`. The old `zeebe-client-java` artifact will be relocation-only, so your build system is redirected to the new `camunda-client-java` artifact. We will discontinue the old artifact in version 8.10 and recommend using the new one. :::note The Zeebe Java client will not be developed further and will only receive bug fixes for as long as version 8.9 is officially supported. This client is scheduled for removal in version 8.10. ::: ### Spring Zeebe SDK Starting with 8.8, the Spring Zeebe SDK will become the new Camunda Spring Boot Starter. The SDK will rely on the new Camunda Java client, designed to enhance the user experience and introduce new features while maintaining compatibility with existing codebases. :::note The Spring Zeebe SDK will not be developed further and will only receive bug fixes for as long as version 8.9 is officially supported. This SDK is scheduled for removal in version 8.10. ::: ### Camunda 8 Self-Managed #### Helm ##### Separated Ingress deprecation The separated Ingress Helm configuration for Camunda 8 Self-Managed has been deprecated in 8.6, and will be removed from the Helm chart in 8.8. Only the combined Ingress configuration is officially supported. See the [Ingress guide](/self-managed/deployment/helm/configure/ingress/ingress-setup.md) for more information on configuring a combined Ingress setup. #### Helm chart: Custom users and clients for Identity You can now configure custom users and OAuth2 clients for Management Identity during Helm installation. See [adding users and clients](/self-managed/deployment/helm/configure/authentication-and-authorization/custom-users-and-clients.md) for details on setting up custom users and clients on Management Identity during initial Helm install. :::caution If your deployment currently defines custom users or clients using environment variables (for example, `KEYCLOAK_CLIENTS_2_PERMISSIONS_0_RESOURCE_SERVER_ID`), additional upgrade steps are required. Remove any environment variables that reference users or clients and migrate to the configuration method described in the guide linked above. ::: ##### ExtraVolumeClaimTemplates You can now add custom `extraVolumeClaimTemplates` to the Zeebe/Core StatefulSet by supplying an array of templates in your Helm values file. This allows you to attach additional persistent volumes to each Zeebe/Core pod for use cases such as custom storage or log directories. **Important:** Kubernetes does not allow you to change the `volumeClaimTemplates` of an existing StatefulSet. If you add, remove, or modify `extraVolumeClaimTemplates` after initial deployment, you must delete and recreate the StatefulSet (which will also delete the pods) for the changes to take effect. This may require additional planning and data migration steps to avoid data loss. ##### Common labels for Camunda resources A new `commonLabels` value is now available and integrates with `camundaPlatform.labels`. This allows you to define mutable labels that are automatically applied to all Camunda resources. By setting `commonLabels`, you can ensure consistent labeling across deployments, making it easier to manage, organize, and identify resources within your Camunda environment. ##### Configure Web Modeler replicas The number of replicas for the Web Modeler REST API and web app deployments can be set with new configuration properties: `webModeler.restapi.replicas` and `webModeler.webapp.replicas`, respectively. ##### External database for Web Modeler REST API The configuration for the external database used by the Web Modeler REST API has been updated to align with the Identity component's database configuration. A new value, `webModeler.restapi.externalDatabase`, is now available and mirrors the structure of `identity.externalDatabase`. To ensure backward compatibility, the existing `webModeler.restapi.externalDatabase.url` field is retained and will take precedence if set. ##### Bitnami Docker repository migration The Camunda Helm charts have been updated to use the new Bitnami Docker repository. See [Bitnami Docker repository migration](/self-managed/upgrade/helm/index.md#bitnami-docker-repository-migration) for migration details. ##### Helm chart: Bitnami subcharts bundled The Bitnami subcharts (PostgreSQL, Keycloak, Elasticsearch, and Common) are bundled directly within the Camunda Helm chart instead of being fetched from external Bitnami repositories at install time. This change reduces the risk of unexpected breaking changes from upstream Bitnami chart updates and gives Camunda full control over the lifecycle of these subcharts. No action is required — existing deployments will continue to work as before when applying the next patch release. #### Adjustments - **New package structure**: - Package `io.camunda.client`: This package contains the new `CamundaClient` and all the features slated for release in version 8.8. - **Properties and environment variables refactoring**: - All old Java client property names will be refactored to more general ones. For instance, `zeebe.client.tenantId` will become `camunda.client.tenantId`. - Similarly, environment variables will be renamed following the same concept: `ZEEBE_REST_ADDRESS` will become `CAMUNDA_REST_ADDRESS`. - **Artifact ID change**: - The `artifactId` will change from `zeebe-client-java` to `camunda-client-java`. ### Connectors Starting with 8.7, the connector runtime will stop using the deprecated community [Spring Zeebe library](https://github.com/camunda-community-hub/spring-zeebe) to communicate with the core APIs of Camunda. The new [Spring Zeebe SDK](/apis-tools/camunda-spring-boot-starter/getting-started.md) will be used instead. Although the official SDK is largely compatible with the community library, some changes might be required in the configuration of Self-Managed connector deployments. We recommend updating the configuration to match the new property format of the [Spring Zeebe SDK](/apis-tools/camunda-spring-boot-starter/getting-started.md) to avoid any issues. The old properties will be removed in a future release. For more information, see the [update guide](/versioned_docs/version-8.7/self-managed/operational-guides/update-guide/860-to-870.md#connectors) and the [connectors configuration guide](/self-managed/components/connectors/connectors-configuration.md). --- ## Release notes These release notes identify the new features included in 8.7, including [alpha feature releases](/components/early-access/alpha/alpha-features.md). ## 8.7.17 ### Spring Zeebe SDK - Spring-Boot 3.5 support As the [Spring-Boot OSS Support](https://spring.io/projects/spring-boot#support) for the bundled Spring-Boot version 3.4 ends in in 2025-12, [Spring-Boot 3.5.x compatibility](../../../../versioned_docs/version-8.7/apis-tools/spring-zeebe-sdk/getting-started.md#version-compatibility) is verified since the `8.7.17` patch onward. ## 8.7 minor | Scheduled release date | Scheduled end of maintenance | All Patch releases | Release blog | Update guide | | ---------------------- | ---------------------------- | --------------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | | 8 April 2025 | 13 October 2026 | [Patch Releases and Changelogs](#technical-changelogs-for-all-87x-patch-releases) | [Release blog](https://camunda.com/blog/2025/04/camunda-8-7-release/) | [Update guide](/versioned_docs/version-8.7/self-managed/operational-guides/update-guide/860-to-870.md) | ### AWS EKS and AWS OpenShift (ROSA) reference architecture Self-Managed Camunda is pleased to announce the release of the Reference Architecture packages for AWS EKS (single-region) and Red Hat OpenShift on AWS (ROSA) in both single and dual-region configurations. - These packages offer a standardized Reference Architecture that includes implementation requirements, Terraform templates, Helm configurations, deployment pipelines, and operational procedures. - Additionally, we have enhanced the user experience by improving test coverage for the documented procedures. :::note As part of this effort, the previously used repositories [camunda-tf-eks-module](https://github.com/camunda/camunda-tf-eks-module) and [camunda-tf-rosa](https://github.com/camunda/camunda-tf-rosa) were deprecated and merged into a new consolidated repository: [camunda-deployment-references](https://github.com/camunda/camunda-deployment-references). ::: ### Bulk publish to shared resources SaaSSelf-ManagedWeb Modeler Develop reusable assets in a pro-code environment, push them to your version control system, then publish them to your Web Modeler environment so anyone in your organization can reuse them with newly modified endpoints. ### Camunda SAP integration SaaSSelf-Managed Camunda now offers a robust SAP integration featuring: - An OData connector for seamless API interactions. - An RFC connector for BAPI/function module access. - A dedicated plugin enabling SAP BTP services within BPMN workflows. This solution ensures compliance with IT governance standards using SAP BTP (Business Technology Platform) and the SAP Cloud connector for enterprise-grade reliability. ### Connector manage and run SaaSSelf-ManagedConnectors {#manage-connectors} Connector manage and run provides a consolidated view of your running inbound connector [webhooks, message queue subscriptions, and polling subscriptions](/reference/glossary.md#inbound-connector) for efficient monitoring and management. - Real-time alerts will notify operators when connectors are not running, preventing unnoticed downtimes. - Use this feature to check your inbound connectors are healthy and running, and troubleshoot unhealthy connectors. To learn more about this feature, see [manage your connectors](/components/hub/organization/manage-clusters/manage-connectors.md). ### Custom JWKS and JWT Algorithms Support Self-Managed Self-Managed customers now have [full control over JWT configurations](/self-managed/components/hub/configuration/modeler-configuration.md) for enhanced security and compatibility. ### Document handling SaaSSelf-Managed We have extended Camunda's [document handling](/components/document-handling/getting-started.md) capabilities by introducing robust integrations and support for AWS S3, local file systems, and document operations within Zeebe. This version enhances document management by providing additional support for secure storage, retrieval, and integration with connectors, improving the efficiency and scalability of document-dependent workflows. ### Dual-region reference architecture for OpenShift Self-Managed We are excited to announce the addition of the [dual-region reference architecture for Red Hat OpenShift](/self-managed/deployment/helm/cloud-providers/openshift/dual-region.md). This new architecture leverages Submariner, a cloud-native technology based on IPSec, to enable inter-cluster communication and service discovery across regions. Learn more about [dual-region operational procedures](/self-managed/deployment/helm/operational-tasks/dual-region-ops.md). ### Dual-region ROSA HCP cluster with Terraform Self-Managed Step through [a detailed tutorial for deploying two Red Hat OpenShift on AWS (ROSA) cluster with Hosted Control Plane (HCP) in two different regions](/self-managed/deployment/helm/cloud-providers/amazon/openshift/terraform-setup-dual-region.md). It is specifically tailored for deploying Camunda 8 using Terraform, a widely-used Infrastructure as Code (IaC) tool. ### End-to-end organization process landscape SaaSSelf-ManagedWeb Modeler Desktop Modeler Automation leaders can visualize all automation projects through a single, hierarchical source of truth of approved processes. Specifically, there are new features for copying reviewed process application versions to a central project where every org member can be invited with a single click. Now, users can more easily communicate their automation efforts and maximize asset reuse. ### Integrate additional Kubernetes definitions Self-Managed The Camunda 8 Helm chart now allows custom Kubernetes manifests to be injected into your `values.yaml`. This enables you to add additional Kubernetes resources such as ConfigMaps, Deployments, or Services into your deployment without modifying the Helm chart itself. For more information, visit the [documentation on injecting Kubernetes manifests](/self-managed/deployment/helm/configure/add-extra-manifests.md). ### Intelligent Document Processing (IDP) SaaSSelf-ManagedWeb Modeler {#idp} Use intelligent document processing (IDP) to integrate automated document processing into your end-to-end processes. - IDP uses artificial intelligence (AI) and machine learning (ML) to identify, extract, and organize data from your structured and unstructured documents into a structured format you can use in your processes. - For example, you can use IDP to extract data from invoices and other document types in your document processing workflow. To learn more about this feature, see [intelligent document processing](/components/hub/workspace/modeler/intelligent-document-processing.md). ### Process applications in Desktop Modeler SaaSSelf-ManagedDesktop Modeler We have enabled developers to manage and work with multi-file BPMN projects directly within Desktop Modeler. This feature brings familiar IDE-like project management capabilities to Modeler, aligning with Web Modeler concepts, projects, and process applications. ### Process application versioning, README, and review SaaSSelf-ManagedWeb Modeler You can now track changes and deploy the right version of the process application to the right environment, keep your process documentation updated, [versioned](/components/hub/workspace/manage-projects/project-versioning.md), and [readable for everyone](/components/hub/workspace/modeler/modeling/advanced-modeling/process-documentation-with-readme-files.md), and enjoy a smooth, out-of-the-box experience ensuring all changes to processes are formally [reviewed](/components/hub/workspace/manage-projects/project-pipeline.md#review) and approved. ### Task appending framework SaaSSelf-ManagedModeler Create and append tasks with resources available in the current project and process application. Find the available processes, decisions, and forms in the append menu to directly create a task linked to the resource. ### Unified deployment experience from Web Modeler Self-ManagedWeb Modeler [Authenticate deployments](/components/hub/workspace/manage-projects/project-versioning.md) with your existing user tokens, rather than entering shared secrets. This further simplifies the deployment process beyond our 8.6 release. ### Known bugs in the 8.7.0 release | Bug / issue | Description | | :------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [XML keeps inbound.type after changing element (#932)](https://github.com/camunda/issues/issues/932) | When changing the element type of an inbound start event connector to a blank start event type, the inbound connector properties are not removed.**Affects:** Web Modeler**Workaround**: Delete and re-create the BPMN element. | | [Fields in Tasklist are not editable for AI-Generated forms (#26079)](https://github.com/camunda/camunda/issues/26079) | Fields in AI-generated user forms may not be editable for certain Chrome browser versions.**Affects:** Web Modeler and Tasklist**Workaround**: Manually recreate the form. | | [PDFs cannot be previewed or downloaded in Firefox, Chrome, or Edge (#28498)](https://github.com/camunda/camunda/issues/28498) | In rare situations, PDFs cannot be previewed due to the used browser.**Affects:** Tasklist**Workaround**: Use a different browser; clear browser cache. | | [Connectors config using EntraId (OIDC) for 8.7.0-alpha5 incorrect (#3135)](https://github.com/camunda/camunda-platform-helm/issues/3135) | Incomplete connectors configuration for Entra ID usage.**Affects:** Connectors**Workaround**: Set an environment variable with the token scope for Operate (see [issue](https://github.com/camunda/camunda-platform-helm/issues/3135)). | | [Uploaded files are not uploaded to the document storage when starting a process from Modeler (#29526)](https://github.com/camunda/camunda/issues/29526) | Files are not uploaded to the document storage when starting a process with a start form from Web Modeler or Play.**Affects:** Document handling and Web Modeler**Workaround**: Start the process from Tasklist or the Tasklist REST API. | | [File picker does not display the name of the uploaded file for completed tasks (#25443)](https://github.com/camunda/camunda/issues/25443) | File picker does not display the name of the uploaded file for completed tasks.**Affects:** Tasklist**Workaround**: File name can be viewed in Operate. | | [File upload fails to AWS storage due to non-standard space in filename (#28375)](https://github.com/camunda/camunda/issues/28375) | File upload fails to AWS storage due to non UTF-8 whitespace character in filename.**Affects:** Tasklist**Workaround**: When using AWS S3 storage, use UTF-8 compatible characters. | | [Failed to replay batch at 'LoggedEvent (#30810)'](https://github.com/camunda/camunda/issues/30810) | When updating from `8.6.13` to `8.7.0`, Zeebe processing can stop after the update in some situations, where multi-instance elements are used.**Affects:** Zeebe**Workaround**: This issue is fixed in `8.7.1`. When affected, going to `8.7.1` fully mitigates the issue. There is no risk of data loss. | | [Zeebe backups fail when using Self-Managed environment in Azure with Managed Identity](https://github.com/camunda/camunda/issues/30860) | When performing Zeebe backups with Azure and Managed Identity in a self-managed environment, Zeebe fails to load the application default credentials that are provided at runtime, preventing backups from taking place.**Affects:** Zeebe**Workaround**: When using Azure backups with in a SM environment, the configuration for the Azure store should be set as environment variables as detailed [here](/self-managed/components/orchestration-cluster/zeebe/configuration/broker.md#zeebebrokerdatabackupazure) instead of using Managed Identity. | ### Technical Changelogs for all 8.7.x patch releases
Overview of all patch releases and their Changelogs in GitHub [Camunda 8.7.29 (05.05.2026)](https://github.com/camunda/camunda/releases/tag/8.7.29)[Camunda 8.7.28 (21.04.2026)](https://github.com/camunda/camunda/releases/tag/8.7.28)[Camunda 8.7.27 (14.04.2026)](https://github.com/camunda/camunda/releases/tag/8.7.27)[Camunda 8.7.26 (30.03.2026)](https://github.com/camunda/camunda/releases/tag/8.7.26)[Camunda 8.7.25 (09.03.2026)](https://github.com/camunda/camunda/releases/tag/8.7.25)[Camunda 8.7.24 (04.03.2026)](https://github.com/camunda/camunda/releases/tag/8.7.24)[Camunda 8.7.23 (03.02.2026)](https://github.com/camunda/camunda/releases/tag/8.7.23)[Camunda 8.7.22 (05.01.2026)](https://github.com/camunda/camunda/releases/tag/8.7.22)[Camunda 8.7.21 (09.12.2025)](https://github.com/camunda/camunda/releases/tag/8.7.21)[Camunda 8.7.20 (02.12.2025)](https://github.com/camunda/camunda/releases/tag/8.7.20)[Camunda 8.7.19 (27.11.2025)](https://github.com/camunda/camunda/releases/tag/8.7.19)[Camunda 8.7.18 (17.11.2025)](https://github.com/camunda/camunda/releases/tag/8.7.18)[Camunda 8.7.17 (04.11.2025)](https://github.com/camunda/camunda/releases/tag/8.7.17)[Camunda 8.7.16 (17.10.2025)](https://github.com/camunda/camunda/releases/tag/8.7.16)[Camunda 8.7.15 (03.10.2025)](https://github.com/camunda/camunda/releases/tag/8.7.15)[Camunda 8.7.14 (01.10.2025)](https://github.com/camunda/camunda/releases/tag/8.7.14)[Camunda 8.7.13 (15.09.2025)](https://github.com/camunda/camunda/releases/tag/8.7.13)[Camunda 8.7.12 (02.09.2025)](https://github.com/camunda/camunda/releases/tag/8.7.12)[Camunda 8.7.11 (25.08.2025)](https://github.com/camunda/camunda/releases/tag/8.7.11)[Camunda 8.7.10 (04.08.2025)](https://github.com/camunda/camunda/releases/tag/8.7.10)[Camunda 8.7.9 (28.07.2025)](https://github.com/camunda/camunda/releases/tag/8.7.9)[Camunda 8.7.8 (16.07.2025)](https://github.com/camunda/camunda/releases/tag/8.7.8)[Camunda 8.7.7 (01.07.2025)](https://github.com/camunda/camunda/releases/tag/8.7.7)[Camunda 8.7.6 (20.06.2025)](https://github.com/camunda/camunda/releases/tag/8.7.6)[Camunda 8.7.5 (05.06.2025)](https://github.com/camunda/camunda/releases/tag/8.7.5)[Camunda 8.7.4 (06.06.2025)](https://github.com/camunda/camunda/releases/tag/8.7.4)[Camunda 8.7.3 (02.06.2025)](https://github.com/camunda/camunda/releases/tag/8.7.3)[Camunda 8.7.2 (13.05.2025)](https://github.com/camunda/camunda/releases/tag/8.7.2)[Camunda 8.7.1 (09.04.2025)](https://github.com/camunda/camunda/releases/tag/8.7.1)[Camunda 8.7.0 (08.04.2025)](https://github.com/camunda/camunda/releases/tag/8.7.0)[Connectors 8.7.20 (05.05.2026)](https://github.com/camunda/connectors/releases/tag/8.7.20)[Connectors 8.7.19 (30.03.2026)](https://github.com/camunda/connectors/releases/tag/8.7.19)[Connectors 8.7.18 (26.03.2026)](https://github.com/camunda/connectors/releases/tag/8.7.18)[Connectors 8.7.17 (10.03.2026)](https://github.com/camunda/connectors/releases/tag/8.7.17)[Connectors 8.7.16 (02.03.2026)](https://github.com/camunda/connectors/releases/tag/8.7.16)[Connectors 8.7.15 (27.01.2026)](https://github.com/camunda/connectors/releases/tag/8.7.15)[Connectors 8.7.14 (07.01.2026)](https://github.com/camunda/connectors/releases/tag/8.7.14)[Connectors 8.7.13 (01.12.2025)](https://github.com/camunda/connectors/releases/tag/8.7.13)[Connectors 8.7.12 (19.11.2025)](https://github.com/camunda/connectors/releases/tag/8.7.12)[Connectors 8.7.11 (05.11.2025)](https://github.com/camunda/connectors/releases/tag/8.7.11)[Connectors 8.7.10 (07.10.2025)](https://github.com/camunda/connectors/releases/tag/8.7.10)[Connectors 8.7.9 (02.10.2025)](https://github.com/camunda/connectors/releases/tag/8.7.9)[Connectors 8.7.8 (01.09.2025)](https://github.com/camunda/connectors/releases/tag/8.7.8)[Connectors 8.7.7 (04.08.2025)](https://github.com/camunda/connectors/releases/tag/8.7.7)[Connectors 8.7.6 (15.07.2025)](https://github.com/camunda/connectors/releases/tag/8.7.6)[Connectors 8.7.5 (02.07.2025)](https://github.com/camunda/connectors/releases/tag/8.7.5)[Connectors 8.7.4 (20.06.2025)](https://github.com/camunda/connectors/releases/tag/8.7.4)[Connectors 8.7.3 (06.06.2025)](https://github.com/camunda/connectors/releases/tag/8.7.3)[Connectors 8.7.2 (09.05.2025)](https://github.com/camunda/connectors/releases/tag/8.7.2)[Connectors 8.7.1 (06.05.2025)](https://github.com/camunda/connectors/releases/tag/8.7.1)[Connectors 8.7.0 (02.04.2025)](https://github.com/camunda/connectors/releases/tag/8.7.0)
## 8.7.0-alpha5 | Release date | Changelog(s) | Blog | | :------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------- | | 11 March 2025 | [ Camunda 8 core ](https://github.com/camunda/camunda/releases/tag/8.7.0-alpha5)[ Connectors ](https://github.com/camunda/connectors/releases/tag/8.7.0-alpha5) | [Release blog](https://camunda.com/blog/2025/03/camunda-alpha-release-march-2025/) | ### Amazon Web Services (AWS) region support SaaS Camunda 8 SaaS now supports Amazon Web Services (AWS) deployments. When creating a new cluster, you can select from the following new [AWS regions](/components/saas/regions.md): - Frankfurt, Europe (eu-central-1) - North America, Virginia (us-east-1) ### BPMN Copilot BPMN-to-text Camunda's BPMN Copilot now also supports generating text from a BPMN diagram. BPMN-to-text allows you to: - Rapidly draft process documentation. - Learn how the process works. - Explain the behavior of the process to stakeholders. To learn more about this feature, see [BPMN Copilot](/components/early-access/alpha/bpmn-copilot/bpmn-copilot.md). ### Camunda 8 Run advanced configuration options The Camunda 8 Run startup script now supports additional configuration parameters: a web application port, the location of keystore TLS certificates, and log level. This release also introduces a new `--docker` option, which starts C8Run with the `docker-compose up` command, and deploys Camunda 8 in containers instead of starting with a Java engine. For more information, see the [Camunda 8 Run documentation](/self-managed/quickstart/developer-quickstart/c8run.md). ### Console cluster health and capacity monitoring Cluster capacity provides a high-level overview of how well a cluster is coping with its current workload. - Use this information to check and monitor if a cluster is appropriately sized for its workload. - Cluster capacity can also be used as an indicator of cluster health. For example, a cluster running at maximum capacity can be an indicator of poor cluster responsiveness. To learn more about this feature, see [cluster capacity](/components/hub/organization/manage-clusters/cluster-capacity.md). ### Intelligent document processing Use intelligent document processing (IDP) to integrate automated document processing into your end-to-end processes. - IDP uses artificial intelligence (AI) and machine learning (ML) to identify, extract, and organize data from your structured and unstructured documents into a structured format you can use in your processes. - For example, you can use IDP to extract data from invoices and other document types in your document processing workflow. :::note IDP only offers support for Camunda 8 Self-Managed development deployment via Docker with the 8.7.0-alpha5 release (see [example deployment](/components/hub/workspace/modeler/idp/idp-configuration.md#idp-docker-example)). Full production support for Camunda 8 SaaS and Camunda 8 Self-Managed is planned for delivery with the 8.7 release. Camunda 8 Run is not supported as IDP requires Web Modeler. ::: To learn more about this feature, see [intelligent document processing](/components/hub/workspace/modeler/intelligent-document-processing.md). ### Play multi-tenancy Self-Managed Use your existing development cluster with tenancy so multiple teams can develop on the same cluster without impacting each other. This feature is available for Web Modeler in Self-Managed environments. ### Process landscape visualization Process landscape visualization provides a comprehensive, hierarchical view of all related processes. This release enables automation leads to: - Visualize the entire process landscape of a project in a single, interactive interface. - Drill down from high-level processes to detailed sub-processes and activities. To learn more about this feature, see [process landscape visualization](/components/hub/workspace/modeler/process-landscape-visualization.md). ### Robotic Process Automation (RPA) production-ready The [RPA solution](/components/rpa/overview.md) is graduating to [production-ready](/components/rpa/production.md), empowering customers to deploy robust, scalable, and maintainable automation workflows seamlessly. As RPA tasks are now available within BPMN diagrams for automation, users can now implement, troubleshoot, and maintain automation RPA scripts in Desktop Modeler, and deploy and manage RPA files in Zeebe. This major update introduces a suite of powerful features designed to enhance the development, deployment, and management of [RPA scripts](/components/rpa/getting-started.md). We recommend reviewing the [current known issues for RPA](https://github.com/camunda/rpa-worker/discussions/categories/known-issues) to ensure environment compatibility. ### Web Modeler governance and change control Web Modeler now supports stronger governance and change control. This ensures safe production deployment for processes with low to medium complexity and criticality. The following capabilities are now available: - Reviews cannot be performed by the user who created the process application version, and the reviewer is logged in the version history of a process application. - Admins can enable production deployments for reviewed process applications as an alternative to using their own deployment pipeline. To learn more about this feature, see [process governance](/components/hub/workspace/manage-projects/project-pipeline.md). ## 8.7.0-alpha4 | Release date | Changelog(s) | Blog | | :--------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------ | | 11 February 2025 | [ Camunda 8 core ](https://github.com/camunda/camunda/releases/tag/8.7.0-alpha4)[ Connectors ](https://github.com/camunda/connectors/releases/tag/8.7.0-alpha4) | [Release blog](https://camunda.com/blog/2025/02/camunda-alpha-release-february-2025/) | ### BPMN Copilot SaaS With the new BPMN Copilot for SaaS, go from 0 to 80% of a process diagram in minutes. Generate process diagrams from natural language descriptions, then collaborate on them with colleagues. For more information, see the [BPMN Copilot documentation](/components/early-access/alpha/bpmn-copilot/bpmn-copilot.md). ### Ad-hoc sub-processes A new [ad-hoc sub-process](/components/modeler/bpmn/ad-hoc-subprocesses/ad-hoc-subprocesses.md) BPMN element is now supported. This new kind of subprocess allows more flexible process flows with a compact visual representation. It is the first step towards dynamic processes and execution of ad-hoc activities. ## 8.7.0-alpha3 | Release date | Changelog(s) | Blog | | :-------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | | 14 January 2025 | [ Camunda 8 core ](https://github.com/camunda/camunda/releases/tag/8.7.0-alpha3)[ Connectors ](https://github.com/camunda/connectors/releases/tag/8.7.0-alpha3.2) | [Release blog](https://camunda.com/blog/2025/01/camunda-alpha-release-january-2025) | ### Amazon OpenSearch Optimize support Self-ManagedOptimize :::note This feature was originally released with 8.7.0-alpha3, and is no longer available in 8.7.0. Amazon OpenSearch Optimize support is now available in [8.8.0-alpha1](/reference/announcements-release-notes/880/880-announcements.md#amazon-opensearch-optimize-support-self-managedoptimize). For more information, see the Camunda 8.7 and 8.8 [release update blog](https://camunda.com/blog/2025/01/camunda-87-88-release-update/). ::: Camunda 8 Self-Managed now fully supports the use of Amazon OpenSearch with Optimize. ### Camunda cluster update API SaaSAPI You can now use the Camunda 8 SaaS [Administration API](/apis-tools/administration-api/administration-api-reference.md) to update a cluster to a new generation. - This allows you to trigger automated simultaneous updates for multiple clusters via the API. - Send a PUT request to the Administration API `Update cluster` endpoint. ### Connectors SaaSSelf-ManagedConnectors New connectors and enhancements are included in this release. #### AWS S3 connector Use the new outbound Amazon S3 connector to interact with [Amazon Simple Storage Service (Amazon S3)](https://aws.amazon.com/S3/) from your BPMN process. This connector supports the following operations: - **Upload Document**: Upload a document to an AWS S3 bucket. - **Download Document**: Download a document from an AWS S3 bucket. - **Delete Document**: Delete a document from an AWS S3 bucket. To learn more about this connector, see [Amazon S3 connector](/components/connectors/out-of-the-box-connectors/amazon-s3.md). #### Box connector Use the new outbound Box connector to interact with [Box.com](https://www.box.com/) account content from your BPMN process. This connector supports the following operations: - **Download File**: Download files into the Camunda document store. - **Upload File**: Upload files from Camunda to Box. - **Delete File**: Delete file items in Box. - **Move File**: Move files between folders in Box. - **Create Folder**: Create new folders in your Box account. - **Delete Folder**: Delete folders from your Box account. - **Search**: Search file items using the Box search API. To learn more about this connector, see [Box connector](/components/connectors/out-of-the-box-connectors/box.md). ### Document handling SaaSSelf-ManagedAPIModeler New features are available as part of the enhanced document handling being delivered with the 8.7 release. - The document store API supports uploading files in batches. - The document reference is extended with a document hash as an additional security mechanism. - The AWS S3 document store is implemented. - User task attachments and a [document preview component](/components/modeler/forms/form-element-library/forms-element-library-document-preview.md) in forms are supported. This enhances document-centric human workflows with file preview and download support in forms, simplifying the handling of large data and documents. - Document handling support is added to the [REST](/components/connectors/protocol/rest.md) and [Amazon Bedrock](/components/connectors/out-of-the-box-connectors/amazon-bedrock.md) connectors. ### GitLab sync SaaSSelf-ManagedModeler Web Modeler now supports native integration between a process application and a branch of a GitLab repository. Non-technical users in orgs running GitLab can now easily access the files in their source of truth, collaborate cross-platform with Desktop Modeler users, and contribute changes to a feature branch that can be easily merged and deployed. To learn more about this feature, see [Git sync](/components/hub/workspace/manage-projects/git-sync.md). ### Process Instance Migration SaaSSelf-ManagedZeebe :::note This feature was originally released with 8.7.0-alpha3, and is no longer available in 8.7.0. Process instance migration is now available in [8.8.0-alpha1](/reference/announcements-release-notes/880/880-announcements.md#process-instance-migration-saasself-managedzeebe). For more information, see the Camunda 8.7 and 8.8 [release update blog](https://camunda.com/blog/2025/01/camunda-87-88-release-update/). ::: Enhanced process instance migration now supports additional BPMN elements, enabling smoother transitions for your workflows and empowering operations teams to seamlessly migrate running process instances to updated process definitions without workarounds or manual intervention. You can migrate running process instances containing the following elements: - **Parallel Gateways**: Ensure smooth transitions for concurrent workflows. - **Inclusive Gateways**: Migrate workflows that rely on inclusive logic. - **Compensation and Escalation Events**: Maintain error-handling and recovery mechanisms. To learn more about migration, see [process instance migration](/components/concepts/process-instance-migration.md). ### Replay scenarios SaaSModeler In Camunda 8 SaaS, you can now use Play to quickly repeat manual test suites by recording and playing back process instances as scenarios. For example, you can validate your process by creating and rerunning scenarios for different paths to check the process works as expected after any diagram changes are made. - As you save completed instances as scenarios, Play calculates the percent of elements covered by the scenario suite. - This is the first step towards bringing automated testing into Web Modeler, and enabling business and IT to collaborate on automated tests. To learn more about this feature, see [Play scenarios](/components/hub/workspace/modeler/validation/play-your-process.md#scenarios). ### Unified deployment experience from Web Modeler Self-ManagedModeler The deployment experience is further simplified for Enterprise customers running Web Modeler Self-Managed. The predefined development stage cluster for the process application is also used when launching Play. This feature is already available in SaaS, and is now also available for Self-Managed as part of unifying the deployment experience. ### User task listeners SaaSSelf-ManagedModelerOperate :::note This feature was originally released with 8.7.0-alpha2, and is no longer available in 8.7.0. User task listeners are now available in [8.8.0-alpha1](/reference/announcements-release-notes/880/880-announcements.md#user-task-listeners-saasself-managedmodeleroperate). For more information, see the Camunda 8.7 and 8.8 [release update blog](https://camunda.com/blog/2025/01/camunda-87-88-release-update/). ::: Task lifecycle management is enhanced with user task listeners, allowing users to react to specific user task lifecycle events. - Process designers can now model task listeners for different events, such as `assigning` and `completing`. - Developers can use the same job infrastructure to activate and complete task listener jobs. - Operations engineers can easily check details of active and completed task listeners within instances, and efficiently resolve task listener incidents. This enhancement streamlines operations and ensures smoother incident handling, improving time to unblock process execution. ### Zeebe user tasks modeling migration support SaaSSelf-ManagedModeler User task implementation type "Zeebe user task" is renamed to "Camunda user task", and set as the default implementation type. :::note As Job-worker user tasks managed by Camunda will be deprecated in Camunda 8.9, Camunda recommends you start using Camunda User Tasks (formerly known as Zeebe User Task) in your process definitions. To learn more, see [Announcements](/reference/announcements-release-notes/870/870-announcements.md#deprecated-job-based-user-tasks-querying). ::: ## 8.7.0-alpha2 | Release date | Changelog(s) | Blog | | :--------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------ | | 10 December 2024 | [ Camunda 8 core ](https://github.com/camunda/camunda/releases/tag/8.7.0-alpha2)[ Connectors ](https://github.com/camunda/connectors/releases/tag/8.7.0-alpha2.1) | [Release blog](https://camunda.com/blog/2024/12/camunda-alpha-release-december-2024/) | :::caution This [alpha release](/reference/announcements-release-notes/release-policy.md) contains a known issue where Self-Managed customers using the 8.7.0-alpha2 Helm chart cannot login to Operate. This issue is due to key architecture refactoring and improvements, and will be resolved in the next release. ::: ### Camunda 8 REST API Query API API :::note The Query API was promoted from an alpha feature to stable in 8.7.0-alpha2, but is now moved back to an alpha feature for the 8.7 release. The stable Query API is now available in [8.8.0-alpha1](/reference/announcements-release-notes/880/880-announcements.md#camunda-8-rest-api-query-api-api). For more information, see the Camunda 8.7 and 8.8 [release update blog](https://camunda.com/blog/2025/01/camunda-87-88-release-update/). ::: You can now use a single Query API in the Camunda 8 REST API to find process and decision data instead of using multiple component APIs. New Query API endpoints are added as follows: - Decision definitions - Decision instances - Decision requirements - Flownode instances - Incidents - Process definitions - Process instances - User tasks - Variables ### Connectors SaaSSelf-ManagedConnectors New connectors and enhancements are included in this release. #### AWS Amazon Comprehend connector The new Amazon Comprehend connector allows you to integrate your BPMN service with Amazon Comprehend, a service which extracts insights about the content of documents, such as personal identifiable information (PII) and key phrases. To learn more about this connector, see [Amazon Comprehend connector](/components/connectors/out-of-the-box-connectors/amazon-comprehend.md). #### Email connector attachments The Email connector is enhanced as follows: - Supports attachments stored in the document store. - Supports custom headers. - Messages can now be sent as plaintext, HTML, or in both formats. To learn more about this connector, see [Email connector](/components/connectors/out-of-the-box-connectors/email-outbound.md). #### Google Gemini connector The new Google Gemini connector allows you to access Gemini multimodal models from Google, capable of understanding virtually any input, and combining different types of information in your BPMN process. To learn more about this connector, see [Google Gemini connector](/components/connectors/out-of-the-box-connectors/google-gemini.md). #### Webhook connector document upload Document upload is now supported by the Webhook connector. Uploads can now be stored in the document store and are available for further processing for start and intermediate events. - Use the `documents` object to access created documents in both the response expression and the result expression. - The `documents` object contains the references for created documents. To learn more about this feature, see [HTTP Webhook connector](/components/connectors/protocol/http-webhook.md). ### Connector Runtime SaaSSelf-ManagedConnectors #### Spring Zeebe SDK and Camunda 8 REST API Migration :::note This feature was originally released with 8.7.0-alpha3, and is no longer available in 8.7.0. The Camunda 8 REST API migration is now available in [8.8.0-alpha1](/reference/announcements-release-notes/880/880-announcements.md#spring-sdk-and-camunda-rest-api-migration). For more information, see the Camunda 8.7 and 8.8 [release update blog](https://camunda.com/blog/2025/01/camunda-87-88-release-update/). ::: The connectors experience is enhanced with the migration from the Spring Zeebe to the Camunda 8 REST API, and the removal of dependency on the Operate client. #### Testing Support migration Connectors are supported in the Camunda Process Test (CPT) Java library you can use to test your BPMN processes and process application. To learn more about this feature, see [Camunda Process Test getting started](/apis-tools/testing/getting-started.md). ### Cluster disk space cleared for paused trial clusters SaaS Cluster disk space is cleared when a trial cluster is paused. - You will need to redeploy processes to the cluster once it is resumed from a paused state. - Cluster configuration settings (for example, API Clients, connector secrets, and IP allowlists) are saved so you can easily resume a cluster. ### Document handling SaaSSelf-Managed New features are available as part of the enhanced document handling being delivered with the 8.7 release. - A new Document API is available as part of the [Camunda 8 REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). - The [Java client](/apis-tools/java-client/getting-started.md) is enhanced to support these new Document API methods. - A document store concept is introduced and implemented as an in-memory and a GCP-based document store. - A new Tasklist [Filepicker component](/components/modeler/forms/form-element-library/forms-element-library-filepicker.md) is added for uploading documents to the document store in a form. - The [Connector SDK](/components/connectors/custom-built-connectors/connector-sdk.md) is enhanced to provide document support in property/variable bindings. - The [Webhook connector](/components/connectors/protocol/http-webhook.md) supports Documents via the `documents` object. ### Export activity logs in Console SaaSConsole You can export activity logs as JSON or CSV files from the Console UI or API. - **UI:** On the Organization management **Activity** tab, click **Export activity**. - **API:** Send a GET request to the Management API `GetJson` or `GetCsv` endpoint. To learn more about this feature, see [view organization activity](/components/hub/organization/manage-organization-settings/view-organization-activity.md). ### Process instance migration SaaSSelf-ManagedZeebe :::note This feature was originally released with 8.7.0-alpha2, and is no longer available in 8.7.0. Process instance migration is now available in [8.8.0-alpha1](/reference/announcements-release-notes/880/880-announcements.md#process-instance-migration-saasself-managedzeebe). For more information, see the Camunda 8.7 and 8.8 [release update blog](https://camunda.com/blog/2025/01/camunda-87-88-release-update/). ::: Enhanced process instance migration allows you to solve problems with process definitions and use the latest process improvements. You can now migrate the following: - Compensation boundary event subscriptions - Escalation boundary events - Escalation event subprocesses ### Singapore region available for SaaS SaaS A new Singapore (asia-southeast1) region is available for SaaS clusters. Use this region to: - Improve overall processing speed and reduce latency if you operate in Singapore and Southeast Asian (SEA) countries. - Keep cluster data within Singapore to support your local data residency and compliance needs. To learn more about supported SaaS regions, see [regions](/components/saas/regions.md). ### Tags and properties in Self-Managed Console Self-ManagedConsole Use custom tags and properties in Self-Managed Console to improve your orchestration cluster management. - Administrators can now assign tags such as `prod`, `dev`, or `test` to clusters for clear identification across environments. - Tags are shown in the Console UI, and accessible via the Administration API to streamline usage reporting and cost allocation. - Custom properties provide contextual information about each cluster. Administrators can add detailed descriptions, team names, and include links to resources such as Grafana dashboards or internal portals, shown in the Console **Cluster Details**. This feature allows you to differentiate clusters, ensure configurations align with production standards (for example, check TLS is enabled, correct partition counts), and improve operational efficiency by making key information more visible. ### Unified deployment experience from Web Modeler Self-ManagedModeler The deployment experience is further simplified for Enterprise customers running Web Modeler Self-Managed. - User tokens are used for deployments instead of machine-to-machine (M2M) tokens generated from a client ID and secret. - You no longer need to enter a client ID and secret in the deploy modal. Instead, simply choose a cluster (or stage for process applications) and deploy. :::note The simplified deployment experience is not supported when [Microsoft Entra ID is used as OIDC provider](/self-managed/deployment/helm/configure/authentication-and-authorization/microsoft-entra.md). You still need to enter a client ID and secret in this case. Support is targeted for [Camunda 8.8](../870-announcements/#deprecated-web-modeler-cluster-authentication-oauth-and-client_credentials-self-managed). ::: ## 8.7.0-alpha1 | Release date | Changelog(s) | Blog | | :--------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------ | | 12 November 2024 | [ Camunda 8 core ](https://github.com/camunda/camunda/releases/tag/8.7.0-alpha1)[ Connectors ](https://github.com/camunda/connectors/releases/tag/8.7.0-alpha1) | [Release blog](https://camunda.com/blog/2024/11/camunda-alpha-release-november-2024/) | ### Activity log information in Console Console Console activity logs now contain information about changes made to secrets (add, update, remove), and Console user removals (unregistered organization users). ### Email connector Connectors The new Email connector allows you to: - Integrate your BPMN service with any email server using POP3, IMAP, or SMTP. - Automate the retrieval, deletion, search, and organization of emails directly within your processes. To learn more about this connector, see [Email connector](/components/connectors/out-of-the-box-connectors/email-inbound.md). ### Generate connector templates (OpenAPI + Postman) SaaSConnectors You can now configure and automatically generate a custom connector template in Web Modeler. This feature simplifies creating consistent, deployable templates, making connector setup quicker and more flexible. - You can start from a blank template or import an existing API definition such as an OpenAPI specification, Swagger specification, or a Postman collection. - For example, download a Postman collection as a YAML file, import this into the generator, and choose which methods to include in the generated template. To learn more about generating connector templates, see [generate a connector template](/components/hub/workspace/modeler/element-templates/element-template-generator.md). ### Monorepo Git sync Modeler When configuring Git sync in Web Modeler, define the optional `/path` option to unlock new use cases. - This option allows you to specify the path to the folder containing your process application files. - Sync with your main branch to perform visual diffing, collaboration, and manual testing in Web Modeler. Remember not to make any changes in this branch. - Edit the `/path` for multiple process applications to integrate Web Modeler with your existing monorepo and code assets. To learn more about configuring Git sync, see [Git sync](/components/hub/workspace/manage-projects/git-sync.md). ### Resize clusters on SaaS SaaSConsole Enterprise customers can flexibly resize their clusters to adjust capacity and performance. - Increase or decrease the cluster size at any time by adding or removing hosting packages. - For example, increase the cluster size to improve performance and add capacity, or decrease the cluster size to free up reservations for another cluster. To learn more about this feature, see [resize a cluster](/components/hub/organization/manage-clusters/manage-cluster.md#resize-a-cluster). ### Unified deployment experience for Web Modeler Self-ManagedModeler #### Deployment stages Predefined deployment stages for process applications are now also available in Web Modeler Self-Managed. - Select your dev, test, stage, and prod clusters to ensure process applications flow easily and predictably through your deployment pipeline. - For added control, do not assign a stage or prod environment to enable rapid iteration in Web Modeler while still ensuring deployments run through your approved pipeline. #### Simplified deployment The deployment experience for Enterprise customers running Web Modeler Self-Managed is simplified. - During Camunda installation, you can configure your Helm chart to decide which clusters are available from Web Modeler by default, and save their connection information. - With this setup, you only need to select a cluster, and add secrets and a tenant ID as required. --- ## 8.8 Release announcements Supported environment changes and breaking changes or deprecations for the Camunda 8.8 release. | Minor release date | Scheduled end of maintenance | Release notes | Release blog | Upgrade guides | | ------------------ | ---------------------------- | ------------------------------------------------------------------------------------ | ------------ | ---------------------------------------------------------------------------------------------- | | 14 October 2025 | 13 April 2027 | [8.8 release notes](/reference/announcements-release-notes/880/880-release-notes.md) | - | [Upgrade guides](/reference/announcements-release-notes/880/whats-new-in-88.md#upgrade-guides) | :::info 8.8 resources - See [release notes](/reference/announcements-release-notes/880/880-release-notes.md) to learn more about new features and enhancements. - See [What's new in Camunda 8.8](/reference/announcements-release-notes/880/whats-new-in-88.md) for important changes to consider when planning your upgrade from Camunda 8.7. - Refer to the [quality board](https://github.com/orgs/camunda/projects/187/views/15) for an overview of known bugs by component and severity. ::: ## Supported environment changes Change #### Elasticsearch and OpenSearch minimal supported versions Elasticsearch 8.16+ and OpenSearch 2.17+ are now supported as minimal versions to ensure you can benefit from the latest, most stable database releases. Older versions are no longer supported. Change #### PostgreSQL, Oracle and Microsoft SQL Server supported versions Management Identity now supports PostgreSQL and Amazon Aurora PostgreSQL versions 16.x and 17.x. Web Modeler now supports PostgreSQL version 18.x, Amazon Aurora PostgreSQL version 17.x, Oracle versions 19c and 23ai and Microsoft SQL Server versions 2019 and 2022. Change #### Zeebe, Operate, Tasklist, and Identity must run on same minor and patch levels From version `8.8.0` onwards, the Zeebe, Operate, Tasklist, and Identity [Orchestration Cluster](/self-managed/reference-architecture/reference-architecture.md#orchestration-cluster) components must run on the exact same `minor`and `patch` level to ensure compatibility. :::info See the [component version matrix](/reference/supported-environments.md#component-version-matrix) or the [Self-Managed reference architecture](/self-managed/reference-architecture/reference-architecture.md#orchestration-cluster) for a component overview. ::: ## Key changes ### 8.8.x patch releases The following key changes were also released as part of an 8.8.x patch release. | Patch release | Type | Key change | | :------------------------------------------------------------- | :-------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------- | | [8.8.9](https://github.com/camunda/camunda/releases/tag/8.8.9) | Breaking change | [Webhook alerts JSON format](#webhook-alerts-json-format) | | [8.8.9](https://github.com/camunda/camunda/releases/tag/8.8.9) | Change | [Spring Boot 4.0 support for Camunda Spring Boot Starter and Process Test ](#spring-boot-40-support-for-camunda-spring-boot-starter-and-process-test) | ### APIs & tools Breaking change #### `GET /decision-instances/{decisionEvaluationInstanceKey}` now validates the key format The [Get decision instance](/apis-tools/orchestration-cluster-api-rest/specifications/get-decision-instance.api.mdx) endpoint previously returned `404 Not Found` when the `decisionEvaluationInstanceKey` path parameter contained invalid characters that did not match the required pattern `^[0-9]+-[0-9]+$`. The endpoint now correctly returns `400 Bad Request` in this case, while `404 Not Found` is reserved for well-formed keys that do not exist. **Action:** Update any client code or error handling that relied on receiving `404 Not Found` for malformed keys to also handle `400 Bad Request`. Breaking change #### Orchestration Cluster: Zeebe Java Client <=8.7.15 with REST API enabled The Zeebe Java Client <=8.7.15 with REST API enabled is incompatible with Camunda 8.8 if you are running: - Either the [Zeebe Java Client <=8.7.15](../../../../versioned_docs/version-8.7/apis-tools/java-client/index.md) OR the [Spring Zeebe SDK <=8.7.15](../../../../versioned_docs/version-8.7/apis-tools/spring-zeebe-sdk/configuration.md#rest-over-grpc), - AND you opted into preferring REST over gRPC (setting `preferRestOverGrpc=true` explicitly on client setup). In this scenario, you will be affected by the following issue [Camunda 8.7 REST client fails on unknown response properties on job activate (#39675)](https://github.com/camunda/camunda/issues/39675). **Impact** When updating your Orchestration Cluster to 8.8 without updating your clients prior to at least 8.7.16, workers will fail to activate jobs with a log message such as the following: ```bash io.camunda.zeebe.client.api.command.MalformedResponseException: Expected to receive a response body, but got a problem: class ProblemDetail { type: about:blank title: Unexpected server response status: 500 detail: {"jobs":[{"type":"check-generation-usage-job","processDefinitionId":"c[...] instance: null } ``` **Required action** You must update your clients to at least 8.7.16, as this contains the fix for this issue. Alternatively, you can opt out of using `preferRestOverGrpc=true` before upgrading your cluster. Breaking change #### Webhook alerts JSON format In 8.8.0, a regression was introduced to [Webhooks Alerting](/components/hub/organization/manage-clusters/manage-alerts.md#webhook-alerts). The JSON format was modified so that the `processVersion` field returns a `String` value representing either the process version tag, if it exists, or otherwise the process version. In 8.8.9, the `processVersion` field reverts to returning an integer value representing the process version only. A new `processVersionTag` field is introduced to include the process version tag when available. **Example JSON format change** Before 8.8.9: ```json { "processVersion": "v2.1.0" // String - could be tag or number } ``` After 8.8.9: ```json { "processVersion": 3, // Integer - always the version number "processVersionTag": "v2.1.0" // String - the version tag (if exists) } ``` **Action required** Adapt any webhook-dependent integrations you have created for 8.8.x to handle the updated JSON structure: 1. Update your integration to read `processVersion` as an integer value representing the process version number. 2. If you need the process version tag, use the new `processVersionTag` field that contains the string value of the version tag (if one exists). 3. Ensure your integration handles cases where `processVersionTag` might be null or absent (for processes without version tags). 4. Test your webhook consumers to verify they correctly parse both the integer `processVersion` and string `processVersionTag` fields. Breaking change #### Bug fix: `FormResult.schema` type corrected from object to string The `schema` property in the `FormResult` response was incorrectly specified as `type: object` in the OpenAPI contract, but the server has always returned it as a JSON `string`. This specification bug is now fixed. This is a bug fix that aligns the OpenAPI contract with actual server behavior, improving correctness for typed client integrations. Applications already handling the runtime `string` value are unaffected. The Camunda Java client is also affected: `io.camunda.client.api.search.response.Form::getSchema()` now returns `String` instead of `Object`. If your Java code casts or processes the return value as `Object`, update it to use `String`. What to do: - Official SDK users: update to the latest SDK version. - Java client users: update calls to `Form::getSchema()` to handle the `String` return type instead of `Object`. - Generated-client users: regenerate your client. If your generated code relied on the incorrect `object` typing, update it to handle `string`. - Handwritten integrations: no change needed if you were already handling the actual `string` response. Removed #### Removed: Tasklist GraphQL API With the Camunda 8.8 release, the deprecated Tasklist GraphQL API is removed. Removed #### Removed: Deprecated OpenAPI objects With the Camunda 8.8 release, deprecated API objects containing number keys are removed, including the corresponding `application/vnd.camunda.api.keys.number+json` content type header. - In previous releases, entity keys were transitioned from `integer (int64)` to `string` types, with deprecated `integer (int64)` keys still supported. Support for `integer (int64)` keys is removed in Camunda 8.8. - To update to Camunda 8.8, API objects using `integer (int64)` keys must be updated to use `string` keys and the `application/json` header. :::info To learn more about the key attribute type change, see [8.7 API key attributes overview](/versioned_docs/version-8.7/apis-tools/camunda-api-rest/camunda-api-rest-overview.md#api-key-attributes). ::: Removed #### Removed: Optimize Index Rollover Prior to the Camunda 8.8 release, Optimize used the following configuration properties to apply index rollover to its External Variable Indices: - `externalVariable.variableIndexRollover.maxIndexSizeGB` - `externalVariable.variableIndexRollover.scheduleIntervalInMinutes` These properties are deleted in Camunda 8.8, with External Variables now stored in a single index. Deprecated #### Deprecated: Web Modeler API milestone endpoints With the Camunda 8.8 release, the [Web Modeler API](/apis-tools/web-modeler-api/index.md) endpoints under `/api/v1/milestones` are deprecated and scheduled for removal in 8.9. You can use the corresponding endpoints under `/api/v1/versions` instead. Deprecated #### Deprecated: Operate and Tasklist v1 REST APIs With the Camunda 8.8 release, the deprecation process for the [Operate](/versioned_docs/version-8.9/apis-tools/operate-api/overview.md) and [Tasklist](/versioned_docs/version-8.9/apis-tools/tasklist-api-rest/tasklist-api-rest-overview.md) REST APIs begins. You can begin migrating to the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) for querying to prepare for this change. | Version | Description | | :----------- | :--------------------------------------------------------------------------------------- | | Camunda 8.8 | These APIs remain available and functional. | | Camunda 8.9 | These APIs remain available but deprecated, and not recommended for new implementations. | | Camunda 8.10 | These APIs are removed. | :::warning Impact on user task access restrictions [User task access restrictions](/versioned_docs/version-8.9/components/tasklist/user-task-access-restrictions.md) are only supported with the Tasklist v1 API. After switching to the v2 API with Tasklist, user task access restrictions do not apply. ::: :::info To learn more about the differences between Tasklist v1 and v2 UI modes, see [Tasklist API versions](/components/tasklist/api-versions.md). ::: Deprecated #### Deprecated: Job-based user tasks querying With the Camunda 8.8 release, the deprecation process for job-based user tasks begins. - As job-based user tasks are not supported for querying/task management with Camunda 8.10, Camunda recommends using **Camunda user task** type (formerly **Zeebe user task**) in your process definitions. - You might still see Zeebe user task references in your XML, but this is the same thing as a Camunda user task. | Version | Description | | :----------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Camunda 8.9 | Job-based user tasks are available for querying, but Modeler automatically applies the Camunda user task and shows a warning message for each job-based user task. | | Camunda 8.10 | With the removal of the Tasklist REST API, job-based user tasks are no longer supported for querying and task management. With Camunda 8.9+, customers can still use job-based user tasks as standard jobs with headers to enable open architecture and composable solutions.For customers who require task lifecycle support and task querying, Camunda recommends using the Camunda User Task type. | Deprecated #### Deprecated: Zeebe Client job worker metrics With the Camunda 8.8 release, the deprecation of Zeebe client job worker metrics is announced. These metrics are scheduled for removal in the Camunda 8.10 release. :::info To learn more, see [Zeebe client job worker](/apis-tools/java-client/job-worker.md) and [Zeebe client job worker concept](/components/concepts/job-workers.md). ::: Deprecated #### Deprecated: Zeebe gRPC DeployProcess endpoint The `DeployProcess` endpoint was deprecated with 8.0, replaced with `DeployResource` RPC. This endpoint is scheduled for removal in the Camunda 8.10 release. Deprecated #### Deprecated: File type `connector_template` in Web Modeler API With the Camunda 8.8 release, the `connector_template` file type in the [Web Modeler API](/apis-tools/web-modeler-api/index.md) endpoint for file creation (`POST /api/v1/files`) is deprecated. This file type will be removed in the Camunda 8.10 release. You should use `element_template` instead, which provides equivalent functionality. Deprecated #### Deprecated: Zeebe Process Test With the Camunda 8.8 release, the deprecation of Zeebe Process Test is announced. - Zeebe Process Test is superseded by [Camunda Process Test](../../../apis-tools/testing/getting-started.md). - Zeebe Process Test is scheduled for removal in the Camunda 8.10 release. :::info To learn more, see [migrate to Camunda Process Test](../../../apis-tools/migration-manuals/migrate-to-camunda-process-test.md) and [Introducing Camunda Process Test - The Next Generation Testing Library](https://camunda.com/blog/2025/04/camunda-process-test-the-next-generation-testing-library/). ::: Deprecated #### Deprecated: Operate & Tasklist usage metrics endpoints With the Camunda 8.8 release, the deprecation of usage metrics endpoints in Operate and Tasklist is announced. - [Deprecated Operate endpoints](/self-managed/components/orchestration-cluster/core-settings/concepts/usage-metrics.md) - [Deprecated Tasklist endpoint](/self-managed/components/orchestration-cluster/core-settings/concepts/usage-metrics.md). :::warning Breaking change The Assignees list is removed from the response. ::: - These endpoints are superseded by [usage metrics endpoint](../../../apis-tools/orchestration-cluster-api-rest/specifications/get-usage-metrics.api.mdx). - The Operate and Tasklist usage metrics endpoints are scheduled for removal in the 8.10 release. Deprecated #### Deprecated: start public process via form in Tasklist With the Camunda 8.8 release, the deprecation of the start public process via form feature is announced. - This SaaS feature is deprecated and does not work with [Tasklist running in V2 mode](/components/tasklist/api-versions.md). This feature will be removed in the 8.10 release. - To continue using this feature with Camunda 8.8, you must run [Tasklist in V1 mode](/components/tasklist/api-versions.md). Change #### Public API definition for greater platform stability To enhance predictability and offer a more stable experience for developers, Camunda introduced the official [public API definition for Camunda 8](/reference/public-api.md). - This document explicitly identifies the components and interfaces that are covered by our semantic versioning guarantees. - By formally defining the public API, Camunda commits to avoiding breaking changes in minor and patch releases for these areas. This empowers you to build on Camunda 8 with greater confidence and reduced maintenance effort. Change #### Camunda Java client and Camunda Spring Boot Starter With the Camunda 8.8 release, Camunda Java Client and Camunda Spring Boot Starter replace the Zeebe Java client and Spring Zeebe SDK. This allows you to use a single consolidated client to interact with Camunda orchestration clusters. The `CamundaClient` replaces the `ZeebeClient`, offering the same functionality and adding new capabilities. The Camunda Spring Boot Starter is based on Spring Boot 3.5, see [version compatibility matrix](/apis-tools/camunda-spring-boot-starter/getting-started.md#version-compatibility). - The new `CamundaClient` uses REST as the default communication protocol, and explicitly uses the configuration option `preferRestOverGrpc=false` to switch to gRPC as the default protocol. (Note: job streaming is only supported via gRPC, but can be used alongside REST for other operations). - If you need to continue using the old `ZeebeClient`, you can use the new version 8.8 `CamundaClient` artifact without issues, as it still contains the related `ZeebeClient` classes. Those classes are marked as deprecated, so you can easily identify code you need to adjust to the `CamundaClient`. - The old `zeebe-client-java` artifact is now relocation-only, so your build system is redirected to the new `camunda-client-java` artifact. We will discontinue the old artifact in version 8.10 and recommend using the new one. - The Zeebe Java client will not be developed further and will only receive bug fixes while version 8.7 is officially supported. :::note - The new Camunda Spring Boot Starter provides the `CamundaClient` when requested. - The `CamundaClient` uses REST as the default communication protocol, while the deprecated `ZeebeClient` still prefers gRPC. - If you want to continue using gRPC by default with the `CamundaClient`, you must explicitly set `camunda.client.prefer-rest-over-grpc: false` in your Spring configuration. - The new `CredentialsProvider` bean creation fails if there is a misconfiguration instead of falling back to a non-operational credentials provider ::: :::info To learn more about migrating to the Camunda Java client, see the [migration guide](/apis-tools/migration-manuals/migrate-to-camunda-java-client.md). ::: Change #### Spring Boot 4.0 support for Camunda Spring Boot Starter and Process Test With the 8.8.9 patch release, dedicated Spring Boot 4.0 based modules are released for the [Camunda Spring Boot Starter](../../../apis-tools/camunda-spring-boot-starter/getting-started.md#spring-boot-40-support) and [Camunda Process Test Spring](../../../apis-tools/testing/getting-started.md?client=spring-sdk#spring-boot-40-support) as drop-in replacements for the default Spring Boot 3.5.x-based modules. You must use these if you want to migrate your application to Spring Boot 4.0. :::note With Camunda 8.9, the default Spring Boot version for the Camunda Spring Boot Starter and Camunda Process Test Spring changes to 4.0. ::: Change #### The Node.js SDK is now the TypeScript SDK With the Camunda 8.8 release, the Node.js SDK now becomes the TypeScript SDK. - The **TypeScript SDK** provides clients for all Camunda 8 APIs. Use it in Node.js environments. - The **Orchestration Cluster API TypeScript client** is a lightweight client for the Camunda 8.8+ Orchestration Cluster REST API. Use it in Node.js or in the browser. To learn more, see the [TypeScript SDK](/apis-tools/typescript/typescript-sdk.md) documentation. ### Connectors Change #### Connector SDK: Core SDK restructuring The internal structure of the Connector SDK has been updated to make the Core SDK more lightweight, with **no dependency on the Camunda client**. Some classes and interfaces have been relocated, which means external connectors may need to be **recompiled** before they can be used with Connector runtime 8.8. This affects the following classes and interfaces previously located in the `io.camunda.document` package: ``` DocumentFactory Document DocumentLinkParameters ``` These classes and interfaces are now located in the `io.camunda.connector.api.document` package. Additionally, the following classes and interfaces from the official Camunda Java client (`io.camunda.client.api.response`) have been **replicated** in the Connector SDK and are now located in the `io.camunda.connector.api.document` package: ``` DocumentMetadata DocumentReference ``` Change #### Connector SDK: Changes to activity logging in inbound connectors The Connector SDK 8.8 introduces a new way to [log activities](/components/hub/organization/manage-clusters/manage-connectors.md#activity-log) in inbound connectors. Objects of the `InboundConnectorContext` class now provide a new overloaded method: ```java void log(Consumer activityBuilderConsumer) ``` This method works with the new `ActivityBuilder` interface. **Usage example:** ```json connector.context() .log( activity -> activity .withSeverity(Severity.INFO) .withTag("Consumer") .withMessage("Successfully processed message") .andReportHealth(Health.up())); ``` The old builder pattern (`Activity.newBuilder()`) is deprecated and will be removed in upcoming releases. The new `ActivityBuilder` interface provides a more flexible and fluent API for logging activities in inbound connectors. Change #### Harmonized Error Contexts for jobError and bpmnError in Connectors With the Camunda 8.8 release, Camunda has harmonized the error context structures returned by the `jobError` and `bpmnError` functions in connectors to align with the corresponding error handling in Camunda core. This ensures that connector errors now follow the same conventions and structure as errors handled by the core engine, supporting greater consistency across process modeling and execution. Updated context structure: - `bpmnError` now returns an entry containing: `errorType`, `errorCode` `errorMessage` `variables` - `jobError` now returns an entry containing : `errorType`, `errorMessage`, `variables`, `retries`, `retryBackoff` Developers and integrators should review any custom connector logic to take full advantage of the new fields and adapt error handling as necessary. :::note These changes do not introduce new fields or richer context, but instead ensure that error data is structured and surfaced consistently between connectors and Camunda core. This makes error handling more predictable, especially for teams working across both domains. ::: ### Data Change #### Elasticsearch and OpenSearch: Index prefixes must differ When upgrading to Camunda 8.8 or setting up 8.8 for the first time, if you run both the Elasticsearch/OpenSearch Exporter and Camunda Exporter on the same Elasticsearch/OpenSearch cluster, their index prefixes must not match, nor be contained within the other. Do not reuse the same prefix for: - Elasticsearch/OpenSearch Exporter indices (legacy exporter): `zeebe.broker.exporters.{elasticsearch|opensearch}.args.index.prefix` - Orchestration Cluster indices: `camunda.data.secondary-storage.{elasticsearch|opensearch}.index-prefix` If these prefixes are identical, or if one prefix includes the other (for example, `custom` and `custom-zeebe`), ILM/ISM policies and wildcard patterns such as `custom*` can target more indices than intended, which may lead to unexpected data loss. For configuration examples and details, see [Helm chart Elasticsearch/OpenSearch indices prefix](../../../self-managed/deployment/helm/configure/database/elasticsearch/configure-elasticsearch-prefix-indices.md). Breaking change #### Elasticsearch and OpenSearch: Single instance With the Camunda 8.8 release, the use of more than one isolated Elasticsearch/OpenSearch instance for exported Zeebe, Operate, and Tasklist data is no longer supported. - If your environment uses multiple Elasticsearch/OpenSearch instances, you must manually migrate the data from each to a single Elasticsearch/OpenSearch cluster before updating to Camunda 8.8. - The migration should target Zeebe, Operate, and Tasklist indices, index templates, aliases, and ILM policies. Change #### Elasticsearch and OpenSearch: Replica default increased to 1 With the Camunda 8.8 release, the default replica count for Camunda indices in Elasticsearch and OpenSearch changes from 0 to 1. This ensures that if an Elasticsearch node goes down, Camunda is not blocked by a temporary outage of the secondary data store. This change increases storage requirements as follows: - **Single-node clusters:** Running with one node turns the cluster state yellow (replicas unassigned). Run at least two master-eligible nodes. - **Multi-node clusters:** Increase disk capacity to at least 2.5× the previously used disk capacity (accounts for watermarks, overhead, and growth). To revert to 0 replicas, set: - YAML: `camunda.database.index.numberOfReplicas: 0` - Environment variable: `CAMUNDA_DATABASE_INDEX_NUMBER_OF_REPLICAS=0` :::info To learn more, see [Elasticsearch changes in Components update 8.7 to 8.8](/versioned_docs/version-8.8/self-managed/upgrade/components/870-to-880.md#elasticsearch). ::: Change #### Camunda Exporter Previously, Camunda web applications used importers and archivers to consume, aggregate, and archive historical data provided by Elasticsearch or OpenSearch (secondary storage) exporters — see [Elasticsearch/OpenSearch](/reference/glossary.md#elasticsearchopensearch). ![87-orchestration-cluster-state](../../img/87-orchestration-cluster-state.png) With the Camunda 8.8 release, a new Camunda Exporter is introduced: - Importing and archiving logic of web components (Tasklist and Operate) is brought closer to the distributed platform (Zeebe). - Benefits include simplified installation, scalability enabled for the web applications, reduced latency when showing runtime and historical data, and reduced data duplication (resource consumption). ![brown-field-without-optimize](../../img/brown-field-orchestration-cluster-without-optimize.png) The new Camunda Exporter helps Camunda achieve a more streamlined architecture, better performance, and improved stability (especially concerning Elasticsearch/OpenSearch secondary storage). :::info To learn more, see the blog post [One Exporter to Rule Them All: Exploring Camunda Exporter](https://camunda.com/blog/2025/02/one-exporter-to-rule-them-all-exploring-camunda-exporter/). ::: #### Harmonized index schema The existing data schema in the secondary storage has been harmonized, to be used by all Camunda components. - This removes unnecessary duplications over multiple indices due to the previous architecture. - With this change, several Operate indices can and will be used by Tasklist. - New indices have been created to integrate Identity into the system. ![Harmonized indices schema](../../img/harmonized-indices-schema.png) ### Deployment Removed #### Helm chart: Separated Ingress deprecation The separated Ingress Helm configuration for Camunda 8 Self-Managed was deprecated in Camunda 8.6 and is removed from the Helm chart in Camunda 8.8. Only the combined Ingress configuration is officially supported. See the [Ingress guide](/self-managed/deployment/helm/configure/ingress/ingress-setup.md) for more information on configuring a combined Ingress setup. - If you are using the recommended Camunda 8 deployment option ([Helm charts](/self-managed/deployment/helm/install/quick-install.md)), the upgrade path from version 8.7 to 8.8 will be straightforward by changing the values file to the new syntax. - New migration guides are also provided to support you when migrating from a previous Camunda version. :::caution Additional upgrade considerations are necessary for deployments that use custom scripts, such as Docker containers, manual installations, or custom-developed Kubernetes deployments. For these deployments, customers can either continue to deploy with their original 8.7 topology and upgrade each component independently, or adopt our Helm chart approach for the upgrade, which allows for unifying the deployment into a single JAR or container executable. ::: Change #### Helm chart: Custom users and clients for Management Identity You can now configure custom users and OAuth2 clients for Management Identity during Helm installation. See [adding users and clients](/self-managed/deployment/helm/configure/authentication-and-authorization/custom-users-and-clients.md) for details on configuring custom users and clients on Management Identity during initial Helm install. :::caution Additional upgrade considerations are required for deployments that use custom environment variables, such as `KEYCLOAK_CLIENTS_2_PERMISSIONS_0_RESOURCE_SERVER_ID`. For these deployments, remove the environment variables that reference users or clients and use the configuration method described in the guide linked above. ::: Breaking change #### Orchestration Cluster: Unified component configuration With the Camunda 8.8 release, the new unified configuration is introduced. - Camunda 8.8 introduces a unified configuration with a shared YAML schema across Orchestration cluster components. This allows you to define all essential cluster and component behavior in a single, centralized configuration system. - In Camunda 8.7 and earlier, managing and configuring core components (Zeebe, Operate, Tasklist, Identity) was done separately. This means some configuration properties have changed or are replaced with new properties. See [Camunda 8.8 property changes](../../../../self-managed/components/orchestration-cluster/core-settings/configuration/configuration-mapping) for more information. :::note Only a partial set of unified configuration properties are introduced in Camunda 8.8, with remaining properties planned for delivery with Camunda 8.9. ::: Deprecated #### Orchestration Cluster: Docker image unification With the Camunda 8.8 release, the [Orchestration Cluster](/self-managed/components/orchestration-cluster/overview.md) is provided as a [unified Docker image](https://hub.docker.com/r/camunda/camunda): `camunda/camunda`. As a result, the following Docker images are deprecated as of Camunda 8.8: - [camunda/zeebe](https://hub.docker.com/r/camunda/zeebe) - [camunda/operate](https://hub.docker.com/r/camunda/operate) - [camunda/tasklist](https://hub.docker.com/r/camunda/tasklist) Deprecated #### Helm chart: Secret management improvements and deprecations With the Camunda 8.8 release, a consistent secret pattern for Helm charts is introduced. The legacy secret configuration is deprecated and will be removed with 8.9, but remains functional during the transition period. :::info See the [secret management guide](/self-managed/deployment/helm/configure/secret-management.md) for migration instructions and examples. ::: Change #### Helm chart: External database for Web Modeler REST API With the Camunda 8.8 release, the configuration for the external database used by the Web Modeler REST API is updated to align with the Identity component's database configuration. - A new value, `webModeler.restapi.externalDatabase`, is now available and mirrors the structure of `identity.externalDatabase`. - To ensure backward compatibility, the existing `webModeler.restapi.externalDatabase.url` field is retained and will take precedence if set. Change #### Helm chart: Default username claim in Web Modeler With the Camunda 8.8 release, the default ID token claim that Web Modeler uses to assign usernames has changed from `name` to `preferred_username`. - This change aligns the configuration with other Camunda 8 components for consistency across the platform. - To continue using the `name` claim, explicitly set `CAMUNDA_IDENTITY_USERNAMECLAIM=name` as an environment variable for the Web Modeler `webapp`. See [Identity / Keycloak configuration](/self-managed/components/hub/configuration/modeler-configuration.md#identity--keycloak-1). Change #### Helm chart: Bitnami Docker repository migration With the Camunda 8.8 release, the Camunda Helm charts are updated to use the new Bitnami Docker repository. :::info See [Bitnami Docker repository migration](/self-managed/upgrade/helm/index.md#bitnami-docker-repository-migration) for migration details. ::: Change #### Bitnami: Alternative container images With the Camunda 8.8 release, alternative container images to the previously used Bitnami open source images are added. This improves security, reliability, and support for Camunda 8 Self-Managed deployments. - These images are hosted by Camunda on `registry.camunda.cloud`. - Beginning with Camunda 8.8, these images are considered the default supported option when deploying Camunda 8 via Helm charts, as they ensure faster delivery of security patches (including CVE fixes) and better alignment with supported environments. - To adopt these images, update your Helm deployment to reference the `values-images-ee.yml` file. :::info Full setup instructions are available in the [installation guide](/self-managed/deployment/helm/install/quick-install.md). ::: New #### Helm chart: Alternative infrastructure methods For production environments, use managed or external services first. If not available, prefer [Kubernetes operators](/self-managed/deployment/helm/configure/operator-based-infrastructure.md) for PostgreSQL, Elasticsearch/OpenSearch, and Keycloak over Bitnami subcharts. Bitnami subcharts remain available for evaluation or proof-of-concept use. Change #### Helm chart: Bitnami subcharts bundled The Bitnami subcharts (PostgreSQL, Keycloak, Elasticsearch, and Common) are bundled directly within the Camunda Helm chart instead of being fetched from external Bitnami repositories at install time. This change reduces the risk of unexpected breaking changes from upstream Bitnami chart updates and gives Camunda full control over the lifecycle of these subcharts. No action is required — existing deployments will continue to work as before when applying the next patch release. New #### Reference architecture: EC2 New EC2 manual and VM blueprint for high availability (HA) multi-AZ clusters. Includes managed OpenSearch, optional Aurora PostgreSQL, dual load balancer pattern, VPN/bastion access, and modular Terraform setup. See [Amazon EC2](/self-managed/deployment/manual/cloud-providers/amazon/aws-ec2.md). New #### Reference architecture: Azure AKS New Azure AKS architecture with a zonal AKS baseline, managed or operator-based data services, unified Ingress and Identity patterns, private networking, and a modular Terraform and Helm workflow. See [Microsoft AKS](/self-managed/deployment/helm/cloud-providers/azure/microsoft-aks/microsoft-aks.md). Change #### Reference architecture: General updates - Managed search (EKS single-region & EC2): OpenSearch upgraded 2.15 → 2.19 (aligns with [supported environments](/reference/supported-environments.md)). - Database layer (EKS & EC2): Aurora PostgreSQL baseline raised 15 → 17 (aligns with [supported environments](/reference/supported-environments.md)). - Identity / global reference architecture: Keycloak now uses Bitnami Premium 26 image (see [OIDC configuration](/self-managed/deployment/helm/configure/authentication-and-authorization/index.md)). - Private access (OpenShift ROSA, EKS, EC2): Added optional VPN pattern (see [EC2 architecture](/self-managed/deployment/manual/cloud-providers/amazon/aws-ec2.md#architecture)). - OpenShift (single & dual region): Validated against OpenShift 4.19 (see [dual region guide](/self-managed/deployment/helm/cloud-providers/openshift/dual-region.md)). - EKS networking: Added alternative NAT gateway strategies (see [EKS Helm guide](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/eks-helm.md)). - High availability: Refreshed dual region deployment material (see [EKS dual region](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/dual-region.md)). - Core diagrams: Updated generic reference architecture visuals for 8.8 Orchestration Cluster changes (see [reference architectures](/self-managed/reference-architecture/reference-architecture.md)). - Terraform module upgrade: AWS EKS module v5 → v6 (review [Terraform EKS setup](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/terraform-setup.md) before upgrading). ### Identity Removed #### Tenant-providing interceptors With the 8.8 release, Camunda announces the removal of tenant-providing interceptors. It is superseded by built-in [tenant management](/versioned_docs/version-8.8/components/identity/tenant.md). Removed #### User storage in Elasticsearch/OpenSearch for Operate or Tasklist With the Camunda 8.8 release, user storage in Elasticsearch/OpenSearch for Operate or Tasklist is no longer supported. You must transition to using [Basic authentication](/self-managed/concepts/authentication/authentication-to-orchestration-cluster.md#basic-authentication) and recreate users in Orchestration Cluster Identity. Removed #### LDAP authentication for Operate or Tasklist With the Camunda 8.8 release, LDAP authentication for Operate or Tasklist is no longer supported. You must transition to use [OIDC or Basic authentication](/self-managed/concepts/authentication/authentication-to-orchestration-cluster.md). ### Marketplace Deprecated #### AWS Marketplace offering for Self-Managed As of October 2025, the Self-Managed AWS Marketplace offering is deprecated and no longer publicly available. Existing customers can continue to use the product until their contracts expire. For future use, refer to the [new AWS Marketplace listing](https://aws.amazon.com/marketplace/pp/prodview-6y664fcnydiqg?sr=0-1&ref_=beagle&applicationId=AWSMPContessa) for more information. ### Modeler Removed #### Removed: Cluster authentication `OAUTH` and `CLIENT_CREDENTIALS` in Web Modeler Self-Managed With the Camunda 8.8 release, the deprecated authentication methods `OAUTH` and `CLIENT_CREDENTIALS` for configured [clusters in Web Modeler Self-Managed](/self-managed/components/hub/configuration/modeler-configuration.md#clusters) are no longer supported. For more information on how to migrate, see the [upgrade guide](/versioned_docs/version-8.8/self-managed/upgrade/components/870-to-880.md#cluster-configuration). Breaking change #### Cluster configuration in Web Modeler Self-Managed The available configuration options for [clusters in Web Modeler Self-Managed](/self-managed/components/hub/configuration/modeler-configuration.md#clusters) now depend on the version of the cluster. For version 8.8 and above, [new configuration options](/self-managed/components/hub/configuration/modeler-configuration.md#additional-configuration-for-cluster-versions--88) are required. For more information on how to modify your existing configuration, see the [upgrade guide](/versioned_docs/version-8.8/self-managed/upgrade/components/870-to-880.md#changed-configuration-options). Deprecated #### Play job-based user tasks With the Camunda 8.8 release, user tasks with a job worker implementation are deprecated and no longer supported in Play from cluster versions 8.8 and above. - [Deprecated Operate endpoints](/self-managed/components/orchestration-cluster/core-settings/concepts/usage-metrics.md) - [Deprecated Tasklist endpoint](/self-managed/components/orchestration-cluster/core-settings/concepts/usage-metrics.md). You should consider migrating to [Camunda user tasks](/components/modeler/bpmn/user-tasks/user-tasks.md#camunda-user-tasks). ### SaaS Removed #### Removed: Starter plan The Camunda SaaS Starter plan is no longer available. - Existing customers using a Starter plan must upgrade to the Enterprise plan or move to the Free plan. - Compare plan features and contact us for advice and an Enterprise plan quote. See [Camunda 8 pricing](https://camunda.com/pricing/?utm_source=docs.camunda.io&utm_medium=referral). --- ## 8.8 Release notes These release notes identify the main new features included in the 8.8 minor release, including [alpha feature releases](/components/early-access/alpha/alpha-features.md). | Minor release date | Scheduled end of maintenance | Changelog(s) | Release blog | Upgrade guides | | ------------------ | ---------------------------- | --------------------------------------------------------------------------- | ------------ | ---------------------------------------------------------------------------------------------- | | 14 October 2025 | 13 April 2027 | [Patch Releases and Changelogs](#technical-changelogs-for-all-88x-releases) | - | [Upgrade guides](/reference/announcements-release-notes/880/whats-new-in-88.md#upgrade-guides) | :::info 8.8 resources - See [release announcements](/reference/announcements-release-notes/880/880-announcements.md) to learn more about supported environment changes and breaking changes or deprecations. - See [What's new in Camunda 8.8](/reference/announcements-release-notes/880/whats-new-in-88.md) for important changes to consider when planning your upgrade from Camunda 8.7. - Refer to the [quality board](https://github.com/orgs/camunda/projects/187/views/15) for an overview of known bugs by component and severity. ::: ### Technical Changelogs for all 8.8.x releases
View GitHub changelogs for all patch releases [Camunda 8.8.24 (05.05.2026)](https://github.com/camunda/camunda/releases/tag/8.8.24)[Camunda 8.8.23 (22.04.2026)](https://github.com/camunda/camunda/releases/tag/8.8.23)[Camunda 8.8.22 (16.04.2026)](https://github.com/camunda/camunda/releases/tag/8.8.22)[Camunda 8.8.21 (30.03.2026)](https://github.com/camunda/camunda/releases/tag/8.8.21)[Camunda 8.8.20 (30.03.2026)](https://github.com/camunda/camunda/releases/tag/8.8.20)[Camunda 8.8.19 (23.03.2026)](https://github.com/camunda/camunda/releases/tag/8.8.19)[Camunda 8.8.18 (20.03.2026)](https://github.com/camunda/camunda/releases/tag/8.8.18)[Camunda 8.8.17 (17.03.2026)](https://github.com/camunda/camunda/releases/tag/8.8.17)[Camunda 8.8.16 (09.03.2026)](https://github.com/camunda/camunda/releases/tag/8.8.16)[Camunda 8.8.15 (04.03.2026)](https://github.com/camunda/camunda/releases/tag/8.8.15)[Camunda 8.8.14 (20.02.2026)](https://github.com/camunda/camunda/releases/tag/8.8.14)[Camunda 8.8.13 (18.02.2026)](https://github.com/camunda/camunda/releases/tag/8.8.13)[Camunda 8.8.12 (17.02.2026)](https://github.com/camunda/camunda/releases/tag/8.8.12)[Camunda 8.8.11 (05.02.2026)](https://github.com/camunda/camunda/releases/tag/8.8.11)[Camunda 8.8.10 (04.02.2026)](https://github.com/camunda/camunda/releases/tag/8.8.10)[Camunda 8.8.9 (06.01.2026)](https://github.com/camunda/camunda/releases/tag/8.8.9)[Camunda 8.8.8 (11.12.2025)](https://github.com/camunda/camunda/releases/tag/8.8.8)[Camunda 8.8.7 (09.12.2025)](https://github.com/camunda/camunda/releases/tag/8.8.7)[Camunda 8.8.6 (02.12.2025)](https://github.com/camunda/camunda/releases/tag/8.8.6)[Camunda 8.8.5 (27.11.2025)](https://github.com/camunda/camunda/releases/tag/8.8.5)[Camunda 8.8.4 (17.11.2025)](https://github.com/camunda/camunda/releases/tag/8.8.4)[Camunda 8.8.3 (05.11.2025)](https://github.com/camunda/camunda/releases/tag/8.8.3)[Camunda 8.8.2 (27.10.2025)](https://github.com/camunda/camunda/releases/tag/8.8.2)[Camunda 8.8.1 (22.10.2025)](https://github.com/camunda/camunda/releases/tag/8.8.1)[Camunda 8.8.0 (09.10.2025)](https://github.com/camunda/camunda/releases/tag/8.8.0)[Connectors 8.8.13 (22.05.2026)](https://github.com/camunda/connectors/releases/tag/8.8.13)[Connectors 8.8.12 (20.05.2026)](https://github.com/camunda/connectors/releases/tag/8.8.12)[Connectors 8.8.11 (05.05.2026)](https://github.com/camunda/connectors/releases/tag/8.8.11)[Connectors 8.8.10 (30.03.2026)](https://github.com/camunda/connectors/releases/tag/8.8.10)[Connectors 8.8.9 (26.03.2026)](https://github.com/camunda/connectors/releases/tag/8.8.9)[Connectors 8.8.8 (26.02.2026)](https://github.com/camunda/connectors/releases/tag/8.8.8)[Connectors 8.8.7 (02.02.2026)](https://github.com/camunda/connectors/releases/tag/8.8.7)[Connectors 8.8.6 (27.01.2026)](https://github.com/camunda/connectors/releases/tag/8.8.6)[Connectors 8.8.5 (07.01.2026)](https://github.com/camunda/connectors/releases/tag/8.8.5)[Connectors 8.8.4 (01.12.2025)](https://github.com/camunda/connectors/releases/tag/8.8.4)[Connectors 8.8.3 (19.11.2025)](https://github.com/camunda/connectors/releases/tag/8.8.3)[Connectors 8.8.2 (06.11.2025)](https://github.com/camunda/connectors/releases/tag/8.8.2)[Connectors 8.8.1 (16.10.2025)](https://github.com/camunda/connectors/releases/tag/8.8.1)[Connectors 8.8.0 (10.10.2025)](https://github.com/camunda/connectors/releases/tag/8.8.0)
## Agentic orchestration Agentic orchestrationAI agentsSaaSSelf-Managed Camunda agentic orchestration allows you to build and orchestrate AI agents within your BPMN-based workflows, enabling human tasks, deterministic rule sets, and AI-driven decisions to collaborate in a robust, end-to-end process. Camunda Agentic orchestration Use the following new features to build and integrate AI agents into your processes: **Feature** **Description** [AI agent connector](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md) Enables AI agents to integrate with an LLM to provide interaction/reasoning capabilities. This connector is designed for use with an ad-hoc sub-process in a feedback loop, providing automated user interaction and tool selection. [MCP Client connector](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-client.md) Connect an AI agent connector to tools exposed by [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) servers. [Ad-hoc tools schema resolver connector](/components/connectors/out-of-the-box-connectors/agentic-ai-ahsp-tools-schema-resolver.md) Can be used independently with other AI connectors for direct LLM interaction. Use this connector if you don’t want to use the AI agent connector but still want to resolve tools for an ad-hoc sub-process or debug tool definitions. [Vector database connector](/components/connectors/out-of-the-box-connectors/embeddings-vector-db.md) Allows embedding, storing, and retrieving LLM embeddings. Use this connector to build AI-based solutions such as context document search, long-term memory for LLMs, and agentic AI interaction. [fromAi() FEEl function](/components/modeler/feel/builtin-functions/feel-built-in-functions-miscellaneous.md) Use in combination with the AI Agent connector. See [AI Agent tool definitions](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-tool-definitions.md). ## APIs & tools APIs & toolsSaaSSelf-Managed ### APIs Use the Orchestration Cluster REST API to interact programmatically with the Orchestration Cluster. - This replaces component APIs (Operate API, Tasklist API, Zeebe API, and much of Identity API) with a single set of endpoints. - This unified API supports both organizational (SaaS) and Self-Managed deployments. - This is now the default and recommended integration point for developers, operators, and automation solutions. - You can also use a Swagger UI to interact with this API. Orchestration Cluster API ### Camunda Java client The new Camunda Java Client replaces the Zeebe Java Client as the official Java library for building process applications that integrate with Camunda 8. This client provides everything you need to programmatically interact with the Orchestration Cluster, whether you are orchestrating microservices, managing human tasks, or visualizing process data. Get started with the Camunda Java client ### Camunda Process Test H2 data layer support Camunda Process Test supports using the [H2 Database Engine](https://www.h2database.com/html/main.html) as the default embedded data layer. - H2 is now automatically provisioned when integrating the Camunda Process Test libraries, eliminating manual database configuration and reducing memory footprint. - H2 support streamlines the developer experience for your Spring Boot and plain Java projects. Process testing is now faster to set up, simpler to maintain, and easier to integrate with your continuous integration workflows. Camunda Process Test ### Camunda Spring Boot Starter The Camunda Spring Boot Starter replaces the Spring Zeebe SDK to simplify interaction between a Spring Boot application and Camunda 8, allowing you to: - Easily integrate process entity management and queries within your workflows. - Seamlessly configure endpoints and authentication via Spring Boot auto-configuration, minimizing boilerplate code. - Rely on an official, standardized approach to guarantee consistency and reduce maintenance costs when upgrading. - Based on Spring Boot 3.5 ([version compatibility matrix](/apis-tools/camunda-spring-boot-starter/getting-started.md#version-compatibility)). Get started with the Camunda Spring Boot Starter ### Process instance tags Introduce optional, immutable **process instance tags** set at creation for lightweight routing, correlation, and future prioritization without inspecting large variable payloads. See [process instance creation](/components/concepts/process-instance-creation.md#tags) and [job workers](/components/concepts/job-workers.md#tags). Process instance creation ### Public API Find out what’s included in Camunda 8's public API, the policies around versioning, and what to expect when upgrading. - The public API is the official contract between Camunda and its users under SemVer. - No breaking changes will be made to the public API in minor or patch releases. - You can safely build on these interfaces with the expectation of stability and backward compatibility. Public API ### Run process segment Manually execute and test individual tasks or segments (connectors, RPA bots, IDP extractions) without running full processes, improving debugging and development efficiency. Run process segment ## Connectors ConnectorsSaaSSelf-Managed ### Ad-hoc tools schema resolver connector This connector implements the tool resolution part of the AI Agent connector, but can also be used independently with other AI connectors for direct LLM interaction. Use this connector if you don’t want to use the AI agent connector but still want to resolve tools for an ad-hoc sub-process or debug tool definitions. Ad-hoc tools schema resolver connector ### AI Agent connector Enable AI agents to integrate with an LLM to provide interaction/reasoning capabilities. Designed for use with an [ad-hoc sub-process](/components/modeler/bpmn/ad-hoc-subprocesses/ad-hoc-subprocesses.md) in a [feedback loop](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-subprocess-example.md), providing automated user interaction and tool selection. AI Agent connector ### Azure Blob Storage connector Store and retrieve documents within Camunda workflows using Azure Blob Storage. Enables seamless document management directly within processes, improves efficiency, and reduces the need for custom integrations. Azure Blob Storage connector ### CSV connector (SaaS) Read, filter, transform, and write CSV data within processes. Reduces technical debt, accelerates development, and broadens integration capabilities with native support for this universal data format. CSV connector ### Google Cloud storage connector Store and retrieve documents directly within Camunda workflows, streamlining document management without custom development. Google Cloud storage connector ### Hubspot Connector Connect your BPMN service with [HubSpot](https://hubspot.com/) and manage your HubsSpot contacts, companies, and deals. HubSpot connector ### MCP Client connector Enable Camunda processes and AI agents to auto-discover and invoke external tools, eliminating hardwired connectors and enabling dynamic, metadata-driven tool integration. MCP client connector ### Vector database connector Enable embedding, storing, and retrieving Large Language Model (LLM) embeddings. Use to build AI-based solutions for your organizations, such as context document search, long-term LLM memory, and agentic AI interaction in combination with the AI Agent connector (RAG). Vector database connector ### Email connector `Message-ID` The Email connector now exposes the `Message-ID` provided by the client in the connector response payload. This allows for improved traceability, easier correlation between sent messages and logs, and better integration with downstream systems that rely on `Message-ID`. :::note This change is backwards-compatible and does not require any action. You can now optionally use the `messageId` field for enhanced tracking when parsing connector responses. ::: ### Intrinsic functions A new `getJson` intrinsic function accepts a document and an optional FEEL expression. It extracts and returns content from a JSON document as an object. - The optional FEEL expression parameter specifies the part that will be extracted from the JSON document content. - If not provided, the whole document is returned as a JSON object. Intrinsic functions ### Job header binding Use the `@Header` annotation to bind a header value (for example, a FEEL expression) to a model class. ### SQL connector Oracle database connection The SQL connector now supports Oracle Database connections. SQL connector ## Console ConsoleSaaSSelf-Managed ### Connector manage and run supports multiple runtimes Connector manage and run in Console now supports management of multiple connector runtime instances. Manage your connectors ### Docker distribution Console is now available as a Self-Managed Docker distribution. Deploy Camunda Console using a Docker image, run Console outside Kubernetes using Docker Compose or other container services, and use Console with Camunda 8 clusters deployed via Docker. Docker Compose developer quickstart ### Regions New regions are available for SaaS clusters on Amazon Web Services: | Type | Region | | :----------------------- | :---------------------------------- | | AWS region | Singapore (ap-southeast-1) | | Secondary backups region | Ireland, Europe (eu-west-1) | | Secondary backups region | Jakarta, Indonesia (ap-southeast-3) | | Secondary backups region | Oregon, North America (us-west-2) | Use these regions to: - Improve overall processing speed and reduce latency if you operate in countries within that region. - Keep cluster data within that country/region to support your local data residency and compliance needs. Regions ### Usage metrics for licence model and tenant Usage metrics now support per-tenant reporting and align with Camunda’s updated licensing model based on the number of tenants. ## Desktop Modeler Desktop ModelerSaaSSelf-Managed ### Settings The new **Settings** window in Desktop Modeler allows you to configure the application and customize your modeling experience. You can select your default execution platform version, as well as other options that were previously only available as flags. Desktop Modeler settings ## Helm chart deployment Helm chartsSelf-Managed ### Alternative container images Camunda provides alternative container images to the previous Bitnami images, offering better security and faster patch delivery. From 8.8, these are the default supported images. - These images are hosted on `registry.camunda.cloud`. - To use them, update your Helm deployment to reference the `values-images-ee.yml` file. Install Camunda with Helm ### Configurable volumes The Helm chart now supports configurable volumes. You can define `PersistentVolumeClaims` or continue using `EmptyDir` through `values.yaml`. ### ExtraVolumeClaimTemplates You can now add custom `extraVolumeClaimTemplates` to the Zeebe/Core StatefulSet by supplying an array of templates in your Helm values file. This allows you to attach additional persistent volumes to each Zeebe/Core pod for use cases such as custom storage or log directories. :::info important Kubernetes does not allow you to change the `volumeClaimTemplates` of an existing StatefulSet. If you add, remove, or modify `extraVolumeClaimTemplates` after initial deployment, you must delete and recreate the StatefulSet (which will also delete the pods) for the changes to take effect. This may require additional planning and data migration steps to avoid data loss. ::: ### Common labels for Camunda resources A new `commonLabels` value is now available and integrates with `camundaPlatform.labels`. This allows you to define mutable labels that are automatically applied to all Camunda resources. By setting `commonLabels`, you can ensure consistent labeling across deployments, making it easier to manage, organize, and identify resources within your Camunda environment. ### Configure Web Modeler replicas The number of replicas for the Web Modeler REST API and web app deployments can be set with new configuration properties: `webModeler.restapi.replicas` and `webModeler.webapp.replicas`, respectively. ### Alternative infrastructure methods (Helm) Production guidance now recommends running PostgreSQL, Elasticsearch / OpenSearch, and Keycloak using **managed or external services first**. If these are not available, use **vendor-supported operators** instead of Bitnami subcharts when moving beyond evaluation environments. Why it matters: - Managed services such as AWS RDS / Aurora, Amazon OpenSearch, and Azure Database for PostgreSQL provide automatic maintenance, scaling, and backups. - Vendor-supported operators (CloudNativePG, Elastic / ECK, Keycloak Operator) automate upgrades, failover, backups, credentials, and certificate rotation when managed services aren’t available or suitable. - Decouples critical data services from the Camunda Helm release cycle and values file structure, reducing blast radius and enabling independent scaling and maintenance. - Enables faster CVE patch adoption and native observability through built-in monitoring and status conditions. - Continue using Bitnami subcharts for local development and proofs of concept, evaluate managed services or vendor-supported operators for staging, and use managed or vendor-supported options for production. Key updates in 8.8 documentation: - A consolidated vendor-supported infrastructure guide with architecture topology and service hand-off points. - Replacement examples for each subchart, including connection secrets, services, and TLS configuration. - Clarified Keycloak Operator behavior: no embedded Service route by default; use Ingress or on-demand port-forwarding for initial administration. ### Reference architecture: Amazon EC2 (manual / VM) New production reference architecture for running the Camunda 8 Orchestration Cluster directly on Amazon EC2 using Terraform to provision networking, IAM, and managed AWS services. Highlights: - Three-node Orchestration Cluster across three Availability Zones for balanced partition placement and quorum resilience. - Managed OpenSearch (baseline 2.19) and optional Aurora PostgreSQL 17 to reduce operational overhead. - Dual load balancer pattern (ALB for HTTP / web apps and REST, NLB for gRPC) supporting protocol-appropriate routing and future mTLS expansion. - Optional VPN / bastion design for secure private subnet administration; security groups follow least-privilege principles. - Modular Terraform stacks for network, security, compute, search, and database, with reusable outputs for automation or configuration management. ### Reference architecture: Azure AKS New Kubernetes reference architecture for Microsoft Azure that combines AKS with Terraform for infrastructure and Helm for platform lifecycle management. Highlights: - Multi-AZ (zonal) AKS baseline with separate node pools for system and workload components when supported in the region. - Flexible data layer options: managed or embedded PostgreSQL and Elasticsearch (Azure Database for PostgreSQL, or embedded Elasticsearch via chart deployment). - Unified Identity and Ingress patterns for the Orchestration Cluster, including shared or split fronts, TLS termination choices, and private endpoints or internal load balancers. - Azure-native networking and security: Private DNS, NSGs, and managed identities mapped to Camunda component requirements. - Terraform + Helm separation enables idempotent infrastructure updates and predictable application rollouts. See the reference architecture guides for full topology diagrams, sizing guidance, and migration considerations. ## Integrations ### Microsoft Teams The new Camunda for Microsoft Teams app allows you to view, claim, and complete Camunda tasks directly in Microsoft Teams. The app integration allows you to: - Start processes from a channel, chat, or the app **Home** tab. - Fill out start forms in Microsoft Teams and submit them to kick off workflows, with optional links to Operate for monitoring. Camunda for Microsoft Teams app :::note The Microsoft Teams integration is released as an [early access](/components/early-access/overview.md) alpha feature to allow you to test and participate in development by sharing feedback before general availability, and is subject to alpha feature limitations. ::: ### SAP Advanced Event Mesh (AEM) connectivity Connect Camunda directly to SAP Advanced Event Mesh (AEM) for event-driven process automation. - New AEM connectors allow you to receive CloudEvents from SAP systems as BPMN messages and publish CloudEvents from Camunda to AEM. - This enables seamless integration with SAP’s event-driven architecture, allowing your Camunda processes to react in real time to business events across SAP BTP, S/4HANA, or ECC. SAP Eventing with SAP Advanced Event Mesh ### ServiceNow Extend the power of your process automation by integrating Camunda with ServiceNow. This integration enables seamless communication between your BPMN workflows and ServiceNow IT Service Management (ITSM), helping you automate routine tasks and accelerate service delivery. The ServiceNow integration allows you to: - Manage ServiceNow data: Create, read, update, and delete records in any ServiceNow table directly from Camunda workflows. - Trigger ServiceNow flows: Start automations built in ServiceNow's Flow Designer as part of an end-to-end process. - Orchestrate ITSM processes: Integrate Camunda tasks with ServiceNow approvals, incidents, and service requests to create unified workflows. ServiceNow integration ## Intelligent document processing (IDP) IDPSaaS ### Bring your own model (BYOM) When using an OpenAI Compatible provider, you can use your own model for document extraction. Any model provider that implements the `/chat/completions` API endpoint is supported. Supported providers are: - **AWS**: Amazon Web Services with Bedrock and Textract (supports both structured and unstructured extraction). - **Azure**: Microsoft Azure with AI Document Intelligence and AI Foundry (unstructured extraction only). - **GCP**: Google Cloud Platform with Vertex AI and Document AI (supports both structured and unstructured extraction). - **OpenAI compatible**: Supports OpenAI and any provider implementing the OpenAI `/chat/completions` API (unstructured extraction only). Document extraction ### Structured data form extraction You can use form-based structured document extraction to capture data from structured documents. - For example, you can use this extraction method for documents with a consistent layout, such as invoices, tax forms (for example, W-2s, VAT declarations), and loan or insurance applications. - Projects can be shared organization-wide, enhancing accessibility to extraction capabilities. Extract structured data ## Migration from Camunda 7 to Camunda 8 Camunda 7 migrationSaaSSelf-Managed ### Data migration tool Use the Data Migrator to copy running process instances from Camunda 7 to Camunda 8. - Copy running process instances (state-preserving). - Copy process variables and their values. - Handle problematic instances gracefully with retry options. - Write custom code to intercept variable migration. Data Migrator ### Diagram Converter The Diagram Converter helps you get a first understanding of migration tasks when moving from Camunda 7 to Camunda 8. It analyzes Camunda 7 model files (BPMN or DMN) and generates a list of tasks required for the migration. It can also automatically convert these files from Camunda 7 format to Camunda 8 format (updating namespaces, XML structures/properties, and simple expression transforms), with a web UI and CLI that outputs XLSX/CSV reports, for prioritization and batch conversion. Diagram Converter ### Code conversion Code conversion utilities provide code mapping tables, conversion patterns, and automatable refactoring recipes to systematically translate Camunda 7 implementation patterns to Camunda 8 equivalents. Code conversion ## Optimize OptimizeSelf-Managed ### Amazon OpenSearch support Camunda 8 Self-Managed now fully supports the use of Amazon OpenSearch with Optimize. ## Orchestration Cluster Orchestration ClusterZeebeOperateTasklistIdentitySaaSSelf-Managed ### Architecture The primary architectural change in 8.8 is the consolidation of the core Zeebe, Operate, Tasklist, and Identity components into the Orchestration Cluster application as a single deployable artifact, distributed as a JAR file or Docker container. This impacts how Camunda 8 is deployed, managed, and scaled. - The Orchestration Cluster (previously automation cluster) is now the core component of Camunda 8. - Use the Orchestration Cluster REST API to interact programmatically with the Orchestration Cluster. - The new unified exporter architecture improves cluster management and data migration, bringing importer and archiving logic of web components (Tasklist and Operate) closer to the distributed platform (Zeebe). The index schema is also harmonized. - The unified configuration for Orchestration Cluster components allows you to define cluster and component behavior. What's new in Camunda 8.8 ### Reference architecture: General updates The 8.8 release cycle includes updates across multiple Self-Managed reference architecture guides and infrastructure baselines. - **Managed search (EKS single-region and EC2)**: Upgraded OpenSearch from 2.15 to 2.19 to align with the latest [supported environments](/reference/supported-environments.md). - **Database layer (EKS and EC2)**: Raised the Aurora PostgreSQL baseline from version 15 to 17. See the updated versions in [supported environments](/reference/supported-environments.md). - **Identity and global architecture**: Standardized Keycloak on the Bitnami Premium 26 image. See [OIDC configuration](/self-managed/deployment/helm/configure/authentication-and-authorization/index.md). - **Private access (OpenShift ROSA, EKS, EC2)**: Documented an optional VPN pattern. See [EC2 architecture](/self-managed/deployment/manual/cloud-providers/amazon/aws-ec2.md#architecture). - **OpenShift (single and dual region)**: Updated validation and guidance for OpenShift 4.19. See [dual region guide](/self-managed/deployment/helm/cloud-providers/openshift/dual-region.md). - **EKS networking**: Added alternative NAT gateway strategies. See [EKS Helm guide](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/eks-helm.md). - **High availability**: Refreshed dual-region operational guides. See [EKS dual region](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/dual-region.md). - **Core diagrams**: Updated generic reference architecture visuals for the unified Orchestration Cluster. See [reference architectures](/self-managed/reference-architecture/reference-architecture.md). - **Terraform module upgrade**: Upgraded the AWS EKS Terraform module from v5 to v6. Review changes in [Terraform EKS setup](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/terraform-setup.md) before upgrading. ### Dynamic activation of ad-hoc sub-processes using job workers Ad-hoc sub-process elements can be activated by job workers: - Define a task in the process model. - When the engine reaches the ad-hoc sub-process, a job is created. Completing this job with a job result lets you define which elements in the ad-hoc sub-process to activate. - Once any flows in the ad-hoc sub-process complete, a new job is created, giving job workers control over what to do next. ### Dynamic partition scaling You can now add new Zeebe partitions to a running cluster to increase capacity without downtime: - New process instances will be started on new partitions immediately. - Existing process instances do not migrate between partitions, so it can take time for the cluster to reach equilibrium. - Existing messages remain on original partitions, potentially causing slight imbalances for message-heavy workloads (future updates will address this) Cluster scaling ### Identity and Management Identity In Camunda 8.8, Orchestration Cluster Identity and Management Identity are two separate components used for Identity management, each with distinct areas of responsibility. - **Identity**: Access and permission management for all Orchestration Cluster components: Zeebe, Operate, Tasklist, and the Orchestration Cluster REST and gRPC API. - **Management Identity**: Continues to manage access for platform components such as Web Modeler, Console, and Optimize. What's new in Camunda 8.8 ### Migrate taken sequence flows flowing to the joining gateway Enhanced migration now supports taken sequence flows leading to joining gateways: - Define migration plans mapping active elements and taken sequence flows - Configure plans via Operate UI or API ### Process instance migration Enhanced process instance migration allows you to solve problems with process definitions and use the latest process improvements. You can now migrate compensation boundary event subscriptions, escalation boundary events, and escalation event subprocesses. Process instance migration ### React to expired messages with a custom exporter You can now use a custom exporter to react to expired messages. - The original message is retrieved from primary storage upon expiration, and the expiry event enhanced with the original message. - Existing functionality remains unchanged, so there is no disruption for current customers or custom exporters. With these updates, developers can subscribe to the expired messages through a custom exporter, examine the event content, and optionally re-publish or handle the message differently. ### Run Orchestration Cluster without secondary storage You can now run Orchestration Cluster in "Zeebe-only" mode without secondary storage: - Ideal for setups not using Query APIs, Operate, or Tasklist - Enable by setting `camunda.database.type=none` - Starts only the required Zeebe components ### User task listeners Task lifecycle management is enhanced with user task listeners, allowing users to react to specific user task lifecycle events. - Process designers can now model task listeners for different events, such as `assigning` and `completing`. - Developers can use the same job infrastructure to activate and complete task listener jobs. - Operations engineers can easily check details of active and completed task listeners within instances, and efficiently resolve task listener incidents. This enhancement streamlines operations and ensures smoother incident handling, improving time to unblock process execution. User task listeners ### Zeebe-managed resilient batch operations All batch operations, such as canceling or resolving incidents in bulk, are now handled by Zeebe instead of Operate. - This change ensures region failovers in the multi-region setup no longer risk losing critical batch commands. - Users will initiate and manage batch operations through the Orchestration Cluster REST API and the Operate UI, but the underlying processing occurs within Zeebe. - By moving batch operations to the core engine, multi-region deployments gain reliability and resilience. ## Robotic Process Automation (RPA) RPASelf-Managed ### Multi-file script support RPA supports multi-file script support to allow you to organize scripts modularly, reuse common automation components, and integrate existing Robot Framework scripts. The execution engine fully supports multi-file scripts and linked resources, improving scalability, maintainability, and flexibility for enterprise automation projects. ### RPA worker offline installer An offline installer package for the [RPA](/components/rpa/overview.md) worker allows installation without internet connectivity. The offline installer removes reliance on external repositories or downloads, ensuring consistent, secure, and hassle-free deployment into air-gapped or restricted environments. ## Web Modeler Web ModelerSaaSSelf-Managed ### Bring your own model (BYOM) BPMN Copilot and FEEL Copilot in the Web Modeler are now available for Camunda Self-Managed customers in a safe, secure way. You can connect these features to your own AI provider and model, even fine-tuning the model yourself to improve its performance and relevance to your organization. ### CI/CD building blocks guide Element templates documentation is improved to support scalable reuse of building blocks, focusing on CI/CD practices for managing templates across teams and environments. Key updates include: - Guidance on integrating element templates and dependencies into CI/CD pipelines - Restructuring the documentation to improve discoverability and usability - Tool-agnostic content covering Web Modeler, dependency management, and local development setups - Moving relevant information from Connectors to the element templates section Element templates in Modeler ### Cluster basic authentication In addition to bearer token authentication, you can now configure Web Modeler in Self-Managed to use Basic authentication for cluster access. - Set the `CAMUNDA_MODELER_CLUSTERS_0_AUTHENTICATION` environment variable value to `BASIC`. - Web Modeler sends a username and password with every request to the cluster. Available authentication methods ### Element template editor onboarding Web Modeler now provides a low-coder friendly UX for creating building blocks. Whether you create an element template from scratch or from a task, you can quickly set and narrow down the properties so process developers using these templates can quickly and confidently wire building blocks together into an E2E process. Generate an element template ### Element template support for all tasks You can now save any configured task as a reusable element template directly from the Web Modeler properties panel: - Save configured tasks (service, user, send, receive, business rule, script tasks, or call activities) as templates. - Edit templates to adjust input/output bindings, validation rules, categories, and more. - Publish templates to your project or organization for reuse. Save tasks as element templates ### Unlock element template fields Element template management is now more flexible for developers and DevOps teams. - You can assign custom semantic IDs and use an intuitive versioning scheme, ensuring templates are portable and retain stable references across different environments. - Template names and file names can be managed independently, and you can quickly import templates using copy and paste, git sync, or CI/CD pipeline. - Safeguards now notify you of ID or version conflicts to prevent accidental overwrites when publishing templates. Manage element templates ### FEEL Copilot Chat with the AI FEEL Copilot for help generating FEEL (Friendly Enough Expression Language) expressions in Web Modeler. FEEL Copilot ### FEEL Playground Use the FEEL Playground to validate and troubleshoot your FEEL expressions when modeling process diagrams in Web Modeler. FEEL Playground ### Git sync Azure DevOps and Bitbucket integration In addition to GitHub and GitLab, Web Modeler now supports integration with Azure DevOps and Atlassian Bitbucket. Git sync ### Low-code process testing You can now save and rerun versioned test scenarios in Web Modeler: - Supports user tasks, connectors, and basic branching logic - Ideal for process developers and CoEs - Enables behavior-driven development with Camunda Process Test Scenarios ### RDBMS support for Oracle and MS SQL in Self-Managed Web Modeler Self-Managed now supports Oracle Database and Microsoft SQL Server for simpler setup and maintenance. Set up Oracle or MS SQL ### Username claim configuration Configure the ID token claim used for usernames via the `CAMUNDA_IDENTITY_USERNAMECLAIM` environment variable to account for differences between identity providers and ensure user-friendly names. ### Version description Use the version **Description** field to track changes alongside the version tag (for example, as a change log or Git commit message). This helps make versioning more intuitive and collaborative, keeps teams aligned, and reduces ambiguity. Web Modeler versioning ## 8.8.0-alpha8 | Release date | Changelog(s) | Blog | | :--------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------- | | 9 September 2025 | [ Camunda 8 core ](https://github.com/camunda/camunda/releases/tag/8.8.0-alpha8)[ Connectors ](https://github.com/camunda/connectors/releases/tag/8.8.0-alpha8) | [Release blog](https://camunda.com/blog/2025/09/camunda-alpha-release-september-2025/) | ### Ad-hoc sub-process dynamic activation using job workers SaaSSelf-ManagedAgentic orchestration Ad-hoc sub-process elements can now be activated by job workers: - Define a task in the process model. - When the engine reaches the ad-hoc sub-process, a job is created. Completing this job with a job result lets you define which elements in the ad-hoc sub-process to activate. - Once any flows in the ad-hoc sub-process complete, a new job is created, giving job workers control over what to do next. :::note The job result also supports fulfilling the completion condition for the ad-hoc sub-process. When this condition is met, the sub-process waits for active children to complete before finishing, unless you explicitly cancel remaining instances in the job result. ::: ### CI/CD building blocks guide SaaSSelf-ManagedModeler Element templates documentation is improved to support scalable reuse of building blocks, focusing on CI/CD practices for managing templates across teams and environments. Key updates include: - Guidance on integrating element templates and dependencies into CI/CD pipelines - Restructuring the documentation to improve discoverability and usability - Tool-agnostic content covering Web Modeler, dependency management, and local development setups - Moving relevant information from Connectors to the element templates section See [element templates in Modeler](/components/modeler/element-templates/about-templates.md) for details. ### Connectors SaaSSelf-ManagedConnectors #### Job header binding Use the `@Header` annotation to bind a header value (for example, a FEEL expression) to a model class. #### SQL connector Oracle database connection The SQL connector now supports Oracle Database connections. See [SQL connector](/components/connectors/out-of-the-box-connectors/sql.md) for more information. :::note You must manually download the Oracle JDBC driver from [Oracle](https://www.oracle.com/database/technologies/appdev/jdbc-downloads.html) due to licensing restrictions. Run the connector in [hybrid mode](/components/connectors/use-connectors-in-hybrid-mode.md) to enable Oracle Database connections. ::: ### Console SaaSSelf-ManagedConsole #### Docker distribution Console is now available as a Self-Managed Docker distribution: - Deploy Camunda Console using a Docker image - Run Console outside Kubernetes using Docker Compose or other container services - Use Console with Camunda 8 clusters deployed via Docker See [Docker Compose developer quickstart](/self-managed/quickstart/developer-quickstart/docker-compose.md) for setup instructions. #### Usage metrics for licence model and tenant Usage metrics now support per-tenant reporting and align with Camunda’s updated licensing model based on the number of tenants. ### Element template support for all tasks SaaSSelf-ManagedModeler You can now save any configured task as a reusable element template directly from the Web Modeler properties panel: - Save configured tasks (service, user, send, receive, business rule, script tasks, or call activities) as templates - Edit templates to adjust input/output bindings, validation rules, categories, and more - Publish templates to your project or organization for reuse See [save tasks as element templates](/components/hub/workspace/modeler/element-templates/save-as-element-templates.md) for details. Additional support includes: - `zeebe:assignmentDefinition` for assigning users or groups - `zeebe:priorityDefinition` for setting task priorities (integer or FEEL expression) - `zeebe:taskSchedule` for templating task scheduling configurations ### Migrate taken sequence flows flowing to the joining gateway Enhanced migration now supports taken sequence flows leading to joining gateways: - Define migration plans mapping active elements and taken sequence flows - Configure plans via Operate UI or API ### Operate and Tasklist API deprecation SaaSSelf-ManagedAPI The Operate and Tasklist APIs are now deprecated in favor of the Orchestration Cluster API for task and process management. :::note The deprecated APIs remain functional in this release but will no longer receive feature updates and will be removed in version 8.10. ::: ### Orchestration Cluster SaaSSelf-ManagedOrchestration Cluster #### Orchestration Cluster Identity **Self-Managed Identity Management**: - Create and manage users, groups, roles, and memberships directly in Identity’s database - Integrate external providers like Keycloak or Microsoft Entra via OIDC - Assign resource-based authorizations using RBAC - Map users, groups, and roles to resources using token claims and application/client mappings - Simplify migration with built-in tools **SaaS enhancements**: - Integrate organizational identity providers for centralized user management - Assign roles, groups, and authorizations per cluster #### Orchestration Cluster scaling Add Zeebe partitions to a running cluster in Self-Managed to increase capacity without downtime: - New partitions start processing tasks immediately - Existing messages remain on original partitions, potentially causing slight imbalances for message-heavy workloads (future updates will address this) #### Run Orchestration Cluster without secondary storage You can now run Orchestration Cluster in "Zeebe-only" mode without secondary storage: - Ideal for setups not using Query APIs, Operate, or Tasklist - Enable by setting `camunda.database.type=none` - Starts only the required Zeebe components ### Web Modeler SaaSSelf-ManagedWeb Modeler #### Cluster Identity compatibility Web Modeler is fully compatible with Camunda 8.8 Orchestration Clusters for a smooth migration. #### Low-code Process Testing You can now save and rerun versioned test scenarios in Web Modeler: - Supports user tasks, connectors, and basic branching logic - Ideal for process developers and CoEs - Enables behavior-driven development with Camunda Process Test #### RDBMS support for Oracle and MS SQL in Self-Managed Web Modeler Self-Managed now supports Oracle Database and Microsoft SQL Server for simpler setup and maintenance. #### Test scenario files You can now save, export, and share test scenarios: - Portable, Git-syncable files enable quick creation of Camunda Process Test scenarios #### Username claim configuration Configure the ID token claim used for usernames via the `CAMUNDA_IDENTITY_USERNAMECLAIM` environment variable to account for differences between identity providers and ensure user-friendly names. ## 8.8.0-alpha7 | Release date | Changelog(s) | Blog | | :------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | | 12 August 2025 | [ Camunda 8 core ](https://github.com/camunda/camunda/releases/tag/8.8.0-alpha7)[ Connectors ](https://github.com/camunda/connectors/releases/tag/8.8.0-alpha7) | [Release blog](https://camunda.com/blog/2025/08/camunda-alpha-release-august-2025/) | ### Camunda 8 API SaaSSelf-ManagedAPI Development continues on the single unified Camunda 8 REST API that consolidates multiple fragmented APIs into a single, coherent interface, simplifying development and improving clarity across Camunda components. ### Process instance tags SaaSSelf-ManagedAPI {#process-instance-tags-alpha7} Introduce optional, immutable **process instance tags** set at creation for lightweight routing, correlation, and future prioritization without inspecting large variable payloads. For a full feature overview see [process instance creation](/components/concepts/process-instance-creation.md#tags) and [job workers](/components/concepts/job-workers.md#tags). ### Camunda 8 Run supports 8.8 architecture Self-ManagedDeveloper Camunda 8 Run now includes Identity, allowing all core applications to run locally in configurations similar to production. This simplifies local development by enabling multiple user authentications and credentials. ### Connectors SaaSConnectors {#connectorsalpha7} #### AI Agent connector - **Hybrid mode/customization**: The AI Agent connector can now be [customized](../../../components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-customization.md) in hybrid mode. This includes an API to define custom memory storage backends apart from the provided ones. - **Added model provider support**: Added support for Azure OpenAI and Google Vertex AI models. To learn more, see [AI Agent connector](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md). #### Azure Blob Storage connector Use the new Azure Blob Storage connector to store and retrieve documents within Camunda workflows using Azure Blob Storage. This enables seamless document management directly within processes, improves efficiency, and reduces the need for custom integrations. To learn more, see [Azure Blob Storage connector](/components/connectors/out-of-the-box-connectors/azure-blob-storage.md). #### CSV connector Use the new CSV connector for SaaS to read, filter, transform, and write CSV data within processes. This reduces technical debt, accelerates development, and broadens integration capabilities with native support for this universal data format. To learn more, see [CSV connector](/components/connectors/out-of-the-box-connectors/csv.md). #### Google Cloud storage connector Use the new Google Cloud storage connector for easy document storage and retrieval directly within Camunda workflows, streamlining document management without custom development. To learn more, see [Google Cloud storage connector](/components/connectors/out-of-the-box-connectors/google-cloud-storage.md). #### MCP Client connector early access Use the new MCP Client connector to allow Camunda processes and AI agents to auto-discover and invoke external tools, eliminating hardwired connectors and enabling dynamic, metadata-driven tool integration. To learn more, see [MCP client](../../../components/connectors/out-of-the-box-connectors/agentic-ai-mcp-client.md). :::note The MCP Client connector is released as an [early access alpha feature](/components/early-access/alpha/alpha-features.md) to allow you to test and participate in development by sharing feedback before general availability, and is subject to alpha feature limitations. ::: #### Vector database connector Improvements are made to the Vector database connector as follows: - Updated the OpenSearch vector store to support non-AWS managed instances. - Added support for OpenAI embedding models. To learn more, see [vector database connector](/components/connectors/out-of-the-box-connectors/embeddings-vector-db.md). #### Fetch latest process definitions The connectors runtime is optimized by fetching only the latest process definition versions from the Orchestration Cluster. This reduces CPU consumption and improves deployment performance, particularly in environments with frequent CI/CD-generated process versions. #### Unlock element template fields [Element template management](/components/connectors/manage-connector-templates.md) is now more flexible for developers and DevOps teams. - You can assign custom semantic IDs and use an intuitive versioning scheme, ensuring templates are portable and retain stable references across different environments. - Template names and file names can be managed independently, and you can quickly import templates using copy and paste, git sync, or CI/CD pipeline. - Safeguards now notify you of ID or version conflicts to prevent accidental overwrites when publishing templates. ### Run process segment SaaSSelf-ManagedAPI This feature allows developers to manually execute and test individual tasks or segments (connectors, RPA bots, IDP extractions) without running full processes, improving debugging and development efficiency. To learn more, see [run process segment](/components/concepts/process-instance-creation.md#run-process-segment) ### Intelligent document processing (IDP) form extraction SaaSIDP You can use form-based structured document extraction to capture data from structured documents. - For example, you can use this extraction method for documents with a consistent layout, such as invoices, tax forms (for example, W-2s, VAT declarations), and loan or insurance applications. - Projects can be shared organization-wide, enhancing accessibility to extraction capabilities. To learn more, see [extract structured data](/components/hub/workspace/modeler/idp/idp-structured-extraction.md). ### Migration to Orchestration Cluster Identity support Self-ManagedSecurity Allows smooth migration from Camunda 8.7 to 8.8 by transferring tenants, roles, and authorizations to the new Orchestration Cluster Identity, minimizing manual administration effort during upgrade. ### RPA multi-file script support RPA Robotic process automation (RPA) now supports multi-file script support, allowing you to organize scripts modularly, reuse common automation components, and integrate existing Robot Framework scripts. The execution engine fully supports multi-file scripts and linked resources, improving scalability, maintainability, and flexibility for enterprise automation projects. ### User task listener metadata and filtering SaaSSelf-ManagedTasklist With this release, user task listener jobs are improved as follows: - Task metadata is now directly embedded in the task listener jobs' properties instead of being exposed as custom headers. This includes attributes such as `assignee`, `dueDate` or `userTaskKey`. - User tasks can now be filtered using partial user task states to understand the current lifecycle state of the user task fully. These improvements simplify job worker development, reduce errors, and enable better observability of the user task lifecycle. ### Unified configuration for the Orchestration Cluster Self-Managed Simplifies configuration by consolidating Operate, Tasklist, and Identity profiles into a unified Camunda 8 Orchestration Cluster application, reducing duplication and complexity for easier deployment and management. ### Zeebe-managed resilient batch operations Self-ManagedZeebe All batch operations, such as canceling or resolving incidents in bulk, are now handled by Zeebe instead of Operate. - This change ensures region failovers in the multi-region setup no longer risk losing critical batch commands. - Users will initiate and manage batch operations through the Orchestration Cluster REST API and the Operate UI, but the underlying processing occurs within Zeebe. - By moving batch operations to the core engine, multi-region deployments gain reliability and resilience. ## 8.8.0-alpha6 | Release date | Changelog(s) | Blog | | :----------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------- | | 8 July 2025 | [ Camunda 8 core ](https://github.com/camunda/camunda/releases/tag/8.8.0-alpha6)[ Connectors ](https://github.com/camunda/connectors/releases/tag/8.8.0-alpha6) | [Release blog](https://camunda.com/blog/2025/07/camunda-alpha-release-july-2025/) | ### Bitbucket sync SaaSWeb Modeler Camunda 8 now supports integration with [Atlassian Bitbucket Cloud](https://bitbucket.org/product/), in addition to GitHub, GitLab, and Azure DevOps. - This helps customers who use Jira for their development processes. - Organization owners and administrators can connect their Web Modeler process applications to Bitbucket Cloud, allowing users to keep their Web Modeler, Desktop Modeler, and official version control projects synced. To learn more, see [Git sync](/components/hub/workspace/manage-projects/git-sync.md?platform=bitbucket). ### Camunda 8 REST API renamed to Orchestration Cluster REST API SaaSSelf-ManagedAPI The Camunda 8 REST API is now called the **Orchestration Cluster REST API**. - This name better reflects its role as a unified REST API for interacting with entities in a [Camunda 8 Orchestration Cluster](/reference/glossary.md#orchestration-cluster), such as processes, tasks, and variables. - The functionality and structure of the API remain unchanged. The name change improves clarity and onboarding across Camunda documentation and resources. To learn more, see [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). ### Connectors SaaSSelf-ManagedConnectors {#connectorsalpha6} #### AI Agent connector - **Structured outputs/JSON mode**: [Configurable response formats](../../../components/connectors/out-of-the-box-connectors/agentic-ai-aiagent-task.md#response) allow you to choose whether the connector returns plain text or JSON for downstream processing. For some models, you can define a JSON schema for returned data. - **Conversation history storage**: History can now be stored in [Camunda's document storage](../../../components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md#memory) rather than in process variables—allowing longer histories without process variable size limits. To learn more, see [AI Agent connector](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md). #### Intrinsic functions A new `getJson` intrinsic function accepts a document and an optional FEEL expression. It extracts and returns content from a JSON document as an object. - The optional FEEL expression parameter specifies the part that will be extracted from the JSON document content. - If not provided, the whole document is returned as a JSON object. To learn more, see [intrinsic functions](/components/connectors/advanced-topics/intrinsic-functions.md). ### Dynamic partition scaling Self-ManagedZeebe You can now add new Zeebe partitions to a running cluster. - Scaling can be performed concurrently when the cluster is running, with zero downtime. - New process instances also start on new partitions, distributing cluster load evenly across partitions. - Process instances do not migrate between partitions, so it can take time for the cluster to reach equilibrium. - New partitions do not take part in correlating messages/signals, except for message/signal start events. To learn more, see [cluster scaling](/self-managed/components/orchestration-cluster/zeebe/operations/cluster-scaling.md). :::caution This feature is not yet fully compatible with backup/restore. ::: ### Helm charts Self-Managed #### Alternative container images Camunda now provides alternative container images to the previously used Bitnami images. These images are hosted on `registry.camunda.cloud`. - From version **8.8**, these are the default supported images, offering better security and faster patch delivery. - To use them, update your Helm deployment to reference the `values-images-ee.yml` file. See the [installation guide](/self-managed/deployment/helm/install/quick-install.md) for details. #### Configurable volumes The Helm chart now supports configurable volumes. You can define `PersistentVolumeClaims` or continue using `EmptyDir` through `values.yaml`. ### Singapore region available for SaaS on Amazon Web Services SaaS A new Singapore (ap-southeast-1) region is now available for SaaS clusters on Amazon Web Services. Use this region to: - Improve overall processing speed and reduce latency if you operate in Singapore and Southeast Asian (SEA) countries. - Keep cluster data within Singapore to support your local data residency and compliance needs. To learn more about supported SaaS regions, see [regions](/components/saas/regions.md). ### Tasklist uses the Orchestration Cluster REST API SaaSSelf-ManagedTasklist Tasklist now uses the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md), replacing the soon-to-be-deprecated Tasklist V1 API. - This change improves compatibility with Camunda 8 RDBMS support and continues to work with Elasticsearch/OpenSearch. - It ensures consistent functionality, better performance, and access to new features—without breaking existing workflows. ### User task listener types SaaSSelf-ManagedTasklist New user task listener types are available: #### `creating` event This event triggers before a user task is created. | Functionality | Description | | :----------------------------- | :--------------------------------------------------------------------------------------------------------------- | | Configurable creation listener | Executes logic before a task appears to users. Task is visible only after all listener jobs finish. | | Controlled task initialization | The creation continues only after listeners succeed. Incidents are raised if logic fails, enabling safe retries. | | Operate UI insights | A “Creating” event appears in the listener tab in Operate. Incidents are flagged for troubleshooting. | | Assign user task | Assign a task programmatically during creation, useful when assignment depends on an external system. | #### `canceling` event This event triggers when a user task is canceled (e.g., by a boundary event or process termination). | Functionality | Description | | :-------------------------------- | :-------------------------------------------------------------------------------------------------------- | | Configurable cancelation listener | Executes logic when a task is canceled. Allows inspection or modification of task data before completion. | | Consistent lifecycle control | Cancelation waits for listener logic to complete. Failures can raise incidents for safe retry. | | Operate UI insights | A “Canceling” event is shown in the listener tab. Incidents are highlighted for visibility. | To learn more, see [user task listeners](/components/concepts/user-task-listeners.md). ### Documentation SaaSSelf-Managed #### Get started updates The [getting started](/guides/getting-started-example.md) documentation now includes: - Example BPMN files and Spring Boot/NodeJS starter projects. - Practical code snippets, such as payment handling. - Updated code to match recent Camunda versions. - Annotations in BPMN files to guide usage and explain results. #### Public API The new [Public API](/reference/public-api.md) documentation outlines what’s included in Camunda 8's public API, the policies around versioning, and what to expect when upgrading. - The public API is the official contract between Camunda and its users under SemVer. - No breaking changes will be made to the public API in minor or patch releases. - You can safely build on these interfaces with the expectation of stability and backward compatibility. ## 8.8.0-alpha5 | Release date | Changelog(s) | Blog | | :----------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------- | | 10 June 2025 | [ Camunda 8 core ](https://github.com/camunda/camunda/releases/tag/8.8.0-alpha5)[ Connectors ](https://github.com/camunda/connectors/releases/tag/8.8.0-alpha5) | [Release blog](https://camunda.com/blog/2025/06/camunda-alpha-release-june-2025/) | ### Agentic orchestration The following [agentic orchestration](/components/agentic-orchestration/agentic-orchestration-overview.md) features are available in this alpha release: #### AI Agent connector ConnectorsWeb ModelerDektop Modeler The AI Agent connector enables AI agents to integrate with an LLM to provide interaction/reasoning capabilities. This connector is designed for use with an [ad-hoc sub-process](/components/modeler/bpmn/ad-hoc-subprocesses/ad-hoc-subprocesses.md) in a [feedback loop](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md#feedback-loop), providing automated user interaction and tool selection. This connector provides: - Support for different LLM providers, such as OpenAI or Anthropic. - Conversational/short-term memory handling to enable feedback loops and follow-up tasks. - Tool resolution and orchestration through tools defined in an ad-hoc sub-process. To learn more about this connector, see [AI Agent connector](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md). #### Vector database connector Connectors The vector database connector allows embedding, storing, and retrieving Large Language Model (LLM) embeddings. This enables building AI-based solutions for your organizations, such as context document search, long-term LLM memory, and agentic AI interaction in combination with the AI Agent connector (RAG). To learn more about this connector, see [vector database connector](/components/connectors/out-of-the-box-connectors/embeddings-vector-db.md). ### Azure DevOps integration for Git sync Web Modeler Camunda now supports [an integration with Azure DevOps](/components/hub/workspace/manage-projects/git-sync.md) that allows for direct syncing with Azure repositories. ### FEEL Copilot Web Modeler Chat with the AI FEEL Copilot for help generating FEEL (Friendly Enough Expression Language) expressions in Web Modeler. To learn more about this feature, see [FEEL Copilot](/components/early-access/alpha/feel-copilot/feel-copilot.md). :::note The FEEL Copilot is released as an [early access alpha feature](/components/early-access/alpha/alpha-features.md) to allow you to test and participate in development by sharing feedback before general availability, and is subject to alpha feature limitations. ::: ### FEEL Playground Web Modeler Use the FEEL Playground to validate and troubleshoot your FEEL expressions when modeling process diagrams in Web Modeler. To learn more about this feature, see [FEEL Playground](/components/modeler/feel/feel-playground.md). ### Identity Identity Self-ManagedSaaS Camunda’s new Identity service enhances authentication and authorization for Self-Managed and SaaS environments: | Feature/enhancement | Description | | :-------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Self-Managed Identity management | Admins can create and manage users, groups, roles, and memberships directly in the Identity database. | | OIDC integration | Integrate external identity providers (IdP) such as Keycloak and Microsoft Entra. | | Role-based access control (RBAC) | Assign roles and group permissions on a per-resource basis for fine-grained access control. Supported resources include Authorization, Claim Mapping Rules, Messages, Batches, Applications, Tenants, Deployments, Process Definitions, Decision Definitions, and more. | | Flexible mapping | In Self-Managed environments, map users, groups, and roles to resource authorizations and tenants. Leverage OIDC token claims and application/client mappings to streamline permission assignments. | | Migration support | Simplified migration tools make it easy for existing customers to transition to the new service. | | Organizational Identity | In SaaS environments, integrate your own IdP to manage organizational users and assign resources cluster-by-cluster. | | Cluster-specific Roles and Groups | In SaaS environments, manage distinct roles, groups, and authorizations for each cluster independently. | #### Identity management for SaaS clusters SaaS [Orchestration Cluster Identity](/versioned_docs/version-8.8/components/identity/identity-introduction.md) is now available for SaaS clusters. Starting with this alpha version, you can manage groups, roles, and authorizations at the cluster level. The following known limitations apply for this alpha version release: | Known limitation | Description | | :------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Authorizations | Before enabling authorization checks in the cluster settings, users must assign themselves to the admin role in Identity for the Orchestration Cluster.**Note:** As authorizations are disabled by default, no changes are required for initial access.Authorizations cannot be assigned to users via the UI, only to groups.Authorizations are not correctly loaded in the UI. | | Navigation, Notifications, and Logout | Links to the other Camunda components in the Orchestration Cluster web applications (Operate, Tasklist, Identity) do not currently work.SaaS notifications are not displayed in Orchestration Cluster components.Log out from Orchestration Cluster web applications is not fully functional. | | Documentation | Documentation is incomplete. | #### Identity management for Helm Chart setups Self-Managed [Orchestration Cluster Identity](/versioned_docs/version-8.8/self-managed/components/orchestration-cluster/identity/overview.md) is now available for OIDC setups in [Helm chart deployments](/self-managed/deployment/helm/install/quick-install.md). Starting with this alpha version, you can configure the Orchestration Cluster components to use the identity provider (IdP) of your choice and enable single sign-on (SSO). The following known limitations apply for this alpha version release: | Known limitation | Description | | :--------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Authorizations | Authorizations cannot be assigned to users via the UI, only to groups.Authorizations are not correctly loaded in the UI. | | Logout | Log out from Orchestration Cluster web applications is not fully functional. | | Documentation | Documentation is incomplete. | ### React to expired messages with a custom exporter Camunda now introduces a mechanism to react to expired messages. The original message is retrieved from the primary storage upon message expiration and the expiry event is enhanced with the original message. Existing functionality remains unchanged, so there is no disruption for current customers or custom exporters. With these updates, developers can subscribe to the expired messages through a custom exporter, examine the event content, and optionally re-publish or handle the message differently. By providing an enhanced event and re-publish flow, this feature strengthens reliability and transparency in business processes without requiring a major upgrade or modifying existing exporters. ### RPA worker offline installer This feature introduces an offline installer package for the Camunda [RPA](/components/rpa/overview.md) worker, allowing installation without internet connectivity. The offline installer removes reliance on external repositories or downloads, ensuring consistent, secure, and hassle-free deployment into air-gapped or restricted environments. ### Tasklist frontend application migration to use Orchestration Cluster REST API Tasklist The Tasklist frontend application is transitioning from the soon-to-be-deprecated Tasklist V1 API to the unified Orchestration Cluster REST API. - This ensures Tasklist remains fully compatible with Camunda 8’s new RDBMS support while continuing to work seamlessly with Elasticsearch and OpenSearch. - You can expect consistent functionality across different data layers, improved performance, and access to new platform features - all without losing existing capabilities or disrupting task management workflows. ## 8.8.0-alpha4 | Release date | Changelog(s) | Blog | | :----------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------- | | 13 May 2025 | [ Camunda 8 core ](https://github.com/camunda/camunda/releases/tag/8.8.0-alpha4)[ Connectors ](https://github.com/camunda/connectors/releases/tag/8.8.0-alpha4) | [Release blog](https://camunda.com/blog/2025/05/camunda-alpha-release-may-2025/) | ### Camunda Process Test H2 data layer support Self-ManagedZeebe {#h2support} Camunda Process Test now supports using the [H2 Database Engine](https://www.h2database.com/html/main.html) as the default embedded data layer. - H2 is now automatically provisioned when integrating the Camunda Process Test libraries, eliminating manual database configuration and reducing memory footprint. - H2 support streamlines the developer experience for your Spring Boot and plain Java projects. Process testing is now faster to set up, simpler to maintain, and easier to integrate with your continuous integration workflows. To learn more about Camunda Process Test, see [Camunda Process Test](/apis-tools/testing/getting-started.md). ### Connector manage and run supports multiple runtimes SaaSSelf-ManagedConsole {#connector-management} Connector manage and run in Console now supports management of multiple connector runtime instances. To learn more about this feature, see [manage your connectors](/components/hub/organization/manage-clusters/manage-connectors.md). ### Connectors SaaSSelf-ManagedConnectors {#connectorsalpha4} #### Email connector {#emailalpha4} The Email connector now exposes the `Message-ID` provided by the client in the connector response payload. This allows for improved traceability, easier correlation between sent messages and logs, and better integration with downstream systems that rely on `Message-ID`. For example: ```json { "sent": true, "subject": "Email subject" "messageId": "" } ``` :::note This change is backwards-compatible and does not require any action. You can now optionally use the `messageId` field for enhanced tracking when parsing connector responses. ::: #### Hubspot connector {#hubspotalpha4} Hubspot connector enhancements include: - The [Get contact by ID](/components/connectors/out-of-the-box-connectors/hubspot.md#get-contact-by-id) operation now supports the retrieval of properties and default contact properties. - The new [Enroll contact to workflow](/components/connectors/out-of-the-box-connectors/hubspot.md#enroll-contact-to-workflow) operation allows you to enroll contacts into a specified workflow. To learn more about this connector, see [HubSpot connector](/components/connectors/out-of-the-box-connectors/hubspot.md). ### Desktop Modeler settings Self-ManagedDesktop Modeler The new **Settings** window in Desktop Modeler allows you to configure the application and customize your modeling experience. You can select your default execution platform version, along with other options that were previously only available as flags. To learn more about these settings, see [Desktop Modeler settings](/components/modeler/desktop-modeler/settings/settings.md). #### Version descriptionSaaSSelf-ManagedWeb Modeler Use the version **Description** field to track changes alongside the version tag (for example, as a change log or Git commit message). This helps make versioning more intuitive and collaborative, keeps teams aligned, and reduces ambiguity. To learn more about versioning your diagrams, see [versions](components/hub/workspace/modeler/modeling/versions.md). ### Web Modeler cluster Basic authentication Self-ManagedWeb Modeler As well as bearer token and client credentials authentication, you can now configure Web Modeler in Self-Managed to use Basic authentication for cluster access. - To use Basic authentication, set the `CAMUNDA_MODELER_CLUSTERS_0_AUTHENTICATION` environment variable value to `BASIC`. - Web Modeler sends a username and password with every request to the cluster. To learn more about Basic authentication, see [available authentication methods](/self-managed/components/hub/configuration/modeler-configuration.md#available-authentication-methods). ## 8.8.0-alpha3 | Release date | Changelog(s) | Blog | | :------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--- | | 08 April 2025 | [ Camunda 8 core ](https://github.com/camunda/camunda/releases/tag/8.8.0-alpha3)[ Connectors ](https://github.com/camunda/connectors/releases/tag/8.8.0-alpha3) | - | ### Ad-hoc sub-process activation API & completion configuration SaaSSelf-Managed {#adhocsubprocess} Agentic process orchestration enhancements include: - An optional `completionCondition` boolean expression for ad-hoc sub-processes that is evaluated every time an inner element is completed. A `cancelRemainingInstances` boolean attribute can also be configured to influence the ad-hoc sub-process behavior when the completion condition is met. - An [Activate activities within an ad-hoc sub-process](/apis-tools/orchestration-cluster-api-rest/specifications/activate-ad-hoc-sub-process-activities.api.mdx) API used to activate selected activities within an ad-hoc sub-process. To learn more about these features, see [ad-hoc sub-processes](/components/modeler/bpmn/ad-hoc-subprocesses/ad-hoc-subprocesses.md). ### Advanced User Task Listeners for Updating Events SaaSSelf-ManagedTasklist {#listeners} Advanced User Task Listeners for Updating Events allow you to define listeners that trigger whenever certain task properties or variables change. - These listeners generate jobs similar to other event-based task listeners, granting direct access to task data as well as the ability to accept or roll back updates (in certain scenarios). - Operators can also view, manage, and resolve incidents caused by these listeners in Operate, ensuring a unified and transparent approach to handling task changes. To learn more about this feature, see [advanced user task listeners for updating events](/components/concepts/user-task-listeners.md). ### HubSpot connector SaaSSelf-ManagedConnectors {#hubspot} Use the new outbound HubSpot connector to connect your BPMN service with [HubSpot](https://hubspot.com/) and manage your HubsSpot contacts, companies, and deals. This connector supports the following operations: - Contacts: Get all contacts, Get contact by id, Get multiple contacts by id, Search contact, Create contact, Update contact, Delete contact. - Companies: Get all companies, Get company by id, Search company, Get all contacts of a company, Add contact to company, Remove contact from company, Create company, Delete company. - Deals: Get all deals, Get deal by id, Search deal, Delete deal. To learn more about this connector, see [HubSpot connector](/components/connectors/out-of-the-box-connectors/hubspot.md). ## 8.8.0-alpha2 | Release date | Changelog(s) | Blog | | :------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------- | | 11 March 2025 | [ Camunda 8 core ](https://github.com/camunda/camunda/releases/tag/8.8.0-alpha2)[ Connectors ](https://github.com/camunda/connectors/releases/tag/8.8.0-alpha2) | [Release blog](https://camunda.com/blog/2025/03/camunda-alpha-release-march-2025/) | ### Camunda Spring Boot Starter for the Camunda 8 REST API A Spring Boot Starter is provided for the Orchestration Cluster REST API to unify process management, user tasks, and identity features under a single dedicated starter. This simplifies the interaction between a Spring Boot application and Camunda 8, allowing you to: - Easily integrate process entity management and queries within your workflows. - Seamlessly configure endpoints and authentication via Spring Boot auto-configuration, minimizing boilerplate code. - Rely on an official, standardized approach to guarantee consistency and reduce maintenance costs when upgrading. - Based on Spring Boot 3.5 ([version compatibility matrix](/apis-tools/camunda-spring-boot-starter/getting-started.md#version-compatibility)). To learn more about this feature, see the [Camunda Spring Boot Starter](/apis-tools/camunda-spring-boot-starter/getting-started.md). ### Camunda 8 Run authentication updates Camunda 8 Run no longer requires authentication when working with APIs. Authentication and authorizations can be optionally enabled to allow requests using Basic authentication, and to test authorizations and permissions. To learn more about this feature, see the [API documentation](/self-managed/quickstart/developer-quickstart/c8run/configuration.md#use-camunda-apis) for Camunda 8 Run. ### Identity management updates SaaSSelf-Managed The [Identity service](/self-managed/components/management-identity/overview.md) is enhanced to deliver greater flexibility, control, and security for both Self-Managed and SaaS users. These updates are part of our broader effort to streamline the platform’s architecture. #### Cluster-level identity management Identity settings will be configured at the Orchestration Cluster level, allowing each cluster to have unique OIDC configurations. This cluster-specific setup empowers organizations to assign different identity providers (IdPs) across clusters, offering improved control over permissions and user group mappings, resulting in a more streamlined and efficient configuration experience. For SaaS customers, identity management in Camunda 8.8 remains consistent with Camunda 8.7, allowing the attachment of a single IdP per organization. However, cluster-level identity capabilities are provided for SaaS as well as Self-Managed. This means that user groups, roles, and access permissions can now be managed at the cluster level, giving SaaS customers the same granular access control as in Self-Managed environments. #### Decoupling from Keycloak Self-Managed Built-in Keycloak integration in Self-Managed is removed, allowing customers to use any compatible IdP. - Keycloak remains fully supported as an external option. For cluster-level identity management it must be connected as an external OIDC provider moving forward. - OpenID Connect (OIDC) remains the standard for seamless integration with chosen IdPs. #### Resource-based authorizations Resource-based authorizations control read and write permissions per specific resource. See [manage users](/components/hub/organization/manage-members/manage-users.md#resource-based-authorizations). - Admin users retain full access, but regular users must be granted specific permissions to perform operations or view resources. - For organizations that build custom front-ends and access Camunda via API, users or clients with API permissions can still access data through the V2 API, respecting their resource permissions. ## 8.8.0-alpha1 | Release date | Changelog(s) | Blog | | :--------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------ | | 11 February 2025 | [ Camunda 8 core ](https://github.com/camunda/camunda/releases/tag/8.8.0-alpha1)[ Connectors ](https://github.com/camunda/connectors/releases/tag/8.8.0-alpha1) | [Release blog](https://camunda.com/blog/2025/02/camunda-alpha-release-february-2025/) | :::note Some features available in 8.8.0-alpha1 were originally released in [8.7.0 alphas](/reference/announcements-release-notes/870/870-release-notes.md). These features are longer planned for release in 8.7.0. For more information, see the Camunda 8.7 and 8.8 [release update blog](https://camunda.com/blog/2025/01/camunda-87-88-release-update/). ::: ### User task listeners SaaSSelf-ManagedModelerOperate Task lifecycle management is enhanced with user task listeners, allowing users to react to specific user task lifecycle events. - Process designers can now model task listeners for different events, such as `assigning` and `completing`. - Developers can use the same job infrastructure to activate and complete task listener jobs. - Operations engineers can easily check details of active and completed task listeners within instances, and efficiently resolve task listener incidents. This enhancement streamlines operations and ensures smoother incident handling, improving time to unblock process execution. To learn more about this feature, see [user task listeners](/components/concepts/user-task-listeners.md). ### Orchestration Cluster REST API Query API API You can now use a single Query API in the Orchestration Cluster REST API to find process and decision data instead of using multiple component APIs. For example, send a request to the [Search decision definitions](/apis-tools/orchestration-cluster-api-rest/specifications/search-decision-definitions.api.mdx) endpoint to search for decision definitions. New Query API endpoints are added as follows: - Decision definitions - Decision instances - Decision requirements - Flownode instances - Incidents - Process definitions - Process instances - User tasks - Variables To learn more about these endpoints, see the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md#query-api) documentation. ### Amazon OpenSearch Optimize support Self-ManagedOptimize Camunda 8 Self-Managed now fully supports the use of Amazon OpenSearch with Optimize. ### Process instance migration SaaSSelf-ManagedZeebe Enhanced process instance migration allows you to solve problems with process definitions and use the latest process improvements. You can now migrate the following: - Compensation boundary event subscriptions - Escalation boundary events - Escalation event subprocesses To learn more about migration, see [process instance migration](/components/concepts/process-instance-migration.md). ### Camunda Exporter Self-Managed A new Camunda Exporter brings the importer and archiving logic of web components (Tasklist and Operate) closer to the distributed platform (Zeebe). The index schema is also being harmonized. To learn more about this feature, see the [Camunda Exporter documentation](/self-managed/components/orchestration-cluster/zeebe/exporters/camunda-exporter.md). ### Backup and restore improvements Self-Managed Camunda backups have been improved and made easier to use. The web application backups are now merged together under one endpoint. ### Connector Runtime SaaSSelf-ManagedConnectors #### Spring SDK and Orchestration Cluster REST API Migration The Connectors experience is enhanced with the migration from the Spring Zeebe to the Orchestration Cluster REST API, and the removal of dependency on the Operate client. --- ## What's new in Camunda 8.8 Important changes in Camunda 8.8 you should consider when upgrading from Camunda 8.7. ## Why upgrade to Camunda 8.8? Camunda 8.8 introduces important architectural changes and enhancements to simplify deployment, improve maintainability, and empower both operations teams and developers. Upgrading to Camunda 8.8 delivers significant benefits: - **Agentic orchestration**: [Build and orchestrate AI agents](#agentic-orchestration) within your BPMN-based workflows, enabling human tasks, deterministic rule sets, and AI-driven decisions to collaborate in a robust, end-to-end process. - **Unified platform**: Camunda 8.8 combines core components into a single [Orchestration Cluster](#orchestration-cluster), reducing system complexity, centralizing operations, and simplifying both deployment and maintenance. - **Enhanced productivity**: This upgrade introduces streamlined [identity and access management](#identity), a consolidated configuration model, and modernized & consolidated [APIs and SDKs](#apis-and-tools), making development, integration, and permission handling faster and more intuitive. - **Increased efficiency**: The new [unified exporter architecture](#unified-exporter) improves performance with accelerated data visibility in Operate and Tasklist, as well as public query APIs. It also enables easier operation and administration, and improves resilience when deploying across multiple data centers. :::info To learn more about the benefits of upgrading to Camunda 8.8, see the blog posts [streamlined deployment with Camunda 8.8](https://camunda.com/blog/2025/03/streamlined-deployment-with-camunda-8-8/) and [introducing enhanced Identity Management in Camunda 8.8](https://camunda.com/blog/2025/03/introducing-enhanced-identity-management-in-camunda-88/). ::: ## Summary of important changes Important changes introduced in Camunda 8.8 are summarized as follows: **What's new/changed** **Summary** [Agentic orchestration](#agentic-orchestration) Improved features and new connectors for building and orchestrating AI agents within your BPMN-based workflows. [Orchestration Cluster](#orchestration-cluster) The Orchestration Cluster (previously automation cluster) is now the core Camunda 8 component. [Identity, authentication, and authorization](#identity) Identity management is now split into two scopes for improved access management, performance, and flexible integration with any OIDC-compatible Identity Provider:**Identity**: Manages authentication and fine-grained authorizations for the Orchestration Cluster and its APIs. **Management Identity**: Controls access for Web Modeler, Console and Optimize. [APIs & tools](#apis-and-tools) New and changed APIs & tools are introduced in Camunda 8.8. :::info - See [release announcements](/reference/announcements-release-notes/880/880-announcements.md) and [release notes](/reference/announcements-release-notes/880/880-release-notes.md) for more detail on what's included in Camunda 8.8. - Ready to upgrade? See the [upgrade guides](#upgrade-guides) to learn more about upgrading from Camunda 8.7 to 8.8. ::: ## Agentic orchestration Camunda agentic orchestration allows you to build and orchestrate AI agents within your BPMN-based workflows, enabling human tasks, deterministic rule sets, and AI-driven decisions to collaborate in a robust, end-to-end process. Agentic orchestration Use the following new features to build and integrate AI agents into your processes: **Feature** **Description** [AI agent connector](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md) Enables AI agents to integrate with an LLM to provide interaction/reasoning capabilities. This connector is designed for use with an ad-hoc sub-process in a feedback loop, providing automated user interaction and tool selection. [MCP Client connector](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-client.md) Connect an AI agent connector to tools exposed by [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) servers. [Ad-hoc tools schema resolver connector](/components/connectors/out-of-the-box-connectors/agentic-ai-ahsp-tools-schema-resolver.md) Can be used independently with other AI connectors for direct LLM interaction. Use this connector if you don’t want to use the AI agent connector but still want to resolve tools for an ad-hoc sub-process or debug tool definitions. [Vector database connector](/components/connectors/out-of-the-box-connectors/embeddings-vector-db.md) Allows embedding, storing, and retrieving LLM embeddings. Use this connector to build AI-based solutions such as context document search, long-term memory for LLMs, and agentic AI interaction. :::info To learn more about agentic orchestration, see the blog post [Enterprise-Grade Agentic Automation Is Here](https://camunda.com/blog/2025/10/enterprise-grade-agentic-automation-is-here/). ::: ## Orchestration Cluster {#orchestration-cluster} The primary architectural change is the consolidation of the core Zeebe, Operate, Tasklist, and Identity components into the Orchestration Cluster (a single unified deployable package). This impacts how Camunda 8 is deployed, managed, and scaled. The Orchestration Cluster (previously automation cluster) is now the core component of Camunda 8. :::note simple deployment The simplest Camunda 8.8 Self-Managed deployment runs as a single Java application or docker container. ::: ### Zeebe, Operate, Tasklist, and Identity In Camunda 8.8, Zeebe, Operate, Tasklist, and Identity are integrated into the Orchestration Cluster application as a single deployable artifact, distributed as a JAR file or Docker container. - [Zeebe](/reference/glossary.md#zeebe) is the [workflow engine](/reference/glossary.md#workflow-engine). - Operate is used for monitoring and troubleshooting [process instances](/reference/glossary.md#process-instance) running in Zeebe. - Tasklist is used for interacting with [user tasks](/reference/glossary.md#user-task) (assigning, completing, and so on). - [Identity](/reference/glossary.md#identity) is used for managing the integrated Orchestration Cluster authentication and authorization. In Camunda 8.7 and earlier, each component (Zeebe, Operate, Tasklist, and Identity) was deployed independently. Orchestration Cluster ### Unified Orchestration Cluster REST API {#orchestration-cluster-api} Camunda 8.8 introduces a single unified Orchestration Cluster REST API you can use to interact programmatically with the Orchestration Cluster. - This replaces component APIs (Operate API, Tasklist API, Zeebe API, and much of Identity API) with a single set of endpoints. - This unified API supports both organizational (SaaS) and Self-Managed deployments. - This is now the default and recommended integration point for developers, operators, and automation solutions. Orchestration Cluster REST API ### Unified Exporter Camunda 8.8 introduces a new unified Camunda Exporter that directly populates data records that are consumable by read APIs on the secondary storage. This significantly reduces latency until eventually consistent data becomes available on Get and Search APIs. In Camunda 8.7 and earlier, dedicated importers/exporters were used for data flows between components (such as Elasticsearch import/export). Camunda Exporter :::info Learn more about the unified Camunda Exporter in the blog post [One Exporter to Rule Them All: Exploring Camunda Exporter](https://camunda.com/blog/2025/02/one-exporter-to-rule-them-all-exploring-camunda-exporter/). ::: ### Unified component configuration Camunda 8.8 introduces unified configuration for Orchestration Cluster components where you can define all essential cluster and component behavior through a single, centralized configuration system. In Camunda 8.7 and earlier, managing and configuring core components (Zeebe, Operate, Tasklist, Identity) was done separately. Core settings and features :::note Only the first partial set of unified configuration properties is introduced in Camunda 8.8. - All remaining unified property changes will be completed by Camunda 8.9. - This remaining work will result in future breaking changes. For example, the secondary database properties will be unified into a secondary-storage properties section. ::: ## Identity, authentication, and authorization {#identity} The Orchestration Cluster Identity component UI handles authentication and authorization for the Orchestration Cluster components and its resources. :::note With this 8.8 change, the source of truth for Identity and Access Management for the Orchestration Cluster (including Zeebe, Operate, Tasklist, and its APIs) is now the Orchestration Cluster itself. This removes the reliance on the separate [Management Identity](/self-managed/components/management-identity/overview.md) (formerly "Identity") component. ::: ### Identity and Management Identity In Camunda 8.8, Orchestration Cluster [Identity](/versioned_docs/version-8.8/components/identity/identity-introduction.md) and [Management Identity](/self-managed/components/management-identity/overview.md) are two separate components used for Identity management, each with distinct areas of responsibility. Identity Management Identity Access and permission management for all Orchestration Cluster components: Zeebe, Operate, Tasklist, and the Orchestration Cluster REST and gRPC API. Unified access management: Authentication and authorizations are handled directly by the Orchestration Cluster across all components and APIs, eliminating any orchestration dependency on Management Identity. Authentication: Supports three authentication modes: No Authentication: No authentication required for API access. Form-based authentication in the UI. Users and groups are managed in Identity. Basic Authentication: Basic authentication for API access. Form-based authentication in the UI. Users and groups are managed in Identity. OIDC: OpenID Connect with any compatible Identity Provider (for example, Keycloak, Microsoft EntraID, Okta). Authorizations per resource: [Authorizations](/components/concepts/access-control/authorizations.md) provide fine-grained access control to Orchestration Cluster resources such as process instances, tasks, and decisions, applied consistently across components and APIs Decoupling from Keycloak: Keycloak is treated as a standard external Identity Provider integrated via OIDC, making it easier to use other providers without special integration. Tenant-Management: Tenants are directly managed within the Orchestration Cluster, allowing for Tenants per Cluster. Continues to manage access for platform components such as Web Modeler, Console, and Optimize. Remains responsible for managing access to platform components such as Web Modeler, Console, and Optimize. Authentication: Supports two authentication modes: Direct Keycloak integration: (default). OIDC: OpenID Connect with any compatible Identity Provider (for example, Keycloak, Microsoft EntraID, Okta). Tenant Management: No longer manages Tenants for the Orchestration Cluster components. Tenants only apply to Optimize. Resource Authorizations: No longer manages resource authorizations for Orchestration Cluster components. ### Tenant interceptors Tenant interceptors are **not supported in Camunda 8.8**. If you are using tenant interceptors in Camunda 8.7, you must migrate to the new Orchestration Cluster Identity [tenant management](/versioned_docs/version-8.8/components/identity/tenant.md). Administrators must: - Migrate tenants into the cluster using either the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/specifications/create-tenant.api.mdx) or the [Orchestration Identity UI](/versioned_docs/version-8.8/components/identity/tenant.md#create-a-tenant). - Assign tenants by: - Direct assignment to [users](/versioned_docs/version-8.8/components/identity/tenant.md#assign-users-to-a-tenant) - Direct assignment to [clients](/versioned_docs/version-8.8/components/identity/tenant.md#assign-clients-to-a-tenant) - Using [mapping rules](/versioned_docs/version-8.8/components/identity/tenant.md#assign-mapping-rules-to-a-tenant) ### Are you affected by 8.8 Identity changes? The 8.8 changes to Identity could affect different user roles in your Organization. For example: | Role | Responsibilities | | :------------------- | :------------------------------------------------------------------------------------------------------------------------------------- | | Administrators | Understand the new Identity setup and migration steps from Camunda 8.7. | | Developers | Learn the new [authorization concepts](/components/concepts/access-control/authorizations.md) and required permissions for API access. | | Information Security | Review and adapt to the new Identity architecture. | ### Benefits Orchestration Cluster Identity provides several advantages: - **Simplified identity management**: The split between Orchestration Cluster Identity and Management Identity provides a clearer separation of concerns, making it easier to manage access for Orchestration Cluster components versus Web Modeler and Console. - **Unified access management**: All identity and authorization features for the Orchestration Cluster components are now contained within the Orchestration Cluster, eliminating any dependency on Management Identity when orchestrating processes. - **Simplified deployment and improved availability**: Removing the dependency on Management Identity streamlines deployment and increases availability by reducing potential points of failure. - **Consistent authorization model**: The new model offers a unified approach to managing permissions across all Orchestration Cluster resources. - **Enhanced security**: Granular access controls improve the overall security posture for cluster resources. - **Improved performance**: Authorization checks are handled internally, reducing latency and improving performance. - **Flexible Identity Provider integration**: Keycloak is now treated as a standard OIDC provider, making it easier to integrate with other Identity Provider and increasing flexibility for users. ### Impact of Identity changes on your 8.7 deployment The impact of these changes and what action you need to take depends on your deployment type. For example: | Area | Impact | | :-------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Authorization | The new [authorization](/components/concepts/access-control/authorizations.md) model introduces fine-grained access control for Orchestration Cluster resources, replacing the previous model. | | Roles, tenants, and mapping rules | Now managed within Orchestration Cluster Identity, replacing the previous Management Identity setup. | | User Task authorizations | [User Task access restrictions](/versioned_docs/version-8.9/components/tasklist/user-task-access-restrictions.md) only apply to the Tasklist v1 API. After switching to the v2 API with Tasklist, user task access restrictions do not apply. | Each deployment type has a clear upgrade path and migration guidance to help administrators transition from Camunda 8.7 to Camunda 8.8. ### Camunda 8 SaaS Resource authorizations, groups, and roles formerly managed via Console are replaced by authorizations, groups, and roles managed within the cluster-specific Identity. - These are automatically migrated during the Camunda 8.8 upgrade to preserve your existing Access Management configuration at the time of the update. - After upgrading a cluster to 8.8, changes to resource authorizations and roles made in Console no longer affect the 8.8 cluster. - Users and clients are created and managed in Console, with their authorizations managed via the Orchestration Cluster. - Console cluster settings in 8.8 allow you to toggle Orchestration Cluster authorizations. Authorizations are enabled by default for any migrated cluster. The automated migration ensures that your users and clients can access the UIs and APIs as before. The following table summarizes where Identity entities are managed in Camunda 8.8 SaaS: | Entity type | Managed via | | :------------- | :----------------------------- | | Users | Console | | Clients | Console | | Roles | Orchestration Cluster Identity | | Groups | Orchestration Cluster Identity | | Authorizations | Orchestration Cluster Identity | | Tenants | n/a (planned for future) | | Mapping rules | n/a (managed by Camunda) | ### Camunda 8 Self-Managed After you deploy all Camunda 8 components in a Self-Managed environment, you will continue to use Management Identity for Web Modeler, Console, and Optimize, but use Orchestration Cluster Identity for Zeebe, Operate, Tasklist, and the Orchestration Cluster REST API. - Roles and permissions for Orchestration Cluster components (previously managed in Management Identity), are now replaced by the new authorizations and roles defined within Orchestration Cluster Identity. - The Identity Migration App that migrates these entities from Management Identity into Orchestration Cluster Identity must be run during your Camunda 8.7 to 8.8 upgrade. Instructions on enabling and configuring the Identity Migration App in the 8.7 to 8.8 migration guide are available for Helm and also docker-compose/bare Java deployments. - Authorization checks are enabled by default for any migrated cluster using the Helm chart. The automated migration ensures that your users and clients can access the UIs and APIs like before. - Management Identity, Keycloak and Postgres are no longer needed for an Orchestration Cluster. They are only needed when using Web Modeler, Console or Optimize. - For the Orchestration Cluster, you can bring your own Identity Provider (for example, Keycloak, Microsoft EntraID, Okta) or use the built-in Basic authentication method. - A special setup is no longer required for Keycloak as it is now integrated like any other Identity Provider via OpenID Connect (OIDC). Management Identity relies by default on Keycloak, but you can also configure it to use any OIDC-compatible Identity Provider. - For OIDC-based Self-Managed deployments, you can reuse groups from your identity provider for authorization, role, and tenant assignment. See [bring your own groups](../../../self-managed/components/orchestration-cluster/admin/bring-your-groups.md). The following table summarizes where Orchestration Cluster Identity entities are managed in Camunda 8.8 Self-Managed: | Entity type | Managed via | | :------------- | :----------------------------- | | Users | Identity Provider | | Clients | Identity Provider | | Roles | Orchestration Cluster Identity | | Groups | Identity Provider | | Authorizations | Orchestration Cluster Identity | | Tenants | Orchestration Cluster Identity | | Mapping Rules | Orchestration Cluster Identity | #### Camunda 8 Self-Managed - Basic Authentication If you are using built-in user management (Basic authentication), Tasklist and Operate specific built-in user management (using Elasticsearch/OpenSearch secondary storage) is no longer supported. - Administrators must migrate their users manually into the Orchestration Cluster. - You must ensure that **usernames are identical**, otherwise users will not be able to see their assigned tasks. In a Basic authentication setup, the Orchestration Cluster provides full functionality: | Entity type | Managed via | | :------------- | :-------------------------------------------- | | Users | Orchestration Cluster Identity | | Clients | n/a (not applicable for Basic authentication) | | Roles | Orchestration Cluster Identity | | Groups | Orchestration Cluster Identity | | Authorizations | Orchestration Cluster Identity | | Tenants | Orchestration Cluster Identity | | Mapping Rules | n/a (not applicable for Basic authentication) | ## APIs & tools {#apis-and-tools} Changes to [APIs & tools](/apis-tools/working-with-apis-tools.md) in 8.8 are summarized as follows: **What's new/changed** **Description** [Orchestration Cluster REST API](#orchestration-cluster) The unified Orchestration Cluster REST API replaces the deprecated V1 component APIs, providing a unified interface for managing and interacting with the Orchestration Cluster. [Camunda Java Client](apis-tools/java-client/getting-started.md) The Camunda Java Client is now the official Java library for connecting to Camunda 8 clusters, automating processes, and implementing job workers. It is designed for Java developers who want to interact programmatically with Camunda 8 via REST or gRPC, and is the successor to the Zeebe Java client. [Camunda Spring Boot Starter](/apis-tools/camunda-spring-boot-starter/getting-started.md) The Camunda Spring Boot Starter replaces the Spring Zeebe SDK. The SDK relies on the Camunda Java client, designed to enhance the user experience and introduce new features while maintaining compatibility with existing codebases. [Camunda Process Test](/apis-tools/testing/getting-started.md) Camunda Process Test (CPT) is a Java library to test your BPMN processes and your process application. CPT is the successor of Zeebe Process Test. Our previous testing library is deprecated and will be removed with version 8.10. [Camunda user tasks](/apis-tools/migration-manuals/migrate-to-camunda-user-tasks.md) Camunda user tasks replace the deprecated job-based user tasks in Camunda 8.8, providing a more robust and flexible way to handle user tasks within process models. [Tasklist GraphQL API](/reference/announcements-release-notes/880/880-announcements.md#deprecated-operate-and-tasklist-v1-rest-apis) The previously deprecated Tasklist GraphQL API is removed in Camunda 8.8. This change is part of the broader architectural evolution towards the Orchestration Cluster REST API, which provides a more unified and consistent interface for managing tasks and workflows. [Zeebe gRPC API endpoints](/reference/announcements-release-notes/880/880-announcements.md#deprecated-zeebe-grpc-api-endpoints) With the 8.8 release, the gRPC API continues but will be disabled by default starting with the 8.10 release. :::info To learn more about upgrading and migrating to 8.8, see the [API & tools upgrade guide](/versioned_docs/version-8.8/apis-tools/migration-manuals/index.md). ::: ## Upgrade guides {#upgrade-guides} Camunda 8.8 lays the foundation for future releases. Upgrading ensures compatibility and access to improved features. The following guides provide detailed information on how you can upgrade to Camunda 8.8. **Guide** **Description** **Who is this guide for?** [Self-Managed upgrade guide](/versioned_docs/version-8.8/self-managed/upgrade/index.md) Evaluate your infrastructure, understand operational changes, and choose the best update strategy for your environment. Operations and platform administrators of Self-Managed installations. [APIs & tools upgrade guide](/versioned_docs/version-8.8/apis-tools/migration-manuals/index.md) Plan and execute an upgrade from Camunda 8.7 to 8.8, focusing on API and tools transitions. Application developers maintaining Camunda-based solutions in Self-Managed Kubernetes or VM environments.Developers using Camunda APIs and tools. ## Migration from Camunda 7 to Camunda 8 Camunda 8.8 includes new tools and enhancements to help you migrate from Camunda 7 to Camunda 8. **What's new** **Description** [Data Migrator](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/index.md) Use the Data Migrator to copy running process instances from Camunda 7 to Camunda 8. [Diagram Converter](/guides/migrating-from-camunda-7/migration-tooling/diagram-converter.md) Analyzes Camunda 7 model files (BPMN or DMN) and generates a list of tasks required for the migration. It can also automatically convert these files from Camunda 7 format to Camunda 8 format. [Code Conversion](/guides/migrating-from-camunda-7/migration-tooling/code-conversion.md) Code conversion utilities provide code mapping tables, conversion patterns, and automatable refactoring recipes to systematically translate Camunda 7 implementation patterns to Camunda 8 equivalents. :::tip Start your migration today with the [Camunda 7 to Camunda 8 migration guide](/guides/migrating-from-camunda-7/index.md). ::: --- ## 8.9 Release announcements | Minor release date | Scheduled end of maintenance | Release notes | Upgrade guides | | ------------------ | ---------------------------- | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- | | 14 April 2026 | 13 October 2027 | [8.9 release notes](/reference/announcements-release-notes/890/890-release-notes.md) | [8.9 upgrade guides](/reference/announcements-release-notes/890/whats-new-in-89.md#upgrade-guides) | :::info 8.9 resources - See [release notes](/reference/announcements-release-notes/890/890-release-notes.md) to learn more about new features and enhancements. - Refer to the [quality board](https://github.com/orgs/camunda/projects/187/views/21) for an overview of known bugs by component and severity. ::: ## Supported environments Change #### Elasticsearch minimum version raised to 8.19+ The minimum supported Elasticsearch version for the Orchestration cluster and Optimize is now 8.19 (previously 8.16+). - This aligns with the Elasticsearch 8 versions maintained by Elastic as of April 2025. - The default Elasticsearch version used by Docker Compose examples and Helm templates has been updated to `8.19.9+` accordingly. - Upgrade your Elasticsearch clusters before moving to Camunda 8.9 to avoid compatibility issues. - For best results, Camunda recommends upgrading to the latest supported Elasticsearch 9.2+ to take advantage of new features and improvements. [OpenSearch and Elasticsearch support](/reference/supported-environments.md#opensearch-and-elasticsearch-support) Change #### Keycloak 25.x no longer supported Camunda 8.9 drops support for Keycloak 25.x. Only Keycloak 26.x is now supported for Management Identity. - Keycloak 25.x has reached end of life and is no longer maintained by the Keycloak project. - This aligns with [Keycloak's updated release strategy](https://www.keycloak.org/2024/10/release-updates), which moved to quarterly minor releases. - Upgrade your Keycloak instance to 26.x before moving to Camunda 8.9. [Supported environments](/reference/supported-environments.md) Change #### OpenSearch minimum version raised to 2.19+ The minimum supported OpenSearch version for the Orchestration cluster and Optimize is now 2.19+ (previously 2.17+). - This aligns with the OpenSearch versions maintained as of April 2025. - Upgrade your OpenSearch clusters before moving to Camunda 8.9 to avoid compatibility issues. - For best results, Camunda recommends upgrading to the latest supported OpenSearch 3.4+ to take advantage of new features and improvements. [Supported environments](/reference/supported-environments.md) New #### New AWS regions Camunda 8.9 adds support for the following new regions in Camunda 8 SaaS. - Paris, Europe (eu-west-3) - North America, Ohio (us-east-2) [Supported AWS regions](/components/saas/regions.md#amazon-web-services-aws-regions) New #### OpenJDK 25 support Camunda 8.9 adds certification for OpenJDK 25 across the Orchestration Cluster, Connectors, Optimize, and supporting tooling. You can now run Self-Managed deployments on OpenJDK 21–25 without additional configuration changes. New #### Support for latest Elasticsearch and OpenSearch versions Camunda 8.9 now supports Elasticsearch 9.2+ and OpenSearch 3.4+, allowing you to take advantage of the latest database features and releases. [OpenSearch and Elasticsearch support](/reference/supported-environments.md#opensearch-and-elasticsearch-support) ## Agentic orchestration Breaking change #### MCP Client and MCP Remote Client connectors [Camunda 8.9.0-alpha2](/reference/announcements-release-notes/890/890-release-notes.md#890-alpha2) introduces breaking changes to the [MCP Client](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-client.md) element templates and runtime configuration. **Action:** To remain compatible, you should update your MCP Client and MCP Remote Client connectors to use element template version `1`. ## APIs & tools :::info 8.9 APIs & Tools migration guide Migrate your API integrations, SDKs, and generated clients to Camunda 8.9 using the [8.9 APIs & Tools migration guide](/apis-tools/migration-manuals/migrate-to-89.md). ::: :::tip Client and API compatibility Camunda clients (Java client, Spring SDK, Node.js SDK) and Camunda Process Test are **forward-compatible** with the Orchestration Cluster, meaning you can upgrade the cluster and clients independently. For example, you can run a client on 8.8 against a cluster on 8.9, see [Client and API compatibility](/reference/public-api.md#client-and-api-compatibility). ::: Breaking change #### `GET /decision-instances/{decisionEvaluationInstanceKey}` now validates the key format The [Get decision instance](/apis-tools/orchestration-cluster-api-rest/specifications/get-decision-instance.api.mdx) endpoint previously returned `404 Not Found` when the `decisionEvaluationInstanceKey` path parameter contained invalid characters that did not match the required pattern `^[0-9]+-[0-9]+$`. The endpoint now correctly returns `400 Bad Request` in this case, while `404 Not Found` is reserved for well-formed keys that do not exist. **Action:** Update any client code or error handling that relied on receiving `404 Not Found` for malformed keys to also handle `400 Bad Request`. Breaking change #### Bug fix: `FormResult.schema` type corrected from object to string The `schema` property in the `FormResult` response was incorrectly specified as `type: object` in the OpenAPI contract, but the server has always returned it as a JSON `string`. This specification bug is now fixed. This is a bug fix that aligns the OpenAPI contract with actual server behavior, improving correctness for typed client integrations. Applications already handling the runtime `string` value are unaffected. The Camunda Java client is also affected: `io.camunda.client.api.search.response.Form::getSchema()` now returns `String` instead of `Object`. If your Java code casts or processes the return value as `Object`, update it to use `String`. **Action:** Take the following action for your integration type: | Integration type | Action required | | :--------------------- | :-------------------------------------------------------------------------------------------------------------------- | | Official SDK users | Update to the latest SDK version. | | Java client users | Update calls to `Form::getSchema()` to handle the `String` return type instead of `Object`. | | Generated-client users | Regenerate your client. If your generated code relied on the incorrect `object` typing, update it to handle `string`. | | Custom integrations | No change needed if you were already handling the actual `string` response. | Breaking change #### Document API response schemas now have explicit required and nullable annotations Starting with 8.9.0, the OpenAPI specification uses distinct schemas for document request and response payloads, and adds explicit `required` / `nullable` annotations to document response types. Previously, a shared `DocumentMetadata` schema was used for both creating and reading documents. Because response fields like `customProperties` are always populated by the server, but optional in requests, a single schema could not accurately express both contracts. This caused incorrect required/optional behavior in generated clients. | Schema | What changed | | :-------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `DocumentMetadata` | Now request-only. `customProperties` is no longer marked as required. | | `DocumentMetadataResponse` (new) | New response schema. `fileName`, `expiresAt`, `size`, `contentType`, `customProperties`, `processDefinitionId`, and `processInstanceKey` are all required. `expiresAt`, `processDefinitionId`, and `processInstanceKey` are nullable. | | `DocumentReference` | `metadata` now references `DocumentMetadataResponse`. Fields `camunda.document.type`, `storeId`, `documentId`, `contentHash`, and `metadata` are now explicitly required. `contentHash` is nullable. | | `DocumentLink` | `url` and `expiresAt` are now explicitly required. | | `candidateGroups` (user task / job API) | Now explicitly marked as a required field in response schemas (`UserTaskResult`, `UserTaskProperties`). | **Action:** Take the following action for your integration type: | Integration type | Action required | | :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Official SDK users | Update to the latest SDK version. | | Generated-client users | Regenerate your client.Update any code that references `DocumentMetadata` in response handling as it is now `DocumentMetadataResponse`.Review nullable annotations on `DocumentReference.contentHash` and `DocumentMetadataResponse` fields. | | Custom integrations | No request-side changes needed. Response fields listed above are now guaranteed to be present (though some may be `null`). | [8.9 API migration guide](../../../apis-tools/migration-manuals/migrate-to-89.md#request-response-schema-split) Breaking change #### Camunda Spring Boot Starter now requires Spring Boot 4.0.x Starting with 8.9.0-alpha3, the [Camunda Spring Boot Starter](../../../apis-tools/camunda-spring-boot-starter/getting-started.md) requires Spring Boot 4.0.x. **Action:** To remain compatible, migrate your application to Spring Boot 4.0.x. :::info Spring Boot support timeline This change aligns with the Spring Boot support policy, as OSS support for Spring Boot 3.x ends in June 2026. See the [Spring Boot support timeline](https://spring.io/projects/spring-boot#support). ::: Breaking change #### OpenAPI enum extensions for new 8.9 capabilities Starting with 8.9.0, new enum literals were added to support expanded functionality. This improves feature coverage, but typed or exhaustive enum-handling code paths may require updates to preserve full completeness checks. Added literals include the following: | Enum | New literal | | :------------------------------------------------------------ | :------------------------- | | `BatchOperationTypeEnum` / `BatchOperationTypeFilterProperty` | `DELETE_DECISION_INSTANCE` | | `ResourceTypeEnum` | `USER_TASK` | | `PermissionTypeEnum` | `COMPLETE` | **Action:** Take the following action for your integration type: | Integration type | Action required | | :--------------------- | :------------------------------------------------------------------------------------------------------- | | Official SDK users | Update to the latest SDK version. | | Generated-client users | Regenerate and add fallback/default handling for enum parsing and matching. | | Custom integrations | Review enum branches (for example, exhaustive `switch`/pattern matches) and add handling for new values. | [8.9 API migration guide](../../../apis-tools/migration-manuals/migrate-to-89.md#enum-extensions) Breaking change #### OpenAPI type-safety enhancements for request and schema types Starting with 8.9.0, parts of the OpenAPI contract use stronger domain types and one schema rename to improve semantic correctness in client applications. This increases compile-time safety and helps prevent semantic substitution errors in typed integrations. Affected contract updates include the following: - `CreateDeploymentData.body.tenantId`: `string` → `TenantId` - `CreateDocumentData.query.documentId`: `string` → `DocumentId` - `SearchCorrelatedMessageSubscriptionsData.body.filter.processDefinitionKey.$eq`: `string` → `ProcessDefinitionKey` - `CorrelatedMessageSubscriptionFilter.processDefinitionKey`: `string` → `ProcessDefinitionKeyFilterProperty | undefined` - `CorrelatedMessageSubscriptionSearchQuery.filter.processDefinitionKey.$eq`: `string` → `ProcessDefinitionKey` - `ProcessInstanceIncidentSearchQuery` renamed to `IncidentSearchQuery` Example request payload update for message subscription filtering: - Before: `"processDefinitionKey": "2251799813685251"` - After (for example): `"processDefinitionKey": { "$eq": "2251799813685251" }` **Action:** Take the following action for your integration type: | Integration type | Action required | | :--------------------- | :-------------------------------------------------------------- | | Official SDK users | Update to the latest SDK version. | | Generated-client users | Regenerate clients and update type mappings/imports. | | Custom integrations | Update request payload construction and affected typed helpers. | [8.9 API migration guide](../../../apis-tools/migration-manuals/migrate-to-89.md#type-safety-enhancements) Breaking change #### Resource deletion endpoint response body Starting with 8.9.0-alpha4, the resource deletion endpoint `POST /resources/{resourceKey}/deletion` in the [Orchestration Cluster API](../../../apis-tools/orchestration-cluster-api-rest/specifications/delete-resource.api.mdx) returns a response body. This provides explicit deletion feedback, making client-side confirmation, auditing, and follow-up workflow logic more reliable. **Action:** If you use an SDK, update to the latest version for compiler and model support. Breaking change #### Search filter validation errors now return structured error collections REST API search endpoints now collect all filter validation errors and return them together in a single `400 Bad Request` response. Previously, only the first conversion error was returned. This fix improves consistency by collecting all validation issues in a single response. - All search filter validation errors are now collected and returned together, instead of stopping at the first error. - The ProblemDetail `title` changed from `"Bad Request"` to `"INVALID_ARGUMENT"`. - The ProblemDetail `detail` now contains more descriptive, structured messages (for example, `"The provided evaluationDate 'invalid' cannot be parsed as a date according to RFC 3339, section 5.6."` instead of `"Failed to parse date-time: [invalid]"`). **Impact** | Customer type | Affected | | :--------------------------------------------------------------------------------------------- | :--------------------------- | | Parsing error response bodies (specifically `title` or `detail` fields) for validation errors. | Yes | | Only checking HTTP status codes. | No | | Sending valid requests. | No (happy path is unchanged) | **Action:** - If your code parses error response bodies from search endpoints, update it to handle a collection of validation errors instead of a single error message. - If your code checks for `"Bad Request"` in the `title` field, update it to check for `"INVALID_ARGUMENT"`. [8.9 API migration guide](../../../apis-tools/migration-manuals/migrate-to-89.md#search-filter-validation-errors) Breaking change #### Streamlined SaaS orchestration architecture Camunda 8.9 introduces the streamlined SaaS orchestration architecture. As part of this change, the primary cluster endpoints have moved to the unified `*.api.camunda.io` domain. The previous component-specific endpoints (for example, `*.zeebe.camunda.io`, `*.operate.camunda.io`, `*.tasklist.camunda.io`) remain available for backward compatibility in 8.9, but are **deprecated** and planned for **removal in Camunda 8.10**. **Impact:** If you use custom applications, scripts, SDKs, or tooling that construct or store cluster URLs (including deployment endpoints), update them to the new unified endpoints to avoid issues when the legacy endpoints are removed. **Action:** Update your integrations to use the new endpoint format and review the streamlined architecture documentation for details. [Streamlined SaaS orchestration architecture](../../../apis-tools/migration-manuals/saas-orchestration-architecture.md) Breaking change #### Type-safe pagination model in the Camunda Java client Starting with 8.9.0, the Camunda Java client uses type-safe pagination interfaces (`AnyPage`, `OffsetPage`, `CursorForwardPage`, `CursorBackwardPage`) instead of the previous `SearchRequestPage` class. Each search or statistics endpoint now exposes only the pagination methods it actually supports. Previously, the API allowed mixing incompatible pagination styles (for example, `.page(p -> p.from(10).after("cursor"))`), which always resulted in a `400 Bad Request` at runtime. This change surfaces that restriction at compile time. This change is **not binary-compatible**. Code compiled against the old API will fail at runtime without recompilation, because the method signature changed from `page(Consumer)` to `page(Consumer)`. All users must recompile, even if their source code does not require changes. **Action:** Take the following action for your integration type: | Integration type | Action required | | :--------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------- | | Lambda-based pagination (for example, `.page(p -> p.from(5).limit(10))`) | Recompile your application. Source code changes are not required for valid pagination patterns. | | Explicit `SearchRequestPage` references | Replace with `AnyPage` (import `io.camunda.client.api.search.page.AnyPage`). | | Explicit `SearchRequestOffsetPage` references | Replace with `OffsetPage`. | | Custom `TypedSearchRequest` implementations | Add a pagination type parameter (3 → 4 generic params). | | Custom `TypedPageableRequest` implementations | Add a pagination type parameter (1 → 2 generic params). | | Storing direction method returns (for example, `SearchRequestPage r = p.from(10)`) | Use `OffsetPage r = p.from(10)`, `CursorForwardPage r = p.after("c")`, or `CursorBackwardPage r = p.before("c")`. | [8.9 API migration guide](../../../apis-tools/migration-manuals/migrate-to-89.md#type-safe-pagination) Breaking change #### `versionTag` now returns `null` instead of empty string when absent Starting with 8.9.0, API response fields for `versionTag` return `null` instead of an empty string `""` when no version tag is set. This properly indicates absence rather than leaking an internal default. Previously, customers had to handle both empty string and absent/null to determine whether a version tag was set. This change simplifies that logic by using `null` consistently to signal absence, aligning with how other optional fields like `businessId` are handled. **Action:** - If your code checks for an empty string (`""`) to detect a missing version tag, update it to check for `null` instead. - Official SDK users: update to the latest SDK version. - Generated-client users: regenerate your client to pick up the updated nullable annotation. Deprecated #### Deprecated enum literals in Orchestration Cluster API v2 The following enum literals in the V2 Orchestration Cluster API are now marked as deprecated: - `UNSPECIFIED` in `DecisionDefinitionTypeEnum` - `UNKNOWN` in `DecisionInstanceStateFilterProperty` - `UNKNOWN` in `DecisionInstanceStateEnum` These values were reintroduced and are now explicitly deprecated to preserve backward compatibility while signaling planned cleanup. **Action:** Avoid these values in new integrations. They are planned for removal in a future release, where removal will be a breaking change. [Orchestration Cluster API reference](../../../apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) New #### Task permission management and new task-worker role With task permission management, you can assign restricted permissions for user task operations. Camunda 8.9 introduces a new built-in Identity role, `task-worker`. Use this role to grant users limited access to work on tasks without assigning broader permissions. [Task permission management](../../../../components/tasklist/user-task-authorization) Removed #### Removed: Web Modeler API milestone endpoints The Web Modeler API endpoints under `/api/v1/milestones` that were deprecated in Camunda 8.8 are now removed in 8.9. **Action:** Use the corresponding endpoints under `/api/v1/versions` instead. :::warning Web Modeler SaaS In Web Modeler SaaS, the endpoints will no longer be available as of April 14, 2026. ::: [Web Modeler API](/apis-tools/web-modeler-api/index.md) ## Connectors Breaking change #### Default secret provider prefix change Starting with Camunda 8.9, the environment-based connector secret provider uses `SECRET_` as the default prefix. Unprefixed environment variables are no longer resolved as connector secrets unless you explicitly configure an empty prefix or a different custom prefix. **Action:** Choose one of the following options before or during your upgrade: - Update your secret environment variables to use the `SECRET_` prefix. - Configure a custom prefix via `camunda.connector.secretprovider.environment.prefix` or `CAMUNDA_CONNECTOR_SECRETPROVIDER_ENVIRONMENT_PREFIX`. - Restore the previous behavior by setting an empty prefix, knowing that Camunda does not recommend this mode for production environments. [connector secrets configuration](/self-managed/components/connectors/connectors-configuration.md#secrets) [Upgrade 8.8 to 8.9](/self-managed/upgrade/components/880-to-890.md#default-secret-provider-prefix-change-breaking) Deprecated #### Deprecated: Operate Connector The Operate Connector is deprecated, following the deprecation of the Operate API in Camunda 8.9 (see [Deprecated: Operate and Tasklist v1 REST APIs](/reference/announcements-release-notes/880/880-announcements.md#deprecated-operate-and-tasklist-v1-rest-apis)). Going forward, you can use the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) via the [REST Connector](/components/connectors/protocol/rest.md). Change #### Inbound connectors now support older process versions Starting with Camunda 8.9, inbound connectors are activated not only for the latest process version, but also for older process versions that have active instances waiting on message subscriptions. Inbound connectors now remain active for any process version that has instances waiting on message subscriptions. This ensures that running process instances can continue to receive messages through inbound connectors, even after a newer version of the process is deployed. This change improves the reliability of long-running processes that depend on inbound connectors to receive external events. [Inbound connector lifecycle](/components/connectors/advanced-topics/inbound-lifecycle.md) Change #### Dedicated artifact for element template generation annotations Starting with Camunda 8.9, use the dedicated artifact `element-template-generator-annotations`. This artifact contains only the annotations required for the Element Template Generator and reduces your project's dependency footprint. Previously, these annotations were included in the `element-template-generator-core` artifact. Replace `element-template-generator-core` with `element-template-generator-annotations` and update the imports. New #### Synchronous webhook results Starting with Camunda 8.9, the Webhook connector supports a synchronous response mode. This allows the connector to wait until a newly created process instance finishes and return the results in the response. For more information, see the [Webhook connector](/components/connectors/protocol/http-webhook.md) documentation. New #### Virtual threads support Camunda 8.9 provides support for virtual threads in the connector runtime. Virtual threads are enabled by default for outbound connectors. For more information on optimizing connector performance with virtual threads, see [connector runtime performance](/self-managed/components/connectors/performance.md). Change #### Kafka consumer connector: auto-generated consumer group ID format changed When upgrading from Camunda 8.8 to 8.9, the auto-generated Kafka consumer group ID changes format. This affects Kafka consumer connectors only when no explicit consumer group ID is configured. The change is a side effect of a runtime improvement that enables cross-version connector deduplication. - **8.8 format:** `kafka-inbound-connector---` (numeric key) - **8.9 format:** `kafka-inbound-connector---` (stable process ID) Because Kafka treats a changed group ID as a brand-new consumer group, affected connectors do not reuse their committed offsets after the upgrade and may reprocess messages. **Action required:** Before upgrading to 8.9, set an explicit **Consumer Group ID** in each Kafka consumer connector configuration to preserve the existing consumer group identity. You can [look up existing consumer groups](https://docs.confluent.io/kafka/operations-tools/manage-consumer-groups.html#list-groups-and-view-offsets) to find the current group ID in use. See the [Kafka connector documentation](/components/connectors/out-of-the-box-connectors/kafka.md?kafka=inbound) for details. ## Data Breaking change #### Camunda 8 Run: Docker Compose support removed The `--docker` flag and bundled Docker Compose files have been removed from Camunda 8 Run in 8.9. Camunda 8 Run and Docker Compose are now separate distribution artifacts. **Action:** If you previously started Docker Compose via `./c8run start --docker`, download the standalone [Docker Compose distribution](/self-managed/quickstart/developer-quickstart/docker-compose.md) from GitHub releases or the Camunda Download Center and run it independently. Breaking change #### Camunda 8 Run with H2 as the default secondary data storage Camunda 8 Run now uses H2 as the default secondary data storage, instead of Elasticsearch. Elasticsearch is no longer bundled with Camunda 8 Run. The `--disable-elasticsearch` flag is also removed, as Elasticsearch is no longer bundled. To use Elasticsearch as secondary storage, configure an external instance via `--config` and the `application.yaml`. When running with H2 (or any other RDBMS) as secondary storage, Camunda is only compatible with the V2 API. As a result, some features are not available in Operate and Tasklist. See [Migrate to the V2 Orchestration Cluster API](/apis-tools/migration-manuals/migrate-to-camunda-api.md) for more details. To continue using features exclusive to the V1 API, configure Camunda 8 Run with an external Elasticsearch instance and switch back to V1 mode. [Camunda 8 Run default secondary storage](/self-managed/quickstart/developer-quickstart/c8run/secondary-storage.md#default-h2-camunda-8-run) Change #### Hierarchy-aware retention for process instance data Starting with Camunda 8.9, retention of process instance data in Elasticsearch/OpenSearch secondary storage becomes hierarchy-aware, meaning child process instances are retained as long as their root process instance is retained. You can control the retention behavior via the process instance retention mode configuration. [Hierarchy-aware retention](/self-managed/components/orchestration-cluster/core-settings/concepts/data-retention.md#hierarchy-aware-retention) New #### Amazon Aurora secondary storage Camunda 8.9 introduces Amazon Aurora as a secondary data store for Orchestration Clusters. [Secondary storage](/self-managed/concepts/secondary-storage/index.md) ## Deployment Breaking change #### Elasticsearch subchart no longer enabled by default Previously, the Elasticsearch subchart was enabled by default. To use OpenSearch, you would need to disable Elasticsearch and enable OpenSearch. With the inclusion of RDBMS as a secondary storage option and the [deprecation of Bitnami subcharts](#helm-chart-bitnami-subcharts-deprecated) (including the bundled Elasticsearch subchart), there is no longer a default secondary storage type. **Action:** - Camunda recommends setting `orchestration.data.secondaryStorage.type` explicitly in your `values.yaml`. - The chart can auto-detect the type from `global.elasticsearch.enabled` or `global.opensearch.enabled`, but Helm will fail with a validation error if no secondary storage is configured. - Alternatively, set `global.noSecondaryStorage: true` to run in engine-only mode without secondary storage. :::note To continue using Elasticsearch provided as a subchart, you must add `global.elasticsearch.enabled: true`, `elasticsearch.enabled: true`, and `orchestration.data.secondaryStorage.type: elasticsearch` to your `values.yaml`. ::: Breaking change #### Helm chart: Default REST port unified to 8080 The Orchestration Cluster's default HTTP port has changed from 8090 to 8080 (`orchestration.service.httpPort`). This aligns the Helm chart with the Orchestration Cluster's default configuration. **Action:** If you have hardcoded port 8090 in network policies, Ingress rules, health check probes, or service mesh configuration, update these references to 8080 or explicitly set `orchestration.service.httpPort: 8090` in your `values.yaml`. Breaking change #### Helm chart: Deprecated secret keys removed Secret configuration keys that were deprecated in Camunda 8.8 are now removed in 8.9. Using any of the removed keys in your `values.yaml` will cause a hard failure during `helm install` or `helm upgrade`. Affected keys by component: | Component | Keys removed in 8.9 | | :-------------------- | :------------------------------------------------------------------------------------------------------------------------------ | | Global image tag | `global.image.tag` | | License | `global.license.key`, `global.license.existingSecret`, `global.license.existingSecretKey` | | Elasticsearch auth | `global.elasticsearch.auth.password`, `global.elasticsearch.auth.existingSecret`, `global.elasticsearch.auth.existingSecretKey` | | OpenSearch auth | `global.opensearch.auth.password`, `global.opensearch.auth.existingSecret`, `global.opensearch.auth.existingSecretKey` | | Identity auth | `global.identity.auth.{admin,identity,optimize}.existingSecret` and `.existingSecretKey` | | Document Store | `global.documentStore.type.{aws,gcp}.existingSecret` and related keys | | Identity | `identity.firstUser.password`, `identity.externalDatabase.password`, and their `existingSecret`/`existingSecretKey` variants | | Connectors | `connectors.security.authentication.oidc.existingSecret`, `.existingSecretKey` | | Orchestration Cluster | `orchestration.security.authentication.oidc.existingSecret`, `.existingSecretKey` | | Web Modeler | `webModeler.restapi.externalDatabase.password`, `webModeler.restapi.mail.smtpPassword`, and their `existingSecret` variants | **Action:** Migrate to the new secret configuration pattern using `*.secret.existingSecret` and `*.secret.existingSecretKey`, or `*.secret.inlineSecret` for non-production environments. [Secret management](/self-managed/deployment/helm/configure/secret-management.md) Breaking change #### Helm chart: `extraConfiguration` format changed from map to ordered list The `.extraConfiguration` Helm value has changed from a **map** (key-value pairs) to an **ordered list** of `file`/`content` entries. This ensures configuration entries are always applied in the order you define them, since maps in Go templates do not guarantee iteration order. The previous map format is **not supported** in Camunda 8.9. If you upgrade without converting to the new list format, Helm will fail during template rendering. **Before (8.8 — map):** ```yaml identity: extraConfiguration: custom-logging.yaml: | logging: level: ROOT: DEBUG ``` **After (8.9 — ordered list):** ```yaml identity: extraConfiguration: - file: custom-logging.yaml content: | logging: level: ROOT: DEBUG ``` **Action:** For every component where you use extraConfiguration, convert each map entry to a list entry. [Migrate extraConfiguration from 8.8 to 8.9](/self-managed/deployment/helm/configure/application-configs.md#migrate-extraconfiguration-from-88-to-89) Breaking change #### Helm chart: Helm CLI v4 does not support duplicate environment variable names Helm v4 enforces that environment variables in a rendered Kubernetes manifest must be unique. If your `values.yaml` overrides an environment variable also set by the chart, you might encounter an error. [Helm 4](/self-managed/deployment/helm/operational-tasks/helm-v4.md) Breaking change #### Helm chart: Secret auto-generation removed The Helm chart no longer automatically generates secrets. The configuration keys `global.secrets.autoGenerated`, `global.secrets.name`, and `global.secrets.annotations` are removed in 8.9. **Action:** All secrets must now be explicitly provided via Kubernetes Secrets referenced in your `values.yaml`. [Secret management](/self-managed/deployment/helm/configure/secret-management.md) Deprecated #### Helm chart: Bitnami subcharts deprecated The four Bitnami-based subcharts (`identityPostgresql`, `identityKeycloak`, `webModelerPostgresql`, `elasticsearch`) are deprecated in Camunda 8.9 and will be removed in Camunda 8.10. If any of these subcharts are enabled, Helm prints a deprecation warning during installation or upgrade. **Action:** Migrate to externally managed services before upgrading to Camunda 8.10. Deprecated #### Helm chart: `global.elasticsearch` and `global.opensearch` deprecated The `global.elasticsearch.*` and `global.opensearch.*` configuration trees are deprecated in Camunda 8.9 and will be removed in Camunda 8.10. These options are not truly global, as only the Orchestration and Optimize components use them. Legacy keys continue to work in Camunda 8.9 with deprecation warnings. Existing deployments will continue to function without changes. **Action:** You should migrate to the new component-specific configuration: | Component | New configuration path | | :------------ | :--------------------------------------------------------------------------------------------------------- | | Orchestration | `orchestration.data.secondaryStorage.elasticsearch.*` / `orchestration.data.secondaryStorage.opensearch.*` | | Optimize | `optimize.database.elasticsearch.*` / `optimize.database.opensearch.*` | Deprecated #### Helm chart: TLS secret configuration pattern The legacy TLS secret configuration using `*.tls.existingSecret` is deprecated. Legacy keys still work in Camunda 8.9 but will cause deprecation warnings and are removed in a future version. This affects the following paths: | Deprecated path | New path | | :---------------------------------------- | :----------------------------------------------- | | `global.elasticsearch.tls.existingSecret` | `global.elasticsearch.tls.secret.existingSecret` | | `global.opensearch.tls.existingSecret` | `global.opensearch.tls.secret.existingSecret` | | `console.tls.existingSecret` | `console.tls.secret.existingSecret` | **Action:** Migrate to the new pattern using `*.tls.secret.existingSecret`. [Secret management](/self-managed/deployment/helm/configure/secret-management.md) Deprecated #### Helm chart: Identity profile renamed to admin The orchestration profile `orchestration.profiles.identity` is deprecated and renamed to `orchestration.profiles.admin`. If your `values.yaml` uses the `identity` profile key, the chart automatically migrates it to `admin` and prints a deprecation warning. **Action:** Update your values file to use `orchestration.profiles.admin`. Deprecated #### Helm chart: Keycloak auth secret configuration The legacy Keycloak auth secret configuration using `global.identity.keycloak.auth.existingSecret` and `global.identity.keycloak.auth.existingSecretKey` is deprecated in Camunda 8.9. **Action:** Migrate to the new standard secret pattern: - `global.identity.keycloak.auth.existingSecret` → `global.identity.keycloak.auth.secret.existingSecret` - `global.identity.keycloak.auth.existingSecretKey` → `global.identity.keycloak.auth.secret.existingSecretKey` Legacy keys continue to work in 8.9 via normalizers, but produce deprecation warnings and will be removed in a future version. See [Secret management](/self-managed/deployment/helm/configure/secret-management.md). Change #### Helm chart: Bitnami subcharts bundled The Bitnami subcharts (PostgreSQL, Keycloak, Elasticsearch, and Common) are bundled directly within the Camunda Helm chart instead of being fetched from external Bitnami repositories at install time. This change reduces the risk of unexpected breaking changes from upstream Bitnami chart updates and gives Camunda full control over the lifecycle of these subcharts. **Action:** No action is required. Existing deployments continue to work as before. Change #### Helm chart: Headless service exposes public API ports The Orchestration component's headless service now includes gRPC and HTTP ports, enabling client-side load balancing for applications that connect directly to individual cluster members. Change #### Helm chart: Connectors OIDC issuer URI preferred over token URL The Connectors component now uses the OIDC issuer URI by preference, making `tokenUrl` optional when `issuerUri` is configured. This simplifies OIDC integration by allowing the token endpoint to be discovered automatically. Change #### Helm chart: Persistent web sessions enabled for RDBMS mode When using RDBMS as the secondary storage type, web sessions now persist across pod restarts. Previously, sessions were discarded, requiring users to re-authenticate after each restart. New #### Helm chart: `springImport` option for `extraConfiguration` entries Each `extraConfiguration` entry now supports an optional `springImport` field (default: `true`). **Action:** Set `springImport: false` to mount configuration files (such as `log4j2.xml` or keystores) into the container without adding them to Spring Boot's `spring.config.import`. This applies to all components that support `extraConfiguration`: orchestration, identity, connectors, optimize, and web-modeler. New #### Helm chart: Authorization, role, and group initialization Camunda 8.9 adds Helm chart support for initializing authorization rules, roles, and groups directly through `values.yaml`. This allows administrators to configure platform access control as part of the initial deployment, reducing manual post-installation setup. New #### Helm chart: Custom users and clients You can now define Identity users and OAuth2 clients directly in `values.yaml`. This simplifies initial deployment setup and enables reproducible, version-controlled Identity configurations. New #### Helm chart: Engine-only mode without secondary storage Camunda 8.9 introduces `global.noSecondaryStorage` mode to allow running the Orchestration engine without any secondary storage (Elasticsearch, OpenSearch, or RDBMS). This is useful for lightweight testing or scenarios where only the core engine is needed. When enabled, Elasticsearch and OpenSearch subcharts must be disabled. Authentication is supported using OIDC (recommended) or Basic authentication with unprotected API mode for development environments. [Learn about authentication with no secondary storage](/self-managed/concepts/secondary-storage/no-secondary-storage.md#authentication) New #### Helm chart `values.yaml` options for RDBMS Camunda 8.9 adds RDBMS configuration options to the Helm chart's `values.yaml` file, providing a first-class alternative to Elasticsearch and OpenSearch. - Configure database connections directly under `orchestration.data.secondaryStorage.rdbms`, including JDBC URL and authentication. - Supports all databases in the [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md): PostgreSQL, Oracle, MariaDB, MySQL, Microsoft SQL Server, H2, and Amazon Aurora. - Advanced authentication and custom JDBC drivers can be configured via init containers or mounted volumes. [Configure RDBMS in Helm charts](/self-managed/deployment/helm/configure/database/rdbms.md) New #### Helm chart: New restore options Camunda 8.9 adds new configuration options for backup restore operations in the Helm chart, giving operators more control over restore behavior via `values.yaml`. New #### Helm chart: Kubernetes Gateway API support Camunda 8.9 adds support for the [Kubernetes Gateway API](https://gateway-api.sigs.k8s.io/) as an alternative to Ingress for routing external traffic to Camunda components. Setting `global.gateway.enabled: true` provisions `Gateway`, `HTTPRoute`, and `GRPCRoute` resources. Configure the gateway class, listeners, and per-component route options under `global.gateway.*` in your `values.yaml`. New #### Helm chart: TLS JKS keystore password configuration Camunda 8.9 adds `global.elasticsearch.tls.jks.password` and `global.opensearch.tls.jks.password` fields, allowing you to set the JKS keystore password directly in `values.yaml` instead of using workarounds. New #### Helm chart: App Integrations exporter configuration Camunda 8.9 adds configuration options for the App Integrations exporter in the Helm chart's `values.yaml`, allowing you to configure how process data is exported for application integrations. New #### Helm chart: Helm template support for pod labels, annotations, and Ingress host `podLabels`, `podAnnotations`, and `global.ingress.host` now support Go template expressions (`tpl`). This enables dynamic configurations such as DataDog APM integration labels and environment-driven GitOps Ingress hostnames. New #### Secure connectivity (AWS PrivateLink) for SaaS Camunda 8.9 introduces Secure connectivity for AWS-hosted Orchestration Clusters in Camunda 8 SaaS. Secure connectivity enables private inbound access from your AWS VPC to your cluster using AWS PrivateLink, without routing traffic over the public internet. - Applies per cluster. - Supports inbound connectivity only. - Public connectivity remains enabled. [Secure connectivity (AWS PrivateLink)](../../../components/saas/secure-connectivity/index.md) New #### Cluster variable support Camunda 8.9 introduces cluster variables, allowing you to centrally manage configuration across your cluster. [Cluster variables](/components/modeler/feel/cluster-variable/overview.md) New #### MySQL and Microsoft SQL Server secondary storage Camunda 8.9 extends RDBMS secondary storage to include MySQL and Microsoft SQL Server as additional options for the Orchestration cluster. New #### RDBMS secondary storage Camunda 8.9 introduces optional RDBMS secondary storage as an alternative to Elasticsearch or OpenSearch. This enables teams to use relational databases such as H2, PostgreSQL, Oracle, or MariaDB for storing and querying process data, reducing operational complexity for non-high-performance use cases. New #### Standardized JDBC driver management for RDBMS Camunda 8.9 adds a standardized JDBC driver management system for manual installations. - A new `/driver-lib` directory separates Camunda-bundled drivers from customer-supplied ones. - Customers can add and configure their own drivers (for example, Oracle JDBC), while maintaining full compliance and version control. Change #### Reference architectures: Operator-based infrastructure All Self-Managed [reference architectures](https://github.com/camunda/camunda-deployment-references) now use Kubernetes operators (CloudNativePG, ECK, Keycloak operator) instead of embedded Bitnami subcharts for infrastructure services. This applies to AKS, EKS, OpenShift, and Kind deployments. **Action:** If you follow a reference architecture and currently use Bitnami-managed infrastructure, use the [migration tooling](/self-managed/deployment/helm/operational-tasks/migration-from-bitnami/index.md) to transition to operator-managed services. Change #### Reference architectures: Generic OIDC replaces Keycloak as default Reference architectures now use auth overlays with generic OIDC instead of Keycloak by default, enabling flexible identity provider integration. **Action:** If you deploy using a reference architecture with the default identity provider configuration, review the updated auth overlay settings. New #### Reference architectures: Amazon ECS on Fargate A new container-based reference architecture is available for deploying Camunda on AWS ECS with Fargate, without requiring Kubernetes. [Deploy to Amazon ECS](/self-managed/deployment/containers/cloud-providers/amazon/aws-ecs.md) New #### Reference architectures: AKS RDBMS variant and Kind local development - A lighter Azure AKS reference architecture using PostgreSQL as secondary storage (no Elasticsearch) with PostgreSQL 17 support is now available. - A Kind (Kubernetes in Docker) reference architecture is available for local development and testing. New #### Reference architectures: OpenShift dual-region with ECK OpenShift dual-region reference architecture now supports ECK (Elastic Cloud on Kubernetes) operator for Elasticsearch. EKS dual-region also uses the ECK operator, and headless service DNS is now used for initial contact points. ## Identity Change #### Orchestration Cluster Identity renamed to Admin Starting with Camunda 8.9, the Orchestration Cluster Identity component has been renamed to **Admin** (also referred to as Orchestration Cluster Admin). Admin is the cluster-level admin UI hosting identity management and other administrative features. Identity management functionality (users, groups, roles, authorizations, tenants, mapping rules, and clients) is unchanged, although some naming, paths, and config keys are updated as follows: - The `admin` Spring profile replaces the `identity` profile. Both profiles work interchangeably in 8.9. The `identity` profile is deprecated and will be removed in a future version. - API paths change from `/identity/*` to `/admin/*`. The old paths redirect to the new paths but are deprecated. - Helm values change from `orchestration.identity.*` to `orchestration.admin.*`. The old values are deprecated. - Documentation paths are updated: `/components/identity/` is now `/components/admin/`. [Introduction to Admin](/components/admin/admin-introduction.md) ## Modeler Breaking change #### Web Modeler: `webapp` component removed The Web Modeler system architecture has been simplified to enable easier and smoother installation and configuration of Web Modeler in a Self-Managed deployment. The separate `webapp` component has been removed and its functionality is now completely integrated into the `restapi` component. This change might require updates to your application configuration. [Migrate configuration](/self-managed/upgrade/components/880-to-890.md#migrate-webapp-configuration) Breaking change #### Web Modeler: Default logging format changed By default, Web Modeler's `restapi` component now logs in a simple, readable format to the console instead of `JSON`. This change aligns with the current Orchestration Cluster logging default as defined in its [logging configuration](/self-managed/components/orchestration-cluster/core-settings/configuration/logging.md#pattern-layout-format). Breaking change #### Web Modeler: Embedded web server changed from Undertow to Tomcat Web Modeler now uses [Apache Tomcat](https://tomcat.apache.org/) as an embedded web server instead of [Undertow](https://undertow.io/). This aligns with the Orchestration Cluster. This enhancement ensures consistency across environments and simplifies setup for administrators. [Embedded web server](/self-managed/upgrade/components/880-to-890.md#embedded-web-server) Breaking change #### Web Modeler: Form deployment changes With Camunda 8.9, you can now deploy forms independently. This enhancement provides greater control over what is deployed and when, enabling more precise management of changes and updates across environments. As part of this improvement, we have removed the automatic deployment of [linked forms](/components/hub/workspace/modeler/modeling/advanced-modeling/form-linking.md). Forms must now be explicitly deployed, giving teams finer control over versioning, release timing, and deployment scope. This change supports more predictable deployments and helps teams manage updates with greater confidence and flexibility. :::info To learn more, see the [8.9.0-alpha5 release notes](/reference/announcements-release-notes/890/890-release-notes.md). ::: Breaking change #### Web Modeler: Invite collaborators who haven't logged in before The behavior across OIDC providers is now aligned. Invitation suggestions only include users who have logged in at least once. This is a breaking change for Web Modeler installations using Keycloak as the OIDC provider. Before 8.9, Keycloak returned all organization users, including those who had never logged in. You can now invite users who have not yet logged in to Web Modeler by entering their email address. They will appear as “invited” in the collaborators panel. After their first log in, they will be added to the project automatically. Inviting the entire organization only applies to users who have logged in at least once. [Add users to projects](/components/hub/workspace/modeler/collaboration/collaboration.md#add-users-to-projects) Breaking change #### Web Modeler: JSON format changes When using JSON as the output for the logs the structure has slightly changed: - `logger`: This field is now renamed to `loggerName`. - `thread`: Previously represented the name of the thread. Now we have an object named `threadContext` with a field `name` that has this value. See [Logging documentation](/self-managed/components/hub/configuration/logging.md#json-structure) for more information. Breaking change #### Web Modeler: Logging framework changed from Logback to Apache Log4j 2 Web Modeler now uses [Apache Log4j 2](https://logging.apache.org/log4j/2.x/) for logging, in alignment with what the Orchestration Cluster uses. This enhancement ensures consistency across environments and simplifies setup for administrators. New #### Web Modeler: RDBMS support (H2, MariaDB, MySQL) Camunda 8.9 adds support for H2, MariaDB, and MySQL as relational databases for Web Modeler. This enhancement aligns Web Modeler's database configuration with the Orchestration cluster, ensuring consistent setup and improved integration across environments. Change #### Web Modeler: The main process label has been removed from process applications Starting with Camunda 8.9, the main process label has been removed from process applications in Web Modeler. This removes all restrictions previously tied to the main process label, aligning the Web Modeler process application experience with the Desktop Modeler. As a result, process applications can now be synced with Git more easily. Additionally, users have the freedom to deploy and run any process within a process application, allowing them to design solutions with more than one primary entry point. Change #### Web Modeler: Users now have full control over version tags in process applications Starting with Camunda 8.9, users have full control over the `versionTag` attribute across all resource types in a process application. The `versionTag` is no longer automatically set on the main process XML when creating a process application version. Instead, users can set the `versionTag` manually in the properties panel for BPMN, DMN, form, and RPA files, allowing them to choose a `versionTag` for each resource independently. --- ## 8.9 Release notes | Minor release date | Scheduled end of maintenance | Changelog(s) | Upgrade guides | | :----------------- | :--------------------------- | :-------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------- | | 14 April 2026 | 13 October 2027 | [Patch Releases and Changelogs](#technical-changelogs-for-all-89x-releases) | [8.9 upgrade guides](/reference/announcements-release-notes/890/whats-new-in-89.md#upgrade-guides) | :::info 8.9 resources - See [What's new in Camunda 8.9](/reference/announcements-release-notes/890/whats-new-in-89.md) for important changes to consider when planning your upgrade from Camunda 8.8. - See [release announcements](/reference/announcements-release-notes/890/890-announcements.md) to learn more about supported environment changes, breaking changes, and deprecations. - Refer to the [quality board](https://github.com/orgs/camunda/projects/187/views/21) for an overview of known bugs by component and severity. ::: ### Technical Changelogs for all 8.9.x releases
Overview of all patch releases and their Changelogs in GitHub [Camunda 8.9.5 (08.05.2026)](https://github.com/camunda/camunda/releases/tag/8.9.5)[Camunda 8.9.4 (06.05.2026)](https://github.com/camunda/camunda/releases/tag/8.9.4)[Camunda 8.9.3 (05.05.2026)](https://github.com/camunda/camunda/releases/tag/8.9.3)[Camunda 8.9.2 (28.04.2026)](https://github.com/camunda/camunda/releases/tag/8.9.2)[Camunda 8.9.1 (21.04.2026)](https://github.com/camunda/camunda/releases/tag/8.9.1)[Camunda 8.9.0 (07.04.2026)](https://github.com/camunda/camunda/releases/tag/8.9.0)[Connectors 8.9.4 (20.05.2026)](https://github.com/camunda/connectors/releases/tag/8.9.4)[Connectors 8.9.3 (06.05.2026)](https://github.com/camunda/connectors/releases/tag/8.9.3)[Connectors 8.9.2 (30.04.2026)](https://github.com/camunda/connectors/releases/tag/8.9.2)[Connectors 8.9.1 (22.04.2026)](https://github.com/camunda/connectors/releases/tag/8.9.1)[Connectors 8.9.0 (08.04.2026)](https://github.com/camunda/connectors/releases/tag/8.9.0)
## Agentic orchestration Self-ManagedCamunda 8 RunAgentic orchestrationAI agents ### Agentic orchestration onboarding in Camunda 8 Run Camunda 8 Run now includes an "I don't have credentials" path so you can deploy and run agents without first configuring external LLM provider credentials. A new local LLM quick start also helps self-managed and enterprise-constrained teams try agentic orchestration without external provider setup. [MCP docs](/reference/mcp-docs/mcp-docs.md) [Get started with agentic orchestration](/guides/getting-started-agentic-orchestration.md) ### Orchestration Cluster MCP Server The Orchestration Cluster now includes a built-in MCP server so AI agents and MCP-compatible clients can discover and invoke Camunda tools using a standardized interface. Improvements to the MCP client include OAuth, API key, and custom-header authentication, streamable HTTP transport, and dedicated operation filters in Standalone mode. [Orchestration Cluster MCP Server](/apis-tools/orchestration-cluster-api-mcp/orchestration-cluster-api-mcp-overview.md) [MCP Client connector](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-client.md) ### A2A Client connectors Camunda 8.9 introduces A2A Client connectors for multi-agent collaboration scenarios, including polling and webhook-based response handling. These connectors let agents discover remote agents, exchange messages, and receive responses through multiple retrieval patterns. [A2A Client connectors](/components/early-access/alpha/a2a-client/a2a-client.md) ### Model provider configuration improvements AI agent model configuration in 8.9 adds AWS Bedrock API key authentication, model timeout settings, and query-parameter support for OpenAI-compatible endpoints. These updates make it easier to connect to enterprise-managed or custom model endpoints with stricter networking and authentication requirements. [AI Agent connector](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md) ## APIs & tools Self-ManagedSaaSOrchestration Cluster APIAgentic orchestrationAI agents ### Camunda c8ctl CLI Use the new command-line interface to interact with the Orchestration Cluster REST API directly from the terminal. Camunda c8ctl CLI is the successor to zbctl, providing a unified way to authenticate, manage processes, and query cluster resources for development, testing, and automation workflows. - Authenticate and connect to clusters, then run common actions directly from your terminal. - Supports core process management and query endpoints, including process instances, user tasks, and workers. [Camunda c8ctl CLI](/apis-tools/c8ctl/getting-started.md) ### Camunda Process Test You can now write and run JSON test cases with Camunda Process Test. - Define process tests in JSON instead of implementing test logic in Java. - Execute JSON test cases as part of standard JUnit 5 runs in local and CI/CD environments. Camunda Process Test also introduces a shared runtime mode as an alternative to managed runtime. Shared runtime uses one runtime across multiple test classes, which can significantly reduce overall test execution time. Camunda recommends enabling it in the configuration. [JSON test cases](/apis-tools/testing/json-test-cases.md) [Shared runtime configuration](/apis-tools/testing/configuration.md#shared-runtime) #### AI agent testing Camunda Process Test now adds dedicated support for AI agent testing, making it easier to test processes where agents choose different actions at runtime and produce non-deterministic output. - Judge assertions use a configured LLM to assess whether AI-generated output satisfies a natural-language expectation, so you can verify response quality and intent. - Conditional behavior helps you model reactive test behavior for agent-driven flows by watching process state changes and completing tasks as they appear, without blocking the test on a predefined path. [Test your AI agents with CPT](/components/agentic-orchestration/evaluate-agents/test-ai-agents.md) ### Camunda Docs MCP server Camunda 8.9 adds a documentation-focused MCP server so IDEs and AI tools can query the latest official Camunda documentation directly. This gives developers a lightweight way to bring product documentation into coding and support workflows without leaving their editor. [Camunda Docs MCP server](/reference/mcp-docs/mcp-docs.md) ## Camunda 8 Run Self-ManagedCamunda 8 Run ### Streamlined local setup and configuration Camunda 8 Run is easier to use in 8.9 with clearer CLI guidance, improved Elasticsearch startup errors, guided Java detection, clean-state support, and more visible connection and credential details. The 8.9 release also includes the unified configuration file and expanded documentation for local development workflows. The `--docker` flag and bundled Docker Compose files have been removed. Docker Compose is now a standalone distribution artifact. C8Run now opens Operate on startup instead of the quickstart documentation page. [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) ### Flexible local secondary storage Camunda 8 Run now uses H2 as the default secondary store for simpler local development, while also supporting the full set of relational secondary storage options such as PostgreSQL, MariaDB, MySQL, Oracle, and Microsoft SQL Server. This makes it easier to start small locally and still mirror production-like database choices when needed. [Camunda 8 Run secondary storage](/self-managed/quickstart/developer-quickstart/c8run/secondary-storage.md) ## Connectivity SaaSNetworking ### Secure connectivity with AWS inbound PrivateLink You can now connect to Camunda SaaS Orchestration clusters from AWS VPCs using AWS PrivateLink. Inbound traffic stays on private AWS networking instead of traversing the public internet, so job workers and inbound connectors can run inside customer VPCs with stronger network isolation and compliance alignment. [Secure connectivity (AWS PrivateLink)](/components/saas/secure-connectivity/index.md) ## Connectors Self-ManagedSaaSConnectors ### Connector runtime and inbound activation improvements In 8.9, the connectors runtime adopts virtual threads by default for better scalability, and inbound connectors can now stay active for older process versions that still have running instances. Together, these changes improve throughput while avoiding situations where active instances of earlier versions cannot complete. ### Connector authentication and usability updates Connector improvements in 8.9 include Amazon Textract usability updates, OAuth 2.0 support for the Azure Blob Storage connector, and `noAuth` SMTP mode for the Email connector. These additions reduce custom setup work across common document, storage, and messaging integrations. [Amazon Textract connector](/components/connectors/out-of-the-box-connectors/amazon-textract.md) [Azure Blob Storage connector](/components/connectors/out-of-the-box-connectors/azure-blob-storage.md) [Email connector](/components/connectors/out-of-the-box-connectors/email-outbound.md) ## Console Self-ManagedSaaSConsole ### Cluster monitoring and backup operations Console includes a new Cluster Metrics endpoint for SaaS clusters and reduces the backup cooldown between scheduled and manual backups to 15 minutes. This makes it easier to integrate cluster monitoring into existing observability stacks while running more frequent backup routines safely. [Cluster Metrics endpoint](/components/saas/monitoring/cluster-metrics-endpoint/index.md) [SaaS backups](/components/saas/backups.md) ### Secrets, descriptions, and usage reporting Camunda 8.9 adds bulk secret import from `.env` files, cluster-to-cluster secret import and export, editable cluster descriptions, and per-tenant usage metrics for Self-Managed environments. Console also adds a new AWS US East region to expand regional deployment choices for SaaS clusters. [Connector secrets](/components/hub/organization/manage-clusters/manage-secrets.md) [Create a cluster](/components/hub/organization/manage-clusters/create-cluster.md) [Supported AWS regions](/components/saas/regions.md#amazon-web-services-aws-regions) ## Desktop Modeler Self-ManagedSaaSDesktop Modeler ### Connection management Desktop Modeler now lets you save and manage Camunda connections directly in the application, including credentials and deployment targets. This reduces deployment friction by letting you reuse saved connections instead of re-entering cluster details for each deployment. ### Clipboard and autosave improvements Desktop Modeler now supports shared clipboard behavior across BPMN tools together with quick duplication via keyboard shortcuts, and it automatically saves changes when you switch tabs or the app loses focus. These improvements make editing faster while reducing the risk of unsaved work. ## Helm chart deployment Helm chartsSelf-Managed ### Alternative infrastructure services for Self-Managed Camunda 8.9 continues to move away from bundled Bitnami infrastructure in Self-Managed Helm deployments. - In 8.9, PostgreSQL, Elasticsearch, and Keycloak sub-charts are disabled by default. - Reference architectures now use external managed services or vendor-supported operators. This update describes infrastructure deployment patterns in reference architectures, not customer data migration. [EKS dual-region reference architecture](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/dual-region.md) [OpenShift dual-region reference architecture](/self-managed/deployment/helm/cloud-providers/openshift/dual-region.md) [Dual-region operational tasks](/self-managed/deployment/helm/operational-tasks/dual-region-ops.md) ### Migration from Bitnami subcharts Camunda 8.9 adds migration guidance for customers moving Self-Managed Helm deployments away from deprecated Bitnami subcharts. - Use the migration guides to move PostgreSQL, Elasticsearch, and Keycloak from Bitnami subcharts to independent infrastructure services. - Follow the documented migration phases to plan backup, cutover, validation, and cleanup with minimal disruption. [Migrate from Bitnami subcharts](/self-managed/deployment/helm/operational-tasks/migration-from-bitnami/index.md) ### Gateway API, templating, and Helm 4 support The 8.9 Helm chart adds Kubernetes Gateway API support, documents templated values in `values.yaml`, and includes guidance for Helm 4 adoption. These updates make it easier to modernize Ingress, reuse dynamic values across environments, and prepare for Helm 3 end of life. [Gateway API setup](/self-managed/deployment/helm/configure/ingress/gateway-api-setup.md) [Helm chart parameters](/self-managed/deployment/helm/chart-parameters.md) [Helm 4](/self-managed/deployment/helm/operational-tasks/helm-v4.md) ### Secondary storage and authorization defaults Helm deployments in 8.9 now support fully integrated RDBMS secondary storage, expose the Orchestration Cluster REST port on 8080 by default, and allow authorization rules to be initialized directly from `values.yaml`. This reduces post-install manual setup and makes Helm-based deployments align more closely with current platform defaults. [Configuring secondary storage](/self-managed/concepts/secondary-storage/configuring-secondary-storage.md) ## Reference architectures Self-ManagedDeployment references Camunda 8.9 brings significant updates to the [deployment references](https://github.com/camunda/camunda-deployment-references) used by Self-Managed reference architectures. Key changes since 8.8 include new deployment options, an operator-based infrastructure model, and migration tooling. ### Operator-based infrastructure All reference architectures now use Kubernetes operators instead of embedded Bitnami subcharts for infrastructure services: - **CloudNativePG operator** replaces Bitnami PostgreSQL. - **ECK (Elastic Cloud on Kubernetes) operator** replaces Bitnami Elasticsearch. - **Keycloak operator** replaces the Bitnami Keycloak Helm subchart. This applies to AKS, EKS, OpenShift, and Kind reference architectures. [AKS reference architecture](/self-managed/deployment/helm/cloud-providers/azure/microsoft-aks/aks-helm.md) [OpenShift reference architecture](/self-managed/deployment/helm/cloud-providers/openshift/redhat-openshift.md) ### Migration from Bitnami to operators A new migration tooling set is available to move existing Self-Managed deployments from Bitnami-managed infrastructure to operator-managed infrastructure. The migration follows a 5-phase process (deploy targets, backup, cutover, validation, cleanup) with typical downtime of 5–40 minutes during the cutover phase. [Migrate from Bitnami subcharts](/self-managed/deployment/helm/operational-tasks/migration-from-bitnami/index.md) ### New reference architectures - **Amazon ECS on Fargate**: A new container-based deployment option on AWS without Kubernetes, using Elastic File System (EFS) for shared storage and restore init containers for backup/restore. - **AKS RDBMS variant**: A lighter Azure AKS reference architecture using PostgreSQL as secondary storage instead of Elasticsearch, with PostgreSQL 17 support. - **Kind local development**: A local Kubernetes-in-Docker reference architecture for development and testing, with Makefile-based commands and support for both PostgreSQL and Elasticsearch secondary storage variants. [Deploy to Amazon ECS](/self-managed/deployment/containers/cloud-providers/amazon/aws-ecs.md) ### Dual-region improvements - EKS and OpenShift dual-region reference architectures now use the ECK operator for Elasticsearch. - Headless service DNS is now used for initial contact points in dual-region setups, improving cross-region connectivity reliability. - OpenShift dual-region with ECK is now available. [EKS dual-region reference architecture](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/dual-region.md) [OpenShift dual-region reference architecture](/self-managed/deployment/helm/cloud-providers/openshift/dual-region.md) ### Additional improvements - **Generic OIDC support**: Reference architectures now use auth overlays with generic OIDC instead of Keycloak by default, enabling flexible identity provider integration. - **Ingress migration**: Kind reference architecture migrated from NGINX Ingress to Contour. - **Storage encryption**: Storage classes now support encryption by default. - **Configurable Elasticsearch index prefixes**: Custom `ES_INDEX_PREFIXES` can now be set for multi-tenant or isolated deployments. - **Helm deprecation checks**: All reference architecture CI workflows now include Helm deprecation detection for unknown keys in deployed values. - **Security**: NGINX Ingress patched to address CVEs; Artifactory authentication added for EC2 artifact downloads. ## Intelligent document processing (IDP) Self-ManagedSaaSIDP ### Classification templates and extraction engine choices Camunda 8.9 adds IDP document classification templates and lets you choose a text extraction engine per template. This gives teams more control over how different document types are categorized and processed, whether they need lightweight parsing, OCR, or multimodal LLM interpretation. ## Integrations Self-ManagedSaaSIntegrations ### Microsoft Teams app for Self-Managed The Camunda for Microsoft Teams app is now available for Self-Managed environments as well as SaaS. This lets teams claim and complete Camunda tasks directly in Microsoft Teams across more deployment models. [Camunda for Microsoft Teams](/components/camunda-integrations/ms-teams/ms-teams.md) ## Migration from Camunda 7 to Camunda 8 ### Conditional events support in Migration Analyzer and Diagram Converter Migration Analyzer and Diagram Converter now detect and convert Camunda 7 conditional events (start, intermediate catch, and boundary events) into Camunda 8-compatible BPMN. The migration report also flags conditional-event follow-up tasks so teams can plan manual review where needed. [Diagram Converter](/guides/migrating-from-camunda-7/migration-tooling/diagram-converter.md) [Convert expressions](/guides/migrating-from-camunda-7/migration-tooling/diagram-converter.md#convert-expressions) ### History Data Migrator Data Migrator now supports migrating historic audit data from Camunda 7 to Camunda 8. [History Data Migrator](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/history.md) ### Audit log migration coverage Camunda 8.9 expands migration support by converting Camunda 7 `UserOperationLog` entries into the Camunda 8 audit log format. This helps preserve user-operation history for compliance and operational review as part of a broader migration program. [History migration coverage](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/limitations.md#camunda-8-history-migration-coverage) ### Camunda 8 conditional events support Camunda 8.9 adds support for BPMN conditional events in the platform itself, complementing the new migration-tooling support for detecting and converting them. This makes migration planning easier because the modeled behavior and the migration path are both available in the same minor release. [Conditional events](/components/modeler/bpmn/conditional-events/conditional-events.md) ## Operate Self-ManagedSaaSOperate ### Dashboard focuses on active processes The Operate dashboard now only displays processes with active instances. Processes without running or incident instances are no longer shown, providing a clean view of what is currently running in your cluster. ### Unified, context-aware process instance view Operate now unifies process instance information into a single, context-aware view. Instead of switching between diagram, instance history, popovers, and bottom panels, you can inspect incidents, variables, executions, and modeling properties in one overview-to-detail flow. ### Batch operations monitoring Operate now includes a dedicated Batch Operations view backed by the Orchestration Cluster API. This gives operators a centralized way to track progress, review item-level results, and control running batch operations. [Monitor batch operations](/components/operate/userguide/monitor-batch-operations.md) ## Orchestration Cluster Self-ManagedSaaSJobs ### Job Dashboard The Job Dashboard provides a unified view of jobs and associated workers across the cluster, including Connectors and RPA. Use it to monitor job creation, completion trends, failures, and worker activity for faster troubleshooting without custom-built monitoring dashboards. ### Cluster variables and process governance Camunda 8.9 adds cluster variables, Business ID support for process instances, and broader task and listener management capabilities. These additions make it easier to centralize runtime configuration, enforce idempotent process starts, manage global user task listeners, and apply finer-grained task permissions. [Cluster variables](/components/modeler/feel/cluster-variable/overview.md) [Global user task listeners](/components/concepts/global-user-task-listeners.md) ### Platform configuration and deployment model updates The Orchestration Cluster continues its 8.9 consolidation work with completed unified configuration property changes, gateway-served application profiles for Operate, Tasklist, and Identity, and declarative preconfiguration support for Identity entities. Self-Managed deployments also gain support for Amazon ECS and improved dynamic tenant access for machine clients. [Property changes in Camunda 8.9](/self-managed/components/orchestration-cluster/core-settings/configuration/configuration-mapping.md) [Deploy to Amazon ECS](/self-managed/deployment/containers/cloud-providers/amazon/aws-ecs.md) ### Storage, backup, and runtime improvements Orchestration Cluster enhancements in 8.9 include scheduled backups, Elasticsearch 9 support, Amazon Aurora as secondary storage, per-broker RocksDB memory sizing, and the unified RocksDB cache model introduced in the first alpha. Together, these changes improve backup automation, storage flexibility, and operational tuning for Self-Managed deployments. [Scheduled backup](/self-managed/operational-guides/backup-restore/rdbms/backup.md#scheduled-backup) [Zeebe memory allocation](/self-managed/components/orchestration-cluster/zeebe/operations/resource-planning.md#memory) ### Process and data management improvements Camunda 8.9 adds support for deleting process and decision data, retaining hierarchy data until root-instance deletion, modifying elements inside multi-instance ad-hoc subprocesses, and updating cluster variables directly in the UI. The release also introduces a centralized user operations audit log and expands audit log access across platform APIs and applications. [Process instance modification](/components/concepts/process-instance-modification.md) [Audit log](/components/audit-log/overview.md) ### Tasklist V1 OpenSearch compatibility OpenSearch 3.6.0 is not supported when Tasklist V1 is enabled. Please use OpenSearch 2.19+ or versions 3.4.x–3.5.x, or migrate to Tasklist V2. :::note Tasklist V1 is planned for removal in version 8.10, so we strongly recommend migrating to Tasklist V2 to avoid future compatibility issues. ::: ## RDBMS secondary storage Self-ManagedData ### Expanded database coverage and driver handling Across the 8.9 release cycle, RDBMS secondary storage expands from early support for H2, PostgreSQL, Oracle, and MariaDB to include MySQL, Microsoft SQL Server, and Amazon Aurora. Camunda 8.9 also introduces standardized JDBC driver handling for manual installations so teams can separate bundled and customer-supplied drivers more cleanly. [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) ### Helm, manual, and production deployment guidance Camunda 8.9 adds end-to-end deployment guidance for RDBMS-backed environments, including Helm configuration, manual installation, production installation guides, SQL and Liquibase scripts, and a dedicated setup guide spanning the Orchestration Cluster and Web Modeler. Web Modeler also gains relational database support that aligns with the broader Orchestration Cluster options. [RDBMS setup guide](/self-managed/concepts/databases/relational-db/rdbms-setup-guide.md) [Manual installation with RDBMS](/self-managed/deployment/manual/rdbms/index.md) [RDBMS Helm deployment](/self-managed/deployment/helm/install/helm-with-rdbms.md) ### Backup, restore, and analytics patterns When using RDBMS secondary storage, Camunda 8.9 adds continuous backup and restore support and documents how to combine an RDBMS-backed Orchestration Cluster with Optimize when analytics require Elasticsearch or OpenSearch. This gives teams a clearer path for disaster recovery and mixed operational versus analytics storage strategies. [Back up and restore](/self-managed/operational-guides/backup-restore/backup-and-restore.md) ## Robotic Process Automation (RPA) Self-ManagedSaaSRPAWeb Modeler ### RPA support in Web Modeler Web Modeler now supports RPA scripts as a first-class file type, including visibility, project management, and deployment workflows without relying on Desktop Modeler. [Get started with RPA](/components/rpa/getting-started.md) ## Web Modeler Self-ManagedSaaSWeb Modeler ### Unified deployment experience across file types in Web Modeler Web Modeler now offers a unified deployment experience across BPMN, DMN, forms, and RPA scripts in process applications. - Deploy any BPMN, DMN, form, or RPA script inside a process application directly from its canvas view. - Deploy and run any BPMN file directly with full flexibility, as the main process label and its restrictions have been removed. - See more clearly which linked files are included when deploying within a process application. - Choose whether to deploy the current file or the applicable process application bundle, depending on context. This update reduces hidden deployment behavior, lowers cognitive load, and makes deployment workflows more predictable and extensible for future asset types. [Process applications in Web Modeler](/components/hub/workspace/manage-projects/manage-projects.md) [Validate and deploy your process application](/components/hub/workspace/manage-projects/deploy-project.md) ### Collaboration and import improvements Web Modeler in 8.9 adds more reliable live collaboration with canvas locking and takeover behavior, and it supports importing large process applications with up to 100 supported files in a single step. These changes improve shared editing workflows and make it easier to bring existing assets into Modeler. [Collaborate in Web Modeler](/components/hub/workspace/modeler/collaboration/collaboration.md) [Import resources into Web Modeler](/components/hub/workspace/modeler/modeling/importing-resources.md) ### Modeling and administration improvements Camunda 8.9 expands Web Modeler with broader event template support, email-based project invitations for all supported OIDC providers, and a simpler Self-Managed system architecture by folding the former `webapp` component into `restapi`. Additional 8.9 updates include embedded Tomcat for Self-Managed deployments and a SaaS endpoint for monitoring Web Modeler egress IP ranges. [Element templates in Modeler](/components/modeler/element-templates/defining-templates.md) [Add users to projects](/components/hub/workspace/modeler/collaboration/collaboration.md#add-users-to-projects) [Embedded web server](/self-managed/upgrade/components/880-to-890.md#embedded-web-server) ## 8.9.0-alpha5 | Release date | Changelog(s) | | :------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 10 March 2026 | [ Camunda 8 core ](https://github.com/camunda/camunda/releases/tag/8.9.0-alpha5)[ Connectors ](https://github.com/camunda/connectors/releases/tag/8.9.0-alpha5) | ### Agentic orchestration Self-ManagedSaaSAgentic orchestrationAI agents #### Orchestration Cluster MCP support The Orchestration Cluster now exposes its operational capabilities via a built-in Model Context Protocol (MCP) server, enabling AI agents and LLM-powered applications to access Camunda data using a standardized interface. - Connect any MCP-compliant client (such as VS Code, GitHub Copilot, or Claude Code) to discover and invoke Camunda tools without custom API integration code. - Available tools cover process definitions, process instances, user tasks, incidents, and variables. - Streamable HTTP transport is supported, using the same authentication model as the REST API. - The MCP server is enabled by default in Camunda 8 Run. You can enable the MCP server in Camunda SaaS via your cluster settings in Console. [Orchestration Cluster MCP Server](/apis-tools/orchestration-cluster-api-mcp/orchestration-cluster-api-mcp-overview.md) ### Audit log Self-ManagedSaaSOperateTasklistIdentity You can use the new user operations audit log to access a record of operations, including who performed an operation, when it was performed, and on which entities the operation was performed. Use the audit log to: - Prove compliance - Meet governance and regulatory requirements - Maintain operational integrity and transparency - Troubleshoot issues This feature is available in Operate, Identity, Tasklist, and the [Camunda 8 REST API](/apis-tools/orchestration-cluster-api-rest/specifications/search-audit-logs.api.mdx). [Audit log](/components/audit-log/overview.md) ### BPMN conditional events Self-ManagedSaaSBPMN Camunda 8 now supports BPMN conditional events, allowing users to start, continue, or interrupt process execution dynamically based on evaluated conditions. This enhancement provides first-class support for conditional start, boundary, and intermediate catch events, making process automation more expressive and migration from Camunda 7 smoother. [Conditional events](/components/modeler/bpmn/conditional-events/conditional-events.md) ### Business ID Self-ManagedSaaSProcess instances You can now create a process instance with a Business ID. - A Business ID is an immutable string identifier that associates a process instance with a meaningful business entity, such as an order ID, claim number, or customer reference. - Business ID uniqueness validation can be enabled at the cluster level to enforce idempotent process starts. If enabled, only one running root process instance per process definition can have the same Business ID. Attempts to start a duplicate instance are deterministically rejected. - Once visibility features are enabled in a future release, you will be able to trace process instances by Business ID retroactively at the process level. ### Camunda 8 Run Self-ManagedCamunda 8 Run #### Expanded RDBMS support You can now configure Camunda 8 Run to use any of the supported secondary storage relational databases instead of the default H2 (for example, PostgreSQL, MariaDB, MySQL, Oracle, or Microsoft SQL Server). This allows you to set up your local environment to match your production deployment, enabling deeper testing, faster debugging, and easier team onboarding. [Camunda 8 Run external relational database options](/self-managed/quickstart/developer-quickstart/c8run/secondary-storage.md#external-relational-database-options) ### Console SaaSConsole #### Reduce manual backup cooldown in SaaS The cooldown time between manual and scheduled backups in SaaS is reduced to 15 minutes. You can now safely combine frequent scheduled backups with additional on‑demand manual backups (for example, hourly scheduled backups plus a daily manual backup) without hitting unexpected rate‑limit conflicts, while still benefiting from separate retention of manual and scheduled backups. [SaaS backups](/components/saas/backups.md) ### Deployment Self-ManagedConfigurationHelmDocker #### ARM Docker production support You can now use the `linux/arm64` image for Docker production environments. [Camunda Docker images](/self-managed/deployment/docker/docker.md) #### Connect to Elastic/OpenSearch with multi-host names You can now configure multiple Elasticsearch or OpenSearch host URLs for Camunda 8 components, removing the requirement for a single endpoint or load balancer in front of your search cluster. - This enables seamless connectivity in environments where managed Elasticsearch or OpenSearch providers supply a list of host URLs rather than a single FQDN. - Multi-host configuration is supported for Zeebe, Operate, Tasklist, Optimize, and Integration components, and can be set via Helm chart values or application configuration. [Property reference](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#data---secondary-storage) #### Proxy support for Elasticsearch and AWS OpenSearch You can now configure outbound proxy settings in Camunda 8 components to connect to Elastic, OpenSearch (AWS OpenSearch). - This enables seamless integration with AWS-hosted OpenSearch services in environments where proxy routing is required (for example, corporate networks or regulated production setups). - This feature is supported for Zeebe, Operate, Tasklist, Optimize, and Integration components. [Property reference](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#data---secondary-storage) #### Gateway API support The Kubernetes Gateway API is now supported in the official Helm chart, allowing you to expose Operate, Tasklist, Console, Web Modeler, Identity, Optimize, and Zeebe (REST and gRPC) using Gateway and Route resources instead of relying on ingress-nginx. You can use new Helm values to choose between Ingress and Gateway per deployment, with documented step-by-step examples and migration guidance so you can move away from ingress-nginx before it is retired in March 2026. [Configure the Helm chart with Gateway API](/self-managed/deployment/helm/configure/ingress/gateway-api-setup.md) #### Helm chart values templating The Helm chart now documents all values supporting Go template expressions, including guidance on how `values.yaml` templating is evaluated. - `podLabels`, `podAnnotations` (all components), and `global.ingress.host` now support templating via `{{ "{{" }} }}` expressions (for example, `{{ "{{" }} .Release.Name }}`). - This feature enables dynamic configuration for multi-environment deployments and integrations, such as Datadog APM. [Helm chart parameters](/self-managed/deployment/helm/chart-parameters.md) #### Helm 4 support As Helm 3 reaches end of life in 2026, Camunda continues to support your migration to Helm 4 with documentation covering how you can deploy Camunda 8.7, 8.8, and 8.9 with Helm 4. [Helm 4](/self-managed/deployment/helm/operational-tasks/helm-v4.md) :::note Camunda 8.10 and beyond will only support Helm 4 to ensure we provide secure solutions for customers. ::: ### Global user task listeners Self-ManagedSaaSListeners You can now use a new Orchestration Cluster API to manage global user task listeners. - The API is available in both SaaS and Self Managed environments and supports full lifecycle management of listener definitions. - Listener execution follows the same semantics as existing global listener behavior, ensuring consistent payloads, predictable integrations, and uniform governance across environments. - You can configure global user task listeners via [unified configuration](/components/concepts/global-user-task-listeners/configuration.md#configure-through-unified-configuration), [Orchestration Cluster API](/components/concepts/global-user-task-listeners/configuration.md#configure-via-orchestration-cluster-api), or the [Admin UI](/components/concepts/global-user-task-listeners/configuration.md#configure-via-admin-ui). [Global User task Listeners](/components/concepts/global-user-task-listeners.md) :::note Configuration-based global user task listeners were introduced for Self-Managed deployments in alpha 3. ::: ### Intelligent Document Processing (IDP) Self-ManagedSaaSIDP #### IDP document classification templates You can now use document classification templates to automatically categorize incoming documents by type (for example, invoice, contract, or claim form) using LLM-powered analysis. - Configure classification templates directly in the UI without writing code. - Define an explicit list of expected document types, or use auto-classification to let the LLM suggest document types. - Customize the system prompt used for classification to fine-tune results for your organization. - Test templates with sample documents across different LLMs to evaluate accuracy before publishing. - Classification results are output as structured data, making them immediately usable in your BPMN process for routing and downstream automation. #### IDP multiple text extraction engines You can now choose the text extraction engine on a per-template basis, allowing you to optimize accuracy, performance, and cost for different document types. - Use lightweight parsing for digitally generated PDFs to reduce processing time and costs. - Select a high-accuracy OCR engine for scanned or image-based documents. - Bypass text extraction entirely to let multimodal LLMs natively interpret document content. ### Integrations Self-ManagedIntegrations The Camunda for Microsoft Teams app is now available in Self-Managed environments as well as SaaS. You can use this app to view, claim, and complete Camunda tasks directly in Microsoft Teams. [Camunda for Microsoft Teams](/components/camunda-integrations/ms-teams/ms-teams.md) ### Modeler Self-ManagedSaaSDesktop ModelerWeb Modeler #### Process application subfolders You can now add subfolders to your process applications, giving you more flexibility when organizing your files and allowing you to sync to your existing version control system without reorganizing the filesystem. [Process applications](/components/concepts/process-applications.md) #### Web Modeler: Improved Self-Managed installation The Web Modeler system architecture has been simplified to enable easier and smoother installation and configuration of Web Modeler in a Self-Managed deployment. The separate `webapp` component has been removed and its functionality is now completely integrated into the `restapi` component. ### Orchestration Cluster Self-ManagedSaaSOperateTasklist :::caution Bug: process instance distribution A [critical bug in 8.9-alpha5](https://github.com/camunda/camunda/issues/47955) causes uneven distribution of newly created process instances amongst partitions, with partition 1 being heavily favoured. As a result, clusters cannot handle as high a throughput as normal. This bug is fixed in the 8.9.0 release. ::: #### Application profile consolidation The Operate, Tasklist, and Identity application profiles are now merged into the existing gateway profile to provide a simplified but flexible deployment model. - These components are now treated as UIs served by the Zeebe Gateway. - Control their inclusion via configuration properties (for example, `camunda.webapps.enabled=operate,identity`). #### Amazon ECS (EC2+Fargate) support Camunda 8 officially supports running Orchestration Clusters on Amazon Elastic Container Service (Amazon ECS). This makes it easier and safer for teams that rely on Amazon ECS (including Fargate) to run Camunda 8 in production without needing to adopt Kubernetes (EKS). This feature relies on AWS S3. - A new guide covers validated deployment patterns for running brokers and related services on Amazon ECS with both EC2 and Fargate launch types. - Build-in storage safety guardrails, such as checks and guidance to prevent unsafe configurations (for example, multiple brokers writing to the same EFS volume). - Cluster membership handling offers improved handling of broker restarts and Amazon ECS task scheduling to ensure stable cluster operation. [Deploy to Amazon ECS](/self-managed/deployment/containers/cloud-providers/amazon/aws-ecs.md) #### Configure RocksDB memory per-broker You can now configure RocksDB memory on a per-broker basis instead of per-partition, simplifying capacity planning and aligning with familiar JVM-style sizing. - This reduces the risk of unexpected out-of-memory crashes, and is crucial for usage with dynamically adding partitions, making broker provisioning safer and more predictable. Going forward, Camunda recommends you use the `FRACTION` allocation strategy. - The default remains per-partition for backwards compatibility, but this is deprecated and will be changed to a fractional approach in 8.10, with a 10% default (using `camunda.data.primary-storage.rocks-db.memory-allocation-strategy=FRACTION` and `camunda.data.primary-storage.rocks-db.memory-fraction=0.1`). - Camunda recommends you test this out before 8.10 to find the right value, or configure the allocation strategy to `PARTITION`. [Zeebe memory allocation](/self-managed/components/orchestration-cluster/zeebe/operations/resource-planning.md#memory) #### Delete processes and decisions (instances and definitions) The Orchestration Cluster API and Operate UI now support secure, consistent deletion of process and decision definitions, as well as batch or individual deletion of finished process and decision instances (including all dependent data) from both primary and secondary storage. This feature ensures compliance, data consistency across regions, and simplified operational management of obsolete process data. #### Dynamic connector access to tenants in multi-tenant environments (Self-Managed) Use the new dynamic client access to tenants `TenantFilter` setting to define tenant access by explicit tenant assignment instead of defining access in the machine client’s configuration. If you use this setting, you no longer need to restart the associated run-time environment when adding new machine clients (such as job workers or connectors). [Java client: Filter by tenant](/apis-tools/java-client/job-worker.md#filtering-by-assigned-tenants) / [Spring Boot: Filter by tenant](/apis-tools/camunda-spring-boot-starter/configuration.md#filtering-by-assigned-tenants) #### Elasticsearch 9 support Camunda 8 now supports Elasticsearch 9 as a secondary data store, allowing you to take advantage of the latest Elasticsearch release for your deployments. - Elasticsearch 9 is a direct 1:1 compatibility update with no changes to Camunda APIs or query behavior. - You can plan your migration to Elasticsearch 9 and benefit from its improved performance and updated features while maintaining full compatibility with Camunda 8 components. - Elasticsearch 8.x continues to be supported for existing deployments. [Supported environments](/reference/supported-environments.md) #### Manage task permissions Granular task-level authorization is now integrated into the Tasklist UI and the Orchestration Cluster REST API. - Property-based task permissions: - Grant users permission to view or work on a task, based on task properties. - Permissions apply when the assignee matches the current user, or when the user belongs to a candidate group (or is listed as a candidate user). This ensures all relevant users have appropriate access, whether directly assigned or eligible to claim the task. - Permissions apply consistently across both the Tasklist UI and the orchestration cluster REST API. - Fine-grained security: Visibility and action permissions are scoped at the individual task level, reducing unauthorized access and improving compliance alignment. This feature strengthens security and usability, and provides a clear, consistent, and secure user experience for task workers, managers, and integrations. [Configure user task authorization](/components/tasklist/user-task-authorization.md) #### Modify elements in multi-instance ad-hoc sub-processes Operators can now dynamically activate, move, or remove element instances inside running multi-instance ad-hoc subprocesses—supporting parallel, sequential, and classic ad-hoc execution patterns. These new runtime capabilities, available in both Operate UI and the API, allow users to adapt, repair, and recover business processes on the fly, supporting flexibility for agentic automation, case management, and critical operations. [Process instance modification](/components/concepts/process-instance-modification.md) #### Monitor batch operations Operate now includes a dedicated Batch Operations view powered by the Orchestration Cluster API. - Users can search and filter batches, view progress and per-item results (including errors), and manage running batches via suspend, resume, and cancel actions. - This capability, aligned with Zeebe-managed batch operations introduced in 8.8, brings consistent and transparent monitoring across distributed partitions for improved operational control. [Monitor batch operations](/components/operate/userguide/monitor-batch-operations.md) #### Operate uses the Orchestration Cluster REST API Operate now uses the Orchestration Cluster REST API as its single interface for accessing and managing process data. This ensures a consistent Operate experience across OpenSearch, Elasticsearch, and RDBMS. You can continue to investigate, manage, and automate process operations without any loss of functionality while also reusing the same REST endpoints for custom tools and integrations. - Operate now uses the Orchestration Cluster REST API as the single backend interface. - The frontend is fully aligned with the Orchestration Cluster REST API (V2) across all supported data stores. - The V2 API has been extended with new endpoints to support Operate functionality. - Batch modification and monitoring behavior is updated to work with the new API. - Proactive permission checks in the UI have been removed: UI elements are now always visible, and permission errors are shown only when an action is attempted. [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) #### Preconfigure Identity entities You can now use declarative configuration for all Identity entities in the Orchestration Cluster, such as groups, tenants, roles, authorizations, and assignments. Previously, you could only use this for users, mapping rules, and default role memberships. #### Retention policy across hierarchy When deleting process instances, all child process and decision instances are retained until the root process instance is deleted. - This feature ensures that audit trails remain intact for the entire process hierarchy. - Even if a called process completes significantly earlier, its data remains available until the parent instance completes and expires under retention policy. - This guarantees continuous visibility and compliance-grade auditability for hierarchical workflows. [Hierarchy-aware data retention](/self-managed/components/orchestration-cluster/core-settings/concepts/data-retention.md#hierarchy-aware-retention) #### Schedule backups with the Orchestration Cluster You can now configure scheduled backup intervals and retention directly in the Orchestration Cluster. - External cron jobs are no longer needed. - Supports setting duration schedules, manual ad‑hoc backups, API‑based updates, metrics, and audit logs. - Backwards compatible with existing backup commands. [Scheduled backup (RDBMS)](/self-managed/operational-guides/backup-restore/rdbms/backup.md#scheduled-backup) #### Update cluster variables You can now update existing cluster variables in the Orchestration Cluster user interface, without needing to delete and recreate the variable. #### User operations audit log A new centralized, queryable audit log records all critical user and client operations across process, identity, and user task domains. - Teams can trace who performed each action and when, what was affected, and if the action was successful. - Audit entries are available via Orchestration Cluster APIs, and integrated into Operate, Tasklist, and Identity with built-in authorization controls. :::note See the [Audit log](#audit-log) entry for more information. ::: ### Process instance migration Self-ManagedSaaS #### Migrate from job-based user tasks to Camunda User Tasks As part of process instance migration, you can now migrate active instances from legacy job‑based user tasks to modern, engine‑managed Camunda User Tasks via both the API and the Operate UI. This lets you standardize on the Orchestration Cluster APIs and the recommended user task type before the removal of job‑based user task querying and management in the consolidated API. [Process instance migration](/components/concepts/process-instance-migration.md) ### RDBMS secondary storage Self-ManagedData #### Installation guide for RDBMS (Orchestration Cluster and Web Modeler) A new installation guide covers how you can configure Camunda 8 with relational databases across the Orchestration Cluster and Web Modeler. The guide covers database provisioning, connections and authentication including Aurora IAM, JDBC driver handling, optional schema management with SQL or Liquibase, and backup and restore considerations so teams can deploy faster with fewer errors and aligned best practices. [RDBMS setup guide](/self-managed/concepts/databases/relational-db/rdbms-setup-guide.md) #### Manual installation supports RDBMS secondary storage Camunda 8 Orchestration Clusters can now be installed manually (VM/bare metal/Java application) with full support for RDBMS (H2, PostgreSQL, Oracle, MariaDB) as secondary storage. [Manual installation with RDBMS](/self-managed/deployment/manual/rdbms/index.md) #### Production installation guides New RDBMS production installation guides for Camunda 8 are available: - Helm: Kubernetes-based orchestration cluster deployment via Helm, using RDBMS secondary storage. - Manual: Deploy and manage Camunda 8 using relational databases in production environments. [RDBMS Helm deployment](/self-managed/deployment/helm/install/helm-with-rdbms.md) #### Run Optimize with Orchestration Cluster RDBMS Orchestration Clusters can run with RDBMS as the sole secondary data store (for straightforward environments) or in combination with Optimize (which requires Elasticsearch or OpenSearch for analytics use cases). Administrators can: - Deploy a simple stack using only RDBMS if analytics are not required. - Add Optimize with Elasticsearch/OpenSearch for teams that require reporting and analytics. - Rely on strict separation of process data ingestion and storage responsibilities between orchestration and analytics. ## 8.9.0-alpha4 | Release date | Changelog(s) | | :--------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 10 February 2026 | [ Camunda 8 core ](https://github.com/camunda/camunda/releases/tag/8.9.0-alpha4)[ Connectors ](https://github.com/camunda/connectors/releases/tag/8.9.0-alpha4) | ### Agentic orchestration Self-ManagedSaaSAgentic orchestrationAI agentsConnectors #### MCP Client connector operations and filter options New operations are added in the `Standalone` mode to the MCP (Model Context Protocol) Client connector: - List resources - List resource templates - Read resource - List prompts - Get prompt In addition, the **Tools** panel in the element template is replaced by dedicated filter options for each operation. :::caution Upgrading the template - The **Tools** section in version 0 and 1 of the template is replaced by a **Filters** section in version 2. - As Tool filters are not migrated during a Template Version upgrade, you must manually upgrade the template with any previously configured Tool filters. ::: [MCP Client connector](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-client.md) ### Camunda 8 Run Self-ManagedCamunda 8 Run #### Expanded RDBMS support You can now configure Camunda 8 Run to use any of the supported secondary storage relational databases instead of the default H2. This allows you to set up your local environment to match your production deployments, enabling deeper testing, faster debugging, and easier team onboarding. #### Streamline your Camunda 8 Run experience Camunda 8 Run is now easier to use with improved setup and configuration. The CLI includes a helpful usage page, clearer error messages, especially for Elasticsearch startup, and prominently displays connection properties and credential information. A revamped Java detection guided setup, log cleanup options, and better defaults for development environments (such as disk watermark thresholds) have been added. You can also start fresh using a new clean-state command, and the unified configuration file is now included and thoroughly documented. [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) ### Connectors Self-ManagedSaaSConnectors #### Fetch active process definitions The connectors runtime now activates inbound connectors for earlier process versions if they have active instances waiting on message subscriptions. Previously, only inbound connectors from the latest process version were activated, which could prevent active instances of older versions from completing. ### Console SaaSConsole #### New AWS US East region With the new Camunda 8 SaaS **AWS US East (us-east-2)** region in North America, you can deploy orchestration workloads with full US data residency and improved regional stability. [Supported AWS regions](/components/saas/regions.md#amazon-web-services-aws-regions) ### Documentation Self-ManagedSaaSDocumentation #### Camunda Docs MCP server Use the Camunda Docs MCP server to query the latest official Camunda 8 documentation in your IDE or AI tool. - If you use an AI coding tool such as Cursor or Copilot, add the MCP server to help ensure more accurate AI responses and code generation using Camunda 8 documentation and context. - Once connected to the MCP server within your editor, you can ask context-aware questions about Camunda. - The MCP server is available at the following URL: `https://camunda-docs.mcp.kapa.ai`. [Camunda Docs MCP server](/reference/mcp-docs/mcp-docs.md) ### Modeler Self-ManagedSaaSDesktop ModelerWeb Modeler #### Collaborate in Web Modeler Live collaboration in Web Modeler is now more reliable with an improved collaboration experience. Once you start editing a diagram, the canvas locks so only you can continue making edits to the diagram. - Other users can view and interact with the diagram, but cannot make changes while it is locked by the current editor. - Users with edit permissions can take over editing of the diagram by clicking **Take over** in the canvas lock bar. This improvement creates a restricted but controlled collaborative environment, and helps prevent conflicts and broken sessions caused by multiple users editing the same diagram. [Collaborate in Web Modeler](/components/hub/workspace/modeler/collaboration/collaboration.md) #### Import large process applications into Web Modeler You can now import large process applications (containing a maximum of 100 supported files such as BPMN, DMN, forms, connector templates, and documentation) in a single step in Web Modeler or via direct import links in Self-Managed environments. [Import resources into Web Modeler](/components/hub/workspace/modeler/modeling/importing-resources.md) #### Shared global clipboard and duplicate elements In Desktop Modeler and Web Modeler, you can copy-paste BPMN elements seamlessly between Camunda 7/8 diagrams and across clipboard-enabled BPMN tools/websites, and also quickly duplicate via **Cmd+D** (Mac) / **Ctrl+D** (Windows). #### Tabs autosave in Desktop Modeler The tabs autosave feature automatically saves diagram changes when you switch tabs in Desktop Modeler or when the app loses focus (for example, app switch, window blur). This ensures work is saved immediately, making it visible to your IDE and other tools without manual intervention. ### Migration from Camunda 7 to Camunda 8 Self-ManagedSaaSCamunda 7 migration #### Audit log coverage for Camunda 7 to Camunda 8 migrations The audit log migrator automatically converts Camunda 7 `UserOperationLog` entries to Camunda 8 AuditLog format, preserving the complete history of user operations including who performed actions, what entities were affected (process instances, tasks, variables, decisions), operation types (create, update, delete, assign, complete), timestamps, and annotations. This ensures uninterrupted audit trail continuity across the migration, enabling customers to meet compliance requirements and maintain operational visibility without manual data reconstruction or workarounds. [Data Migrator history migration coverage](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/limitations.md#camunda-8-history-migration-coverage) ### Orchestration Cluster Self-ManagedSaaSData #### Schedule backups with the Orchestration Cluster You can now configure scheduled backup intervals and retention directly in the Orchestration Cluster. - External cron jobs are no longer needed. - Supports setting duration schedules, manual ad‑hoc backups, API‑based updates, metrics, and audit logs. - Backwards compatible with existing backup commands. #### Unified configuration for the Orchestration Cluster In Camunda 8.9, the remaining unified configuration project property changes are complete. - All 8.9 property changes are documented in the [Camunda 8.9 property changes](/self-managed/components/orchestration-cluster/core-settings/configuration/configuration-mapping.md#camunda-89-property-changes) table. - You can search, sort, and filter the table to show breaking changes, direct mappings, and new properties. - For more information on each property (including default values), see the [property reference](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md). [Property changes in Camunda 8.9](/self-managed/components/orchestration-cluster/core-settings/configuration/configuration-mapping.md) :::note Only the first partial set of the unified configuration project properties was introduced in Camunda 8.8. ::: ### Process instance migration Self-ManagedSaaS #### Migrate from job-based user tasks to Camunda User Tasks As part of process instance migration, you can now migrate active instances from legacy job‑based user tasks to modern, engine‑managed Camunda User Tasks via both the API and the Operate UI. This lets you standardize on the Orchestration Cluster APIs and the recommended user task type before the removal of job‑based user task querying and management in the consolidated API. [Process instance migration](/components/concepts/process-instance-migration.md#migrate-job-worker-user-tasks-to-camunda-user-tasks) ### RDBMS secondary storage Self-ManagedData #### Continuous backup and restore You can now back up and restore Camunda 8 when using RDBMS as secondary storage. - Independent backup control plans handle primary and secondary backups separately while ensuring they align when restored. - Disaster recovery is improved as you can recover Camunda instances with greater precision in data consistency. Additionally, the new restore API syntax now supports `--from` and `--to` timestamp flags, enabling automatic selection of a compatible backup range. - When no specific backup or timerange is specified, a restore is performed to the latest known position with no user interaction. - Ensures version compatibility across backups and offers an override via `--allow-version-mismatch`. - Reduces manual restore effort and enhances confidence in backup integrity, with reduced Recovery Time Objective (RTO). [Back up and restore](/self-managed/operational-guides/backup-restore/backup-and-restore.md) ### Helm chart deployment Self-ManagedHelm chart #### Default REST port unified to 8080 The Orchestration Cluster's default HTTP port in the Helm chart has changed from 8090 to 8080 (`orchestration.service.httpPort`), aligning with the Orchestration Cluster's default configuration. #### Authorization initialization via Helm You can now initialize authorization rules directly through `values.yaml`, reducing manual post-installation setup for access control configuration. ## 8.9.0-alpha3 | Release date | Changelog(s) | | :-------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 13 January 2026 | [ Camunda 8 core ](https://github.com/camunda/camunda/releases/tag/8.9.0-alpha3)[ Connectors ](https://github.com/camunda/connectors/releases/tag/8.9.0-alpha3) | ### Agentic orchestration Self-ManagedSaaSAgentic orchestrationAI agentsConnectors #### AWS Bedrock API key authentication support The Amazon Bedrock model configuration now allows authentication using (long-term) Bedrock API keys as an alternative to the already existing authentication methods. #### Model timeout configuration The AI Agent connectors now support setting a timeout value on supported models. #### Query parameters support on OpenAI compatible models The OpenAI compatible model configuration now allows configuration of query parameters to be added to the model endpoint URL. This might be needed for custom API endpoints requiring additional metadata (such as API versions) to be set via query parameters. [AI Agent connector](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md) ### Camunda 8 Run Self-ManagedCamunda 8 Run #### Streamline your Camunda 8 Run experience Camunda 8 Run is now easier to use with improved setup and configuration. The CLI includes a helpful usage page, clearer error messages, especially for Elasticsearch startup, and prominently displays connection properties and credential information. A revamped Java detection guided setup, log cleanup options, and better defaults for development environments (such as disk watermark thresholds) have been added. You can also start fresh using a new clean-state command, and the unified configuration file is now included and thoroughly documented. [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) #### Use H2 for data storage Camunda 8 Run now includes H2 as the default secondary data store, providing: - A lighter, simpler local development experience. - Lower memory usage. - A fully functional stack that doesn't require an external database. New documentation shows you how to: - Install Camunda 8 Run with H2 as the default secondary storage. - Seamlessly switch from H2 to Elasticsearch or OpenSearch when required. ### Console SaaSConsole Camunda 8.9 provides a Cluster Metrics endpoint for SaaS clusters. - Activate a secure metrics endpoint for your cluster and integrate it with Prometheus, Datadog, or any monitoring system that supports Prometheus scraping. - Get real-time visibility into cluster performance, troubleshoot faster, and integrate with your existing observability stack. [Cluster Metrics endpoint](/components/saas/monitoring/cluster-metrics-endpoint/index.md) ### Global user task listeners Self-Managed Camunda 8.9 introduces configuration-based global user task listeners for Self-Managed deployments. Administrators can define cluster-wide listeners using configuration files or environment variables, ensuring they are applied consistently from cluster startup and preserved across backup and restore operations. All user task lifecycle events emit payloads containing full variable context and metadata, enabling standardized integrations across all processes. [Global User task Listeners](/components/concepts/global-user-task-listeners.md) ### Modeler Self-ManagedSaaSDesktop ModelerWeb Modeler #### Desktop Modeler: Manage Camunda connections You can now manage Camunda connections directly in Desktop Modeler: - Add, edit, delete, and save multiple connections. - Securely store credentials and connection settings. - Deploy directly to saved connections. - Select an existing Orchestration Cluster or add a new one during deployment. This streamlines the deployment workflow and reduces setup friction. #### Web Modeler: Create event templates You can now create, discover, and apply templates for more BPMN event types, including message, signal, and timer, directly within the element template editor. You can also create global event templates that: - Are reusable across projects. - Standardize event configurations (for example, message names or payload structures). - Help ensure consistency across teams and models. [Element templates in Modeler](/components/modeler/element-templates/defining-templates.md) #### Web Modeler: Invite users via email As a Self-Managed administrator, you can now invite users to Web Modeler projects via email across all OIDC providers, eliminating the need to wait for users to log in first. - Email-based invitations work for all OIDC providers (Keycloak, Entra ID, Okta, Auth0), matching SaaS behavior. - Keycloak no longer receives special treatment; all providers follow the same invitation workflow. This enables faster project provisioning and a consistent administrator experience across identity providers. [Add users to projects](/components/hub/workspace/modeler/collaboration/collaboration.md#add-users-to-projects) ### Orchestration Cluster Self-ManagedSaaSDataFEEL expressions #### Manage configuration with cluster variables Camunda 8.9 now supports cluster variables, letting you centrally manage configuration across your cluster. You can access these variables directly in the Modeler using FEEL expressions: | Variable | Scope | Priority | | :--------------------- | :---------------------------------- | :------- | | `camunda.vars.cluster` | Global | Lowest | | `camunda.vars.tenant` | Tenant | Medium | | `camunda.vars.env` | Merged view with automatic priority | Highest | For example, if the same variable exists in multiple scopes, the priority is as follows: - A Tenant variable overrides a Global variable. - A Process-level variable has the highest priority, overriding both. This hierarchy allows you to create cascading configurations, where specific contexts override broader defaults. Cluster variables support simple key-value pairs and nested objects, which you can access with dot notation for complex structures. You can manage all cluster variables via the Orchestration Cluster API. You can edit cluster variables directly [Cluster variables](/components/modeler/feel/cluster-variable/overview.md) #### Use Amazon Aurora for secondary storage Camunda 8.9 now supports Amazon Aurora as a secondary data store for orchestration clusters, in addition to existing options. - Supports Aurora PostgreSQL (compatible with PostgreSQL 14–17). - Designed for secure, high-performance, cloud-native deployments. - Seamless integration with AWS features, including: - IAM / IRSA authentication. - High availability and failover. - Alignment with DBA best practices. Helm charts and manual installation guides now include tested configurations and step-by-step references for Aurora, reducing operational complexity and accelerating adoption for AWS-centric organizations. ### Process instance migration Self-ManagedSaaSAgentic orchestrationAI agents Camunda 8.9 now supports migration of process instances that include ad-hoc subprocesses, covering both single-instance and multi-instance (parallel and sequential) variants. With this enhancement, you can: - Safely migrate running instances. - Update AI agent flows. - Modernize process definitions without losing execution state. This unlocks more flexible, agent-driven orchestration and faster iteration on live automation. [Process instance migration](/components/concepts/process-instance-migration.md#migrate-active-elements-inside-ad-hoc-subprocesses) ### RDBMS secondary storage Self-ManagedData Camunda 8.9 Helm charts now support RDBMS as fully integrated secondary storage options for orchestration clusters, providing a first-class alternative to Elasticsearch and OpenSearch. With this update, administrators can: - Use RDBMS as an alternative to Elasticsearch or OpenSearch. - Configure database connections directly in `values.yaml`. - Enable advanced authentication and custom JDBC drivers. This allows enterprises to run Camunda 8 on familiar, enterprise-managed RDBMS infrastructure aligned with existing security, backup, and compliance requirements. #### No default secondary storage in Helm With RDBMS support, the Helm chart no longer defaults to Elasticsearch as secondary storage. You must now explicitly set `orchestration.data.secondaryStorage.type` to `elasticsearch`, `opensearch`, or `rdbms`. This also introduces `global.noSecondaryStorage` for engine-only deployments without any secondary storage. [Configuring secondary storage](/self-managed/concepts/secondary-storage/configuring-secondary-storage.md) ## 8.9.0-alpha2 | Release date | Changelog(s) | Blog | | :--------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--- | | 09 December 2025 | [ Camunda 8 core ](https://github.com/camunda/camunda/releases/tag/8.9.0-alpha2)[ Connectors ](https://github.com/camunda/connectors/releases/tag/8.9.0-alpha2) | - | ### Agentic orchestration Self-ManagedSaaSAgentic orchestrationAI agentsConnectors #### A2A Client connectors Agent-to-Agent (A2A) Client connectors allow you to interact with remote agents using the [A2A protocol](https://a2a-protocol.org/v0.3.0/specification/). **Connector** **Description** [A2A Client connector](/components/early-access/alpha/a2a-client/a2a-client-connector.md) Interact with A2A agents, by retrieving the remote agent’s Agent Card and sending messages to the agent. [A2A Client Polling connector](/components/early-access/alpha/a2a-client/a2a-client-polling-connector.md) Poll for responses from asynchronous A2A tasks. Typically paired with the A2A Client connector when using the Polling response retrieval method. [A2A Client Webhook connector](/components/early-access/alpha/a2a-client/a2a-client-webhook-connector.md) Receive callbacks from remote A2A agents via HTTP webhooks. Typically paired with the A2A Client connector when using the Notification response retrieval method. These connectors support multi-agent collaboration scenarios when combined with the AI Agent connector, as well as providing the ability to discover remote agents, send messages, and receive responses through multiple mechanisms. [A2A Client connectors](/components/early-access/alpha/a2a-client/a2a-client.md) #### MCP client authentication and transport protocol The Camunda Model Context Protocol (MCP) client now supports OAuth, API key, and custom header–based authentication. - System administrators can configure secure, policy-compliant access for Camunda AI agents. - AI developers can discover and invoke enterprise MCP tools safely without exposing open endpoints. MCP client connectors now also support connections using the [streamable HTTP](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#streamable-http) transport protocol. [MCP Client](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-client.md) :::note breaking changes This feature introduces breaking changes in the element templates and the runtime configuration of the MCP Client. To learn more, see [announcements](890-announcements.md#agentic-orchestration). ::: ### Connectors Self-ManagedSaaSConnectors #### Amazon Textract connector improvements The Amazon Textract connector is improved with input field visibility and polling fixes, new sections for enhanced usability, and updated documentation. [Amazon Textract connector](/components/connectors/out-of-the-box-connectors/amazon-textract.md) #### Azure Blob Storage connector supports OAuth 2.0 The Azure Blob Storage connector now supports OAuth2.0 authentication with Microsoft Azure. [Azure Blob Storage connector OAuth 2.0](/components/connectors/out-of-the-box-connectors/azure-blob-storage.md#oauth-20) #### Email connector supports SMTP no authentication mode The Email connector now supports `noAuth` authentication mode for SMTP. This feature is useful for customers running local mail servers without authentication requirements. [Email connector](/components/connectors/out-of-the-box-connectors/email-outbound.md) #### Runtime performance improvements with virtual threads executor (Self-Managed) Connectors now use a virtual threads executor by default, using Project Loom to improve performance and scalability. This allows the connector runtime to handle a larger number of concurrent jobs with lower resource consumption, particularly benefiting I/O-bound workloads typical in connector operations. ### Console Self-ManagedSaaSConsole #### Bulk import secrets (SaaS) You can now add/import secrets in Console by directly uploading or pasting the contents of a .env file. - Key–value pairs are automatically parsed, validated, and added as secrets. - This helps reduce configuration errors and copy-pasting when adding secrets. [Connector secrets](/components/hub/organization/manage-clusters/manage-secrets.md) #### Cluster description (SaaS) You can now add a cluster description when creating a cluster or by editing the cluster settings. This helps you document context, ownership, or add operational notes without changing the cluster name. [Create a cluster](/components/hub/organization/manage-clusters/create-cluster.md) #### Import cluster secrets (SaaS) You can now import and export connector secrets between clusters within your organization. Export a cluster’s secrets to a key-value file for backup or external workflows, and import secrets from another cluster in a single action. Imports automatically match keys, update existing values, create missing ones, and provide clear feedback on the result. Permissions are enforced so that only authorized users can perform these actions. [Connector secrets](/components/hub/organization/manage-clusters/manage-secrets.md) #### Usage metrics for licence model and tenant (Self-Managed) Self-Managed environment usage metrics now support per-tenant reporting and align with Camunda’s updated licensing model based on the number of tenants. :::note This feature is already available in the Camunda 8.8 release for Camunda 8 SaaS. ::: ### RDBMS secondary storage Self-ManagedData #### Configure external RDBMS in Helm Configure an external relational database (RDBMS) as secondary storage for the Orchestration Cluster when deploying with Helm. - Supports all databases listed in the RDBMS support policy. - Includes full configuration parameters, history-cleanup options, and exporter settings. - Describes how to load JDBC drivers via init containers, custom images, or mounted volumes. - Provides steps to verify database connectivity. [Configure RDBMS in Helm chart](/self-managed/deployment/helm/configure/database/rdbms.md) #### Open-source OpenSearch support You can now use the [open-source OpenSearch](https://opensearch.org/) project for data storage in a Self-Managed deployment. - This allows you to run a fully open source observability stack without using Elasticsearch or the Amazon OpenSearch Service. - For configuration instructions, see the updated Helm chart values and compatibility matrix. [Secondary storage](/self-managed/concepts/secondary-storage/index.md) #### RDBMS version support policy A new Camunda 8 Relational Database Management System RDBMS support policy provides information about: - Officially supported database versions. - The process for adopting new database versions. - Timelines for phasing out older database versions. [RDBMS version support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) #### SQL and Liquibase database scripts SQL and Liquibase scripts are provided for all Camunda-supported databases. - These scripts include database and schema creation, drop, and upgrade routines. - Scripts follow best practices for each supported database type and version. - The full script package is distributed as part of the official Camunda distribution, available via GitHub or Artifactory. [SQL and Liquibase scripts](/self-managed/deployment/helm/configure/database/access-sql-liquibase-scripts.md) ### Modeler Self-ManagedSaaSDesktop ModelerWeb Modeler #### Element template signal support Element templates now support reusable [BPMN signals](/components/modeler/bpmn/signal-events/signal-events.md). - The `bpmn:Signal#property` binding allows you to set the name of a `bpmn:Signal` referred to by the templated element. - This binding is only valid for templates of events with `bpmn:SignalEventDefinition`. [Element template `bpmn:Signal` binding](/components/modeler/element-templates/template-properties.md#signal-name-bpmnsignalproperty) #### Web Modeler: Embedded web server changed from Undertow to Tomcat (Self-Managed) Web Modeler now uses [Apache Tomcat](https://tomcat.apache.org/) as an embedded web server instead of Undertow. Aligning Web Modeler logging with the Orchestration Cluster makes it easier for administrators to configure and maintain Self-Managed deployments. [Embedded web server](/self-managed/upgrade/components/880-to-890.md#embedded-web-server) #### Web Modeler: IP egress monitoring (SaaS) A new `/meta/ip-ranges` REST API endpoint allows you to monitor SaaS Web Modeler egress IP addresses. - For example, the endpoint is available at https://api.cloud.camunda.io/meta/ip-ranges. - Send a GET request to the endpoint to retrieve a list of egress IP addresses. - Only IP addresses for the related services are exposed (Web Modeler). :::note IP address changes - You should periodically monitor this list via the API, and make any changes in your systems as required. - Although expected changes are published via the API at least 24 hours in advance, in exceptional cases Camunda might have to update these addresses within 24 hours and without prior notice. See [static outbound IP addresses](/components/saas/ip-addresses.md#static-outbound-ip-addresses). ::: ## 8.9.0-alpha1 | Release date | Changelog(s) | Blog | | :--------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--- | | 13 November 2025 | [ Camunda 8 core ](https://github.com/camunda/camunda/releases/tag/8.9.0-alpha1)[ Connectors ](https://github.com/camunda/connectors/releases/tag/8.9.0-alpha1) | - | ### RDBMS secondary storage #### JDBC driver management for RDBMS integrations Self-ManagedConfiguration Camunda 8.9 introduces a standardized approach to JDBC driver management for RDBMS integrations in manual installations. - A new `/driver-lib` directory separates Camunda-bundled drivers from customer-supplied ones, providing a clear and compliant structure for database connectivity. - Drivers that Camunda can legally distribute are included by default. Customers can add and configure their own drivers (for example, Oracle JDBC). - Configuration options allow full control, including explicit driver-class designation when required. This change simplifies compliance and setup for RDBMS environments, ensuring consistent connectivity across PostgreSQL, Oracle, MariaDB, and H2. #### MySQL and Microsoft SQL Server secondary storage Self-ManagedData Camunda 8.9 extends RDBMS secondary storage to include MySQL and Microsoft SQL Server as additional database options for the Orchestration cluster. - This enhancement provides greater flexibility for enterprises that depend on these databases due to policy, licensing, or ecosystem requirements, enabling smoother onboarding and infrastructure alignment. - Zeebe’s primary execution storage remains Raft + RocksDB. :::note This alpha release introduces foundational support only. External configuration and Operate integration follows in upcoming alpha releases. ::: #### RDBMS secondary storage (H2, PostgreSQL, Oracle, MariaDB) Self-ManagedData Camunda 8.9 introduces RDBMS secondary storage as an alternative to Elasticsearch or OpenSearch for storing and querying process data. This feature enables organizations to use relational databases such as H2, PostgreSQL, Oracle, or MariaDB as the secondary storage layer, reducing operational complexity for teams that do not need the scale or performance of Elasticsearch or OpenSearch and prefer an RDBMS-based solution. Key highlights: - **Flexible database choice:** Use relational databases instead of Elasticsearch or OpenSearch. - **Separation of concerns:** Zeebe’s primary execution storage remains Raft + RocksDB; this update only extends the secondary storage layer. - **Consistent APIs:** Continue using the same REST API and data format as with Elasticsearch or OpenSearch—no query or integration changes needed. - **Simplified operations:** Leverage existing RDBMS expertise without maintaining Elasticsearch or OpenSearch clusters. :::note This alpha release introduces support for H2 in Camunda 8 Run only. Operate and external RDBMS configuration follows in upcoming alpha releases. ::: ### Web Modeler #### RDBMS support (H2, MariaDB, MySQL) Self-ManagedSaaSDataWeb Modeler Web Modeler now supports H2, MariaDB, and MySQL as relational database systems, aligning with the configurations supported by the Orchestration cluster. This enhancement ensures consistency across environments, simplifies setup for administrators, and improves integration for both SaaS and Self-Managed deployments. ### Orchestration Cluster #### Unified cache for RocksDB In Camunda 8.9, RocksDB state storage uses a single shared cache and write buffer per broker instead of per partition. This behavior is controlled by the RocksDB memory allocation strategy (`PARTITION`, `BROKER`, `FRACTION`). See [resource planning](/self-managed/components/orchestration-cluster/zeebe/operations/resource-planning.md) for details on the available strategies and recommended settings. :::note In Camunda 8.10, the default memory allocation strategy changes from `PARTITION` to `FRACTION`. This may result in a different amount of memory being allocated to RocksDB. Test the `FRACTION` strategy in Camunda 8.9 to prepare for this change. Alternatively, explicitly set the strategy to `PARTITION` to keep the previous memory allocation behavior. ::: --- ## What's new in Camunda 8.9 ## Why upgrade to Camunda 8.9? Upgrading to Camunda 8.9 delivers significant benefits and keeps your installation aligned and ready for future releases. - **Agentic orchestration**: [Build and orchestrate AI agents](#agentic-orchestration) within your BPMN-based workflows, enabling human tasks, deterministic rule sets, and AI-driven decisions to collaborate in a robust, end-to-end process. - **RDBMS secondary storage**: Flexible data layer via first‑class RDBMS secondary storage options, including Amazon Aurora. - **Observability**: Simpler configuration and observability, with cluster variables, audit logs, and a new SaaS metrics endpoint. - **Improved developer experience**: A smoother developer and operator experience for Camunda 8 Run and Modeler. ## Summary of important changes Important changes in Camunda 8.9 are summarized as follows: **What's new/changed** **Summary** [Agentic orchestration](#agentic-orchestration) Refined AI and MCP connector capabilities and Orchestration Cluster MCP support. [Audit log](#audit-log) Access a detailed record of operations. [Camunda 8 Run](#camunda8run) Improved CLI/configuration experience and secondary storage enhancements. [Global user task listeners](#listeners) Define configuration‑based, cluster‑wide user task listeners. [Helm chart deployment](#helm-chart-deployment) RDBMS and secondary storage configuration, `*.secret.existingSecret` pattern migration, default 8080 REST port, Helm 4 support, and more. [Migration from Camunda 7](#migration) New migration tools History Data Migrator and Identity Data Migrator. [Desktop Modeler](#desktop-modeler) New cluster connection management. [Web Modeler](#web-modeler) Simplified architecture and installation, migration to Log4j2 and Tomcat, RDBMS support, event templates, and email invitations. [Orchestration Cluster](#ocluster) Identity renamed to Admin, Amazon ECS (EC2+Fargate) support, cluster variables, Cluster Metrics endpoint for SaaS clusters, and configuration improvements and enhancements. [RDBMS secondary storage](#rdbms) Relational database support as secondary storage for the Orchestration Cluster. [Supported environments](#environments) Updated support for Java, Elasticsearch/OpenSearch, RDBMS, Helm, and connector runtime (including virtual threads). :::info learn more and upgrade - See [release announcements](/reference/announcements-release-notes/890/890-announcements.md) and [release notes](/reference/announcements-release-notes/890/890-release-notes.md) for a full summary of what's included in Camunda 8.9, including all breaking changes and deprecations, and supported environment changes. - Ready to upgrade? See the [upgrade guides](#upgrade-guides) to learn more about upgrading from Camunda 8.8 to 8.9. ::: ## Agentic orchestration {#agentic-orchestration} Build AI agents more easily with enhanced Camunda AI and MCP connectors in 8.9. ### AI Agent and model configuration improvements Enhancements make AI interactions more robust, configurable, and compatible with a wider range of AI providers. | Feature | Description | | :--------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------- | | [AI Agent connectors](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md) | Timeout configuration at the model level, allowing you to cap how long AI calls can run. | | [Amazon Bedrock connector](/components/connectors/out-of-the-box-connectors/amazon-bedrock.md) | Support for long‑term API key authentication in addition to existing methods. | | OpenAI-compatible models | Custom query parameters in the endpoint URL, useful for setting API versions, and passing additional metadata required by custom API gateways. | ### MCP Client connector authentication The MCP Client connector now supports multiple authentication strategies for connecting to MCP servers (OAuth, API key, and custom header–based authentication). [MCP Client connector](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-client-connector.md) ### Orchestration Cluster MCP support The Orchestration Cluster now exposes its operational capabilities via a built-in Model Context Protocol (MCP) server, enabling AI agents and LLM-powered applications to access Camunda data via a standardized interface. [Orchestration Cluster MCP Server](/apis-tools/orchestration-cluster-api-mcp/orchestration-cluster-api-mcp-overview.md) ## Audit log Use the new user operations audit log to access a record of operations, including who performed an operation, when it was performed, and on which entities the operation was performed. Use the audit log to prove compliance, meet governance and regulatory requirements, maintain operational integrity and transparency, and troubleshoot issues. [Audit log](/components/audit-log/overview.md) ## BPMN conditional events Camunda 8 now supports BPMN conditional events, allowing users to start, continue, or interrupt process execution dynamically based on evaluated conditions. This enhancement provides first-class support for conditional start, boundary, and intermediate catch events, making process automation more expressive and migration from Camunda 7 smoother. [Conditional events](/components/modeler/bpmn/conditional-events/conditional-events.md) ## Camunda 8 Run {#camunda8run} Camunda 8.9 focuses on making Camunda 8 Run simpler to use in local and development scenarios. ### Improved CLI and configuration experience The Camunda 8 Run CLI and configuration is enhanced to: - Provide more helpful usage output and clearer error messages (for example, around Elasticsearch startup). - Include guided Java detection, sensible development defaults (such as disk watermark thresholds), and a clean‑state command for resetting a local environment. - Ship a fully documented unified configuration file by default. These changes reduce friction when setting up Camunda 8 Run for the first time, debugging local setup issues, and switching between different configurations or environments. :::info Breaking change The `--docker` flag and bundled Docker Compose files have been removed from Camunda 8 Run. Docker Compose is now a standalone distribution artifact. See [release announcements](/reference/announcements-release-notes/890/890-announcements.md#camunda-8-run-docker-compose-support-removed) for migration details. ::: [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) ### Secondary storage Camunda 8 Run now uses H2 as the default secondary data store. - You can start a fully functional local stack without provisioning Elasticsearch or OpenSearch. - Memory footprint is reduced compared to a full external search cluster. - Developers can bootstrap quickly with minimal configuration. You can also configure Camunda 8 Run to use any of the supported secondary storage relational databases instead of the default H2 (for example, PostgreSQL, MariaDB, MySQL, Oracle, or Microsoft SQL Server). [Configure Camunda 8 Run secondary storage](/self-managed/quickstart/developer-quickstart/c8run/secondary-storage.md#configure-or-switch-secondary-storage-h2-or-elasticsearch) ## Global user task listeners {#listeners} Use configuration‑based global user task listeners in your Self‑Managed deployments. - Administrators can define cluster‑wide listeners using configuration files or environment variables. - Listeners are active from cluster startup and preserved across backup and restore. - All user task lifecycle events emit rich payloads - full variable context at the time of the event, and standardized metadata for traceability and integration. [Global User task Listeners](/components/concepts/global-user-task-listeners.md) ## Helm chart deployment Important changes to Helm chart deployment in 8.9 are as follows: ### Default REST port changed The Orchestration Cluster's default HTTP port has changed from 8090 to 8080. You should update any hardcoded port references in network policies, Ingress rules, or service configuration. ### Helm 4 support As Helm 3 reaches end of life in 2026, Camunda continues to support your migration to Helm 4 with documentation covering how you can deploy Camunda 8.7, 8.8, and 8.9 with Helm 4. [Helm 4](/self-managed/deployment/helm/operational-tasks/helm-v4.md) :::note Camunda 8.10 and beyond will only support Helm 4 to ensure we provide secure solutions for customers. ::: ### RDBMS as secondary storage The Helm chart now supports relational databases as a first-class secondary storage option alongside Elasticsearch and OpenSearch. You can configure database connections directly under `orchestration.data.secondaryStorage.rdbms` in your `values.yaml`. ### Secondary storage must be explicitly configured The Helm chart no longer defaults to Elasticsearch as the secondary storage type. - You must now explicitly set `orchestration.data.secondaryStorage.type` in your `values.yaml` to either `elasticsearch`, `opensearch`, or `rdbms`. - Without this configuration, `helm install` or `helm upgrade` will fail with a validation error. - For engine-only deployments that do not require secondary storage, the new `global.noSecondaryStorage` mode is available. #### Are you affected? You are affected if you are upgrading a Self-Managed Helm deployment from 8.8 to 8.9 and have not previously explicitly set a secondary storage type. ### Secret configuration migration Secret configuration keys that were deprecated in Camunda 8.8 are now removed in 8.9. - Using any of these removed keys will result in a hard failure during Helm operations. - In addition, the `global.secrets.autoGenerated`, `global.secrets.name`, and `global.secrets.annotations` keys are removed. This means secrets are no longer automatically generated by the Helm chart. All secret configuration must now use the new standardized pattern: | Key | Description | | :--------------------------- | :------------------------------------------------ | | `*.secret.existingSecret` | Reference an existing Kubernetes Secret. | | `*.secret.existingSecretKey` | Specify the key within the Secret. | | `*.secret.inlineSecret` | Provide a plain-text value (non-production only). | #### Are you affected? You are affected if your `values.yaml` uses any of the legacy secret keys (such as `global.elasticsearch.auth.existingSecret`, `identity.firstUser.password`, `connectors.security.authentication.oidc.existingSecret`, or similar). See the [release announcements](/reference/announcements-release-notes/890/890-announcements.md#helm-chart-deprecated-secret-keys-removed) for the full list of removed keys. ## Reference architectures {#reference-architectures} Camunda 8.9 introduces major changes to the [deployment references](https://github.com/camunda/camunda-deployment-references) used by Self-Managed reference architectures. ### Operator-based infrastructure replaces Bitnami All reference architectures (AKS, EKS, OpenShift, Kind) now use Kubernetes operators — CloudNativePG, ECK, and Keycloak operator — instead of embedded Bitnami subcharts for infrastructure services. If you follow a reference architecture, your next deployment will use operator-managed PostgreSQL, Elasticsearch, and Keycloak. Existing deployments can migrate using the new [migration tooling](/self-managed/deployment/helm/operational-tasks/migration-from-bitnami/index.md). #### Are you affected? You are affected if you deploy Self-Managed using a reference architecture from the `camunda-deployment-references` repository and rely on Bitnami-managed infrastructure. Follow the migration guide to transition to operator-managed services. ### New deployment options - **Amazon ECS on Fargate** is now available as a container-based deployment without Kubernetes. - **AKS RDBMS variant** provides a lighter Azure deployment using PostgreSQL as secondary storage (no Elasticsearch). - **Kind local development** reference architecture is available for local testing with Makefile-based commands. ### Dual-region changes - EKS and OpenShift dual-region now use the ECK operator for Elasticsearch. - Headless service DNS is used for initial contact points, improving cross-region connectivity. ## Migration from Camunda 7 to Camunda 8 {#migration} Camunda 8.9 introduces the following additional tools and features to help you migrate from Camunda 7 to Camunda 8. :::tip Start your migration today with the [Camunda 7 to Camunda 8 migration guide](/guides/migrating-from-camunda-7/index.md). ::: ### History Data Migrator The History Data Migrator migrates audit trail data (the history of process execution, including active, completed and canceled instances) to Camunda 8 when RDBMS is used as secondary storage. [History Data Migrator](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/history.md) ### Identity Data Migrator The Identity Data Migrator migrates authorizations that control access to resources in Camunda, helping you preserve permissions when migrating to Camunda 8. [Identity Data Migrator](/guides/migrating-from-camunda-7/migration-tooling/data-migrator/identity.md) ## Desktop Modeler {#desktop-modeler} ### Manage Camunda connections You can now manage multiple Camunda connections in Desktop Modeler: - Add, edit, delete, and save connection profiles. - Securely store credentials and configuration for each connection. - Deploy directly to a saved connection, and select clusters during deployment. [Connect to Camunda 8 in Desktop Modeler](/components/modeler/desktop-modeler/connect-to-camunda-8.md) ## Web Modeler {#web-modeler} Camunda 8.9 introduces the following enhancements and changes in Web Modeler. ### Event templates and email invitations The following usability improvements simplify collaboration and help teams keep event configurations consistent. | Feature | Description | | :---------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [Element templates](/components/modeler/element-templates/defining-templates.md) | Create templates for message, signal, and timer events, and reuse and share templates across projects to standardize message names, payloads, and timer definitions. | | [Email invitations](/components/hub/workspace/modeler/collaboration/collaboration.md#add-users-to-projects) | Invite new users to Web Modeler projects via email, regardless of OIDC provider, and use a consistent invitation flow across Keycloak, Entra ID, Okta, Auth0, and other providers. | ### Improved Self-Managed installation The Web Modeler system architecture has been simplified to enable easier and smoother installation and configuration of Web Modeler in a Self-Managed deployment. The separate `webapp` component has been removed, and its functionality is now completely integrated into the `restapi` component. This change might require updates to your application configuration. [Migrate configuration](/self-managed/upgrade/components/880-to-890.md#migrate-webapp-configuration) ### Log4j2 and Tomcat changes | Feature | Description | | :-------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Logging framework changes from Logback to Log4j2 | Web Modeler's `restapi` component uses [Apache Log4j 2](https://logging.apache.org/log4j/2.x/) for logging instead of [Logback](https://logback.qos.ch/). You can now also change the log levels at runtime. | | Embedded web server changes from Undertow to Tomcat | Web Modeler's `restapi` component uses [Apache Tomcat](https://tomcat.apache.org/) instead of [Undertow](https://undertow.io/). | These changes could affect your organization if you are in a Self-Managed environment and: - You are using a custom Logback and/or Undertow configuration. - You are using any tools that consume the log files. You should: - Review your logging and server configuration for Web Modeler. - Update any tooling that relies on old log formats or server behavior. - Validate the new setup in a staging environment before upgrading production. [Web Modeler logging](/self-managed/components/hub/configuration/logging.md) ## Orchestration Cluster {#ocluster} Orchestration Cluster changes and highlights in 8.9 include the following: ### Amazon ECS (EC2+Fargate) support Camunda 8 officially supports running Orchestration Clusters on Amazon Elastic Container Service (Amazon ECS). This makes it easier and safer for teams that rely on Amazon ECS (including Fargate) to run Camunda 8 in production without needing to adopt Kubernetes (EKS). This feature relies on AWS S3. [Deploy to Amazon ECS](/self-managed/deployment/containers/cloud-providers/amazon/aws-ecs.md) ### Cluster variables for shared configuration Use cluster variables to define configuration values once and reuse them across processes and tenants using FEEL expressions. | Variable | Scope | Priority | | :--------------------- | :---------------------------------- | :------- | | `camunda.vars.cluster` | Global | Lowest | | `camunda.vars.tenant` | Tenant | Medium | | `camunda.vars.env` | Merged view with automatic priority | Highest | You can use cluster variables to centralize: - URLs, endpoints, and credentials references. - Feature flags and environment names. - Shared configuration used across multiple processes or applications. [Cluster variables](/components/modeler/feel/cluster-variable/overview.md) ### Cluster Metrics endpoint for SaaS clusters Use the new Cluster Metrics endpoint for SaaS clusters to: - Expose cluster metrics compatible with Prometheus‑style scraping. - Integrate directly with tools such as Prometheus, Datadog, or other metrics backends that support Prometheus endpoints. - Gain real‑time visibility into cluster performance and health. This helps operations teams consolidate Camunda monitoring into existing observability stacks, and standardize dashboards, alerts, and SLOs across environments. [Cluster Metrics endpoint](/components/saas/monitoring/cluster-metrics-endpoint/index.md) ### Configure RocksDB memory per-broker You can now configure RocksDB memory on a per-broker basis instead of per-partition, simplifying capacity planning and aligning with familiar JVM-style sizing. [Zeebe memory allocation](/self-managed/components/orchestration-cluster/zeebe/operations/resource-planning.md#memory) ### Orchestration Cluster Identity renamed to Admin Starting with Camunda 8.9, the Orchestration Cluster Identity component is renamed to **Admin** (Orchestration Cluster Admin). Admin is the cluster-level admin UI hosting identity management and other administrative features. Only the name has changed. Identity management (users, groups, roles, authorizations, tenants, mapping rules, and clients) is unchanged. - The `admin` Spring profile replaces the `identity` profile. Both profiles work interchangeably in 8.9. The `identity` profile is deprecated and will be removed in a future version. - API paths change from `/identity/*` to `/admin/*`. The old paths redirect to the new paths but are deprecated. - Helm values change from `orchestration.identity.*` to `orchestration.admin.*`. The old values are deprecated. - Documentation paths are updated: `/components/identity/` is now `/components/admin/`. [Introduction to Admin](/components/admin/admin-introduction.md) ### Unified component configuration {#ucc} In Camunda 8.9, the remaining unified configuration project property changes are complete. - All 8.9 property changes are documented in the [Camunda 8.9 property changes](/self-managed/components/orchestration-cluster/core-settings/configuration/configuration-mapping.md#camunda-89-property-changes) table. - Search, sort, and filter the table to see breaking changes, direct mappings, and new properties. - For more information on each property (including default values), see the [property reference](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md). [Property changes in Camunda 8.9](/self-managed/components/orchestration-cluster/core-settings/configuration/configuration-mapping.md) :::note Only the first partial set of the unified configuration project properties was introduced in Camunda 8.8. ::: ## RDBMS secondary storage {#rdbms} Camunda 8.9 introduces relational databases as first‑class secondary storage options for the Orchestration Cluster. ### RDBMS secondary storage for orchestration data You can now use relational databases to store and query orchestration data instead of relying solely on Elasticsearch or OpenSearch. Supported options include H2 (for development and test scenarios), PostgreSQL, Oracle, MariaDB, MySQL, Microsoft SQL Server, and Amazon Aurora PostgreSQL. - Zeebe’s primary execution storage remains unchanged (Raft + RocksDB). - The same REST API and data model are used regardless of whether you choose Elasticsearch/OpenSearch or an RDBMS as secondary storage. - You can select the storage option that best matches your operational, compliance, and cost requirements. This is particularly useful if you already standardize on one of the supported RDBMS platforms, want to avoid operating and scaling additional search clusters where they are not required, or prefer existing RDBMS tooling for backup, monitoring, and compliance. [Secondary storage](/self-managed/concepts/secondary-storage/index.md) ### Amazon Aurora as managed secondary storage Camunda 8.9 adds support for Amazon Aurora PostgreSQL as secondary storage, enabling: - A fully managed, cloud‑native RDBMS for secondary storage. - Integration with AWS features such as IAM/IRSA, high availability, and automatic failover. - Helm and manual installation guidance with tested configurations for Aurora. Aurora is a good fit if you run Camunda on AWS and want managed RDBMS operations instead of self‑managing PostgreSQL, and integration with existing AWS security and reliability practices. [Using AWS Aurora PostgreSQL with Camunda](/self-managed/concepts/databases/relational-db/configuration.md#usage-with-aws-aurora-postgresql) ### RDBMS version support policy and database scripts To clarify long‑term support, Camunda 8.9 introduces: - An RDBMS support policy covering supported database versions and adoption of new versions. - SQL and Liquibase scripts for all supported databases, distributed as part of the official Camunda distribution. This helps DBAs and operations teams validate that existing database versions are supported, plan upgrades based on a predictable policy and script set, and standardize roll‑outs and migrations across environments. [RDBMS version support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) ### Standardized JDBC driver management For manual installations, Camunda 8.9 introduces a standardized directory and configuration for JDBC drivers: - A new `/driver-lib` directory separates drivers bundled by Camunda from customer‑supplied drivers (for example, Oracle or specific vendor builds). - Configuration options allow you to choose which drivers are active, and explicitly configure driver classes where required. This structure simplifies license‑compliant driver distribution, multi‑database support in regulated environments, and provides a clear separation between Camunda-managed and customer-managed artifacts. [Loading JDBC drivers into pods](/self-managed/deployment/helm/configure/database/rdbms.md#loading-jdbc-drivers-into-pods) ## Supported environments {#environments} Camunda 8.9 updates several platform and environment baselines. Highlights include: | Environment | Description | | :------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Connector runtime | Virtual threads enabled by default for outbound connectors, improving concurrency and resource usage where supported. | | Elasticsearch/OpenSearch | Updated minimum versions for Self‑Managed deployments.Recommended upgrades to Elasticsearch 9.2+ and OpenSearch 3.4+ to align with Camunda 8.9 defaults and benefit from recent database improvements. | | Helm and RDBMS configuration | New RDBMS configuration options in the Helm values file for orchestration data.Expanded secret and configuration patterns aligned with the Orchestration Cluster architecture. | | Java | Certification for OpenJDK 25 across core components and tooling, alongside existing OpenJDK 21–24 support. | | [SaaS regions](/components/saas/regions.md) | Support for an additional AWS US East (us-east-2) region in North America (and new European region options) so you can select clusters closer to your data and users. | :::info For complete details, including breaking changes and deprecations, see [release announcements](./890-announcements.md) and [supported environments](/reference/supported-environments.md). ::: ## Upgrade guides {#upgrade-guides} The following guides offer detailed information on how to upgrade to Camunda 8.9. **Guide** **Description** **Who is this guide for?** [Self-Managed upgrade guide](/self-managed/upgrade/index.md) Evaluate your infrastructure, understand operational changes, and choose the best update strategy for your environment. Operations and platform administrators of Self-Managed installations. [APIs & tools upgrade guide](/apis-tools/migration-manuals/migrate-to-89.md) Plan and execute an upgrade from Camunda 8.8 to 8.9, focusing on API and tools transitions. Application developers maintaining Camunda-based solutions in Self-Managed Kubernetes or VM environments.Developers using Camunda APIs and tools. --- ## Release announcements and release notes **Version** **Release date** **Announcements** **Release notes** **Scheduled End of maintenance** 8.10 13 October 2026 [8.10 release announcements](/reference/announcements-release-notes/8100/8100-announcements.md) [8.10 release notes](/reference/announcements-release-notes/8100/8100-release-notes.md) 11 April 2028 8.9 14 April 2026 [8.9 release announcements](/reference/announcements-release-notes/890/890-announcements.md) [8.9 release notes](/reference/announcements-release-notes/890/890-release-notes.md) 13 October 2027 8.8 14 October 2025 [8.8 release announcements](/reference/announcements-release-notes/880/880-announcements.md) [8.8 release notes](/reference/announcements-release-notes/880/880-release-notes.md) 13 April 2027 8.7 8 April 2025 [8.7 release announcements](/reference/announcements-release-notes/870/870-announcements.md) [8.7 release notes](/reference/announcements-release-notes/870/870-release-notes.md) 13 October 2026 ## Release announcements Release announcements include: - Changes in supported environments (updates regarding OpenJDK, Spring Boot, Elasticsearch, Keycloak, Amazon OpenSearch, Docker tags, Helm chart versions, and so on). - Key changes (for example, deprecations and breaking changes). :::info - See [upgrade to Camunda 8.9](/self-managed/upgrade/index.md) for guidance on upgrading your Self-Managed Camunda 8 application or server installation. - See [supported environments](/reference/supported-environments.md) for environments and technologies supported by Camunda 8. ::: ## Release notes Release notes include details of new product features and enhancements. Release notes are provided for each minor release and include alphas released during the development cycle. ## Changelogs Technical changelogs for Camunda 8 are available on GitHub: | Changelog | Repository | | :----------------- | :------------------------------------------------------------------------------------------------------------- | | Camunda 8 Core | [github.com/camunda/camunda/releases](https://github.com/camunda/camunda/releases) | | Connectors | [github.com/camunda/connectors/releases](https://github.com/camunda/connectors/releases) | | Camunda Helm chart | [github.com/camunda/camunda-platform-helm/releases](https://github.com/camunda/camunda-platform-helm/releases) | | Desktop Modeler | [github.com/camunda/camunda-modeler/releases](https://github.com/camunda/camunda-modeler/releases) | ## Release policy Review the [release policy](/reference/announcements-release-notes/release-policy.md) to learn more about Camunda releases, including alpha features and alpha releases. --- ## Release policy Camunda 8 follows the [Camunda release policy](https://camunda.com/release-policy/) with the following specific clarifications. :::info You can find deprecation and support announcements on the [announcements](/reference/announcements-release-notes/overview.md#announcements) page. ::: ![Stable and alpha channels when provisioning a cluster](../img/diagram-releases.png) ## Alpha features and releases It is important to understand the different ways the term "alpha" is used in the context of Camunda releases and features. ### Alpha feature Refers to a feature or component released as an alpha version, in an early state for you to test and participate in development by sharing your feedback before the feature reaches [general availability](#general-availability-ga). Some alpha features require turning on for your cluster before you can use them. See [alpha features](/components/early-access/alpha/alpha-features.md). ### Alpha release Refers to a release made available between minor versions that allows you to preview an upcoming minor version and the alpha features included (for example, `8.6.0-alpha1`, `8.6.0-alpha2`, and so on). Camunda strives to release this type of release on a monthly basis. To learn more about the alpha features included in each alpha release, see [release notes](/reference/announcements-release-notes/overview.md#release-notes). :::note - Alpha releases cannot be updated to newer releases and are not suitable for use in production. - An alpha release can also be made available where the entire version is an alpha with [alpha limitations](/components/early-access/alpha/alpha-features.md#alpha). - "Alpha channel" refers to the channel you can use when provisioning a SaaS cluster. See [alpha channel](#alpha-channel). ::: ## General availability (GA) Once features and components are released and considered stable, they become generally available. Stable features and components are: - Ready for production use for most users with minimal risk. - Supported by [L1 Priority-level support](https://camunda.com/services/enterprise-support-guide/) for production use. - Fully documented. A release or component is considered stable if it has passed all verification and test stages and can be released to production. ## SaaS provisioning In Camunda 8 SaaS we differentiate between components that are part of a Camunda 8 cluster (cluster components), and components outside the cluster (non-cluster components). ### Cluster components A cluster typically consists of the following components: - [Zeebe](/components/zeebe/zeebe-overview.md) - [Operate](/components/operate/operate-introduction.md) - [Tasklist](/components/tasklist/introduction-to-tasklist.md) - [Optimize](/components/optimize/what-is-optimize.md) You can provision cluster components using one of two channels, following the [Camunda release policy](https://camunda.com/release-policy/). ![Stable and alpha channels when provisioning a cluster](../img/channels.png) #### Stable channel You can use the **Stable** channel to access [general availability](#general-availability-ga) features for cluster components. - Provides the latest feature and patch releases ready for most users at minimal risk. - Releases follow semantic versioning and can be updated to the next minor or patch release without data loss. - On the stable channel, all supported minor versions are made available for provisioning. #### Alpha channel You can use the **Alpha** channel to access [alpha features](/components/early-access/alpha/alpha-features.md) and patch releases for cluster components. - Provides alpha releases to preview and prepare for the next stable release. - Alpha releases provide a short-term stability point to test new features and give feedback before they are released to the stable channel. Use an alpha release to test the upcoming minor release with your infrastructure. ### Non-cluster components Non-cluster components include: - [Camunda Hub](/components/hub/index.md) - [Connectors](/components/connectors/introduction.md) Non-cluster component versions are released continuously. - Customers are automatically updated to the latest component version when it is ready for release. - Admins can [enable alpha features](/components/hub/organization/manage-organization-settings/enable-alpha-features.md) for non-cluster components in organization settings. ### New Camunda 8 versions When a new Camunda 8 version is released, we try to provide the new version on our managed service at the same time. An **Update available** notification is shown in Camunda Hub, recommending that you update to the latest version. ![Camunda Hub with notice to update the cluster in Camunda 8 SaaS](../img/update-console.png) #### Generation names The generation naming scheme in Camunda 8 SaaS no longer includes the patch version. - The naming scheme used for the Camunda 8.5 generations is `Camunda .+gen`, where `N` is incremented with every atomic change to the component version set. - This decouples the generation name from the particular patch level of the components it contains, as some component versions such as connectors are decoupled from other components. - You can learn about the particular component patch version changes in the update dialogue to the latest generation available. #### Update or restart for critical issues In our managed service, Camunda reserves the right to force update or restart a cluster immediately and without notice in advance if there is a critical security or stability issue. ## Self-Managed You can stay up to date via [release blogs](https://camunda.com/blog/category/releases/), [announcements](/reference/announcements-release-notes/overview.md#announcements), or releases on [GitHub](https://github.com/camunda) and [Docker Hub](https://hub.docker.com/u/camunda). ### Helm chart Since the 8.4 release, the [Camunda 8 Self-Managed Helm chart](https://artifacthub.io/packages/helm/camunda/camunda-platform) version is decoupled from the version of the application. For example, the chart version is 9.0.0 and the application version is 8.4.x. To learn more about the applications version included in the Helm chart, see the [Camunda 8 Helm chart version matrix](https://helm.camunda.io/camunda-platform/version-matrix/). ### New Camunda 8 versions If you are running Camunda 8 Self-Managed, see the [update guide](/self-managed/upgrade/components/index.md) to learn how to update your Camunda 8 application or server installation to a newer version of Camunda 8. ## Dependency maintenance policies Camunda provides [a standard support policy](https://camunda.com/release-policy/) of 18 months for a particular minor version from the date it is released. During this time, patches are regularly released containing security and bug fixes, some of which may come from dependency updates. Therefore, for the vast majority of dependencies Camunda _only_ applies patch updates. However, certain dependencies used by Camunda 8 may have a shorter maintenance policy than Camunda itself. Camunda may adopt a different update policy for these dependencies, as listed below. ### Spring Boot/Framework/Security updates Spring has a [different maintenance window](https://spring.io/projects/spring-boot#support) than Camunda for its open-source software (OSS) offering. Camunda addresses this with Spring Boot/Framework/Security update policies for the Camunda Orchestration Cluster and client libraries. :::info The update policy covers the Spring Framework and Security versions, as well as [other dependencies managed via Spring Boot](https://docs.spring.io/spring-boot/appendix/dependency-versions/coordinates.html). ::: #### Orchestration Cluster The Camunda Orchestration Cluster leverages Spring Boot to implement core functionality, like application configuration, REST infrastructure (including security), and other production-ready features. Camunda ensures that **the latest available patch releases make use of a Spring version within an active support window**. Therefore, Orchestration Cluster patch releases **may contain updates to newer Spring Boot minor versions**. In cases where a Spring major version's OSS support ends before Camunda's own support window, Camunda may utilize Spring enterprise support artifacts from a vendor determined on a case-by-case basis to ensure continued maintenance and security coverage for the Orchestration Cluster components. #### Client libraries and SDKs Camunda clients or SDKs, such as the [Camunda Spring Boot Starter](../../apis-tools/camunda-spring-boot-starter/getting-started.md), that are meant to be included in third-party applications, may depend on a particular Spring Boot release. To ensure backward compatibility, every Camunda library patch release references the same Spring Boot minor version as the one referenced in the Camunda library's latest minor release. However, Camunda tests against newer Spring Boot minor releases and declares compatible versions in the library's compatibility matrix. For an example, refer to the [Camunda Spring Boot Starter Compatibility Matrix](../../apis-tools/camunda-spring-boot-starter/getting-started.md#version-compatibility). If the library is compatible with a newer Spring version with an active OSS support window, Camunda declares compatibility one month before the support window ends, at the latest. With this policy, you can safely update your applications to a new Spring Boot version by overriding the default Spring release with another compatible version. --- ## Support and feedback [Support](https://camunda.com/services/enterprise-support-guide/) Request support via the [Enterprise support process](https://camunda.com/services/enterprise-support-guide/).Find support options in the [Help Center](/components/saas/help-center.md) or [Camunda community forum](https://forum.camunda.io/). [Bugs](https://forum.camunda.io/) Send us your bug reports via a support ticket (Enterprise customers) or via the [Camunda community forum](https://forum.camunda.io/).Review our technical bug trackers in [GitHub](https://github.com/camunda/camunda/issues) or the [quality board](https://github.com/orgs/camunda/projects/187/views/5). [Community](https://forum.camunda.io/) Participate in our community via the [Camunda community forum](https://forum.camunda.io/), where you can exchange ideas with other Camunda users and Camunda employees.For Camunda community programs and resources, visit the [Camunda Developer Hub](https://camunda.com/developers). [Feature requests](https://roadmap.camunda.com/c/236-how-to-submit-a-feature-request) Submit a feature request in the [Product Roadmap Portal](https://roadmap.camunda.com/c/236-how-to-submit-a-feature-request).[Open an issue](https://github.com/camunda/camunda/issues) for smaller, technical enhancement requests. [Sales/other](https://camunda.com/contact/) [Contact us](https://camunda.com/contact/) with sales inquiries, information about Camunda 8 performance and benchmarking, or any other queries not covered by the other channels. [Security issues](/reference/notices.md) For security-related issues, see [security notices](/reference/notices.md) for current information on known issues and how to report a vulnerability so we can solve the problem as quickly as possible.Note: Do not use GitHub for security-related issues. ## Locate your Camunda 8 credentials Need help finding your Camunda 8 credentials? Submit a **Help Request** to obtain your credentials: 1. Log in to [Jira](https://jira.camunda.com/secure/Dashboard.jspa). 1. Click **Create** in the navigation bar at the top of the page. This launches a **Create Issue** pop-up. 1. In the **Issue Type** field, select **Help Request**. 1. In the **Help Request Type** field, click the option that reads **I need the credentials for downloading Camunda**. 1. In the **Summary** and **Description** fields, **I need the credentials for downloading Camunda** will populate by default. 1. (Optional) Add more details, such as the priority level or authorized support contacts. 1. Click **Create** at the bottom of the pop-up **Create Issue** box. 1. Once these steps are completed your request is generated. :::tip For more information on how to submit a self-service help request, see the [Camunda support guide](https://camunda.com/services/enterprise-support-guide/). ::: --- ## Data collection ## About data collection Camunda collects telemetry data to evaluate contractual usage, provide a better user experience, and improve its products. This page describes which telemetry data is collected, how Camunda ensures privacy, and what options you have to modify which telemetry data is sent to Camunda. This information is designed to help you understand what telemetry data includes and excludes, applies only to interactions with Camunda's products, and will be updated periodically. ## Purposes Camunda collects certain types of data we call “telemetry data” for the purposes described below: - Billing - Improving the user experience by tracking and analyzing usage of the software - Ensuring the security, stability, and functionality of the foftware - Providing support and guidance to customers to help optimize product usage and new functionalities. ## Principles Camunda follows certain principles in its collection and use of telemetry data to ensure the privacy of its customers and the success of its product development efforts: - Camunda will use telemetry data subject to applicable law (including opt-in and opt-out functionalities for personal data where necessary). Telemetry data is generally aggregated unless users opt-in for personalized use of their telemetry data (for example, to provide additional support or optimize product usage to customers). - Telemetry data does not include any data shared in process instances or uploaded in customer clusters. Therefore, **no end-user or end-customer personal data**, personal information (PII), or protected health information (PHI) uploaded to a customer cluster is part of telemetry data. - Telemetry data does **not include payment information**. - Camunda does **not sell any personal (user) information.** - **For Self-Managed customers, telemetry data is always fully anonymous and only sent upon admin/owner enablement from the customer.** - Data collected from end-users such as form fills or process variables are not part of telemetry data. For example, if part of your process involves a user filling in a shipping address, that address is not telemetry data. - Assets like the BPMN diagram describing how a process is defined and executed are not telemetry data. Telemetry data does not include information about how customers develop their processes, like keystrokes or BPMN diagrams. Instead, it includes user-provided identifiers like a process ID to track which Camunda software features are used when developing a process. - Customers are responsible for avoiding sharing intellectual property, personal data or sensitive data through interaction with AI features. The data collected by different AI features is shared [below](#usage-telemetry-data-saas-and-desktop-modeler-only). - Camunda will not use telemetry data in any way that identifies the source of the telemetry data to third parties except as necessary for Camunda to enforce its rights and contractual obligations, such as charging fees for overage of usage metrics or complying with a lawful subpoena. ## Telemetry data collection Telemetry data includes contractual metrics, environment, and usage data. Telemetry data is collected automatically in SaaS (except for personalized telemetry data which is only used via user discretionary opt-in) and collected via admin discretionary opt-in for Self-Managed platforms. Each category of telemetry data is described below. ### Contractual metrics telemetry data Contractual metrics telemetry data includes a limited set of contractually agreed [usage metrics](/reference/data-collection/usage-metrics.md) to evaluate usage metric use and bill for overages. One example is how many process instance usage metrics are used compared to the number of process instance usage metrics purchased by customers. These are summary usage metrics that contain no sensitive information and that are collected automatically for SaaS customers and sent in a report generated by Self-Managed customers. ### Environment telemetry data (Self-Managed only) Environment telemetry data includes information about your Self-Managed installations to enable better support and product improvement decisions. Self-Managed customers may choose to send a very limited set of environment information through the Console component's telemetry mechanism. View the [telemetry](/self-managed/components/hub/telemetry.md) page for a description of this data. Environment telemetry data is not relevant to SaaS installations. ### Usage telemetry data (SaaS and Desktop Modeler only) Usage telemetry data includes limited product usage data to help make better product improvement decisions and enable outreach to support users. This section describes the types of data and how they are collected. - Feature Usage: - SaaS System Actions: All SaaS organizations submit basic information about which features are being used as part of telemetry data collection. When certain features are used, Camunda logs which feature is used and basic information about how it has been used. This information is tied to a pseudonymized organization. - SaaS User Actions: Users that opt-in to personalization cookies gain access to in-app tutorials, whereas analytics cookies cause data to be automatically submitted about which features they interact with in Camunda’s UI as part of telemetry data. In addition to the data collected from system actions described above, Camunda collects cursor activity, geographical area, browser information, and basic biographical information limited to email, name, and city/region/country for user actions. If a user interacts through API, then personal information is not collected. - Desktop Modeler User Actions: Users opting into collection of telemetry data in [Desktop Modeler](/components/modeler/desktop-modeler/telemetry/telemetry.md) send data to Camunda to track how certain features are used, as described in the linked document. - AI Usage: Camunda's AI features, currently available in SaaS only, are clearly labeled as AI features. For Enterprise organizations, these features must be enabled by the customer via opt-in in the [Console](/components/hub/organization/manage-organization-settings/enable-alpha-features.md#enable-ai-powered-features). Depending on the feature, they may collect different information. - Camunda [Docs AI](/components/hub/workspace/modeler/modeling/advanced-modeling/camunda-docs-ai.md) records the entire conversation to provide ongoing support. - Camunda [Copilots](/components/early-access/alpha/bpmn-copilot/bpmn-copilot.md) only gather usage telemetry data. Camunda automatically logs all information sent to and from our AI models for system monitoring by a limited set of operators. Camunda will only use the data from free users for product and model improvement. ### Example Below is an example of user action data collected by the platform: ```json { "event": "modeler:deploy:confirm", "properties": { "time": 1721228056.002, "distinct_id": "auth0|669533a8339ceebe5e8f7fed", "$browser": "Microsoft Edge", "$browser_version": 126, "$city": "Gotham City", "$current_url": "https://modeler.camunda.io/diagrams/a8c077ae-22d6-4be3-bebb-a847f40376fe--batsymbol-activate?v=736,217,1", "$device_id": "190b6d254651ec-0a7e1ef548a163-4c657b58-e1000-190b6d2518f1ec", "$initial_referrer": "https://console.camunda.io/", "$initial_referring_domain": "console.camunda.io", "$insert_id": "xjsmufevamu6v5y7", "$lib_version": "2.53.0", "$mp_api_endpoint": "api-js.mixpanel.com", "$mp_api_timestamp_ms": 1721228056805, "$os": "Windows", "$referrer": "https://dsm-1.operate.camunda.io/", "$referring_domain": "dsm-1.operate.camunda.io", "$region": "New Jersey", "$screen_height": 1080, "$screen_width": 1920, "$user_id": "auth0|669533a8339ceebe5e8f7fed", "clusterId": "ea9ddef9-f1e3-4241-a37c-655334c45de8", "clusterTag": "dev", "clusterVersion": "8.5", "connectors": ["io.camunda.connectors.HttpJson.v2"], "containsUserTasks": true, "deployType": "single-file", "deployedForms": { "Form_0ec4ghh": "764a75e7-85a8-448f-8a1f-4952cc8a189d" }, "fileId": "a8c077ae-22d6-4be3-bebb-af97040123fe", "fileType": "bpmn", "license": "Free", "mp_country_code": "US", "mp_lib": "web", "mp_processing_time_ms": 1721228056937, "mp_sent_by_lib_version": "2.53.0", "orgId": "30ba73a-4b2f-433f-80e5-d41176874bb5", "org_id": "30ba73a-4b2f-433f-80e5-d41176874bb5", "organizationId": "30ba73a-4b2f-433f-80e5-d41176874bb5", "stage": "prod", "success": true, "userId": "auth0|669533a8339ceebe5e8f7fed", "version": "8.5.4" } } ``` --- ## Usage metrics ## Definition of metrics There are four main usage metrics that have an impact on Camunda 8 pricing. The specific metrics that apply to billing may vary depending on your contractual agreement with Camunda. It is important to understand these definitions, their impact on billing, and how to retrieve them. ### Root process instance The number of **root process instance** executions started. This is also known as process instances (PI). A **root process instance** has no parent process instance, i.e. it is a top-level execution. ### Decision instance The number of evaluated **decision instances** (DI). A **decision instance** is a [DMN decision table](/components/modeler/dmn/decision-table.md) or a [DMN literal expression](/components/modeler/dmn/decision-literal-expression.md). In a Decision Requirements Diagram (DRD) each evaluated decision table or expression is counted separately. ### Task user The number of **task users** (TU) that have served as assignees. ### Tenant A tenant is a logically isolated space within a shared Camunda 8 installation, with its own data, configurations, and user permissions. Usage metrics can report how many tenants were active in a period and, when available, a per-tenant breakdown of process, decision, and task-user usage. ## Retrieve metrics in SaaS In Camunda 8 SaaS an **Owner** or **Admin** of an organization can retrieve the information from the **Billing** page. You can access the **Billing** page by selecting **Organization Management** in the Camunda Console navigation bar. ## Retrieve metrics on Self-Managed :::caution Important note for Enterprise users Some Enterprise agreements require the following indices from Elasticsearch for at least 18 months: Usage metrics are stored in the `camunda-usage-metric-8.8.0_` and `camunda-usage-metric-tu-8.8.0_` indices. ::: On Camunda 8 Self-Managed, you can get the usage metrics via the [Get usage metrics system API][]. For more details about usage metrics, visit the [Usage Metrics concept][] page. [Usage Metrics concept]: ../../self-managed/components/orchestration-cluster/core-settings/concepts/usage-metrics.md [Get usage metrics system API]: ../../apis-tools/orchestration-cluster-api-rest/specifications/get-usage-metrics.api.mdx --- ## Source code and third-party dependencies ## About Camunda provides users and customers with source code access as part of its [written offer](https://legal.camunda.com/licensing-and-other-legal-terms#written-offer-source-code), as well as CycloneDX SBOMs listing all direct and transitive third-party dependencies and their respective licenses. :::info Learn more about CycloneDX SBOMs (International Standard for Bill of Materials) at [https://cyclonedx.org/](https://cyclonedx.org/). ::: ## Camunda 8 | Type | Description | | :----------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Dependencies | Download reports listing all direct and transitive third-party dependencies and their respective licenses from the public portal:SBOM: [CycloneDX](https://portal.fossa.com/p/camunda/release/4905/latest)Attribution: [TXT](https://portal.fossa.com/p/camunda/release/4904/latest) | | Source code | Access the source code for Camunda 8 at [github.com/camunda/camunda](https://github.com/camunda/camunda). | ## Desktop Modeler | Type | Description | | :----------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Dependencies | Download reports listing all direct and transitive third-party dependencies and their respective licenses from the public portal:SBOM: [CycloneDX](https://portal.fossa.com/p/camunda/release/4918/latest)Attribution: [TXT](https://portal.fossa.com/p/camunda/release/4917/latest) | | Source code | Access the source code for Desktop Modeler at [github.com/camunda/camunda-modeler](https://github.com/camunda/camunda-modeler). | ## Web Modeler | Type | Description | | :----------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Dependencies | Download reports listing all direct and transitive third-party dependencies and their respective licenses from the public portal:SBOM: [CycloneDX](https://portal.fossa.com/p/camunda/release/4920/latest)Attribution: [TXT](https://portal.fossa.com/p/camunda/release/4919/latest) | | Source code | Access to source code is provided [on demand](mailto:dependency-request@camunda.com). | ## Connectors | Type | Description | | :----------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Dependencies | Download reports listing all direct and transitive third-party dependencies and their respective licenses from the public portal:SBOM: [CycloneDX](https://portal.fossa.com/p/camunda/release/4946/latest)Attribution: [TXT](https://portal.fossa.com/p/camunda/release/4945/latest) | | Source code | Access the source code for connectors at [github.com/camunda/connectors](https://github.com/camunda/connectors). | ## Console Self-Managed | Type | Description | | :----------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Dependencies | Download reports listing all direct and transitive third-party dependencies and their respective licenses from the public portal:Frontend SBOM: [CycloneDX](https://portal.fossa.com/p/camunda/release/4951/latest)Frontend Attribution: [TXT](https://portal.fossa.com/p/camunda/release/4950/latest)Backend SBOM: [CycloneDX](https://portal.fossa.com/p/camunda/release/4949/latest)Backend Attribution: [TXT](https://portal.fossa.com/p/camunda/release/4948/latest) | | Source code | Access to source code is provided [on demand](mailto:dependency-request@camunda.com). | ## Management Identity | Type | Description | | :----------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Dependencies | Download reports listing all direct and transitive third-party dependencies and their respective licenses from the public portal:SBOM: [CycloneDX](https://portal.fossa.com/p/camunda/release/5086/latest)Attribution: [TXT](https://portal.fossa.com/p/camunda/release/5085/latest) | | Source code | Access to source code is provided [on demand](mailto:dependency-request@camunda.com). | --- ## Glossary(Reference) Explore and understand definitions for key Camunda 8 terms and abbreviations. A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ## A ### Admin Use Admin in the [Orchestration Cluster](#orchestration-cluster) to administer authentication, authorization, and cluster administration features. :::note Admin was previously named "Identity" in Camunda 8.8. The component was renamed in 8.9 to reflect its expanded scope. ::: - [Admin overview](/components/admin/admin-introduction.md) ### Agentic orchestration The governed coordination and management of AI agents, humans, and systems in a blended deterministic and dynamic process workflow to achieve defined goals. For example, orchestrate when and how AI agents act within a process, what tools they can use, and how results are validated, with guardrails for reliability, auditability, and human-in-the-loop. You can build AI agents with Camunda using BPMN. - [Agentic orchestration](/components/agentic-orchestration/agentic-orchestration-overview.md) ### Artificial intelligence (AI) A broad field of computer science focused on creating machines that can perform tasks requiring human-like intelligence. In practice, AI involves programming computers to learn, reason, and self-correct when solving problems. For example, AI applications include language understanding, image recognition, decision-making, and automation of complex tasks. ### AI agent An autonomous system, typically powered by an [LLM](#large-language-model-llm), that perceives its environment, makes decisions, and acts to achieve goals. AI agents can perform tasks, interact with other agents or systems, and operate with varying degrees of independence. For example, build an invoice-processing AI agent in Camunda with BPMN, using an ad-hoc subprocess and an AI Agent connector to provide LLM reasoning, tool calling, and short-term memory in a governed feedback loop. - [AI agents](/components/agentic-orchestration/ai-agents.md) - [Build your first AI Agent](/guides/getting-started-agentic-orchestration.md) ### Audit log The [audit log](../components/audit-log/overview.md) is a record of operations, including who performed them, when, and on which entities. Use the audit log to prove compliance, meet governance and regulatory requirements, maintain operational integrity and transparency, and troubleshoot issues. ## B ### Backpressure Backpressure is a protection mechanism that prevents [Zeebe brokers](#zeebe-broker) from being overloaded when they receive more [client](#zeebe-client) requests than they can process with acceptable latency. Zeebe brokers determine backpressure by using dynamic backpressure algorithms or - if enabled - flow control limits, which measure the rate of records written by the [exporter](#zeebe-exporter). When backpressure is activated, client requests are rejected to maintain system stability. - [Backpressure](/self-managed/components/orchestration-cluster/zeebe/operations/backpressure.md) - [Flow control](/self-managed/operational-guides/configure-flow-control/configure-flow-control.md) ### Broker See [Zeebe Broker](#zeebe-broker). ### BPMN model See [Process](#process). ### BTP BTP stands for [SAP](#sap) Business Technology Platform, which is a cloud-based platform that provides tools and services for data management, analytics, application development, and integration within the SAP ecosystem. Camunda can integrate with SAP BTP to orchestrate business processes across SAP and non-SAP systems. By doing so, it enables automation and visibility of workflows that span multiple services and applications hosted on BTP, enhancing agility and process control in enterprise environments. - [BTP plugin](/components/camunda-integrations/sap/btp-plugin.md) ## C ### Catalog A collection of reusable automation assets, such as element templates. The catalog is synced with your external Git repositories, governed at the organization-level in Camunda Hub, and used by delivery teams across workspaces and projects. - [Catalog](/components/hub/organization/manage-catalog/index.md) ### Client See [Zeebe Client](#zeebe-client). ### Cluster See [Zeebe cluster](#zeebe-cluster). ### Cluster variable A cluster [variable](../../components/concepts/variables/) is a centrally managed configuration value available across a Camunda cluster. It can be defined globally or at the tenant level and is used to provide environment-specific settings, such as API endpoints, feature flags, and shared configuration. ### Camunda 8 Camunda 8 is a universal process orchestrator that allows you to orchestrate and automate complex business processes that span people, systems, and devices. Camunda 8 consists of the following key components: | Component | Description | | :--------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [Orchestration Cluster](#orchestration-cluster) | Powers the automation and orchestration of [processes](#process). | | [Connectors](#connector) | Out-of-the-box integration with external systems. | | [Optimize](/components/optimize/what-is-optimize.md) | Business intelligence tooling, allowing you to analyze bottlenecks and examine improvements in [processes](#process) automated with Camunda. | | [Camunda Hub](/components/hub/index.md) | Manage organizational resources, manage projects, analyze operations and business value, and deliver agentic processes at scale with Camunda Hub. | | Modelers | Allows business users and developers to design and implement [processes](#process), decisions, and [user task](#user-task) forms:Use [Desktop Modeler](/components/modeler/desktop-modeler/index.md) locally on Mac, Windows, and Linux.Use the [Camunda Hub modeler](/components/hub/workspace/modeler/launch-modeler.md) in the browser. | | [Management Identity](#management-identity) | Authentication and authorization for the components outside the [Orchestration Cluster](#orchestration-cluster) (Optimize and Camunda Hub). | ### Command A command represents an action to be taken or executed. Example commands include: deploy a process, execute a process, etc. - [Internal processing](/components/zeebe/technical-concepts/internal-processing.md#events-and-commands) ### Connector [Connectors](/components/connectors/introduction.md) are reusable building blocks you can use to easily connect [processes](#process) to external systems, applications, and data. Connector types: - [Outbound](#outbound-connector) - [Inbound](#inbound-connector) - [Protocol](#protocol-connector) ### Connector runtime The [connector runtime](/components/connectors/custom-built-connectors/connector-sdk.md#runtime-environments) is the execution environment responsible for running connector logic, resolving authentication, handling secrets, and communicating with external systems. In SaaS, the runtime is fully managed. In Self-Managed environments, the runtime can run inside the cluster or in hybrid mode. ### Connector template A [connector template](/components/connectors/custom-built-connectors/connector-templates.md) is a type of element template used to configure connectors in Modeler. Templates define UI fields, metadata, and bindings required for connector operations. Modeler internally labels all templates as element templates, but connector templates are the subset specifically used to configure connectors. ### Context window The amount of text (in [tokens](#token-ai)) a model can consider at once when generating a response. A larger context window allows the model to handle longer inputs. ### Correlation Correlation refers to the act of matching a [message](#message) with an inflight [process instance](#process-instance). - [Message correlation](/components/concepts/messages.md) ### Correlation key A correlation is an attribute within a [message](#message) that is used to match the message against a certain [variable](#process-variable) within an inflight message. If the value of the correlation key matches the value of the variable within the [process instance](#process-instance), the message is matched. - [Message correlation](/components/concepts/messages.md) ### CSAP CLI CSAP CLI stands for Camunda SAP Integration Command-Line Interface. It's a standalone tool (`csap`) that simplifies configuring and building Camunda’s SAP integration modules—like the RFC connector, OData connector, and BTP plugin—for deployment. Camunda uses `csap` to automate setup steps: it interactively or via scripted flags configures connectors and plugins, resolves dependencies, and produces deployment-ready artifacts. This makes deploying SAP integrations (including BTP plugins) straightforward and repeatable in environments like Camunda SaaS. - [CSAP CLI](/components/camunda-integrations/sap/csap-cli.md) ## D ### Deployment A process cannot execute unless it is known by the [broker](#zeebe-broker). Deployment is the process of pushing or deploying processes to the [broker](#zeebe-broker). - [Zeebe Deployment](/apis-tools/zeebe-api/gateway-service.md#deployresource-rpc) ## E ### Elasticsearch/OpenSearch Elasticsearch and OpenSearch are search and analytics engines commonly used as document-store secondary storage backends for indexing and querying exported runtime data. They are populated with process orchestration data and consumed by components such as Operate, Tasklist, and Optimize. See also: [Secondary storage](#secondary-storage) ### Element A BPMN element is part of a [process](#process), defining one part of its BPMN model. Elements are the building blocks of a process and comprise [flow nodes](#flow-node), sequence flows, participants, data objects, and more. - [BPMN elements](/components/modeler/bpmn/bpmn-primer.md#bpmn-elements) ### Element template Use an element template to extend [Modeler](/components/modeler/about-modeler.md) with domain-specific diagram [elements](#element). The user edits such elements through a UI defined by the element template, and in the process configures BPMN element properties in simple and predictable ways. Element templates are used by [connectors](#connector) to create the connector-specific [element](#element) configuration. - [Element templates](/components/modeler/element-templates/about-templates.md) ### Embedding (vector embedding) A vector representation of data, including words, sentences, images, in a numerical space, where similar items are positioned near each other. Embeddings allow AI systems to compare meaning and perform tasks like semantic search. - [Vector database connector](/components/connectors/out-of-the-box-connectors/embeddings-vector-db.md) ### Event An event represents a state change associated with an aspect of an executing [process instance](#process-instance). Events capture [variable](#process-variable) changes, state transition in process elements, and so on. An event is represented by a timestamp, the variable name, and variable value. Events are stored in an append-only log. - [Internal processing](/components/zeebe/technical-concepts/internal-processing.md#events-and-commands) ### Execution platform version In Desktop Modeler and Web Modeler, the execution platform version is the Camunda runtime version that a diagram targets. It determines which execution semantics and validation rules are applied during modeling. The execution platform version is not a deployed process definition version, a Web Modeler version, or a SaaS cluster generation. - [Desktop Modeler flags](/components/modeler/desktop-modeler/flags/flags.md#default-execution-platform-version) ### Execution listener An execution listener is a mechanism that allows users to execute custom logic at specific points during [workflow](#workflow) execution. Execution listeners can be attached to [BPMN elements](#element) to react to lifecycle events, such as when an element starts or ends. This feature facilitates pre-processing and post-processing tasks without cluttering the BPMN model, functioning similarly to [job workers](#job-worker) by leveraging the same infrastructure. - [Execution listeners](/components/concepts/execution-listeners.md) ### Exporter See [Zeebe Exporter](#zeebe-exporter). ## F ### Fine-tuning The process of adapting a pre-trained AI model to a specific task or dataset, allowing it to specialize while leveraging prior knowledge. ### Flow node Flow nodes refer to a specific subset of BPMN [elements](#element). They comprise events, activities, and gateways. Other elements, such as sequence flows, participants, and data objects, are not considered flow nodes. ### Follower In a clustered environment, a [broker](#zeebe-broker) which is not a [leader](#leader) is a follower of a given partition. A follower can become the new leader when the old leader is no longer reachable. - [Clustering](/components/zeebe/technical-concepts/clustering.md#raft-consensus-and-replication-protocol) ## G ### Gateway Gateway can refer to different concepts in Camunda docs, depending on the context: - In BPMN, a gateway controls how a process flow branches, merges, or synchronizes. - In runtime and API contexts, the [Zeebe Gateway](#zeebe-gateway) is the component that exposes APIs and routes requests to Zeebe brokers. - In Kubernetes deployment docs, Gateway can also refer to Kubernetes Gateway API resources. ### Generative AI Any AI system that can produce new content, such as text, images, or audio, in response to prompts. Generative AI doesn’t just analyze existing data; it creates original output that is often contextually relevant to the input. ### Generation In Camunda 8 SaaS, a generation is the release identifier for the version set running in a cluster. Console uses generations instead of a single engine version because the underlying component versions can change independently. A generation is not a process definition version, a version tag, or a Web Modeler version. - [Generation names](/reference/announcements-release-notes/release-policy.md#generation-names) ### GPAI model A general-purpose AI model trained on large amounts of data using self-supervision at scale, capable of performing a wide range of tasks. Used in the EU AI Act to classify foundation models. - [AI usage guidelines](/guides/build-with-ai/ai-usage-guidelines.md#the-ai-models-behind-these-features) ## H ### Hallucination When an AI confidently produces incorrect or fabricated information that seems plausible. It reflects the model’s tendency to produce patterns that appear coherent but lack factual accuracy. ### H2 H2 is a lightweight relational database engine used as a secondary storage backend for local development and evaluation in Camunda. H2 can run in two modes: - **In-memory**: Data is stored only in memory and lost when the application stops. Useful for temporary testing. - **File-based (embedded)**: Database files are persisted to disk on the same host as the component using them. Suitable for local development where data persistence across restarts is needed. H2 is not intended for production usage. For Camunda secondary storage, H2 is a single-broker option and is not a valid backend for multi-broker clusters. - [Secondary storage](/self-managed/concepts/secondary-storage/index.md) See also: [Secondary storage](#secondary-storage) ### Human-in-the-Loop (HITL) A human review and approval step before AI-generated outputs with legal, financial, or safety-relevant effects are acted upon. ### Human task Camunda 8 allows you to orchestrate processes with human tasks, which may be [user tasks](#user-task) or [manual tasks](#manual-task). - [Human task orchestration](/guides/getting-started-orchestrate-human-tasks.md) ### Hybrid mode Hybrid mode, or a hybrid Self-Managed distribution, allows you to run a separate instance of the [connectors](#connector) runtime in a Self-Managed or local fashion. This instance can be attached to either a SaaS cluster, or another Self-Managed cluster with its own connector runtime. For example, this is useful when working with services that must be isolated within a private network and cannot be exposed to the public internet, or if infrastructure amendments need to be applied to the connector runtime, such as SSL certificates or mounted volumes. - [Use connectors in hybrid mode](/components/connectors/use-connectors-in-hybrid-mode.md) ## I ### Inbound connector Inbound [Connectors](#connector) in Camunda 8 enable workflows to receive data or messages from external systems or services, making it possible to integrate workflows into a wider business process or system architecture. Inbound connectors include three subtypes - [webhooks](#webhook), [subscriptions](#subscription), and polling. Unlike [outbound connectors](#outbound-connector), inbound connectors are **stateful**. The Java code of the inbound connector has a lifecycle suitable for long-running operations, such as listening for messages on a queue or waiting for a webhook to be called. Each element referencing an inbound connector will lead to the creation of one inbound connector instance. A process definition with one webhook start event and two additional webhooks as intermediate catch events would therefore lead to the creation of three inbound connector instances. ### Incident An incident represents an error condition which prevents [Zeebe](#zeebe) from advancing an executing [process instance](#process-instance). Zeebe will create an incident if there was an uncaught exception thrown in your code and the number of retries of the given step is exceeded. - [Incident](/components/concepts/incidents.md) ### Ingress An Ingress is a Kubernetes object that manages external access to the services within a Kubernetes cluster. An **Ingress controller** is required to route traffic to your services according to the rules defined on the Ingress. - [Ingress setup](/self-managed/deployment/helm/configure/ingress/ingress-setup.md) ## J ### Job A job represents a distinct unit of work within a [business process](#process). Service tasks represent such jobs in your process and are identified by a unique id. A job has a type to allow specific job workers to find jobs that they can work on. - [Job workers](/components/concepts/job-workers.md) ### Job activation timeout This is the amount of time the broker will wait for a complete or fail response from the job worker. This comes after a job has been submitted to the job worker for processing and before it marks the job as available again for other job workers. - [Job workers](/components/concepts/job-workers.md#requesting-jobs) ### Job worker A [Zeebe Client](#zeebe-client) that polls for and executes available [jobs](#job). An uncompleted job prevents [Zeebe](#zeebe) from advancing process execution to the next step. - [Job workers](/components/concepts/job-workers.md) ## K ### Kill switch A technically and organizationally secured mechanism that can be triggered at any time by authorized personnel to immediately place an AI use case, together with its connected tools and interfaces, into a safe state. This includes stopping ongoing and planned actions, preventing new executions, revoking or blocking access rights, and logging all measures in an auditable manner. - [AI usage guidelines](/guides/build-with-ai/ai-usage-guidelines.md#human-oversight) ## L ### Large language model (LLM) A large language model (LLM) is a type of AI program specifically designed to understand and generate human-like text. These models are trained on massive amounts of text data, enabling them to learn the structure of language and perform a variety of tasks, such as conversation, summarization, and code generation. ### Leader In a clustered environment, one [broker](#zeebe-broker) (the [leader](#leader)) is responsible for process execution and housekeeping of data within a [partition](#partition). Housekeeping includes taking [snapshots](#snapshot), [replication](#replication), and running [exports](#zeebe-exporter). - [Clustering](/components/zeebe/technical-concepts/clustering.md#raft-consensus-and-replication-protocol) ### Log The log is comprised of an ordered sequence of records written to persistent storage. The log is appended-only and is stored on disk within the broker. - [Partitions](/components/zeebe/technical-concepts/partitions.md#partition-data-layout) ## M ### Management Identity The Management Identity component provides authentication and authorization for the [Camunda 8](#camunda-8) components outside the [Orchestration Cluster](#orchestration-cluster): Camunda Hub and Optimize. ### Manual task A manual task defines a task that requires human interaction but no external tooling or UI interface. For example, a user reviewing a document or completing a physical task. Manual tasks are part of [human task orchestration](/guides/getting-started-orchestrate-human-tasks.md), but differ from [user tasks](#user-task) which define an actionable task assisted by a workflow engine or software application. - [Manual tasks](/components/modeler/bpmn/manual-tasks/manual-tasks.md) ### Multi-tenancy Multi-tenancy allows a single Camunda 8 installation to serve multiple tenants while keeping each tenant's data, configurations, and access logically isolated. - [Multi-tenancy](/components/concepts/multi-tenancy.md) ### Message A message contains information to be delivered to interested parties during execution of a [process instance](#process-instance). Messages can be published via Kafka or [Zeebe](#zeebe)’s internal messaging system. Messages are associated with timestamp and other constraints such as time-to-live (TTL). - [Messages](/components/concepts/messages.md) ## O ### Orchestration Cluster The Orchestration Cluster is the core component of [Camunda 8](#camunda-8), powering the automation and orchestration of [processes](#process). An Orchestration Cluster includes: - [Zeebe](#zeebe) as the [workflow engine](#workflow-engine) - Operate for monitoring and troubleshooting [process instances](#process-instance) running in [Zeebe](#zeebe) - Tasklist for interacting with [user tasks](#user-task) (assigning, completing, and so on) - [Identity](#identity) for managing the integrated authentication and authorization - APIs for interacting with the Orchestration Cluster programmatically ### Outbound connector Outbound [Connectors](#connector) in Camunda 8 allow workflows to trigger with external systems or services, making it possible to integrate workflows with other parts of a business process or system architecture. ## P ### Partition A partition represents a logical grouping of data in a [Zeebe Broker](#zeebe-broker). This data includes [process variables](#process-variable) stored in RocksDB, [commands](#command), and [events](#event) generated by [Zeebe](#zeebe) stored in the [log](#log). The number of partitions is defined by configuration. - [Partitions](/components/zeebe/technical-concepts/partitions.md) ### Polling connector An inbound polling [connector](#connector) that periodically polls an external system or service for new data using HTTP polling. A [Camunda workflow](#workflow) uses this type of connector to retrieve data from a remote system that does not support real-time notifications or webhooks, but instead requires the client to periodically request updates. ### Primary storage Primary storage is the authoritative store for workflow execution state managed by the Orchestration Cluster. In Self-Managed deployments, Zeebe brokers persist partition logs and snapshots on local disk. This data is required to execute workflows, recover after failures, and replicate state across brokers. Primary storage is not a search or analytics backend, and it is not configured as an external database (such as Elasticsearch/OpenSearch, H2, or an external RDBMS). See also: [Orchestration Cluster](#orchestration-cluster), [Log](#log), [Partition](#partition), [Snapshot](#snapshot), [Secondary storage](#secondary-storage) ### Process The general business construct — what you want to automate. In Camunda, a process is [modeled using BPMN](#process-model), then [deployed as a process definition](#process-definition), and finally [executed as a process instance](#process-instance). In runtime discussions, [_executing a process_](/components/concepts/processes.md) may be used as shorthand for deploying a process definition and starting an instance. ### Process definition A [process model](#process-model) that has been deployed to the engine and versioned. Identified by: - **processDefinitionKey**: Unique identifier generated by the engine - **process ID**: Identifier assigned in the process model - **version**: Version number assigned by the engine The engine uses process definitions to start [process instances](#process-instance). ### Process definition version A process definition version is the numeric version assigned by the Orchestration Cluster each time you deploy a process definition with the same process ID. Operate, Optimize, and APIs often shorten this to version. A process definition version is different from a version tag, which is a user-defined label, and from a Web Modeler version, which is a saved file or project snapshot. - [Process definition](#process-definition) - [Migrate process instances](/components/operate/userguide/process-instance-migration.md) ### Process instance A [process instance](/components/concepts/process-instance-creation.md) is an execution of a [process definition](#process-definition), uniquely identified by its **processInstanceKey**. Each instance represents one run of the process and carries metadata from its originating process definition (process ID, version, and processDefinitionKey). A process instance can be active (currently running), completed, or terminated. In runtime discussions, [_executing a process_](/components/concepts/processes.md) may be used as shorthand for deploying a process definition and starting an instance. ### Process instance tag An optional, immutable, lightweight label attached when a process instance is created. Tags provide fast, structured metadata for routing, correlation, prioritization, and analytics segmentation without inspecting large variable payloads. - Tags on process instance creation: [Process instance creation – Tags](/components/concepts/process-instance-creation.md#tags) - Tags on jobs: [Job workers](/components/concepts/job-workers.md#tags) ### Process model The BPMN representation of a [process](/components/concepts/processes.md), created in a BPMN file (for example, in Modeler). Identified by its **process ID** (`bpmn:process id` attribute). A single BPMN file can contain multiple process models. ### Process variable A process variable represents the execution state (i.e data) of a process instance. These variables capture business process parameters which are the input and output of various stages of the process instance and which also influence process flow execution. - [Variables](/components/concepts/variables.md) - [Data flow](/components/modeler/bpmn/data-flow.md) ### Project A collection of related files in a Camunda Hub workspace you can work on and deploy as a single bundle. A workspace may contain multiple projects. - [Project](/components/hub/workspace/manage-projects/manage-projects.md) ### Prompt A prompt is the input provided to a [generative AI model](#generative-ai). Prompt engineering refers to the practice of crafting effective instructions to achieve desired results from the AI. ### Protocol connector Protocol connectors are a type of [Connector](#connector) in Camunda that can serve as either [inbound](#inbound-connector) or [outbound](#outbound-connector) connectors, supporting a variety of technical protocols. These connectors are highly generic, designed to provide a flexible and customizable means of integrating with external systems and services. Protocol connectors can be customized to meet the needs of specific use cases using [element templates](#element-template), with no additional coding or deployment required. Examples of protocol connectors include HTTP REST, SOAP, GraphQL, as well as message queue connectors. ### Public API The public API represents the official set of interfaces in Camunda 8 that are covered by [Semantic Versioning (SemVer)](https://semver.org/) stability guarantees. APIs included in the public API contract will not introduce breaking changes in minor or patch releases, ensuring backwards compatibility for your integrations. The public API is a subset of all available Camunda 8 APIs - many APIs are available for external use but are not included in the formal stability commitment. - [Public API](/reference/public-api.md) ## R ### RDBMS RDBMS (Relational Database Management System) refers to a user-managed relational database used as a secondary storage backend in Camunda 8 Self-Managed deployments, depending on the component and configuration. An external RDBMS is used for query and retention use cases, not for core workflow execution state. - [Helm database configuration (RDBMS)](/self-managed/deployment/helm/configure/database/rdbms.md) See also: [Secondary storage](#secondary-storage) ### Record A record represents a command or an event. For example, a command to create a new [process instance](#process-instance), or a state transition of an executing [process instance](#process-instance) representing an [event](#event) at a given point in time would result to generation of a record. During the execution lifecycle of a process instance, numerous records are generated to capture various commands and events generated. Records are stored in the log. - [Internal processing](/components/zeebe/technical-concepts/internal-processing.md#events-and-commands) ### Reference architecture Reference architectures provide comprehensive blueprints for designing and implementing scalable, robust, and adaptable Camunda 8 self-managed installations. Reference architectures serve as starting points that should be adapted to fit the specific needs and constraints of your organization and infrastructure. - [Reference architectures](/self-managed/reference-architecture/reference-architecture.md) ### Replication Replication is the act of copying data in a [partition](#partition) from a [leader](#leader) to its [followers](#follower) within a clustered [Zeebe](#zeebe) deployment. After replication, the leader and followers of a partition will have the exact same data. Replication allows the system to be resilient to [brokers](#zeebe-broker) going down. - [Clustering](/components/zeebe/technical-concepts/clustering.md#raft-consensus-and-replication-protocol) ### Replication factor This is the number of times data in a [partition](#partition) is copied. This depends on the number of [brokers](#zeebe-broker) in a [cluster](#zeebe-cluster). A cluster with one [leader](#leader) and two [followers](#follower) has a replication factor of three, as data in each partition needs to have three copies. We recommend running an odd replication factor. - [Partitions](/components/zeebe/technical-concepts/partitions.md#replication) ### Request timeout How long a [client](#zeebe-client) waits for a response from the [broker](#zeebe-broker) after the client submits a request. If a response is not received within the client request timeout, the client considers the broker unreachable. - [Orchestration Cluster REST API](/apis-tools/zeebe-api-rest/zeebe-api-rest-overview.md) - [Zeebe API (gRPC)](/apis-tools/zeebe-api/grpc.md) ### RFC RFC stands for Remote Function Call, a protocol used by SAP to enable communication and data exchange between SAP systems or between SAP and external systems. Camunda can use RFC to call SAP functions directly as part of a business process. This allows Camunda to trigger SAP transactions, retrieve data, or update records within an SAP system, integrating SAP functionality seamlessly into broader automated workflows. - [RFC](/components/camunda-integrations/sap/csap-cli.md) ### Robotic process automation (RPA) The use of software robots to automate repetitive, rule-based business tasks. RPA bots emulate human actions in digital systems, enhancing speed and accuracy. ## S ### SAP SAP stands for Systems, Applications, and Products in Data Processing; it's an enterprise software platform used to manage business operations such as finance, supply chain, and HR. Camunda integrates with SAP to automate and orchestrate workflows that involve SAP systems, allowing for greater flexibility, transparency, and control over complex business processes. - [SAP](/components/camunda-integrations/overview.md) ### Secondary storage Secondary storage is used for indexing, search, analytics, and long-term retention. Data in secondary storage is typically exported from [primary storage](#primary-storage) and consumed by tools for observability and analytics. Examples of secondary storage backends include: - [Document store (Elasticsearch/OpenSearch)](#elasticsearchopensearch) - [RDBMS](#rdbms) - [Secondary storage concepts](/self-managed/concepts/secondary-storage/index.md) - [Managing secondary storage](/self-managed/concepts/secondary-storage/managing-secondary-storage.md) ### Segment The [log](#log) consists of one or more segments. Each segment is a file containing an ordered sequence records. Segments are deleted when the log is compacted. - [Resource planning](/self-managed/components/orchestration-cluster/zeebe/operations/resource-planning.md#event-log) ### Snapshot The state of all active [process instances](#process-instance), (these are also known as inflight process instances) are stored as records in an in-memory database called RocksDB. A snapshot represents a copy of all data within the in-memory database at any given point in time. Snapshots are binary images stored on disk and can be used to restore execution state of a [process](#process). The size of a snapshot is affected by the size of the data. Size of the data depends on several factors, including complexity of the [model](#bpmn-model), the size and quantity of variables in each process instance, and the total number of executing [process instances](#process-instance) in a [broker](#zeebe-broker). - [Resource planning](/self-managed/components/orchestration-cluster/zeebe/operations/resource-planning.md#snapshots) ### Soft pause exporting Soft pause exporting is a feature that allows you to continue exporting records from [Zeebe](#zeebe), but without deleting those [records](#record) ([log](#log) compaction) from Zeebe. This is particularly useful during hot backups. - [Exporting API](/self-managed/components/orchestration-cluster/zeebe/operations/management-api.md) - [Backup and restore](/self-managed/operational-guides/backup-restore/zeebe-backup-and-restore.md) ### Subscription inbound connector An [inbound connector](#inbound-connector) that subscribes to a message queue. This way, a [Camunda workflow](#workflow) can receive messages from an external system or service (like Kafka or RabbitMQ) using message queuing technology. This type of inbound connector is commonly used in distributed systems where different components of the system need to communicate with each other asynchronously. ## T ### Tenant A tenant is a logically isolated space within a shared Camunda 8 installation, with its own data, configurations, and user permissions. - [Tenant management](/components/admin/tenant.md) - [Multi-tenancy](/components/concepts/multi-tenancy.md) ### Temperature A parameter that regulates the randomness or creativity of AI-generated text. Lower values result in more focused and predictable responses, while higher values lead to more creative and varied outputs. ### Token (AI) The smallest unit of text such as a word, subword, or character, that a language model processes. Models read and generate text as a sequence of tokens. Often, pricing for AI models is based on the number of input/output tokens. ### Token (process instance) In the context of a running process instance in Camunda, a token represents the current point of execution within the BPMN process model. You can think of it as a marker that moves through the process diagram, following the sequence flows as tasks and events are completed. When a process starts, a token is created at the start event and advances with each completed step. Once the token reaches the end event, it is consumed and the process instance ends. Tokens are not data themselves, but they determine which elements of the process are currently active. ## U ### User task A user task is used to model work that needs to be done by a human and is assisted by a workflow engine or software application. This differs from [manual tasks](#manual-task), which are not assisted by external tooling. With 8.7, Camunda offers job worker-based user tasks managed by Camunda, also known as Camunda user tasks (and formerly known as Zeebe user tasks). Note that you may still see references of **Zeebe user tasks** in your XML, but this is the same thing as Camunda user tasks. Camunda recommends using Camunda user tasks in your process definitions. With 8.7, **job-worker** user tasks are available for querying, but Camunda Modeler automatically applies the **Camunda user task** and shows a warning message for each job worker user task. - [User tasks](/components/modeler/bpmn/user-tasks/user-tasks.md) - [Migrate to Camunda user tasks](/apis-tools/migration-manuals/migrate-to-camunda-user-tasks.md) ### User task listener A user task listener allows users to execute custom logic in response to specific user task lifecycle events, such as assigning or completing a task. User task listeners are attached to BPMN user tasks and facilitate validation, custom task assignment, and other operations during user task execution. They operate similarly to [job workers](#job-worker), leveraging the same infrastructure for processing external logic. - [User task listeners](/components/concepts/user-task-listeners.md) ## V ### Variable A variable stores data for a [process instance](#process-instance) or a local scope within a process. Variables can hold JSON values and are used to pass business data between tasks, expressions, and events. - [Variables](/components/concepts/variables.md) ### Version In Camunda 8, version is an overloaded term. Depending on context, it can refer to a [process definition version](#process-definition-version), a [version tag](#version-tag), a [Web Modeler version](#web-modeler-version), an [execution platform version](#execution-platform-version), or a SaaS [generation](#generation). ### Version tag A version tag is a user-defined string label for a specific resource or snapshot. For deployed BPMN, DMN, and form resources, a version tag can be used to identify a resource version and to resolve dependencies with `versionTag` binding. In Web Modeler project versioning, a version tag labels a saved project snapshot. A version tag is not generated automatically and does not replace the numeric process definition version. - [Resource binding types](/components/best-practices/modeling/choosing-the-resource-binding-type.md#versiontag) - [Project versioning](/components/hub/workspace/manage-projects/project-versioning.md) ### Web Modeler version A Web Modeler version is a saved snapshot of a BPMN or DMN file, or of an entire project. Diagram versions were previously called milestones. Web Modeler versions help you compare, restore, review, and deploy snapshots. They are distinct from deployed process definition versions in the Orchestration Cluster. - [Versions](/components/hub/workspace/modeler/modeling/versions.md) - [Project versioning](/components/hub/workspace/manage-projects/project-versioning.md) ## W ### Webhook connector Webhooks are a subtype of [inbound connector](#inbound-connector). A webhook is a way for web applications to send real-time notifications or data to other applications or services when certain events occur. When a webhook is set up, the application sends a POST request containing data to a pre-configured URL, which triggers a workflow. ### Worker See [Job worker](#job-worker). ### Workflow See [process](#process). ### Workflow engine A workflow engine is an essential part of any process automation tool. We call it an “engine” because it drives business processes from start to finish, no matter how complex the process and decision logic need to be. [Zeebe](#zeebe) is the workflow engine powering Camunda 8. ### Workflow instance See [process instance](#process-instance). ### Workflow instance variable See [process variable](#process-variable). ### Workspace A collaboration environment within an organization, representing a team or business domain. A workspace is assigned members, roles, projects, and clusters, so all related work happens in one shared space. - [Workspace](/components/hub/workspace/index.md) ## Z ### Zeebe Zeebe is a highly scalable, cloud-native workflow engine used to automate business processes. It acts as the core component of Camunda 8. Zeebe is part of the [Orchestration Cluster](#orchestration-cluster) in Camunda 8. The main components of Zeebe are: - [Clients](#zeebe-client) - [Gateways](#zeebe-gateway) - [Brokers](#zeebe-broker) - [Exporters](#zeebe-exporter) A Zeebe deployment typically consists of multiple brokers and gateways, forming a [Zeebe cluster](#zeebe-cluster). ### Zeebe Broker The Zeebe Broker is the distributed [workflow engine](#workflow-engine) that tracks the state of active [process instances](#process-instance). The Zeebe Broker is the main part of the [Zeebe cluster](#zeebe-cluster), which does all the heavy work like processing, replicating, exporting, and everything based on [partitions](#partition). A Zeebe deployment often consists of more than one broker. Brokers can be partitioned for horizontal scalability and replicated for fault tolerance. - [Zeebe Broker](/components/zeebe/technical-concepts/architecture.md#brokers) ### Zeebe Client A Zeebe Client interacts with the [Zeebe Broker](#zeebe-broker) on behalf of the business application. Clients retrieve work from the [Zeebe cluster](#zeebe-cluster) via polling or job push. - [Zeebe Client](/components/zeebe/technical-concepts/architecture.md#clients) ### Zeebe cluster A Zeebe cluster represents a configuration of one or more [brokers](#zeebe-broker) collaborating to execute [processes](#process). Each [broker](#zeebe-broker) in a cluster acts as a [leader](#leader) or a [follower](#follower). - [Clustering](/components/zeebe/technical-concepts/clustering.md) ### Zeebe Exporter The Zeebe Exporter system provides an event stream of state changes within Zeebe. It represents a sink to which Zeebe will submit all [records](#record) within the [log](#log). This gives users of Zeebe an opportunity to persist [records](#record) with the log for future use as this data will not be available after log compaction. - [Zeebe Exporter](/components/zeebe/technical-concepts/architecture.md#exporters) ### Zeebe Gateway The Zeebe Gateway is a component of the [Zeebe cluster](#zeebe-cluster); it can be considered the contact point for the Zeebe cluster that allows [Zeebe clients](#zeebe-client) to communicate with [Zeebe brokers](#zeebe-broker) inside a Zeebe cluster. - [Zeebe Gateway](/self-managed/components/orchestration-cluster/zeebe/zeebe-gateway/zeebe-gateway-overview.md) --- ## Legal center - [Licensing and other legal terms](https://legal.camunda.com/licensing-and-other-legal-terms) - [Privacy and data protection](https://legal.camunda.com/privacy-and-data-protection) - [Environmental, social, and governance](https://legal.camunda.com/ESG) - [Data processing agreement](https://legal.camunda.com/dpa) --- ## Licensing ## Zeebe, Operate, Tasklist, Identity, and Optimize The source code for Zeebe, Operate, Tasklist, Identity, and Optimize is licensed to our users and customers under the [Camunda License v1](https://legal.camunda.com/licensing-and-other-legal-terms#camunda-license). The compiled software of these components is distributed under a proprietary license, which can be either the [Camunda Self-Managed Non-Production license](https://legal.camunda.com/#self-managed-non-production-terms) or the Camunda Self-Managed Enterprise Edition license (a copy of which you obtain when you contact Camunda). To use the software in production, [purchase the Camunda Self-Managed Enterprise Edition](https://camunda.com/platform/camunda-platform-enterprise-contact/). ## Camunda connectors The Connector SDK, REST connector, Connector Runtime Docker image, and Connectors Bundle Docker image are licensed under the [Apache 2.0 license](https://www.apache.org/licenses/LICENSE-2.0). The source code of all other connectors provided by Camunda out of the box is licensed under the [Camunda License v1](https://legal.camunda.com/licensing-and-other-legal-terms#camunda-license). The compiled software is distributed under a proprietary license, which can be either the [Camunda Self-Managed Non-Production license](https://legal.camunda.com/#self-managed-non-production-terms) or the Camunda Self-Managed Enterprise Edition license (a copy of which you obtain when you contact Camunda). To use the software in production, [purchase the Camunda Self-Managed Enterprise Edition](https://camunda.com/platform/camunda-platform-enterprise-contact/). ## Web Modeler and Console The source code for the Web Modeler and Console is licensed to our users and customers under a proprietary license and is only available to users or customers on demand. The compiled software of these components is distributed under a proprietary license, which can be either the [Camunda Self-Managed Non-Production license](https://legal.camunda.com/#self-managed-non-production-terms) or the Camunda Self-Managed Enterprise Edition license (a copy of which you obtain when you contact Camunda). To use the software in production, [purchase the Camunda Self-Managed Enterprise Edition](https://camunda.com/platform/camunda-platform-enterprise-contact/). ## Desktop Modeler The source code of Desktop Modeler is licensed under the MIT license as stated in the [LICENSE file](https://github.com/camunda/camunda-modeler/blob/master/LICENSE) in the root of the source code repository. This file is also shipped as `LICENSE.camunda-modeler.txt` with each modeler distribution. ## Camunda 8 documentation License information for our documentation can be found in the [LICENSE.txt](https://github.com/camunda/camunda-docs/blob/main/LICENSE.txt) of the Camunda 8 documentation repository. ## Terms & conditions For information not covered by the above license links, visit our [Terms and Conditions](https://legal.camunda.com/licensing-and-other-legal-terms). --- ## Camunda Docs MCP server ## About Add the Camunda Docs [(Model Context Protocol) MCP server](https://modelcontextprotocol.io/docs/getting-started/intro) to give your AI agent direct access to the latest official Camunda 8 documentation. For example, if you use an AI coding tool such as [Cursor](https://cursor.com/) or [Copilot](https://code.visualstudio.com/docs/copilot/overview), adding the MCP server can help ensure more accurate AI responses and code generation by accessing the current Camunda 8 documentation and context. The MCP server is available at the following URL: ``` https://camunda-docs.mcp.kapa.ai ``` ## Install ### One-click install You can perform a one-click installation or copy the MCP server URL via the **Use MCP** dropdown. ### Manual install The steps required to manually install the MCP server varies depending on your IDE/AI tool. For example: **Prerequisites:** VS Code 1.102+ with GitHub Copilot enabled. Create an `mcp.json` file in your workspace `.vscode` folder: ```json title=".vscode/mcp.json" { "servers": { "camunda docs": { "type": "http", "url": "https://camunda-docs.mcp.kapa.ai" } } } ``` :::info For more information, refer to the [MCP servers in VS Code](https://code.visualstudio.com/docs/copilot/customization/mcp-servers) documentation. ::: Add the following to your `.cursor/mcp.json` file: ```json title=".cursor/mcp.json" { "mcpServers": { "camunda docs": { "type": "http", "url": "https://camunda-docs.mcp.kapa.ai" } } } ``` :::info For more information, refer to the [Cursor MCP](https://cursor.com/docs/context/mcp) documentation. ::: ChatGPT Desktop supports MCP servers in developer mode: 1. Open ChatGPT Desktop. 1. Navigate to **Settings > Features**. 1. Enable **Developer mode**. 1. Navigate to **Settings > MCP Servers**. 1. Click **Add Server** and enter: - **Name**: `camunda docs` - **URL**: `https://camunda-docs.mcp.kapa.ai` :::info For more information, refer to the [ChatGPT Desktop MCP](https://platform.openai.com/docs/guides/developer-mode) documentation. ::: Use the server URL `https://camunda-docs.mcp.kapa.ai` and refer to your client's documentation for installation instructions. Most clients accept the standard MCP protocol JSON configuration format: ```json { "mcpServers": { "camunda docs": { "url": "https://camunda-docs.mcp.kapa.ai" } } } ``` ## Usage and limits :::caution caution The Camunda Docs MCP server is not designed for use in production environments, high-volume automation, or as part of a CI/CD pipeline. It is provided to help support Camunda developer IDE queries and for coding assistance, evaluation, and testing. You must always check and validate AI generated content and code as responses can be inaccurate. ::: Once connected to the MCP server within your editor, you can ask context-aware questions about Camunda. For example: - "What is BPMN?" - "How do I build an AI agent?" - "What properties are changed for Camunda 8.8?" ### Authentication When connecting to the MCP server for the first time, you must authenticate with either Google or GitHub. This authentication is only used to enforce per-user rate limits and prevent abuse of the Camunda Docs MCP server: - 40 requests per user per hour. - 200 requests per user per day. If you sign in with GitHub, Kapa does not request access to your repositories, organizations, or email address. It uses only a stable GitHub user ID for rate limiting. :::note The MCP server is powered by the Kapa.ai AI assistant. Refer to the [Kapa documentation](https://docs.kapa.ai/integrations/mcp/overview#authentication) to learn more about authentication. ::: ### Tools The MCP server exposes a single semantic search tool: `search_camunda_knowledge_sources` This tool allows AI tools/agents to perform semantic retrieval over the Camunda 8 documentation and other public knowledge sources, such as forum posts, repos, podcasts, and product blogs. ## Example VS Code Copilot integration 1. Install the Camunda Docs MCP server. 1. In VS Code, open Copilot Chat. 1. Select **Agent** mode from the **Set Agent** drop-down menu. 1. Click **Configure Tools** to check the `search_camunda_knowledge_sources` tool is available and selected. 1. Ask context-aware questions about Camunda. --- ## Security notices :::tip Subscribe to security notices Subscribe to the [RSS feed](pathname:///rss/security/notices.xml) for security updates and get automatic notifications when new security notices are published. ::: ## Report a security issue or vulnerability Report security vulnerabilities to Camunda immediately, following the instructions at [Camunda Security](https://camunda.com/security#report-a-vulnerability). :::info To learn more about security at Camunda, including our security policy, security issue management, and more, see [Camunda.com/security](https://camunda.com/security). ::: ## Notice 48 ### Publication date May 13, 2026 ### Products affected - Camunda Identity - Camunda Zeebe - Camunda Tasklist - Camunda Operate - Camunda Web Modeler ### Impact The application was vulnerable to [CVE-2026-42198](https://nvd.nist.gov/vuln/detail/CVE-2026-42198), where an attacker with the privileges to impersonate or perform a man-in-the-middle (MITM) attack on the PostgreSQL server can force the JDBC driver to perform SCRAM authentication with a very large iteration count. This can lead to high CPU usage during the SCRAM PBKDF2 computation before authentication can fail. Web Modeler and Identity are affected by this CVE when run with their default configuration. Zeebe, Operate, and Tasklist are only affected in a self-managed setup when running the application with RDBMS as secondary storage with a PostgreSQL server configured as the endpoint. ### How to determine if the installation is affected You are using: - Camunda Identity ≤ 8.9.2, ≤ 8.8.11, or ≤ 8.7.18 - Camunda Zeebe ≤ 8.9.2, ≤ 8.8.23, or ≤ 8.7.28 - Camunda Tasklist ≤ 8.9.2, ≤ 8.8.23, or ≤ 8.7.28 - Camunda Operate ≤ 8.9.2, ≤ 8.8.23, or ≤ 8.7.28 - Camunda Web Modeler ≤ 8.9.2, ≤ 8.8.13, or ≤ 8.7.20 ### Solution Camunda has provided the following releases which contain the fix: - Camunda Identity 8.9.3, 8.8.12, 8.7.19 - Camunda Zeebe 8.9.3, 8.8.24, 8.7.29 - Camunda Tasklist 8.9.3, 8.8.24, 8.7.29 - Camunda Operate 8.9.3, 8.8.24, 8.7.29 - Camunda Web Modeler 8.9.3, 8.8.14, 8.7.21 ## Notice 47 ### Publication date May 7, 2026 ### Products affected - Camunda Identity - Camunda Zeebe - Camunda Tasklist - Camunda Operate - Camunda Optimize - Camunda Web Modeler ### Impact The application was vulnerable to [CVE-2026-40973](https://nvd.nist.gov/vuln/detail/CVE-2026-40973), where a local attacker could take control of the embedded Spring Boot `ApplicationTemp` directory. If `server.servlet.session.persistent` is set to `true`, this could allow session hijacking or arbitrary code execution. Default Camunda 8 deployments are not affected. ### How to determine if the installation is affected You are using: - Camunda Identity ≤ 8.9.1, ≤ 8.8.11, or ≤ 8.7.18 - Camunda Zeebe ≤ 8.9.1, ≤ 8.8.23, or ≤ 8.7.28 - Camunda Tasklist ≤ 8.9.1, ≤ 8.8.23, or ≤ 8.7.28 - Camunda Operate ≤ 8.9.1, ≤ 8.8.23, or ≤ 8.7.28 - Camunda Optimize ≤ 8.9.1, ≤ 8.8.8, or ≤ 8.7.20 - Camunda Web Modeler ≤ 8.9.1, ≤ 8.8.13, or ≤ 8.7.20 ### Solution Camunda has provided the following releases which contain the fix: - Camunda Identity 8.9.2, 8.8.12, 8.7.19 - Camunda Zeebe 8.9.2, 8.8.24, 8.7.29 - Camunda Tasklist 8.9.2, 8.8.24, 8.7.29 - Camunda Operate 8.9.2, 8.8.24, 8.7.29 - Camunda Optimize 8.9.2, 8.8.24, 8.7.21 - Camunda Web Modeler 8.9.2, 8.8.14, 8.7.21 ## Notice 46 ### Publication date May 7, 2026 ### Products affected - Camunda Web Modeler ### Impact The version of `axios` used by Camunda Web Modeler was affected by [CVE-2026-42264](https://github.com/advisories/GHSA-q8qp-cvcw-x6jj), a prototype pollution vulnerability in the HTTP adapter that could allow credential injection and request hijacking when `Object.prototype` is polluted by another dependency in the same process. ### How to determine if the installation is affected You are using: - Web Modeler Self-Managed ≤ 8.8.13, or ≤ 8.7.20 ### Solution Camunda has provided the following releases that contain the fix: - Web Modeler Self-Managed 8.8.14, 8.7.21 ## Notice 45 ### Publication date April 24, 2026 ### Products affected - Camunda Tasklist - Camunda Zeebe - Camunda Operate ### Impact The application was vulnerable to [CVE-2026-5588](https://nvd.nist.gov/vuln/detail/CVE-2026-5588), where the PKIX CompositeVerifier in the embedded Bouncy Castle cryptography library could accept an empty signature sequence as valid, weakening certificate signature verification and potentially allowing acceptance of improperly signed certificates. ### How to determine if the installation is affected You are using: - Camunda Tasklist 8.9.0, ≤ 8.8.22, or ≤ 8.7.27 - Camunda Zeebe 8.9.0, ≤ 8.8.22, or ≤ 8.7.27 - Camunda Operate 8.9.0, ≤ 8.8.22, or ≤ 8.7.27 ### Solution Camunda has provided the following releases which contain the fix: - Camunda Tasklist 8.9.1, 8.8.23, 8.7.28 - Camunda Zeebe 8.9.1, 8.8.23, 8.7.28 - Camunda Operate 8.9.1, 8.8.23, 8.7.28 ## Notice 44 ### Publication date April 24, 2026 ### Products affected - Camunda Tasklist - Camunda Zeebe - Camunda Operate ### Impact The application was vulnerable to [CVE-2025-14813](https://nvd.nist.gov/vuln/detail/CVE-2025-14813), where the GOSTCTR implementation in the embedded Bouncy Castle cryptography library used an incorrect one-byte counter and could not securely encrypt or decrypt more than 255 blocks, potentially compromising the confidentiality and integrity of data encrypted with this algorithm. ### How to determine if the installation is affected You are using: - Camunda Tasklist 8.9.0, ≤ 8.8.22, or ≤ 8.7.27 - Camunda Zeebe 8.9.0, ≤ 8.8.22, or ≤ 8.7.27 - Camunda Operate 8.9.0, ≤ 8.8.22, or ≤ 8.7.27 ### Solution Camunda has provided the following releases which contain the fix: - Camunda Tasklist 8.9.1, 8.8.23, 8.7.28 - Camunda Zeebe 8.9.1, 8.8.23, 8.7.28 - Camunda Operate 8.9.1, 8.8.23, 8.7.28 ## Notice 43 ### Publication date Apr 7, 2026 ### Products affected - Management Identity - Camunda Tasklist - Camunda Zeebe - Camunda Operate - Camunda Optimize - Camunda Web Modeler ### Impact When applications specify HTTP response headers for servlet applications using Spring Security, there is the possibility that the HTTP headers will not be written. This is related to [CVE-2026-22732](https://nvd.nist.gov/vuln/detail/CVE-2026-22732). ### How to determine if the installation is affected You are using: - Management Identity 8.7.4 - 8.7.10, 8.7.12 - 8.7.16, 8.8.0 - 8.8.2, or 8.8.5 - 8.8.9 - Zeebe 8.7.21 - 8.7.25 - Tasklist 8.7.21 - 8.7.25 - Operate 8.7.22 - 8.7.25 - Optimize 8.7.14 - 8.7.18 or 8.8.2 - 8.8.7 - Web Modeler Self-Managed ≤ 8.6.26, ≤ 8.7.18, or ≤ 8.8.11 ### Solution Camunda has provided the following releases which contain the fix: - Management Identity 8.7.17, 8.8.10 - Zeebe 8.7.26 - Tasklist 8.7.26 - Operate 8.7.26 - Optimize 8.7.19, 8.8.8 - Web Modeler Self-Managed 8.6.27, 8.7.19, 8.8.12 The fix was deployed to Web Modeler SaaS on March 23, 2026, 17:26 CET. ## Notice 42 ### Publication date Mar 09, 2026 ### Products affected - Management Identity ### Impact The application was vulnerable to [CVE-2026-24734](https://nvd.nist.gov/vuln/detail/CVE-2026-24734), which allowed an attacker to bypass revocation checks of client SSL certificates if a certain server configuration was used. ### How to determine if the installation is affected You are using: - Management Identity ≤ 8.8.7, ≤ 8.7.14, or ≤ 8.6.27 ### Solution Camunda has provided the following releases which contain the fix: - Management Identity 8.8.8, 8.7.15, 8.6.28 ## Notice 41 ### Publication date Mar 9, 2026 ### Products affected - Camunda Web Modeler ### Impact The version of `fast-xml-parser` used by Camunda Web Modeler was affected by [CVE-2026-26278](https://nvd.nist.gov/vuln/detail/CVE-2026-26278), a vulnerability which could be exploited as a vector for denial of service attacks by forcing the parser to do an unlimited amount of entity expansions. ### How to determine if the installation is affected You are using: - Web Modeler Self-Managed ≤ 8.8.8, ≤ 8.7.16, or ≤ 8.6.25 ### Solution Camunda has provided the following releases that contain the fix: - Web Modeler Self-Managed 8.8.9, 8.7.17, 8.6.26 This issue does not affect Web Modeler SaaS. ## Notice 40 ### Publication date Feb 23, 2026 ### Products affected C7 to C8 Migration Tooling ### Impact The version of Tomcat used by the Diagram Converter Webapp was affected by: - [CVE-2025-66614](https://nvd.nist.gov/vuln/detail/CVE-2025-66614) - [CVE-2026-24734](https://nvd.nist.gov/vuln/detail/CVE-2026-24734) ### How to determine if the installation is affected You are using: - C7 to C8 Migration Tooling 0.2.0 **AND** - the Diagram Converter Webapp ### Solution Camunda has released the **C7 to C8 Migration Tooling 0.2.1**, which includes the fix. ## Notice 39 ### Publication date Feb 10, 2026 ### Products affected - Camunda Web Modeler ### Impact The version of `fast-xml-parser` used by Camunda Web Modeler was affected by [CVE-2026-25128](https://nvd.nist.gov/vuln/detail/CVE-2026-25128), a RangeError vulnerability that could crash any application that processes untrusted XML input. ### How to determine if the installation is affected You are using: - Web Modeler Self-Managed ≤ 8.8.6, ≤ 8.7.15, or ≤ 8.6.24 ### Solution Camunda has provided the following releases that contain the fix: - Web Modeler Self-Managed 8.8.7, 8.7.16, 8.6.25 The fix was deployed to Web Modeler SaaS on February 2, 2026, 15:15 CET. ## Notice 38 ### Publication date Jan 8, 2026 ### Products affected - Camunda Web Modeler ### Impact The version of `qs` used by Camunda Web Modeler was affected by CVE-2025-15284, an improper input validation vulnerability that allows HTTP DoS. ### How to determine if the installation is affected You are using: - Web Modeler Self-Managed ≤ 8.8.4, ≤ 8.7.14, or ≤ 8.6.23 ### Solution Camunda has provided the following releases that contain the fix: - Web Modeler Self-Managed 8.8.5, 8.7.15, 8.6.24 The fix was deployed to Web Modeler SaaS on January 7, 2026, 13:45 CET. ## Notice 37 ### Publication date December 12, 2025 ### Products affected - Camunda Tasklist - Camunda Zeebe - Camunda Operate ### Impact The application is vulnerable to [CVE-2025-12183](https://nvd.nist.gov/vuln/detail/CVE-2025-12183), which allows remote attackers to cause denial of service and read adjacent memory via untrusted compressed input. ### How to determine if the installation is affected You are using: - Tasklist/Zeebe/Operate ≤ 8.8.6, ≤ 8.7.20, or ≤ 8.6.32 ### Solution Camunda has provided the following releases which contain the fix: - Tasklist/Zeebe/Operate 8.8.7, 8.7.21, 8.6.33 ## Notice 36 ### Publication date December 03, 2025 ### Products affected - Management Identity ### Impact The application is vulnerable to [CVE-2025-53066](https://nvd.nist.gov/vuln/detail/CVE-2025-53066), which allows an unauthenticated attacker with network access via multiple protocols to compromise Oracle Java SE, Oracle GraalVM for JDK, Oracle GraalVM Enterprise Edition. Successful attacks of this vulnerability can result in unauthorized access to critical data or complete access to all Oracle Java SE, Oracle GraalVM for JDK, Oracle GraalVM Enterprise Edition accessible data. ### How to determine if the installation is affected You are using: - Management Identity ≤ 8.8.2, ≤ 8.7.10, or ≤ 8.6.22 ### Solution Camunda has provided the following releases which contain the fix: - Management Identity 8.8.3, 8.7.11, 8.6.23 ## Notice 35 ### Publication date November 26, 2025 ### Products affected - Camunda Web Modeler Self-Managed - Camunda Management Identity ### Impact The embedded JDBC driver for Amazon Aurora PostgreSQL (`software.amazon.jdbc:aws-advanced-jdbc-wrapper`) was affected by [CVE-2025-12967](https://nvd.nist.gov/vuln/detail/CVE-2025-12967), which may allow for privilege escalation to the `rds_superuser` role. A low privilege authenticated user can create a crafted function that could be executed with permissions of other Amazon Relational Database Service (RDS) users. ### How to determine if the installation is affected You are using: - Web Modeler Self-Managed ≤ 8.8.2, ≤ 8.7.12, or ≤ 8.6.21 with Amazon Aurora PostgreSQL - Management Identity ≤ 8.8.1, ≤ 8.7.9, or ≤ 8.6.21 with Amazon Aurora PostgreSQL ### Solution Camunda has provided the following releases which contain the fix: - Web Modeler Self-Managed 8.8.3, 8.7.13, 8.6.22 - Management Identity 8.8.2, 8.7.10, 8.6.22 ## Notice 34 ### Publication date November 11, 2025 ### Products affected - Camunda Web Modeler Self-Managed ### Impact The version of the MSSQL JDBC driver `com.microsoft.sqlserver:mssql-jdbc` used by Web Modeler was affected by [CVE-2025-59250](https://nvd.nist.gov/vuln/detail/CVE-2025-59250), which allows improper input validation that could enable an attacker to perform spoofing over a network. ### How to determine if the installation is affected You are using Web Modeler Self-Managed version <= 8.8.1 and Microsoft SQL Server as database vendor. ### Solution Camunda has provided the following release which contains the fix: - Web Modeler Self-Managed 8.8.2 ## Notice 33 ### Publication date October 22, 2025 ### Products affected - Camunda Orchestration Cluster ### Impact A bug in signal broadcast command processing allowed unauthorized users to trigger signal start events or signal intermediate catch events in certain process definitions without the required create or update permissions. This did not allow users to access process definitions of other tenants, or leak any information about these process instances back to the unauthorized users. ### How to determine if the installation is affected You are using: - Orchestration Cluster 8.8.0 ### Solution Camunda has provided the following release which contains the fix: - Orchestration Cluster 8.8.1 ## Notice 32 ### Publication date October 21, 2025 ### Products affected - Camunda Management Identity ### Impact The embedded Apache Tomcat was affected by [CVE-2025-48989](https://nvd.nist.gov/vuln/detail/CVE-2025-48989) which made Tomcat vulnerable to the MadeYouReset attack. ### How to determine if the installation is affected You are using: - Management Identity 8.7.0 - 8.7.4 or 8.7.6 - 8.7.7 ### Solution Camunda has provided the following release which contains the fix: - Management Identity 8.7.8 ## Notice 31 ### Publication date October 16, 2025 ### Products affected - Camunda Web Modeler ### Impact The embedded Undertow web server was affected by [CVE-2025-9784](https://nvd.nist.gov/vuln/detail/CVE-2025-9784), a flaw where malformed client requests can trigger server-side stream resets without incrementing abuse counters. This issue, referred to as the "MadeYouReset" attack, allows malicious clients to induce excessive server workload by repeatedly causing server-side stream aborts and could be exploited to cause a denial of service (DoS). ### How to determine if the installation is affected You are using: - Web Modeler Self-Managed 8.8.0, ≤ 8.7.10, or ≤ 8.6.19 ### Solution Camunda has provided the following releases which contain the fix: - Web Modeler Self-Managed 8.8.1, 8.7.11, 8.6.20 The fix was deployed to Web Modeler SaaS on October 14, 2025, 14:26 CET. ## Notice 30 ### Publication date October 7th, 2025 ### Products affected - Camunda Tasklist - Camunda Zeebe - Camunda Operate - Camunda Optimize - Camunda Management Identity ### Impact The embedded Netty was affected by [CVE-2025-58056](https://nvd.nist.gov/vuln/detail/CVE-2025-58056), an HTTP request smuggling vulnerability in Netty. Incorrect parsing of chunked transfer encoding could allow attackers to craft malicious requests that are interpreted inconsistently by proxies and Netty. ### How to determine if the installation is affected You are using: - Tasklist 8.7.0 - 8.7.12 or 8.5.0 - 8.5.22 - Zeebe 8.7.0 - 8.7.12 or 8.5.0 - 8.5.24 - Operate 8.7.0 - 8.7.12 or 8.5.0 - 8.5.20 - Optimize 8.7.0 - 8.7.9 or 8.6.0 - 8.6.16 - Management Identity 8.7.0 - 8.7.6 or 8.6.0 - 8.6.19 or 8.5.0 - 8.5.21 ### Solution Camunda has provided the following releases which contain the fix: - Tasklist 8.7.13, 8.5.23 - Zeebe 8.7.13, 8.5.25 - Operate 8.7.13, 8.5.21 - Optimize 8.7.10, 8.6.17 - Management Identity 8.7.7, 8.6.20, 8.5.22 ## Notice 29 ### Publication date October 3, 2025 ### Products affected - Camunda Zeebe ### Impact Zeebe may be affected by [CVE-2024-41996](https://nvd.nist.gov/vuln/detail/CVE-2024-41996), which allows remote attackers to trigger expensive server-side DHE modular-exponentiation calculations, potentially causing asymmetric resource consumption and DoS attacks. ### How to determine if the installation is affected You are potentially affected if you have configured Zeebe to accept DHE or ECDHE cipher suites through the `server.ssl.ciphers` property or `SERVER_SSL_CIPHERS` environment variable. Default Zeebe installations are not affected. ### Solution Configure the `server.ssl.ciphers` property or `SERVER_SSL_CIPHERS` environment variable to exclude DHE and ECDHE cipher suites. For example: ``` server.ssl.ciphers=TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA ``` There is no known mitigation other than disabling the use of DHE and ECDHE cipher suites. ## Notice 28 ### Publication date September 9, 2025 ### Products affected - Camunda Optimize ### Impact Optimize was affected by [CVE-2025-5115](https://nvd.nist.gov/vuln/detail/CVE-2025-5115), which allows a remote attacker to repeatedly send malformed HTTP/2 frames that exhaust a Jetty server’s CPU and memory, causing a denial-of-service. ### How to determine if the installation is affected You are using: - Optimize 8.7.0 - 8.7.8 or 8.6.0 - 8.6.15 ### Solution Camunda has provided the following releases which contain the fix: - Optimize 8.7.9, 8.6.16 ## Notice 27 ### Publication date August 27, 2025 ### Products affected - Camunda Optimize ### Impact Optimize's email functionality was affected by [CVE-2025-7962](https://nvd.nist.gov/vuln/detail/CVE-2025-7962), which allowed for SMTP injection by providing forged email recipient addresses that could lead to malicious content being sent to arbitrary recipients. ### How to determine if the installation is affected You are using: - Optimize 8.7.0 - 8.7.7 or 8.6.0 - 8.6.14 ### Solution Camunda has provided the following releases which contain the fix: - Optimize 8.7.8, 8.6.15 ## Notice 26 ### Publication date August 27, 2025 ### Products affected - Camunda Optimize ### Impact Optimize was affected by [CVE-2025-53864](https://nvd.nist.gov/vuln/detail/CVE-2025-53864) which allows a remote attacker to cause a denial of service via a deeply nested JSON object supplied in a JWT claim set, because of uncontrolled recursion. ### How to determine if the installation is affected You are using: - Optimize 8.7.0 - 8.7.7 or 8.6.0 - 8.6.14 ### Solution Camunda has provided the following releases which contain the fix: - Optimize 8.7.8, 8.6.15 ## Notice 25 ### Publication date August 27, 2025 ### Products affected - Camunda Tasklist - Camunda Zeebe - Camunda Operate - Camunda Optimize ### Impact The embedded Apache Tomcat was affected by [CVE-2025-48989](https://nvd.nist.gov/vuln/detail/CVE-2025-48989) which made Tomcat vulnerable to the MadeYouReset attack. ### How to determine if the installation is affected You are using: - Tasklist 8.7.0 - 8.7.10 or 8.6.0 - 8.6.24 or 8.5.0 - 8.5.20 - Zeebe 8.7.0 - 8.7.10 or 8.6.0 - 8.6.24 - Operate 8.7.0 - 8.7.10 or 8.6.0 - 8.6.24 or 8.5.0 - 8.5.18 - Optimize 8.7.0 - 8.7.7 or 8.6.0 - 8.6.14 ### Solution Camunda has provided the following releases which contain the fix: - Tasklist 8.7.11, 8.6.25, 8.5.21 - Zeebe 8.7.11, 8.6.25 - Operate 8.7.11, 8.6.25, 8.5.19 - Optimize 8.7.8, 8.6.15 ## Notice 24 ### Publication date August 27, 2025 ### Products affected - Camunda Tasklist - Camunda Zeebe - Camunda Operate - Camunda Management Identity - Camunda Optimize ### Impact The embedded Netty was affected by [CVE-2025-55163](https://nvd.nist.gov/vuln/detail/CVE-2025-55163) which allows malformed HTTP/2 control frames usage that results in resource exhaustion and distributed denial of service. ### How to determine if the installation is affected You are using: - Tasklist 8.7.0 - 8.7.10 or 8.6.0 - 8.6.24 or 8.5.0 - 8.5.20 - Zeebe 8.7.0 - 8.7.10 or 8.6.0 - 8.6.24 or 8.5.0 - 8.5.22 - Operate 8.7.0 - 8.7.10 or 8.6.0 - 8.6.24 or 8.5.0 - 8.5.18 - Management Identity 8.7.0 - 8.7.5 or 8.6.0 - 8.6.18 or 8.5.0 - 8.5.19 - Optimize 8.7.0 - 8.7.7 or 8.6.0 - 8.6.14 ### Solution Camunda has provided the following releases which contain the fix: - Tasklist 8.7.11, 8.6.25, 8.5.21 - Zeebe 8.7.11, 8.6.25, 8.5.23 - Operate 8.7.11, 8.6.25, 8.5.19 - Management Identity 8.7.6, 8.6.19, 8.5.20 - Optimize 8.7.8, 8.6.15 ## Notice 23 ### Publication date July 31, 2025 ### Products affected - Camunda Tasklist - Camunda Zeebe - Camunda Operate - Camunda Management Identity - Camunda Optimize ### Impact The embedded Spring Boot Tomcat was affected by [CVE-2025-53506](https://nvd.nist.gov/vuln/detail/CVE-2025-53506) which allowed for uncontrolled resource consumption that could be used to exhaust system resources in a potential DoS (denial of service) attack. ### How to determine if the installation is affected You are using: - Tasklist 8.7.0 - 8.7.8 or 8.6.0 - 8.6.22 or 8.5.0 - 8.5.18 - Zeebe 8.7.0 - 8.7.8 or 8.6.0 - 8.6.22 - Operate 8.7.0 - 8.7.8 or 8.6.0 - 8.6.22 or 8.5.0 - 8.5.16 - Management Identity 8.7.0 - 8.7.4 or 8.6.0 - 8.6.17 or 8.5.0 - 8.5.18 - Optimize 8.7.0 - 8.7.6 or 8.6.0 - 8.6.12 ### Solution Camunda has provided the following releases which contain the fix: - Tasklist 8.7.9, 8.6.23, 8.5.19 - Zeebe 8.7.9, 8.6.23 - Operate 8.7.9, 8.6.23, 8.5.17 - Management Identity 8.7.5, 8.6.18, 8.5.19 - Optimize 8.7.7, 8.6.13 ## Notice 22 ### Publication date July 31, 2025 ### Products affected - Camunda Tasklist - Camunda Zeebe - Camunda Operate ### Impact Part of our RESTful API that supported multipart file uploads was affected by [CVE-2025-52520](https://nvd.nist.gov/vuln/detail/CVE-2025-52520), which could lead to potential DoS (denial of service) attacks. ### How to determine if the installation is affected You are using: - Tasklist 8.6.0 - 8.6.22 or 8.7.0 - 8.7.8 - Zeebe 8.6.0 - 8.6.22 or 8.7.0 - 8.7.8 - Operate 8.6.0 - 8.6.22 or 8.7.0 - 8.7.8 ### Solution Camunda has provided the following releases which contain the fix: - Tasklist 8.6.22 - Tasklist 8.7.9 - Zeebe 8.6.23 - Zeebe 8.7.9 - Operate 8.6.23 - Operate 8.7.9 ## Notice 21 ### Publication date June 18, 2025 ### Products affected Camunda Web Modeler Self-Managed ### Impact The version of `org.postgresql:postgresql` used by Camunda Web Modeler Self-Managed was affected by [CVE-2025-49146](https://nvd.nist.gov/vuln/detail/CVE-2025-49146) potentially allowing a man-in-the-middle attacker to intercept connections when the PostgreSQL JDBC driver was configured with channel binding set to required. ### How to determine if the installation is affected You are using Camunda Web Modeler Self-Managed version 8.6.0 - 8.6.12, or 8.7.0 - 8.7.3. ### Solution Camunda has provided the following releases which contain the fix: - Camunda Web Modeler Self-Managed 8.6.12 - Camunda Web Modeler Self-Managed 8.7.3 ## Notice 20 ### Publication date June 17, 2025 ### Products affected Camunda Optimize ### Impact Camunda Optimize was affected by a vulnerability that allowed an attacker to gain improper access to Optimize data by using a modified JWT (JSON Web Token). ### How to determine if the installation is affected You are using Camunda Optimize ≤ 8.6.9 or ≤ 8.7.2. ### Solution Camunda has provided the following release which contains a fix: - [Camunda Optimize 8.6.10](https://github.com/camunda/camunda/releases/tag/8.6.10-optimize) - [Camunda Optimize 8.7.3](https://github.com/camunda/camunda/releases/tag/8.7.3-optimize) ## Notice 19 ### Publication date May 21, 2025 ### Products affected Camunda Web Modeler ### Impact The version of `nodejs` used by Camunda Web Modeler was affected by [CVE-2025-23166](https://nvd.nist.gov/vuln/detail/CVE-2025-23166) potentially allowing an adversary to remotely crash the Node.js runtime. ### How to determine if the installation is affected You are using Camunda Web Modeler Self-Managed version ≤ 8.4.17, ≤ 8.5.18, ≤ 8.6.10, or ≤ 8.7.1. ### Solution Camunda has provided the following releases which contain the fix: - Camunda Web Modeler Self-Managed 8.4.18 - Camunda Web Modeler Self-Managed 8.5.19 - Camunda Web Modeler Self-Managed 8.6.11 - Camunda Web Modeler Self-Managed 8.7.2 The fix was deployed to Web Modeler SaaS on May 19, 2025, 15:10 CET. ## Notice 18 ### Publication date April 8, 2025 ### Products affected Camunda Optimize ### Impact Camunda Optimize was affected by a vulnerability that allowed an attacker to modify a JWT (JSON Web Token) so that they would be given improper access to Optimize. ### How to determine if the installation is affected You are using Camunda Optimize ≤ 8.4.15, ≤ 8.5.12, ≤ 8.6.6, ≤ 8.7.0, ≤ 3.11.20, ≤ 3.12.15, ≤ 3.13.12, ≤ 3.14.3, ≤ 3.15.1. ### Solution Camunda has provided the following release which contains a fix: - Camunda Optimize 8.4.16, 8.5.13, 8.6.7, 8.7.0, 3.12.16, 3.13.13, 3.14.4, 3.15.2 ## Notice 17 ### Publication date April 8, 2025 ### Products affected Camunda Zeebe ### Impact When parsing unknown fields in the Protobuf Java Lite and Full library, a maliciously crafted message can cause a StackOverflow error and lead to a program crash. - As Zeebe makes extensive use of Protobuf, this could lead to denial-of-service (DoS) issues on the server side. - This issue allows an attacker to send specific payloads that will always result in `StackOverflowException`. This could lead to Zeebe Gateway performance issues and affect system availability. - Although the Zeebe Gateway will not crash, it will spend more time working on these requests. An attacker could use this opportunity to slow it down and make it unusable by sending a large number of requests within a short time frame. No data is leaked, lost, or corrupted. This issue only affects application availability. [Learn more about this CVE at the GitHub Advisory Database](https://github.com/advisories/GHSA-735f-pc8j-v9w8). ### How to determine if the installation is affected You are using Camunda Zeebe 8.6.11. ### Solution Camunda has provided the following release which contains a fix: - [Camunda Zeebe 8.6.13](https://github.com/camunda/camunda/releases/tag/8.6.13) ## Notice 16 ### Publication date March 14, 2025 ### Products affected Camunda Zeebe ### Impact Some Camunda Zeebe versions were affected by a vulnerability that allowed a malicious attacker to craft network packets that could crash the Zeebe Gateway. ### How to determine if the installation is affected You are using Camunda Zeebe 8.6.0 - 8.6.11 ### Solution Camunda has provided the following release which contains a fix: - [Camunda Zeebe 8.6.12](https://github.com/camunda/camunda/releases/tag/8.6.12) ## Notice 15 ### Publication date March 11, 2025 ### Products affected Camunda Optimize ### Impact Some Camunda Optimize versions were affected by a vulnerability that allowed a malicious attacker to craft Camunda URLs that could execute JavaScript code. ### How to determine if the installation is affected You are using Camunda Optimize ≤ 8.6.5. ### Solution Camunda has provided the following release which contains a fix: - [Camunda Optimize 8.6.6](https://github.com/camunda/camunda/releases/tag/8.6.6-optimize) ## Notice 14 ### Publication date March 11, 2025 ### Products affected Camunda Web Modeler ### Impact The version of `koa` used by Camunda Web Modeler was affected by the following vulnerability: - https://nvd.nist.gov/vuln/detail/CVE-2025-25200 ### How to determine if the installation is affected You are using Camunda Web Modeler Self-Managed version ≤ 8.3.16, ≤ 8.4.14, ≤ 8.5.15, or ≤ 8.6.7. ### Solution Camunda has provided the following releases which contain the fix: - Camunda Web Modeler Self-Managed 8.3.17 - Camunda Web Modeler Self-Managed 8.4.15 - Camunda Web Modeler Self-Managed 8.5.16 - Camunda Web Modeler Self-Managed 8.6.8 The fix was deployed to Web Modeler SaaS on February 14, 2025, 08:50 CET. ## Notice 13 ### Publication date July 18, 2024 ### Products affected Camunda Management Identity ### Impact The version of `Apache Tomcat` used by Camunda Management Identity was affected by the following vulnerability: - https://nvd.nist.gov/vuln/detail/CVE-2024-34750 ### How to determine if the installation is affected You are using Camunda Management Identity version 8.5.3 or previous. ### Solution Camunda has provided the following release which contains a fix: - Camunda Management Identity 8.5.4 ## Notice 12 ### Publication date October 3, 2023 ### Products affected Camunda Desktop Modeler ### Impact The version of `libwebp` shipped with Camunda Desktop Modeler was affected by the following vulnerability: - https://nvd.nist.gov/vuln/detail/CVE-2023-4863 ### How to determine if the installation is affected You are using Camunda Desktop Modeler version 5.15.1 or previous. ### Solution Camunda has provided the following release which contains a fix: - [Camunda Desktop Modeler 5.15.2](https://downloads.camunda.cloud/release/camunda-modeler/5.15.2/) ## Notice 11 ### Publication date April 17, 2023 ### Products affected Tasklist ### Impact The Tasklist REST API functionality of Tasklist 8.2.0 and 8.2.1 allows unauthenticated access to the following methods/URLs: - GET /v1/tasks/\{taskId} - POST /v1/tasks/search - POST /v1/tasks/\{taskId}/variables/search - POST /v1/forms/\{formId} - POST /v1/variables/\{variableId} Find more information about the methods in our [Tasklist REST API documentation](/versioned_docs/version-8.9/apis-tools/tasklist-api-rest/tasklist-api-rest-overview.md). Therefore, if you use Tasklist 8.2.0 or 8.2.1, and if you have sensible data stored in process variables (accessed by user tasks), this data could have been accessed by users knowing the endpoint of the Tasklist instance without authentication. ### How to determine if the installation is affected You are using Tasklist version 8.2.0 or 8.2.1. ### Solution Camunda has provided the following releases which contain a fix - [Tasklist 8.2.2](https://github.com/camunda/camunda-platform/releases/tag/8.2.2) ## Notice 10 ### Publication Date: November 10, 2022 ### Products affected: Tasklist ### Impact: The Tasklist Docker image contain an OpenSSL version 3.0.2 for which the following CVEs have been published: - https://nvd.nist.gov/vuln/detail/CVE-2022-3602 - https://nvd.nist.gov/vuln/detail/CVE-2022-3786 At this point, Camunda is not aware of any specific attack vector in Tasklist allowing attackers to exploit the vulnerability but recommends applying fixes as mentioned in the Solution section below. ### How to determine if the installation is affected You are Tasklist version (8.0.3 ≥ version ≤ 8.0.7) or ≤ 8.1.2 ### Solution Camunda has provided the following releases which contain a fix - [Tasklist 8.1.3](https://github.com/camunda/camunda-platform/releases/tag/8.1.3) - [Tasklist 8.0.8](https://github.com/camunda/camunda-platform/releases/tag/8.0.8) ## Notice 9 ### Publication Date: April 11, 2022 ### Products affected: Zeebe, Operate, Tasklist, IAM ### Impact: Zeebe, Operate, Tasklist and IAM are using the Spring framework for which the following CVE has been published: https://nvd.nist.gov/vuln/detail/CVE-2022-22965 At this point, Camunda is not aware of any specific attack vector in Zeebe, Operate, Tasklist or IAM allowing attackers to exploit the vulnerability but recommends applying fixes as mentioned in the Solution section below. ### How to determine if the installation is affected You are using Zeebe, Operate or Tasklist version ≤ 1.2.11 or ≤ 1.3.6 ### Solution Camunda has provided the following releases which contain a fix - [Zeebe, Operate and Tasklist 1.3.7](https://github.com/camunda-cloud/zeebe/releases/tag/1.3.7) - [Zeebe, Operate and Tasklist 1.2.12](https://github.com/camunda-cloud/zeebe/releases/tag/1.2.12) ## Notice 8 ### Publication Date: December 31, 2021 ### Products affected: Zeebe, Operate, Tasklist ### Impact: Zeebe, Operate and Tasklist bundle log4j-core for which the following CVE has been published: https://nvd.nist.gov/vuln/detail/CVE-2021-44832. At this point, Camunda is not aware of any specific attack vector in Zeebe, Operate or Tasklist allowing attackers to exploit the vulnerability but recommends applying fixes as mentioned in the Solution section below. ### How to determine if the installation is affected You are using Zeebe, Operate or Tasklist version ≤ 1.2.8 or ≤ 1.1.9 ### Solution Camunda has provided the following releases which contain a fix - [Zeebe, Operate and Tasklist 1.2.9](https://github.com/camunda-cloud/zeebe/releases/tag/1.2.9) - [Zeebe, Operate and Tasklist 1.1.10](https://github.com/camunda-cloud/zeebe/releases/tag/1.1.10) ## Notice 7 ### Publication Date: December 31, 2021 ### Products affected: IAM ### Impact: IAM bundles log4j libraries for which the following CVE has been published: https://nvd.nist.gov/vuln/detail/CVE-2021-44832. Specifically, IAM bundles log4j-api and log4j-to-slf4j. However, IAM does not bundle the log4j-core library which contains the vulnerability referred to by the CVE. As a result, Camunda does not consider IAM to be affected by the vulnerability. Still, Camunda recommends applying fixes as mentioned in the Solution section below. ### How to determine if the installation is affected You are using IAM version ≤ 1.2.8 ### Solution Camunda has provided the following releases which contain a fix - [IAM 1.2.9](https://github.com/camunda-cloud/zeebe/releases/tag/1.2.9) ## Notice 6 ### Publication Date: December 22, 2021 ### Products affected: Zeebe, Operate, Tasklist ### Impact: Zeebe, Operate and Tasklist bundle log4j-core for which the following CVE has been published: https://nvd.nist.gov/vuln/detail/CVE-2021-45105. At this point, Camunda is not aware of any specific attack vector in Zeebe, Operate or Tasklist allowing attackers to exploit the vulnerability but recommends applying fixes as mentioned in the Solution section below. ### How to determine if the installation is affected You are using Zeebe, Operate or Tasklist version ≤ 1.2.7 or ≤ 1.1.8 ### Solution Camunda has provided the following releases which contain a fix - [Zeebe, Operate and Tasklist 1.2.8](https://github.com/camunda-cloud/zeebe/releases/tag/1.2.8) - [Zeebe, Operate and Tasklist 1.1.9](https://github.com/camunda-cloud/zeebe/releases/tag/1.1.9) ## Notice 5 ### Publication Date: December 22, 2021 ### Products affected: IAM ### Impact: IAM bundles log4j libraries for which the following CVE has been published: https://nvd.nist.gov/vuln/detail/CVE-2021-45105. Specifically, IAM bundles log4j-api and log4j-to-slf4j. However, IAM does not bundle the log4j-core library which contains the vulnerability referred to by the CVE. As a result, Camunda does not consider IAM to be affected by the vulnerability. Still, Camunda recommends applying fixes as mentioned in the Solution section below. IAM bundles logback libraries for which the following CVE has been published: https://nvd.nist.gov/vuln/detail/CVE-2021-42550. At this point, Camunda is not aware of any specific attack vector in IAM allowing attackers to exploit the vulnerability but recommends applying fixes as mentioned in the Solution section below. ### How to determine if the installation is affected You are using IAM version ≤ 1.2.7 ### Solution Camunda has provided the following releases which contain a fix - [IAM 1.2.8](https://github.com/camunda-cloud/zeebe/releases/tag/1.2.8) ## Notice 4 ### Publication Date: December 17, 2021 ### Products affected: Zeebe, Operate, Tasklist ### Impact: Zeebe, Operate and Tasklist bundle log4j-core for which the following CVE has been published: https://nvd.nist.gov/vuln/detail/CVE-2021-45046. At this point, Camunda is not aware of any specific attack vector in Zeebe, Operate or Tasklist allowing attackers to exploit the vulnerability but recommends applying fixes as mentioned in the Solution section below. ### How to determine if the installation is affected You are using Zeebe, Operate or Tasklist version ≤ 1.2.6 or ≤ 1.1.7 ### Solution Camunda has provided the following releases which contain a fix - [Zeebe, Operate and Tasklist 1.2.7](https://github.com/camunda-cloud/zeebe/releases/tag/1.2.7) - [Zeebe, Operate and Tasklist 1.1.8](https://github.com/camunda-cloud/zeebe/releases/tag/1.1.8) ## Notice 3 ### Publication Date: December 17, 2021 ### Products affected: IAM ### Impact: IAM bundles log4j libraries for which the following CVE has been published: https://nvd.nist.gov/vuln/detail/CVE-2021-45046. Specifically, IAM bundles log4j-api and log4j-to-slf4j. However, IAM does not bundle the log4j-core library which contains the vulnerability referred to by the CVE. As a result, Camunda does not consider IAM to be affected by the vulnerability. Still, Camunda recommends applying fixes as mentioned in the Solution section below. IAM bundles logback libraries for which the following CVE has been published: https://nvd.nist.gov/vuln/detail/CVE-2021-42550. At this point, Camunda is not aware of any specific attack vector in IAM allowing attackers to exploit the vulnerability but recommends applying fixes as mentioned in the Solution section below. ### How to determine if the installation is affected You are using IAM version ≤ 1.2.6 ### Solution Camunda has provided the following releases which contain a fix - [IAM 1.2.7](https://github.com/camunda-cloud/zeebe/releases/tag/1.2.7) ## Notice 2 ### Publication Date: December 14, 2021 ### Products affected: Zeebe, Operate, Tasklist ### Impact: Zeebe, Operate and Tasklist bundle log4j-core for which the following CVE has been published: https://nvd.nist.gov/vuln/detail/CVE-2021-44228. At this point, Camunda is not aware of any specific attack vector in Zeebe, Operate or Tasklist allowing attackers to exploit the vulnerability but recommends applying fixes as mentioned in the Solution section below. ### How to determine if the installation is affected You are using Zeebe, Operate or Tasklist version ≤ 1.2.5 or ≤ 1.1.6 ### Solution Camunda has provided the following releases which contain a fix - [Zeebe, Operate and Tasklist 1.2.6](https://github.com/camunda-cloud/zeebe/releases/tag/1.2.6) - [Zeebe, Operate and Tasklist 1.1.7](https://github.com/camunda-cloud/zeebe/releases/tag/1.1.7) Apply the patches mentioned above or set the JVM option `-Dlog4j2.formatMsgNoLookups=true` ## Notice 1 ### Publication Date: December 14, 2021 ### Products affected: IAM ### Impact: IAM bundles log4j libraries for which the following CVE has been published: https://nvd.nist.gov/vuln/detail/CVE-2021-44228. Specifically, IAM bundles log4j-api and log4j-to-slf4j. However, IAM does not bundle the log4j-core library which contains the vulnerability referred to by the CVE. As a result, Camunda does not consider IAM to be affected by the vulnerability. Still, Camunda recommends applying fixes as mentioned in the Solution section below. ### How to determine if the installation is affected You are using IAM version ≤ 1.2.5 ### Solution Camunda has provided the following releases which contain a fix - [IAM 1.2.6](https://github.com/camunda-cloud/zeebe/releases/tag/1.2.6) --- ## Reference Camunda 8 reference, including release, security, and support information. See what's new in our latest release, read release notes and release announcements, and access published security notices, licensing, supported environments, and source code access information. Release announcements and release notes ## Release announcements and release notes Keep up-to-date with upcoming, current, and past Camunda releases. :::info Release policy Learn about the [Camunda release policy](/reference/announcements-release-notes/release-policy.md) and specific clarifications about provisioning in SaaS and Self-Managed. ::: ## Security, licensing, and support information Reference information including published security notices, licensing, supported environments, and source code access. ## Support and feedback [Get support](contact.md) for Camunda or send us your feedback. --- ## Camunda 8 public API ## What is the public API? Camunda 8 follows [Semantic Versioning (SemVer)](https://semver.org/) to provide users with a stable and reliable platform. A key requirement of SemVer is a clearly defined public API. The public API is the official contract between Camunda and its users under SemVer. No breaking changes will be made to the public API in minor or patch releases. You can safely build on these interfaces with the expectation of stability and backward compatibility. - This is a subset of all available APIs. Many [APIs](/apis-tools/working-with-apis-tools.md) are public-facing but not covered by the SemVer stability contract. - Only components explicitly listed on this page (see [Included in the public API](#included-in-the-public-api)) are covered. Anything not listed is _not_ guaranteed under SemVer. - The public API contract begins with version 8.8. :::note The term "public API" refers to the SemVer definition of stable interfaces, not external APIs available to users. ::: ### Included in the public API The following components are officially part of the Camunda 8 public API: - [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md): The primary interface for interacting with the Orchestration Cluster. - [Camunda Process Test](/apis-tools/testing/getting-started.md): Testing library to test BPMN processes and process applications. - [Camunda Java client](/apis-tools/java-client/getting-started.md): Java Client to interact with Orchestration Cluster REST API and Zeebe gRPC. - [Camunda Spring Boot Starter](/apis-tools/camunda-spring-boot-starter/getting-started.md): Spring Boot Client to interact with Orchestration Cluster REST API and Zeebe gRPC. ## What is not included in the public API Some APIs are excluded from the public API by design. While we aim for stability, they may evolve more quickly or follow different lifecycles. Only the APIs listed in [Included in the public API](#included-in-the-public-api) are covered by SemVer guarantees. All others are considered outside the public API. The [excluded APIs listed below](#excluded-apis) are commonly used, but this list is not exhaustive. ### Alpha endpoints within the public API Some endpoints in otherwise stable APIs are marked as [alpha features](/components/early-access/alpha/alpha-features.md) and are **not** included in the public API guarantee. Alpha endpoints: - Are clearly marked in API docs. - May introduce breaking changes in minor or patch releases. - Follow the [alpha feature policy](/components/early-access/alpha/alpha-features.md#alpha) rather than SemVer. - Are released for early feedback before general availability. Check the API documentation before building on any alpha endpoint. ### Excluded APIs The following APIs are **explicitly excluded** from the public API: - [Web Modeler API](/apis-tools/web-modeler-api/index.md): Used for browser-based modeling. - [Administration API](/apis-tools/administration-api/administration-api-reference.md): For administrative operations and system configuration. - [Optimize API](/apis-tools/optimize-api/overview.md): Used for analytics, reporting, and performance insights. - [Orchestration Cluster MCP Server](/apis-tools/orchestration-cluster-api-mcp/orchestration-cluster-api-mcp-overview.md): Exposes Camunda capabilities through the Model Context Protocol. Tool schemas and behavior may evolve across versions. ### Policy for non-public APIs Though not covered by the public API contract, we aim to provide a consistent experience. For non-public APIs, we commit to: - Following API versioning best practices. - Announcing deprecations at least two minor versions in advance (for example, deprecated in 8.9, removed no earlier than 8.11). - Avoiding breaking changes to configuration, endpoints, or backup-related features within the same minor release range. This balance allows continuous improvement to tools like Web Modeler and Console, while preserving stability for orchestration logic. ## Client and API compatibility Public API components follow strict compatibility guarantees so you can upgrade clients and clusters independently. | Component | Forward-compatible | Backward-compatible | | ----------------------------------------------------------------------------------------------------------------------- | :----------------: | :-----------------: | | [Camunda Java client](/apis-tools/java-client/getting-started.md) | ✅ Yes | ✅ Yes | | [Spring SDK](/apis-tools/camunda-spring-boot-starter/getting-started.md) | ✅ Yes | ✅ Yes | | [Node.js SDK](https://github.com/camunda/camunda-nodejs-sdk) | ✅ Yes | ✅ Yes | | [Camunda Process Test](/apis-tools/testing/getting-started.md) | ✅ Yes | ✅ Yes | | [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) | — | ✅ Yes | - **Forward-compatible**: A client can work with a _newer_ version of the Orchestration Cluster than the client version. - **Backward-compatible**: A client can be upgraded to a _newer_ version without requiring changes to your application code. ### Clients and SDKs The Camunda Java client, Spring SDK, Node.js SDK, and Camunda Process Test (CPT) are both **forward-compatible** with new releases of the Orchestration Cluster while being **backward-compatible** regarding application integration. - **Forward compatibility**: Your application can use an older client version and still work correctly against a newer cluster. For example, an application using Camunda Java client **8.8.3** works against an Orchestration Cluster running **8.9.1**. This allows you to upgrade the Orchestration Cluster first without immediately updating your client libraries. - **Backward compatibility**: When you upgrade the client or SDK version in your application, no code changes are required. New minor and patch versions of the clients do not introduce breaking changes. For example, upgrading the Spring SDK dependency from **8.8.x** to **8.9.x** does not require changes to your application code. ### Camunda Process Test (CPT) CPT is both forward-compatible with the Orchestration Cluster and Connectors runtime and backward-compatible regarding application integration: - **Forward-compatible**: You can change the version of the Orchestration Cluster/Connectors bundle in your tests to a newer version without any changes. - **Backward-compatible**: Upgrading the CPT dependency version does not require changes to your existing tests. ### REST APIs The [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) is **backward-compatible**. No breaking changes are introduced to existing endpoints in newer versions of the Orchestration Cluster. For example, if you build a custom client implementation against the **8.8** REST APIs, it continues to work against a newer Orchestration Cluster version such as **8.9**. :::note As REST API endpoints are part of the Orchestration Cluster itself, forward compatibility does not apply. Your cluster version determines the available endpoints. ::: ### Recommended upgrade order When upgrading across minor versions, the recommended approach is: 1. **Upgrade the Orchestration Cluster first.** Forward compatibility of clients ensures your existing applications continue to work. 2. **Upgrade your client libraries.** Backward compatibility of clients ensures no code changes are needed. --- ## Supported environments ## About supported environments **If a particular technology is not listed below, Camunda cannot resolve issues caused by its usage.** You can: - [Raise a feature request](/reference/contact.md) that will be evaluated by our product teams to provide official support from Camunda. - [Make a help request](/reference/contact.md) to work with Camunda consulting services. Recommendations are denoted with [recommended]. Other listed options are also supported. :::note Minimum versions The versions listed on this page are the minimum version required if appended with a `+`. Pay attention to where the `+` falls, as most of our dependencies follow [semantic versioning](https://semver.org/) (semver), where `x.y.z` correspond to MAJOR.MINOR.PATCH. Higher or more recent versions will be compatible with Camunda, with respect to semver. For example, 1.2+ means support for the minor version 2, and any higher minors (1.3, 1.4, etc.) and patches (1.2.1, 1.2.2, etc.), but not majors, like 2.x. ::: ## Web browsers - Google Chrome latest [recommended] - Mozilla Firefox latest - Microsoft Edge latest ## Desktop Modeler - Windows 10 / 11 - macOS 13 / 14 / 15 / 26 - Ubuntu LTS (latest) ## Clients - **Zeebe Java Client**: OpenJDK 8+ - **Connector SDK**: OpenJDK 17+ - **Camunda Spring Boot Starter**: OpenJDK 17+, Spring Boot 4.0.x - **Helm CLI**: v4.x is required for Camunda 8.10 (chart 15.x) and later. See [Helm 4](/self-managed/deployment/helm/operational-tasks/helm-v4.md) for details, and the [version matrix](https://helm.camunda.io/camunda-platform/version-matrix/) for minimum patch versions. ## Camunda 8 Self-Managed We recommend running Camunda 8 Self-Managed in a Kubernetes environment. We provide officially supported [Helm charts](/self-managed/setup/overview.md) for this. See the [installation guide](/self-managed/setup/overview.md) to learn more about the available installation options. ### Deployment options With the correct configuration, Camunda 8 Self-Managed can be deployed on any [Certified Kubernetes](https://www.cncf.io/training/certification/software-conformance/#benefits) distribution (cloud or on-premises). However, we officially test and support a specific list of platforms. The following are tested and supported deployment options for Kubernetes, Docker, and manual installation: - [Stock Kubernetes](/self-managed/deployment/helm/install/quick-install.md) - [Cloud service providers](/self-managed/deployment/helm/install/quick-install.md) [recommended] - [Amazon EKS](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/amazon-eks.md) - [Microsoft AKS](/self-managed/deployment/helm/cloud-providers/azure/microsoft-aks/microsoft-aks.md) - [Google GKE](/self-managed/deployment/helm/cloud-providers/gcp/google-gke.md) - [Red Hat OpenShift](/self-managed/deployment/helm/cloud-providers/openshift/redhat-openshift.md) - [Docker](/self-managed/deployment/docker/docker.md) (`linux/amd64`, `linux/arm64`) - [Manual](/self-managed/deployment/manual/install.md) :::note Helm chart compatibility Ensure the Camunda component versions are compatible with the Helm chart version as defined in the [matrix](https://helm.camunda.io/camunda-platform/version-matrix/). ::: ### Sizing The [sizing of a Camunda 8 installation](/components/best-practices/architecture/sizing-your-environment.md) depends on various influencing factors. Ensure to [determine these factors](../components/best-practices/architecture/sizing-your-environment.md#understanding-influencing-factors), and conduct [benchmarking](../components/best-practices/architecture/sizing-your-environment.md#running-experiments-and-benchmarks) to validate an appropriate environment size for your test, integration, or production environments. ### Persistent volumes Camunda supports different types of storage volumes, including block storage and network file systems (NFS). For details on typical volume usage, refer to these examples: - [Amazon EKS](/self-managed/reference-architecture/kubernetes.md#amazon-eks-1) - [Microsoft AKS](/self-managed/reference-architecture/kubernetes.md#microsoft-aks) - [Google GKE](/self-managed/reference-architecture/kubernetes.md#google-gke) #### Network File Systems Camunda guarantees support for Amazon Elastic File System (EFS). If you want to use another NFS, it must meet these requirements: - Be POSIX-compliant. - Never reorder file operations. - Retry I/O operations across temporary network failures, instead of failing on timeout. - Do not surface network-related failures in the client process. - **Only one container may mount the disk in write mode at a time.** Two containers mounting the same disk in write mode could cause data corruption. #### Performance Regardless of the type, the network storage volumes you use must meet these requirements: - They must be capable of **at least 1,000 IOPS**. - The latency of write/msync operations must be in the **low single digit milliseconds** under normal conditions. Ideally, it's in the order of microseconds. - The p99 latency must be **lower than 300 milliseconds**. ### Helm charts version matrix Camunda Helm chart version `15.x.x` works with Camunda version `8.10.x` and requires the Helm v4 CLI. Check the [Helm chart version matrix](https://helm.camunda.io/camunda-platform/version-matrix/) for more details. ## Component requirements Requirements for components are as follows: | Component | Java version | Other requirements | | :--------------------------------------------------------- | :------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Orchestration Cluster (Zeebe, Operate, Tasklist, Identity) | OpenJDK 21–25 | Elasticsearch 9.2+Elasticsearch 8.19+OpenSearch 3.4+OpenSearch 2.19+For supported relational databases and versions when using an RDBMS (for example, as secondary storage), see the [RDBMS version support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) | | Optimize | OpenJDK 21–25 | Elasticsearch 9.2+Elasticsearch 8.19+OpenSearch 3.4+OpenSearch 2.19+ | | Connectors | OpenJDK 21–25 | – | | Management Identity | OpenJDK 17+ | Keycloak 26.xSupported relational databases and versions are defined in the [RDBMS version support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md)PostgreSQL is required for [certain features](/self-managed/components/management-identity/miscellaneous/configuration-variables.md#database-configuration) | | Web Modeler | – | Supported relational databases and versions are defined in the [RDBMS version support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) | | Self-Managed Console | – | – | :::info Optimize compatibility When running Optimize, make sure you use an [Elasticsearch exporter](/self-managed/components/orchestration-cluster/zeebe/exporters/elasticsearch-exporter.md) or [OpenSearch exporter](/self-managed/components/orchestration-cluster/zeebe/exporters/opensearch-exporter.md) version that is compatible with your Optimize version. ::: :::info RDBMS support For a complete list of supported RDBMS versions, JDBC driver information (bundled vs. user-supplied), and component compatibility when using relational databases as secondary storage, see the [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md). ::: ### OpenSearch and Elasticsearch support - Camunda 8 supports both [Amazon OpenSearch](https://aws.amazon.com/opensearch-service) and the open-source [OpenSearch](https://opensearch.org/) distribution. - Due to a [limitation](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/or1.html#or1-considerations) for the index refresh interval, [OR1 instances](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/or1.html) are not supported with Amazon OpenSearch. See [use Amazon OpenSearch Service with the Helm chart](/self-managed/deployment/helm/configure/database/using-external-opensearch.md). - When running Elasticsearch, you must have the [appropriate Elasticsearch privileges](/self-managed/concepts/databases/elasticsearch/elasticsearch-privileges.md). - Camunda 8 works with the Elasticsearch [default distribution](https://www.elastic.co/downloads/elasticsearch) available with the [Free or Gold+ Elastic license](https://www.elastic.co/pricing/faq/licensing#summary). ### Component version matrix The following matrix shows which component versions work together. Components within the same column must share the same `minor` and `patch` version. | [Orchestration Cluster](../self-managed/reference-architecture/reference-architecture.md#orchestration-cluster) | [Management](../self-managed/reference-architecture/reference-architecture.md#web-modeler-and-console) | Design | | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------- | | Orchestration Cluster 8.9.xConnectors 8.9.xOptimize 8.9.x | Management Identity 8.9.xSelf-Managed Console 8.9.x | Web Modeler 8.9.xDesktop Modeler 5.46+ | :::note You can use newer versions of Desktop and Web Modeler with older versions of the Orchestration Cluster. ::: --- ## Camunda 8 Self-Managed Get started with our self-hosted alternative to Camunda 8 SaaS. Deploy and manage Camunda in your own infrastructure. Quickstart guides and infrastructure and deployment options help you build a robust process automation platform for your organization. Developer and administrator quickstart :::info Upgrade to Camunda 8.9 - Existing customer? Upgrade your Self-Managed deployment to 8.9 using the [upgrade to 8.9 guide](/self-managed/upgrade/index.md). - See [what's new in Camunda 8.9](/reference/announcements-release-notes/890/whats-new-in-89.md), [release announcements](/reference/announcements-release-notes/890/890-announcements.md), and [release notes](/reference/announcements-release-notes/890/890-release-notes.md). ::: --- ## 880 To 890 --- ## Configuration(Connectors) You can configure the connector runtime environment in the following ways: - Specify the Zeebe instance to connect to. - Define the connector functions to run. - Provide the secrets that should be available to the connectors. :::note Starting from version 8.8, the connector runtime no longer requires a connection to Operate. It now depends only on the Orchestration Cluster REST API and Zeebe. ::: To connect to the **Orchestration Cluster**, the connector runtime uses the [Camunda Spring Boot Starter](/apis-tools/camunda-spring-boot-starter/getting-started.md). Any configuration available in the Spring Boot Starter can also be applied to the connector runtime environment. Below are some of the most common configuration options for the connector runtime. For a complete list, see the [Camunda Spring Boot Starter configuration reference](/apis-tools/camunda-spring-boot-starter/configuration.md#zeebe). :::note This guide presents configuration properties as environment variables, while the Camunda Spring Boot Starter documentation uses Java configuration properties. The two formats are interchangeable. You can also use Java configuration properties in the connector runtime environment. For example, the Java configuration property `camunda.client.grpc-address` can be set as the environment variable `CAMUNDA_CLIENT_GRPCADDRESS` in the connector runtime. ::: ## Configure the Orchestration Cluster connection for Self-Managed ### Connection URL To connect to the Orchestration Cluster, provide the following configuration: ```bash CAMUNDA_CLIENT_MODE=self-managed CAMUNDA_CLIENT_GRPCADDRESS=http://localhost:26500 CAMUNDA_CLIENT_RESTADDRESS=http://localhost:8080 ``` ```yaml camunda: client: mode: self-managed grpc-address: http://localhost:26500 rest-address: http://localhost:8080 ``` ### HTTPS configuration If using an HTTPS connection, you may need to provide a certificate to validate the Zeebe Gateway's certificate chain. ```bash CAMUNDA_CLIENT_CACERTIFICATEPATH=/path/to/certificate.pem ``` ```yaml camunda: client: ca-certificate-path: /path/to/certificate.pem ``` ### Authentication methods Choose the authentication method for your environment: By default, no authentication will be used. **Environment variables** ```bash CAMUNDA_CLIENT_AUTH_METHOD=none ``` **Application.yaml** ```yaml camunda: client: auth: method: none ``` To activate basic authentication: **Environment variables** ```bash CAMUNDA_CLIENT_AUTH_METHOD=basic CAMUNDA_CLIENT_AUTH_USERNAME= CAMUNDA_CLIENT_AUTH_PASSWORD= ``` **Application.yaml** ```yaml camunda: client: auth: method: basic username: password: ``` To activate OIDC-based authentication: **Environment variables** ```bash CAMUNDA_CLIENT_AUTH_METHOD=oidc CAMUNDA_CLIENT_AUTH_CLIENTID=xxx CAMUNDA_CLIENT_AUTH_CLIENTSECRET=xxx CAMUNDA_CLIENT_AUTH_TOKENURL=http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token CAMUNDA_CLIENT_AUTH_AUDIENCE= CAMUNDA_CLIENT_AUTH_SCOPE= ``` **Application.yaml** ```yaml camunda: client: auth: method: oidc client-id: client-secret: token-url: http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token audience: scope: ``` **Notes for Microsoft Entra ID** - Instead of `scope: CLIENT_ID_OC`, use: `scope: CLIENT_ID_OC + "/.default"`. - The `token-url` is typically formatted as: `https://login.microsoftonline.com//oauth2/v2.0/token`. :::note Audience validation If you have configured the audiences property for the Orchestration Cluster (`camunda.security.authentication.oidc.audiences`), the Orchestration Cluster will validate the audience claim in the token against the configured audiences. Ensure your token has the correct audience from the Orchestration Cluster configuration, or add your audience in the configuration. This is often the client ID you used when setting up the Orchestration Cluster. ::: See the [Camunda Spring Boot Starter documentation](../../../../apis-tools/camunda-spring-boot-starter/getting-started#self-managed) for more information on authentication properties. ## Configure the Orchestration Cluster connection for SaaS To use Camunda 8 SaaS, specify the connection properties: ```bash CAMUNDA_CLIENT_MODE=saas CAMUNDA_CLIENT_AUTH_CLIENTID=xxx CAMUNDA_CLIENT_AUTH_CLIENTSECRET=xxx CAMUNDA_CLIENT_CLOUD_REGION=bru-2 CAMUNDA_CLIENT_CLOUD_CLUSTERID=xxx ``` ```yaml camunda: client: mode: saas auth: client-id: xxx client-secret: xxx cloud: region: bru-2 cluster-id: xxx ``` If you are connecting a local connector runtime to a SaaS cluster, you may want to review our [guide to using connectors in hybrid mode](/components/connectors/use-connectors-in-hybrid-mode.md). ## Manual discovery of connectors By default, the connector runtime picks up outbound connectors available on the classpath automatically. To disable this behavior, use the following environment variables to configure connectors explicitly: | Environment variable | Purpose | | :-------------------------------------------- | :------------------------------------------------------------ | | `CONNECTOR_{NAME}_FUNCTION` (required) | Function to be registered as job worker with the given `NAME` | | `CONNECTOR_{NAME}_TYPE` (optional) | Job type to register for worker with `NAME` | | `CONNECTOR_{NAME}_INPUT_VARIABLES` (optional) | Variables to fetch for worker with `NAME` | | `CONNECTOR_{NAME}_TIMEOUT` (optional) | Timeout in milliseconds for worker with `NAME` | Through this configuration, you define all job workers to run. Specifying optional values allows you to override `@OutboundConnector`-provided connector configuration. ```bash CONNECTOR_HTTPJSON_FUNCTION=io.camunda.connector.http.rest.HttpJsonFunction CONNECTOR_HTTPJSON_TYPE=non-default-httpjson-task-type ``` ## Disabling Individual Connectors To disable individual connectors you can provide a comma separated list to `CONNECTOR_INBOUND_DISABLED` and `CONNECTOR_OUTBOUND_DISABLED` respectively. These list must contain the _connector type_ (e.g. `io.camunda:http-json:1`). To disable two outbound connectors, you can set the environment variable as follows: ```bash CONNECTOR_OUTBOUND_DISABLED=io.camunda:example:1,com.acme:custom-connector:2 ``` This can be found as the `` in the BPMN XML, the `zeebe:taskDefinition` property [in the element template](https://github.com/camunda/connectors/blob/8d2304754e202b56ae8c821746e99e1e9ef50c73/connectors/http/rest/element-templates/http-json-connector.json#L48) or in the `OutboundConnector` annotation for outbound connectors. The inbound connector type can be found as ``, the `inbound.type` property [in the element template](https://github.com/camunda/connectors/blob/8d2304754e202b56ae8c821746e99e1e9ef50c73/connectors/webhook/element-templates/webhook-connector-start-message.json#L51) or in the `InboundConnector` annotation. ## Disabling connector discovery :::warning We do not guarantee that all the Camunda provided connectors will be discovered via SPI. If you want to have a connector runtime without out-of-the-box connectors, we recommend building a custom runtime with only the connectors you want to use. ::: To disable the discovery of connectors via SPI or environment variables as explained [in this section](#manual-discovery-of-connectors), set the following environment variables: `CONNECTOR_INBOUND_DISCOVERY_DISABLED` and `CONNECTOR_OUTBOUND_DISCOVERY_DISABLED`. Note that this does not prevent the registration of connectors via Spring Beans or other mechanisms. ## Secrets Providing secrets to the runtime environment can be achieved in different ways, depending on your setup. Starting with Camunda 8.9, the environment-based secret provider applies the prefix `SECRET_` by default when resolving secrets. Only environment variables that start with this prefix are available as connector secrets. This improves security by preventing all environment variables from being exposed as connector secrets. Existing secrets that do not use the configured prefix will no longer resolve until you update either the environment variables or the prefix configuration. #### Configure a custom prefix To use a custom prefix, configure it via the Java property or environment variable and name your secrets accordingly: ```bash export CAMUNDA_CONNECTOR_SECRETPROVIDER_ENVIRONMENT_PREFIX='SUPER_SECRETS_' export SUPER_SECRETS_MY_SECRET='foo' # Resolved via {{ secrets.MY_SECRET }} ``` #### Restore the previous behavior (unsafe) To restore the previous behavior where all environment variables can be used as connector secrets, set the prefix to an empty value: ``` camunda.connector.secret-provider.environment.prefix= ``` :::warning When no prefix is configured, the connector runtime logs a warning that this mode is unsafe because all environment variables are exposed as connector secrets. Camunda does not recommend this mode for production environments. ::: The following environment variables can be used to configure the default secret provider: | Name | Description | Default value | | ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | `CAMUNDA_CONNECTOR_SECRETPROVIDER_ENVIRONMENT_ENABLED` | Whether the default secret provider is enabled. | `true` | | `CAMUNDA_CONNECTOR_SECRETPROVIDER_ENVIRONMENT_PREFIX` | Prefix applied to the secret name before lookup. Only environment variables starting with this prefix are available as secrets. Set to empty to disable (unsafe). | `SECRET_` | | `CAMUNDA_CONNECTOR_SECRETPROVIDER_ENVIRONMENT_TENANTAWARE` | Whether the secret provider should be tenant-aware. | `false` | If the secret provider is set to be tenant-aware, the secret format will change to `${prefix}${tenantId}_${secretName}`: Example with empty prefix: ```bash export CAMUNDA_CONNECTOR_SECRETPROVIDER_ENVIRONMENT_TENANTAWARE=true export tenant1_MY_SECRET='foo' # This will be resolved by using {{ secrets.MY_SECRET }} from tenant1 ``` Example with prefix set: ```bash export CAMUNDA_CONNECTOR_SECRETPROVIDER_ENVIRONMENT_TENANTAWARE=true export CAMUNDA_CONNECTOR_SECRETPROVIDER_ENVIRONMENT_PREFIX='SUPER_SECRETS_' export SUPER_SECRETS_tenant1_MY_SECRET='foo' # This will be resolved by using {{ secrets.MY_SECRET }} from tenant1 ``` Connector secrets can be used in Helm charts, for example by referencing a [Kubernetes secret](https://kubernetes.io/docs/concepts/configuration/secret/): ```yaml connectors: envFrom: - secretRef: name: camunda-connector-secrets ``` ``` apiVersion: v1 kind: Secret metadata: name: camunda-connector-secrets stringData: MY_SECRET: foo ``` Review the documentation on [managing secrets in Helm charts](/self-managed/deployment/helm/configure/secret-management.md) for additional details. To inject secrets into the [Docker images of the runtime](/self-managed/deployment/docker/docker.md#connectors), they must be available in the environment of the Docker container. For example, you can inject secrets when running a container: ```bash docker run --rm --name=connectors -d \ -v $PWD/connector.jar:/opt/app/ \ # Add a connector jar to the classpath -e MY_SECRET=secret \ # Set a secret with value -e SECRET_FROM_SHELL \ # Set a secret from the environment --env-file secrets.txt \ # Set secrets from a file camunda/connectors-bundle:latest ``` The secret `MY_SECRET` value is specified directly in the `docker run` call, whereas the `SECRET_FROM_SHELL` is injected based on the value in the current shell environment when `docker run` is executed. The `--env-file` option allows using a single file with the format `NAME=VALUE` per line to inject multiple secrets at once. In the [manual setup](/self-managed/deployment/manual/install.md#connectors-1), inject secrets during connector execution by providing them as environment variables before starting the runtime environment. You can, for example, export them beforehand as follows: ```bash export MY_SECRET='foo' ``` Reference the secret in the connector's input in the prefixed style `{{secrets.MY_SECRET}}`. Create your own implementation of the `io.camunda.connector.api.secret.SecretProvider` interface that [comes with the SDK](https://github.com/camunda/connectors/blob/main/connector-sdk/core/src/main/java/io/camunda/connector/api/secret/SecretProvider.java). Package this class and all its dependencies as a JAR, for example `my-secret-provider-with-dependencies.jar`. This needs to include a file `META-INF/services/io.camunda.connector.api.secret.SecretProvider` that contains the fully qualified class name of your secret provider implementation. Add this JAR to the runtime environment, depending on your deployment setup. Your secret provider will serve secrets as implemented. To use this JAR with [Camunda Helm charts](https://artifacthub.io/packages/helm/camunda/camunda-platform), build an [init container](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/) to create a volume with your secret provider, and mount it into the connectors pod. For example, use the following file as input for your `helm install` command: ```bash connectors: extraVolumes: - name: workdir emptyDir: {} extraVolumeMounts: # Mount the secret provider # The Connectors pod will pick up the secret provider from /opt/app during startup - name: workdir mountPath: /opt/app/file-secret-provider-2.1.2.jar subPath: file-secret-provider-2.1.2.jar initContainers: - name: install image: busybox:1.36.1 command: ["sh", "-c"] args: # Download a the custom secret provider into the volume - | wget -O /work-dir/file-secret-provider-2.1.2.jar https://artifacts.camunda.com/artifactory/camunda-consulting/com/camunda/consulting/connector/file-secret-provider/2.1.2/file-secret-provider-2.1.2.jar volumeMounts: - name: workdir mountPath: "/work-dir" securityContext: runAsUser: 1000 # redundant as 1000 is not root but good to have # as the runtime will do verification that no process will # run as root within the container runAsNonRoot: true ``` For Docker images, you can add the JAR by using volumes, for example: ```bash docker run --rm --name=connectors -d \ -v $PWD/my-secret-provider-with-dependencies.jar:/opt/app/my-secret-provider-with-dependencies.jar \ # Specify secret provider -e CAMUNDA_CLIENT_ZEEBE_GRPCADDRESS=http://ip.address.of.zeebe:26500 \ # Specify grpc Zeebe address camunda/connectors:latest ``` In manual installations, add the JAR to the `-cp` argument of the Java call: ```bash java -cp 'connector-runtime-application-VERSION-with-dependencies.jar:...:my-secret-provider-with-dependencies.jar' \ io.camunda.connector.runtime.ConnectorRuntimeApplication ``` ## HTTP proxy configuration You can configure connectors to route HTTP requests through a proxy server. See [HTTP proxy configuration](./http-proxy-configuration.md) for details. ## Truststore If your connector runtime needs to connect to external systems over HTTPS, you might need to provide a custom truststore. To configure the truststore, use the following environment variables: - `JAVAX_NET_SSL_TRUSTSTORE`: Path to the truststore file (e.g., `/path/to/truststore.jks`) - `JAVAX_NET_SSL_TRUSTSTOREPASSWORD`: Password for the truststore ## Multi-tenancy The Connector Runtime supports multiple tenants for inbound and outbound connectors. These are configurable in [Orchestration Cluster Admin](/components/admin/tenant.md). A single Connector Runtime can serve a single tenant or can be configured to serve multiple tenants. By default, the runtime uses the tenant ID `` for all Zeebe-related operations like handling jobs and publishing messages. :::info Support for **outbound connectors** with multiple tenants requires a dedicated tenant job worker config (described below). **Inbound connectors** automatically work for all tenants the configured Connector Runtime client has access to. This can be configured in Admin via the application assignment. ::: ### Environment variables The Connector Runtime uses the following environment variables to configure multi-tenancy: | Name | Description | Default value | | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | CAMUNDA_CLIENT_TENANTID | The default tenant ID used to communicate with Zeebe. Changing this value will set a new default tenant ID used for fetching jobs and publishing messages. | `` | | CAMUNDA_CLIENT_WORKER_DEFAULTS_TENANTIDS | The default tenant IDs (comma separated) used to activate jobs. To run the Connector Runtime in a setup where a single runtime serves multiple tenants, add each tenant ID to this list. | `` | If you are using an embedded version of the Connector Runtime, you can specify the tenant information in your Spring configuration like in this example `application.properties` file: ```bash camunda.client.tenant-id=myTenant camunda.client.worker.defaults.tenant-ids=myTenant ``` ### Outbound connector config The Connector Runtime uses the default tenant for outbound connector-related features. If support for a different tenant or multiple tenants should be enabled, the tenants need to be configured individually using the following environment variables. If you want to use outbound connectors for a single tenant that is different from the default tenant, you can specify a different default tenant ID using: ```bash CAMUNDA_CLIENT_TENANTID=myTenant ``` This will change the default tenant ID used for fetching jobs and publishing messages to the tenant ID `myTenant`. It is possible to adjust the polling interval of connectors polling process definitions to Operate by setting the environment variable `CAMUNDA_CONNECTOR_POLLING_INTERVAL`. This variable allows you to control how often connectors fetch the process definitions, with the interval specified in milliseconds. For example, setting `CAMUNDA_CONNECTOR_POLLING_INTERVAL=20000` will configure the connectors to poll every 20 seconds. Example: ```bash CAMUNDA_CONNECTOR_POLLING_INTERVAL=10000 ``` :::note Inbound connectors will still be enabled for all tenants the Connector Runtime client has access to. ::: To run the connector Runtime in a setup where a single runtime serves multiple tenants, add each tenant ID to the list of the default job workers: ```bash CAMUNDA_CLIENT_ZEEBE_DEFAULTS_TENANTIDS=`myTenant, otherTenant` ``` In this case, the `CAMUNDA_CLIENT_TENANTID` will **not** be used for the configuration of job workers. ### Inbound Connector configuration The Connector Runtime fetches process definitions from the Orchestration Cluster REST API, and executes all inbound connectors within those processes independently of the outbound connector configuration without any additional configuration required from the user. To restrict the Connector Runtime inbound connector feature to a single tenant or multiple tenants, use Admin and assign the tenants the connector application should have access to. ### Troubleshooting To ensure seamless integration and functionality, the multi-tenancy feature must also be enabled across **all** associated components [if not configured in Helm](../../deployment/helm/configure/configure-multi-tenancy.md) so users can view any data from tenants for which they have authorizations configured in Admin. Find more information (including links to component-specific configuration pages) on the [multi-tenancy concepts page](/components/concepts/multi-tenancy.md). ## Logging ### Changing the log level The log level can be changed globally by setting the environment variable `LOGGING_LEVEL_IO_CAMUNDA_CONNECTOR=DEBUG`. This changes the default log level for the `io.camunda.connector` package to `DEBUG`. You can can use this package based log level approach also with custom connectors by providing your package (`my.package`) via this variable: `LOGGING_LEVEL_MY_PACKAGE=DEBUG`. To change the log level for all packages, change it for the `root` logger: `LOGGING_LEVEL_ROOT=DEBUG`. ### Google Stackdriver (JSON) logging To enable Google Stackdriver compatible JSON logging, set the environment variable `CONNECTORS_LOG_APPENDER=stackdriver` on the Connector Runtime. --- ## HTTP proxy configuration Configure HTTP proxy settings for Camunda connectors in Self-Managed environments. ## Configuration methods In Self-Managed environments, you can configure connectors to route HTTP requests through a proxy server using one of these two methods: | Configuration type | Scope | Example | | :---------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [JVM properties](https://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html) | JVM, **the whole runtime** will be affected. **Any HTTP client** used internally (for example, in a connector, or the Zeebe client) might be affected. | `-Dhttp.proxyHost=proxy -Dhttp.proxyPort=3128 -Dhttps.proxyHost=proxy -Dhttps.proxyPort=3128 -Dhttp.nonProxyHosts=OTHER_DOMAIN` | | Environment variables | Connector-scoped, **only supported connectors** such as the REST connector will be affected | `CONNECTOR_HTTP_PROXY_HOST=proxy; CONNECTOR_HTTP_PROXY_PORT=3128; CONNECTOR_HTTPS_PROXY_HOST=proxy; CONNECTOR_HTTPS_PROXY_PORT=3128; CONNECTOR_HTTP_NON_PROXY_HOSTS=OTHER_DOMAIN;` | ## HTTP/HTTPS properties Depending on the **target URL**, you can set the proxy as an HTTP or HTTPS protocol handler: - A target URL such as `http://example.com` will use the HTTP protocol handler. - A target URL such as `https://example.com` will use the HTTPS protocol handler. ### JVM properties You can set the following standard JVM properties for HTTP and HTTPS: | Property (HTTP target URL) | Property (HTTPS target URL) | Description | | :------------------------- | :-------------------------- | :------------------------------------------------------- | | `http.proxyHost` | `https.proxyHost` | The host name of the proxy server. | | `http.proxyPort` | `https.proxyPort` | The port number (default is 80 for HTTP, 443 for HTTPS). | Some HTTP clients might offer more properties to configure the proxy. For example, the [Apache HTTP client](https://hc.apache.org/httpcomponents-client-5.6.x/current/httpclient5/apidocs/org/apache/hc/client5/http/impl/classic/HttpClientBuilder.html) used in the REST connector offers the following properties: | Property (HTTP target URL) | Property (HTTPS target URL) | Description | | :------------------------- | :-------------------------- | :------------------------------------------------ | | `http.proxyUser` | `https.proxyUser` | _(optional)_ The username to log in to the proxy. | | `http.proxyPassword` | `https.proxyPassword` | _(optional)_ The password to log in to the proxy. | The `http.nonProxyHosts` property applies to both HTTP and HTTPS target URLs: | Property | Description | | :------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `http.nonProxyHosts` | _(optional)_ A list of hosts to connect to directly, bypassing the proxy.Specify as a list of patterns, separated by \|.Patterns can start or end with a `*` for wildcards.Any host matching one of these patterns uses a direct connection instead of a proxy. | :::note To ensure Camunda can properly access Camunda components when using JVM properties, non-proxy hosts must contain `camunda-platform-zeebe|camunda-platform-keycloak`. ::: :::important The connector runtime uses internal HTTP calls between pods (for example, for `/connectors/inbound-instances`) by resolving the headless service name to pod IPs. These internal calls use the resolved IP (for example, `10.7.108.230`), not the original service hostname. If you configure JVM-wide proxies using `-Dhttp.proxyHost` or `-Dhttps.proxyHost`, make sure `http.nonProxyHosts` (and/or `CONNECTOR_HTTP_NON_PROXY_HOSTS`) also includes patterns for your cluster CIDRs (for example, `10.*` or `10.244.*`), not only service DNS names like `*.svc.cluster.local`. Otherwise, internal pod-to-pod traffic may be routed through your corporate proxy and fail with 403 or TLS parsing errors. ::: ### Environment variables As an alternative to using JVM properties, the proxy settings can also be set with environment variables: | Variable (HTTP target URL) | Variable (HTTPS target URL) | Description | | :------------------------------ | :------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------- | | `CONNECTOR_HTTP_PROXY_HOST` | `CONNECTOR_HTTPS_PROXY_HOST` | The host name of the proxy server. | | `CONNECTOR_HTTP_PROXY_PORT` | `CONNECTOR_HTTPS_PROXY_PORT` | The port number. | | `CONNECTOR_HTTP_PROXY_USER` | `CONNECTOR_HTTPS_PROXY_USER` | _(optional)_ The username to log in to the proxy. | | `CONNECTOR_HTTP_PROXY_PASSWORD` | `CONNECTOR_HTTPS_PROXY_PASSWORD` | _(optional)_ The password to log in to the proxy. | | `CONNECTOR_HTTP_PROXY_SCHEME` | `CONNECTOR_HTTPS_PROXY_SCHEME` | _(optional)_ The scheme of the proxy server. This allows you to use the `https` protocol to contact your proxy. The default is `http`. | The `CONNECTOR_HTTP_NON_PROXY_HOSTS` variable applies to both HTTP and HTTPS target URLs: | Variable | Description | | :------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `CONNECTOR_HTTP_NON_PROXY_HOSTS` | _(optional)_ A list of hosts to connect to directly, bypassing the proxy.Specify as a list of patterns, separated by \|.Patterns can start or end with a `*` for wildcards.Any host matching one of these patterns uses a direct connection instead of a proxy.Connectors will also use the exception list provided by the `http.nonProxyHosts` JVM property if existing. | ### Plain proxy variables The standard proxy variables support configuring a TLS-based proxy (running under `https://`) via the `SCHEME` variable. However, some connector integrations use HTTP clients (such as the JDK HttpClient) that do not support TLS-based proxy connections. For these connectors, an alternative set of **plain proxy variables** is available. When both plain and standard variables are configured, the **plain variables take priority**. If the plain variables are not set, the connector falls back to the standard variables. | Variable (HTTP target URL) | Variable (HTTPS target URL) | Description | | :------------------------------------ | :------------------------------------- | :------------------------------------------------------------------ | | `CONNECTOR_HTTP_PLAIN_PROXY_HOST` | `CONNECTOR_HTTPS_PLAIN_PROXY_HOST` | The host name of the proxy server. | | `CONNECTOR_HTTP_PLAIN_PROXY_PORT` | `CONNECTOR_HTTPS_PLAIN_PROXY_PORT` | The port number. | | `CONNECTOR_HTTP_PLAIN_PROXY_USER` | `CONNECTOR_HTTPS_PLAIN_PROXY_USER` | _(optional)_ The username to log in to the proxy. | | `CONNECTOR_HTTP_PLAIN_PROXY_PASSWORD` | `CONNECTOR_HTTPS_PLAIN_PROXY_PASSWORD` | _(optional)_ The password to log in to the proxy. | | `CONNECTOR_HTTP_PLAIN_PROXY_SCHEME` | `CONNECTOR_HTTPS_PLAIN_PROXY_SCHEME` | _(optional)_ The scheme of the proxy server. The default is `http`. | :::note There is no plain variant of `NON_PROXY_HOSTS`. The standard `CONNECTOR_HTTP_NON_PROXY_HOSTS` variable applies to both standard and plain proxy configurations. ::: The following connectors support plain proxy variables: - [AI Agent connector](/components/connectors/out-of-the-box-connectors/agentic-ai-aiagent.md) - [MCP Client connector](/components/connectors/out-of-the-box-connectors/agentic-ai-mcp-client.md) - [A2A Client connector](/components/early-access/alpha/a2a-client/a2a-client.md) - [Vector database connector](/components/connectors/out-of-the-box-connectors/embeddings-vector-db.md) Not all providers within each connector support proxy configuration. Refer to the individual connector documentation for details on unsupported providers. The [REST connector](/components/connectors/protocol/rest.md) and the core SDK HTTP client use only the standard proxy variables. ## How proxy configuration works The process consists of two main steps: configuration and request handling. ### Set your configuration First, define how the proxy should behave. These are the available configuration options: - Enable or disable proxying. - Define which URLs should skip the proxy, listed as `nonProxyHosts`. - Define which URLs require authentication. ### Handle requests When a connector makes an HTTP request, it's handled according to your previously set configuration: 1. Check if the proxy is enabled: 1. Yes: Proceed with proxying. 1. No: Do not proxy; the request is handled directly. 1. Check if the host is listed in `nonProxyHosts`: 1. Yes: Do not proxy; the request bypasses the proxy. 1. No: Proceed with proxying. 1. Check if the proxy requires authentication: 1. Yes: The request is proxied only if authentication succeeds; otherwise, it returns an authentication error. 1. No: The request is proxied normally. --- ## Overview(Connectors) The concept of a [Connector](/components/connectors/introduction.md) consists of two parts: - The business logic is implemented by a connector function and executed by a [Connector runtime environment](/components/connectors/custom-built-connectors/connector-sdk.md#runtime-environments). - The user interface during modeling is provided using a [Connector template](/components/connectors/custom-built-connectors/connector-templates.md). In a [Self-Managed](/self-managed/about-self-managed.md) environment, you manage the execution environment for connectors yourself. Using our [Connector runtime environments](/components/connectors/custom-built-connectors/connector-sdk.md#runtime-environments), you can consume any set of connectors, including the [out-of-the-box connectors](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md) and custom connectors developed using the **[Connector SDK](/components/connectors/custom-built-connectors/connector-sdk.md)** and [Connector templates](/components/connectors/custom-built-connectors/connector-templates.md). You can find connectors developed by Camunda, partners, and the community in [Camunda Marketplace](https://marketplace.camunda.com/en-US/home). :::note Some out-of-the-box connectors are licensed under the [Camunda Self-Managed Free Edition license](https://camunda.com/legal/terms/cloud-terms-and-conditions/camunda-cloud-self-managed-free-edition-terms/). ::: ## Connector runtime The connector runtime environment can be installed using the supported [deployment options](/self-managed/setup/overview.md#deployment-options). Currently, we support an installation of connectors with [Docker](/self-managed/deployment/docker/docker.md#connectors), [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md), [Helm charts](/self-managed/setup/overview.md), and the [manual setup](/self-managed/deployment/manual/install.md#connectors-1). ## Connector templates For using connectors in the Web or Desktop Modeler, you need to [provide connector templates](/components/connectors/custom-built-connectors/connector-templates.md#providing-and-using-connector-templates). For the [out-of-the-box connectors](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md) provided by Camunda, the Connectors release provides a set of all connector templates related to one [release version](https://github.com/camunda/connectors/releases). If you use the [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md) installation, you can thus fetch all connector templates that match the versions of the connectors used in the backend. Alternatively, you can fetch the JSON templates from the respective connector's releases in the respective connectors folder in the [repository](https://github.com/camunda/connectors) at `connectors/{connector name}/element-templates`. You can use the connector templates as provided or modify them to your needs as described in our [Connector templates guide](/components/connectors/custom-built-connectors/connector-templates.md). Review our [Connectors Awesome List](https://github.com/camunda-community-hub/camunda-8-connectors/tree/main) to find more connectors. --- ## Connector runtime performance This guide explains how to optimize the configuration of outbound connectors in your self-managed environment to achieve consistent and predictable performance. ## Overview Connector runtime performance primarily depends on: - The level of concurrency (`max in-flight` or `max-jobs-active`). - The performance and rate limits of the external systems your connectors interact with. - Whether the runtime uses polling or job streaming for job acquisition. Before tuning the runtime, ensure you have proper observability in place and confirm where the bottleneck is. ## Measure outbound connector performance The performance of a connector runtime is typically evaluated by two metrics: - Jobs processed per second (throughput) - Average processing time per activated job The connector runtime exposes various metrics via `/actuator/prometheus`, including: ``` camunda_client_worker_job_total camunda_connector_outbound_execution_time_seconds executor_seconds_count{name="camundaClientExecutor"} executor_seconds_max{name="camundaClientExecutor"} ``` Use Prometheus to scrape the metrics and visualize them. While no official Grafana dashboard is provided for connectors, you can use the [connectors community Grafana dashboard](https://github.com/camunda-community-hub/connectors-grafana-dashboard) as a starting point. ## Identify the bottleneck To better understand what adjustments you need, verify where the bottleneck is: - Is your third-party system limiting throughput? - Is CPU or memory saturated? - Is the connector runtime operating in streaming or polling mode? ### Job streaming :::note Starting with connector runtime version 8.9.0+, job streaming is enabled by default. ::: If your runtime operates in polling mode, you may observe the following side effects: - The runtime does not pick up new jobs immediately and instead waits until the previous batch of jobs is fully processed, even though there are available threads. - Job distribution is uneven between multiple replicas of the connector runtime, causing some replicas to be idle while others are overloaded. To mitigate these issues, you can enable job streaming by setting the following property in your connector runtime configuration: ``` camunda.client.worker.defaults.stream-enabled=true ``` We recommend enabling job streaming unless you have specific reasons to use polling mode. To learn more, see [Job streaming in the gateway](/self-managed/components/orchestration-cluster/zeebe/zeebe-gateway/job-streaming.md). ## Configure `max-in-flight` The maximum concurrency (`max-in-flight`) defines how many jobs the connector runtime can work on in parallel. The right configuration for `max-in-flight` depends on the executor type used by the connector runtime. ### Virtual threads executor (default) Starting with connector runtime version 8.9.0+, the connector runtime uses virtual threads by default. Virtual threads are lightweight threads that allow high concurrency with low resource consumption. They are optimized for I/O-bound workloads such as connectors. This feature builds on Project Loom, which is part of the standard Java Development Kit (JDK) starting with JDK 19. The current long-term support (LTS) release is JDK 21. When you use virtual threads, the `max-in-flight` configuration defines the maximum number of virtual threads that can process jobs in parallel. The connector runtime can efficiently handle a large number of virtual threads, so you can often set a high `max-in-flight` value (for example, 500) without running into resource constraints. To configure the `max-in-flight` for a connector runtime using virtual threads, set the following property: ``` camunda.client.worker.defaults.max-jobs-active=500 ``` There is no practical upper limit for `max in-flight` when using virtual threads, but usually, the performance bottleneck will be due to objective CPU limitations or external system rate limits. We recommend gradually increasing the `max-in-flight` value while monitoring the performance and resource usage of your connector runtime until you reach an optimal configuration. ### Platform threads executor You can disable virtual threads and use platform threads instead by setting the following property: ``` camunda.connector.virtual-threads.enabled=false ``` Connector runtime versions before 8.9.0 use platform threads by default. When using platform threads, use the configuration options provided by the [Camunda Spring Boot Starter](/apis-tools/camunda-spring-boot-starter/configuration.md): ``` camunda.client.worker.defaults.max-jobs-active=20 camunda.client.execution-threads=20 ``` The `max-jobs-active` property defines the maximum number of jobs that are activated in parallel. The `execution-threads` property defines the size of the thread pool used to process jobs. If you set `max-jobs-active` higher than `execution-threads`, the connector runtime queues jobs until a thread becomes available for processing. If you set `execution-threads` higher than `max-jobs-active`, some threads remain idle while waiting for jobs to be activated. --- ## Camunda Hub configuration Camunda Hub Self-Managed can be configured using environment variables and configuration parameters. :::note Underscores in environment variables correspond to configuration file key levels. ::: ## Environment variables Console environment variables can be set in Helm values via the `console.env` key. For more details, check [Console Helm values](https://artifacthub.io/packages/helm/camunda/camunda-platform#console-parameters). ### Keycloak settings | Environment variable | Description | Example value | | :--------------------------- | :------------------------------ | :----------------------------------------- | | `KEYCLOAK_BASE_URL` | Base URL for Keycloak. | `https://example.com/auth` | | `KEYCLOAK_INTERNAL_BASE_URL` | Internal base URL for Keycloak. | `http://camunda-platform-keycloak:80/auth` | | `KEYCLOAK_REALM` | Realm for Keycloak. | `camunda-platform` | ### Console settings | Environment variable | Description | Example value | | :---------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------- | | `CAMUNDA_IDENTITY_AUDIENCE` | Audience for the Console client. | `console` | | `CAMUNDA_IDENTITY_CLIENT_ID` | Client ID for the Console client. | `console` | | `CAMUNDA_CONSOLE_CONTEXT_PATH` | Context path for Console. | `console` | | `CAMUNDA_CONSOLE_CUSTOMERID` | Unique identifier for the customer. | `customer-id` | | `CAMUNDA_CONSOLE_INSTALLATIONID` | Unique installation ID of the current customer installation. | `installation-id` | | `CAMUNDA_CONSOLE_TELEMETRY` | Telemetry mode for Console Self-Managed: `disabled`, `online`, or `download`. | `online` | | `CAMUNDA_CONSOLE_DISABLE_AUTH` | Disables authentication for Console. When set to `true`, users can access Console without logging in and API requests do not require authorization. All Identity-related variables are ignored when authentication is disabled. | `true` | | `CAMUNDA_LICENSE_KEY` | Camunda 8 license key. For Helm installations, configure this in the `values.yaml` file. See [License key](/self-managed/deployment/helm/configure/license-key.md) for details.Without a license key, Console will display **Non‑Production License** in the navigation bar and log a warning; functionality is unaffected. See [Camunda Enterprise page](https://camunda.com/platform/camunda-platform-enterprise-contact/). | N/A | | `CAMUNDA_CONSOLE_REDIRECT_URL` | _(Optional)_ Overrides the redirect URL for the authentication flow. By default, the origin URL is used. Default: empty string. | `https://example.com/callback` | | `CAMUNDA_CONSOLE_REDIRECT_TRAILING_SLASH` | _(Optional)_ Controls whether a trailing slash is added to the redirect URL for the authentication flow. Default: `true`. | `false` | | `CAMUNDA_IDENTITY_JWT_ALGORITHMS` | _(Optional)_ List of trusted JWS algorithms used for JWT validation. Only necessary if the algorithms cannot be derived from the JWK set response. | `ES256` | ### SSL/TLS settings | Environment variable | Description | Example value | | :---------------------------------------------- | :------------------------------------------------------------------------------------------- | :----------------------------------- | | `SERVER_SSL_ENABLED` | _(Optional)_ Whether to enable SSL support. Default: `false`. | `true` | | `SERVER_SSL_CERTIFICATE` | _(Optional)_ Path to the PEM-encoded SSL certificate file. | `file:/full/path/to/certificate.pem` | | `SERVER_SSL_CERTIFICATE_PRIVATE_KEY` | _(Optional)_ Path to the PEM-encoded private key file for the SSL certificate. | `file:/full/path/to/key.pem` | | `SERVER_SSL_PASSPHRASE` | _(Optional)_ Passphrase for the key. | `passphrase` | | `MANAGEMENT_SERVER_SSL_ENABLED` | _(Optional)_ Whether to enable SSL for management server routes. Default: `false`. | `true` | | `MANAGEMENT_SERVER_SSL_CERTIFICATE` | _(Optional)_ Path to the PEM-encoded certificate file for the management server. | `file:/full/path/to/certificate.pem` | | `MANAGEMENT_SERVER_SSL_CERTIFICATE_PRIVATE_KEY` | _(Optional)_ Path to the PEM-encoded private key file for the management server certificate. | `file:/full/path/to/key.pem` | | `MANAGEMENT_SERVER_SSL_PASSPHRASE` | _(Optional)_ Passphrase for the management server certificate key. | `passphrase` | ### Proxy These settings are useful when the application needs to make outgoing network requests in environments that require traffic to pass through a proxy server. | Environment variable | Description | Example value | Default value | | :------------------- | :--------------------------------------------------------------------------------------------- | :------------------------------------ | :------------ | | `http_proxy` | Specifies the proxy server to be used for outgoing HTTP requests. | `http://proxy.example.com:8080` | - | | `https_proxy` | Specifies the proxy server to be used for outgoing HTTPS requests. | `https://secureproxy.example.com:443` | - | | `no_proxy` | A comma-separated list of domain names or IP addresses for which the proxy should be bypassed. | `localhost,127.0.0.1,.example.com` | - | :::note These proxy variables (`http_proxy`, `https_proxy`, `no_proxy`) are lowercase by convention. Many systems and tools expect them in lowercase. ::: ### Experimental Features The following environment variables enable experimental features: | Environment variable | Description | Example value | Default value | | :-------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------- | :------------ | :------------ | | `CAMUNDA_CONSOLE_EXPERIMENTAL_DISCOVERY_MODE` | This mode allows [orchestration clusters](../../orchestration-cluster/zeebe/configuration/broker.md) to register itself by calling an API in Console. | `true` | `false` | ## Telemetry You can enable telemetry and usage collection to help us improve our product by sending several telemetry metrics to Camunda. The information we collect will contribute to continuous product enhancement and help us understand how Camunda is used. We do not collect sensitive information and limit data points to several metrics. For more information, you can download collected data set metrics from the telemetry page at anytime. By enabling data collection and reporting, you can get a new page to introspect Camunda 8 component metrics. Usually accessible via monitoring tools like Prometheus, you can now access these metrics directly in Console. By default, telemetry collection is disabled and no data is collected. When `CAMUNDA_CONSOLE_TELEMETRY` env var or `telemetry` parameter is set to `online`, the telemetry feature is activated and the collected data is sent once every 24 hours via HTTPS. When `CAMUNDA_CONSOLE_TELEMETRY` env var or `telemetry` parameter is set to `download`, the telemetry feature is activated. Data collected **will not** be sent to Camunda automatically, but could be downloaded from Console and shared with us on request. To enable usage collection, configure the parameters described in the next section. ## Configuration parameters To enable telemetry, the following parameters need to be configured. Camunda will provide you with the customer ID (Camunda Docker username) needed to send telemetry data to Camunda. | Parameter | Description | Example value | | :----------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------- | | `customerId` | Unique identifier of the customer. This is also a Camunda Docker registry username. | `customername` | | `installationId` | Unique installation ID of the current customer installation. | `my-deployment` | | `telemetry` | Telemetry config for Console Self-Managed: `disabled`, `online`, or `download`. | `online` | | `managed.releases.tags` | Assign cluster tags to indicate what type of cluster it is. Default tags are `dev`, `stage`, `test`, or `prod`, but users can assign any custom tag. | `- dev` (list of strings) | | `managed.releases.custom-properties` | List of custom properties users can add to their cluster with custom descriptions and custom links on the cluster details page. | See custom properties section | Console environment variables could be set in Helm. For more details, check [Console Helm values](https://artifacthub.io/packages/helm/camunda/camunda-platform#console-parameters). For example: ```yaml console: env: - name: CAMUNDA_CONSOLE_CUSTOMERID value: customername - name: CAMUNDA_CONSOLE_INSTALLATIONID value: my-deployment - name: CAMUNDA_CONSOLE_TELEMETRY value: online ``` ### Override configuration parameters You can override only specific configuration parameters as needed. The YAML structure mirrors the environment variable hierarchy. If a parameter must be changed for a specific cluster, the `name` and `namespace` fields must be set with the exact values so correlations can be made accordingly. #### Example Given the following configuration provided by Helm: ```yaml camunda: console: customerId: customer-id installationId: camunda-platform-id-dev-console-sm-main telemetry: disabled managed: method: plain releases: - name: camunda-platform namespace: camunda-platform-namespace version: 9.1.2 components: - name: Console id: console version: ... url: https://... readiness: https://... metrics: https://... - name: Keycloak id: keycloak version: ... url: https://... - name: Identity id: identity version: ... url: https://... readiness: https://... metrics: https://... - name: WebModeler id: webModelerWebApp version: ... url: https://... - name: Zeebe Gateway id: zeebeGateway version: ... urls: grpc: grpc://... http: https://... readiness: https://... metrics: https://... - name: Zeebe id: zeebe version: ... ``` The following example of an `overrideConfiguration` changes the `customerId` and adds `tags` and `custom-properties` for the cluster with name `camunda-platform` in namespace `camunda-platform-namespace`: ```yaml console: overrideConfiguration: camunda: console: customerId: "new-customer-id" managed: releases: - name: camunda-platform namespace: camunda-platform-namespace tags: - production custom-properties: - description: "This is a custom description of the cluster." links: - name: "Camunda" url: "https://camunda.com" - name: "Camunda Docs" url: "https://docs.camunda.io" - name: "Grafana" url: "https://..." ``` ### Custom properties Custom properties are useful to add custom information to the **Cluster details** page in Console. A custom property contains a description and multiple links. The following example shows one custom property for a cluster: ```yaml console: overrideConfiguration: camunda: console: customerId: "new-customer-id" managed: releases: - name: camunda-platform namespace: camunda-platform custom-properties: - description: "Useful links to Camunda resources." links: - name: "Camunda Blog" url: "https://camunda.com/blog/" - name: "Camunda Docs" url: "https://docs.camunda.io" ``` ## Using a different OpenID Connect (OIDC) authentication provider than Keycloak By default, Console uses Keycloak to provide authentication. You can use a different OIDC provider by following the steps described in the [OIDC connection guide](/self-managed/deployment/helm/configure/authentication-and-authorization/external-oidc-provider.md). ## Monitoring To help understand how Console operates, we expose the following endpoints by default: | Endpoint | Port | Path | | :----------------------------------------------- | :----- | :------------------ | | Metrics endpoint with default Prometheus metrics | `9100` | `/prometheus` | | Readiness probe | `9100` | `/health/readiness` | | Liveness probe | `9100` | `/health/liveness` | ## Troubleshooting | Problem | Solution | | :--------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | | Invalid parameter: redirect_uri | Ensure the correct redirect URL is configured for the application Console in Identity. The redirect URL must match the Console URL. | | JWKS for authentication is not reachable | To verify a user's access token the JWKS needs to be reachable. Make sure the environment variable `KEYCLOAK_INTERNAL_BASE_URL` is set correctly. | | Console shows error 401 | Make sure the logged-in user has the role `Console` assigned in the Identity service. | --- ## Copilot Alpha Web Modeler supports using large language models (LLMs) to help users create BPMN diagrams, write FEEL expressions, and build forms. You can configure the default LLM provider for BPMN, FEEL, and form copilots. Copilot supports the following LLM providers: | LLM provider | Configuration value | | ---------------- | ------------------- | | Anthropic | `ANTHROPIC` | | AWS Bedrock | `BEDROCK` | | Azure AI | `AZURE_AI` | | Azure OpenAI | `AZURE_OPENAI` | | Google Vertex AI | `VERTEX_AI` | | Hugging Face | `HUGGING_FACE` | | Ollama | `OLLAMA` | | OpenAI | `OPENAI` | ## Configuration To enable Copilot, set the AI feature flag (`FEATURE_AI_ENABLED` / `camunda.modeler.feature.ai-enabled`) to `true`. Then configure the default LLM provider for BPMN, FEEL, and form copilots. Each provider has its own configuration options described below. | Environment variable | Description | Example value | Default value | | ------------------------------------------- | ------------------------------------------------------------------------------ | ------------- | ------------- | | `FEATURE_AI_ENABLED` | Enables Copilot. | `true` | `false` | | `RESTAPI_BPMN_COPILOT_DEFAULT_LLM_PROVIDER` | Default provider for BPMN Copilot. | `BEDROCK` | – | | `RESTAPI_FEEL_COPILOT_DEFAULT_LLM_PROVIDER` | Default provider for FEEL Copilot. | `OPENAI` | – | | `RESTAPI_FORM_COPILOT_DEFAULT_LLM_PROVIDER` | Default provider for form Copilot. | `VERTEX_AI` | – | | `RESTAPI_COPILOT_REQUEST_TIMEOUT` | [optional] Overall request timeout in milliseconds for Copilot requests in UI. | `200000` | `300000` | ```yaml camunda.modeler: feature.ai-enabled: true copilot: default-bpmn-copilot-llm-provider: BEDROCK default-feel-copilot-llm-provider: OPENAI default-form-copilot-llm-provider: VERTEX_AI client.copilot-request-timeout: 200s # optional, default: 300s ``` ### BPMN Copilot | Environment Variable | Description | Example Value | Default Value | | ----------------------------------------------------- | ------------------------------------------------------------------------- | -------------------- | ------------- | | `RESTAPI_BPMN_COPILOT_TEMPERATURE` | [optional] Sampling temperature. | `0.2` | `0.3` | | `RESTAPI_BPMN_COPILOT_TOP_P` | [optional] Nucleus sampling probability. | `0.90` | `0.95` | | `RESTAPI_BPMN_COPILOT_TOP_K` | [optional] Top-K sampling (if supported by the model). | `100` | `64` | | `RESTAPI_BPMN_COPILOT_MAX_TOKENS` | [optional] Maximum new tokens per responses. | `4096` | `8192` | | `RESTAPI_BPMN_COPILOT_TIMEOUT` | [optional] Overall request timeout. | `45s` | `60s` | | `RESTAPI_BPMN_COPILOT_LOG_REQUEST` | [optional] Log raw requests (not recommended in production). | `true` | `false` | | `RESTAPI_BPMN_COPILOT_LOG_RESPONSE` | [optional] Log raw responses (not recommended in production). | `true` | `false` | | `RESTAPI_BPMN_COPILOT_CONNECTION_ACQUISITION_TIMEOUT` | [optional] Connection pool acquisition timeout. | `10s` | `30s` | | `RESTAPI_BPMN_COPILOT_LOGIT_BIAS` | [optional] JSON object mapping token IDs to bias values (model-specific). | `{"123":-2,"456":3}` | `{}` | | `RESTAPI_BPMN_COPILOT_MAX_CONNECTIONS` | [optional] Maximum HTTP connections. | `300` | `200` | | `RESTAPI_BPMN_COPILOT_READ_TIMEOUT` | [optional] Read timeout per request. | `120s` | `60s` | ```yaml camunda.modeler.default-bpmn-copilot-llm-configuration: temperature: 0.2 # optional, default: 0.3 top-p: 0.90 # optional, default: 0.95 top-k: 100 # optional, default: 64 max-tokens: 4096 # optional, default: 8192 timeout: 45s # optional, default: 60s log-request: true # optional, default: false log-response: true # optional, default: false connection-acquisition-timeout: 10s # optional, default: 30s logit-bias: '{"123":-2,"456":3}' # optional, default: {} max-connections: 300 # optional, default: 200 read-timeout: 120s # optional, default: 60s ``` ### FEEL Copilot | Environment variable | Description | Example value | Default value | | ----------------------------------------------------- | ------------------------------------------------------------------------- | -------------------- | ------------- | | `RESTAPI_FEEL_COPILOT_TEMPERATURE` | [optional] Sampling temperature. | `0.2` | `0.3` | | `RESTAPI_FEEL_COPILOT_TOP_P` | [optional] Nucleus sampling probability. | `0.90` | `0.95` | | `RESTAPI_FEEL_COPILOT_TOP_K` | [optional] Top-K sampling (if supported by the model). | `100` | `64` | | `RESTAPI_FEEL_COPILOT_MAX_TOKENS` | [optional] Maximum new tokens per response. | `4096` | `8192` | | `RESTAPI_FEEL_COPILOT_TIMEOUT` | [optional] Overall request timeout. | `45s` | `60s` | | `RESTAPI_FEEL_COPILOT_LOG_REQUEST` | [optional] Log raw requests (not recommended in production). | `true` | `false` | | `RESTAPI_FEEL_COPILOT_LOG_RESPONSE` | [optional] Log raw responses (not recommended in production). | `true` | `false` | | `RESTAPI_FEEL_COPILOT_CONNECTION_ACQUISITION_TIMEOUT` | [optional] Connection pool acquisition timeout. | `10s` | `30s` | | `RESTAPI_FEEL_COPILOT_LOGIT_BIAS` | [optional] JSON object mapping token IDs to bias values (model-specific). | `{"123":-2,"456":3}` | `{}` | | `RESTAPI_FEEL_COPILOT_MAX_CONNECTIONS` | [optional] Maximum HTTP connections. | `300` | `200` | | `RESTAPI_FEEL_COPILOT_READ_TIMEOUT` | [optional] Read timeout per request. | `120s` | `60s` | ```yaml camunda.modeler.default-feel-copilot-llm-configuration: temperature: 0.2 # optional, default: 0.3 top-p: 0.90 # optional, default: 0.95 top-k: 100 # optional, default: 64 max-tokens: 4096 # optional, default: 8192 timeout: 45s # optional, default: 60s log-request: true # optional, default: false log-response: true # optional, default: false connection-acquisition-timeout: 10s # optional, default: 30s logit-bias: '{"123":-2,"456":3}' # optional, default: {} max-connections: 300 # optional, default: 200 read-timeout: 120s # optional, default: 60s ``` ### Form Copilot | Environment variable | Description | Example value | Default value | | ----------------------------------------------------- | ------------------------------------------------------------------------- | -------------------- | ------------- | | `RESTAPI_FORM_COPILOT_TEMPERATURE` | [optional] Sampling temperature. | `0.2` | `0.3` | | `RESTAPI_FORM_COPILOT_TOP_P` | [optional] Nucleus sampling probability. | `0.90` | `0.95` | | `RESTAPI_FORM_COPILOT_TOP_K` | [optional] Top-K sampling (if supported by the model). | `100` | `64` | | `RESTAPI_FORM_COPILOT_MAX_TOKENS` | [optional] Maximum new tokens per response. | `4096` | `8192` | | `RESTAPI_FORM_COPILOT_TIMEOUT` | [optional] Overall request timeout. | `45s` | `60s` | | `RESTAPI_FORM_COPILOT_LOG_REQUEST` | [optional] Log raw requests (not recommended in production). | `true` | `false` | | `RESTAPI_FORM_COPILOT_LOG_RESPONSE` | [optional] Log raw responses (not recommended in production). | `true` | `false` | | `RESTAPI_FORM_COPILOT_CONNECTION_ACQUISITION_TIMEOUT` | [optional] Connection pool acquisition timeout. | `10s` | `30s` | | `RESTAPI_FORM_COPILOT_LOGIT_BIAS` | [optional] JSON object mapping token IDs to bias values (model-specific). | `{"123":-2,"456":3}` | `{}` | | `RESTAPI_FORM_COPILOT_MAX_CONNECTIONS` | [optional] Maximum HTTP connections. | `300` | `200` | | `RESTAPI_FORM_COPILOT_READ_TIMEOUT` | [optional] Read timeout per request. | `120s` | `60s` | ```yaml camunda.modeler.default-form-copilot-llm-configuration: temperature: 0.2 # optional, default: 0.3 top-p: 0.90 # optional, default: 0.95 top-k: 100 # optional, default: 64 max-tokens: 4096 # optional, default: 8192 timeout: 45s # optional, default: 60s log-request: true # optional, default: false log-response: true # optional, default: false connection-acquisition-timeout: 10s # optional, default: 30s logit-bias: '{"123":-2,"456":3}' # optional, default: {} max-connections: 300 # optional, default: 200 read-timeout: 120s # optional, default: 60s ``` ### AWS Bedrock :::warning When configuring AWS Bedrock, make sure the model is available in the provided AWS region. ::: | Environment Variable | Description | Example Value | | --------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------- | | `RESTAPI_COPILOT_AWS_DEFAULT_MODEL_ID` | Default model ID for AWS Bedrock provider. | `anthropic.claude-3-5-sonnet-20240620-v1:0` | | `RESTAPI_COPILOT_AWS_REGION` | AWS region for Bedrock. | `us-east-1` | | `RESTAPI_BPMNCOPILOT_ACCESS_KEY_ID` | AWS access key ID for Bedrock (if not using instance or role credentials). | `AKIA...` | | `RESTAPI_BPMNCOPILOT_SECRET_ACCESS_KEY` | AWS secret access key for Bedrock (if not using instance or role credentials). | `wJalrXUtnFEMI/K7MDENG/bPxRfiCY...` | ```yaml camunda.modeler.copilot.providers.bedrock: default-model-id: anthropic.claude-3-5-sonnet-20240620-v1:0 region: us-east-1 access-key-id: AKIA... # optional, if not using instance or role credentials secret-access-key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCY..." # optional, if not using instance or role credentials ``` ### OpenAI :::note This configuration applies to OpenAI and OpenAI-compatible providers. ::: Provide exactly one of the following: - An API key for OpenAI's public API (no custom endpoint needed). - A custom endpoint for OpenAI-compatible providers or proxies. For OpenAI-compatible providers, you can authenticate with: - A bearer token. - Username and password (Basic authentication). - Custom authentication headers. When using the Bring your own model option in Self-Managed, results may vary depending on your chosen LLM's capabilities. If a weaker or smaller model is used, it may fail to generate a valid BPMN XML. In such cases, the Copilot library attempts automatic repair up to three times. If those attempts fail, the system will return an empty XML and an optional chat message instead of a model. :::tip Camunda recommends using a stronger model, such as GPT-4 or comparable, for reliable BPMN generation. ::: | Environment variable | Description | Example value | | ------------------------------------------ | --------------------------------------------------------------------------------------------- | ------------------------------------ | | `RESTAPI_COPILOT_OPEN_AI_DEFAULT_MODEL_ID` | Default model ID for OpenAI provider. | `gpt-4.1` | | `RESTAPI_FEELCOPILOT_API_KEY` | [conditionally required] API key for OpenAI public API. | `sk-live-********` | | `RESTAPI_COPILOT_OPENAI_ENDPOINT` | [conditionally required] Custom endpoint for OpenAI-compatible APIs (proxies or self-hosted). | `https://my-proxy.example.com/v1` | | `RESTAPI_COPILOT_OPENAI_BEARER` | [optional] Bearer token header to use instead of `api-key` with compatible gateways. | `my-shared-bearer-token` | | `RESTAPI_COPILOT_OPENAI_USERNAME` | [optional] Username to authenticate with an OpenAI-compatible gateway. | `api_user` | | `RESTAPI_COPILOT_OPENAI_PASSWORD` | [optional] Password to authenticate with an OpenAI-compatible gateway. | `s3cr3t` | | `RESTAPI_COPILOT_OPENAI_HEADERS` | [optional] Extra HTTP headers as a JSON map (string). | `{"X-Org":"camunda","X-Trace":"on"}` | ```yaml camunda.modeler.copilot.openai: default-model-id: gpt-4.1 api-key: sk-live-******** # conditionally required endpoint: https://my-proxy.example.com/v1 # conditionally required bearer: my-shared-bearer-token # optional username: api_user # optional password: s3cr3t # optional headers: '{"X-Org":"camunda","X-Trace":"on"}' # optional ``` ### Azure OpenAI | Environment variable | Description | Example value | | ------------------------------------------------ | ------------------------------------------------- | ---------------------------------- | | `RESTAPI_COPILOT_AZURE_OPEN_AI_DEFAULT_MODEL_ID` | Default model (deployment name) for Azure OpenAI. | `gpt-4o` | | `RESTAPI_COPILOT_AZURE_OPENAI_API_KEY` | Azure OpenAI API key. | `az-aoai-key-**\*\*\*\***` | | `RESTAPI_COPILOT_AZURE_OPENAI_ENDPOINT` | Azure OpenAI endpoint. | `https://my-aoai.openai.azure.com` | ```yaml camunda.modeler.copilot.azure-open-ai: default-model-id: gpt-4o api-key: "az-aoai-key-***" endpoint: https://my-aoai.openai.azure.com ``` ### Azure AI :::note Azure AI supports authentication with an API key or Microsoft Entra ID (formerly Azure AD) using the OAuth 2.0 client credentials flow. ::: | Environment variable | Description | Example value | | ------------------------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | | `RESTAPI_COPILOT_AZURE_AI_DEFAULT_MODEL_ID` | Default model for Azure AI (Inference). | `gpt-4o-mini` | | `RESTAPI_COPILOT_AZURE_AI_ENDPOINT` | Endpoint for Azure AI (Inference). Use the endpoint from `Azure AI Inference SDK`. | `https://********-resource.cognitiveservices.azure.com/openai/deployments/gpt-4o` | | `RESTAPI_COPILOT_AZURE_AI_API_KEY` | [conditionally required] API key for Azure AI (alternative to OAuth credentials). | `az-ai-key-**\*\*\*\***` | | `RESTAPI_COPILOT_AZURE_AI_CLIENT_ID` | [conditionally required] Azure AI OAuth client ID. | `00000000-0000-0000-0000-000000000000` | | `RESTAPI_COPILOT_AZURE_AI_CLIENT_SECRET` | [conditionally required] Azure AI OAuth client secret. | `**\*\*\*\***` | | `RESTAPI_COPILOT_AZURE_AI_TENANT_ID` | [conditionally required] Azure AD tenant ID for OAuth. | `11111111-2222-3333-4444-555555555555` | | `RESTAPI_COPILOT_AZURE_AI_AUTHORITY_HOST` | [conditionally required] Authority host for Azure OAuth. | `https://login.microsoftonline.com` | ```yaml camunda.modeler.copilot.azure-ai: default-model-id: gpt-4o-mini endpoint: https://my-resource.cognitiveservices.azure.com/openai/deployments/gpt-4o api-key: "az-ai-key-***" # conditionally required (alternative to OAuth) client-id: 00000000-0000-0000-0000-000000000000 # conditionally required (OAuth) client-secret: "***" # conditionally required (OAuth) tenant-id: 11111111-2222-3333-4444-555555555555 # conditionally required (OAuth) authority-host: https://login.microsoftonline.com # conditionally required (OAuth) ``` ### Google Vertex AI | Environment variable | Description | Example value | | -------------------------------------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------- | | `RESTAPI_COPILOT_VERTEX_AI_DEFAULT_MODEL_ID` | Default model ID for Google Vertex AI (Gemini). | `gemini-1.5-pro-002` | | `RESTAPI_COPILOT_VERTEX_AI_PROJECT_ID` | GCP project ID for Vertex AI. | `my-gcp-project` | | `RESTAPI_COPILOT_VERTEX_AI_LOCATION` | Vertex AI location or region. | `us-central1` | | `RESTAPI_COPILOT_VERTEX_AI_CREDENTIALS_JSON` | Vertex AI service account JSON (string). | `{"type":"service_account","project_id":"my-proj","client_email":"...","private_key":"..."}` | ```yaml camunda.modeler.copilot.vertex-ai: default-model-id: gemini-1.5-pro-002 project-id: my-gcp-project location: us-central1 credentials-json: '{"type":"service_account","project_id":"my-proj","client_email":"...","private_key":"..."}' ``` ### Anthropic | Environment variable | Description | Example value | Default value | | ------------------------------------------------- | ------------------------------------------------------------------------ | ---------------------------- | ------------- | | `RESTAPI_COPILOT_ANTHROPIC_DEFAULT_MODEL_ID` | Default model ID for Anthropic. | `claude-3-5-sonnet-20240620` | - | | `RESTAPI_COPILOT_ANTHROPIC_API_KEY` | Anthropic API key. | `sk-ant-**\*\*\*\***` | - | | `RESTAPI_COPILOT_ANTHROPIC_CACHE_SYSTEM_MESSAGES` | [optional] Enable client-side caching of system messages (if supported). | `false` | `true` | | `RESTAPI_COPILOT_ANTHROPIC_CACHE_TOOLS` | [optional] Enable client-side caching of tool schemas (if supported). | `false` | `true` | ```yaml camunda.modeler.copilot.anthropic: default-model-id: claude-3-5-sonnet-20240620 api-key: "sk-ant-***" cache-system-messages: false # optional, default: true cache-tools: false # optional, default: true ``` ### Ollama | Environment variable | Description | Example value | | ----------------------------------------- | ------------------------------------------------------------- | ------------------------------------ | | `RESTAPI_COPILOT_OLLAMA_DEFAULT_MODEL_ID` | Default model ID for Ollama. | `llama3.1` | | `RESTAPI_COPILOT_OLLAMA_BASE_URL` | Ollama server base URL. | `http://localhost:11434` | | `RESTAPI_COPILOT_OLLAMA_HEADERS` | [optional] Extra HTTP headers to send as a JSON map (string). | `{"X-Org":"camunda","X-Trace":"on"}` | ```yaml camunda.modeler.copilot.ollama: default-model-id: llama3.1 base-url: http://localhost:11434 headers: '{"X-Org":"camunda","X-Trace":"on"}' # optional ``` ### Hugging Face | Environment variable | Description | Example value | Default value | | ----------------------------------------------- | ------------------------------------------------------------------------ | --------------------------------------------- | ------------- | | `RESTAPI_COPILOT_HUGGING_FACE_DEFAULT_MODEL_ID` | Default model ID for Hugging Face Inference. | `mistralai/Mixtral-8x7B-Instruct-v0.1` | - | | `RESTAPI_COPILOT_HUGGING_FACE_BASE_URL` | Base URL for Hugging Face Inference endpoint (if self-hosted or custom). | `https://api-inference.huggingface.co/models` | - | | `RESTAPI_COPILOT_HUGGING_FACE_ACCESS_TOKEN` | Access token for Hugging Face. | `hf\_**\*\*\*\***` | - | | `RESTAPI_COPILOT_HUGGING_FACE_WAIT_FOR_MODEL` | [optional] Wait for model to warm up before responding. | `false` | `true` | | `RESTAPI_COPILOT_HUGGING_FACE_RETURN_FULL_TEXT` | [optional] Return the full generated text (not just the completion). | `true` | `true` | ```yaml camunda.modeler.copilot.hugging-face: default-model-id: mistralai/Mixtral-8x7B-Instruct-v0.1 base-url: https://api-inference.huggingface.co/models access-token: "hf_***" wait-for-model: false # optional, default: true return-full-text: true # optional, default: true ``` --- ## Database(Configuration) This page describes advanced database connection configuration for Web Modeler. For a general setup guide, visit the [configuration overview](modeler-configuration.md#database). :::tip Need end-to-end guidance? For a unified setup guide covering provisioning, topology decisions, driver management, and backup strategies across both Orchestration Cluster and Web Modeler, see the [end-to-end RDBMS setup guide](/self-managed/concepts/databases/relational-db/rdbms-setup-guide.md). This guide is useful both when starting a new setup and when harmonizing existing component configurations. ::: Web Modeler supports multiple database vendors such as PostgreSQL, MySQL, MariaDB, and Microsoft SQL Server. You can choose the one that best fits your environment. | Database | Default driver included | Notes | | ---------- | ----------------------- | ---------------------------------------------------------------------- | | PostgreSQL | ✅ Yes | | | H2 | ✅ Yes | For development, testing, or evaluation only. | | MariaDB | ✅ Yes | Must use a case-sensitive collation. | | MySQL | ❌ No | Driver must be provided manually; must use a case-sensitive collation. | | MSSQL | ✅ Yes | Must use a case-sensitive collation. | | Oracle | ❌ No | Driver must be provided manually. | ## Configuring SSL for the database connection To configure SSL between Web Modeler and the database: - Modify the JDBC URL using `SPRING_DATASOURCE_URL` and add connection parameters. - Provide SSL certificates and keys to the `restapi` component, if required. Consult the [PostgreSQL documentation](https://jdbc.postgresql.org/documentation/ssl/) for details on SSL modes and their security guarantees. For a full list of available connection parameters, see the [PostgreSQL connection parameters reference](https://jdbc.postgresql.org/documentation/use/#connection-parameters/). Below are examples for common SSL configurations, ordered by increasing security. ### SSL mode `require` An SSL connection is established, but this mode remains vulnerable to person-in-the-middle attacks. Modify the JDBC URL as follows: ```bash jdbc:postgresql://[DB_HOST]:[DB_PORT]/[DB_NAME]?sslmode=require ``` No certificates are required for this mode. ### SSL mode `verify-full` Web Modeler verifies the server’s identity by checking its certificate. This mode prevents person-in-the-middle attacks. 1. Provide the root certificate that signed the server certificate: `myCA.crt -> ~/.postgresql/root.crt` 2. Modify the JDBC URL: ```bash jdbc:postgresql://[DB_HOST]:[DB_PORT]/[DB_NAME]?sslmode=verify-full ``` ### SSL mode `verify-full` with client certificates In this mode, both the server and Web Modeler authenticate each other using certificates. 1. Mount client certificates: - `myClientCertificate.pk8 -> ~/.postgresql/postgresql.pk8` - `myClientCertificate.crt -> ~/.postgresql/postgresql.crt` 2. Provide the root certificate: `myCA.crt -> ~/.postgresql/root.crt` 3. Modify the JDBC URL: ```bash jdbc:postgresql://[DB_HOST]:[DB_PORT]/[DB_NAME]?sslmode=verify-full ``` 4. Configure the database server to verify client certificates. See the [PostgreSQL SSL documentation](https://www.postgresql.org/docs/current/ssl-tcp.html). ## Running Web Modeler on Amazon Aurora PostgreSQL Web Modeler supports connecting to **Amazon Aurora PostgreSQL**. To connect, update the following environment variables: 1. Set the JDBC URL: ```bash SPRING_DATASOURCE_URL="jdbc:aws-wrapper:postgresql://[DB_HOST]:[DB_PORT]/[DB_NAME]" ``` 2. Set the driver class: ```bash SPRING_DATASOURCE_DRIVER_CLASS_NAME="software.amazon.jdbc.Driver" ``` For all available driver parameters, see the [AWS Advanced JDBC Driver documentation](https://github.com/awslabs/aws-advanced-jdbc-wrapper/wiki/UsingTheJdbcDriver#aws-advanced-jdbc-driver-parameters). ### AWS IAM authentication To enable IAM database authentication for Aurora PostgreSQL: 1. Modify the JDBC URL: ```bash SPRING_DATASOURCE_URL="jdbc:aws-wrapper:postgresql://[DB_HOST]:[DB_PORT]/[DB_NAME]?wrapperPlugins=iam" ``` 2. Set the username: ```bash SPRING_DATASOURCE_USERNAME="[IAM_DB_USER]" ``` The username must match a database user configured for IAM authentication as described in the [Amazon Aurora documentation](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/UsingWithRDS.IAMDBAuth.DBAccounts.html#UsingWithRDS.IAMDBAuth.DBAccounts.PostgreSQL). 3. Remove the password variable: ```bash unset SPRING_DATASOURCE_PASSWORD ``` When using IAM authentication, ensure Web Modeler has permission to generate IAM authentication tokens (for example, through an attached IAM role or access key). ## Using alternative database vendors ### H2 The H2 driver is included by default. To use a different driver, set `SPRING_DATASOURCE_DRIVER_CLASS_NAME` to the fully qualified class name of your driver. Otherwise, omit this variable. ```sh SPRING_DATASOURCE_URL="jdbc:h2:mem:[DB_NAME]" # See https://www.h2database.com/html/features.html SPRING_DATASOURCE_USERNAME="[DB_USER]" SPRING_DATASOURCE_PASSWORD="[DB_PASSWORD]" SPRING_DATASOURCE_DRIVER_CLASS_NAME="[YOUR_CUSTOM_DRIVER]" # Optional ``` ```yaml webModeler: restapi: externalDatabase: enabled: true url: "jdbc:h2:mem:[DB_NAME]" user: "[DB_USER]" password: "[DB_PASSWORD]" env: - name: SPRING_DATASOURCE_DRIVER_CLASS_NAME value: "[YOUR_CUSTOM_DRIVER]" ``` ```yaml spring: datasource: url: jdbc:h2:mem:[DB_NAME] username: [DB_USER] password: [DB_PASSWORD] driver-class-name: [YOUR_CUSTOM_DRIVER] # Optional ``` H2 is intended for local development or testing only, not for production environments. #### Custom schema By default, H2 uses the `PUBLIC` schema. To use a custom schema, add an initialization command to the JDBC URL: ```yaml jdbc:h2:mem:[DB_NAME];INIT=CREATE SCHEMA IF NOT EXISTS [CUSTOM_SCHEMA]\;SET SCHEMA [CUSTOM_SCHEMA] ``` ### MariaDB The MariaDB driver is provided by default, so no additional steps are necessary to provide the driver. To use a custom database driver, set `SPRING_DATASOURCE_DRIVER_CLASS_NAME` to the fully qualified class name of your driver. Otherwise, omit this variable. ```sh SPRING_DATASOURCE_URL="jdbc:mariadb://[DB_HOST]:[DB_PORT]/[DB_NAME]" SPRING_DATASOURCE_USERNAME="[DB_USER]" SPRING_DATASOURCE_PASSWORD="[DB_PASSWORD]" SPRING_DATASOURCE_DRIVER_CLASS_NAME="[YOUR_CUSTOM_DRIVER]" # Optional; omit to use default MariaDB driver ``` ```yaml webModeler: restapi: externalDatabase: enabled: true url: "jdbc:mariadb://[DB_HOST]:[DB_PORT]/[DB_NAME]" user: "[DB_USER]" password: "[DB_PASSWORD]" env: - name: SPRING_DATASOURCE_DRIVER_CLASS_NAME value: "[YOUR_CUSTOM_DRIVER]" ``` ```yaml spring: datasource: url: jdbc:mariadb://[DB_HOST]:[DB_PORT]/[DB_NAME] username: [DB_USER] password: [DB_PASSWORD] driver-class-name: [YOUR_CUSTOM_DRIVER] # Optional ``` #### Case sensitivity MariaDB uses case-insensitive collations by default. To enable case sensitivity, set the database collation to a case-sensitive one such as `utf8mb4_bin`. :::note If a case-insensitive collation is used, you may encounter unexpected behavior. For example, in [IDP extraction](/components/hub/workspace/modeler/idp/idp-unstructured-extraction.md#extract-fields), a field named `amount` and another named `Amount` would be treated as identical because the database does not distinguish between them. ::: ### MSSQL The MSSQL driver is provided by default, so no additional steps are required. To use a custom database driver, set `SPRING_DATASOURCE_DRIVER_CLASS_NAME` to the fully qualified class name of your driver. Otherwise, omit this variable. ```sh SPRING_DATASOURCE_URL="jdbc:sqlserver://[DB_HOST]:[DB_PORT];databaseName=[DB_NAME]" SPRING_DATASOURCE_USERNAME="[DB_USER]" SPRING_DATASOURCE_PASSWORD="[DB_PASSWORD]" SPRING_DATASOURCE_DRIVER_CLASS_NAME="[YOUR_CUSTOM_DRIVER]" # Optional; omit to use default MSSQL driver ``` ```yaml webModeler: restapi: externalDatabase: enabled: true url: "jdbc:sqlserver://[DB_HOST]:[DB_PORT];databaseName=[DB_NAME]" user: "[DB_USER]" password: "[DB_PASSWORD]" env: - name: SPRING_DATASOURCE_DRIVER_CLASS_NAME # Optional; omit to use default MSSQL driver value: "[YOUR_CUSTOM_DRIVER]" ``` ```yaml spring: datasource: url: jdbc:sqlserver://[DB_HOST]:[DB_PORT];databaseName=[DB_NAME] username: [DB_USER] password: [DB_PASSWORD] driver-class-name: [YOUR_CUSTOM_DRIVER] # Optional; omit to use default MSSQL driver ``` #### Case sensitivity MSSQL is case-insensitive by default. To enable case sensitivity, set the database collation to a case-sensitive one such as `Latin1_General_CS_AS`. Otherwise, you may encounter unexpected behavior. The only current restriction is that extraction fields in [IDP extraction](../../../../components/hub/workspace/modeler/idp/idp-unstructured-extraction.md#extract-fields) will not be case-sensitive. This means that if you have a field named `amount`, you cannot create another field named `Amount`, because the database treats them as the same identifier. #### Custom schema MSSQL supports custom schemas, but this is not configurable within Web Modeler. To use a custom schema, set the database user’s **default schema**. ### MySQL The MySQL driver is **not provided by default** in Camunda 8 distributions. You must download and provide it manually for the application to load. 1. Download the appropriate (platform-independent) MySQL driver: [https://dev.mysql.com/downloads/connector/j/](https://dev.mysql.com/downloads/connector/j/) 2. If you are using Docker or Kubernetes, ensure that the folder with the library is properly mounted as a volume at this location: `/driver-lib`. It will be automatically loaded by the application. To use a custom database driver, set `SPRING_DATASOURCE_DRIVER_CLASS_NAME` to the fully qualified class name of your driver. Otherwise, omit this variable. ```sh SPRING_DATASOURCE_URL="jdbc:mysql://[DB_HOST]:[DB_PORT]/[DB_NAME]" SPRING_DATASOURCE_USERNAME="[DB_USER]" SPRING_DATASOURCE_PASSWORD="[DB_PASSWORD]" SPRING_DATASOURCE_DRIVER_CLASS_NAME="[YOUR_CUSTOM_DRIVER]" # Optional; omit to use default MySQL driver ``` ```yaml webModeler: restapi: externalDatabase: enabled: true url: "jdbc:mysql://[DB_HOST]:[DB_PORT]/[DB_NAME]" user: "[DB_USER]" password: "[DB_PASSWORD]" env: - name: SPRING_DATASOURCE_DRIVER_CLASS_NAME # Optional; omit to use default MySQL driver value: "[YOUR_CUSTOM_DRIVER]" extraVolumeMounts: - name: mysql-driver mountPath: /driver-lib extraVolumes: - name: mysql-driver emptyDir: {} initContainers: - name: fetch-jdbc-drivers image: alpine:3.22.1 imagePullPolicy: "Always" command: [ "sh", "-c", "wget https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-j-9.5.0.tar.gz -O /driver-lib/mysql.tar.gz && tar -xzf /driver-lib/mysql.tar.gz -C /driver-lib --strip-components=1", ] volumeMounts: - name: mysql-driver mountPath: /driver-lib securityContext: runAsUser: 1001 ``` ```yaml spring: datasource: url: jdbc:mysql://[DB_HOST]:[DB_PORT]/[DB_NAME] username: [DB_USER] password: [DB_PASSWORD] driver-class-name: [YOUR_CUSTOM_DRIVER] # Optional; omit to use default MySQL driver ``` #### Case sensitivity MySQL usually uses **case-insensitive** collations by default. To enable case sensitivity, set the database collation to a case-sensitive one such as `utf8mb4_0900_as_cs`. Otherwise, you may encounter unexpected behavior. The only current restriction is that extraction fields in [IDP extraction](../../../../components/hub/workspace/modeler/idp/idp-unstructured-extraction.md#extract-fields) will not be case-sensitive. This means that if you have a field named `amount`, you cannot create another field named `Amount`, because the database treats them as the same identifier. ### Oracle The Oracle driver is **not provided by default** in Camunda 8 distributions. You must download and provide it manually for the application to load. 1. Download the appropriate Oracle driver: [https://www.oracle.com/database/technologies/appdev/jdbc-downloads.html](https://www.oracle.com/database/technologies/appdev/jdbc-downloads.html) 2. If you are using Docker or Kubernetes, ensure that the folder with the library is properly mounted as a volume at this location: `/driver-lib`. It will be automatically loaded by the application. To use a custom database driver, set `SPRING_DATASOURCE_DRIVER_CLASS_NAME` to the fully qualified class name of your driver. Otherwise, omit this variable. ```sh SPRING_DATASOURCE_URL="jdbc:oracle:thin:@//[DB_HOST]:[DB_PORT]/[DB_NAME]" SPRING_DATASOURCE_USERNAME="[DB_USER]" SPRING_DATASOURCE_PASSWORD="[DB_PASSWORD]" SPRING_DATASOURCE_DRIVER_CLASS_NAME="[YOUR_CUSTOM_DRIVER]" # Optional; omit to use default Oracle driver ``` ```yaml webModeler: restapi: externalDatabase: enabled: true url: "jdbc:oracle:thin:@//[DB_HOST]:[DB_PORT]/[DB_NAME]" user: "[DB_USER]" password: "[DB_PASSWORD]" env: - name: SPRING_DATASOURCE_DRIVER_CLASS_NAME # Optional; omit to use default Oracle driver value: "[YOUR_CUSTOM_DRIVER]" extraVolumeMounts: - name: oracle-driver mountPath: /driver-lib extraVolumes: - name: oracle-driver emptyDir: {} initContainers: - name: fetch-jdbc-drivers image: alpine:3.22.1 imagePullPolicy: "Always" command: [ "sh", "-c", "wget https://download.oracle.com/otn-pub/otn_software/jdbc/237/ojdbc17.jar -O /driver-lib/ojdbc.jar", ] volumeMounts: - name: oracle-driver mountPath: /driver-lib securityContext: runAsUser: 1001 ``` ```yaml spring: datasource: url: jdbc:oracle:thin:@//[DB_HOST]:[DB_PORT]/[DB_NAME] username: [DB_USER] password: [DB_PASSWORD] driver-class-name: [YOUR_CUSTOM_DRIVER] # Optional; omit to use default Oracle driver ``` --- ## Identity(Configuration) ## Using a different OpenID Connect (OIDC) authentication provider than Keycloak By default, Web Modeler uses Keycloak for providing authentication. You can use a different OIDC provider by following the steps described in the [OIDC connection guide](/self-managed/components/management-identity/configuration/connect-to-an-oidc-provider.md). --- ## Logging(Configuration) ## Logging configuration for the `restapi` component Web Modeler's `restapi` component uses [Apache Log4j 2](https://logging.apache.org/log4j/2.x/) for logging. By default, the `restapi` component logs to the Docker container's standard output. To change the default logging behavior, create a custom configuration file and let the `restapi` know of it by specifying the following environment variable: ```properties LOGGING_CONFIG=file:/full/path/to/custom-log4j2-spring.xml ``` Refer to [Spring Boot's logging documentation](https://docs.spring.io/spring-boot/how-to/logging.html#howto.logging.log4j) for more information on how to customize the `log4j2` configuration for specific use cases, such as logging to a file. Enabling `DEBUG` logging for the `restapi` component can be useful for troubleshooting purposes, e.g. for [debugging Zeebe connection issues](../troubleshooting/troubleshoot-zeebe-connection.md#how-can-i-debug-log-grpc--zeebe-communication). By default, Web Modeler's `restapi` component logs in a simple, readable format to the console. You can configure log levels, output formats, and appenders, and adjust logging dynamically at runtime. ### Changing log level at runtime You can adjust log levels dynamically using Spring Boot Actuator [`loggers`](https://docs.spring.io/spring-boot/api/rest/actuator/loggers.html) endpoints: ```bash curl 'http://localhost:8091/actuator/loggers/io.camunda' \ -i -X POST \ -H 'Content-Type: application/json' \ -d '{"configuredLevel":"DEBUG"}' ``` Replace `io.camunda` with the logger you want to adjust. :::note The base URL may differ depending on your environment configuration. The example above assumes execution from the same host running the Web Modeler `restapi` component. This URL is only callable via the [management port](https://docs.spring.io/spring-boot/reference/actuator/monitoring.html#actuator.monitoring.customizing-management-server-port), usually not publicly available. ::: ### Default Log4j2 configuration The default `log4j2-spring.xml` used by Web Modeler's `restapi` component is as follows: ```xml ``` :::note This is a simplified example. The actual `log4j2.xml` may include additional appenders, use different file paths, or have slightly different patterns. ::: ### Environment variables | Purpose | Variable | Component(s) | Example / Notes | | --------------------- | --------------------------- | ------------ | ----------------------------- | | Global log level | `CAMUNDA_LOG_LEVEL` | All | `DEBUG`, `INFO`, `WARN`, etc. | | Modeler package level | `CAMUNDA_MODELER_LOG_LEVEL` | RestApi | Overrides global level | ### JSON logging appenders | Appender | Description | Enable / Variable | | ------------------ | ---------------------------------------------------- | ------------------------------------------------------------------------------------- | | Console | Standard text output. | `CAMUNDA_MODELER_LOG_APPENDER=Console` | | Stackdriver (JSON) | JSON output for Google Cloud / Stackdriver. | `CAMUNDA_MODELER_LOG_APPENDER=Stackdriver` | | RollingFile | Writes logs to a rotating file, disabled by default. | `CAMUNDA_LOG_FILE_APPENDER_ENABLED=true` + `CAMUNDA_MODELER_LOG_APPENDER=RollingFile` | #### JSON structure When using the `Stackdriver` appender, this is the structure of the entries : | Field | Type | Description | | --------------------------------------- | ----------------- | ---------------------------------------------------------------------------------- | | `timestamp` | string (ISO-8601) | Log event timestamp in UTC using pattern `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'`. | | `severity` | string | Normalized log severity mapped from Log4j level (e.g., TRACE→DEBUG, WARN→WARNING). | | `message` | string | The log message, stringified by the message resolver. | | `logging.googleapis.com/sourceLocation` | object | Metadata describing the location in source code where the log originated. | | ├─ `file` | string | File name where the logging call occurred. | | ├─ `line` | number | Line number in the source file. | | └─ `function` | string | Fully qualified class and method name (via `%C.%M`). | | `logging.googleapis.com/labels` | object (MDC) | Key/value pairs from the Mapped Diagnostic Context. | | `threadContext` | object | Information about the thread producing the log event. | | ├─ `id` | number | Thread ID. | | ├─ `name` | string | Thread name. | | └─ `priority` | number | Thread priority. | | `loggerName` | string | The logger’s name (typically the class name). | | `exception` | string (optional) | Stringified stack trace of any thrown exception. | | `correlationId` | string (optional) | Value of the MDC entry `correlationId`, if present. | See the following example: ```json { "timestamp": "2025-11-20T10:55:57.885Z", "severity": "INFO", "message": "Example log message", "logging.googleapis.com/sourceLocation": { "file": "RequestLoggingFilter.java", "line": 91, "function": "io.camunda.modeler.util.logging.RequestLoggingFilter.customAfterRequest" }, "logging.googleapis.com/labels": { "correlationId": "04284456-b95b-4121-a54b-6c48be6d3afd" }, "threadContext": { "id": 55, "name": "http-nio-8081-exec-1", "priority": 5 }, "loggerName": "io.camunda.modeler.util.logging.RequestLoggingFilter", "correlationId": "04284456-b95b-4121-a54b-6c48be6d3afd" } ``` ### Pattern layout The default layout displays **time only**, thread name, MDC context, log level, logger name, and message. #### Example pattern ```perl %d{HH:mm:ss.SSS} [%t] %notEmpty{[%X] }%-5level %logger{36} - %msg%n ``` | Feature | Pattern | | ------------------- | ---------------------------------- | | Timestamp | Time only | | Logger name | Package initials + full class name | | Newline after level | No | | Tab before logger | No | ### Client log level The `restapi` component also serves the client application running in the browser. To control the verbosity of the client logs, adjust the environment variable `LOG_LEVEL_CLIENT`. ```properties LOG_LEVEL_CLIENT=DEBUG ``` :::info For `LOG_LEVEL_*` options, see [understanding log levels](/self-managed/operational-guides/monitoring/log-levels.md#understanding-log-levels). ::: ## Logging configuration for the `websocket` component By default, the `websocket` component logs to the Docker container's standard output. ### Logging to a file To enable additional log output to a file, follow these steps: 1. Mount a volume to the directory `/var/www/html/storage/logs`. The logs will be written to a file named `laravel.log` located inside this directory. 2. Adjust the following environment variable: ```properties LOG_CHANNEL=single ``` --- ## Web Modeler Configuration Web Modeler Self-Managed consists of two components: [`restapi`](#configuration-of-the-restapi-component) and [`websocket`](#configuration-of-the-websocket-component). Each component is configured separately as described below. - The `restapi` component is a Spring Boot application. Its configuration is stored in a YAML file (`application.yml`) by default. All Web Modeler-specific settings are prefixed with `camunda.modeler`. - The `websocket` (PHP/Laravel) component is configured via environment variables. :::note Configuration methods The two components support configuration through environment variables. For the `restapi` component, environment variables can be used as an alternative to `application.yml` following [Spring Boot conventions](https://docs.spring.io/spring-boot/reference/features/external-config.html#features.external-config.typesafe-configuration-properties.relaxed-binding.environment-variables): convert the property to uppercase, remove any dashes, and replace any delimiters (`.`) with `_`. For example, the property `camunda.modeler.clusters[0].name` is represented by the environment variable `CAMUNDA_MODELER_CLUSTERS_0_NAME`. If you are using the Camunda 8 Helm chart, read more about the different configuration options in the chart's [Helm chart values documentation](https://artifacthub.io/packages/helm/camunda/camunda-platform#webmodeler-parameters). You can pass environment variables to each component via `webModeler.restapi.env` and `webModeler.websocket.env` in your `values.yaml`. ::: For a working example configuration showing how the components are correctly wired together, see the [Docker Compose file for Web Modeler](/self-managed/quickstart/developer-quickstart/docker-compose.md). ## Licensing ## Configuration of the `restapi` component As a Spring Boot application, the `restapi` component supports any standard [Spring configuration](https://docs.spring.io/spring-boot/reference/features/external-config.html) method. The examples below show configuration in two formats: - **Environment variables** – suitable for Docker Compose or direct shell usage. - **`application.yml`** – the native Spring Boot configuration file format. :::tip Passing JVM options When running the `restapi` component in a container (Docker / Kubernetes), use the `JAVA_TOOL_OPTIONS` environment variable to pass JVM arguments, for example for trust store settings or proxy configuration. ::: ### General | Environment variable | Description | Example value | Default value | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ------------- | | `RESTAPI_SERVER_URL` | URL at which users access Web Modeler in the browser (used to construct redirect URLs in the client-side login flow as well as links in notification emails). | `https://modeler.example.com`,`https://example.com/modeler` | - | | `SERVER_SERVLET_CONTEXTPATH` | [optional]Context path of the URL. Must be set if `RESTAPI_SERVER_URL` does not point to the root path of a (sub-)domain. | `/modeler` | - | | `SERVER_HTTPS_ONLY` | [optional]Enforce the usage of HTTPS when users access Web Modeler (by redirecting from `http://` to `https://`). | `true` | `true` | ```yaml camunda.modeler.server: url: https://modeler.example.com # or https://example.com/modeler https-only: true # optional, default: true server: servlet: context-path: /modeler # optional; required if server-url does not point to root path ``` ### Clusters Clusters must be configured using the following options to access the cluster from within Web Modeler. If no clusters are configured, you will not be able to perform any actions that require a cluster (for example, deploy, start an instance, or Play a process). The Camunda 8 [Helm](/self-managed/deployment/helm/install/quick-install.md) and [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md) distributions provide a local Zeebe cluster configured by default. To add additional clusters, increment the `0` value for each entry (for example `clusters[1]` or `CAMUNDA_MODELER_CLUSTERS_1_NAME`). :::info Cluster version The available configuration options depend on the version of the cluster: - [Common configuration (all cluster versions)](#common-configuration-all-cluster-versions) - [Additional configuration for cluster versions >= 8.8](#additional-configuration-for-cluster-versions--88) - [Additional configuration for cluster versions < 8.8](#additional-configuration-for-cluster-versions--88-1) ::: #### Common configuration (all cluster versions) | Environment variable | Description | Example value | | ------------------------------------------- | --------------------------------------------------------------------------------- | ---------------- | | `CAMUNDA_MODELER_CLUSTERS_0_ID` | A unique identifier to use for your cluster. | `test-cluster-1` | | `CAMUNDA_MODELER_CLUSTERS_0_NAME` | The name of your cluster. | `Test Cluster 1` | | `CAMUNDA_MODELER_CLUSTERS_0_VERSION` | The Camunda version used by this cluster. | `8.8.0` | | `CAMUNDA_MODELER_CLUSTERS_0_AUTHENTICATION` | The [authentication](#available-authentication-methods) to use with your cluster. | `BEARER_TOKEN` | ```yaml camunda.modeler.clusters: - id: test-cluster-1 name: Test Cluster 1 version: 8.8.0 authentication: BEARER_TOKEN # See "Available authentication methods" below ``` #### Additional configuration for cluster versions >= 8.8 | Environment variable | Description | Example value | | --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | | `CAMUNDA_MODELER_CLUSTERS_0_URL_GRPC` | [Internal or external](#notes-on-host-names-and-port-numbers) address where the [Zeebe gRPC API](/apis-tools/zeebe-api/grpc.md) can be reached. | `grpc://camunda:26500`,`grpcs://camunda.example.com:26500` | | `CAMUNDA_MODELER_CLUSTERS_0_URL_REST` | [Internal or external](#notes-on-host-names-and-port-numbers) address where the cluster's REST APIs can be reached. Used as the base URL for requests to the [Orchestration Cluster API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). | `http://camunda:8080`,`https://camunda.example.com` | | `CAMUNDA_MODELER_CLUSTERS_0_URL_WEBAPP` | [External](#notes-on-host-names-and-port-numbers) address where the cluster's web applications can be reached in a browser. | `https://camunda.example.com` | | `CAMUNDA_MODELER_CLUSTERS_0_AUTHORIZATIONS_ENABLED` | Indicates if [authorizations are enabled](/self-managed/components/orchestration-cluster/admin/overview.md#enable-api-authentication-and-authorizations) for the cluster. If `true`, users will see a hint when they deploy from Web Modeler. | `true` | ```yaml camunda.modeler.clusters: - # ...common configuration from above url: grpc: "grpc://camunda:26500" # or grpcs://camunda.example.com:26500 rest: "http://camunda:8080" # or https://camunda.example.com webapp: "https://camunda.example.com" authorizations: enabled: true ``` #### Additional configuration for cluster versions < 8.8 | Environment variable | Description | Example value | | ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | | `CAMUNDA_MODELER_CLUSTERS_0_URL_ZEEBE_GRPC` | [Internal or external](#notes-on-host-names-and-port-numbers) address where the [Zeebe gRPC API](/versioned_docs/version-8.7/apis-tools/zeebe-api/grpc.md) can be reached. | `grpc://camunda-zeebe-gateway:26500`,`grpcs://zeebe.example.com:26500` | | `CAMUNDA_MODELER_CLUSTERS_0_URL_ZEEBE_REST` | [Internal or external](#notes-on-host-names-and-port-numbers) address where the [Camunda 8 REST API](/versioned_docs/version-8.7/apis-tools/camunda-api-rest/camunda-api-rest-overview.md) can be reached. | `http://camunda-zeebe-gateway:8080`,`https://zeebe.example.com` | | `CAMUNDA_MODELER_CLUSTERS_0_URL_OPERATE` | [Internal or external](#notes-on-host-names-and-port-numbers) address where the [Operate REST API](/versioned_docs/version-8.7/apis-tools/operate-api/overview.md) can be reached. | `http://camunda-operate:80`,`https://operate.example.com` | | `CAMUNDA_MODELER_CLUSTERS_0_URL_TASKLIST` | [Internal or external](#notes-on-host-names-and-port-numbers) address where the [Tasklist REST API](/versioned_docs/version-8.7/apis-tools/tasklist-api-rest/tasklist-api-rest-overview.md) can be reached. | `http://camunda-tasklist:80`,`https://tasklist.example.com` | ```yaml camunda.modeler.clusters: - # ...common configuration from above url: zeebe-grpc: "grpc://camunda-zeebe-gateway:26500" zeebe-rest: "http://camunda-zeebe-gateway:8080" operate: "http://camunda-operate:80" tasklist: "http://camunda-tasklist:80" ``` #### Available authentication methods | Method | Description | When to use? | | -------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `BEARER_TOKEN` | Web Modeler sends the authenticated user's token in the `Authorization` header with every request to the cluster. | **Cluster version >= 8.8**The cluster uses [OIDC authentication](/self-managed/components/orchestration-cluster/admin/connect-external-identity-provider.md) with the same identity provider as Web Modeler._Note_: You need to ensure that the cluster [accepts Web Modeler's token audience](/self-managed/components/orchestration-cluster/admin/connect-external-identity-provider.md#step-4-configure-the-oidc-connection-details).**Cluster version < 8.8**The cluster uses [Camunda Identity-based authentication](/versioned_docs/version-8.7/self-managed/zeebe-deployment/security/client-authorization.md#camunda-identity-authorization) and the external identity provider supports access tokens with multiple audiences (example provider: Keycloak)._Note_: For the token to be accepted by the different cluster components, it must contain each component's audience. | | `BASIC` | Web Modeler sends a username and password with every request to the cluster. The credentials have to be provided by the user in the UI. | **Cluster version >= 8.8**The cluster uses Basic authentication.**Cluster version < 8.8**not supported | | `NONE` | Web Modeler does not send any authentication information. | **Cluster version >= 8.8**The cluster API is [configured as unprotected](/self-managed/components/orchestration-cluster/admin/overview.md#enable-api-authentication-and-authorizations) and can be used without authentication.**Cluster version < 8.8**The authentication / token validation in the Zeebe Gateway is [disabled](/versioned_docs/version-8.7/self-managed/zeebe-deployment/security/client-authorization.md#camunda-identity-authorization). | ### Database Web Modeler currently supports PostgreSQL, Oracle, Microsoft SQL Server (MSSQL), MySQL, MariaDB, and H2 as persistent data storage. :::info Oracle and MySQL driver The Oracle and MySQL drivers are not provided by default and must be downloaded and supplied for the application to load. Refer to the [Oracle](database.md#oracle) and [MySQL](database.md#mysql) database configuration section for details. ::: | Environment variable | Description | Example value | | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------- | | `SPRING_DATASOURCE_URL` | JDBC URL of the database | `jdbc:postgresql://postgres.example.com:5432/modeler-db` | | `SPRING_DATASOURCE_USERNAME` | Database user name | `modeler-user` | | `SPRING_DATASOURCE_PASSWORD` | Database user password | \*\*\* | | `SPRING_DATASOURCE_DRIVER_CLASS_NAME` | [optional]Java class name of the database driver | `software.amazon.jdbc.Driver` | | `SPRING_DATASOURCE_HIKARI_SCHEMA` | [optional; only supported for PostgreSQL]Database schema.Defaults to the default schema of the database user (usually `public`) if not set.Refer to the [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS) for naming restrictions. | `custom_schema` | ```yaml spring: datasource: url: jdbc:postgresql://postgres.example.com:5432/modeler-db username: modeler-user password: "***" # driver-class-name: software.amazon.jdbc.Driver # optional hikari: schema: custom_schema # optional; only supported for PostgreSQL ``` Refer to the [Advanced Database Configuration Guide](./database.md) for additional details on how to configure Web Modeler's database connection. ### SMTP / email Web Modeler requires an SMTP server to send notification emails to users. | Environment variable | Description | Example value | Default value | | --------------------------- | -------------------------------------------------------------------------- | --------------------- | ------------- | | `RESTAPI_MAIL_HOST` | SMTP server host name | `smtp.example.com` | - | | `RESTAPI_MAIL_PORT` | SMTP server port | `587` | - | | `RESTAPI_MAIL_USER` | [optional]SMTP user name | `modeler-user` | - | | `RESTAPI_MAIL_PASSWORD` | [optional]SMTP user password | \*\*\* | - | | `RESTAPI_MAIL_ENABLE_TLS` | Enforce TLS encryption for SMTP connections (using STARTTLS). | `true` | `true` | | `RESTAPI_MAIL_FROM_ADDRESS` | Email address used as the sender of emails sent by Web Modeler. | `noreply@example.com` | - | | `RESTAPI_MAIL_FROM_NAME` | [optional]Name displayed as the sender of emails sent by Web Modeler. | `Camunda` | `Camunda` | ```yaml camunda.modeler.mail: from-address: noreply@example.com from-name: Camunda # optional, default: Camunda spring: mail: host: smtp.example.com port: 587 user: modeler-user # optional password: "***" # optional properties: mail.smtp.auth: true # set to true if user and password are provided mail.smtp.starttls.enable: true # default: true; set to false to disable STARTTLS encryption mail.smtp.starttls.required: true # default: true; set to false to avoid enforcing STARTTLS ``` ### WebSocket Web Modeler uses a [WebSocket server](#configuration-of-the-websocket-component) to send events (e.g. "file updated", "comment added", "user opened diagram") between the backend and the client application in the browser. This enables features like real-time notifications and immediate UI updates. | Environment variable | Description | Example value | Default value | | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | ------------- | | `RESTAPI_PUSHER_HOST` | [Internal](#notes-on-host-names-and-port-numbers) host name of the WebSocket server. | `modeler-websockets` | - | | `RESTAPI_PUSHER_PORT` | [Internal](#notes-on-host-names-and-port-numbers) port number of the WebSocket server. | `8060` | `8060` | | `RESTAPI_PUSHER_APP_ID` | _must be the same as_ [`PUSHER_APP_ID`](#configuration-of-the-websocket-component) | `web-modeler` | - | | `RESTAPI_PUSHER_KEY` | _must be the same as_ [`PUSHER_APP_KEY`](#configuration-of-the-websocket-component) | \*\*\* | - | | `RESTAPI_PUSHER_SECRET` | _must be the same as_ [`PUSHER_APP_SECRET`](#configuration-of-the-websocket-component) | \*\*\* | - | | `CLIENT_PUSHER_HOST` | [External](#notes-on-host-names-and-port-numbers) host name on which the Web Modeler client accesses the WebSocket server from the browser. | `ws.example.com` | - | | `CLIENT_PUSHER_PORT` | [External](#notes-on-host-names-and-port-numbers) port number on which the Web Modeler client accesses the WebSocket server from the browser. | `443` | `80` | | `CLIENT_PUSHER_PATH` | [optional]_must be the same as_ [`PUSHER_APP_PATH`](#configuration-of-the-websocket-component) | `/modeler-ws` | `/` | | `CLIENT_PUSHER_FORCE_TLS` | Enable TLS encryption for WebSocket connections initiated by the browser. | `true` | `false` | ```yaml camunda.modeler: pusher: host: modeler-websockets port: 8060 # default: 8060 app-id: web-modeler key: "***" secret: "***" client: host: ws.example.com port: 443 # default: 80 path: /modeler-ws # optional, default: / force-tls: true # default: false ``` ### Identity / Keycloak Web Modeler uses Keycloak as the default authentication provider (using OAuth 2.0 + OpenID Connect) and integrates with [Management Identity](/self-managed/components/management-identity/overview.md) for user management and authorization (see [Manage access and permissions](/self-managed/components/management-identity/access-management/access-management-overview.md)). | Environment variable | Description | Example value | Default value | | ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ------------------------ | | `CAMUNDA_IDENTITY_BASEURL` | [Internal](#notes-on-host-names-and-port-numbers) base URL of the Identity API (used to fetch user data). | `http://identity:8080` | - | | `CAMUNDA_MODELER_OAUTH2_TOKEN_USERNAMECLAIM` | ID token claim used to assign usernames. | `preferred_username` | `name` | | `CAMUNDA_MODELER_SECURITY_JWT_AUDIENCE_INTERNAL_API` | Expected value of the audience claim in user access tokens (used for JWT validation). | `web-modeler-api` | `web-modeler-api` | | `CAMUNDA_MODELER_SECURITY_JWT_AUDIENCE_PUBLIC_API` | Expected value of the audience claim in M2M access tokens required for [Web Modeler's API](/apis-tools/web-modeler-api/authentication.md?environment=self-managed) (used for JWT validation). | `web-modeler-public-api` | `web-modeler-public-api` | | `RESTAPI_OAUTH2_TOKEN_ISSUER_BACKEND_URL` | [optional][Internal](#notes-on-host-names-and-port-numbers) URL used to request Keycloak's [OpenID Provider Configuration](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig); if not set, `SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI` is used. | `http://keycloak:18080/auth/realms/camunda-platform` | - | | `SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI` | URL of the token issuer (used for JWT validation). | `https://keycloak.example.com/auth/realms/camunda-platform` | - | | `SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI` | [optional] URL of the JWK Set endpoint (used for JWT validation). Only necessary if URL cannot be derived from the OIDC configuration endpoint. | `https://keycloak.example.com/auth/realms/camunda-platform/protocol/openid-connect/certs` | - | | `SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWS_ALGORITHMS` | [optional] List of trusted JWS algorithms used for JWT validation. Only necessary if the algorithms cannot be derived from the JWK Set response. | `ES256` | - | | `OAUTH2_CLIENT_ID` | Client ID of the Web Modeler application configured in Identity. | `web-modeler` | - | | `OAUTH2_CLIENT_SCOPE` | [optional]OIDC scopes requested during authentication, determining what user information is included in the token. | `full` | `openid email profile` | | `OAUTH2_CLIENT_FETCH_REQUEST_CREDENTIALS` | [optional]Configuration whether credentials should be sent along with requests to the OIDC provider, see [documentation](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials#value). Use this if you are using a proxy that requires cookies. | `include` | - | ```yaml camunda: identity: base-url: http://identity:8080 issuer-backend-url: http://keycloak:18080/auth/realms/camunda-platform # optional modeler: security: jwt: issuer: backend-url: http://keycloak:18080/auth/realms/camunda-platform # optional audience: internal-api: web-modeler-api # default: web-modeler-api public-api: web-modeler-public-api # default: web-modeler-public-api oauth2: client-id: web-modeler client: fetch-request-credentials: include # optional scope: openid email profile # optional token.username-claim: name # optional, default: name spring: security: oauth2: resourceserver: jwt: issuer-uri: https://keycloak.example.com/auth/realms/camunda-platform jwk-set-uri: https://keycloak.example.com/auth/realms/camunda-platform/protocol/openid-connect/certs # optional jws-algorithms: ES256 # optional ``` :::note Helm behavior The `restapi` component default for `CAMUNDA_MODELER_OAUTH2_TOKEN_USERNAMECLAIM` is `name`. In Helm-based setups, OIDC configuration commonly uses `preferred_username`, so usernames may appear as email-style identifiers unless you explicitly set `CAMUNDA_MODELER_OAUTH2_TOKEN_USERNAMECLAIM=name` for the Web Modeler `restapi` environment. ::: Refer to the [advanced Identity configuration guide](./identity.md) for additional details on how to connect a custom OpenID Connect (OIDC) authentication provider. ### Camunda client Web Modeler uses the [Camunda Java client](/apis-tools/java-client/getting-started.md) to connect to Zeebe. To customize the client configuration, you can provide optional properties. | Environment variable | Description | Example value | Default Value | | ------------------------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------- | ---------------------------- | | `CAMUNDA_CA_CERTIFICATE_PATH` | [optional]Path to a root CA certificate to be used instead of the certificate in the default store. | `/path/to/certificate` | - | | `CAMUNDA_CLIENT_CONFIG_PATH` | [optional]Path to the client's OAuth credential cache. | `/path/to/credentials/cache.txt` | `$HOME/.camunda/credentials` | | `CAMUNDA_CLIENT_REQUESTTIMEOUT` | [optional]The request timeout used when communicating with a target Zeebe cluster. | `60000` | `10000` | | `CAMUNDA_AUTH_CONNECT_TIMEOUT` | [optional]The connection timeout for requests to the OAuth server. | `30000` | `5000` | | `CAMUNDA_AUTH_READ_TIMEOUT` | [optional]The data read timeout for requests to the OAuth server. | `30000` | `5000` | ```yaml camunda: ca-certificate-path: /path/to/certificate # optional client: config-path: /path/to/credentials/cache.txt # optional, default: $HOME/.camunda/credentials request-timeout: 60000 # optional, default: 10000 auth: connect-timeout: 30000 # optional, default: 5000 read-timeout: 30000 # optional, default: 5000 ``` For more details, [see the Zeebe connection troubleshooting section](/self-managed/components/hub/troubleshooting/troubleshoot-zeebe-connection.md). ### Logging | Environment variable | Description | Example value | Default value | | ----------------------------------- | -------------------------------------------------------------------- | --------------------------------------------- | ------------- | | `LOGGING_CONFIG` | [optional]Path to custom Log4j2 configuration. | `file:/full/path/to/custom-log4j2-spring.xml` | - | | `CAMUNDA_MODELER_LOG_LEVEL` | [optional]Defines the log level for the Web Modeler components. | `DEBUG` | `INFO` | | `CAMUNDA_LOG_FILE_APPENDER_ENABLED` | [optional]To enable logging to a file. | `true` | `false` | | `CAMUNDA_MODELER_LOG_APPENDER` | [optional]Defines which appender to use for logging. | `Stackdriver` | `Console` | | `LOG_LEVEL_CLIENT` | [optional]Log level for the client. | `DEBUG` | `WARN` | ```yaml camunda.modeler.client.logging.level: DEBUG # optional, default: WARN logging: config: file:/full/path/to/custom-log4j2-spring.xml # optional ``` Refer to the [advanced logging configuration guide](./logging.md#logging-configuration-for-the-restapi-component) for additional details on how to customize the `restapi` logging output. :::info - For `LOG_LEVEL_*` options, see [understanding log levels](/self-managed/operational-guides/monitoring/log-levels.md#understanding-log-levels). ::: ### SSL | Environment variable | Description | Example value | Default value | | ----------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------ | ------------- | | `SERVER_SSL_ENABLED` | [optional]Whether to enable SSL support. | `true` | `false` | | `SERVER_SSL_CERTIFICATE` | [optional]Path to a PEM-encoded SSL certificate file. | `file:/full/path/to/certificate.pem` | - | | `SERVER_SSL_CERTIFICATE_PRIVATE_KEY` | [optional]Path to a PEM-encoded private key file for the SSL certificate. | `file:/full/path/to/key.pem` | - | | `MANAGEMENT_SERVER_SSL_ENABLED` | [optional]Whether to enable SSL support for the management server routes. | `true` | `false` | | `MANAGEMENT_SERVER_SSL_CERTIFICATE` | [optional]Path to a PEM-encoded SSL certificate file. | `file:/full/path/to/certificate.pem` | - | | `MANAGEMENT_SERVER_SSL_CERTIFICATE_PRIVATE_KEY` | [optional]Path to a PEM-encoded private key file for the SSL certificate. | `file:/full/path/to/key.pem` | - | | `RESTAPI_PUSHER_SSL_ENABLED` | [optional]Whether to enable communication via SSL to the `websocket` component. | `true` | `false` | ```yaml server: ssl: enabled: true # optional, default: false certificate: file:/full/path/to/certificate.pem certificate-private-key: file:/full/path/to/key.pem management: server: ssl: enabled: true # optional, default: false certificate: file:/full/path/to/certificate.pem certificate-private-key: file:/full/path/to/key.pem camunda.modeler: pusher: ssl-enabled: true # optional, default: false; enables SSL to the websocket component ``` Refer to the [advanced SSL configuration guide](./modeler-ssl.md) for additional details on how to set up secure connections (incoming & outgoing) to the Web Modeler components. ### Monitoring and health probes {#monitoring} The `restapi` component is a Spring Boot application that includes the [Spring Boot Actuator](https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready), providing health check and metrics endpoints out of the box. These endpoints are served on a separate management port (default: `8091`). By default, Web Modeler uses the following actuator configuration: ```yaml management: server: port: 8091 endpoints: access: default: none web: exposure: include: health, info, prometheus, loggers base-path: / path-mapping: health: health prometheus: metrics endpoint: prometheus: access: read-only health: access: read-only probes: enabled: true # make readiness endpoint additionally available on main server port, so that it gets publicly exposed group: readiness: additional-path: "server:/health" info: access: read-only loggers: access: unrestricted info: git: enabled: false health: defaults: enabled: false metrics: distribution: percentiles: http.server.requests: - 0.5 - 0.9 - 0.99 ``` | Environment variable | Description | Example value | Default value | | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------- | -------------------- | ----------------------------------- | | `MANAGEMENT_SERVER_PORT` | [optional]Port for the management server (health and metrics endpoints). | `8091` | `8091` | | `MANAGEMENT_ENDPOINTS_ACCESS_DEFAULT` | [optional]Default access level for all actuator endpoints. | `read-only` | `none` | | `MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE` | [optional]Comma-separated list of actuator endpoints to expose over the web. | `health, prometheus` | `health, info, prometheus, loggers` | | `MANAGEMENT_ENDPOINTS_WEB_BASE_PATH` | [optional]Base path for all web-exposed actuator endpoints. | `/actuator` | `/` | | `MANAGEMENT_ENDPOINTS_WEB_PATH_MAPPING_HEALTH` | [optional]Custom path mapping for the health endpoint. | `/health` | `health` | | `MANAGEMENT_ENDPOINTS_WEB_PATH_MAPPING_PROMETHEUS` | [optional]Custom path mapping for the Prometheus endpoint. | `/prometheus` | `metrics` | | `MANAGEMENT_ENDPOINT_PROMETHEUS_ACCESS` | [optional]Access level for the Prometheus endpoint. | `unrestricted` | `read-only` | | `MANAGEMENT_ENDPOINT_HEALTH_ACCESS` | [optional]Access level for the health endpoint. | `unrestricted` | `read-only` | | `MANAGEMENT_ENDPOINT_HEALTH_PROBES_ENABLED` | [optional]Whether Kubernetes-style readiness and liveness probes are enabled. | `true` | `true` | | `MANAGEMENT_ENDPOINT_HEALTH_GROUP_READINESS_ADDITIONAL_PATH` | [optional]Expose the readiness probe on an additional path (e.g. on the main server port). | `server:/health` | `server:/health` | | `MANAGEMENT_ENDPOINT_INFO_ACCESS` | [optional]Access level for the info endpoint. | `unrestricted` | `read-only` | | `MANAGEMENT_ENDPOINT_LOGGERS_ACCESS` | [optional]Access level for the loggers endpoint. | `read-only` | `unrestricted` | | `MANAGEMENT_INFO_GIT_ENABLED` | [optional]Whether Git info is exposed via the info endpoint. | `true` | `false` | | `MANAGEMENT_HEALTH_DEFAULTS_ENABLED` | [optional]Whether default health indicators are enabled. | `true` | `false` | | `MANAGEMENT_METRICS_DISTRIBUTION_PERCENTILES_HTTP_SERVER_REQUESTS` | [optional]Comma-separated list of percentiles to publish for HTTP server request metrics. | `0.5, 0.9, 0.99` | `0.5, 0.9, 0.99` | #### Available endpoints | Endpoint | Description | | -------------------------------- | ------------------ | | `:8091/metrics` | Prometheus metrics | | `:8091/health/readiness` | Readiness probe | | `:8091/health/liveness` | Liveness probe | For more details, including Kubernetes probe configuration examples and `websocket` health endpoints, see the [Monitoring](../monitoring.md) page. ### Git Sync Web Modeler supports syncing files via [Git Sync](/components/hub/workspace/manage-projects/git-sync.md). Provide the base URL for your provider if you are using a self-hosted GitLab, GitHub, or Azure DevOps Server instance. | Provider | Environment variable | Description | Default value | | ------------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | | All providers | `CAMUNDA_MODELER_GITSYNC_MAXFILES` | Maximum number of allowed files for sync operations. | `50` | | All providers | `CAMUNDA_MODELER_GITSYNC_MAXINMEMORYSIZE` | Maximum memory size that can be processed by calls to the Git provider. This limits the maximum file size that can be synced. | `4MB` | | GitHub | `CAMUNDA_MODELER_GITSYNC_GITHUB_BASEURL` | The base URL of your self-hosted GitHub instance. | `https://api.github.com` | | GitLab | `CAMUNDA_MODELER_GITSYNC_GITLAB_BASEURL` | The base URL of your self-hosted GitLab instance. | `https://gitlab.com/api/v4` | | Azure DevOps | `CAMUNDA_MODELER_GITSYNC_AZURE_BASEURL` | The base URL of your self-hosted Azure DevOps Server instance. | `https://dev.azure.com` | | Azure DevOps | `CAMUNDA_MODELER_GITSYNC_AZURE_API_VERSION` | The Azure DevOps API versions to use. | `7.1` | | Azure DevOps | `CAMUNDA_MODELER_GITSYNC_AZURE_AUTHORITY_BASE_PATH` | URL used to access authentication and authorization services for Microsoft cloud identities. | `https://login.microsoftonline.com` | | Azure DevOps | `CAMUNDA_MODELER_GITSYNC_AZURE_SCOPE` | OAuth scope requested for Azure DevOps authentication. | `https://app.vssps.visualstudio.com/.default` | | Bitbucket | `CAMUNDA_MODELER_GITSYNC_BITBUCKET_BASEURL` | The base URL of Bitbucket Cloud. | `https://api.bitbucket.org/2.0/repositories` | ```yaml camunda.modeler: gitsync: max-files: 50 # default max-in-memory-size: 4MB # default github: base-url: https://api.github.com # default gitlab: base-url: https://gitlab.com/api/v4 # default azure: base-url: https://dev.azure.com # default api-version: "7.1" # default authority-base-path: https://login.microsoftonline.com # default scope: https://app.vssps.visualstudio.com/.default # default bitbucket: base-url: https://api.bitbucket.org/2.0/repositories # default ``` ### Feature flags | Environment variable | Description | Example value | Default value | | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | ------------- | | `PLAY_ENABLED` | [optional]Enables the [**Play** mode](../../../../components/hub/workspace/modeler/validation/play-your-process.md) in the BPMN editor, allowing users to test processes in a playground environment. | `true` | `true` | | `ZEEBE_BPMN_DEPLOYMENT_ENABLED` | [optional]Enables the [**Deploy** and **Run**](../../../../components/hub/workspace/modeler/run-or-publish-your-process.md) actions in the BPMN editor.When disabled, it prevents users from deploying and starting instances of processes via the UI. | `false` | `true` | | `ZEEBE_DMN_DEPLOYMENT_ENABLED` | [optional]Enables the [**Deploy**](../../../../components/hub/workspace/modeler/run-or-publish-your-process.md) action in the DMN editor.When disabled, it prevents users from deploying decisions via the UI. | `false` | `true` | | `MARKETPLACE_ENABLED` | [optional]Enables the integration of the [Camunda Marketplace](https://marketplace.camunda.com). If enabled, users can browse the Marketplace and download [resources](../../../../components/hub/workspace/modeler/modeling/camunda-marketplace.md) directly inside Web Modeler. | `false` | `true` | ```yaml camunda: modeler.feature: bpmn-deployment-enabled: true # default: true dmn-deployment-enabled: true # default: true play-enabled: true # default: true marketplace: enabled: true # default: true ``` ### Unstable configuration options These are unstable options that are not officially supported and may be removed without deprecation in future releases. They are intended for testing and feedback purposes only. | Environment variable | Description | Example value | Default value | | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | ------------- | | `CAMUNDA_MODELER_RESOURCE_IMPORT_ALLOW_PRIVATE_IP_ADDRESS` | Allow importing resources from a host that resolves to a private IP address. Enabling this option weakens server-side request forgery (SSRF) protections and can significantly increase security exposure. | `true` | `false` | ```yaml camunda.modeler.resource-import.allow-private-ip-address: true # default: false; enabling this option weakens server-side request forgery (SSRF) protections and can significantly increase security exposure. ``` ## Configuration of the `websocket` component The [WebSocket](https://en.wikipedia.org/wiki/WebSocket) server shipped with Web Modeler Self-Managed is based on the [laravel-websockets](https://laravel.com/docs/10.x/broadcasting#open-source-alternatives-php) open source package and implements the [Pusher Channels Protocol](https://pusher.com/docs/channels/library_auth_reference/pusher-websockets-protocol/). The `websocket` component is configured via environment variables. When using the Camunda Helm chart, you can pass these variables via `webModeler.websocket.env` in your `values.yaml`. See the [Helm chart values docs](https://artifacthub.io/packages/helm/camunda/camunda-platform#webmodeler-parameters) for all available configuration options. | Environment variable | Description | Example value | Default value | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- | ------------- | | `PUSHER_APP_ID` | ID of the single application/tenant configured for Web Modeler. | `web-modeler` | - | | `PUSHER_APP_KEY` | A unique key used for authentication. Provide a random alphanumeric string of at least 20 characters. | \*\*\* | - | | `PUSHER_APP_SECRET` | A unique secret used for authentication. Provide a random alphanumeric string of at least 20 characters. | \*\*\* | - | | `PUSHER_APP_PATH` | [optional]Base path of the WebSocket endpoint. Can be used to expose the endpoint on a sub path instead of the domain root (e.g. `https://example.com/modeler-ws`). | `/modeler-ws` | `/` | ### Logging | Environment variable | Description | Example value | Default Value | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------- | ------------- | | `LOG_CHANNEL` | [optional]Log channel driver, see [Laravel documentation](https://laravel.com/docs/10.x/logging#available-channel-drivers) | `single` | `stack` | Refer to the [Advanced Logging Configuration Guide](./logging.md#logging-configuration-for-the-websocket-component) for additional details on how to customize the `websocket` logging output. ### SSL | Environment variable | Description | Example value | Default Value | | ----------------------- | ------------------------------------------------------------------------------ | ------------------------------- | ------------- | | `PUSHER_SSL_CERT` | [optional]Path to a PEM-encoded SSL certificate file. | `/full/path/to/certificate.pem` | - | | `PUSHER_SSL_KEY` | [optional]Path to a PEM-encoded private key file for the SSL certificate. | `/full/path/to/key.pem` | - | | `PUSHER_SSL_PASSPHRASE` | [optional]Passphrase for the private key file. | `change-me` | - | Refer to the [advanced SSL configuration guide](./modeler-ssl.md) for additional details on how to set up secure connections (incoming & outgoing) to the Web Modeler components. ## Notes on host names and port numbers - _Internal_ refers to host names and port numbers that are only used inside a Docker Compose network or Kubernetes cluster for backend-to-backend communication. - _External_ refers to host names and port numbers that are exposed to the outside and can be reached from a web browser. --- ## SSL By default, communication between Web Modeler and Identity and the Web Modeler components is not encrypted, as it usually happens backend-to-backend within the same [Docker](/self-managed/deployment/docker/docker.md) network or [Kubernetes](/self-managed/deployment/helm/install/quick-install.md) cluster. However, you can enable TLS-encrypted communication by following the steps below (for example, if backend-to-backend communication is not possible in a custom Camunda 8 installation setup). ## Configuring secure connections to Identity ### Configure the Identity base URL For the `modeler-restapi` container, provide a URL that starts with `https://` (for example `https://identity.example.com`) as the base URL of the Identity instance. ``` CAMUNDA_IDENTITY_BASEURL=https://identity.example.com ``` ```yaml camunda.identity.base-url: https://identity.example.com ``` ## Configuring secure connections for Web Modeler components ### Configure `restapi` SSL certificate SSL can be configured declaratively by setting the respective properties offered by Spring Boot (make sure that the provided certificate path is accessible from the container, for example via a mounted volume): ``` RESTAPI_SERVER_URL=https://web-modeler.example.com SERVER_SSL_ENABLED=true SERVER_SSL_CERTIFICATE=file:/full/path/to/certificate.pem SERVER_SSL_CERTIFICATE_PRIVATE_KEY=file:/full/path/to/key.pem ``` Additionally, you can configure SSL separately for the management routes of the `restapi` component: ``` MANAGEMENT_SERVER_SSL_ENABLED=true MANAGEMENT_SERVER_SSL_CERTIFICATE=file:/full/path/to/certificate.pem MANAGEMENT_SERVER_SSL_CERTIFICATE_PRIVATE_KEY=file:/full/path/to/key.pem ``` ```yaml camunda.modeler.server.url: https://web-modeler.example.com server: ssl: enabled: true certificate: file:/full/path/to/certificate.pem certificate-private-key: file:/full/path/to/key.pem ``` Additionally, you can configure SSL separately for the management routes of the `restapi` component: ```yaml management: server: ssl: enabled: true certificate: file:/full/path/to/certificate.pem certificate-private-key: file:/full/path/to/key.pem ``` Refer to the [Spring Boot documentation](https://docs.spring.io/spring-boot/how-to/webserver.html#howto.webserver.configure-ssl) for more information on configuration options. #### Use secure connections between the `restapi` and `websocket` components To use secure connections between the `restapi` and `websocket` components: ``` RESTAPI_PUSHER_SSL_ENABLED=true ``` ```yaml camunda.modeler.pusher.ssl-enabled: true ``` ### Configure `websocket` SSL certificate SSL can be configured by setting the following environment variables (make sure that the provided certificate path is accessible from the container, e.g. via a mounted volume): ``` PUSHER_SSL_CERT=/full/path/to/certificate.pem PUSHER_SSL_KEY=/full/path/to/key.pem PUSHER_SSL_PASSPHRASE=your-passphrase ``` :::info Currently, there is no option to configure SSL for the `websocket` management routes separately from the application routes. ::: ## (Optional) Provide a custom certificate If you are using a custom (self-signed) TLS certificate for either the `restapi` or Identity, you need to make Web Modeler accept the certificate. For the `modeler-restapi` container: - Add the certificate to a custom Java trust store (using the [`keytool`](https://docs.oracle.com/en/java/javase/21/docs/specs/man/keytool.html) utility). - Configure the trust store as described in the [Zeebe connection troubleshooting guide](../troubleshooting/troubleshoot-zeebe-connection.md#provide-the-certificate-to-the-jvm-trust-store). --- ## Console SSL configuration By default, communication between Console, Identity, and other components is not encrypted, as it usually occurs backend-to-backend within the same [Docker](/self-managed/deployment/docker/docker.md) network or [Kubernetes](/self-managed/deployment/helm/install/quick-install.md) cluster. TLS-encrypted communication can be enabled by following the steps below (for example, if backend-to-backend communication is not possible in a custom Camunda 8 installation setup). ## Configure Console for secure connections Console can be configured using [environment variables](/self-managed/components/hub/configuration/configuration.md#environment-variables) to enable secure connections to both Console and Identity. ### Configure the Identity base URL Set the base URL (starting with `https://`) of your Identity instance using the following properties: | Environment variable | Example value | | ---------------------------- | ------------------------------ | | `KEYCLOAK_BASE_URL` | `https://identity.example.com` | | `KEYCLOAK_INTERNAL_BASE_URL` | `https://identity.example.com` | ### Configure SSL certificate Enable and configure SSL by setting the following properties: | Environment variable | Description | Example value | | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------ | | `SERVER_SSL_ENABLED` | To enable SSL, set to `true`. | `true` | | `SERVER_SSL_CERTIFICATE` | The path to a PEM-encoded SSL certificate file. Ensure the provided path is accessible from the container (for example, via a mounted volume). | `file:/full/path/to/certificate.pem` | | `SERVER_SSL_CERTIFICATE_PRIVATE_KEY` | The path to a PEM-encoded private key file for the SSL certificate. Ensure the provided path is accessible from the container (for example, via a mounted volume). | `file:/full/path/to/key.pem` | | `SERVER_SSL_PASSPHRASE` | _Optional_ A passphrase for the private key. | `passphrase` | SSL can be configured separately for the management routes using the `MANAGEMENT_` properties: | Environment variable | Description | Example value | | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------ | | `MANAGEMENT_SERVER_SSL_ENABLED` | To enable SSL, set to `true`. | `true` | | `MANAGEMENT_SERVER_SSL_CERTIFICATE` | The path to a PEM-encoded SSL certificate file. Ensure the provided path is accessible from the container (for example, via a mounted volume). | `file:/full/path/to/certificate.pem` | | `MANAGEMENT_SERVER_SSL_CERTIFICATE_PRIVATE_KEY` | The path to a PEM-encoded private key file for the SSL certificate. Ensure the provided path is accessible from the container (for example, via a mounted volume). | `file:/full/path/to/key.pem` | | `MANAGEMENT_SERVER_SSL_PASSPHRASE` | _Optional_ A passphrase for the private key. | `passphrase` | ## (Optional) Provide a custom certificate If you are using a custom (self-signed) TLS certificate in Console or Identity, configure Console to accept the certificate. Create a secret with the value of the key being the filename: ```bash kubectl create secret generic consoletls --from-file=console.crt=console.crt ``` Once the secret is created, it can be used in the values.yaml: ```yaml console: tls: enabled: true existingSecret: consoletls certKeyFilename: console.crt ``` --- ## Camunda Hub(Hub) Install and configure Camunda Hub components in your Self-Managed environment. ## Installation and configuration Camunda Hub is a unified platform that provides governance, observability, and delivery capabilities—all in one place. In this section, you will learn how to install, and configure Camunda Hub in your Self-Managed environment. --- ## Monitoring(Hub) Web Modeler Self-Managed consists of two components (`restapi` and `websocket`), each exposing their own endpoints for health monitoring and metrics collection. For configuration details, including the default Actuator settings and the management port, see the [Monitoring and health probes](./configuration/modeler-configuration.md#monitoring) section on the configuration page. ## Available endpoints ### `restapi` The `restapi` component is a Spring Boot application that includes the [Spring Boot Actuator](https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready), providing health check and metrics endpoints out of the box. These endpoints are served on a separate management port (default: `8091`). You can configure it with the [`management.server.port`](./configuration/modeler-configuration.md#monitoring) property or the `MANAGEMENT_SERVER_PORT` environment variable. | Endpoint | Description | | -------------------------------- | ------------------ | | `:8091/metrics` | Prometheus metrics | | `:8091/health/readiness` | Readiness probe | | `:8091/health/liveness` | Liveness probe | ### `websocket` The `websocket` component provides a basic health check endpoint on its default application port (`8060`). | Endpoint | Description | | ------------------ | ------------ | | `:8060/up` | Health check | :::note The `websocket` component does not expose a metrics endpoint. ::: ## Using probes in Kubernetes For details on setting Kubernetes probe parameters, see [Kubernetes configure probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes). **Readiness probe:** ```yaml readinessProbe: httpGet: path: /health/readiness port: 8091 initialDelaySeconds: 30 periodSeconds: 30 ``` **Liveness probe:** ```yaml livenessProbe: httpGet: path: /health/liveness port: 8091 initialDelaySeconds: 30 periodSeconds: 30 ``` --- ## Camunda Hub (Self-Managed) telemetry At Camunda, we strive to offer an excellent user experience at a high and stable level. On a strictly opt-in basis, we are looking to collect environment and usage data to improve your user experience further. These insights help us understand typical environment setups and product usage patterns and will be used to make informed product improvement decisions that benefit you. ## Design Camunda 8 Camunda Hub Self-Managed incorporates a telemetry mechanism designed to periodically report usage data to Camunda. This telemetry operates seamlessly in the background and is activated only when telemetry is explicitly enabled. The data collection mechanism is structured to function without interrupting or degrading the performance of the implemented processes. When enabled, the collected data is sent once every 24 hours via HTTPS. Once telemetry is enabled, a new Camunda Hub telemetry page will be enabled and will display collected information. This page can be used to view component metrics as a simple monitoring interface. Camunda Hub will collect a subset of metrics, which you can download and inspect anytime. ![Telemetry Page View](./img/telemetry-page-view.png) ### Collected data The telemetry feature categorizes the collected data into general Camunda data and metrics data: - General data: This includes information about deployed Camunda components, customer ID, and unique installation identifier. - Metrics data: Captures selected Prometheus metrics available for each component via monitoring endpoint. For example, command counts, metrics related to process instances, CPU load, and memory allocation. ### How to enable telemetry By default, the telemetry configuration is set to `disabled`. Telemetry can be activated by setting the appropriate configuration in the Camunda 8 Helm chart or setting the appropriate environment variables in the Camunda Hub configuration. Refer to [Camunda Hub configuration](./configuration/configuration.md) for more information. ### Telemetry configuration options explained When the telemetry configuration is set to `disabled` (default), Camunda Hub will not collect or send any metrics. This feature will not be running. When the telemetry configuration is set to `download`, Camunda Hub collects metrics and shows them on a telemetry page. However, it will not send any information to Camunda automatically. With this option, you can report telemetry to Camunda by downloading this data from the telemetry page and sending it to our team on request. When the telemetry configuration is set to `online` (recommended), Camunda Hub collects metrics and shows them on a telemetry page. Camunda Hub will collect metrics regularly and automatically send this data once every 24 hours. This mode will simplify sharing this data with us and provide us with better insight into Camunda 8 use. ### Example You can download an example [data file](https://github.com/camunda/camunda-docs/blob/main/docs/self-managed/components/hub/telemetry-sample.json). Below is an extract to highlight the types of data collected: ```json { "installation": { "cid": "customer", "iid": "any-text-identifier" }, "dataPoints": [ { "entities": [ { "id": "installation", "type": "global" }, { "id": "operate", "type": "component" }, { "id": "a22efefbb00546c04512fc7d4b455e77", "type": "cluster" } ], "key": "component-count", "type": 1, "transferredValue": "1", "value": 1, "tsFrom": 1712228731639 }, { "entities": [ { "id": "installation", "type": "global" }, { "id": "optimize", "type": "component" }, { "id": "a22efefbb00546c04512fc7d4b455e77", "type": "cluster" } ], "key": "component-count", "type": 1, "transferredValue": "1", "value": 1, "tsFrom": 1712228731639 }, { "entities": [ { "id": "a22efefbb00546c04512fc7d4b455e77", "type": "cluster" }, { "id": "operate", "type": "component" } ], "key": "jvm_memory_max_bytes", "type": 0, "tags": { "area": "heap", "id": "G1 Old Gen" }, "transferredValue": "5.36870912E8", "value": "5.36870912E8", "tsFrom": 1712228731639 }, ``` ### Legal and privacy considerations Before enabling telemetry in Camunda 8 Camunda Hub Self-Managed, ensure you are authorized to take this step and that the installation or activation of the telemetry functionality is not in conflict with any company-internal policies, compliance guidelines, any contractual or other provisions or obligations of your company. Camunda cannot be held responsible for the unauthorized installation or activation of this function. --- ## Troubleshoot database connection issues You try to start Web Modeler, and encounter issues with the database connection. ## Using a non-empty schema As Web Modeler uses [Flyway](https://www.red-gate.com/products/flyway/community/) to manage schema updates, the schema should not be shared. Before the first initialization, ensure no tables or functions are present in your schema. If your database setup requires mandatory tables or functions, Flyway may throw an exception like `Found non-empty schema(s) "" without schema history table!` To overcome this issue, add the property `spring.flyway.baselineOnMigrate: true` to your Web Modeler configuration and remove it after the schema has been initialized. ## Secure connection to standard PostgreSQL Refer to the [database configuration guide](../configuration/database.md#configuring-ssl-for-the-database-connection) for details on how to configure a secure connection to PostgreSQL. ## Secure connection to Amazon Aurora fails You configured a custom SSL certificate in your remote Amazon Aurora PostgreSQL instance and want Web Modeler to accept that certificate. ### Add Amazon Root CA to trust store By default, the Java version used by `modeler-restapi` ships with the Amazon Root CA. If you passed a custom trust store to `modeler-restapi`'s JVM process (e.g. via `JAVA_TOOL_OPTIONS` as described in [the Zeebe connection troubleshooting guide](./troubleshoot-zeebe-connection.md#provide-the-certificate-to-the-jvm-trust-store)), ensure the Amazon Trust Services CA are in `modeler-restapi`'s trust store (see the [Amazon Aurora documentation](https://aws.amazon.com/blogs/security/how-to-prepare-for-aws-move-to-its-own-certificate-authority/)). ## IAM authentication against Amazon Aurora fails You switched from standard username/password authentication to IAM authentication and Web Modeler can't obtain a connection to the database. ### Ensure the IAM account has all privileges to the Web Modeler database After switching from standard username/password authentication to IAM authentication, privileges to Web Modeler's database might still be associated with the old username. Ensure the IAM account has all privileges to the Web Modeler database. --- ## Troubleshoot missing data Troubleshoot and resolve your Web Modeler missing data issues. ## Issue When logged in to Web Modeler, all your previous data appears to be missing. ## Cause You must ensure the externally managed user ID does not change. Web Modeler uses the value of the `sub` (subject) claim in the JSON Web Token (JWT) issued by the configured OIDC provider (default Keycloak) to identify users and correlate them with their data created in Web Modeler. It is important that this value does not change over time, for example when the user is deleted and recreated in Keycloak, reimported from an external user directory, or when reinstalling/updating/switching Keycloak instances. - If the `sub` claim value changes for an existing user, Web Modeler creates a new user record for this user in the database the next time the user logs in. - In this case, the user no longer sees any of the projects they previously had access to, because the project permissions are still assigned to the old user record. :::note The missing/orphaned projects and all contained files remain in the Web Modeler database. ::: ## Resolution To restore project access for the affected users, Web Modeler admins can use the [super-user mode](../../../../components/hub/workspace/modeler/collaboration/collaboration.md#super-user-mode) to reassign collaborators to orphaned projects. --- ## Troubleshoot other problems Troubleshoot and resolve problems that are not covered by the other troubleshooting guides. ## Unable to create BPMN or DMN diagrams ### Issue When creating a new BPMN or DMN diagram via the Web Modeler UI, the diagram creation request fails with a `HTTP 403 Forbidden` response. Other requests to the application succeed. ### Cause When your Web Modeler installation is running behind a web application firewall (WAF), the firewall may block the request. Some WAF rules block requests that contain certain characters in the request body, such as XML payloads used in BPMN and DMN diagrams. ### Resolution Ensure that your WAF doesn't block requests to Web Modeler. A rule that is known to interfere with Web Modeler requests is AWS WAF's `CrossSiteScripting_Body` rule included in its [core rule set](https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-baseline.html#aws-managed-rule-groups-baseline-crs). Similar solutions like [ModSecurity](https://modsecurity.org/) may also block requests to Web Modeler based on the used rule set. --- ## Troubleshoot proxy configuration issues Troubleshoot and resolve issues in Web Modeler caused by incorrect or incomplete proxy configuration. ## Issue Users experience a variety of failures when Web Modeler attempts to communicate with external services. These issues can manifest as: - Failed authentication due to the inability to access the JWKS (JSON Web Key Set) endpoint. Error message: "Expected 200 OK from the JSON Web Key Set HTTP response." - Failure to reach other external services, such as the Camunda Marketplace. ## Cause Proxy settings must be correctly configured for Web Modeler to route outgoing requests through a network proxy. Common issues occur when: - The proxy server is not properly configured or unreachable. - Requests to external services are being blocked by the proxy configuration. - Authentication requests, such as those to the OIDC provider, fail when the JWKS endpoint is unreachable via the proxy. ## Resolution Ensure a correct proxy configuration for the `restapi` component. The proxy is configured with standard JVM system properties passed via the environment variable `JAVA_TOOL_OPTIONS`: ```properties JAVA_TOOL_OPTIONS=-Dhttps.proxyHost= -Dhttps.proxyPort= ``` --- ## Troubleshoot Zeebe connection issues You try to connect (i.e., to deploy) to a remote Zeebe cluster and Web Modeler reports an error. To resolve this issue, check if you can connect to Zeebe through another client. If that doesn't work, resolve the general connection issue first (see [the platform deployment troubleshooting section](self-managed/operational-guides/troubleshooting.md), for example.) If that works, further debug your Zeebe connection with the help of the information stated below. Enabling [debug logging in `modeler-restapi`](#how-can-i-debug-log-grpc--zeebe-communication) may also help to understand the issue. ## Zeebe connection times out ### Increase the Zeebe client timeout Web Modeler uses the [Zeebe Java client](/apis-tools/java-client/getting-started.md) to connect to Zeebe. Depending on your infrastructure, the default timeouts configured may be too short. You can pass custom timeouts in milliseconds for Web Modeler's Zeebe client to `modeler-restapi` via three individual environment variables: ```shell ZEEBE_CLIENT_REQUESTTIMEOUT=30000 # limit the time to wait for a response from the Zeebe Gateway ZEEBE_AUTH_CONNECT_TIMEOUT=60000 # limit the time to wait for a connection to the OAuth server ZEEBE_AUTH_READ_TIMEOUT=60000 # limits the time to wait for a response from the OAuth server ``` ## Secure connection to Zeebe fails If you provide a cluster URL starting with `https`, Web Modeler will try to establish a secure connection to the Zeebe instance. In the process, it strictly validates the server's Application-Layer Protocol Negotiation (ALPN) support and its certificates presented against well-known certificate authorities. Failure to connect may have several reasons: ### Configure the gateway to accept secure connections Ensure you properly configure the remote cluster URL to accept secure connections. Refer to the [Zeebe Gateway configuration documentation](/self-managed/components/orchestration-cluster/zeebe/security/secure-client-communication.md#gateway) for additional information. ### Configure the gateway to support ALPN [Inspect the connection](#how-can-i-get-details-about-a-secure-remote-connection) to understand if ALPN is supported by the server. Secure connections to Zeebe require an Ingress controller that supports HTTP/2 over TLS with protocol negotiation via ALPN. Ensure you properly [configured your Zeebe Ingress to support ALPN](self-managed/operational-guides/troubleshooting.md#zeebe-ingress-grpc). ### Configure `modeler-restapi` to trust a custom Zeebe SSL certificate [Inspect the connection](#how-can-i-get-details-about-a-secure-remote-connection) to understand which certificates are being returned by the server and ensure you configure Web Modeler for [custom SSL certificates](#how-can-i-provide-a-custom-zeebe-ssl-certificate). If intermediate signing authorities sign the server certificate, ensure the remote endpoint [serves both server and intermediate certificates](https://nginx.org/en/docs/http/configuring_https_servers.html#chains) to Web Modeler. ### Make the OAuth token cache location writeable for the `modeler-restapi` process When using the `OAuth` authentication method for deploying to Zeebe, Web Modeler caches OAuth tokens in a file-based cache. By default, the cache location is writeable by the `modeler-restapi` process. If you run `modeler-restapi` as a non-root user (e.g. via Kubernetes' `securityContext.runAsUser` option), you must ensure to provide a writeable cache file location to `modeler-restapi` via the `ZEEBE_CLIENT_CONFIG_PATH` environment variable: ```shell ZEEBE_CLIENT_CONFIG_PATH=/path/to/credentials/cache.txt ``` ## How can I provide a custom Zeebe SSL certificate? You configured a custom SSL certificate in your (remote) Zeebe deployment and want Web Modeler to accept that certificate. Web Modeler strictly validates the remote server certificate trust chain. If you use a custom SSL server certificate, you must make the signing CA certificate known to Web Modeler, not the server certificate itself. ### Provide the certificate via an environment variable `modeler-restapi` reads a trusted certificate from the environment variable `ZEEBE_CA_CERTIFICATE_PATH`. This solution is recommended for most users: ```shell ZEEBE_CA_CERTIFICATE_PATH=/path/to/certificate ``` The provided path has to be accessible from the `modeler-restapi` container (e.g. via a mounted volume). ### Provide the certificate to the JVM trust store Alternatively, you may pass a custom trust store to `modeler-restapi` via the environment variable `JAVA_TOOL_OPTIONS`: ```shell JAVA_TOOL_OPTIONS="-Djavax.net.ssl.trustStore=/path/to/truststore.jks -Djavax.net.ssl.trustStorePassword=changeit" ``` Analogous to above, the provided path has to be accessible from the `modeler-restapi` container (e.g. via a mounted volume). :::caution Be aware that passing a custom trust store via `JAVA_TOOL_OPTIONS` overrides the default JVM trust store. This means that all certificates shipped by default with the Java runtime (e.g. the Amazon Trust Services CA for connecting to a secured Aurora instance) are no longer trusted unless explicitly added. ::: ## How can I get details about a secure remote connection? You can use the following command to retrieve information about HTTP/2 over TLS support (ALPN) and certificates provided by a remote endpoint: ```shell > openssl s_client -alpn h2 -connect google.com:443 -servername google.com [...] --- Certificate chain 0 s:/CN=*.google.com i:/C=US/O=Google Trust Services LLC/CN=GTS CA 1C3 1 s:/C=US/O=Google Trust Services LLC/CN=GTS CA 1C3 i:/C=US/O=Google Trust Services LLC/CN=GTS Root R1 2 s:/C=US/O=Google Trust Services LLC/CN=GTS Root R1 i:/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA --- [...] --- New, TLSv1/SSLv3, Cipher is AEAD-CHACHA20-POLY1305-SHA256 Server public key is 256 bit Secure Renegotiation IS NOT supported Compression: NONE Expansion: NONE ALPN protocol: h2 SSL-Session: Protocol : TLSv1.3 Cipher : AEAD-CHACHA20-POLY1305-SHA256 Session-ID: Session-ID-ctx: Master-Key: Start Time: 1687516295 Timeout : 7200 (sec) Verify return code: 0 (ok) --- ``` ## How can I debug log gRPC / Zeebe communication? You can also start `modeler-restapi` with gRPC debug logging turned on to get detailed [logging output](../configuration/logging.md) on communication to Zeebe: ```shell LOGGING_LEVEL_IO_GRPC=TRACE LOGGING_LEVEL_IO_CAMUNDA_MODELER=DEBUG ``` --- ## Usage metrics(Hub) With Camunda Hub, you can access usage metrics from Operate and Tasklist. The corresponding page allows you to select the time period and the clusters, and displays the subsequent consumption data. The same endpoints are queried as described [in the usage metrics documentation](/self-managed/components/orchestration-cluster/core-settings/concepts/usage-metrics.md). :::note To retrieve usage metrics, you must be running Tasklist or Operate. ::: --- ## Manage access and permissions With Management Identity, you can manage and control access to management and modeling component REST APIs and custom applications using permissions and roles. :::note This section describes how to manage access to Web Modeler, Console, and Optimize. For access control to Orchestration Cluster components and their resources, refer to the [Orchestration Cluster authorizations](/components/concepts/access-control/authorizations.md) instead. ::: ## About permissions When using and managing permissions, it is important to understand the following key concepts: - APIs represent the different Camunda 8 management and modeling components, such as Web Modeler, Optimize, and so on. - Each [API defines its own set of permissions](#permissions) that to control API access. - Permissions are [organized using roles](./manage-permissions.md#manage-permissions-for-roles) that can be assigned to users either directly or via Groups. - You can also [assign permissions to your custom application](./manage-permissions.md#manage-application-permissions), such as a job worker for example. :::tip For detailed instructions, see the [guide about managing permissions](./manage-permissions.md). ::: ## Permissions Each API (representing a component) defines its own set of permissions to control API access. The following permissions are available: | Component | API | Permissions available | | :------------------ | :--------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Management Identity | `Camunda Identity Resource Server` | `read`: Read access to Management Identity UI`read:users`: Access only the Users UI and related subpages.`write`: Write access to Management Identity UI. | | Optimize | `Optimize API` | `write:*`: Read and Write access to Optimize UI and APIs. | | Web Modeler | `Web Modeler Internal API` | `write:*`: Access to Web Modeler UI, further managed by [project permissions](../../../../components/hub/workspace/modeler/collaboration/collaboration.md#access-rights-and-permissions).`admin:*`: Elevated access to Web Modeler UI (see [super-user mode](../../../../components/hub/workspace/modeler/collaboration/collaboration.md#super-user-mode) and [publishing connector templates](/components/connectors/manage-connector-templates.md#publish-a-connector-template)). | | Web Modeler | `Web Modeler API` | `create:*`: Access to `POST` endpoints of the [API](/apis-tools/web-modeler-api/index.md).`read:*`: Access to `GET` endpoints of the [API](/apis-tools/web-modeler-api/index.md).`update:*`: Access to `PATCH` and `PUT` endpoints of the [API](/apis-tools/web-modeler-api/index.md).`delete:*`: Access to `DELETE` endpoints of the [API](/apis-tools/web-modeler-api/index.md). | :::note Permissions granted to a user or M2M application are added to the `permissions.{audience}` claim of the access token. ::: --- ## Manage permissions This guide describes how to manage permissions for roles and applications in Management Identity: - You can grant permissions to either [roles](../application-user-group-role-management/manage-roles.md) or [applications](../application-user-group-role-management/applications.md). - When a permission is granted to a role, all users assigned to the role and members of a group with the assigned role, are granted the respective access. ## Manage role permissions ### Assign a permission to a role To assign a permission to a role, take the following steps: 1. Navigate to the **Roles** tab, click the role, and select **Permissions > Assign permissions**. 2. Select the API which contains the permission(s) you want to assign. 3. Select the permission(s) you would like to assign and click **Add**. On confirmation, the modal will close, the table will update, and your assigned permission will be shown. ### Delete a permission from a role To delete a permission from a role, take the following steps: 1. Navigate to the **Roles** tab. Click the role you would like to delete permissions from. 2. Navigate to the **Permissions** tab. 3. Click the trash icon next to the permission you want to remove. On confirmation, the modal will close, the table will update, and the assigned permission will be removed from the role. ## Manage application permissions ### Assign a permission to an application To assign a permission to an application using Identity, take the following steps: 1. Navigate to the **Applications** tab and click on an application. Then, select the **Access to APIs** tab and click **Assign Permissions**: ![assign-a-permission-application-tab](../img/assign-a-permission-application-tab.png) 2. Select the API which contains the permission you want to assign. 3. Select the permissions you would like to assign and click **Add**. On confirmation, the modal will close, the table will update, and your assigned permissions will be shown: ![assign-a-permission-application-refreshed-table](../img/assign-a-permission-application-refreshed-table.png) ### Delete a permission from an application To delete a permission from an application, take the following steps: 1. Navigate to the **Applications** tab. Click the application you would like to delete permissions from. 2. Navigate to the **Access to APIs** tab. 3. Click the trash icon next to the permission you want to remove. On confirmation, the modal will close, the table will update, and the assigned permission will be removed from the application. --- ## Manage applications In Management Identity, an application represents an entity that can request Management Identity to authenticate a user or a service for accessing management and modeling components (Web Modeler, Console, and Optimize). ## About applications Camunda 8 provides a set of preconfigured applications. The following applications are managed by Management Identity: - Console - Optimize - Web Modeler - Management Identity As a Management Identity user you can also add your own custom applications. For example, you can provide a service with M2M access to management and modeling component REST APIs, such as a custom application that needs to access Optimize or Web Modeler APIs. ## Application types To align with the [OAuth 2.0 standard](https://oauth.net/2/client-types/), Camunda distinguishes between _confidental_ and _public_ clients. Applications are also categorized by usage pattern, using the _M2M_ application type in Identity, for systems to communicate using _confidental_ clients without direct user interaction. - Confidential - Machine-to-machine (M2M) - Public The application type is selected when you [create an application](#add-an-application), based on its ability to securely store and use secrets, as well as the mode of authentication it uses. | Application type | Secret | User login flow | M2M authentication | | :--------------- | :----- | :-------------- | :----------------- | | Confidential | Yes | Yes | Yes | | M2M | Yes | No | Yes | | Public | No | Yes | No | :::info - To learn more about OAuth client types, refer to [OAuth 2.0 Client Types](https://oauth.net/2/client-types/), - To learn more about confidential and public applications, refer to [confidential and public applications](https://auth0.com/docs/get-started/applications/confidential-and-public-applications). ::: ## Manage applications ### Add an application 1. Log in to the Identity interface and navigate to the **Applications** tab: ![add-application-tab](../img/add-application-tab.png) 2. Click the **Add application** button located on the top right of the table and a modal will open. 3. Enter a name for your application. In this guide we will use a set of example values. Select the type of your application based on our [guide](/self-managed/components/management-identity/application-user-group-role-management/applications.md#types-of-applications). Depending on the selected type, you might need to enter at least one redirect URI. Once you have entered the required details, click **Add**: 4. On confirmation, the modal will close, and the list updates to show your new application. Click on your new application to view the details. This includes your generated client ID and client secret depending on the selected [application type](/self-managed/components/management-identity/application-user-group-role-management/applications.md#application-types). ![add-application-refreshed-table](../img/add-application-refreshed-table.png) ## Application permissions You can control the access an application has to Camunda 8 management and modeling components by [assigning permissions](/self-managed/components/management-identity/access-management/manage-permissions.md#assign-a-permission-to-an-application) to the application. :::info For an overview of permissions available, see [available permissions](/self-managed/components/management-identity/access-management/access-management-overview.md#permissions). ::: --- ## Manage users, groups, roles, and applications Manage and organize access to Camunda 8 management and modeling components ([Camunda Hub](../../hub/index.md) and [Optimize](../../optimize/overview.md)) using users, groups, roles, and applications. ## About managing users in Management Identity In Management Identity, a user represents a human who interacts with Camunda 8 management and modeling components. - You do not create or manage users in Management Identity itself. Users are managed in Keycloak or your connected IdP. - Management Identity allows you to organize and manage access to the components for these users with groups and roles. ## Groups Use groups to manage user access by organizing users into groups (group members) and assigning roles. Instead of assigning roles to individual users, map a set of roles to a group, with all group members automatically inheriting the role permissions. - [Manage Groups](./manage-groups.md) ## Roles Use roles to simplify and standardize access control across your system, help enforce consistent permission sets, reduce errors, and scale access management as your organization grows. Roles define the actions a user or application can perform in Camunda 8 management and modeling components by grouping together a set of related [permissions](../access-management/manage-permissions.md). You can assign roles [directly to a user](manage-roles.md#assign-a-role-to-a-user) or [using groups](/self-managed/components/management-identity/application-user-group-role-management/manage-groups.md#assign-roles-to-a-group). - [Manage roles](./manage-roles.md) ## Applications In Management Identity, an application represents an entity that can request Management Identity to authenticate a user or a service for accessing management and modeling components. Camunda 8 has a set of preconfigured applications, but as a user of Management Identity you can also add your own applications. For example, you can provide a service with M2M access to management and modeling component APIs, such as a custom application that needs to access Optimize or Web Modeler APIs. - [Manage applications](./applications.md) --- ## Groups(Application-user-group-role-management) Use groups to manage and organize user access to Camunda 8 management and modeling components (Web Modeler, Console, and Optimize). ## About groups Organize your users into groups as an efficient and scalable way of controlling access to Camunda 8 management and modeling components. - Instead of assigning roles to individual users, [map a set of roles to a group](#assign-roles-to-a-group). - All group members automatically inherit the role permissions of a group. :::note You do not create or manage users in Management Identity itself. Users are managed in Keycloak or your connected IdP. ::: ## Manage groups ### Create a group 1. Navigate to the **Groups** tab. 2. Click the **Create group** button located on the top right of the table and a modal will open. 3. Enter the group name and click **Create group**. On confirmation, the modal closes, the table updates, and your new group is shown. ### Modify or delete a group 1. Navigate to the **Groups** tab. 2. Use the pencil icon to edit the group or the trash icon to remove a group. ## Manage group users ### Assign users to a group 1. Navigate to the **Groups** tab. Select the group you want to assign a member to from the table. 2. Click **Assign members** and a modal will open. 3. Search and select the member to assign to the group. After selecting the member, click **Assign**. On confirmation, the modal closes, the table updates, and your assigned members are shown. ### Remove a user from a group 1. Navigate to the **Groups** tab. 2. Click the trash icon next to the member you want to remove from the group. On confirmation, the modal closes, the table updates, and your member is removed from the group. ## Manage group roles ### Assign roles to a group 1. Navigate to the **Groups** tab. Select the group you want to assign a role to from the table, and click on the **Roles** tab. 2. Click **Assign roles** and a modal will open. 3. Select the roles to assign to the group. When you have selected the roles, click **Add**. On confirmation, the modal closes, the table updates, and your assigned roles are shown. ### Remove a role from a group 1. Navigate to the **Groups** tab. Select the group. 2. Navigate to the **Roles** tab. 3. Click the trash icon next to the user you want to remove from the group. On confirmation, the modal closes, the table updates, and your role is removed from the group. --- ## Manage roles Use Management Identity roles to simplify and standardize access control for Camunda 8 management and modeling components (Web Modeler, Console, and Optimize). ## About roles Roles help you to enforce consistent permission sets, reduce errors, and scale access management as your organization grows. - Roles define the actions a user or application can perform in Camunda 8 management and modeling components by grouping together a set of related [permissions](../access-management/manage-permissions.md). - You can assign roles [directly to a user](#assign-a-role-to-a-user) or [using groups](../application-user-group-role-management/manage-groups.md#assign-roles-to-a-group). ## Default roles Management Identity includes a set of default roles that are available out-of-the-box. These roles are designed to cover common use cases and can be assigned to users and groups to grant them access to different management and modeling components. The following table lists the default roles and their descriptions: | Name | Description | | :------------------ | :---------------------------------------------------------------------------------------------------------- | | Default user role | The role does not grant any permissions by default. It is applied to all users, including service accounts. | | Management Identity | Provides full access to [Management Identity](../overview.md). | | Console | Provides full access to Console. | | Optimize | Grants full access to [Optimize](../../optimize/overview.md). | | Web Modeler | Grants access to Web Modeler for creating and collaborating on projects. | | Web Modeler Admin | Grants full access to Web Modeler, including all projects and the ability to manage collaborators. | ## Add a role To add a role, take the following steps: 1. Navigate to the **Roles** tab. 2. Click the **Add role** button located on the top right of the table and a modal will open to enter the name and description. On confirmation, the modal will close, the table will update, and the new role will be shown. ## Delete a role Roles can be deleted in two ways: through the trash button in the table view, or in the overflow menu once the role is selected. 1. Navigate to the **Roles** tab. 2. Search for a role by clicking the magnifying glass next to **Add role**. 3. Click the trash button next to the role or click the role, click the overflow menu, then **Delete**. On confirmation, the modal will close, the table will update, and the role will be removed. ## Assign a role to a user To assign a role to a user, take the following steps: 1. Navigate to the **Users** tab. 2. Click on the user you want to assign a role to to view their details. 3. Click on **Assigned roles** to view the roles currently assigned to the user. 4. Click the **Assign roles** button located on the top right of the table and a modal will open. 5. Select the role you want to assign to the user and click **Add**. On confirmation, the modal will close, the table will update, and the newly assigned role will be shown for the user. ## Delete an assigned role from a user To delete an assigned role from a user, take the following steps: 1. Navigate to the **Users** tab. 2. Click on the user you want to remove a role from to view their details. 3. Click on **Assigned roles** to view the roles currently assigned to the user. 4. Click the trash icon next to the role. On confirmation, the modal will close, the table will update, and the role will be removed from the user. --- ## Authentication(Management-identity) Depending on your [configuration](/self-managed/components/management-identity/configuration/identity-configuration-overview.md), users and applications authenticate with Camunda 8 via the IdP using the [OAuth 2.0 protocol](https://oauth.net/2/), using either a login page or [M2M tokens](#m2m-machine-to-machine-authentication). ## Login page authentication When a user accesses a Camunda 8 web application, they are shown the login page provided by the configured IdP. ## M2M (machine-to-machine) authentication Applications can authenticate with Camunda 8 using M2M (machine-to-machine) tokens. ### Prerequisites The following prerequisites are required for M2M authentication: | Prerequisite | Description | | :----------------------------------------------------------------------------------------------------------------- | :------------------------------------- | | [Management Identity](/self-managed/components/management-identity/overview.md) | A running Management Identity service. | | [Application](/self-managed/components/management-identity/application-user-group-role-management/applications.md) | An application for your service. | | Client ID | The client ID of your application. | | Client secret | The client secret of your application. | | REST client | A REST client of your choice. | ### Generate a token In the following example, the Keycloak instance that supports Management Identity can be found via `http://localhost:18080`. This might be different for your Management Identity configuration, adjust the host name (and port if required) if required. To request a token, use the following cURL command, replacing the placeholders with your application details: ``` curl --location --request POST 'http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'client_id=[CLIENT_ID]' \ --data-urlencode 'client_secret=[CLIENT_SECRET]' \ --data-urlencode 'grant_type=client_credentials' ``` The following shows an example successful authentication response: ``` { "access_token": "", "expires_in": 300, "refresh_expires_in": 0, "token_type": "Bearer", "not-before-policy": 0 } ``` You can use the token to authenticate an application with Camunda components, for example to access Operate REST APIs. --- ## Use an alternative database for Management Identity Use an alternative database for Management Identity if your internal policies or compliance requirements prevent the use of PostgreSQL. ## Database versions Identity is tested against the following alternative relational databases: | Camunda version | Database version | Driver version | | --------------- | ---------------- | -------------- | | 8.7.1 | Oracle 19C | 21.3.0.0 | | 8.7.1 | SQL Server 2019 | 12.10.0.jre11 | ## Oracle database configuration ### Driver provision As the Oracle driver is not provided by default in each of the Camunda 8 distributions, you must download the driver and supply it for the application to load. 1. Download the appropriate Oracle driver: https://download.oracle.com/otn-pub/otn_software/jdbc/237/ojdbc17.jar. 2. When starting the application, set `-cp "/app/ojdbc.jar:/app/identity.jar"` in the `java` command during startup. This is only required for Oracle. 3. If you are using docker or kubernetes, ensure that the folder with the library is properly mounted as a volume. ```sh SPRING_DATASOURCE_URL="jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=tcps)(HOST=${IDENTITY_DATABASE_HOST:})(PORT=${IDENTITY_DATABASE_PORT:}))(CONNECT_DATA=(SERVICE_NAME=${IDENTITY_DATABASE_NAME:}))(SECURITY=(SSL_SERVER_CERT_DN=\"CN={CERT_CN}, O={CERT_ORG},L={..},ST={..},C={..}\")))" SPRING_DATASOURCE_DRIVER_CLASS_NAME=oracle.jdbc.OracleDriver SPRING_JPA_DATABASE=oracle JAVA_TOOL_OPTIONS=$JAVA_OPTS ``` ```yaml identity: externalDatabase: enabled: true # These three configuration options are added so that spring knows to connect to oracledb using it's client library env: - name: SPRING_DATASOURCE_URL value: 'jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=tcps)(HOST=${IDENTITY_DATABASE_HOST:})(PORT=${IDENTITY_DATABASE_PORT:}))(CONNECT_DATA=(SERVICE_NAME=${IDENTITY_DATABASE_NAME:}))(SECURITY=(SSL_SERVER_CERT_DN="CN={CERT_CN}, O={CERT_ORG},L={..},ST={..},C={..}")))' - name: SPRING_DATASOURCE_DRIVER_CLASS_NAME value: oracle.jdbc.OracleDriver - name: JAVA_TOOL_OPTIONS value: $JAVA_OPTS - name: SPRING_JPA_DATABASE value: oracle # Overriding identity.command is required so that the new driver in /app will be loaded upon startup. command: - /bin/sh - -c - | java -cp "/extraDrivers/ojdbc.jar:/app/identity.jar" org.springframework.boot.loader.launch.JarLauncher # Extra volumes are mounted for any TLS certs necessary for the database: extraVolumeMounts: - name: "keystore-secret" secret: secretName: "keystore-secret" - name: jdbcdrivers mountPath: /extraDrivers extraVolumes: - name: "keystore-secret" mountPath: "/usr/local/certificates" - name: jdbcdrivers emptyDir: {} initContainers: - name: fetch-jdbc-drivers image: alpine:3.19 imagePullPolicy: "Always" command: [ "sh", "-c", "wget https://download.oracle.com/otn-pub/otn_software/jdbc/237/ojdbc17.jar -O /extraDrivers/ojdbc.jar", ] volumeMounts: - name: jdbcdrivers mountPath: /extraDrivers securityContext: runAsUser: 1001 ``` ```yaml spring: datasource: url: jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=tcps)(HOST=${IDENTITY_DATABASE_HOST:})(PORT=${IDENTITY_DATABASE_PORT:}))(CONNECT_DATA=(SERVICE_NAME=${IDENTITY_DATABASE_NAME:}))(SECURITY=(SSL_SERVER_CERT_DN=\"CN={CERT_CN}, O={CERT_ORG},L={..},ST={..},C={..}\"))) driver-class-name: oracle.jdbc.OracleDriver jpa: database: oracle ``` ## MSSQL database configuration ### Driver provision As the driver for MSSQL is provided by default in identity, you do not need to download it or supply it in the classpath. ```sh SPRING_DATASOURCE_URL="jdbc:sqlserver://${IDENTITY_DATABASE_HOST:}:${IDENTITY_DATABASE_PORT:};databaseName=${IDENTITY_DATABASE_NAME:};encrypt=true;hostNameInCertificate={CACERT_/CN};trustServerCertificate=false" SPRING_DATASOURCE_DRIVER_CLASS_NAME=com.microsoft.sqlserver.jdbc.SQLServerDriver SPRING_JPA_DATABASE=sql_server JAVA_TOOL_OPTIONS=$JAVA_OPTS ``` ```yaml identity: externalDatabase: enabled: true # These three configuration options are added so that spring knows to connect to oracledb using it's client library env: - name: SPRING_DATASOURCE_URL value: "jdbc:sqlserver://${IDENTITY_DATABASE_HOST:}:${IDENTITY_DATABASE_PORT:};databaseName=${IDENTITY_DATABASE_NAME:};encrypt=true;hostNameInCertificate={CACERT_/CN};trustServerCertificate=false" - name: SPRING_DATASOURCE_DRIVER_CLASS_NAME value: com.microsoft.sqlserver.jdbc.SQLServerDriver - name: SPRING_JPA_DATABASE value: sql_server - name: JAVA_TOOL_OPTIONS value: $JAVA_OPTS # Extra volumes are mounted for any TLS certs necessary for the database: extraVolumeMounts: - name: "keystore-secret" secret: secretName: "keystore-secret" extraVolumes: - name: "keystore-secret" mountPath: "/usr/local/certificates" ``` ```yaml spring: datasource: url: jdbc:sqlserver://${IDENTITY_DATABASE_HOST:}:${IDENTITY_DATABASE_PORT:};databaseName=${IDENTITY_DATABASE_NAME:};encrypt=true;hostNameInCertificate={CACERT_/CN};trustServerCertificate=false username: user password: AStrongPassword driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver jpa: database: sql_server ``` ## Troubleshooting The following troubleshooting tips are provided to help you with common issues: | Tip | Description | | :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Check Keystore path | Access (or "exec into") the running container where the application is deployed and confirm that the Java process running inside the container is configured with the correct keystore path. | | Check certificates | Confirm that any SSL/TLS certificate required for secure communication with the database exists in the mounted location on the filesystem. | | Test database connection | Test and verify the connection from the pod to the database using simple tools and utilities, such as JDBC tool, ping, curl, and so on. | --- ## Configure an external IdP using Keycloak If you are using the default (built-in) Keycloak you can configure an external identity provider (IdP) for user authentication, such as OpenID Connect, SAML, LDAP, or Active Directory. :::info Deploying with Helm? If you deploy Camunda 8 Self-Managed with Helm, use the [Helm chart guide for configuring an external IdP with Keycloak](/self-managed/deployment/helm/configure/authentication-and-authorization/internal-keycloak.md). ::: ## Configuration steps 1. Log in to the Keycloak Administrator Console. Open the URL you have configured for Keycloak in your browser. :::tip When using the example [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md) setup, Keycloak is available at [http://localhost:18080/](http://localhost:18080/). ::: 2. Click **Administrator Console** and log in using the Keycloak administrator credentials. - The default administrator username is `admin`. - When deploying Camunda 8 with [Helm charts](/self-managed/setup/overview.md), you can extract the password as detailed in [secrets extraction](/self-managed/upgrade/helm/index.md#secrets-extraction). - Using the example [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md) setup, the password is set via `KEYCLOAK_ADMIN_PASSWORD` environment variable and is `admin` per default. 3. Select the realm you are using with Camunda 8. By default, this is **Camunda-platform**. ![keycloak-realm-select](../img/keycloak-admin-realm-select.png) 4. Add an identity provider using either of the following methods: - To add an OpenID Connect or SAML provider, select **Identity Providers** in the main menu, click **Add provider...**, and enter all the required configuration settings. ![keycloak-add-identity-provider](../img/keycloak-add-identity-provider.png) - To connect to your LDAP, Active Directory, or Kerberos server, select **User Federation** in the main menu, click **Add provider...**, and fill in all required configuration settings. ![keycloak-add-user-federation](../img/keycloak-add-user-federation.png) :::tip Keycloak supports a wide variety of authentication options, such as mapping external user groups, roles, or scopes to internal roles, and configuring the login screen and flow when multiple providers are added. Visit the Keycloak documentation for your version of Keycloak for details on [adding a provider](https://www.keycloak.org/docs/latest/server_admin/#adding-a-provider), [configuring authentication](https://www.keycloak.org/docs/latest/server_admin/index.html#configuring-authentication_server_administration_guide), and [integrating identity providers](https://www.keycloak.org/docs/latest/server_admin/index.html#_identity_broker). ::: --- ## Connect to an existing Keycloak instance This guide describes how to connect Management Identity to your existing Keycloak instance. :::info Deploying with Helm? If you deploy Camunda 8 Self-Managed with Helm, use the [Helm chart guide for connecting to an external Keycloak instance](/self-managed/deployment/helm/configure/authentication-and-authorization/external-keycloak.md) instead. ::: ## Prerequisites - Access to your [Keycloak Admin Console](https://www.keycloak.org/docs/latest/server_admin/#using-the-admin-console) - A basic understanding of [administering realms and clients](https://www.keycloak.org/docs/latest/server_admin/#assembly-managing-clients_server_administration_guide) in Keycloak :::note Clients in Camunda 8 SaaS and applications in Camunda 8 Self-Managed serve a similar purpose. One key difference is that for Camunda 8 SaaS, you can set up specific [client connection credentials](/components/hub/organization/manage-clusters/manage-api-clients.md#create-a-client), whereas in Management Identity, an application is created with credentials automatically assigned. ::: ## Steps :::caution Keycloak URLs As of version 8.5.3, Management Identity uses the Keycloak frontend URL instead of the backend URL. This change may affect you if the frontend URL is blocked from other services (including Camunda applications) and could impact Management Identity functionality. To avoid connectivity issues, ensure your Keycloak frontend URL is accessible by adjusting your network, firewall, or security settings as needed. This adjustment is crucial to maintain the integration with Keycloak and ensure compatibility. ::: To connect Management Identity to an existing Keycloak instance, take the following steps for your Camunda installation. ### Prepare an existing Keycloak realm Management Identity can either create a Keycloak realm called `camunda-platform` with all settings from scratch, or it can use an already existing Keycloak realm. If you would like to use an existing Keycloak realm, prepare following the steps below. Otherwise you can skip this section. 1. Log in to your Keycloak Admin Console. 1. Select the realm you want to connect Management Identity to (for example, **camunda-platform**). ![keycloak-admin-realm-select](../img/keycloak-admin-realm-select.png) :::warning Management Identity only supports Keycloak realms where the realm name and realm ID are the same value. This is not the case for realms created through the Keycloak UI (where the ID becomes a generated value). You can specify both name and ID when using [Keycloak's JSON import feature](https://www.keycloak.org/server/importExport). ::: 1. In the navigation menu, select **Clients**, then click **Create**. 1. Enter a client ID and click **Next**. :::note What client ID should I use? By default, Management Identity uses the client ID `camunda-identity`. If you use a different ID, set it in the Management Identity application [environment variables](/self-managed/components/management-identity/miscellaneous/configuration-variables.md). ::: ![keycloak-admin-client-add-1](../img/keycloak-admin-client-add-1.png) 1. Turn **Client authentication** on, select **Service accounts roles**, ensure **Authorization** is off, and click **Next**. ![keycloak-admin-client-add-2](../img/keycloak-admin-client-add-2.png) 1. In the **Root URL** field, enter the URL where your Management Identity instance will be hosted, then click **Save**. ![keycloak-admin-client-add-3](../img/keycloak-admin-client-add-3.png) 1. Open the created client, then select the **Service account roles** tab. ![keycloak-admin-client-update-1](../img/keycloak-admin-client-update-1.png) 1. Click **Assign role**, then change the filter to **Filter by clients**. ![keycloak-admin-client-update-2](../img/keycloak-admin-client-update-2.png) 1. Select the `manage-clients`, `manage-realm`, and `manage-users` roles, then click **Assign**. :::note Why does Management Identity need these roles? Management Identity allows users to manage entities related to Camunda. These roles provide the necessary access to the Keycloak realm. ::: 1. Open the **Credentials** tab and copy the client secret. ### Configure and start the application Configure Management Identity with the following environment variables: 1. `IDENTITY_CLIENT_ID`: The ID of the client you created (or `camunda-identity` if you let Management Identity create it automatically). 1. `KEYCLOAK_REALM`: The realm name you want to use (or `camunda-platform` if using the default). 1. `KEYCLOAK_SETUP_USER`: The username of an administrative Keycloak user. 1. `KEYCLOAK_SETUP_PASSWORD`: The password for the administrative user. :::tip If you use a non-default realm, you need to set additional Keycloak-specific variables. See [environment variables](/self-managed/components/management-identity/miscellaneous/configuration-variables.md) for details. ::: Start the Management Identity application. :::note What does Management Identity create when starting? When starting, Management Identity creates a base configuration required for operation. See [starting configuration](/self-managed/components/management-identity/miscellaneous/starting-configuration.md) for details. ::: :::tip Helm chart setup To run a full Camunda cluster with an existing Keycloak instance, see [Helm chart setup for existing Keycloak](/self-managed/deployment/helm/configure/authentication-and-authorization/external-keycloak.md#create-a-secret). ::: ## Considerations When connecting Management Identity to a shared realm, accurately determining what clients should and should not be displayed in the UI is not possible. Therefore, the clients in the realm you connect Management Identity to will be shown in the UI and can have their secrets viewed and updated. Users with access to Management Identity should be considered as having administrator-level access to the system. --- ## Connect Management Identity to an identity provider Configure Management Identity for your Camunda 8 Self-Managed deployment. This guide covers application-level configuration, including environment variables and IdP settings. :::note - A full list of supported and unsupported features when using an OIDC provider is available in the [OIDC features table](#supported-and-unsupported-oidc-features). - To connect to a Keycloak authentication provider, see [connect to an existing Keycloak instance](/self-managed/components/management-identity/configuration/connect-to-an-existing-keycloak.md). ::: :::info Deploying with Helm? If you deploy Camunda 8 Self-Managed with Helm, use the [Helm chart authentication and authorization guides](/self-managed/deployment/helm/configure/authentication-and-authorization/index.md) to configure OIDC and Management Identity. ::: ## Prerequisites - Information about your OIDC provider's configuration, including the issuer URL. - Ability to create applications in your OIDC provider. - Ability to access the following information about the applications you have created in your OIDC provider: - Client ID - Client secrets - Audience - A [claim name and value](/self-managed/components/management-identity/miscellaneous/configuration-variables.md#oidc-configuration) to use for initial access. - Your OIDC provider must issue access tokens that contain an `aud` (audience) claim matching the configured audience, as Camunda validates this claim for auth flows. Providers that are not configured to emit, or do not emit, the `aud` claim in their access tokens are not supported. Configure your identity provider to emit this claim if supported. See [known limitations](#oidc-provider-known-limitations) for details. :::note The steps below are a general approach for the Camunda components; it is important you reference the [component-specific configuration](#component-specific-configuration) to ensure the components are configured correctly. ::: ## Configuration Steps 1. Identify what management and modeling components you need to use in Camunda 8: [Camunda Hub](../../hub/index.md) and [Optimize](../../optimize/overview.md). 2. In your OIDC provider, **create an application for each of the management and modeling components you want to connect**. Web Modeler requires two applications: one for the UI, and one for the API. - The expected redirect URI of the component you are configuring an app for can be found in [component-specific configuration](#component-specific-configuration). :::note Redirect URIs serve as an approved list of destinations across identity providers. Only the URLs specified in the redirect URIs configuration will be permitted as valid redirection targets for authentication responses. This security measure ensures that tokens and authorization codes are only sent to pre-approved locations, preventing potential unauthorized access or token theft. ::: 3. For each management and modeling components, ensure the appropriate application type is used: - Web applications requiring confidential access/a confidential client: - **Optimize** - **Management Identity** - **Web Modeler API** - Web applications requiring public access/a public client: - **Console** - **Web Modeler UI** 4. Make a note of the following values for each application you create: - Client ID - Client secret - Audience 5. Set the following environment variables or Helm values for the component you are configuring an app for: :::note You can connect to your OIDC provider through either environment variables or Helm values. Ensure only one configuration option is used. ::: ```yaml camunda: identity: type: "GENERIC" baseUrl: authUrl: tokenUrl: jwksUrl: clientId: clientSecret: audience: identity: initialClaimName: initialClaimValue: spring: profiles: active: oidc ``` ``` CAMUNDA_IDENTITY_TYPE=GENERIC CAMUNDA_IDENTITY_BASE_URL= CAMUNDA_IDENTITY_ISSUER= CAMUNDA_IDENTITY_ISSUER_BACKEND_URL= // this is used for container to container communication CAMUNDA_IDENTITY_CLIENT_ID= CAMUNDA_IDENTITY_CLIENT_SECRET= CAMUNDA_IDENTITY_AUDIENCE= IDENTITY_INITIAL_CLAIM_NAME= IDENTITY_INITIAL_CLAIM_VALUE= SPRING_PROFILES_ACTIVE=oidc ``` :::note Once set, you cannot update your initial claim name and value using environment variables or Helm values. You must change these values directly in the database. ::: Additional considerations For authentication, the Camunda components use the scopes `email`, `openid`, `offline_access`, and `profile`. :::tip Optional scopes The `offline_access` scope is optional. If this scope is included, your OIDC provider issues a refresh token to Management Identity on user login. Management Identity uses the refresh token to renew the user's access token when it expires, so that sessions remain active without requiring the user to log in again. If `offline_access` is not included, users will be redirected to the OIDC provider for re-authentication whenever their access token expires. For more information, see the [OpenID Connect Core specification](https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess). If your organization restricts this scope for security reasons, you can adjust the scopes with: ``` CAMUNDA_IDENTITY_AUTH_SCOPES="openid profile email" ``` This configuration allows login without the `offline_access` scope. ::: Steps :::note Ensure you register a new application for each component. ::: 1. Identify what management and modeling components you need to use in Camunda 8: [Camunda Hub](../../hub/index.md) and [Optimize](../../optimize/overview.md). 2. Within the Entra ID admin center, [register a new application](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app) for **each component you would like to connect**. Web Modeler requires two applications: one for the UI, and one for the API. 3. Navigate to the new application's **Overview** page, and make note of the **Client ID**. This will also be used as the audience ID. 4. Within your new application, [configure a platform](https://learn.microsoft.com/en-gb/entra/identity-platform/quickstart-register-app#configure-platform-settings) for the appropriate component: - **Web**: - Optimize - Management Identity - Web Modeler API - **Single-page application**: - Console - Web Modeler UI 5. Add your component's **Microsoft Entra ID** redirect URI, found under [Component-specific configuration](#component-specific-configuration). :::note Redirect URIs serve as an approved list of destinations across identity providers. Only the URLs specified in the redirect URIs configuration will be permitted as valid redirection targets for authentication responses. This security measure ensures that tokens and authorization codes are only sent to pre-approved locations, preventing potential unauthorized access or token theft. ::: 6. [Create a new client secret](https://learn.microsoft.com/en-gb/entra/identity-platform/quickstart-register-app?tabs=client-secret#add-credentials), and note the new secret's value for later use. The secret ID is not needed, only the secret value is required. 7. Set the following environment variables or Helm values for the component you are configuring an app for: :::note You can connect to your OIDC provider through either environment variables or Helm values. Ensure only one configuration option is used. ::: ```yaml camunda: identity: type: "MICROSOFT" baseUrl: authUrl: https://login.microsoftonline.com//v2.0 tokenUrl: https://login.microsoftonline.com//v2.0 clientId: clientSecret: audience: identity: initialClaimName: initialClaimValue: spring: profiles: active: oidc ``` ``` CAMUNDA_IDENTITY_TYPE=MICROSOFT CAMUNDA_IDENTITY_BASE_URL= CAMUNDA_IDENTITY_ISSUER=https://login.microsoftonline.com//v2.0 CAMUNDA_IDENTITY_ISSUER_BACKEND_URL=https://login.microsoftonline.com//v2.0 CAMUNDA_IDENTITY_CLIENT_ID= CAMUNDA_IDENTITY_CLIENT_SECRET= CAMUNDA_IDENTITY_AUDIENCE= IDENTITY_INITIAL_CLAIM_NAME= IDENTITY_INITIAL_CLAIM_VALUE= SPRING_PROFILES_ACTIVE=oidc ``` :::danger Once set, your initial claim name and value cannot be updated using environment variables or Helm values, and must be changed directly in the database. ::: Additional considerations Due to technical limitations regarding [third party content](https://openid.net/specs/openid-connect-frontchannel-1_0.html#ThirdPartyContent), front channel single sign out is not supported. This means that when a user logs out of one component, they will not be logged out of the other components. For authentication, the management and modeling components use the scopes `email`, `openid`, `offline_access`, `profile`, and `/.default`. To ensure your users are able to successfully authenticate with Entra ID, you must ensure that either there is an [admin consent flow configured](https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/configure-admin-consent-workflow) or grant consent on behalf of your users using the [admin consent](https://learn.microsoft.com/en-gb/entra/identity/enterprise-apps/user-admin-consent-overview#admin-consent) process. The client should be configured to support `grant_type`: - To **create** an M2M token, the `client_credentials` grant type is required. The response contains an access token. - To **renew** a token using a refresh token, the `refresh_token` grant type is required. - To **create** a token via authorization flow, the `authorization_code` grant type is required. The response contains both access and refresh tokens. To successfully authenticate with Entra ID, you should use the `v2.0` API. This means that the `CAMUNDA_IDENTITY_ISSUER_BACKEND_URL` value should end with `/v2.0`. Follow the [Microsoft Entra instructions](https://learn.microsoft.com/en-us/entra/identity-platform/reference-microsoft-graph-app-manifest#configure-the-app-manifest-in-the-microsoft-entra-admin-center) to configure the app manifest, and set the [requestedAccessTokenVersion](https://learn.microsoft.com/en-us/entra/identity-platform/reference-microsoft-graph-app-manifest#api-attribute) under `Api:` to `2`: ```json "requestedAccessTokenVersion": 2, ``` ### Component-specific configuration | Component | Redirect URI | Notes/Limitations | | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Management Identity | **Microsoft Entra ID:** `https:///auth/login-callback` **Helm:** `https://` | | | Optimize | **Microsoft Entra ID:** `https:///api/authentication/callback` **Helm:** `https://` | There is a fallback if you use the existing environment variables to configure your authentication provider. If you use a custom `yaml`, update your properties to match the new values in this guide.When using an OIDC provider, the following Optimize features are not currently available: - The **User permissions** tab in collections- The **Alerts** tab in collections- Digests- Accessible usernames for owners of resources (the `sub` claim value is displayed instead). | | Web Modeler | **Microsoft Entra ID:** `https:///login-callback` **Helm:** `https://` | Web Modeler requires two clients: one for the UI, and one for the API. Required configuration variables for the `restapi` component: `OAUTH2_CLIENT_ID=[ui-client-id]` `CAMUNDA_IDENTITY_BASEURL=[identity-base-url]` `CAMUNDA_IDENTITY_TYPE=[provider-type]` `CAMUNDA_MODELER_SECURITY_JWT_AUDIENCE_INTERNAL_API=[ui-audience]` `CAMUNDA_MODELER_SECURITY_JWT_AUDIENCE_PUBLIC_API=[api-audience]` `SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI=[provider-issuer]` `SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI=[provider-jwks-url]`. | | Console | **Microsoft Entra ID:** `https://` **Helm:** `https://` | #### Example component values The examples below use these public URLs: - Management Identity: `https://identity.example.com` - Optimize: `https://optimize.example.com` - Web Modeler: `https://modeler.example.com` - Console: `https://console.example.com` With those URLs, configure the following redirect URIs in your OIDC provider: - Management Identity: `https://identity.example.com/auth/login-callback` - Optimize: `https://optimize.example.com/api/authentication/callback` - Web Modeler UI: `https://modeler.example.com/login-callback` - Console: `https://console.example.com/` For Web Modeler, the `restapi` component configuration varies by provider. The example below uses Keycloak; for other providers, retrieve the issuer and JWK set URLs from your provider's [OpenID configuration endpoint](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig) (typically `https:///.well-known/openid-configuration`): ```shell OAUTH2_CLIENT_ID=web-modeler CAMUNDA_IDENTITY_BASEURL=http://identity:8080 CAMUNDA_IDENTITY_TYPE=GENERIC CAMUNDA_MODELER_SECURITY_JWT_AUDIENCE_INTERNAL_API=web-modeler-api CAMUNDA_MODELER_SECURITY_JWT_AUDIENCE_PUBLIC_API=web-modeler-public-api SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI=https://idp.example.com/realms/camunda SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI=https://idp.example.com/realms/camunda/protocol/openid-connect/certs ``` Use the internal Identity base URL for `CAMUNDA_IDENTITY_BASEURL`, not the public redirect URL. For Microsoft Entra ID, replace the provider-specific values: ```shell CAMUNDA_IDENTITY_TYPE=MICROSOFT SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI=https://login.microsoftonline.com//v2.0 SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI=https://login.microsoftonline.com//discovery/v2.0/keys ``` ## Supported and unsupported OIDC features When using [Management Identity](/self-managed/components/management-identity/overview.md) with an OIDC provider, not all features of authentication and authorization are available. The following table lists OIDC supported and unsupported features. | Feature name | Description | Availability | | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :---------------------------------------------------------------------: | | Single sign-on (SSO) | Redirects users to the identity provider for authentication, and enables seamless login across applications. Zeebe, Operate, Tasklist, Optimize, Identity, and Connectors use an identity provider-issued JWT. Web Modeler and Console use PCKE (Proof Key for Code Exchange) to connect to the Camunda Identity service. | | | Authentication flows (Authorization code flow with PKCE) | Securely handles user login using the recommended authorization code flow with PKCE (Proof Key for Code Exchange) for security. Web Modeler and Console use PCKE, but do not directly connect to the identity provider with OIDC. Web Modeler and Console use PCKE to connect to the Camunda Identity service. | | | ID token handling | Validates and extracts user identity details from the ID token after authentication. | | | Access token management | Uses access tokens issued by the identity provider to securely access protected APIs. | | | Session management | Ensures users remain logged in across sessions and applications or automatically re-authenticate when needed. | | | Logout handling (RP-initiated logout) | Triggers user logout from the application and notifies the identity provider to sign out users across all connected systems. | | | Role and group synchronization | Maps user roles and permissions from the identity provider into the client application for access control. | | | User profile management | Fetches user details from the UserInfo endpoint after authentication to personalize the user experience. | | To request a missing feature, please [contact us](/reference/contact.md). ## OIDC provider known limitations Camunda requires the `aud` (audience) claim in JWT access tokens for authentication, including machine-to-machine (M2M) flows. The claim must match the configured audience (see [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3)). OIDC providers that do not include the `aud` claim in access tokens issued via the OAuth 2.0 Client Credentials flow are not compatible with Camunda for M2M authentication. ### AWS Cognito AWS Cognito does not include the `aud` claim in Client Credentials access tokens, using `client_id` instead. Camunda rejects these tokens with errors such as `Token audiences are [], expected at least one of [...]`, resulting in `UNAUTHENTICATED` errors for Connectors and other M2M clients. Cognito is not currently supported for M2M authentication. For tracking and updates, see [camunda/camunda#44650](https://github.com/camunda/camunda/issues/44650). --- ## Configuration(Configuration) Configure Management Identity for your Camunda 8 Self-Managed deployment. This guide covers application-level configuration, including environment variables and IdP settings. :::info Deploying with Helm? If you deploy Camunda 8 Self-Managed with Helm, use the [Helm chart authentication and authorization guides](/self-managed/deployment/helm/configure/authentication-and-authorization/index.md) to configure OIDC and Management Identity: ::: ## Configure Management Identity IdP The default Camunda 8 Self-Managed deployment uses built-in Keycloak as an identity provider (IdP). You can configure your Management Identity IdP using the following options: | IdP configuration | Description | | :------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------- | | [Connect to an identity provider](./connect-to-an-oidc-provider.md) | Connect to an OpenID Connect (OIDC) authentication provider to replace Keycloak. | | [Connect to an existing Keycloak instance](./connect-to-an-existing-keycloak.md) | Connect Management Identity to your existing Keycloak instance. | | [Configure an external IdP using Keycloak](./configure-external-identity-provider.md) | Configure an external identity provider using Keycloak, such as OpenID Connect, SAML, LDAP, or Active Directory. | :::note - Management Identity relies on a PostgreSQL. When running Management Identity with an external OIDC provider, you can [connect to an alternative Database](./alternative-db.md) if your internal policies or compliance requirements prevent the use of PostgreSQL. - Keycloak is started as a component in a [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md) and [Helm](/self-managed/deployment/helm/install/quick-install.md) Camunda 8 self-managed deployment. ::: --- ## Initialize tenants for Optimize Configure initial [tenants](/components/concepts/multi-tenancy.md) for Optimize in Camunda 8 Self-Managed. ## About Optimize tenants Tenants managed within Management Identity only apply to [Optimize](../../optimize/overview.md). Furthermore, they're only effective when the following conditions are met: - You've [enabled multi-tenancy checks for your Orchestration Cluster](/components/admin/tenant.md). - Your tenants have the same identifiers in Orchestration Cluster Admin and Management Identity. In this guide, you'll learn how to initialize tenants in your app configuration. ## Before you begin Before you begin, [configure a database](../miscellaneous/configuration-variables.md#database-configuration). Management Identity requires a database to support multi-tenancy When deploying Camunda 8 with Docker, you can programmatically configure tenants in Management Identity in two ways: - `application.yaml` - Environment variables When using Helm to deploy Camunda 8, you must configure tenants using environment variables. Configuration using [Helm values](https://artifacthub.io/packages/helm/camunda/camunda-platform#parameters) is not supported. ## Initialize tenants in Management Identity First, enable the Management Identity multi-tenancy flag: ```yaml identity: flags: multi-tenancy: "true" ``` ```sh MULTITENANCY_ENABLED=true ``` With multi-tenancy enabled, initialize your tenants: ```yaml identity: tenants: - name: My tenant tenantId: my-tenant members: - type: USER username: username - type: GROUP group-name: group name - type: APPLICATION application-id: application-id ``` Each member type has a corresponding property you use to set the member identifier: | Member type | Property | | ------------- | ---------------- | | `USER` | `username` | | `GROUP` | `group-name` | | `APPLICATION` | `application-id` | In some contexts, like the Management Identity UI, the "Application ID" is referred to as the "Client ID". ```sh IDENTITY_TENANTS_0_NAME="My tenant" IDENTITY_TENANTS_0_TENANTID="my-tenant" IDENTITY_TENANTS_0_MEMBERS_0_TYPE="USER" IDENTITY_TENANTS_0_MEMBERS_0_USERNAME="username" IDENTITY_TENANTS_0_MEMBERS_1_TYPE="GROUP" IDENTITY_TENANTS_0_MEMBERS_1_GROUPNAME="group name" IDENTITY_TENANTS_0_MEMBERS_2_TYPE="APPLICATION" IDENTITY_TENANTS_0_MEMBERS_2_APPLICATIONID="application-id" ``` Each member type has a corresponding property you use to set the member identifier: | Member type | Property | | ------------- | -------------------------------------------- | | `USER` | `IDENTITY_TENANTS_0_MEMBERS_0_USERNAME` | | `GROUP` | `IDENTITY_TENANTS_0_MEMBERS_0_GROUPNAME` | | `APPLICATION` | `IDENTITY_TENANTS_0_MEMBERS_0_APPLICATIONID` | In some contexts, like the Management Identity UI, the "Application ID" is referred to as the "Client ID". ## Next steps - [Enable multi-tenancy checks for your Orchestration Cluster](/components/admin/tenant.md) - [Manage tenants in the Management Identity application](../manage-tenants.md) --- ## Get started with Management Identity Get started with Management Identity in Self-Managed by learning how to open and log in to the Management Identity interface. :::note Management Identity is included in the [Docker-Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md) and [Helm charts](/self-managed/deployment/helm/install/quick-install.md) deployments of Camunda 8 Self-Managed. With the default configuration, Management Identity uses an included Keycloak container/pod. ::: ## Log in to Management Identity Once Management Identity has successfully started, you can open the **Log in** page and log in to Management Identity. If you are running the default configuration, you can access the Management Identity interface via the following URLs: - [Docker-Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md): `http://localhost:8084/` - [Helm](/self-managed/deployment/helm/install/quick-install.md): Follow your [`port-forward` or Ingress configuration](/self-managed/deployment/helm/configure/ingress/accessing-components-without-ingress.md) - [Manual](/self-managed/deployment/manual/install.md): `http://localhost:8080/` ## Default user In the default configuration, Management Identity creates a default example user during installation. You can log in with this example user account using the following credentials: ```text Username: demo Password: demo ``` :::tip Want to create more users? Management Identity uses the users managed in Keycloak. To create a user, refer to [Keycloak's documentation on creating a user](https://www.keycloak.org/docs/latest/server_admin/#proc-creating-user_server_administration_guide) for your version of Keycloak. ::: ## Management Identity home page You are directed to the home page once you have successfully logged in. ![identity-landing-page](./img/identity-landing-page.png) ## Next steps Once you log in to Management Identity, you can start managing authentication, access and permissions to Web Modeler, Console and Optimize. - [Manage users, groups, roles, and applications](application-user-group-role-management/identity-application-user-group-role-management-overview.md) - [Manage access and permissions](access-management/access-management-overview.md) - [Manage tenants](manage-tenants.md) :::info Learn more about how to [configure Management Identity](configuration/identity-configuration-overview.md) and [authentication](authentication.md). ::: --- ## Tenants for Optimize Multi-tenancy in Camunda 8 allows a single installation to host multiple tenants (such as departments, teams, or external clients) while maintaining per-tenant isolation of data and processes in a shared environment. :::info To learn more, see [multi-tenancy](/components/concepts/multi-tenancy.md). ::: ## About Optimize tenants Tenants managed within Management Identity **apply only to [Optimize](../optimize/overview.md)**, allowing you to isolate data access for reports and dashboards. This is effective only if you have [multi-tenancy checks enabled for your Orchestration Cluster](/components/admin/tenant.md). If you enable multi-tenancy for Optimize, you must create tenants with the same identifiers as those configured for your Orchestration Cluster. This alignment is important for correct data isolation and association in Optimize. ## Enable multi-tenancy for Optimize By default, multi-tenancy is disabled in Management Identity. To enable multi-tenancy: 1. Enable the [`MULTITENANCY_ENABLED` feature flag](/self-managed/components/management-identity/miscellaneous/configuration-variables.md#feature-flags). 2. [Configure a database](/self-managed/components/management-identity/miscellaneous/configuration-variables.md#database-configuration). ## Create a tenant :::note A `` tenant is automatically created during Identity startup. ::: 1. Log in to Management Identity and select the **Tenants** tab. ![tenant-management-tab](./img/tenant-management-tab.png) 2. Click **Create Tenant** and a modal will open. 3. Enter a name and ID for the tenant, and click **Create tenant**: ![tenant-management-modal-1](./img/tenant-management-modal-1.png) On creation, the modal closes and the table updates with your new tenant. 4. Click on your new tenant to view the details: ![tenant-management-details](./img/tenant-management-details.png) ## Tenant assignments To assign [users, groups and applications](./application-user-group-role-management/identity-application-user-group-role-management-overview.md) to a tenant: ### Assign users to a tenant 1. Click **Assigned users** to view the users assigned to the tenant, and click **Assign users**: ![tenant-management-assign-users](./img/tenant-management-assign-users-tab.png) 1. Search and select the users to assign to the tenant. After selecting the users, click **Assign users**: ![tenant-management-assign-users-modal](./img/tenant-management-assign-users-modal.png) On confirmation, the modal closes, the table updates, and the assigned users are shown: ![tenant-management-assign-users-refreshed](./img/tenant-management-assign-users-refreshed.png) ### Assign groups to a tenant 1. Click **Assigned groups** to view the groups assigned to the tenant, and click **Assign groups**: ![tenant-management-assign-groups](./img/tenant-management-assign-groups-tab.png) 1. Search and select the groups to assign to the tenant. After selecting the groups, click **Assign groups**: ![tenant-management-assign-groups-modal](./img/tenant-management-assign-groups-modal.png) On confirmation, the modal closes, the table updates, and the assigned groups are shown: ![tenant-management-assign-groups-refreshed](./img/tenant-management-assign-groups-refreshed.png) ### Assign applications to a tenant 1. Click **Assigned applications** to view the applications assigned to the tenant, and click **Assign applications**: ![tenant-management-assign-applications](./img/tenant-management-assign-applications-tab.png) 1. Search and select the applications to assign to the tenant. After selecting the applications, click **Assign applications**: ![tenant-management-assign-applications-modal](./img/tenant-management-assign-applications-modal.png) On confirmation, the modal closes, the table updates, and the assigned applications are shown: ![tenant-management-assign-applications-refreshed](./img/tenant-management-assign-applications-refreshed.png) --- ## Mapping rules(Management-identity) Use mapping rules to dynamically assign Management Identity entities to your users based on claims in your JWT tokens. :::note You can only use mapping rules if Management Identity is configured to use [OIDC-based authentication](/self-managed/components/management-identity/configuration/connect-to-an-oidc-provider.md). ::: ## About mapping rules You can assign two types of entities with mapping rules: - Tenants - Roles :::note A `Default` mapping rule is created during startup using the [IDENTITY_INITIAL_CLAIM_NAME and IDENTITY_INITIAL_CLAIM_VALUE environment variables](/self-managed/components/management-identity/miscellaneous/configuration-variables.md#oidc-configuration) to allow an initial user access to the Identity interface. Once you have access to the Identity interface, configure the additional mapping rules to ensure your users have the correct access to the Camunda components. ::: ## Add a mapping rule 1. Log in to the Identity interface and navigate to the **Mappings** tab. ![mapping-rule-management-tab](./img/mapping-rule-management-tab.png) 1. Click the **Add mapping** button and select the type of mapping to create. You can create a mapping for a role or tenant. ![mapping-rule-add](./img/mapping-rule-add-mapping.png) 1. Fill in the fields for the mapping rule and click **Create**. ![mapping-rule-add-modal](./img/mapping-rule-add-mapping-modal.png) :::note The operator option is used to define how we evaluate the rules against your tokens. The options are: - **Contains**: Used for array-based claims, such as a list of roles. - **Equals**: Used for string-based claims, such as a string ID. ::: The created mapping rule can be seen in the table. ![mapping-rule-refreshed-table](./img/mapping-rule-refreshed-table.png) ## Edit a mapping rule 1. Click the pencil icon on the row of the mapping rule you want to update. 2. Update the fields for the mapping rule and click **Update**. :::note You can only update the entities applied by the mapping rule and the claims used to map the entities. You cannot update the mapping rule type. ::: ## Delete a mapping rule Click on the **trash can** icon on the row of the mapping rule you want to delete. ![mapping-rule-delete-modal](./img/mapping-rule-delete-modal.png) After confirming, the mapping rule is deleted and no longer appears in the table. --- ## Monitor Management Identity Monitor the health and operation of the Management Identity component in your Self-Managed deployment. ## Monitoring endpoints You can use the following default exposed endpoints to monitor how the Management Identity component is operating. | Endpoint | Default port | Purpose | | :--------------------- | :----------- | :------------------------------------------------------------------------- | | `/actuator/health` | `8082` | Provide the health status of the application, often used in health checks. | | `/actuator/prometheus` | `8082` | Provide operational application metrics. | --- ## Configuration variables As a Spring Boot application, Identity supports any standard [Spring configuration](https://docs.spring.io/spring-boot/reference/features/external-config.html) method. ## Core configuration The core configuration variables are as follows: | Environment variable | Description | Default value | | :----------------------------------- | :---------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `IDENTITY_AUTH_PROVIDER_BACKEND_URL` | Used to support container to container communication. | http://localhost:18080/auth/realms/camunda-platform | | `IDENTITY_AUTH_PROVIDER_ISSUER_URL` | Used to denote the token issuer. | http://localhost:18080/auth/realms/camunda-platform | | `IDENTITY_BASE_PATH` | Used to configure Identity to run on a subpath (Requires HTTPs for `IDENTITY_URL`). | | | `IDENTITY_CLIENT_ID` | The client ID for the Identity client. | camunda-identity | | `IDENTITY_CLIENT_SECRET` | The client secret for the Identity client. | | | `IDENTITY_LOG_LEVEL` | The level of which to log messages at. | INFO | | `IDENTITY_LOG_PATTERN` | The pattern to use when logging. | `%clr{%d{yyyy-MM-dd HH:mm:ss.SSS}}{faint} %clr{%5p} %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n%xwEx` | | `IDENTITY_URL` | The URL of the Identity service. | http://localhost:8080 | | `KEYCLOAK_REALM` | The name of the Keycloak Realm to connect to. | camunda-platform | | `KEYCLOAK_SETUP_USER` | The username of a user with admin access to Keycloak. | admin | | `KEYCLOAK_SETUP_PASSWORD` | The password of a user with admin access to Keycloak. | admin | | `KEYCLOAK_SETUP_REALM` | The realm that the setup user is in. | master | | `KEYCLOAK_SETUP_CLIENT_ID` | The client to use for authentication during setup of the provided Keycloak. | admin-cli | | `KEYCLOAK_URL` | The URL of the Keycloak instance to use. | http://localhost:18080/auth | ## Camunda Identity SDK configuration Use the following names and values for the Identity SDK to ensure proper authentication and authorization with Identity and the IdP for all components. | Environment variable | Property | Description | Default value | | :----------------------------------- | :-------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------- | | `CAMUNDA_IDENTITY_ISSUERBACKENDURL` | `camunda.identity.issuer-backend-url` | The back-channel URL to the Identity provider, used for token verification. | - | | `CAMUNDA_IDENTITY_AUDIENCE` | `camunda.identity.audience` | The required audience of the auth token. | - | | `CAMUNDA_IDENTITY_TYPE` | `camunda.identity.type` | Define what kind of authentication type you will use (`KEYCLOAK`, `MICROSOFT`, `GENERIC`). | `KEYCLOAK` | | `CAMUNDA_IDENTITY_BASEURL` | `camunda.identity.base-url` | The base URL of the Camunda Identity instance. | - | | `CAMUNDA_IDENTITY_ISSUER` | `camunda.identity.issuer` | The front-channel URL to the Identity provider, used for login redirect, fetching refresh tokens and logout. | - | | `CAMUNDA_IDENTITY_JWKSURL` | `camunda.identity.jwks-url` | Defines the JWKS URL, which is used by the services to validate the JWT tokens. If nothing is set, it will use the WellKnownEndpoint. | - | | `CAMUNDA_IDENTITY_CLIENTID` | `camunda.identity.client-id` | Defines the client ID, which is used by Zeebe in authentication flows. | - | | `CAMUNDA_IDENTITY_CLIENTSECRET` | `camunda.identity.client-secret` | The client secret for the Identity client. | - | | `CAMUNDA_IDENTITY_AUTHSCOPES` | `camunda.identity.auth-scopes` | Defines the scopes that should be applied to the token, provided as list separated by spaces. | `openid email offline_access` | | `CAMUNDA_IDENTITY_USEBACKENDAUTHURL` | `camunda.identity.use-backend-auth-url` | Whether the fetching refresh tokens and logout will be performed against the `issuer` or the `issuer-backend-url`. | `false` | | `CAMUNDA_IDENTITY_CLOCKSKEW` | `camunda.identity.clock-skew` | Sets the allowed clock skew when validating JWT issuance and expiration. Format: ISO 8601 | `60S` | | `CAMUNDA_IDENTITY_MAXSAVEPOSTSIZE` | `camunda.identity.max-save-post-size` | Maximum bytes Tomcat buffers per connection during the HTTP/1.1 to HTTP/2 (h2c) upgrade. Set to `-1` to remove the limit. | `131072` | ## License configuration See [licensing](/self-managed/components/orchestration-cluster/core-settings/configuration/licensing.md). ## OIDC configuration Claims are name/value pairs used to represent an individual identity. Configure your initial claim and value to match the claim used with your OIDC provider. For example, to use your Microsoft Entra unique account ID, set `IDENTITY_INITIAL_CLAIM_NAME` to `oid`, and `IDENTITY_INITIAL_CLAIM_VALUE` to the ID. :::caution Once set, you cannot update your initial claim name and value using environment or Helm values. You must change these values directly in the database. ::: | Environment variable | Description | Default value | | :----------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------- | :------------ | | `IDENTITY_INITIAL_CLAIM_NAME` | The type of claim to use for the initial user. Examples can include `oid`, `name` or `email`. | `oid` | | `IDENTITY_INITIAL_CLAIM_VALUE` | The value of the claim to use for the initial user. For the default `oid`, the value usually corresponds to the unique ID of your user account. | | ## Component configuration Identity supports component configuration using preset values. To configure a component for use within Identity, set two variables: | Environment variable | Description | Default value | | :------------------------------------ | :---------------------------------------------- | :------------ | | `KEYCLOAK_INIT__SECRET` | The secret used for authentication flows. | No default | | `KEYCLOAK_INIT__ROOT_URL` | The root URL of where the component is hosted. | No default | | `KEYCLOAK_INIT__CLIENT_ID` | The client to create and use for the component. | `` | :::note Identity supports the following values for the `` placeholder: `OPERATE`, `OPTIMIZE`, `TASKLIST`, and `WEBMODELER`. For the `WEBMODELER` value, only the `KEYCLOAK_INIT__ROOT_URL` variable is required to be set. For the `KEYCLOAK_INIT__CLIENT_ID` value, the default is the component name in lowercase except for `WEBMODELER`, which is`web-modeler`. ::: ## Database configuration Identity requires a database to store information about resource authorization and [multi-tenancy](/components/concepts/multi-tenancy.md) or if you are [using an external IdP](/self-managed/components/management-identity/configuration/configure-external-identity-provider.md). | Environment variable | Description | | :--------------------------- | :-------------------------------------------------- | | `IDENTITY_DATABASE_HOST` | The host of the database. | | `IDENTITY_DATABASE_PORT` | The port of the database. | | `IDENTITY_DATABASE_NAME` | The name of the database to connect to. | | `IDENTITY_DATABASE_USERNAME` | The username of a user with access to the database. | | `IDENTITY_DATABASE_PASSWORD` | The password of a user with access to the database. | :::note There are no default values for the variables above. See [supported environments](/reference/supported-environments.md#camunda-platform-8-self-managed) for a list of supported databases. You may also review the [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md). ::: ### Running Identity on Amazon Aurora PostgreSQL Identity supports running on Amazon Aurora PostgreSQL. To connect Identity with your Amazon Aurora PostgreSQL instance, make the following configuration adjustments: 1. Modify the `SPRING_DATASOURCE_URL` environment variable: `jdbc:aws-wrapper:postgresql://[DB_HOST]:[DB_PORT]/[DB_NAME]`. 2. Add the environment variable `SPRING_DATASOURCE_DRIVER_CLASS_NAME` with the value `software.amazon.jdbc.Driver`. For a full list of available driver parameters visit the [AWS JDBC Driver documentation](https://github.com/awslabs/aws-advanced-jdbc-wrapper/wiki/UsingTheJdbcDriver#aws-advanced-jdbc-driver-parameters). #### AWS IAM authentication To use AWS Identity and Access Management (IAM) database authentication with your Amazon Aurora PostgreSQL instance, in addition to the adjustments described [above](#running-identity-on-amazon-aurora-postgresql), follow these steps: 1. Modify the `SPRING_DATASOURCE_URL` environment variable as follows: `jdbc:aws-wrapper:postgresql://[DB_HOST]:[DB_PORT]/[DB_NAME]?wrapperPlugins=iam`. 2. Modify the `SPRING_DATASOURCE_USERNAME` environment variable to match the database user you configured for AWS IAM authentication as described in the [Amazon Aurora documentation](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/UsingWithRDS.IAMDBAuth.DBAccounts.html#UsingWithRDS.IAMDBAuth.DBAccounts.PostgreSQL). 3. Remove the `SPRING_DATASOURCE_PASSWORD` environment variable. ## Feature flags Identity uses feature flag environment variables to enable and disable features; the supported flags are: | Environment variable | Description | Default value | | :--------------------------- | :----------------------------------------------- | :------------ | | RESOURCE_PERMISSIONS_ENABLED | Controls the resource authorizations feature. | false | | MULTITENANCY_ENABLED | Controls the multi-tenancy feature for Optimize. | false | :::note Setting either of the feature flags to `true` requires a database connection. To configure a database connection, see [database configuration](#database-configuration). ::: ## Logging ### Google Stackdriver (JSON) logging To enable Google Stackdriver compatible JSON logging, set the environment variable `IDENTITY_LOG_APPENDER=Stackdriver` on Identity. --- ## Configure logging Configure and use logging to access detailed operational information for Identity . ## Identity logging configuration The Identity component uses the [Apache Log4j2](https://logging.apache.org/log4j/2.x/) framework to control the log level and log format. The logging configuration included in the Identity image is as follows: ```xml %clr{%d{yyyy-MM-dd HH:mm:ss.SSS}}{faint} %clr{%5p} %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n%xwEx %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{1.} %enc{%msg}%n logs/identity.%d{yyyy-MM-dd-mm-ss}.log ``` ## General configuration options Identity provides support for configuring the log level: | Environment variable | Accepted values | | -------------------- | ------------------------------------------------ | | `IDENTITY_LOG_LEVEL` | OFF, FATAL, ERROR, WARN, INFO, DEBUG, TRACE, ALL | ## Supported logging outputs As part of configuration, Identity provides multiple appenders for outputting logs. To configure which logging appender is used, set the `IDENTITY_LOG_APPENDER` environment variable to either `Console`, `Stackdriver`, or `File`. ### `Console` Console logging produces messages to standard output and is the default log appender. The Console log appender offers additional configuration options as follows: | Environment variable | Accepted values | | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | `IDENTITY_LOG_PATTERN` | _See the [Log4j2 pattern layout docs](https://logging.apache.org/log4j/2.x/manual/layouts.html#PatternLayout) for possible placeholders._ | ### `Stackdriver` The Stackdriver log appender produces messages to standard output in a format that is compatible with the GCP cloud platform. This appender uses the [GCP layout](https://github.com/apache/logging-log4j2/blob/2.x/log4j-layout-template-json/src/main/resources/GcpLayout.json) provided by the [Log4j2](https://logging.apache.org/log4j/2.x/manual/) library. ### `File` The File log appender produces messages to a rotating log file. The File log appender offers additional configuration options as follows: | Environment variable | Accepted values | | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `IDENTITY_LOG_FILE_PATTERN` | _See the [Log4j2 pattern layout docs](https://logging.apache.org/log4j/2.x/manual/layouts.html#PatternLayout) for possible placeholders._ | | `IDENTITY_LOG_FILE_ROTATION_DAYS` | _See the [Log4j2 time-based triggering policy -> interval](https://logging.apache.org/log4j/2.x/manual/appenders.html#timebased-triggering-policy) for possible values._ | | `IDENTITY_LOG_FILE_ROTATION_SIZE` | _See the [Log4j2 size-bsed triggering policy](https://logging.apache.org/log4j/2.x/manual/appenders.html#sizebased-triggering-policy) for possible values._ | ## Custom logging configuration You can provide your own logging configuration by mounting a configuration file to the Identity container and setting the path to the file using the following variable: | Environment variable | Purpose | | -------------------- | ------------------------------------------------------------------------------------------------------------- | | `LOGGING_CONFIG` | The path to your [Log4j2 config XML](https://logging.apache.org/log4j/2.x/manual/configuration.html#XML) file | :::note To write logs to a file in a containerized environment, the mounted directory containing the log file has to be writable under the user running Identity. ::: --- ## Prepare Identity for production When moving Identity to a production environment, you should consider the following. ## Keycloak dependency As Keycloak is an external-based dependency of Identity, Camunda recommends looking at [Keycloak's documentation on production configuration](https://www.keycloak.org/server/configuration-production) to ensure your Keycloak instance is production-ready. ### Backing up To ensure recovery is possible, Camunda recommends regularly backing up the database that supports Keycloak. #### Helm deployment If you deployed Camunda 8 using Camunda [Helm charts](/self-managed/setup/overview.md), by default there will be a Postgres database deployed with it. In this case, Camunda recommends reading the [Postgres documentation](https://www.postgresql.org/docs/current/backup.html) for guidance on backing up. #### Alternative deployment If your Keycloak service uses a different database provider than Postgres, Camunda recommends referencing the backup section of the documentation for your chosen provider and version. ## Enable TLS A safe and healthy exchange of secure data requires Transport Layer Security (TLS). - TLS support for Identity can be enabled by setting configuration values. Refer to [Spring - Configure SSL](https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto.webserver.configure-ssl) for more information. - To enable TLS alongside Keycloak, refer to the Keycloak documentation regarding [TLS enablement](https://www.keycloak.org/server/enabletls). ## Setting Identity URL To ensure authentication flows are successful, the `IDENTITY_URL` should be set to the URL of the Identity service. --- ## Keycloak resource management When using Keycloak as an IdP, Identity uses the following Keycloak resources: | Identity resource | Keycloak resource (and how it is filtered) | | :---------------------------- | :---------------------------------------------- | | Application | Client (without authorization) | | Application/permissions | Client/Service Account roles | | API | Client (with authorization) | | API/permissions | Client/Roles | | Role | Realm role (with attribute `camunda_role=true`) | | Role/permissions | Realm role/Associated role | | Group | Group | | Group/Member | Group/Member | | Group/Authorization\* | (not saved to Keycloak) | | Group/Roles | Group/Role mapping | | User | User | | User/Assigned role | User/Role mapping | | User/Authorization\* | (not saved to Keycloak) | | Tenant\* | (not saved to Keycloak) | | Tenant/Assigned user\* | (not saved to Keycloak) | | Tenant/Assigned group\* | (not saved to Keycloak) | | Tenant/Assigned application\* | (not saved to Keycloak) | \* This resource is only activated with the relevant feature flag. --- ## Starting configuration for Identity Identity requires a set of base configurations to operate correctly. When Identity is started, it will create or update the following entities in Keycloak. ## Clients | Name | Client ID | Service accounts | Created/updated with component | | :------------------------------- | :------------------------------- | :--------------- | :----------------------------- | | Identity | camunda-identity | enabled | All | | Camunda Identity Resource Server | camunda-identity-resource-server | enabled | All | | Operate | operate | enabled | Operate | | Operate API | operate-api | enabled | Operate | | Optimize | optimize | enabled | Optimize | | Optimize API | optimize-api | enabled | Optimize | | Tasklist | tasklist | enabled | Tasklist | | Tasklist API | tasklist-api | enabled | Tasklist | | Web Modeler | web-modeler | disabled | Web Modeler | | Web Modeler API | web-modeler-api | enabled | Web Modeler | ## Roles | Name | Created/updated with component | | :---------- | :----------------------------- | | Identity | All | | Operate | Operate | | Optimize | Optimize | | Tasklist | Tasklist | | Web Modeler | Web Modeler | ## Client scopes | Name | Protocol | Description | | :--------------- | :------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | camunda-identity | openid-connect | A default client scope that contains mappers to augment the token generated with information required by the components of Camunda. Contains the mappers described in the [mappers](#mappers) section. | ## Mappers | Name | Protocol Mapper | Description | | :--------------- | :-------------------------------- | :-------------------------------------------------------------------------------------------------------- | | email | oidc-usermodel-property-mapper | Adds the email user attribute to the `access`, `ID`, and `user info` tokens using the claim name `email`. | | full name | oidc-full-name-mapper | Adds the user's full name to the `access`, `ID`, and `user info` tokens. | | permissions | oidc-usermodel-client-role-mapper | Adds the user's client roles to the `access` token with the claim name `permissions.${client_id}`. | | audience resolve | oidc-audience-resolve-mapper | Adds the audiences the user has access to in the `audience` claim. | --- ## Troubleshooting Management Identity Troubleshoot common issues with Management Identity in a Self-Managed deployment. ## Management Identity pod crashloops/continually restarts If the Management Identity pod crash loops, or the pod continually restarts, it is likely there is an issue with the Keycloak connection. ### Solution 1. Find the name of the Management Identity pod by running: ``` kubectl get pods ``` The output should look similar to: ``` NAME READY STATUS RESTARTS AGE c8-local-identity-6fd96d59c4-8lzxv 1/1 Running 2 (24s ago) 85s c8-local-keycloak-0 1/1 Running 0 30m c8-local-operate-69b765f7bb-hjcts 1/1 Running 0 30m c8-local-postgresql-0 1/1 Running 0 30m c8-local-zeebe-0 1/1 Running 0 30m c8-local-zeebe-gateway-678f4c7bfb-w8ght 1/1 Running 0 30m elasticsearch-master-0 1/1 Running 0 30m ``` 2. Using the pod name from the output above, view the logs: ``` kubectl logs ``` 3. Observe the most recent logs for an error message or stacktrace, for example: ``` 2022-07-04 15:52:04.250 ERROR 1 --- [main] i.c.i.i.k.config.KeycloakConfiguration : Failure #1. Unable to connect to Keycloak. 2022-07-04 15:52:09.252 WARN 1 --- [main] i.c.i.i.k.config.KeycloakConfiguration : Retrying... ``` ## Management Identity is unable to connect to Keycloak If you are seeing an error message like the one below in your Management Identity service logs, there is an issue with the connection Management Identity is trying to make and the Keycloak service: ``` 2022-07-04 15:52:04.250 ERROR 1 --- [main] i.c.i.i.k.config.KeycloakConfiguration : Failure #1. Unable to connect to Keycloak. ``` This can be caused by: - The Keycloak service has not started/is not ready. - Management Identity making requests from an external IP address. See details on resolving these issues below. ### Solution 1: The Keycloak service has not started/is not ready The Keycloak service can take time to start due to the supporting systems. Keycloak is ready to accept connections when the following log lines are visible: ``` 15:24:24,094 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: Keycloak 16.1.1 (WildFly Core 18.0.4.Final) started in 33171ms - Started 718 of 1020 services (699 services are lazy, passive or on-demand) 15:24:24,098 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:9990/management 15:24:24,100 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:9990 ``` When the Keycloak service is ready for connections, start (or restart) the Management Identity pod. ### Solution 2: Management Identity making requests from an external IP address By default, Keycloak requires TLS on requests that originate from what it considers to be an external source. The Keycloak documentation for [setting up SSL](https://www.keycloak.org/docs/latest/server_admin/#_ssl_modes) maintains a list of what they consider to be an external IP address under the `external requests` section. The solution to this issue depends largely on your environment. As a starting point, consider these options: - Configure communication between the services (for example in a cluster) to use IP ranges within the ranges Keycloak expects. - If configuring IP ranges is not an option, you can disable the SSL requirement in Keycloak itself: 1. In the `master` realm, set `Require SSL` to `none` by following the steps in [SSL modes](https://www.keycloak.org/docs/latest/server_admin/#_ssl_modes) for your version of Keycloak. 2. Restart the Management Identity service. 3. In the `camunda-platform` realm, set `Require SSL` to `none` by following the steps in [SSL modes](https://www.keycloak.org/docs/latest/server_admin/#_ssl_modes) for your version of Keycloak. 4. Restart the Management Identity service again. Management Identity should now start successfully. :::danger warning Camunda would only recommend that requirements for SSL are disabled in a development environment. ::: --- ## Management Identity The Management Identity component in Camunda 8 Self-Managed is used to manage authentication, access, and authorization for components outside the [Orchestration Cluster](/self-managed/components/orchestration-cluster/overview.md): [Camunda Hub](../hub/index.md) and [Optimize](../optimize/overview.md). Management Identity controls who can sign in to Console, Modeler, and Optimize, which is separate from the cluster identity stack provided by [Admin (formerly Orchestration Cluster Identity)](/self-managed/components/orchestration-cluster/admin/overview.md). Admin controls access to Zeebe, Operate, Tasklist, and the Orchestration Cluster API within each cluster. ## About Management Identity Management Identity is included by default in the [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md) and [Helm charts](/self-managed/deployment/helm/install/quick-install.md) deployments of Camunda 8 Self-Managed, and is configured by default to use a packaged Keycloak instance as an identity provider (IdP). - Administrators can use Management Identity to manage Camunda 8 users, groups, roles, permissions, and applications. - Users (interacting via Camunda web components) and applications (interacting via Camunda APIs, such as job workers) are supported, using secure authorization based on OAuth 2.0 standards. - Users log in to web components via an IdP login page. Applications authenticate via machine-to-machine (M2M) tokens. - You can integrate Management Identity with an [external OIDC provider](./configuration/connect-to-an-oidc-provider.md), [connect to an existing Keycloak instance](./configuration/connect-to-an-existing-keycloak.md), or [configure an external IdP using Keycloak](./configuration/configure-external-identity-provider.md). ## Get started If you're new to Management Identity, learn how to access and log in to the Management Identity UI. - [Get started with Management Identity](identity-first-steps.md) ## Core concepts Learn about the core concepts of Management Identity. ### Authentication Management Identity supports two types of authentication: - **Web login**: Users access web applications through an IdP login page. - **Machine-to-machine (M2M)**: Applications authenticate using tokens for API access. Both methods use the [OAuth 2.0 protocol](https://oauth.net/2/) for secure authentication. - [Learn about authentication methods](authentication.md) ### User, group, role, and application management Organize and control access using a role-based access control (RBAC) model. - [Manage users, groups, roles, and applications](application-user-group-role-management/identity-application-user-group-role-management-overview.md) ### Access management Control who can access what by assigning permissions through roles. - [Manage access and permissions](access-management/access-management-overview.md) ### Multi-tenancy Isolate data and access in Optimize between different customers or business units by organizing resources into tenants. This is effective only if you have [multi-tenancy checks enabled for your Orchestration Cluster](/components/admin/tenant.md). - [Manage tenants for Optimize](manage-tenants.md) ### Mapping rules Automatically assign roles and tenants to users based on information in their authentication tokens (JWT claims). This enables dynamic access control when integrating with external identity providers. - [Configure mapping rules](mapping-rules.md) ## Reference - [Configure Management Identity](configuration/identity-configuration-overview.md) - [Monitor Management Identity](miscellaneous/application-monitoring.md) - [Configuration variables](miscellaneous/configuration-variables.md) - [Configure logging](miscellaneous/configure-logging.md) - [Prepare Management Identity for production](miscellaneous/making-identity-production-ready.md) - [Keycloak resource management](miscellaneous/resource-management.md) - [Starting configuration](miscellaneous/starting-configuration.md) - [Troubleshoot Management Identity](miscellaneous/troubleshoot-identity.md) --- ## Set up a connection To deploy diagrams, start process instances, or test tasks, you must first connect Desktop Modeler to a Camunda 8 Orchestration Cluster. Follow the steps below to set up a connection. To connect to **Camunda 8 SaaS**, visit the [Camunda 8 SaaS guide](../../../../components/modeler/desktop-modeler/connect-to-camunda-8.md). 1. Click the **Connection manager**. For new installations, this will show **No connection**. If you have previously selected a connection, it will show the name of that connection. ![Connection manager button](./img/connection-selector-offline.png) 2. To add a new connection, open the settings. You can either open the connection manager and click **Manage connections** or open the settings directly (`Cmd/Ctrl + ,`). By default, a local c8run connection is already configured. If you have previously used Desktop Modeler to deploy a diagram, that connection will also be available as **Unnamed Connection**. You can rename this connection to something more descriptive in the connection manager settings. ![Connection manager is opened](./img/connection-selector-offline-open.png) 3. Click **Add connection**. ![Connection manager showing add button](./img/connection-manager-add.png) 4. Select **Camunda 8 Self-Managed** as the target. ![empty self-managed connection](./img/connection-with-sm.png) 5. Enter the cluster URL, and optionally the tenant ID and Operate URL. :::caution You can connect to Camunda 8 both securely and insecurely through the `https` and `http` protocols. Secured connections to a remote endpoint are established only if the remote server certificate is trusted by the app. Ensure that root and intermediate certificates you trust are [known to the app](/components/modeler/desktop-modeler/flags/flags.md#zeebe-ssl-certificate). Multi-tenancy is available only when authentication is enabled through [Orchestration Cluster Admin](../../orchestration-cluster/admin/overview.md). ::: ![deployment via Camunda 8](./img/connection-with-endpoint.png) 6. Select your authentication method, and enter the required credentials. For **Basic authentication**, enter your username and password. ![Basic authentication configuration](./img/connection-with-basic-auth.png) For **OAuth**, enter the credentials for your OAuth provider. These credentials are configured during the default [Helm installation](/self-managed/deployment/helm/install/quick-install.md). You can find them in [Orchestration Cluster Admin](/self-managed/components/orchestration-cluster/admin/overview.md) or set them using Zeebe [environment variables](/self-managed/components/orchestration-cluster/zeebe/security/client-authorization.md#environment-variables). :::note When using Modeler to deploy a process model or start a process instance, you may run into issues with [resource authorizations](/components/concepts/access-control/authorizations.md). Make sure your [client](/components/admin/client.md) has the right authorizations assigned to it. ::: ![oauth configuration](./img/connection-with-oauth.png) | Name | Description | Example value | | --------------- | --------------------------------------- | ---------------------------------------------------------------------------------------- | | Client ID | The name of your Zeebe client. | `zeebe` | | Client secret | The password of your Zeebe client. | `zecret` | | OAuth token URL | The full path to the token endpoint. | `https:///auth/realms/camunda-platform/protocol/openid-connect/token` | | OAuth audience | The permission name for Zeebe. | `zeebe.example.com` | | OAuth scope | The permissions available to the token. | `Zeebe,Tasklist,Operate` | If the connection is established successfully, you can leave the settings and go back to the connection manager, where your new connection is now available. 7. Select the connection you just created to use it for [deployment](./deploy-to-self-managed.md) or other tools like [task testing](../../../../components/modeler/desktop-modeler/task-testing.md) or [starting a new process instance](../../../../components/modeler/desktop-modeler/start-instance.md): ![New connection selected in connection manager](./img/connection-selector-new-connection.png) :::note As a next step, [deploy your diagram](./deploy-to-self-managed.md). ::: --- ## Deploy diagram Desktop Modeler can directly deploy diagrams and start process instances in Camunda 8 Self-Managed. Follow the steps below to deploy a diagram: 1. Ensure you have already [set up a connection](./connect-to-self-managed.md) and selected it. ![Self-Managed cluster connected](./img/deploy-selection.png) 2. Click the **Deploy** icon: ![deploy icon](./img/deploy-icon.png) 3. Click **Deploy** to perform the actual deployment: ![deploy diagram](./img/deploy-diagram.png) 4. If the deployment is successful, you will see a confirmation message: ![deployment successful](./img/deploy-success.png) --- ## History cleanup To satisfy data protection laws or just for general storage management purposes, Optimize provides an automated cleanup functionality. There are two types of history cleanup: - Process data cleanup - External variable cleanup By default, all types of history cleanup are disabled. They can be enabled individually by config and the cleanup is applied accordingly. ## Setup The most important settings are `cronTrigger` and `ttl`; their global default configuration is the following: ``` historyCleanup: cronTrigger: '0 1 * * *' ttl: 'P2Y' ``` `cronTrigger` - defines at what interval and when the history cleanup should be performed in the format of a cron expression. The default is 1AM every day. To avoid any impact on daily business, it is recommended to schedule the cleanup outside of business hours. See the [Configuration Description](./system-configuration.md#history-cleanup-settings) for further insights into this property and its format. `ttl` - is the global time to live period of data contained in Optimize. The field that defines the age of a particular entity differs between process, decision, and event data. Refer to the corresponding subsection in regard to that. The default value is `'P2Y'`, which means by default data older than _2 years_ at the point in time when the cleanup is executed gets cleaned up. For details on the notation, see the [Configuration Description](./system-configuration.md#history-cleanup-settings) of the ttl property. All the remaining settings are entity type specific and will be explained in the following subsections. ## Process data The age of process instance data is determined by the `endTime` field of each process instance. Running instances are never cleaned up. To enable the cleanup of process instance data, the `historyCleanup.processDataCleanup.enabled` property needs to be set to `true`. Another important configuration parameter for process instance cleanup is the `historyCleanup.processDataCleanup.cleanupMode`. It determines what in particular gets deleted when a process instance is cleaned up. The default value of `all` results in the whole process instance being deleted. For other options, review the [configuration description](./system-configuration.md#history-cleanup-settings) of the `historyCleanup.processDataCleanup.cleanupMode` property. To set up a process definition-specific `ttl` or different `cleanupMode` you can also provide process specific settings using the `perProcessDefinitionConfig` list which overrides the global settings for the corresponding definition key. In this example, process instances of the key `MyProcessDefinitionKey` would be cleaned up after two months instead of two years, and when the cleanup is performed, only their associated variables would be deleted instead of the complete process instance. ``` historyCleanup: ttl: 'P2Y' processDataCleanup: enabled: true cleanupMode: 'all' perProcessDefinitionConfig: 'MyProcessDefinitionKey': ttl: 'P2M' cleanupMode: 'variables' ``` ## Example Here is an example of what a complete cleanup configuration might look like: ``` historyCleanup: cronTrigger: '0 1 * * 0' ttl: 'P1Y' processDataCleanup: enabled: true cleanupMode: 'variables' perProcessDefinitionConfig: 'VeryConfidentProcess': ttl: 'P1M' cleanupMode: 'all' 'KeepTwoMonthsProcess': ttl: 'P2M' ``` The above configuration results in the following setup: - The cleanup is scheduled to run every Sunday at 1AM. - The global `ttl` of any data is one year. - The process data cleanup is enabled. - The `cleanupMode` performed on all process instances that passed the `ttl` period is just clearing their variable data but keeping the overall instance data like activityInstances. - There is a process specific setup for the process definition key `'VeryConfidentProcess'` that has a special `ttl` of one month and those will be deleted completely due the specific `cleanupMode: 'all'` configuration for them. - There is another process specific setup for the process definition key `'KeepTwoMonthsProcess'` that has a special `ttl` of two months. --- ## Localization(Configuration) To present a localized version of Optimize to users corresponding to their default browser language, Optimize provides the possibility to configure localizations. ## Default locale configuration The distributions of Optimize contain the default localization files under `./config/localization/`. The default localizations available are `en` for English and `de` for German. You can also find community maintained localizations in [this repository](https://github.com/camunda/camunda-optimize-translations). Additionally, English is configured as the default `fallbackLocale`. Fallback in this case means whenever a user has a browser configured with a language that is not present in the `availableLocales` list, Optimize will use the `fallbackLocale`. The default locale configuration in `./config/environment-config.yaml` looks like the following: ``` locales: availableLocales: ['en', 'de'] fallbackLocale: 'en' ``` For more details on the configuration keys, refer to the [localization configuration section](./system-configuration.md#localization). ## Custom locale configuration Custom locales can be added by creating a locale file under `./config/localization/` and adding it to the `availableLocales` configuration. :::note Configuring a custom locale means you have to maintain it yourself and update it in the context of an Optimize upgrade. There is currently no changelog of new localization entries available, and it is required that each localization file contains an entry for each key used by Optimize. ::: As an example, a custom localization can be created by making a copy of the `./config/localization/en.json` named `/config/localization/es.json` and adding it to the available locales in `./config/environment-config.yaml` ``` locales: availableLocales: ['en', 'de', 'es'] fallbackLocale: 'en' ``` --- ## Logging(3) Camunda Optimize provides logging facilities that are preconfigured to use _INFO_ logging level which provides minimal output of information in log files. This level can be adjusted using the `environment-log4j2.xml` configuration file. ## Google Stackdriver (JSON) logging To enable Google Stackdriver compatible JSON logging, set the environment variable `OPTIMIZE_LOG_APPENDER=Stackdriver` before starting Optimize. ## Default logging configuration Although one could potentially configure logging levels for all packages, it is recommended to set logging levels for the following three Optimize parts using only exact package reference as follows: - Optimize runtime environment: ```xml ``` - Optimize upgrade: ```xml ``` - Communication to Elasticsearch: ```xml ``` If you are running Optimize with Docker, use the following environment variables to configure its logging levels: - `OPTIMIZE_LOG_LEVEL`: Sets the logging level for the Optimize log. - `UPGRADE_LOG_LEVEL`: Sets the logging level for the Optimize upgrade log. - `ES_LOG_LEVEL`: Sets the logging level for Elasticsearch. Whether using the configuration file or Docker environment variables, to define the granularity of the information shown in the log you can set one of the following log levels: - **error**: Shows errors only. - **warn**: Like **error**, but displays warnings as well. - **info**: Logs everything from **warn** and the most important information about state changes or actions in Optimize. - **debug**: In addition to **info**, writes information about the scheduling process, alerting as well as the import of the engine data. - **trace**: Like **debug**, but in addition, writes all requests sent to the Camunda engine as well as all queries towards Elasticsearch to the log output. --- ## Multi-tenancy(Configuration) Camunda 8 Self-Managed only Multi-tenancy is the ability of Camunda 8 to serve multiple distinct [tenants](/self-managed/components/management-identity/manage-tenants.md) or clients within a single installation. From version 8.3 onwards, Optimize has been enhanced to support multi-tenancy for Self-Managed setups. More information about the feature can be found in the [multi-tenancy concepts](/components/concepts/multi-tenancy.md). Optimize imports the relevant tenant information from Zeebe records and retrieves each user's tenant authorizations from Identity, so that the logged-in users only have access to the data on tenants that they are authorized to see in Identity. Because tenant authorizations are cached in Optimize to improve performance, there could be a delay until any changes made to tenant authorizations in Identity are visible in Optimize. ## Default tenant authorizations in Optimize If multi-tenancy is enabled across components, users will be allowed to view any data from tenants for which they have authorizations configured in Identity. If multi-tenancy is disabled in Optimize, all users will be allowed to view data from the `` tenant only and no data from other tenants. If multi-tenancy is enabled in Optimize, but disabled in Identity or Identity is not reachable for other reasons, users will not have any tenant authorizations in Optimize and will not be able to access the data of any tenants in Optimize. ## Configuration In a Self-Managed Camunda 8 environment, the following two configurations settings are required for multi-tenancy: | YAML path | Environment variable | Default value | Description | | -------------------------- | ------------------------------------- | ------------- | -------------------------------------------------------- | | multitenancy.enabled | CAMUNDA_OPTIMIZE_MULTITENANCY_ENABLED | false | Enables the Camunda 8 multi-tenancy feature in Optimize. | | security.auth.ccsm.baseUrl | CAMUNDA_OPTIMIZE_IDENTITY_BASE_URL | null | The base URL of Identity. | The `CAMUNDA_OPTIMIZE_MULTITENANCY_ENABLED` environment variable enables the feature in Optimize. The multi-tenancy feature must be enabled in all other components as well using their respective multi-tenancy feature flags. The `CAMUNDA_OPTIMIZE_IDENTITY_BASE_URL` environment variable has to be set to enable Optimize to retrieve tenant authorizations from Identity. If this base URL is not configured, Optimize will not be able to retrieve tenant authorizations and users will not be able to access any tenant's data in Optimize. If required, the tenant authorization cache in Optimize can also be configured via these optional settings: | YAML path | Environment variable | Default value | Description | | ------------------------------------------------- | ------------------------------------------------------------------------------ | ------------- | ------------------------------------------------------------------ | | caches.cloudTenantAuthorizations.maxSize | CAMUNDA_OPTIMIZE_CACHES_CLOUD_TENANT_AUTHORIZATIONS_MAX_SIZE | 10000 | The maximum size of the Camunda 8 tenant authorizations cache. | | caches.cloudTenantAuthorizations.defaultTtlMillis | CAMUNDA_OPTIMIZE_CACHES_CLOUD_TENANT_AUTHORIZATIONS_MIN_FETCH_INTERVAL_SECONDS | 300000 | The time in milliseconds the tenant authorizations will be cached. | ### Troubleshooting To ensure seamless integration and functionality, the multi-tenancy feature must also be enabled across **all** associated components [if not configured in Helm](/self-managed/deployment/helm/configure/configure-multi-tenancy.md) so users can view any data from tenants for which they have authorizations configured in Identity. --- ## Object and list variable support ## Object variables Complex object variables can be imported into Optimize and thereafter be used in reports and filters. During import, Optimize flattens the given object variable to create individual variables for each property of the object, resulting in multiple "sub variables" for each imported object variable. For example, an object variable called `user` with the properties `firstName` and `lastName` will result in two flattened variables: `user.firstName` and `user.lastName`. These variables can be used within reports and filters. Additionally, to the flattened properties, Optimize also imports the entire raw value of the object variable. In the above example, this would result in a variable called `user` with value `{"firstName": "John", "lastName": "Smith"}`. This raw object variable can be inspected in Raw Data Reports but is not supported in other report types or filters. ## List variables Optimize also supports object variables which are JSON serialized lists of primitive types, for example a list of strings or numbers. Note that for Camunda 7 and external variables, the `type` of list variables must still be set to `Object`. During import, Optimize also evaluate how many entries are in a given list and persists this in an additional `_listSize` variable. For example, a list variable with the name `users` and the values `["John Smith", "Jane Smith"]` will result in two imported variables: one `users` variable with the two given values, and one variable called `users._listSize` with value `2`. Both can be used in reports and filters. However, filters are not yet fully optimized for list support, and some filter terms may be initially misleading. This is because filters currently apply to each list item individually rather than the entire list. For example, an "is" filter on a list of string values filters for those instances where any individual list item is equal to the given term, for example, instances whose list variable "contains" the selected value. Similarly, the "contains" filter matches process instances whose list variable contains at least one value which in turn contains the given substring. The value of list properties within objects as well as variables which are lists of objects rather than primitives can be inspected in the raw object variable value column accessible in raw data reports. ## Optimize configuration The import of object variable values is enabled by default and can be disabled using the `zeebe.includeObjectVariableValue` configuration. Alternatively, this can be set using the `CAMUNDA_OPTIMIZE_ZEEBE_INCLUDE_OBJECT_VARIABLE` Environment Variable. Depending on where the imported object variables originate, the following configuration is required to ensure that your system produces object variable data that Optimize can import correctly: If you are creating object variables using a Zeebe process, ensure date properties within the JSON object are stored using a common **date format** (for example `yyyy-MM-dd'T'HH:mm:ss.SSSZ`) other than unix timestamps. If Optimize imports unix timestamp date properties, these properties cannot be identified and parsed as dates and will instead be persisted as number variables. External variables of type object require an additional field called `serializationDataFormat` which specifies which data format was used to serialize the given object. Refer to the [external object variable API section](../../../../apis-tools/optimize-api/external-variable-ingestion.md) for further details on how to ingest external variables. --- ## Security instructions This page provides an overview of how to secure a Camunda Optimize installation. For Camunda's security policy, a list of security notices, and a guide on how to report vulnerabilities, visit the [general security documentation](https://docs.camunda.org/security/). This guide also identifies areas where we consider security issues to be relevant for the Camunda Optimize product and list those in the subsequent sections. Compliance for those areas is ensured based on common industry best practices and influenced by security requirements of standards like OWASP Top 10 and others. Optimize already comes with a myriad of settings and security mechanism by default. In the following you will find the parts that still need manual adjustments. ## Disable HTTP For security reasons, we recommend using Optimize over HTTPS and disabling HTTP. You can disable HTTP by setting the HTTP property in the container settings to an empty/null value. Consult the respective section in the [configuration guide](./system-configuration.md#container) for the more details. ## Fine tune Optimize security headers Over time, various client-side security mechanisms have been developed to protect web applications from various attacks. Some of these security mechanisms are only activated if the web application sends the corresponding HTTP headers in its server responses. Optimize adds several of these headers which can be fine-tuned in the [configuration](./system-configuration.md#security) to ensure appropriate security. Optimize stores its data in Elasticsearch or OpenSearch, which are search engines that act as document-store backends. To protect access to this data, the database should be configured carefully as well. Refer to the official security guidelines for [ElasticSearch](https://www.elastic.co/guide/en/elasticsearch/reference/master/secure-cluster.html#secure-cluster) or [OpenSearch](https://opensearch.org/docs/latest/getting-started/security). Within the Optimize configuration, you can then enable SSL and/or the credentials to be used when Camunda Optimize connects to the database. See [Elasticsearch Security](./system-configuration.md#elasticsearch-security) or [OpenSearch Security](./system-configuration.md#opensearch-security) for details. --- ## Shared Elasticsearch/OpenSearch cluster In case you have a large shared Elasticsearch/OpenSearch cluster that you want to operate multiple Optimize instances on that are intended to run in complete isolation from each other, it is required to change the [`*.settings.index.prefix`](./system-configuration.md#index-settings) setting for each Optimize instance. :::note Heads Up! Although a shared Elasticsearch/OpenSearch cluster setup is possible, it's recommended to operate a dedicated Elasticsearch/OpenSearch cluster per Optimize instance. This is due to the fact that a dedicated cluster provides the highest reliability (no resource sharing and no breaking side effects due to misconfiguration) and flexibility (e.g. Elasticsearch/OpenSearch and/or Optimize upgrades can be performed independently between different Optimize setups). ::: The following illustration demonstrates this use case with two Optimize instances that connect to the same Elasticsearch/OpenSearch cluster but are configured with different `*.settings.index.prefix` values. This results in different indexes and aliases created on the cluster, strictly isolating the data of both Optimize instances, so no instance accesses the data of the other instance. :::note Warning Changing the value of `*.settings.index.prefix` after an instance was already running results in new indexes being created with the new prefix value. There is no support in migrating data between indexes based on different prefixes. ::: \* Elasticsearch index prefix settings path: `es.settings.index.prefix` \* OpenSearch index prefix settings path: `opensearch.settings.index.prefix` ![Shared Elasticsearch Cluster Setup](img/shared-elasticsearch-cluster.png) --- ## Camunda 8 system configuration ### General settings Optimize imports process, variable, incident, and user task data from the Zeebe Elasticsearch or OpenSearch exporters in the Orchestration Cluster. For exporter configuration options (including filters), see: - [Elasticsearch exporter](/self-managed/components/orchestration-cluster/zeebe/exporters/elasticsearch-exporter.md). - [OpenSearch exporter](/self-managed/components/orchestration-cluster/zeebe/exporters/opensearch-exporter.md). The settings below control how Optimize connects to and paginates this exporter data. | YAML path | Default value | Description | | ----------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | zeebe.enabled | false | Toggles whether Optimize should attempt to import data from the connected Zeebe instance. | | zeebe.name | zeebe-record | The index prefix used for exported Zeebe records. This must match the `index.prefix` configured in the Elasticsearch or OpenSearch exporter that Optimize reads from. | | zeebe.partitionCount | 1 | The number of partitions configured for the Zeebe record source. | | zeebe.maxImportPageSize | 200 | The max page size for importing Zeebe data. | ### Exporter-side filters and Optimize data completeness This section describes how exporter-side filters affect Optimize data imports and data completeness. For YAML configuration details and property syntax, refer to the Elasticsearch and OpenSearch exporter documentation. Starting from Camunda 8.9, the Elasticsearch and OpenSearch exporters provide optional filters that can reduce the amount of data written for Optimize: - Variable names: Inclusion and exclusion lists with match modes such as exact, starts with, and ends with. - Variable value types: Inclusion and exclusion lists for inferred types such as `String`, `Number`, `Boolean`, `Object` and `Null`. - Variable scope: Disable export of all local-scope variables (`export-local-variables-enabled: false`) and apply separate name/type inclusion and exclusion lists for root vs. local variables. - BPMN process IDs: Inclusion and exclusion lists by `bpmnProcessId` that drop all records tied to selected processes. - Optimize mode: Keeps only the record value types and intents required by Optimize and drops other record types not used by Optimize. These filters run inside the exporter and permanently drop matching records from the exported stream. Optimize cannot import data that was never exported. #### Non‑retroactive filters and permanent gaps Exporter-side filters are not retroactive: - Filters only affect records produced after the configuration change. - If a process is excluded (or not included) for some period and later re‑enabled, records from the excluded period were never exported. - As a result, Optimize will always show a permanent gap in that time window for the affected process, even if you later remove the filter or inclusion list. The same principle applies to variable‑name and variable‑type filters and other exporter-side filters: records that were dropped during a given time window cannot be recovered in Optimize, even if you subsequently relax the filters. #### Changing filters on existing clusters (sequence vs position) On clusters that have already exported data to Optimize, enabling or changing exporter-side filters mid‑stream can cause Optimize to miss some re‑exported events. As long as the exporter configuration (including any existing filters) remains unchanged, the exporter assigns a monotonically increasing `sequence` to each exported record, and Optimize can reliably resume imports from the “last seen” sequence. The Elasticsearch/OpenSearch exporter uses at-least-once delivery: after a failover, restart, or snapshot replay, it may export the same event again. If you change exporter-side filters between the original export and the re‑export, some records that were previously exported may now be filtered out. The remaining records are renumbered in this shorter stream. As a result, an event that Optimize already imported can reappear with a different sequence and be skipped when Optimize resumes from the previous “last seen” sequence. To keep Optimize imports consistent and avoid gaps, we recommend changing exporter-side filters only when the exporter has no pending records and Optimize has already imported all available data from that exporter. #### Supported versions - Exporter-side filters and Optimize mode are introduced in Camunda 8.9 and are not backported to earlier 8.x versions. - On clusters running earlier versions, the exporters always write an unfiltered event stream and this section does not apply. #### Required record types for Optimize Optimize requires at least the following record value types to populate standard reports: - `PROCESS` - `PROCESS_INSTANCE` - `INCIDENT` - `USER_TASK` - `VARIABLE` If exporter settings or Optimize mode disable these value types, Optimize data and reports will become incomplete or fail to load. ### Licensing Camunda 8 Self-Managed only Installations of Camunda 8 Self-Managed which require a license can provide their license key to the components as an environment variable: | Environment variable | Description | Default value | | --------------------- | -------------------------------------------------------------------- | ------------- | | `CAMUNDA_LICENSE_KEY` | Your Camunda 8 license key, if your installation requires a license. | None | For Helm installations, license keys can be configured globally in your `values.yaml` file. See [License key](/self-managed/deployment/helm/configure/license-key.md) for more details. :::note Camunda 8 components without a valid license may display **Non-Production License** in the navigation bar and issue warnings in the logs. These warnings have no impact on Optimize startup or functionality. To obtain a license, visit the [Camunda Enterprise page](https://camunda.com/platform/camunda-platform-enterprise-contact/). ::: ### Settings required for multi-tenancy Camunda 8 Self-Managed only For more information on multi-tenancy in Camunda 8 Self-Managed environments, refer to [this page](./multi-tenancy.md). To use multi-tenancy, the feature must be enabled across all components. | YAML path | Default value | Description | | -------------------------- | ------------- | -------------------------------------------------------- | | multitenancy.enabled | false | Enables the Camunda 8 multi-tenancy feature in Optimize. | | security.auth.ccsm.baseUrl | null | The base URL of Identity. | ### Settings related to Camunda user tasks (formerly Zeebe user tasks) Camunda 8 Self-Managed only For more information on user task reporting in Camunda 8 Self-Managed, refer to our [user task analytics documentation](/components/optimize/userguide/process-analysis/user-task-analytics.md). | YAML path | Default value | Description | | ----------------------------------- | ------------- | -------------------------------------------------------------------- | | ui.userTaskAssigneeAnalyticsEnabled | true | Enables assignee based analytics in Camunda 8 Self-Managed Optimize. | --- ## Overview(Configuration) All distributions of Camunda Optimize come with a predefined set of configuration options that can be overwritten by the user, based on current environment requirements. To do that, have a look into the folder named `config` which contains a file called `environment-config.yaml` with values that override the default Optimize properties. You can see a sample configuration file with all possible configuration fields and their default values [here](service-config.yaml). In the following section, you will find descriptions and default values of the configuration fields with their respective YAML path and environment variable. :::note Heads Up For changes in the configuration to take effect, you need to restart Optimize! ::: ### Java system properties & OS environment variable placeholders To externalize configuration properties from the `environment-config.yaml`, Optimize provides variable placeholder support. The order in which placeholders are resolved is the following: 1. Java system properties 2. OS environment variables The placeholder format is `${VARIABLE_NAME}` and allows you to refer to a value of a Java system property or OS environment variable of your choice. The `VARIABLE_NAME` is required to contain only lowercase or uppercase letters, digits and underscore `_` characters and shall not begin with a digit. The corresponding regular expression is `([a-zA-Z_]+[a-zA-Z0-9_]*)`. The following example illustrates the usage: ``` security: auth: token: secret: ${AUTH_TOKEN_SECRET} ``` Given this variable is set before Optimize is started, for example on Unix systems with: ``` export AUTH_TOKEN_SECRET=sampleTokenValue ``` The value will be resolved at startup to `sampleTokenValue`. However, if the same variable is provided at the same time as a Java system property, for example via passing `-DAUTH_TOKEN_SECRET=othertokenValue` to the Optimize startup script: ``` ./optimize-startup.sh -DAUTH_TOKEN_SECRET=othertokenValue ``` The value would be resolved to `othertokenValue` as Java system properties have precedence over OS environment variables. :::note For Windows users, to pass Java system properties to the provided Windows Batch script `optimize-startup.bat`, you have to put them into double quotes when using the `cmd.exe` shell, as shown below. ::: ``` optimize-startup.bat "-DAUTH_TOKEN_SECRET=othertokenValue" ``` For the Windows Powershell in three double quotes: ``` ./optimize-startup.bat """-DAUTH_TOKEN_SECRET=othertokenValue""" ``` #### Default values For variable placeholders it's also possible to provide default values using the following format: `${VARIABLE_NAME:DEFAULT_VALUE}`. The `DEFAULT_VALUE` can contain any character except `}`. The following example illustrates the usage: ``` security: auth: token: secret: ${AUTH_TOKEN_SECRET:defaultSecret} ``` ### Security These values control mechanisms of Optimize related security, e.g. security headers and authentication. | YAML path | Environment variable | Default value | Description | | ------------------------------------------------ | ------------------------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | | | security.auth.token.lifeMin | | 60 | Optimize uses token-based authentication to keep track of which users are logged in. Define the lifetime of the token in minutes. | | security.auth.token.secret | CAMUNDA_OPTIMIZE_SECURITY_AUTH_TOKEN_SECRET | null | Optional secret used to sign authentication tokens, it's recommended to use at least a 64-character secret. If set to `null` a random secret will be generated with each startup of Optimize. | | security.responseHeaders.HSTS.max-age | CAMUNDA_OPTIMIZE_SECURITY_RESPONSE_HEADERS_HSTS_MAX_AGE | 63072000 | HTTP Strict Transport Security (HSTS) is a web security policy mechanism which helps to protect websites against protocol downgrade attacks and cookie hijacking. This field defines the time, in seconds, that the browser should remember that this site is only to be accessed using HTTPS. If you set the number to a negative value no HSTS header is sent. | | security.responseHeaders.HSTS.includeSubDomains | | true | HTTP Strict Transport Security (HSTS) is a web security policy mechanism which helps to protect websites against protocol downgrade attacks and cookie hijacking. If this optional parameter is specified, this rule applies to all the site’s subdomains as well. | | security.responseHeaders.X-XSS-Protection | | 1; mode=block | This header enables the cross-site scripting (XSS) filter in your browser. Can have one of the following options: `0`: Filter disabled. `1`: Filter enabled. If a cross-site scripting attack is detected, in order to stop the attack, the browser will sanitize the page. `1; mode=block`: Filter enabled. Rather than sanitize the page, when a XSS attack is detected, the browser will prevent rendering of the page. `1; report=http://[YOURDOMAIN]/your_report_URI`: Filter enabled. The browser will sanitize the page and report the violation. This is a Chromium function utilizing CSP violation reports to send details to a URI of your choice. | | security.responseHeaders.X-Content-Type-Options | | true | Setting this header will prevent the browser from interpreting files as a different MIME type to what is specified in the Content-Type HTTP header (e.g. treating text/plain as text/css). | | security.responseHeaders.Content-Security-Policy | | base-uri 'self' | A Content Security Policy (CSP) has significant impact on the way browsers render pages. By default Optimize uses the base-uri directive which restricts the URLs that can be used to the Optimize pages. Find more details in [Mozilla's Content Security Policy Guide](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy). | ### Import These values relate to Optimize data import. | YAML path | Environment variable | Default value | Description | | ------------------------------------------ | --------------------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | | | import.skipDataAfterNestedDocLimitReached | CAMUNDA_OPTIMIZE_IMPORT_DATA_SKIP_DATA_AFTER_NESTED_DOC_LIMIT_REACHED | false | Some data can no longer be imported to a given document if its number of nested documents has reached the configured limit. Enable this setting to skip this data during import if the nested document limit has been reached. | | import.currentTimeBackoffMilliseconds | | 300000 | The time interval the import backs off from the current tip of the time, to reread potentially missed concurrent writes. | | import.elasticsearchJobExecutorThreadCount | | 1 | Number of threads being used to process the import jobs per data type that are writing data to Elasticsearch. | | import.elasticsearchJobExecutorQueueSize | | 5 | Adjust the queue size of the import jobs per data type that store data to Elasticsearch. A too large value might cause memory problems. | ### External API This section focuses on common properties related to the External REST API of Optimize. It is mandatory to configure one of the values below if the External REST API is to be used. If neither is configured an error will be thrown and all requests to the External API will get rejected. If both are configured then the `jwtSetUri` will take precedence and the `accessToken` will be ignored. | YAML path | Environment variable | Default value | Description | | --------------- | ----------------------------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | api.accessToken | OPTIMIZE_API_ACCESS_TOKEN | null | Secret static shared token to be provided to the secured REST API in the authorization header. Will be ignored if `api.jwtSetUri` is also set. | | api.jwtSetUri | SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI | null | Complete URI to get public keys for JWT validation, e.g. `https://weblogin.cloud.company.com/.well-known/jwks.json` | | api.audience | CAMUNDA_OPTIMIZE_API_AUDIENCE | optimize | Optimize tries to match this with the `aud` field contained in the JWT token. Only used when `jwtSetUri` is set. | ### Container Settings related to embedded Jetty container, which serves the Optimize application. | YAML path | Environment variable | Default value | Description | | -------------------------------- | ------------------------------------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | container.host | CONTAINER_HOST | localhost | A host name or IP address to identify a specific network interface on which to listen. | | container.contextPath | CAMUNDA_OPTIMIZE_CONTEXT_PATH | null | Allows you to specify a custom context path. If set, must start with a leading '/' | | container.ports.http | CAMUNDA_OPTIMIZE_CONTAINER_PORTS_HTTP | 8090 | A port number that will be used by Optimize to process HTTP connections. If set to null, or left empty, HTTP connections won't be accepted. | | container.ports.https | CAMUNDA_OPTIMIZE_CONTAINER_PORTS_HTTPS | 8091 | A port number that will be used by Optimize to process secure HTTPS connections. | | container.ports.actuator | MANAGEMENT_SERVER_PORT | 8092 | A port number that will be used by Optimize's Actuator management server, defaults to 8092 | | container.keystore.location | CAMUNDA_OPTIMIZE_CONTAINER_KEYSTORE_LOCATION | keystore.jks | HTTPS requires an SSL Certificate. When you generate an SSL Certificate, you are creating a keystore file and a keystore password for use when the browser interface connects. This field specifies the location of this keystore file. | | container.keystore.password | CAMUNDA_OPTIMIZE_CONTAINER_KEYSTORE_PASSWORD | optimize | Password of keystore file. | | container.status.connections.max | CAMUNDA_OPTIMIZE_CONTAINER_STATUS_CONNECTIONS_MAX | 10 | Maximum number of web socket connections accepted for status report. | | container.accessUrl | CONTAINER_ACCESS_URL | null | Optional URL to access Optimize (used for links to Optimize in e.g. alert emails). If no value specified the container host and port are used instead. | | container.http2Enabled | CAMUNDA_OPTIMIZE_CONTAINER_HTTP2_ENABLED | false | Enable use of HTTP/2 for Optimize | | container.enableSniCheck | CAMUNDA_OPTIMIZE_CONTAINER_ENABLE_SNI_CHECK | true | Determines whether SNI checking should be enabled. | ### Elasticsearch These settings are only relevant when operating Optimize with Elasticsearch. #### Elasticsearch connection settings Everything that is related to building the connection to Elasticsearch. Please note that you can define a number of connection points in a cluster. Therefore, everything that is under `es.connection.nodes` is a list of nodes Optimize can connect to. If you have built an Elasticsearch cluster with several nodes it is recommended to define several connection points so that if one node fails, Optimize is still able to talk to the cluster. | YAML path | Environment variable | Default value | Description | | --------------------------------------------- | -------------------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | es.connection.timeout | | 10000 | Maximum time without connection to Elasticsearch that Optimize should wait until a timeout triggers. | | es.connection.responseConsumerBufferLimitInMb | | 100 | Maximum size of the Elasticsearch response consumer heap buffer. This can be increased to resolve errors from Elasticsearch relating to the entity content being too long | | es.connection.pathPrefix | | | The path prefix under which Elasticsearch is available. | | es.connection.nodes[*].host | OPTIMIZE_ELASTICSEARCH_HOST | localhost | The address/hostname under which the Elasticsearch node is available. | | es.connection.nodes[*].httpPort | OPTIMIZE_ELASTICSEARCH_HTTP_PORT | 9200 | A port number used by Elasticsearch to accept HTTP connections. | | es.connection.proxy.enabled | | false | Whether an HTTP proxy should be used for requests to Elasticsearch. | | es.connection.proxy.host | | null | The proxy host to use, must be set if es.connection.proxy.enabled = true. | | es.connection.proxy.port | | null | The proxy port to use, must be set if es.connection.proxy.enabled = true. | | es.connection.proxy.sslEnabled | | false | Whether this proxy is using a secured connection (HTTPS). | | es.connection.proxy.username | | null | Optional username for HTTP Basic proxy authentication. If both username and password are set, a preemptive `Proxy-Authorization` header is sent with each request. | | es.connection.proxy.password | | null | Optional password for HTTP Basic proxy authentication. | | es.connection.skipHostnameVerification | CAMUNDA_OPTIMIZE_ELASTICSEARCH_CONNECTION_SKIP_HOSTNAME_VERIFICATION | false | Determines whether the hostname verification should be skipped. | #### Elasticsearch index settings | YAML path | Environment variable | Default value | Description | | ---------------------------------------- | -------------------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | es.settings.index.prefix | CAMUNDA_OPTIMIZE_ELASTICSEARCH_SETTINGS_INDEX_PREFIX | optimize | The prefix prepended to all Optimize index and alias names. Custom values allow to operate multiple isolated Optimize instances on one Elasticsearch cluster. NOTE: Changing this after Optimize was already run before will create new empty indexes. | | es.settings.index.number_of_replicas | CAMUNDA_OPTIMIZE_ELASTICSEARCH_SETTINGS_INDEX_NUMBER_OF_REPLICAS | 1 | How often data should be replicated to handle node failures. | | es.settings.index.number_of_shards | CAMUNDA_OPTIMIZE_ELASTICSEARCH_SETTINGS_INDEX_NUMBER_OF_SHARDS | 1 | How many shards should be used in the cluster for process instance and decision instance indices. All other indices will be made up of a single shard. Note: this property only applies the first time Optimize is started and the schema/mapping is deployed on Elasticsearch. If you want this property to take effect again, you need to delete all indices (and with that all data) and restart Optimize. | | es.settings.index.refresh_interval | | 2s | How long Elasticsearch waits until the documents are available for search. A positive value defines the duration in seconds. A value of -1 means that a refresh needs to be done manually. | | es.settings.index.nested_documents_limit | CAMUNDA_OPTIMIZE_ELASTICSEARCH_SETTINGS_INDEX_NESTED_DOCUMENTS_LIMIT | 10000 | Optimize uses nested documents to store list information such as activities or variables belonging to a process instance. This setting defines the maximum number of activities/variables/incidents that a single process instance can contain. This limit helps to prevent out of memory errors and should be used with care. For more information, please refer to the Elasticsearch documentation on this topic. | #### Elasticsearch Security Define a secured connection to be able to communicate with a secured Elasticsearch instance. | YAML path | Environment variable | Default value | Description | | --------------------------------------- | ------------------------------------------------------------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | es.security.username | CAMUNDA_OPTIMIZE_ELASTICSEARCH_SECURITY_USERNAME | | The Basic authentication (x-pack) username. | | es.security.password | CAMUNDA_OPTIMIZE_ELASTICSEARCH_SECURITY_PASSWORD | | The Basic authentication (x-pack) password. | | es.security.ssl.enabled | CAMUNDA_OPTIMIZE_ELASTICSEARCH_SSL_ENABLED | false | Used to enable or disable TLS/SSL for the HTTP connection. | | es.security.ssl.certificate | CAMUNDA_OPTIMIZE_ELASTICSEARCH_SECURITY_SSL_CERTIFICATE | | The path to a PEM encoded file containing the certificate (or certificate chain) that will be presented to clients when they connect. | | es.security.ssl.certificate_authorities | CAMUNDA_OPTIMIZE_ELASTICSEARCH_SECURITY_SSL_CERTIFICATE_AUTHORITIES | [ ] | A list of paths to PEM encoded CA certificate files that should be trusted, e.g. ['/path/to/ca.crt']. Note: if you are using a public CA that is already trusted by the Java runtime, you do not need to set the certificate_authorities. | | es.security.ssl.selfSigned | CAMUNDA_OPTIMIZE_ELASTICSEARCH_SECURITY_SSL_SELF_SIGNED | false | Used to specify that the certificate was self-signed. | #### Elasticsearch backup settings | YAML path | Environment variable | Default value | Description | | ------------------------ | --------------------------------------- | ------------- | ------------------------------------------------------------------------ | | es.backup.repositoryName | CAMUNDA_OPTIMIZE_BACKUP_REPOSITORY_NAME | "" | The name of the snapshot repository to be used to back up Optimize data. | ### OpenSearch These settings are only relevant when operating Optimize with OpenSearch. #### OpenSearch connection settings This section details everything related to building the connection to OpenSearch. :::note You can define a number of connection points in a cluster. Therefore, everything under `opensearch.connection.nodes` is a list of nodes Optimize can connect to. If you have built an OpenSearch cluster with several nodes, it is recommended to define several connection points so if one node fails, Optimize is still able to talk to the cluster. ::: | YAML path | Environment variable | Default value | Description | | ---------------------------------------------- | ----------------------------------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | | CAMUNDA_OPTIMIZE_DATABASE | `elasticsearch` | Type of database being used. Either `elasticsearch` or `opensearch` | | opensearch.connection.timeout | | 10000 | Maximum time without connection to OpenSearch that Optimize should wait until a timeout triggers. | | opensearch.connection.pathPrefix | | | The path prefix under which OpenSearch is available. | | opensearch.connection.nodes[*].host | CAMUNDA_OPTIMIZE_OPENSEARCH_HOST | localhost | The address/hostname under which the OpenSearch node is available. | | opensearch.connection.nodes[*].httpPort | CAMUNDA_OPTIMIZE_OPENSEARCH_HTTP_PORT | 9200 | A port number used by OpenSearch to accept HTTP connections. | | opensearch.connection.skipHostnameVerification | CAMUNDA_OPTIMIZE_OPENSEARCH_CONNECTION_SKIP_HOSTNAME_VERIFICATION | false | Determines whether the hostname verification should be skipped. | | opensearch.connection.awsEnabled | CAMUNDA_OPTIMIZE_OPENSEARCH_AWS_ENABLED | false | Determines if AWS credentials shall be used for authentication | | opensearch.connection.proxy.enabled | | false | Whether an HTTP proxy should be used for requests to OpenSearch. | | opensearch.connection.proxy.host | | null | The proxy host to use, must be set if opensearch.connection.proxy.enabled = true. | | opensearch.connection.proxy.port | | null | The proxy port to use, must be set if opensearch.connection.proxy.enabled = true. | | opensearch.connection.proxy.sslEnabled | | false | Whether this proxy is using a secured connection (HTTPS). | | opensearch.connection.proxy.username | | null | Optional username for HTTP Basic proxy authentication. If both username and password are set, a preemptive `Proxy-Authorization` header is sent with each request. | | opensearch.connection.proxy.password | | null | Optional password for HTTP Basic proxy authentication. | #### OpenSearch index settings | YAML path | Environment variable | Default value | Description | | ------------------------------------------------ | ----------------------------------------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | opensearch.settings.index.prefix | CAMUNDA_OPTIMIZE_OPENSEARCH_SETTINGS_INDEX_PREFIX | optimize | The prefix prepended to all Optimize index and alias names. Custom values allow you to operate multiple isolated Optimize instances on one OpenSearch cluster. NOTE: Changing this after Optimize has already run will create new empty indexes. | | opensearch.settings.index.number_of_replicas | CAMUNDA_OPTIMIZE_OPENSEARCH_SETTINGS_INDEX_NUMBER_OF_REPLICAS | 1 | How often data should be replicated to handle node failures. | | opensearch.settings.index.number_of_shards | CAMUNDA_OPTIMIZE_OPENSEARCH_SETTINGS_INDEX_NUMBER_OF_SHARDS | 1 | How many shards should be used in the cluster for process instance and decision instance indices. All other indices will be made up of a single shard. NOTE: This property only applies the first time Optimize is started and the schema/mapping is deployed on OpenSearch. If you want this property to take effect again, you need to delete all indices (and with that all data) and restart Optimize. | | opensearch.settings.index.refresh_interval | | 2s | How long OpenSearch waits until the documents are available for search. A positive value defines the duration in seconds. A value of -1 means a refresh needs to be done manually. | | opensearch.settings.index.nested_documents_limit | CAMUNDA_OPTIMIZE_OPENSEARCH_SETTINGS_INDEX_NESTED_DOCUMENTS_LIMIT | 10000 | Optimize uses nested documents to store list information such as activities or variables belonging to a process instance. This setting defines the maximum number of activities, variables, or incidents that a single process instance can contain. This limit helps to prevent out of memory errors and should be used with care. For more information, refer to the OpenSearch documentation on this topic. | #### OpenSearch security Define a secured connection to be able to communicate with a secured OpenSearch instance. | YAML path | Environment variable | Default value | Description | | ----------------------------------------------- | ---------------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | opensearch.security.username | CAMUNDA_OPTIMIZE_OPENSEARCH_SECURITY_USERNAME | | The Basic authentication username. | | opensearch.security.password | CAMUNDA_OPTIMIZE_OPENSEARCH_SECURITY_PASSWORD | | The Basic authentication password. | | opensearch.security.ssl.enabled | CAMUNDA_OPTIMIZE_OPENSEARCH_SSL_ENABLED | false | Used to enable or disable TLS/SSL for the HTTP connection. | | opensearch.security.ssl.certificate | CAMUNDA_OPTIMIZE_OPENSEARCH_SECURITY_SSL_CERTIFICATE | | The path to a PEM encoded file containing the certificate (or certificate chain) that will be presented to clients when they connect. | | opensearch.security.ssl.certificate_authorities | CAMUNDA_OPTIMIZE_OPENSEARCH_SECURITY_SSL_CERTIFICATE_AUTHORITIES | [ ] | A list of paths to PEM encoded CA certificate files that should be trusted, for example ['/path/to/ca.crt']. NOTE: if you are using a public CA that is already trusted by the Java runtime, you do not need to set the certificate_authorities. | | opensearch.security.ssl.selfSigned | CAMUNDA_OPTIMIZE_OPENSEARCH_SECURITY_SSL_SELF_SIGNED | false | Used to specify that the certificate was self-signed. | #### OpenSearch backup settings | YAML path | Environment variable | Default value | Description | | -------------------------------- | --------------------------------------- | ------------- | ------------------------------------------------------------------------ | | opensearch.backup.repositoryName | CAMUNDA_OPTIMIZE_BACKUP_REPOSITORY_NAME | "" | The name of the snapshot repository to be used to back up Optimize data. | ### Email Settings for the email server to send email notifications, e.g. when an alert is triggered. | YAML path | Environment variable | Default value | Description | | ------------------------------------- | ------------------------------------------------------- | ------------- | --------------------------------------------------------------------------------------------------------------- | | email.enabled | CAMUNDA_OPTIMIZE_EMAIL_ENABLED | false | A switch to enable the email sending functionality. | | email.address | CAMUNDA_OPTIMIZE_EMAIL_ADDRESS | | Email address that can be used to send notifications. | | email.hostname | CAMUNDA_OPTIMIZE_EMAIL_HOSTNAME | | The smtp server name. | | email.port | CAMUNDA_OPTIMIZE_EMAIL_PORT | 587 | The smtp server port. This one is also used as SSL port for the security connection. | | email.checkServerIdentity | CAMUNDA_OPTIMIZE_EMAIL_CHECK_SERVER_IDENTITY | false | A switch to control checking the identity of the email server. | | email.authentication.enabled | CAMUNDA_OPTIMIZE_EMAIL_AUTHENTICATION_ENABLED | | A switch to enable email server authentication. | | email.authentication.username | CAMUNDA_OPTIMIZE_EMAIL_AUTHENTICATION_USERNAME | | Username of your smtp server. | | email.authentication.password | CAMUNDA_OPTIMIZE_EMAIL_AUTHENTICATION_PASSWORD | | Corresponding password to the given user of your smtp server. | | email.authentication.securityProtocol | CAMUNDA_OPTIMIZE_EMAIL_AUTHENTICATION_SECURITY_PROTOCOL | | States how the connection to the server should be secured. Possible values are 'NONE', 'STARTTLS' or 'SSL/TLS'. | ### Digest Settings influencing the process digest feature. | YAML path | Default value | Description | | ------------------ | --------------- | -------------------------------------------------------------------- | | digest.cronTrigger | 0 0 9 \* \* MON | Cron expression to define when enabled email digests are to be sent. | ### History cleanup settings Settings for automatic cleanup of historic process instances based on their end time. :::note Two types of history cleanup are available for Camunda 8 users at this time - process data cleanup and external variable cleanup. For more information, see [History cleanup](/self-managed/components/optimize/configuration/history-cleanup.md). ::: | YAML path | Environment variable | Default value | Description | | --------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | historyCleanup.cronTrigger | CAMUNDA_OPTIMIZE_HISTORY_CLEANUP_CRON_TRIGGER | `'0 1 * * *'` | Cron expression to schedule when the cleanup should be executed, defaults to 01:00 A.M. As the cleanup can cause considerable load on the underlying database, it is recommended to schedule it outside of office hours. You can either use the default Cron (5 fields) or the Spring Cron (6 fields) expression format here. | | historyCleanup.ttl | CAMUNDA_OPTIMIZE_HISTORY_CLEANUP_TTL | 'P2Y' | Global time-to-live (TTL) period for process/event data. The relevant property differs between entities. For process data, it's the `endTime` of the process instance. For ingested events, it's the `time` field. The format of the string is ISO_8601 duration. The default value is two years. For details on the notation, refer to [https://en.wikipedia.org/wiki/ISO_8601#Durations](https://en.wikipedia.org/wiki/ISO_8601#Durations) Note: The time component of the ISO_8601 duration is not supported. Only years (Y), months (M), and days (D) are supported. | | historyCleanup.processDataCleanup.enabled | CAMUNDA_OPTIMIZE_HISTORY_CLEANUP_PROCESS_DATA_CLEANUP_ENABLED | false | A switch to activate the history cleanup of process data. \[true/false\] | | historyCleanup.processDataCleanup.cleanupMode | CAMUNDA_OPTIMIZE_HISTORY_CLEANUP_PROCESS_DATA_CLEANUP_CLEANUP_MODE | 'all' | Global type of the cleanup to perform for process instances, possible values: 'all' - delete everything related and including the process instance that passed the defined TTL 'variables' - only delete variables of a process instance. | | historyCleanup.processDataCleanup.batchSize | CAMUNDA_OPTIMIZE_HISTORY_CLEANUP_PROCESS_DATA_CLEANUP_BATCH_SIZE | 10000 | Defines the batch size in which Camunda engine process instance data gets cleaned up. It may be reduced if requests fail due to request size constraints. In most cases, this should not be necessary and has only been experienced when connecting to an AWS Elasticsearch instance. | | historyCleanup.processDataCleanup.perProcessDefinitionConfig | | | A list of process definition specific configuration parameters that will overwrite the global cleanup settings for the specific process definition identified by its $\{key}. | | historyCleanup.processDataCleanup .perProcessDefinitionConfig.$\{key}.ttl | | | TTL to use for process instances of the process definition with the $\{key}. | | historyCleanup.processDataCleanup .perProcessDefinitionConfig.$\{key}.cleanupMode | | Cleanup mode to use for process instances of the process definition with the $\{key}. | ### Localization Define the languages that can be used by Optimize. | YAML path | Default value | Description | | ----------------------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | localization.availableLocales | ['en','de'] | All locales available in the Optimize Frontend. Note: for languages other than the default there must be a `.json` file available under ./config/localization. | | localization.fallbackLocale | 'en' | The fallback locale used if there is a locale requested that is not available in availableLocales. The fallbackLocale is required to be present in localization.availableLocales. | ### UI configuration Customize the Optimize UI e.g. by adjusting the logo, head background color etc. | YAML path | Environment variable | Default value | Description | | ----------------------------- | ----------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | ui.logoutHidden | CAMUNDA_OPTIMIZE_UI_LOGOUT_HIDDEN | false | Setting this property to true will hide the logout option from the user menu. This is useful if you are using single sign-on and it is not possible for users to logout. | | ui.maxNumDataSourcesForReport | CAMUNDA_OPTIMIZE_UI_MAX_NUM_REPORT_DATA_SOURCES | 100 | The maximum number of data sources available for a report. The minimum value is two, the maximum is 1024. | ### External variable ingestion REST API configuration | YAML path | Default value | Description | | ----------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------- | | externalVariable.import.enabled | false | Controls whether external ingested variable data is processed and imported to process instance data. | | externalVariable.import.maxPageSize | 10000 | Determines the page size for the import of ingested external variable data to process instance data. | ### Other Settings of Sharing, Optimize entities, and CSV Export. | YAML path | Environment variable | Default value | Description | | -------------------------- | -------------------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | entity.authorizedEditors | | 'all' | Define which users are authorized to Create, Edit, Copy and Delete Optimize entities outside of a collection. Available options: 'all', 'none'. | | entity.kpiRefreshInterval | | 600 | Define the interval in which the kpi import scheduler should run in seconds | | export.csv.authorizedUsers | | 'all' | Define which users are authorized to download CSVs. Available options: 'all', 'superuser', 'none'. | | export.csv.limit | | 1000 | Maximum number of records returned by CSV export. Note: Increasing this value comes at a memory cost for the Optimize application that varies based on the actual data. As a rough guideline, an export of a 50000 raw data report records containing 8 variables on each instance can cause temporary heap memory peaks of up to ~200MB with the actual CSV file having a size of ~20MB. Please adjust the heap memory accordingly, see [Adjust Optimize heap size](../overview.md#adjust-optimize-heap-size) on how to do that. | | export.csv.delimiter | | , | The delimiter used for the CSV export. The value defaults to a comma, however other common CSV delimiters such as semicolons (";") and tabs ("\\t") can also be used. | | sharing.enabled | CAMUNDA_OPTIMIZE_SHARING_ENABLED | true | Enable/disable the possibility to share reports and dashboards. | --- ## Enable or disable variable import Learn how to configure Camunda Optimize to control variable import and enhance performance. ## Overview By default, Camunda Optimize imports process variables to provide deep insights into both process performance and business context. However, in high-throughput environments or data-sensitive scenarios, importing variables may impact system performance and resource usage. Variable import affects several areas of Optimize’s operation: - **Import performance**: Importing large or numerous variables can slow down data import. - **Memory usage**: Variable data increases memory consumption during processing. - **Storage requirements**: Imported variables contribute to overall storage demands. - **Indexing duration**: Additional variable data extends indexing time. If your organization primarily focuses on process performance metrics rather than detailed business context, disabling variable import can help improve scalability and responsiveness. ## Optimize configuration Variable import is controlled using the `CAMUNDA_OPTIMIZE_ZEEBE_VARIABLE_IMPORT_ENABLED=true|false` environment variable. ### Configuration options | Value | Behavior | Recommended use case | | ------- | -------------------------------------------- | -------------------------------------------------------- | | `true` | Variables are imported and indexed (default) | Standard deployments requiring complete business context | | `false` | Variable import is disabled | High-throughput or performance-optimized environments | :::note Disabling variable import means variable-based reports and filters will no longer be available in the Optimize interface. Ensure this setting aligns with your reporting and monitoring needs before applying the configuration. ::: :::note When re-enabled, partial or full variable import may happen depending on your cluster [retention](/self-managed/components/orchestration-cluster/zeebe/exporters/elasticsearch-exporter.md#retention) configuration. ::: --- ## Optimize on Self-Managed All distributions of Optimize come with a predefined set of configuration options that can be overwritten by the user, based on current environment requirements. To do that, have a look into the folder named `config`. There are two files, one called `environment-config.yaml` with values that override the default Optimize properties and another called `environment-logback.xml`, which sets the logging configuration. You can see all supported values and read about logging configuration [here](./configuration/system-configuration.md). ## Optimize web container configuration Refer to the [configuration section on container settings](./configuration/system-configuration.md) for more information on how to adjust the Optimize web container configuration. ## Elasticsearch/OpenSearch configuration You can customize the [Elasticsearch/OpenSearch connection settings](./configuration/system-configuration.md#connection-settings) as well as the [index settings](./configuration/system-configuration.md#index-settings). ## Camunda 8 specific configuration For Camunda 8, Optimize imports process data from Zeebe records exported by the [Elasticsearch exporter](../orchestration-cluster/zeebe/exporters/elasticsearch-exporter.md) or [OpenSearch exporter](../orchestration-cluster/zeebe/exporters/opensearch-exporter.md) from the same cluster that Optimize uses to store its own data. For the relevant Optimize import options and version support, refer to the [Camunda 8 system configuration](./configuration/system-configuration-platform-8.md). Starting with Camunda 8.9, you can optionally reduce the amount of data exported for Optimize by configuring exporter-side filters (for example, by variable name, variable type, or BPMN process ID) in the Elasticsearch/OpenSearch exporters. Use the [Elasticsearch exporter](../orchestration-cluster/zeebe/exporters/elasticsearch-exporter.md) and [OpenSearch exporter](../orchestration-cluster/zeebe/exporters/opensearch-exporter.md) documentation for the exact configuration options, and see the [Camunda 8 system configuration](./configuration/system-configuration-platform-8.md) for their implications on Optimize imports and supported versions. ## Recommended additional configurations ### Adjust Optimize heap size By default, Optimize is configured with 1GB JVM heap memory. Depending on your setup and actual data, you might still encounter situations where you need more than this default for a seamless operation of Optimize. To increase the maximum heap size, you can set the environment variable `OPTIMIZE_JAVA_OPTS` and provide the desired JVM system properties; for example, for 2GB of Heap: ```bash OPTIMIZE_JAVA_OPTS=-Xmx2048m ``` --- ## Bring your own groups When Camunda 8 Self-Managed is configured with OpenID Connect (OIDC), the Orchestration Cluster can read a configurable claim from each token and treat its values as Camunda group IDs. This lets you use groups that already exist in your identity provider (IdP) as the basis for Camunda's authorization, role, and tenant assignment. :::note This feature is Self-Managed only. Bring your own groups (external IdP groups) is not available in Camunda 8 SaaS. ::: ## When to use external IdP groups Use external IdP groups if: - Your IdP is already the source of truth for user-to-group membership. - You want a single place to manage group membership for both sign-in and Camunda authorization. - You need group-based authorization in Camunda but do not want to duplicate group data between your IdP and Camunda. Do not use external IdP groups if: - You need to manage groups through the Orchestration Cluster REST API or Identity UI. - You need Camunda to list or browse IdP-managed groups. - You are running Camunda 8 SaaS. ## Prerequisites - A Self-Managed Camunda 8 deployment running 8.8 or later. - OIDC authentication configured for the Orchestration Cluster. See [connect to an external identity provider](./connect-external-identity-provider.md). - An IdP that can include a groups claim in the issued ID or access token. The claim value must be a JSON array of strings, where each string is a group ID. ## Configure the groups claim Set `camunda.security.authentication.oidc.groups-claim` to the name of the claim that contains the user's groups. The claim value must be a JSON array of strings, where each entry is a group ID. For nested claims, use a [JSONPath expression](https://www.rfc-editor.org/rfc/rfc9535.html) — the same mechanism used by `username-claim`. For example, `$['camundaorg']['groups']` resolves to the `groups` array inside a nested `camundaorg` object. ```yaml camunda.security.authentication.oidc.groups-claim: ``` ``` CAMUNDA_SECURITY_AUTHENTICATION_OIDC_GROUPSCLAIM= ``` See the [`groupsClaim` configuration reference](../core-settings/configuration/properties.md) for the full property definition. For multi-IdP setups where each provider may use a different claim name, see [connect multiple identity providers](./connect-multiple-identity-providers.md). ## Use IdP groups in Camunda Once `groups-claim` is set, the group IDs extracted from each token can be used anywhere a Camunda group ID can be used. The group ID in the token and the Camunda-side group ID must match **exactly**: comparisons are case-sensitive and treat group IDs as opaque strings. ### Role assignment Assign IdP groups to Camunda roles to grant every member of that group the role's permissions on sign-in. See [assign users, clients, groups, or mapping rules to roles via configuration](./overview.md#assign-users-clients-groups-or-mapping-rules-to-roles-via-configuration). ### Authorizations Grant authorizations directly to IdP groups to control access to Camunda resources such as process definitions, decisions, or tenants. See [authorizations](../../../../components/concepts/access-control/authorizations.md). ### Tenant assignment Add IdP groups to tenants so that every member of that group gains access to the tenant. See [tenants](../../../../components/admin/tenant.md). ## Limitations and behavior - Self-Managed only. The feature is not available in Camunda 8 SaaS. - No group sync or listing. Groups exist only at the moment a token is evaluated. They are not persisted in Camunda and are not browsable in the Identity UI or the REST API. - REST group management is disabled while the claim is set. When `groups-claim` is set, the `/v2/groups/**` endpoints return HTTP 404. This is a hard switch, not a merge — you cannot manage some groups via REST and others via the claim at the same time. - Per-token evaluation. Group membership is re-read from every token. Changes in your IdP take effect on the next sign-in or token refresh; they do not propagate to already-active sessions. - Array shape required. The claim value must always be a JSON array, even for users who belong to a single group. - Clients use the same claim. Machine-to-machine tokens resolve their groups through the same `groups-claim` configuration as user tokens. ## Troubleshooting If groups from your IdP are not being applied in Camunda, check the following: - The claim is present in the issued token. Decode an ID or access token for a user who should have the groups and verify the claim appears with the expected array value. Your IdP may require explicit scope or claim-mapping configuration to include it. - JSONPath matches the actual structure. If your groups claim is nested, make sure the JSONPath expression in `groups-claim` resolves to the array itself, not its parent object. - Group IDs match exactly. Camunda matches group IDs as literal strings. A trailing space, different case, or stray prefix will cause Camunda-side assignments to silently miss. For deeper investigation, see [debugging authentication](./debugging-authentication.md). --- ## Connect Admin to an identity provider Configure Admin to use an external identity provider (IdP) via OpenID Connect (OIDC) at the application level, including claims and mapping rules. :::info Deploying with Helm? If you deploy Camunda 8 Self-Managed with Helm, use the [Helm chart authentication and authorization guides](/self-managed/deployment/helm/configure/authentication-and-authorization/index.md) to configure OIDC and Admin. ::: ## About Authentication and authorization You can configure IdP integration to control authentication and authorization for both the web components and machine-to-machine (M2M) API access (for connectors and workers). | Access type | Description | | :------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [Web components](#web-components) | Users authenticate via the OIDC [Authorization Code Flow](https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth).Users are redirected to your IdP to log in, and Camunda receives a token to establish the session.Claims from the token are used to identify the user's username, and (optionally) groups for easier assignment and management.Mapping Rules can be used to map token claims to Camunda roles, authorizations, or tenants.As user information is not stored in the Orchestration Cluster using OIDC, features such as user search and user validation on assigning authorizations or tenants and user management are not available in the Orchestration Cluster Admin UI. | | [Machine-to-machine (M2M) API access](#machine-to-machine-m2m-api-access) | Connectors, job workers or other implementations that use the Orchestration Cluster REST or gRPC APIs (for example, by using one of the Camunda Clients) use the [OAuth Client Credentials Flow](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4) to obtain a JWT access token for authentication.This authentication method for Camunda Clients is separate from the interactive user login and typically requires additional configuration in your IdP and Camunda. | ## Web components Configure Admin so your users can use your IdP to authenticate to the Orchestration Cluster. ### Prerequisites - A Camunda 8 Orchestration Cluster (Self-Managed). - Access to an OIDC-compliant IdP (for example, Keycloak, Auth0, Okta, Microsoft EntraID). - Client credentials (client ID, client secret, issuer URI) from your IdP. ### Step 1: Prepare your IdP Before configuring Camunda, you must first prepare your IdP: 1. Register a new application/client for the Orchestration Cluster in your IdP. - Set the application type to "Web" or "Confidential". - Enable OIDC support. - Configure the necessary scopes (for example, `openid`, `profile`, `email`). - Ensure the client is allowed to access user information. 2. Set the **redirect URI** to match your Camunda deployment (default: `http://localhost:8080/sso-callback`). 3. Assign the users or groups who require access to Camunda 8. 4. (Optional) Configure group or other claims if you want to use group-based authorizations or mapping rules in Camunda. 5. Note the **client ID**, **client secret**, and **issuer URI** as these are required during Camunda configuration. :::note For most IdPs, the default claim for the username is `sub` (subject). If you want to use a different claim (for example, `preferred_username` or `email`), configure your IdP to include it in the token, and update the [Orchestration Cluster configuration](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#camunda.security.authentication.oidc). ::: ### Step 2: Choose your deployment and configuration method You can configure OIDC using `application.yaml` or environment variables. Select the option that best fits your deployment approach. ### Step 3: Set the authentication method to OIDC Set the authentication method to OIDC using the following settings: ```yaml camunda.security.authentication.method: oidc ``` ``` CAMUNDA_SECURITY_AUTHENTICATION_METHOD=oidc ``` ### Step 4: Configure the OIDC connection details Set the following properties using the respective values from your IdP: ```yaml camunda.security.authentication.oidc.client-id: camunda.security.authentication.oidc.client-secret: camunda.security.authentication.oidc.issuer-uri: camunda.security.authentication.oidc.redirect-uri: camunda.security.authentication.oidc.username-claim: camunda.security.authentication.oidc.audiences: camunda.security.authentication.oidc.scope: ["openid"] ``` ``` CAMUNDA_SECURITY_AUTHENTICATION_OIDC_CLIENTID= CAMUNDA_SECURITY_AUTHENTICATION_OIDC_CLIENTSECRET= CAMUNDA_SECURITY_AUTHENTICATION_OIDC_ISSUERURI= CAMUNDA_SECURITY_AUTHENTICATION_OIDC_REDIRECTURI= CAMUNDA_SECURITY_AUTHENTICATION_OIDC_USERNAMECLAIM= CAMUNDA_SECURITY_AUTHENTICATION_OIDC_AUDIENCES= CAMUNDA_SECURITY_AUTHENTICATION_OIDC_SCOPE=["openid"] ``` :::tip If your OIDC provider needs to be reached using different URLs from the browser and backend (for example, when running in Docker or behind a reverse proxy), you can configure separate URIs instead of a single `issuer-uri`. See [use separate OIDC provider URIs for browser and backend](./special-oidc-cases.md#use-separate-oidc-provider-uris-for-browser-and-backend) for details. ::: ## Redirect URI Use the redirect URI to define where the Identity Provider (IdP) sends users back after successful authentication. By default, the redirect URI is: `{baseUrl}/sso-callback` At runtime, `{baseUrl}` resolves to the URL used to access your Orchestration Cluster deployment. In most cases, you do not need to change this value. You may need to customize the redirect URI in advanced scenarios, such as: - When your deployment is accessed through reverse proxies or load balancers - When you need to pass context information while configuring multiple OIDC providers Regardless of customization, the redirect URI must always point to the `/sso-callback` endpoint of your Orchestration Cluster deployment. Most Identity Providers require you to explicitly configure allowed redirect URIs for security reasons. Ensure the value configured in your IdP exactly matches the redirect URI used here, whether it is static or dynamically resolved using `{baseUrl}`. :::note `{baseUrl}` is dynamically resolved for each request based on the URL used to access the Orchestration Cluster instance. It is composed of the following parts: - `{scheme}`: The transport scheme (`http` or `https`) - `{host}`: The hostname used to connect to the instance - `{port}`: The port number, if specified (omitted if not used) - `{contextPath}`: The context path of the Orchestration Cluster instance, if configured (omitted if none) For example: - Accessing the instance via `https://camunda.acme.com/identity` resolves `{baseUrl}` to `https://camunda.acme.com` - Accessing the instance via `https://services.acme.com:18080/camunda/` resolves `{baseUrl}` to `https://services.acme.com:18080/camunda` ::: - **Username claim**: By default, the `sub` (subject) claim from the token is used as the username. If you want to use a different claim (such as `preferred_username` or `email`), ensure your IdP includes it in the token and set the `username-claim` property accordingly. You can use a [JSONPath expression](https://www.rfc-editor.org/rfc/rfc9535.html) to locate the username claim in the token (for example, `$['camundaorg']['username']`). :::info If you're using Web Modeler and want to allow deployments to the Orchestration Cluster from there (with the [`BEARER_TOKEN` authentication](/self-managed/components/hub/configuration/modeler-configuration.md#available-authentication-methods)), both applications must use the same IdP. You also need to make the cluster accept the token passed by Web Modeler. To do so, include the Web Modeler UI's token audience in the configured list of audiences. ::: #### Example IdP configuration The following examples show typical OIDC settings in `application.yaml`. Adapt the values to your environment and IdP configuration. ```yaml camunda.security.authentication.oidc.client-id: camunda.security.authentication.oidc.client-secret: camunda.security.authentication.oidc.issuer-uri: "https://login.microsoftonline.com//v2.0" camunda.security.authentication.oidc.redirect-uri: "http://localhost:8080/sso-callback" camunda.security.authentication.oidc.username-claim: "oid" camunda.security.authentication.oidc.audiences: camunda.security.authentication.oidc.scope: ["openid", "profile", "/.default"] ``` ```yaml camunda.security.authentication.oidc.client-id: camunda.security.authentication.oidc.client-secret: camunda.security.authentication.oidc.issuer-uri: "https:///realms/" camunda.security.authentication.oidc.redirect-uri: "http://localhost:8080/sso-callback" camunda.security.authentication.oidc.username-claim: "preferred_username" camunda.security.authentication.oidc.audiences: camunda.security.authentication.oidc.scope: ["openid", "profile", "email"] ``` ### Step 5: Restart the Orchestration Cluster After updating your configuration, (re)start the Orchestration Cluster for the configuration changes to be applied. ### Step 6: Test user authentication At this point, you should be able to log in to the Orchestration Cluster using any user account from your IdP that is assigned to this client (application). :::note If login is successful, you will see that you are not authorized to access the Orchestration Cluster UIs. This is expected, as you have not yet configured an Admin user or any authorizations for the user. ::: ### Step 7: Assign Admin role to users [Authorizations](../../../../components/concepts/access-control/authorizations.md) are enabled by default. This means users cannot access any Orchestration Cluster UI or APIs - except authorizations that have been granted to them. To allow users to access the Orchestration Cluster UI, you can assign the "Admin" role to a user from your IdP: ```yaml camunda.security.initialization.defaultRoles.admin.users: [] ``` ``` CAMUNDA_SECURITY_INITIALIZATION_DEFAULTROLES_ADMIN_USERS_0= ``` 1. Replace `` with the username provided by your IdP (matching the value of the claim configured as `username-claim`). 1. Restart your Orchestration Cluster and verify that the chosen user has the Admin Role, for example by visiting `localhost:8080/identity/roles/admin/users`. 1. If the username is shown, continue configuring your own groups, mapping rules, or setting up [authorizations](../../../../components/concepts/access-control/authorizations.md) for other users. :::tip For more details on assigning users to the default roles, see the [corresponding documentation](overview.md#assign-users-clients-groups-or-mapping-rules-to-roles-via-configuration). ::: ### (Optional) Step 8: Configure bring your own groups You can manage groups in the Orchestration Cluster or bring groups that you have already configured in your IdP. For the latter, proceed as follows: 1. Configure your IdP to include a groups claim in the token (for example, `groups` or `roles`). The value should be an array of Strings where each entry is the ID of a group. 1. Set the `groups-claim` property in your Camunda configuration to match the claim name. Similar to the `username-claim`, you can use a [JSONPath expression](https://www.rfc-editor.org/rfc/rfc9535.html) to locate the groups claim in the token (for example, `$['camundaorg']['groups']`). You can then use these groups for role and authorization assignment, and tenant assignment. ```yaml camunda.security.authentication.oidc.groups-claim: ``` ``` CAMUNDA_SECURITY_AUTHENTICATION_OIDC_GROUPSCLAIM= ``` ### (Optional) Step 9: Mapping rules You can use mapping rules for advanced scenarios, such as mapping IdP claims to Camunda roles, authorizations, or tenants. See [mapping rules documentation](components/admin/mapping-rules.md) for more information on how to define mapping rules. ## Machine-to-machine (M2M) API access Configure job workers, connectors, or custom client applications to use the Orchestration Cluster REST or gRPC API with OAuth access tokens provided by your Identity Provider. ### Prerequisites - The Orchestration Cluster (Self-Managed) user interface is [configured against your IdP](#user-interface). - Client credentials (client ID, client secret, authorization server URI) from your IdP. ### Step 1: Configure the OIDC client id claim When it receives a request with an access token, the Orchestration Cluster needs to identify the client based on a claim in the token's payload. You can determine this claim by applying the following setting to the Orchestration Cluster: ```yaml camunda.security.authentication.oidc.client-id-claim: ``` ``` CAMUNDA_SECURITY_AUTHENTICATION_OIDC_CLIENTIDCLAIM= ``` As an example, assume that your client's access token payload looks like this: ```json { "sub": "client123", "client-identifier": "123" } ``` If you have set `camunda.security.authentication.oidc.client-id-claim` to `client-identifier`, then the Orchestration Cluster will use `123` as your client's ID when it applies memberships in groups, roles, and tenants, as well as authorizations. #### How principal identification works When both `camunda.security.authentication.oidc.username-claim` and `camunda.security.authentication.oidc.client-id-claim` are configured, and a token containing both claims is presented to the platform, the Orchestration Cluster uses the following logic to identify a single principal for the request: 1. If the client id claim is present, the request is treated as a client request with the corresponding ID. 1. If the client id is not present, the request is treated as a user request with the corresponding username matching the username-claim. 1. If neither the client id claim nor the username claim are set, then the request is rejected. #### Controlling the principal identification order In most cases we expect the default detection logic to be sufficient, however if you have a use-case for changing the order, the configuration property `camunda.security.authentication.oidc.prefer-username-claim` can be set to `true`. In this case the logic will be: 1. If the username claim is present, the request is treated as a user request with the corresponding username. 1. If the username claim is not present, the request is treated as a client request with the corresponding ID matching the client id claim. 1. If neither the client id claim nor the username claim are set, then the request is rejected. #### Recommendations for client ID and username claim configuration We recommend to set client id claim and username claim as follows: - The client id claim should not be the same as the username claim. - The claim that is checked first (based on the ordering defined in [How principal identification works](#how-principal-identification-works) and [Controlling the principal identification order](#controlling-the-principal-identification-order)) should not be present in tokens for the opposite principal type. As an example, using the default logic in [How principal identification works](#how-principal-identification-works), if the client ID claim is `client_id`, then tokens issued for users should not contain a `client_id` claim. - For ease of use, the client id claim's value should be the client id from the Identity Provider. This way, you can use the same value across both your Identity Provider and the Orchestration Cluster as the identifier of your client. ### Step 2: Prepare your IdP Next, configure a client in your IdP: 1. Register a new application/client for your job worker in your IdP. - Create a new application/client in your IdP. - Configure the necessary scopes (for example, `openid`). - Create a new client secret. - Ensure that the client's access tokens include the client id claim as configured in the previous step. 2. Note the **client ID**, **client secret**, and **authorization URI** as these are required during Camunda configuration. ### Step 3: Configure your worker application Depending on your application type (for example, standalone Java application, Spring Boot application), the configuration steps may vary. Refer to the documentation of your chosen Camunda Client for details on how to configure authentication using client credentials. - Orchestration Cluster REST and gRPC API clients: See [REST API authentication](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md#oidc-based-authentication-using-client-credentials). - Java Client: See [Java client authentication](/apis-tools/java-client/getting-started.md?authentication=oidc-self-managed#step-2-connect-to-your-camunda-8-cluster). - Spring Boot Starter: See [Spring Boot Starter authentication](/apis-tools/camunda-spring-boot-starter/getting-started.md?authentication=oidc#step-3-configure-the-camunda-8-connection). - Connectors: See [Connector authentication](/self-managed/components/connectors/connectors-configuration.md). - **Audience Validation**: If you have configured the audiences property for the Orchestration Cluster (`camunda.security.authentication.oidc.audiences`), the Orchestration Cluster will validate the audience claim in the token against the configured audiences. Make sure your token has the correct audience from the Orchestration Cluster above, or add your audience in the Orchestration Cluster configuration. :::note As per default authorizations are enabled, your application will only be able to retrieve the topology, with other requests requiring you to configure [authorizations](../../../../components/concepts/access-control/authorizations.md) for the client. You should use your `client id` when configuring authorizations. ::: 1) Add the dependency to your Java Project: ```xml io.camunda camunda-client-java 8.8.x ``` 2. Update your Java code to configure and verify authentication: ```java private static final String clientId = ""; private static final String clientSecret = ""; private static final String authorizationServer = ""; private static final String audience = ""; private static final String ocAudience = ""; private static final String clusterGrpcLocal = "grpc://localhost:26500"; private static final String clusterRestLocal = "http://localhost:8080"; // Build a new OAuthCredentialsProvider final OAuthCredentialsProvider credentialsProvider = new OAuthCredentialsProviderBuilder() .authorizationServerUrl(authorizationServer) .audience(audience) .clientId(clientId) .clientSecret(clientSecret) .scope(ocAudience) // for Microsoft EntraID typically use: ocAudience + "/.default" .build(); // Build a new Camunda Client with the CredentialsProvider try (CamundaClient client = CamundaClient.newClientBuilder() .grpcAddress(URI.create(clusterGrpcLocal)) .restAddress(URI.create(clusterRestLocal)) .credentialsProvider(credentialsProvider) .build()) { // Send a topology request to verify authentication Topology t = client.newTopologyRequest().send().join(); System.out.println(t.toString()); } ``` 1. Add the dependency to your Java Project: ```xml io.camunda camunda-spring-boot-starter ${version.camundastarter} ``` 2. Configure your application.yaml: ```yaml camunda: client: mode: self-managed auth: client-id: client-secret: token-url: audience: scope: grpc-address: grpc://localhost:26500 rest-address: http://localhost:8080 ``` 3. Verify authentication in code: ```java @SpringBootApplication public class App implements CommandLineRunner { @Autowired private CamundaClient client; public static void main(String[] args) { SpringApplication.run(App.class, args); } @Override public void run(final String... args) { Topology t = client.newTopologyRequest().send().join(); System.out.println(t.toString()); } } ``` 1. Configure your application.yaml: ```yaml camunda: client: mode: self-managed auth: client-id: client-secret: token-url: audience: scope: grpc-address: grpc://localhost:26500 rest-address: http://localhost:8080 ``` 2. Add the following dependencies to your project: ```xml io.camunda.connector spring-boot-starter-camunda-connectors ${version.connectors} ``` Note: You can run the Connector Runtime simply using Helm or Docker Image. #### Identity Provider Example Configurations ```java private static final String clientId = ""; private static final String clientSecret = ""; private static final String authorizationServer = "https://login.microsoftonline.com//oauth2/v2.0/token"; private static final String audience = ""; private static final String ocAudience = "" + "/.default"; private static final String clusterGrpcLocal = "grpc://localhost:26500"; private static final String clusterRestLocal = "http://localhost:8080"; ``` ```java private static final String clientId = ""; private static final String clientSecret = ""; private static final String authorizationServer = "https:///realms//protocol/openid-connect/token"; private static final String audience = ""; private static final String ocAudience = ""; private static final String clusterGrpcLocal = "grpc://localhost:26500"; private static final String clusterRestLocal = "http://localhost:8080"; ``` ## Logout handling (RP-initiated logout) When Orchestration Cluster Admin is configured with an external OIDC-compliant identity provider (IdP), you can enable relying party (RP)-initiated logout so that signing out of Camunda also triggers a logout at the IdP. :::note This applies to Self-Managed Orchestration Clusters only. RP-initiated logout is not supported in SaaS. ::: ### Configure RP-initiated logout #### Enable or disable RP-initiated logout Enable or disable RP-initiated logout using the following setting: ```yaml camunda: security: authentication: oidc: idp-logout-enabled: true|false ``` ``` CAMUNDA_SECURITY_AUTHENTICATION_OIDC_IDPLOGOUTENABLED= ``` :::note RP-initiated logout is enabled by default for all new deployments. ::: **Logout behavior depends on this setting:** - **Orchestration Cluster-only logout (RP-initiated logout disabled)** - Clears only the Orchestration Cluster session. - No request is sent to the IdP, so the user remains signed in there. - **RP-initiated logout (enabled)** (default for new 8.9+ installations) - Clears the Orchestration Cluster session and calls the IdP logout endpoint. - Signs the user out of Orchestration Cluster Admin, Tasklist, Operate, and the IdP. - Redirects the user to the login page of the Camunda app where logout was initiated. - Logout behavior for other applications using the same IdP depends on the IdP’s RP-initiated logout implementation. Existing Self-Managed deployments upgraded from earlier versions can continue to use Orchestration Cluster-only logout. To preserve the previous behavior after upgrading, explicitly set the `IDPLOGOUTENABLED` configuration property to `false`. #### Configure the IdP logout endpoint By default, Admin retrieves the IdP logout endpoint from the OIDC discovery document (the `.well-known` endpoint). If you do not use `issuer-uri` and instead configure endpoints manually, define the logout endpoint alongside `authorization-uri`, `token-uri`, and `jwk-set-uri`, as shown in the [special OIDC configuration cases](./special-oidc-cases.md): ```yaml camunda.security: authentication: oidc: authorization-uri: https://login.microsoftonline.com//oauth2/v2.0/authorize token-uri: https://login.microsoftonline.com//oauth2/v2.0/token jwk-set-uri: https://login.microsoftonline.com//discovery/v2.0/keys end-session-endpoint-uri: https://login.microsoftonline.com//oauth2/v2.0/logout ``` ```bash CAMUNDA_SECURITY_AUTHENTICATION_OIDC_ENDSESSIONENDPOINTURI=https://login.microsoftonline.com//oauth2/v2.0/logout CAMUNDA_SECURITY_AUTHENTICATION_OIDC_AUTHORIZATIONURI=https://login.microsoftonline.com//oauth2/v2.0/authorize CAMUNDA_SECURITY_AUTHENTICATION_OIDC_TOKENURI=https://login.microsoftonline.com//oauth2/v2.0/token CAMUNDA_SECURITY_AUTHENTICATION_OIDC_JWKSETURI=https://login.microsoftonline.com//discovery/v2.0/keys ``` #### Configure the post-logout redirect URL in the IdP To ensure users are redirected correctly after logout, configure a post-logout redirect URL in your IdP. The post-logout URL is the Camunda hostname, context path (if applicable) plus `/post-logout`. For example, if Admin is accessible at `http://localhost:8080`, configure the following post-logout redirect URL in your IdP: ``` http://localhost:8080/post-logout ``` For hostname and context path configuration, adjust the URL accordingly. For example, if your Camunda deployment is accessible at `http://localhost:8080/orchestration`, configure the following post-logout redirect URL in the IdP: ``` http://localhost:8080/orchestration/post-logout ``` ## Troubleshooting ### General authentication issues If authentication does not work as expected: - Check your application logs for authentication errors. - Ensure your IdP client is configured to allow the specified redirect URI. - Verify the claim names match your IdP's token claims. ### RP-initiated logout If RP-initiated logout does not behave as expected, check your application logs for the following messages. #### Unable to determine end-session endpoint If you configure OIDC using explicit authorization, token, or logout URIs instead of the issuer URI, and do not specify a logout endpoint, the following message is logged: ``` Unable to determine end-session endpoint for OIDC logout. Falling back to {baseLogoutUrl} without logout hint. ``` Ensure you either: - Configure the `issuer-uri` so Admin can retrieve the logout endpoint from the OIDC discovery document, or - Explicitly set the `end-session-endpoint-uri`. #### No client registration found If the `registrationId` used for logout (the identifier of the configured OIDC client registration) cannot be resolved, Admin cannot construct an RP-initiated logout request. The following message is logged: ``` No client registration found for id `{registrationId}`. Falling back to {baseLogoutUrl} without logout hint. ``` Verify that the configured client registration ID matches the OIDC client definition in your IdP configuration. #### Missing login_hint / logout_hint Some IdPs require a `logout_hint` parameter for RP-initiated logout. Admin derives `logout_hint` from the OIDC user's `login_hint` claim. This claim typically contains a user identifier, such as a username or email, which the IdP uses to identify the session to terminate. If no `login_hint` is present, the following message is logged and the logout request is sent without a logout hint: ``` No 'login_hint' claim found in OIDC user. Falling back to '{baseLogoutUrl}' without logout hint. ``` Ensure that your IdP includes a `login_hint` claim in the ID token if your IdP requires `logout_hint` during logout. #### No post-logout redirect URL configured You must explicitly configure the post-logout redirect URL in your IdP. If no valid post-logout redirect URL is available, Admin falls back to a default path. In this case, the following message is logged: ``` No valid post-logout redirect URL found in session, falling back to default: '/' ``` Ensure that the post-logout redirect URL (`/post-logout`) is registered in your IdP configuration. ## Further resources - [OIDC configuration reference](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md) - [OpenID Connect (OIDC) overview](https://openid.net/connect/) - [Camunda authentication and authorization](../../../../components/concepts/access-control/authorizations.md) - [Helm chart authentication and authorization configuration](/self-managed/deployment/helm/configure/authentication-and-authorization/index.md) --- ## Connect to multiple identity providers Camunda 8 Orchestration Cluster supports multiple OpenID Connect (OIDC) identity providers (IdPs). This lets users from different organizations authenticate using their preferred provider. With multiple IdPs configured: - Users choose an IdP when logging in to a cluster. - Camunda validates and accepts access tokens from trusted issuers for UI and API access. - You can support identity federation and multi-tenancy scenarios with more flexible authentication. ## Overview When multiple OIDC providers are set up, the cluster login page lets users select the provider they wish to use. For API access, any bearer token issued by one of the configured and trusted issuers is accepted; all others are denied. ## Prerequisites - Camunda 8 Orchestration Cluster (Self-Managed) deployed. - At least two OIDC-compliant IdPs (for example, Microsoft Entra ID, Keycloak, Okta). - Administrative access to each IdP (to register applications and obtain credentials). ## Configure multiple identity providers Configure multiple IdPs by defining each provider as a separate registration in `application.yaml` or using environment variables. Each provider is identified by a unique ``, which is a user-defined label (e.g., `ENTRA`, `KEYCLOAK`, `PARTNER_X`). ### Configure OIDC connection details for each provider Define each provider’s settings using the following pattern (`` is your chosen ID): ```yaml camunda.security.authentication.providers.oidc..client-id: camunda.security.authentication.providers.oidc..client-name: camunda.security.authentication.providers.oidc..client-secret: camunda.security.authentication.providers.oidc..issuer-uri: camunda.security.authentication.providers.oidc..redirect-uri: camunda.security.authentication.providers.oidc..token-uri: camunda.security.authentication.providers.oidc..jwk-set-uri: camunda.security.authentication.providers.oidc..scope: ["openid"] camunda.security.authentication.providers.oidc..audiences: camunda.security.authentication.providers.oidc..grant-type: ``` ``` CAMUNDA_SECURITY_AUTHENTICATION_PROVIDERS_OIDC__CLIENTID= CAMUNDA_SECURITY_AUTHENTICATION_PROVIDERS_OIDC__CLIENTNAME= CAMUNDA_SECURITY_AUTHENTICATION_PROVIDERS_OIDC__CLIENTSECRET= CAMUNDA_SECURITY_AUTHENTICATION_PROVIDERS_OIDC__ISSUERURI= CAMUNDA_SECURITY_AUTHENTICATION_PROVIDERS_OIDC__REDIRECTURI= CAMUNDA_SECURITY_AUTHENTICATION_PROVIDERS_OIDC__AUTHORIZATIONURI= CAMUNDA_SECURITY_AUTHENTICATION_PROVIDERS_OIDC__TOKENURI= CAMUNDA_SECURITY_AUTHENTICATION_PROVIDERS_OIDC__JWKSETURI= CAMUNDA_SECURITY_AUTHENTICATION_PROVIDERS_OIDC__SCOPE= CAMUNDA_SECURITY_AUTHENTICATION_PROVIDERS_OIDC__AUDIENCES= CAMUNDA_SECURITY_AUTHENTICATION_PROVIDERS_OIDC__GRANTTYPE= ``` Example configuration for two providers: ```yaml # Keycloak camunda.security.authentication.providers.oidc.keycloak.client-name: "Keycloak - MyCompany" camunda.security.authentication.providers.oidc.keycloak.client-id: camunda.security.authentication.providers.oidc.keycloak.client-secret: camunda.security.authentication.providers.oidc.keycloak.issuer-uri: "https:///realms/" camunda.security.authentication.providers.oidc.keycloak.redirect-uri: "http://localhost:8080/sso-callback" camunda.security.authentication.providers.oidc.keycloak.audiences: camunda.security.authentication.providers.oidc.keycloak.scope: ["openid", "profile", "email"] # Microsoft EntraID camunda.security.authentication.providers.oidc.entraid.client-name: "Microsoft EntraID - ContractorCompany" camunda.security.authentication.providers.oidc.entraid.client-id: camunda.security.authentication.providers.oidc.entraid.client-secret: camunda.security.authentication.providers.oidc.entraid.issuer-uri: "https://login.microsoftonline.com//v2.0" camunda.security.authentication.providers.oidc.entraid.redirect-uri: "http://localhost:8080/sso-callback" camunda.security.authentication.providers.oidc.entraid.audiences: camunda.security.authentication.providers.oidc.entraid.scope: ["openid", "profile", "/.default"] ``` :::tip The `issuer-uri` property is required for each provider configuration. ::: You can repeat these variables for any number of OIDC IdPs by using a unique `` for each. ### Configure global OIDC claims The following OIDC-related properties are shared by all configured providers and must be the same for every IdP: ```yaml camunda.security.authentication.oidc.client-id-claim: camunda.security.authentication.oidc.groups-claim: camunda.security.authentication.oidc.username-claim: camunda.security.authentication.oidc.organization-id: ``` ``` CAMUNDA_SECURITY_AUTHENTICATION_OIDC_CLIENTIDCLAIM= CAMUNDA_SECURITY_AUTHENTICATION_OIDC_GROUPSCLAIM= CAMUNDA_SECURITY_AUTHENTICATION_OIDC_USERNAMECLAIM= CAMUNDA_SECURITY_AUTHENTICATION_OIDC_ORGANIZATIONID= ``` These define: - Which claim to use as the client ID and username - The claim containing group information (if applicable) - The claim for organization or tenant assignment All IdPs must use the same claims for these purposes. ### Login page behavior After you configure multiple IdPs and restart Camunda 8 Orchestration Cluster, users can select an identity provider on the login page and sign in. ### API authentication For machine-to-machine (M2M) API calls (REST/gRPC), Camunda accepts access tokens (bearer tokens) from any of the configured IdPs, as long as the `issuer` claim matches one of the trusted issuer URIs. If an access token’s issuer is not configured, the request is denied. ### Best practices - Each IdP should use a unique and meaningful ``. - Ensure the `issuer URI` is present and correct for each provider. - Configure claims consistently across all IdPs (for example, client ID, username, and groups). ### Overall workflow 1. Prepare and register applications/clients in each IdP. Obtain the client ID, client secret, and issuer URI. 2. Configure each IdP using environment variables or `application.yaml` (or Helm values). 3. Configure global OIDC claims that work across all providers. 4. Restart Camunda 8 Orchestration Cluster to apply the changes. 5. Test login with accounts from each IdP. 6. Assign roles and authorizations as needed. For details, see [Authorizations](../../../../components/concepts/access-control/authorizations.md). ### Troubleshooting - If users cannot log in, verify that the client configuration, issuer URI, and claims match the values configured in the IdP. - Check the Orchestration Cluster logs for missing or mismatched issuer and client configuration. - If a provider does not appear on the login page, verify that its configuration is complete. ### Further resources - [Configure external Identity Providers](./connect-external-identity-provider.md) - [Camunda authentication and authorization](../../../../components/concepts/access-control/authorizations.md) - [OIDC configuration reference](../core-settings/configuration/properties.md) --- ## Debugging the authentication flow This guide explains how to debug issues in the **authentication and authorization flow** of the Orchestration Cluster. These techniques help identify where and why access may be denied or restricted. Common questions you can answer with these steps: - Why can’t I log into the web applications? - Why does my search request return empty results? The flow consists of three key steps: 1. **Request authentication** - **Input:** HTTP request - **Output:** Spring `Authentication` object with user identity - **Layer:** Spring Security 2. **Establish Orchestration Cluster user context** - **Input:** Spring `Authentication` - **Output:** `CamundaAuthentication` object with roles, groups, and tenant memberships - **Layer:** Orchestration Cluster authentication 3. **Apply authorizations** - **Input:** `CamundaAuthentication` - **Output:** Application data, filtered by authorizations - **Layer:** Orchestration Cluster search and workflow engine Typical failure points: - Step 1: Invalid credentials (for example, failed Basic authentication). - Step 2: Missing role or group memberships. - Step 3: Authorizations not yet configured or missing. To isolate the issue, use: - [Reviewing logs](#reviewing-logs) - [Reviewing data](#reviewing-data) - [Reviewing configuration](#reviewing-configuration) ## Reviewing logs Enable detailed logging to trace authentication decisions: ```yaml logging.level: org.springframework.security: TRACE io.camunda: authentication: DEBUG security: DEBUG ``` ``` LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_SECURITY=TRACE LOGGING_LEVEL_IO_CAMUNDA_AUTHENTICATION=DEBUG LOGGING_LEVEL_IO_CAMUNDA_SECURITY=DEBUG ``` With these settings, you can trace request handling and how Spring Security filter chains determine authentication outcomes. ## Reviewing data To review the assignment of users and clients to roles, groups, or tenants—as well as which authorizations are in place—you can use the [Admin UI](/components/admin/admin-introduction.md). If you do not have access to the API, you can also check the same data in the following Elasticsearch/OpenSearch indexes: - `camunda-authorization` - `camunda-group` - `camunda-mapping-rule` - `camunda-role` - `camunda-tenant` - `camunda-user` - `camunda-web-session` ## Reviewing configuration To review the effective configuration of your Orchestration Cluster, you can call the [Spring Boot Actuator endpoint](https://docs.spring.io/spring-boot/reference/actuator/endpoints.html#actuator.endpoints) at: ``` :/actuator/configprops ``` For example, with a Camunda 8 Run installation, this endpoint is available at `http://localhost:9600/actuator/configprops`. In other setups, replace `http://localhost:9600` with the URL to your Orchestration Cluster's actuator port and endpoint. Note that the actuator port differs from the Orchestration Cluster API port and may not always be accessible, depending on your deployment setup. Here is an excerpt from an example installation: ```json { ... "camunda.security-io.camunda.application.commons.security.CamundaSecurityConfiguration$CamundaSecurityProperties": { "prefix": "camunda.security", "properties": { ... "authentication": { "method": "OIDC", "authenticationRefreshInterval": "PT30S", "unprotectedApi": false, "oidc": { "issuerUri": "https://myoidcprovider.example.com", "clientId": "my-oidc-client", "clientSecret": "******", "grantType": "authorization_code", "redirectUri": "http://localhost:8080/sso-callback", "scope": [ "openid", "profile" ], "usernameClaim": "preferred_username", "clientIdClaim": "oid", "authorizeRequest": {} } } ... } } } ``` In the response, review the settings in the `camunda.security` section, compare them against the [configuration reference](../core-settings/configuration/properties.md#authentication), and confirm they match your intended values. This is especially useful if you are applying the configuration via Helm values or environment variables and want to double-check that your configuration was applied correctly. --- ## MCP processes The Orchestration Cluster admin UI provides an overview of the processes currently registered as MCP tools in the [Processes MCP Server](/apis-tools/processes-mcp/processes-mcp-overview.md). ## View registered MCP processes Navigate to **MCP Processes** in the Orchestration Cluster admin UI to see all processes currently exposed as MCP tools. ![Admin UI page listing processes registered as MCP tools, showing tool name, tool description, process name, version, and tenant](img/mcp-tools-admin.png) The following information is displayed for each registered process: | Column | Description | | :------------------- | :------------------------------------------------------------------------------------------------ | | **Tool name** | The MCP tool identifier configured in the MCP start event element template. | | **Tool Description** | The plain-language description of the tool's function, as configured in the MCP start event. | | **Process Name** | The name of the BPMN process registered as an MCP tool. | | **Version** | The process definition version currently registered. Only the latest deployed version is exposed. | | **Tenant** | The tenant the process belongs to. | ## Troubleshoot - **A process is not appearing after deployment**: Verify that the process was deployed successfully and that the MCP start event element template is applied to the start event. Check for deployment errors in Operate. - **A process shows an unexpected version:** The Processes MCP Server always exposes the latest deployed version. If a previous version is showing, a newer deployment may have failed. See [version binding](/apis-tools/processes-mcp/processes-mcp-version-binding.md) for more details. --- ## Admin in Self-Managed Admin (formerly Orchestration Cluster Identity) is included by default with the [Orchestration Cluster](/self-managed/reference-architecture/reference-architecture.md#orchestration-cluster) in all Self-Managed installation methods. Within a cluster, Admin provides unified, cluster-level identity management and authorization. :::note Profile transition In Camunda 8.9, the `admin` Spring profile replaces the `identity` profile. Both profiles work interchangeably in 8.9. The `identity` profile is deprecated and will be removed in a future version. ::: :::info The following guides cover Admin configuration in Self-Managed environments. For information on using and managing Admin, see [Admin user guides](/components/admin/admin-introduction.md). ::: ## Initial setup Using the default setup for [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) will result in a cluster with: 1. Web components login enabled 2. API authentication disabled 3. Authorizations disabled 4. An initial user with username/password: `demo` / `demo` 5. An `admin` role with full permissions, applied to the `demo` user To modify the [initial configuration](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md), define your custom values in `application.yaml`, and pass this file at startup using the `--config` flag. See [this section](/self-managed/quickstart/developer-quickstart/c8run/configuration.md#enable-authentication-and-authorization) for details. :::note - In Helm installations, API authentication and authorization are enabled by default. You can adjust these settings in `application.yaml` or using environment variables. - As a Spring Boot application, the Orchestration Cluster supports standard [Spring configuration](https://docs.spring.io/spring-boot/reference/features/external-config.html) methods. [Review configurations which apply to all components within the Orchestration Cluster](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md). ::: ## Configure initial users If users are managed within the Orchestration Cluster (that is, without an external Identity Provider), you can create an initial user in three ways: If you use an external Identity Provider instead, continue with [connect Admin to an identity provider](./connect-external-identity-provider.md). - Using the [Orchestration Cluster UI](#option-1-orchestration-cluster-ui) - Using the [Setup endpoint of the Orchestration Cluster REST API](#option-2-setup-rest-api) - Using the [configuration](#option-3-configuration) :::info Production and no-clickops setups For production environments, prefer **Option 3: configuration** so your admin bootstrap, role assignments, and authentication settings are defined declaratively in `application.yaml`, environment variables, or Helm-managed application configuration. The UI and Setup API options are useful for manual bootstrap, but they are not the best fit for repeatable, Git-managed deployments. Typical production next steps are: - Define initial users with the [configuration examples](#option-3-configuration) and assign them to roles with the [role assignment examples](#assign-users-clients-groups-or-mapping-rules-to-roles-via-configuration). - If you deploy with Helm, provide these settings through [application configs](/self-managed/deployment/helm/configure/application-configs.md) and the [Helm authentication and authorization guides](/self-managed/deployment/helm/configure/authentication-and-authorization/index.md). ::: :::warning After completing the initial setup, ensure at least one user remains assigned to the `admin` role. If no admin user exists, a third party could create a new admin account and gain full access. ::: ### Option 1: Create an initial admin user in Orchestration Cluster UI {#option-1-orchestration-cluster-ui} If no admin user exists, the orchestration cluster web applications show a screen for creating the initial user: ![identity-create-initial-user](./img/create-initial-user.png) This user will be assigned to the `admin` role and granted all permissions in the system. Once an admin user exists, this screen is no longer shown. ### Option 2: Create an initial admin user via the Setup endpoint of Orchestration Cluster REST API{#option-2-setup-rest-api} You can create the first admin user by calling the Setup API endpoint: `POST /v2/setup/user` ([API documentation](/apis-tools/orchestration-cluster-api-rest/specifications/create-admin-user.api.mdx)) with the following JSON request body: ```json { "username": "", "password": "", "name": "", "email": "" } ``` This endpoint is only available if **no user is assigned to the `admin` role**. This option is convenient for scripted bootstrap, but for long-lived production environments we recommend keeping the desired state in configuration. ### Option 3: Define initial users via the configuration{#option-3-configuration} To configure initial users programmatically, include the relevant definitions in your `application.yaml` or environment variables. ```yaml camunda: security: initialization: users: - username: password: name: email: # add more users to this list as desired ``` ```shell CAMUNDA_SECURITY_INITIALIZATION_USERS_0_USERNAME= CAMUNDA_SECURITY_INITIALIZATION_USERS_0_PASSWORD= CAMUNDA_SECURITY_INITIALIZATION_USERS_0_NAME= CAMUNDA_SECURITY_INITIALIZATION_USERS_0_EMAIL= # add more users as desired by repeating the variables with an incremented index, # like CAMUNDA_SECURITY_INITIALIZATION_USERS_1_USERNAME ``` ```yaml orchestration: security: initialization: users: - username: password: name: email: # add more users to this list as desired ``` :::note By default, a user is not assigned to any roles and therefore has no permissions. See the following section to learn how to assign a user to a role via configuration. ::: #### Assign users, clients, groups, or mapping rules to roles via configuration The orchestration cluster provides a number of [built-in roles](/components/concepts/access-control/authorizations.md#default-roles) with predefined permissions for easier setup. To assign users, clients, groups, or [mapping rules](/components/concepts/access-control/mapping-rules.md) to roles, add the appropriate properties to your `application.yaml` or set them as environment variables. ```yaml camunda: security: initialization: defaultRoles: : users: - # add more users to this list as desired clients: - # add more clients to this list as desired groups: - # add more groups to this list as desired mappings: - # add more mappings to this list as desired ``` ```shell CAMUNDA_SECURITY_INITIALIZATION_DEFAULTROLES__USERS_0= CAMUNDA_SECURITY_INITIALIZATION_DEFAULTROLES__CLIENTS_0= CAMUNDA_SECURITY_INITIALIZATION_DEFAULTROLES__GROUPS_0= CAMUNDA_SECURITY_INITIALIZATION_DEFAULTROLES__MAPPINGS_0= # add more members as desired by repeating the variables with an incremented index, # like CAMUNDA_SECURITY_INITIALIZATION_DEFAULTROLES__USERS_1 ``` Replace `` with the ID of the role you want to configure. :::note Helm deployments When you deploy with Helm, configure these properties via an `application.yaml` file using [application configs](/self-managed/deployment/helm/configure/application-configs.md) (for example with `orchestration.extraConfiguration`), rather than as dedicated Helm values. ::: Here is an example how to configure a user `demo` to become a member of the admin role: ```yaml camunda: security: initialization: defaultRoles: admin: users: - demo ``` ```shell CAMUNDA_SECURITY_INITIALIZATION_DEFAULTROLES_ADMIN_USERS_0=demo ``` You can assign a user to multiple roles by listing them in the respective section of each role. ## Enable API authentication and authorizations In Camunda 8 Run installations, Basic authentication is enabled for the Orchestration Cluster web components, but the API is unprotected, and [authorizations](/components/admin/authorization.md) are disabled. API protection and authorizations can both be enabled by modifying your `application.yaml` or environment variables: ```yaml camunda: security: authentication: unprotected-api: false authorizations: enabled: true ``` ```shell CAMUNDA_SECURITY_AUTHENTICATION_UNPROTECTED-API=false CAMUNDA_SECURITY_AUTHORIZATIONS_ENABLED=true ``` ```yaml orchestration: security: authentication: unprotectedApi: false authorizations: enabled: true ``` :::note To enable authorizations, API protection must also be enabled. ::: Basic authentication credentials are then required when making API requests, as in the following: ```shell curl --request POST 'http://localhost:8080/v1/process-definitions/search' \ -u demo:demo \ --header 'Content-Type: application/json' \ --data-raw '{}' ``` --- ## Special OIDC configuration cases This page provides guidance for less common OIDC configuration scenarios. ## Use separate OIDC provider URIs for browser and backend In some cases, the Orchestration Cluster backend and the user’s browser may need to communicate with the OIDC provider using different URLs. For example, during the [OIDC authorization code flow](https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow): - The browser is redirected to the OIDC provider for login. - Once the user grants consent, the Orchestration Cluster backend exchanges the authorization code for an ID/access token by calling the provider’s token endpoint. In most environments—such as production or centrally managed OIDC providers—both backend and browser can use the same URL. In that case, configure only the [OIDC issuer property](../core-settings/configuration/properties.md#camundasecurityauthenticationoidc) `camunda.security.authentication.oidc.issuer-uri`. The Orchestration Cluster will then request the provider’s well-known endpoint and resolve the required URIs (e.g., authorization and token endpoints). However, in some development environments (for example, Docker Compose), the backend and browser may need different URLs: - The Orchestration Cluster backend might access the OIDC provider through a domain name valid only inside the Docker network. - The browser may need to use `localhost` with a mapped port. In these cases, omit the `camunda.security.authentication.oidc.issuer-uri` property and configure the endpoints explicitly. ```yaml camunda.security: authentication: oidc: authorization-uri: http://localhost:18080/protocol/openid-connect/auth token-uri: http://:18080/protocol/openid-connect/token jwk-set-uri: http://:18080/protocol/openid-connect/certs end-session-endpoint-uri: http://:18080/protocol/openid-connect/logout ``` ``` CAMUNDA_SECURITY_AUTHENTICATION_OIDC_AUTHORIZATIONURI=http://localhost:18080/protocol/openid-connect/auth CAMUNDA_SECURITY_AUTHENTICATION_OIDC_TOKENURI=http://:18080/protocol/openid-connect/token CAMUNDA_SECURITY_AUTHENTICATION_OIDC_JWKSETURI=http://:18080/protocol/openid-connect/certs CAMUNDA_SECURITY_AUTHENTICATION_OIDC_ENDSESSIONENDPOINTURI=http://:18080/protocol/openid-connect/logout ``` The exact property values depend on your OIDC provider and environment. ## Specify resources in token and authorization requests The Orchestration Cluster supports [RFC 8707 (Resource Indicators for OAuth 2.0)](https://datatracker.ietf.org/doc/html/rfc8707). With this, you can specify target resources when requesting access tokens and performing authorization with your OIDC provider. This is useful when your OIDC provider issues different tokens depending on the intended resource (audience). By including resource indicators in token and authorization requests, you can ensure the Orchestration Cluster receives tokens with the appropriate audience claims for your environment. Configure resource indicators using the `camunda.security.authentication.oidc.resource` property: ```yaml camunda: security: authentication: oidc: resource: - https://api.example.com - https://another-resource.example.com ``` Configure resource indicators using the `CAMUNDA_SECURITY_AUTHENTICATION_OIDC_RESOURCE_0` environment variable: ``` CAMUNDA_SECURITY_AUTHENTICATION_OIDC_RESOURCE_0=https://api.example.com CAMUNDA_SECURITY_AUTHENTICATION_OIDC_RESOURCE_1=https://another-resource.example.com ``` ## Use the Bearer JWT client authentication method In environments that require additional security, you can configure the Orchestration Cluster backend to use the Bearer JWT client authentication method, **private key JWT**, instead of the standard client ID and secret method. With this approach, no client secret is used as a credential. Instead, a client assertion JWT is generated and signed using the client’s certificate. For more details on the private key JWT authentication method, refer to [OAuth 2.0 Private Key JWT](https://oauth.net/private-key-jwt/). The OIDC client credentials flow continues to operate normally, the only difference is the type of client credentials used to authenticate with the IdP. Consult your IdP’s documentation for instructions on configuring private key JWT. For example, see [Keycloak documentation](https://www.keycloak.org/securing-apps/authz-client#_client_authentication_with_signed_jwt). Below is the minimal required client credential configuration when using the private key JWT method. Note the absence of `clientSecret`: ```yaml camunda: security: authentication: oidc: clientId: clientAuthenticationMethod: private_key_jwt assertion: keystore: path: password: keyAlias: keyPassword: ``` A comprehensive list of available configuration properties can be found in [OIDC configuration reference](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#camundasecurityauthenticationoidc). --- ## Backups(Concepts) When running an orchestration cluster with [secondary storage](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#secondary-storage), you must configure a snapshot repository in your chosen database: - [Elasticsearch snapshot repository](https://www.elastic.co/guide/en/elasticsearch/reference/current/snapshot-restore.html) - [OpenSearch snapshot repository](https://docs.opensearch.org/docs/latest/tuning-your-cluster/availability-and-recovery/snapshots/snapshot-restore/) The Orchestration Cluster is configured with the snapshot repository name to trigger database snapshots. This ensures coherent backups. :::info Learn more about the backup procedure and why it must be triggered in the [backup guide](/self-managed/operational-guides/backup-restore/backup-and-restore.md). ::: ## Configure this in Kubernetes with Helm When you deploy Camunda 8 Self-Managed on Kubernetes, set the backup repository name as an application configuration value. See [application configurations](/self-managed/deployment/helm/configure/application-configs.md) for how to provide configuration keys using Helm (for example, via values files or environment variable mappings). ## Configuration parameters | Configuration key | Description | Default value | | ------------------------------------- | -------------------------------- | ------------- | | `camunda.data.backup.repository-name` | ES / OS snapshot repository name | - | --- ## Data retention(Concepts) The Orchestration Cluster centrally manages data retention for all data using unified storage and policy configuration. All cluster data, including deployed process definitions, process instance state, user operations, and technical metadata, is written to secondary storage. Depending on your configuration, this secondary storage uses a document-store backend ([Elasticsearch/OpenSearch](/reference/glossary.md#elasticsearchopensearch)) or an [RDBMS](/reference/glossary.md#rdbms). The data representing process instance state becomes immutable after the process instance is finished, and it becomes eligible for archiving. :::note Secondary storage is configurable. Choose the backend that best fits your requirements for indexing, querying, retention, and operations. See [configuring secondary storage](/self-managed/concepts/secondary-storage/configuring-secondary-storage.md) for setup guidance, and refer to [secondary storage](/reference/glossary.md#secondary-storage) for terminology and conceptual context. ::: When using Elasticsearch/OpenSearch, finished data is moved to a dated index (for example, `operate-variable_2020-01-01`), with the suffix representing the completion date of the associated process or operation. Data from both main and dated indices remains searchable and visible in the UI. For RDBMS backends, the exporter does not create dated indices. Data remains in the same tables and stays visible until retention policies delete it. ## Archive period The time between a process instance finishing and being moved to a dated index can be configured using the [waitPeriodBeforeArchiving](/self-managed/components/orchestration-cluster/zeebe/exporters/camunda-exporter.md#configurations) parameter. Refer to that configuration for the current default value. ## Hierarchy-aware retention Starting with Camunda 8.9, retention in Elasticsearch/OpenSearch secondary storage becomes hierarchy-aware for process instance data. This means child process instances (for example, started via Call Activities) are retained as long as their root process instance is retained, instead of being cleaned up independently based on their own end time. ### Retention modes The following retention modes determine how process instance hierarchies are retained. When using Elasticsearch or OpenSearch, you can control how process instance hierarchies and legacy process instance hierarchies (where the root process instance was started before upgrading to 8.9) are handled by configuring the following properties: - `camunda.data.secondary-storage.elasticsearch.history.process-instance-retention-mode` - `camunda.data.secondary-storage.opensearch.history.process-instance-retention-mode` See the [property reference](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md) for details and all available secondary storage settings. The following values are available: - `PI_HIERARCHY` (default starting with 8.9) - For process instance hierarchies started on 8.9 and later, the archiver treats the entire hierarchy (root + descendants + related records) as one retention unit. - For legacy process instance hierarchies (where the root process instance was started before upgrading to 8.9), the archiver keeps the pre-8.9 per-process-instance behavior. This also applies to any process instances started after the upgrade that belong to such legacy hierarchies. - `PI_HIERARCHY_IGNORE_LEGACY` - Applies hierarchy-based retention only to process instance hierarchies started on 8.9 and later. - Ignores legacy process instance hierarchies (no automated per-instance archiving/deletion), intended when legacy cleanup is handled separately. - `PI` (legacy per-process-instance behavior) - Preserves the pre-8.9 behavior and performs retention per process instance (as in 8.8 and earlier). - Child process instances can be archived/deleted independently of the root instance. ## Data cleanup The amount of stored data can grow significantly over time. Therefore, we recommend implementing a data cleanup strategy. Dated indices, which contain only finished process instances, may be safely removed from Elasticsearch/OpenSearch. In the Orchestration Cluster, strategies for the deletion of archived data can be defined via the [retention configuration](/self-managed/components/orchestration-cluster/zeebe/exporters/camunda-exporter.md?configuration=retention#options). --- ## Monitoring(Concepts) The Orchestration Cluster includes the [Spring Boot Actuator](https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready), which provides health checks, metrics, and other monitoring endpoints out of the box. ## Default configuration By default, the Orchestration Cluster uses the following Actuator configuration (differences noted inline): ```yaml # Disable default health indicators # https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-health-indicators management.health.defaults.enabled: false # Enable Kubernetes health groups # https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-kubernetes-probes management.health.probes.enabled: true # (Operate) management.endpoint.health.probes.enabled: true # (Tasklist) # Expose selected Actuator endpoints management.endpoints.web.exposure.include: health, prometheus, loggers, usage-metrics, backup(s) ``` Operate uses `backup`, while Tasklist uses `backups`. ## Available endpoints With this configuration, the following endpoints are available in the Orchestration Cluster: - `:9600/actuator/prometheus` – Prometheus metrics - `:9600/actuator/health/liveness` – Liveness probe - `:9600/actuator/health/readiness` – Readiness probe You can override these defaults by adjusting the configuration parameters. ## Using probes in Kubernetes For details on setting Kubernetes probe parameters, see [Kubernetes configure probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes). **Readiness probe:** ```yaml readinessProbe: httpGet: path: /actuator/health/readiness port: 9600 initialDelaySeconds: 30 periodSeconds: 30 ``` **Liveness probe:** ```yaml livenessProbe: httpGet: path: /actuator/health/liveness port: 9600 initialDelaySeconds: 30 periodSeconds: 30 ``` --- ## Schema and data migration The orchestration cluster persists runtime and task data in secondary storage. This page describes schema and migration behavior for document-store secondary storage (Elasticsearch/OpenSearch), where indices and templates are created automatically on first startup. For RDBMS secondary storage guidance, see [RDBMS configuration](/self-managed/concepts/databases/relational-db/configuration.md). ## Schema For document-store backends, cluster data is stored in indices governed by a schema version. Index names follow this pattern: ```text {cluster-index-prefix}-{legacy-prefix}-{datatype}-{schemaversion}_[{date}] ``` - `cluster-index-prefix` – Prefix for index names (default: ``). - `legacy-prefix` – Legacy component prefix (e.g. `operate`, `tasklist`, `camunda`). - `datatype` – Identifies the type of data stored (e.g., `user`, `variable`, `task`). - `schemaversion` – Version of the index schema. - `date` – Optional date for archived data. > The version in the index name is specific to the schema and may differ from the cluster software version. See [data retention](/self-managed/components/orchestration-cluster/core-settings/concepts/data-retention.md). For configuration details related to document-store secondary storage, see [Camunda Exporter](/self-managed/components/orchestration-cluster/zeebe/exporters/camunda-exporter.md). From 8.8 onwards, no schema migrations are required when upgrading the orchestration cluster. > See also: [Version compatibility checks](./version-compatibility.md) for the rules governing supported upgrade paths and how schema version metadata is validated. --- ## Usage metrics(Concepts) :::warning Deprecated endpoints As of the 8.8 release, the actuator usage metrics endpoints are **deprecated** and will be removed in the 8.10 release. Use the [new usage metrics endpoint](#usage-metrics-endpoint-recommended) instead. ::: ## Usage metrics endpoint (recommended) ### Overview The usage metrics endpoint retrieves metrics for a specific date range, including: - **Process instances:** Total created - **Decision instances:** Total executed - **User tasks assigned:** Total unique assignees - **Active tenants:** Total active tenants - **Tenants:** List of active tenants with per-tenant metrics > **Export interval** > Usage metrics are exported every 5 minutes by default. This may cause a short delay in reported metrics. > You can adjust this interval in `application.properties` or via environment variables: > > ```properties > zeebe.broker.experimental.engine.usageMetrics.exportInterval=5m > ``` ### API details | Parameter | Required | Description | Format / Default | | ------------- | -------- | ---------------------------- | ----------------------------------------- | | `startTime` | Yes | Start of date range | ISO 8601: `2025-09-16T12:30:45.123-00:00` | | `endTime` | Yes | End of date range | ISO 8601: `2025-09-16T12:30:45.123-00:00` | | `tenantId` | No | Filter by tenant | String | | `withTenants` | No | Include per-tenant breakdown | `false` (default) | **Endpoint:** ``` http://:/v2/system/usage-metrics?startTime={startTime}&endTime={endTime}&tenantId={tenantId}&withTenants={withTenants} ``` More info: [usage metrics API](/apis-tools/orchestration-cluster-api-rest/specifications/get-usage-metrics.api.mdx) ### Examples **Without tenant breakdown:** ``` http://:/v2/system/usage-metrics?startTime={startTime}&endTime={endTime}&withTenants=false ``` _Response:_ ```json { "processInstances": 5, "decisionInstances": 23, "activeTenants": 2, "assignees": 3, "tenants": {} } ``` **With tenant breakdown:** ``` http://:/v2/system/usage-metrics?startTime={startTime}&endTime={endTime}&withTenants=true ``` _Response:_ ```json { "processInstances": 5, "decisionInstances": 23, "activeTenants": 2, "assignees": 3, "tenants": { "tenant1": { "processInstances": 1, "decisionInstances": 2, "assignees": 1 }, "tenant2": { "processInstances": 4, "decisionInstances": 21, "assignees": 3 } } } ``` ## Best practices for monitoring - Monitor overall cluster activity by combining process, decision, and task metrics. - Track trends over time to better understand resource usage and user engagement. - Integrate metrics into dashboards or automation scripts for centralized monitoring and alerting. ## Deprecated usage metrics actuator endpoints As of 8.8, the following actuator endpoints are **deprecated** and will be removed in the 8.10 release. Use the [new usage metrics endpoint](#usage-metrics-endpoint-recommended) instead. | Endpoint | Description | Status | | -------------------------------------------- | -------------------------- | ---------- | | `/actuator/usage-metrics/process-instances` | Total process instances | Deprecated | | `/actuator/usage-metrics/decision-instances` | Total decision instances | Deprecated | | `/actuator/usage-metrics/assignees` | Unique user task assignees | Deprecated | **All endpoints accept:** - `startTime` (optional) - `endTime` (optional) - `tenantId` (optional) Format: `yyyy-MM-dd'T'HH:mm:ss.SSSZZ` (e.g., `1970-11-14T10:50:26.963-0100`) ### Examples **Process instances:** ``` http://:/actuator/usage-metrics/process-instances?startTime={startTime}&endTime={endTime}&tenantId={tenantId} ``` _Response:_ ```json { "total": 99 } ``` **Decision instances:** ``` http://:/actuator/usage-metrics/decision-instances?startTime={startTime}&endTime={endTime}&tenantId={tenantId} ``` _Response:_ ```json { "total": 80 } ``` **Task assignments:** ``` http://:/actuator/usage-metrics/assignees?startTime={startTime}&endTime={endTime}&tenantId={tenantId} ``` _Response:_ ```json { "total": 2 } ``` :::warning Breaking change Assignees list removed from response. ::: This endpoint allows reconciliation of users across multiple cluster components and provides insights into active task participants. --- ## Version compatibility checks This page describes how Camunda 8 validates version compatibility when you upgrade a Self-Managed Orchestration Cluster. It covers: - What defines a compatible or incompatible upgrade path - How the **broker** enforces version rules - How **secondary storage management** performs similar checks ## Semantic version basics Camunda 8 versions follow the `MAJOR.MINOR.PATCH` format (for example, `8.8.3`). Early access builds include a pre-release suffix (for example, `8.8.0-alpha1`). - **Major**: No cross-major upgrades or downgrades are supported directly. - **Minor**: Feature releases. You must move only one minor step at a time and follow the required upgrade procedure described below. - **Patch**: Bug or security fixes. You can move forward within the same minor (for example, `8.8.1 → 8.8.3`). - **Pre-release (alpha)**: Builds tagged with `-alpha*` are not valid endpoints in a supported upgrade path. ## Required upgrade procedure All minor version upgrades in a Self-Managed Orchestration Cluster must follow this procedure: 1. **Upgrade to the latest patch version of your current minor.** For example, before upgrading from `8.7.x` to `8.8.y`, first upgrade to the latest `8.7` patch. This is required so that schema version metadata is present and the schema compatibility check can succeed. 2. **Upgrade to the next minor version.** Do not skip minor versions. For example, `8.7.x → 8.8.y` is supported, but `8.6.x → 8.8.y` is not. 3. After reaching the target minor, **upgrade to the latest patch version of the target minor.** For example, after upgrading to `8.8.0`, update to the latest `8.8` patch. You can execute this procedure across multiple validation environments (for example, dev → test → stage → prod) before production rollout. You must not: - Skip the latest-patch upgrade of the source minor before a minor upgrade. The schema compatibility check requires the source minor to be at its latest patch. - Skip minor versions. - Downgrade minor or major versions. - Include pre-release (`-alpha*`) versions in an upgrade chain. Failure to follow this procedure results in an unsupported upgrade path. The broker or schema manager will block startup to prevent unsafe migrations. ## Supported upgrade paths The examples below show representative compatible and incompatible paths. Patch versions can vary as long as minor-version rules are followed. | Scenario | Example | Compatibility | | -------------------------------------------------- | -------------------- | ---------------------------------------------------------- | | Patch upgrade | 8.8.1 → 8.8.3 | Compatible | | Minor upgrade (single step, latest patch required) | 8.7.5 → 8.8.3 | Compatible | | Minor upgrade (skipping a minor) | 8.6.9 → 8.8.3 | Incompatible | | Patch downgrade | 8.8.3 → 8.8.1 | Incompatible (broker); secondary storage skips (see below) | | Minor downgrade | 8.8.3 → 8.7.5 | Incompatible (broker); secondary storage skips (see below) | | Major change | 8.x ↔ 9.x | Incompatible | | Alpha build involved | 8.8.0-alpha1 ↔ 8.8.0 | Incompatible | ## Broker behavior The broker validates the previous persisted version against the new binary version during startup: - **Compatible:** Proceeds with state (DB) migration if required (skipped for restarts or no-op cases). - **Incompatible:** Starts but marks itself unhealthy and does not apply any state or data migration. Check startup logs, fix the unsupported upgrade path, and restart. ### Why incompatible paths are blocked Skipping minors, downgrading, or involving pre-release versions can result in an incompatible state that the broker cannot safely reconcile. Blocking these paths prevents partial or irreversibly invalid migrations. ## Secondary storage (Elasticsearch or OpenSearch) schema manager Secondary storage holds exported data. Starting with Camunda 8.8, schema upgrades are designed to be backward compatible, allowing older application nodes to continue writing to newer schemas. ### When version checks are available | Capability | Introduced in patches | | --------------------------------------------------------------------------------------- | ----------------------- | | Store schema version metadata (no enforcement) | 8.6.31 / 8.7.18 / 8.8.3 | | Enforce compatibility rules (same matrix as broker, with tolerant skips for downgrades) | 8.8.3 | If you upgrade from an earlier patch that does **not** store schema version metadata, the schema manager treats it as an indeterminate case (assumed fresh install) and proceeds. ### Schema manager rules All minor version upgrades must follow the required upgrade procedure described above and proceed strictly minor-by-minor. The schema manager compares the stored schema version (the last successful schema upgrade) with the current application version: | Case | Action | Metadata updated? | Notes | | -------------------------------------------------- | ------------- | -------------------- | --------------------------------------------- | | Patch upgrade | Update schema | Yes (to new version) | | | Minor upgrade (single step, latest patch required) | Update schema | Yes | | | Minor downgrade | Skip | No | Tolerated for rolling update restarts | | Patch downgrade | Skip | No | Avoids churn; schema stays forward compatible | | Skipped minor (multi-step) | Fail startup | No | Prevents unsupported jump | | Alpha build involved | Fail startup | No | Must use stable releases for upgrade path | | Major change | Fail startup | No | Not supported | ### Where the schema version is stored The schema version metadata is stored in a dedicated metadata index. For current versions, this appears as an index named: ```text operate-metadata-8.8.0_ ``` Within that index, the document holding the current schema baseline uses the identifier: ```text id = "schema-version" ``` The document value reflects the last successfully applied version (for example, `8.8.3`). This value is updated only after a successful schema upgrade. If the document or index is missing (for example, if you upgraded from an older patch that didn’t write it), the system treats the startup as a fresh baseline and writes it after initialization. ### Failure behavior If the schema manager detects an incompatible path, it fails fast during application startup before modifying indices. Existing indices remain unchanged. ### Rolling updates and restarts Because schema upgrades are backward compatible from 8.8 onward, a temporary mix of versions during a rolling update is safe. Older patch or minor nodes encountering a newer stored schema version skip schema changes automatically. ## Pre-release (alpha) builds Alpha builds (`-alpha*`) are for evaluation and are **not** valid sources or targets for a supported production upgrade path. Always upgrade between stable releases. ## Recommended operational steps 1. Follow the [required upgrade procedure](#required-upgrade-procedure) for every minor upgrade. 1. Do not include pre-release builds in production upgrade chains. 1. Investigate any broker health status showing `brokerStatus: DOWN` after an upgrade. This typically indicates a rejected upgrade path. ## See also - [Schema and data migration](./schema-and-migration.md) --- ## Admin: Identity as Code This page explains how to configure Identity as Code in the Camunda 8 Self-Managed Orchestration Cluster. Use Identity as Code to create users, roles, groups, authorizations, mapping rules, and tenants at application start. ## Use cases Identity as Code simplifies configuring Self-Managed orchestration clusters across multiple stages. You can create [all identity-related entities](/components/admin/admin-introduction.md#manage-access) on one stage and then deploy them to other stages without further interaction, reducing the chance of error. Another use case is local development, where a cluster might be recreated regularly. After Admin creates an entity, changing its configuration does not update the existing entity. Admin checks only the ID to decide whether an entity already exists. When you deploy with Helm, the most reliable approach is to provide Identity as Code settings through [application configs](/self-managed/deployment/helm/configure/application-configs.md) using `orchestration.extraConfiguration`. The Helm examples below use this pattern so you can apply the same approach consistently across all entity types. ## Configure authorizations ```bash CAMUNDA_SECURITY_INITIALIZATION_AUTHORIZATIONS_0_OWNER_TYPE=USER CAMUNDA_SECURITY_INITIALIZATION_AUTHORIZATIONS_0_OWNER_ID=john.doe CAMUNDA_SECURITY_INITIALIZATION_AUTHORIZATIONS_0_RESOURCE_TYPE=RESOURCE CAMUNDA_SECURITY_INITIALIZATION_AUTHORIZATIONS_0_RESOURCE_ID=* CAMUNDA_SECURITY_INITIALIZATION_AUTHORIZATIONS_0_PERMISSIONS=CREATE,READ ``` ```yaml orchestration: extraConfiguration: - file: identity-as-code.yaml content: | camunda: security: initialization: authorizations: - ownerType: USER ownerId: john.doe resourceType: RESOURCE resourceId: "*" permissions: - CREATE - READ ``` ## Configure groups ```bash CAMUNDA_SECURITY_INITIALIZATION_GROUPS_0_GROUP_ID=test-group CAMUNDA_SECURITY_INITIALIZATION_GROUPS_0_NAME="Test Group" CAMUNDA_SECURITY_INITIALIZATION_GROUPS_0_DESCRIPTION="A cool test group!" CAMUNDA_SECURITY_INITIALIZATION_GROUPS_0_CLIENTS="ClientA,ClientB,ClientC" CAMUNDA_SECURITY_INITIALIZATION_GROUPS_0_MAPPING_RULES="RuleA,RuleB,RuleC" CAMUNDA_SECURITY_INITIALIZATION_GROUPS_0_USERS="UserA,UserB,UserC" ``` ```yaml orchestration: extraConfiguration: - file: identity-as-code.yaml content: | camunda: security: initialization: groups: - groupId: test-group name: Test Group description: A cool test group! clients: - ClientA - ClientB - ClientC mappingRules: - RuleA - RuleB - RuleC users: - UserA - UserB - UserC ``` ## Configure mapping rules ```bash CAMUNDA_SECURITY_INITIALIZATION_MAPPINGRULES_0_CLAIMNAME=isAllowedToDoStuff CAMUNDA_SECURITY_INITIALIZATION_MAPPINGRULES_0_CLAIMVALUE=true CAMUNDA_SECURITY_INITIALIZATION_MAPPINGRULES_0_MAPPINGRULEID=my-mapping-rule ``` ```yaml orchestration: extraConfiguration: - file: identity-as-code.yaml content: | camunda: security: initialization: mappingRules: - claimName: isAllowedToDoStuff claimValue: "true" mappingRuleId: my-mapping-rule ``` ## Configure Roles ```bash CAMUNDA_SECURITY_INITIALIZATION_ROLES_0_ROLE_ID=test-role CAMUNDA_SECURITY_INITIALIZATION_ROLES_0_NAME="Test Role" CAMUNDA_SECURITY_INITIALIZATION_ROLES_0_DESCRIPTION="A cool test role!" CAMUNDA_SECURITY_INITIALIZATION_ROLES_0_CLIENTS="client1,client2" CAMUNDA_SECURITY_INITIALIZATION_ROLES_0_GROUPS="group1,group2" CAMUNDA_SECURITY_INITIALIZATION_ROLES_0_MAPPING_RULES="m1,m2" CAMUNDA_SECURITY_INITIALIZATION_ROLES_0_USERS="UserA,UserB,UserC" ``` ```yaml orchestration: extraConfiguration: - file: identity-as-code.yaml content: | camunda: security: initialization: roles: - roleId: test-role name: Test Role description: A cool test role! clients: - client1 - client2 groups: - group1 - group2 mappingRules: - m1 - m2 users: - UserA - UserB - UserC ``` ## Configure tenants ```bash CAMUNDA_SECURITY_INITIALIZATION_TENANTS_0_TENANT_ID=tenantId CAMUNDA_SECURITY_INITIALIZATION_TENANTS_0_NAME="test tenant" CAMUNDA_SECURITY_INITIALIZATION_TENANTS_0_DESCRIPTION="test tenant description" CAMUNDA_SECURITY_INITIALIZATION_TENANTS_0_CLIENTS='R1,R2,R3,R4' CAMUNDA_SECURITY_INITIALIZATION_TENANTS_0_GROUPS='R1,R2,R3,R4' CAMUNDA_SECURITY_INITIALIZATION_TENANTS_0_MAPPING_RULES='R1,R2,R3,R4' CAMUNDA_SECURITY_INITIALIZATION_TENANTS_0_ROLES='R1,R2,R3,R4' CAMUNDA_SECURITY_INITIALIZATION_TENANTS_0_USERS='UserA,UserB,UserC' ``` ```yaml orchestration: extraConfiguration: - file: identity-as-code.yaml content: | camunda: security: initialization: tenants: - tenantId: tenantId name: test tenant description: test tenant description clients: - R1 - R2 - R3 - R4 groups: - R1 - R2 - R3 - R4 mappingRules: - R1 - R2 - R3 - R4 roles: - R1 - R2 - R3 - R4 users: - UserA - UserB - UserC ``` ## Configure users When configuring users, never hardcode the password. Resolve it from a vault instead. ```bash CAMUNDA_SECURITY_INITIALIZATION_USERS_0_EMAIL=john.doe@example.com CAMUNDA_SECURITY_INITIALIZATION_USERS_0_NAME="john doe" CAMUNDA_SECURITY_INITIALIZATION_USERS_0_PASSWORD=***** CAMUNDA_SECURITY_INITIALIZATION_USERS_0_USERNAME=john.doe ``` ```yaml orchestration: extraConfiguration: - file: identity-as-code.yaml content: | camunda: security: initialization: users: - email: john.doe@example.com name: John Doe password: "*****" username: john.doe ``` --- ## Property changes in Camunda 8.9 Changes to component configuration properties introduced in Camunda 8.9. ## About unified configuration property changes In Camunda 8.9, all remaining [unified configuration property changes](/versioned_docs/version-8.8/reference/announcements-release-notes/880/whats-new-in-88.md) are complete. :::info To learn more about the property changes introduced in Camunda 8.8, see [property changes in Camunda 8.8](/versioned_docs/version-8.8/self-managed/components/orchestration-cluster/core-settings/configuration/configuration-mapping.md). ::: ### New properties and backwards compatibility Backwards compatibility between new Camunda 8.9 unified properties and existing legacy properties is as follows: | Type | Description | | :--------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Breaking change | New unified property with an existing equivalent legacy property or set of properties.Backwards compatibility is **not** supported.You should move legacy properties marked with breaking change to the new unified set before upgrading to Camunda 8.9.You can keep legacy properties in your configuration file as long as they match the new unified configuration equivalent property. However, this is not recommended as it can lead to misconfiguration. If the values do not match, the application will not start, and an error will be logged. | | Direct mapping | New unified property with a direct mapping to an existing equivalent legacy property or set of properties.Backwards compatibility is supported as follows:If you have defined the new property, it is used.If you have not defined the new property, the legacy property is used. | | New | New unified property without an existing equivalent legacy property. | ### Recommended actions **As part of upgrading to Camunda 8.9, replace any legacy properties shown in the [Camunda 8.9 property changes table](#camunda-89-property-changes) below with the equivalent new unified configuration property.** You can define configuration properties as environment variables using [Spring Boot conventions](https://docs.spring.io/spring-boot/reference/features/external-config.html#features.external-config.typesafe-configuration-properties.relaxed-binding.environment-variables). To define an environment variable, convert the configuration property to uppercase, remove any dashes, and replace any delimiters (.) with \_. For example: | Property | Environment variable | | :----------------------------------------- | :-------------------------------------- | | `camunda.api.grpc.address` | `CAMUNDA_API_GRPC_ADDRESS` | | `camunda.api.grpc.min-keep-alive-interval` | `CAMUNDA_API_GRPC_MINKEEPALIVEINTERVAL` | ### Example In this example, an application uses the following legacy configuration: ``` camunda.database.url=http://prod-db.com:54321 camunda.operate.opensearch.url=http://prod-db.com:54321 camunda.tasklist.opensearch.url=http://prod-db.com:54321 ``` Remove the legacy properties and add the corresponding new property: ``` camunda.data.secondary-storage.opensearch.url=http://prod-db.com:54321 ``` ## Camunda 8.9 property changes The following table shows new unified properties introduced in 8.9 and their equivalent legacy properties. - Use the search box to find a specific property or set of properties matching your search term. - Apply filter badges to filter the table by property change type. - Sort the table alphabetically using the column headers. :::info Learn more about new properties (including default values) in the [property reference](./properties.md). ::: --- ## CSRF protection Cross-Site Request Forgery (CSRF) is a type of malicious exploit where unauthorized commands are transmitted from a user that the web application trusts. In a CSRF attack, an attacker tricks a victim's browser into making unwanted requests to a web application where the victim is authenticated. For a comprehensive understanding of CSRF attacks and prevention methods, refer to the [MDN Web Docs on CSRF](https://developer.mozilla.org/en-US/docs/Glossary/CSRF). Review the configuration details in the [properties documentation](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#camundasecuritycsrf). :::caution Disabling CSRF protection is not recommended for production environments as it leaves your application vulnerable to cross-site request forgery attacks. ::: ## How CSRF protection works in Camunda - **Token generation**: A unique CSRF token is generated and stored in a secure, HTTP-only cookie named `X-CSRF-TOKEN`. - **Token validation**: For state-changing requests (POST, PUT, DELETE, etc.), the server validates that the CSRF token in the request header `X-CSRF-TOKEN` matches the one in the cookie. - **Safe methods**: GET, HEAD, TRACE, and OPTIONS requests are considered safe and don't require CSRF validation. ## Protected vs unprotected paths ### Protected paths (require CSRF token) - `/api/**` – API endpoints (except specifically excluded paths) - `/v1/**`, `/v2/**` – Versioned API endpoints - All state-changing operations (POST, PUT, DELETE, PATCH) ### Unprotected paths (no CSRF token required) - `/actuator/**` – Health and monitoring endpoints - `/v2/license` – Public license endpoint - `/error` – Error handling - Authentication endpoints (`/login`, `/logout`) - Safe HTTP methods (GET, HEAD, OPTIONS, TRACE) ## Security considerations - Always use HTTPS in production to prevent token interception. - Consider additional security headers configured in the security settings. - Regularly review and update the list of unprotected paths. --- ## Licensing(Configuration) --- ## Logging(4) This page explains how to configure and manage logging in the **Camunda 8 Self-Managed Orchestration Cluster**, which includes **Zeebe**, **Operate**, **Tasklist**, and **Admin**. All components use the **Log4j2** framework. You can configure log levels, output formats, appenders, and adjust logging dynamically at runtime. ## Default Log4j2 configuration The default `log4j2.xml` (Zeebe, Operate, Tasklist, Admin) representation: ```xml ``` :::note This is a simplified example. The actual `log4j2.xml` may include additional appenders, different file paths, or slightly different patterns. See the full [logging documentation](/self-managed/components/orchestration-cluster/core-settings/configuration/logging.md#default-logging-configuration) for the official default file and additional settings. ::: ## Environment variables | Purpose | Variable | Component(s) | Example / Notes | | ------------------- | ------------------- | ------------------- | ----------------------------- | | Global log level | `CAMUNDA_LOG_LEVEL` | All | `DEBUG`, `INFO`, `WARN`, etc. | | Zeebe package level | `ZEEBE_LOG_LEVEL` | Zeebe | Overrides global level | | Atomix / clustering | `ATOMIX_LOG_LEVEL` | Atomix / Raft | Default WARN if unset | | Elasticsearch logs | `ES_LOG_LEVEL` | `org.elasticsearch` | | ## JSON logging appenders | Appender | Description | Enable / Variable | | ------------------ | --------------------------------------------------- | ----------------------------------------------------------------- | | Console | Standard text output | `*_LOG_APPENDER=Console` | | Stackdriver (JSON) | JSON output for Google Cloud / Stackdriver | `*_LOG_APPENDER=Stackdriver` | | RollingFile | Writes logs to a rotating file, disabled by default | `CAMUNDA_LOG_FILE_APPENDER_ENABLED=true` + set component variable | ## Pattern layout/format - Default layout shows **time only**, thread name, MDC context, log level, logger name, and message. **Example pattern:** ```perl %d{HH:mm:ss.SSS} [%t] %notEmpty{[%X] }%-5level %logger{36} - %msg%n ``` | Feature | Old pattern | New pattern | | ------------------- | ------------------- | ---------------------------------- | | Timestamp | Full date and time | Time only | | Logger name | Up to 36 characters | Package initials + full class name | | Newline after level | Yes | No | | Tab before logger | Yes | No | ## Changing log level at runtime You can adjust log levels dynamically using Spring Boot Actuator endpoints: ```bash curl 'http://localhost:9600/actuator/loggers/io.camunda' \ -i -X POST \ -H 'Content-Type: application/json' \ -d '{"configuredLevel":"DEBUG"}' ``` Replace `io.camunda` with the logger you want to adjust. ## Sensitive data By default, loggers that may handle sensitive information (e.g., variable values, actor names) are set to **INFO**. To capture more detail (DEBUG or TRACE), explicitly configure those loggers via their logger names or component-specific variables. :::warning Enabling verbose logging may expose sensitive data. Use debug/trace levels only temporarily for troubleshooting. ::: ## Best practices - Always check the inline default configuration before customizing. - Use component-specific `*_LOG_LEVEL` and `*_LOG_APPENDER` variables for fine-grained control. - Enable the RollingFile or Stackdriver appenders only when needed; console is simpler for stdout logs. - Monitor logging setup across environments (dev/test/prod) to avoid surprises caused by log verbosity. --- ## Property reference As a Spring Boot application, the Orchestration Cluster supports standard [Spring configuration](https://docs.spring.io/spring-boot/reference/features/external-config.html) methods. The following configurations apply to all components within the Orchestration Cluster. --- ## Webserver & security ## Setting a custom context path All components support customizing the **context-path** using the default Spring configuration. Example for `application.yml`: ```yaml server.servlet.context-path: / ``` Example for environment variable: ``` SERVER_SERVLET_CONTEXT_PATH=/ ``` The default context-path is `/`. Replace `` with the identifier for the service (for example, `operate`, `tasklist`, or another component). :::note - The same context-path and security configuration patterns apply across most Camunda 8 Self-Managed components. - Component-specific property names and defaults may vary. Check each service’s reference documentation for details. ::: ## Propagation of membership changes to active sessions When a user logs in, the Orchestration Cluster determines their associations at once (membership in roles, groups, tenants; application authorizations) and stores them into the user's active web session. When these associations change (e.g. user is removed from a group; authorizations change), then this is not reflected in this cached state immediately but only until the next refresh interval comes. The default interval is 30 seconds but can be configured via the `camunda.security.authentication.authentication-refresh-interval` property to a higher/lower value if needed considering a trade-off between the extra load for session refresh and the criticality of having sync authentications. The property format is an ISO8601 duration, for example `PT10M` to set it to 10 minutes. For more information on ISO8601 duration format, refer to [ISO8601](https://en.wikipedia.org/wiki/ISO_8601#Durations). --- ## Core settings and features Learn about unified configuration for the Camunda 8 Orchestration Cluster components Operate, Tasklist, Zeebe, and Admin (formerly Orchestration Cluster Identity). ## About Orchestration Cluster configuration With the release of version 8.8, configuration and operational concepts for the Orchestration Cluster are unified. This means: - Many configuration options previously documented separately for each component are now consolidated into a single, centralized configuration file and management approach. - Several conceptual topics—such as security headers, authentication, data retention, and usage metrics—apply across all Orchestration Cluster components rather than to just one. - This shared section provides a single source of truth for these cross-cutting concerns, making it easier to find, understand, and maintain orchestration cluster configuration and concepts. This section includes: - **Unified configuration topics:** Centralized documentation on all cluster-wide configuration settings grouped by theme (e.g., security, logging, monitoring). - **Conceptual documentation:** Explanations of cluster-wide behaviors, features, and best practices relevant across multiple components. - **Migration and legacy information:** Guidance related to upgrading and migrating to version 8.8+, including notes on deprecated features and legacy support. - **Cross-component topics:** Information on shared aspects such as HTTP security headers, authentication mechanisms, data retention policies, and usage metrics. :::caution Camunda 8.9 unified configuration breaking changes Only the first partial set of unified configuration properties is introduced in Camunda 8.8. - All remaining unified property changes will be completed by Camunda 8.9. - This remaining work will result in future breaking changes. For example, the secondary database properties will be unified into a secondary-storage properties section, and new application launch modes (`camunda.mode`) and webapps toggles (`camunda.webapps.*`) are part of the completed unified configuration. ::: --- ## Configuration(Operate) As a Spring Boot application, Operate supports any standard [Spring configuration](https://docs.spring.io/spring-boot/reference/features/external-config.html) method. By default, the configuration for Operate is stored in a YAML file (`application.yml`). All Operate-related settings are prefixed with `camunda.operate`. :::note Configuration properties can be defined as environment variables using [Spring Boot conventions](https://docs.spring.io/spring-boot/reference/features/external-config.html#features.external-config.typesafe-configuration-properties.relaxed-binding.environment-variables). To define an environment variable, convert the configuration property to uppercase, remove any dashes, and replace any delimiters (`.`) with `_`. For example, the property `camunda.operate.elasticsearch.clustername` is represented by the environment variable `CAMUNDA_OPERATE_ELASTICSEARCH_CLUSTERNAME`. ::: The following parts are configurable: ## Licensing See the [core settings documentation](/self-managed/components/orchestration-cluster/core-settings/configuration/licensing.md). ## Webserver and security See the [core settings documentation](/self-managed/components/orchestration-cluster/core-settings/configuration/webserver.md). ## Secondary storage Review the [secondary storage documentation](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#secondary-storage) and [secondary storage configuration](/self-managed/concepts/secondary-storage/configuring-secondary-storage.md). ## Operation executor Operations are user operations, like cancellation of process instance(s) or updating the variable value. Operations are executed in a multi-threaded manner. | Name | Description | Default value | | ---------------------------------------------- | -------------------------------- | ------------- | | camunda.operate.operationExecutor.threadsCount | How many threads should be used. | 3 | ### Snippet from application.yml ```yaml camunda.operate: operationExecutor: threadsCount: 3 ``` ## Monitoring Operate See the [core settings documentation](/self-managed/components/orchestration-cluster/core-settings/concepts/monitoring.md). ## Logging See the [core settings documentation](/self-managed/components/orchestration-cluster/core-settings/configuration/logging.md). ## Backups See the [core settings documentation](/self-managed/components/orchestration-cluster/core-settings/concepts/backups.md). --- ## Operate overview Overview about Operate. --- ## Deployment configuration The Orchestration Cluster REST API supports file and multipart uploads when deploying BPMN diagrams and other resources. By default, the maximum allowed request size is 4MB. You can increase this limit in Self-Managed cluster through configuration of the Zeebe Gateway, Broker, and the REST layer (e.g. Spring Boot + Tomcat). This guide walks you through adjusting these settings. ### Gateway and Broker configuration The `maxMessageSize` default value is 4MB. You can configure this setting in the: - [Gateway config](../orchestration-cluster/zeebe/configuration/gateway.md#zeebegatewaynetwork) - [Broker config](/self-managed/components/orchestration-cluster/zeebe/configuration/broker.md#zeebebrokernetwork) If you increase this value, you must also adjust the configuration for the deployment REST endpoint to match. ### REST API server configuration If you're using the REST API and submitting files via multipart upload (e.g., using `POST /v2/deployments`), make sure your application is configured to accept the updated request sizes. If you increase the `maxMessageSize` to 10MB, increase these property values to 10MB as well. For Spring Boot applications: ```properties spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-request-size=10MB ``` ### Tomcat multipart settings Tomcat, which underlies many Java web applications, also applies multipart limits. If you're uploading multiple files as part of a multipart request, note that Tomcat limits the number of parts per request using the `server.tomcat.max-part-count` property. By default, this is set to 50 in the orchestration API. You can increase the limit to allow more files by setting the property in your configuration: ```properties server.tomcat.max-part-count=100 ``` or using an environment variable: ```properties SERVER_TOMCAT_MAX_PART_COUNT=100 ``` The default is `50` in the orchestration API layer. #### Max parameter count Tomcat also enforces a separate limit on the total number of request parameters via the `server.tomcat.max-parameter-count` property. Since each file upload typically counts as both a part and a parameter, the lower of these two limits will determine how many files can be uploaded. ### Reference For more information on defaults and options, refer to the [Apache Tomcat documentation](https://tomcat.apache.org/). --- ## Orchestration Cluster Install and configure Orchestration Cluster components in your Self-Managed environment. ## Installation and configuration The Orchestration Cluster is the core component of Camunda 8, powering the automation and orchestration of processes. The Orchestration Cluster includes: - Zeebe as the workflow engine - Operate for monitoring and troubleshooting process instances running in Zeebe. - Tasklist for interacting with user tasks (assigning, completing, and so on). - Admin (formerly Orchestration Cluster Identity) for managing the integrated authentication and authorization. - APIs for interacting with the Orchestration Cluster programmatically. In this section, you will learn how to install, and configure these components in your Self-Managed environment. --- ## Tasklist overview Overview about Tasklist. --- ## Configuration(Tasklist) As a Spring Boot application, Tasklist supports any standard [Spring configuration](https://docs.spring.io/spring-boot/reference/features/external-config.html) method. By default, the configuration for Tasklist is stored in a YAML file `application.yml`. All Tasklist-related settings are prefixed with `camunda.tasklist`. :::note Configuration properties can be defined as environment variables using [Spring Boot conventions](https://docs.spring.io/spring-boot/reference/features/external-config.html#features.external-config.typesafe-configuration-properties.relaxed-binding.environment-variables). To define an environment variable, convert the configuration property to uppercase, remove any dashes, and replace any delimiters (`.`) with `_`. For example, the property `server.servlet.context-path` is represented by the environment variable `SERVER_SERVLET_CONTEXT_PATH`. ::: The following components are configurable: ## Licensing See the [core settings documentation](/self-managed/components/orchestration-cluster/core-settings/configuration/licensing.md). ## Webserver See the [core settings documentation](/self-managed/components/orchestration-cluster/core-settings/configuration/webserver.md). ## Secondary storage Review the [secondary storage documentation](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#secondary-storage) and [secondary storage configuration](/self-managed/concepts/secondary-storage/configuring-secondary-storage.md). ## Intra-cluster secure connection You can enable intra-cluster TLS-secured connections between Tasklist and Zeebe by applying the following configuration properties: | Name | Description | Example value | | :---------------------------------------------------- | :----------------------------------------------------------------------------------------------------------- | :---------------------------------- | | `zeebe.gateway.cluster.initialContactPoints` | Zeebe Gateway initial contact points. | `[gateway-0:26502,gateway-1:26502]` | | `zeebe.gateway.cluster.security.enabled` | Enables secure connections via Transport Layer Security (TLS). | `true` | | `zeebe.gateway.cluster.security.certificateChainPath` | Path to the certificate used by Zeebe. Required if the certificate isn't registered in the operating system. | `/path/to/cert.pem` | | `zeebe.gateway.cluster.security.privateKeyPath` | Path to the certificate's private key used by Zeebe. | `/path/to/private.key` | | `zeebe.gateway.cluster.advertisedHost` | Advertised hostname in the cluster. | `tasklist` | | `zeebe.gateway.cluster.memberId` | Member ID for the cluster. | `tasklist` | For extended configuration and guidelines, refer to [Secure cluster communication](../zeebe/security/secure-cluster-communication.md) and [Gateway configuration](../zeebe/configuration/gateway.md). ## Monitoring and health probes See the [core settings documentation](/self-managed/components/orchestration-cluster/core-settings/concepts/monitoring.md). ## Logging See the [core settings documentation](/self-managed/components/orchestration-cluster/core-settings/configuration/logging.md). ## Allow non-self assignment :::danger The `allow-non-self-assignment` flag applied only to the removed Tasklist V1 API. In Camunda 8.10 and later, Tasklist uses only the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md), so this flag is not part of the current configuration model. Use [authorization-based access control](/components/concepts/access-control/authorizations.md#available-resources) to manage who can assign or update user tasks. ::: ## Tasklist API mode configuration Starting with Camunda 8.10, Tasklist no longer supports switching between V1 and V2 modes. Tasklist always uses the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md), and the legacy `CAMUNDA_TASKLIST_V2_MODE_ENABLED` / `camunda.tasklist.V2ModeEnabled` settings are no longer part of the current configuration model. ## Backups See the [core settings documentation](/self-managed/components/orchestration-cluster/core-settings/concepts/backups.md). --- ## Custom styling You can customize the Tasklist user interface (UI) to visually align it with your organization's brand identity. You can adjust the appearance of various UI elements, such as backgrounds, layers, controls, buttons, and text. The Tasklist UI uses the [Carbon Design System](https://carbondesignsystem.com/), the comprehensive [open-source](https://github.com/carbon-design-system/carbon) design system created by IBM to ensure consistency, efficiency, and scalability across projects, particularly developers who need to create or maintain UI components. ## Customize the Tasklist user interface To customize the user interface, you can override specific Carbon Design System [design tokens](https://carbondesignsystem.com/elements/color/tokens) with your own CSS values. These tokens control various aspects of the UI's appearance. For example, you can override the `--cds-background` token to change the Tasklist UI background color. You can override these tokens in the `custom.css` file located in the distribution archive or within the Docker image at `/usr/local/tasklist/config/custom.css`. A typical workflow to customize the Tasklist UI is as follows: 1. **Identify design tokens**: Review your own visual identity guidelines and identify the design tokens you want to update. You will modify these tokens in the `custom.css` file. 2. **Override the tokens in custom.css**: Add or modify token values in the `custom.css` file. The syntax for styling is plain CSS. For example, to change the background color use the following syntax, replacing the `--cds-background` token hex values with your own color: ```yaml :root[data-carbon-theme='g10']{ /* Light theme customization */ --cds-background: #FFFF00; } :root[data-carbon-theme='g100']{ /* Dark theme customization */ --cds-background: #008000; } ``` 3. **Test your custom styles**: Test your custom styles in both light and dark modes to verify that they are applied correctly across all Tasklist UI components. 4. **Validate accessibility and visual contrast**: Check that your custom styles maintain good visual contrast between elements. For example, verify that text is easily readable against backgrounds, buttons are distinguishable, and important elements such as links and icons stand out properly. Contrast is especially important for accessibility and readability in both light and dark modes. 5. **Iterate based on results**: If necessary, refine your customizations based on the results of your testing. Adjust values in the `custom.css` file to ensure a consistent look and feel throughout the Tasklist UI. :::note If you do not add any custom CSS configuration in the `custom.css` file, the Tasklist UI defaults to its original visual identity. ::: ## Common design tokens You can override the following commonly used design tokens to customize the Tasklist UI. | Design token | Description | | :-------------------------------- | :----------------------------------------------------------- | | `--cds-background` | Default background color of the Tasklist UI. | | `--cds-background-brand` | Feature background color. | | `--cds-layer-01` | Color for primary layer surfaces like containers and cards. | | `--cds-layer-selected-01` | Selected color for `layer-01`. | | `--cds-text-primary` | Primary color for text elements. | | `--cds-text-secondary` | Secondary color for less prominent text and input labels. | | `--cds-link-primary` | Color for primary links. | | `--cds-link-primary-hover` | Color for primary links when hovered. | | `--cds-link-secondary` | Color for secondary links. | | `--cds-link-inverse` | Color for links against dark backgrounds. | | `--cds-icon-primary` | Primary color for icons. | | `--cds-icon-secondary` | Secondary color for icons. | | `--cds-icon-interactive` | Color for interactive icons. | | `--cds-button-primary` | Primary color for buttons. | | `--cds-button-primary-hover` | Hover state color for primary buttons. | | `--cds-button-primary-active` | Active state color for primary buttons. | | `--cds-button-secondary` | Secondary color for buttons. | | `--cds-button-secondary-hover` | Hover state color for secondary buttons. | | `--cds-button-secondary-active` | Active state color for secondary buttons. | | `--cds-button-tertiary` | Tertiary color for buttons. | | `--cds-button-tertiary-hover` | Hover state color for tertiary buttons. | | `--cds-button-tertiary-active` | Active state color for tertiary buttons. | | `--cds-button-disabled` | Color for disabled buttons. | | `--cds-field-01` | Color for default input fields. | | `--cds-focus` | Color for elements in focus, such as borders and underlines. | | `--cds-border-interactive` | Color for selected and active borders. | | `--cds-border-subtle-00` | Subtle borders paired with `background`. | | `--cds-border-subtle-01` | Subtle borders paired with `$layer-01` | | `--cds-border-subtle-selected-01` | Selected color for `$border-subtle-01` | --- ## User task access restrictions were removed(Tasklist) User task access restrictions were removed in Camunda 8.10 together with Tasklist V1. For the release-level summary of this removal, see the [8.10 release announcement](/reference/announcements-release-notes/8100/8100-announcements.md#removal-of-legacy-apis-tasklist-v1-dependent-features-and-zeebe-process-test). Use [user task authorization](/components/tasklist/user-task-authorization.md) and [authorization-based access control](/components/concepts/access-control/authorizations.md) for current Tasklist access control. If you are migrating from an older installation, review [Tasklist API changes](/components/tasklist/api-versions.md) to understand which legacy V1 features were removed. --- ## Broker configuration A complete broker configuration template is available in the [Zeebe repo](https://github.com/camunda/camunda/blob/main/dist/src/main/config/broker.yaml.template). ## Conventions Take the following conventions into consideration when working with the broker configuration. ### Byte sizes Buffers and data values referencing sizing must be specified as strings and follow the following format: "10U" where U (unit) must be replaced with KB = Kilobytes, MB = Megabytes or GB = Gigabytes. If unit is omitted then the default unit is simply bytes. Example: `sendBufferSize = "16MB"` (creates a buffer of 16 Megabytes) ### Time units Timeouts, intervals, and the likes, must be specified either in the standard ISO-8601 format used by java.time.Duration, or as strings with the following format: "VU", where: - V is a numerical value (e.g. 1, 5, 10, etc.) - U is the unit, one of: ms = Millis, s = Seconds, m = Minutes, or h = Hours ### Paths Relative paths are resolved relative to the installation directory of the broker. ## Configuration We provide tables with environment variables, application properties, a description, and corresponding default values in the following sections. We also describe a few use cases for each type of configuration. Configuration names are noted as the **header** of each documented section, while the **field** values represent properties to set the configuration. :::note The Zeebe Broker is a Spring Boot application. As such, [many common Spring Boot properties will work out of the box](https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html). Additionally, its REST server is a Spring Boot server (powered by Spring MVC), and can be configured using the standard `server.*` properties. Its management server (for example, where actuator endpoints live) is configured as a child application context, and is also a Spring MVC server. It can be configured via `management.server.*` properties. Finally, the REST server is only serving requests _if, and only if, the embedded gateway is enabled via_ `zeebe.broker.gateway.enable: true`. ::: ### server The `server` configuration allows you to configure the main REST server. Below are a few common ones, but you can find a more exhaustive list [in the official Spring documentation](https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#appendix.application-properties.server). | Field | Description | Example value | | ----- | ------------------------------------------------------------------------------------------------------------------------- | ------------- | | host | Sets the host the REST server binds to. This setting can also be overridden using the environment variable `SERVER_HOST`. | 0.0.0.0 | | port | Sets the port the REST server binds to. This setting can also be overridden using the environment variable `SERVER_PORT`. | 8080 | #### server.compression | Field | Description | Example value | | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | enabled | If true, enables compression of responses for the Orchestration Cluster REST API. This setting can also be overridden using the environment variable `SERVER_COMPRESION_ENABLED`. | false | #### server.ssl Allows you to configure the SSL security for the REST server. | Field | Description | Example value | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | enabled | If true, enables TLS for the Orchestration Cluster REST API. This setting can also be overridden using the environment variable `SERVER_SSL_ENABLED`. | false | | certificate | The path to a PEM encoded certificate. This setting can also be overridden using the environment variable `SERVER_SSL_CERTIFICATE`. | | | certificate-private-key | The path to a PKCS1 or PKCS8 private key for the configured certificate. This setting can also be overridden using the environment variable `SERVER_SSL_CERTIFICATEPRIVATEKEY`. | | #### YAML snippet ```yaml server: host: 0.0.0.0 port: 8080 compression: enabled: true ssl: enabled: true certificate: /path/to/my/cert.pem certificate-private-key: /path/to/my/private.key ``` ### server.servlet | Field | Description | Example value | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- | | context-path | The context path prefix for all REST API requests. For example, if you configure `/zeebe`, then the client's REST address would be `http://localhost:8080/zeebe`. This setting can also be overridden using the environment variable `SERVER_SERVLET_CONTEXTPATH`. | `/` | #### YAML snippet ```yaml server.servlet: context-path: / ``` ### management.server The `management.server` configuration allows you to configure the management server. | Field | Description | Example value | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | host | Sets the host the management server binds to. This setting can also be overridden using the environment variable `MANAGEMENT_SERVER_HOST`. | 0.0.0.0 | | port | Sets the port the management server binds to. This setting can also be overridden using the environment variable `MANAGEMENT_SERVER_PORT`. | 9600 | | base-path | The context path prefix for all management endpoints. For example, if you configure `/zeebe`, your actuator endpoints will be at `http://localhost:9600/zeebe/actuator/configprops`. This setting can also be overridden using the environment variable `MANAGEMENT_SERVER_BASEPATH`. | `/` | #### YAML snippet ```yaml management.server: host: 0.0.0.0 port: 9600 base-path: / ``` ### zeebe.broker.gateway To configure the embedded gateway, see [Gateway config docs](/self-managed/components/orchestration-cluster/zeebe/configuration/gateway.md). | Field | Description | Example value | | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | enable | Enable the embedded gateway to start on broker startup. This setting can also be overridden using the environment variable `ZEEBE_BROKER_GATEWAY_ENABLE`. | false | #### YAML snippet ```yaml broker: gateway: enable: false ``` ### zeebe.broker.network This section contains the network configuration. Particularly, it allows to configure the hosts and ports the broker should bind to. The broker exposes two sockets: 1. command: the socket which is used for gateway-to-broker communication 2. internal: the socket which is used for broker-to-broker communication | Field | Description | Example Value | | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | host | Controls the default host the broker should bind to. Can be overwritten on a per binding basis for client, management and replication. This setting can also be overridden using the environment variable `ZEEBE_BROKER_NETWORK_HOST`. | 0.0.0.0 | | advertisedHost | Controls the advertised host (the contact point advertised to other brokers); if omitted defaults to the host. This is particularly useful if your broker stands behind a proxy. This setting can also be overridden using the environment variable `ZEEBE_BROKER_NETWORK_ADVERTISEDHOST`. | 0.0.0.0 | | portOffset | If a port offset is set it will be added to all ports specified in the config or the default values. This is a shortcut to not always specifying every port. The offset will be added to the second last position of the port, as Zeebe requires multiple ports. As example a portOffset of 5 will increment all ports by 50, i.e. 26500 will become 26550 and so on. This setting can also be overridden using the environment variable `ZEEBE_BROKER_NETWORK_PORTOFFSET`. | 0 | | maxMessageSize | Sets the maximum size of the incoming and outgoing messages (i.e. commands and events). This setting can also be overridden using the environment variable `ZEEBE_BROKER_NETWORK_MAXMESSAGESIZE`. | 4MB | | socketReceiveBuffer | Sets the size of the socket's receive buffer for the broker. If omitted defaults to 1MB. This setting can also be overridden using the environment variable `ZEEBE_BROKER_NETWORK_SOCKETRECEIVEBUFFER`. | 4MB | | socketSendBuffer | Sets the size of the socket's send buffer for the broker. If omitted defaults to 1MB. This setting can also be overridden using the environment variable `ZEEBE_BROKER_NETWORK_SOCKETSENDBUFFER`. | 4MB | #### YAML snippet ```yaml network: host: 0.0.0.0 advertisedHost: 0.0.0.0 portOffset: 0 maxMessageSize: 4MB socketReceiveBuffer: 4MB socketSendBuffer: 4MB ``` ### zeebe.broker.network.security | Field | Description | Example Value | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- | | enabled | Enables TLS authentication between this gateway and other nodes in the cluster. This setting can also be overridden using the environment variable `ZEEBE_BROKER_NETWORK_SECURITY_ENABLED`. | false | | certificateChainPath | Sets the path to the certificate chain file. This setting can also be overridden using the environment variable `ZEEBE_BROKER_NETWORK_SECURITY_CERTIFICATECHAINPATH`. | | | privateKeyPath | Sets the path to the private key file location. This setting can also be overridden using the environment variable `ZEEBE_BROKER_NETWORK_SECURITY_PRIVATEKEYPATH`. | | | keyStore | Configures the keystore file containing both the certificate chain and the private key; currently only supports PKCS12 format. | | | keyStore.filePath | The path for keystore file; This setting can also be overridden using the environment variable `ZEEBE_BROKER_NETWORK_SECURITY_KEYSTORE_FILEPATH`. | /path/key.pem | | keyStore.password | Sets the password for the keystore file, if not set it is assumed there is no password; This setting can also be overridden using the environment variable `ZEEBE_BROKER_NETWORK_SECURITY_KEYSTORE_PASSWORD` | changeme | #### YAML snippet ```yaml security: enabled: false certificateChainPath: null privateKeyPath: null keyStore: filePath: null password: null ``` ### zeebe.broker.network.commandApi | Field | Description | Example Value | | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | host | Overrides the host used for gateway-to-broker communication. This setting can also be overridden using the environment variable `ZEEBE_BROKER_NETWORK_COMMANDAPI_HOST`. | 0.0.0.0 | | port | Sets the port used for gateway-to-broker communication. This setting can also be overridden using the environment variable `ZEEBE_BROKER_NETWORK_COMMANDAPI_PORT`. | 26501 | | advertisedHost | Controls the advertised host; if omitted defaults to the host. This is particularly useful if your broker stands behind a proxy. This setting can also be overridden using the environment variable `ZEEBE_BROKER_NETWORK_COMMANDAPI_ADVERTISEDHOST`. | 0.0.0.0 | | advertisedPort | Controls the advertised port; if omitted defaults to the port. This is particularly useful if your broker stands behind a proxy. This setting can also be overridden using the environment variable `ZEEBE_BROKER_NETWORK_COMMANDAPI_ADVERTISEDPORT`. | 25601 | #### YAML snippet ```yaml commandApi: host: 0.0.0.0 port: 26501 advertisedHost: 0.0.0.0 advertisedPort: 25601 ``` ### zeebe.broker.network.internalApi | Field | Description | Example Value | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- | | host | Overrides the host used for internal broker-to-broker communication. This setting can also be overridden using the environment variable `ZEEBE_BROKER_NETWORK_INTERNALAPI_HOST`. | 0.0.0.0 | | port | Sets the port used for internal broker-to-broker communication. This setting can also be overridden using the environment variable `ZEEBE_BROKER_NETWORK_INTERNALAPI_PORT`. | 26502 | | advertisedHost | Controls the advertised host; if omitted defaults to the host. This is particularly useful if your broker stands behind a proxy. This setting can also be overridden using the environment variable `ZEEBE_BROKER_NETWORK_INTERNALAPI_ADVERTISEDHOST`. | 0.0.0.0 | | advertisedPort | Controls the advertised port; if omitted defaults to the port. This is particularly useful if your broker stands behind a proxy. This setting can also be overridden using the environment variable `ZEEBE_BROKER_NETWORK_INTERNALAPI_ADVERTISEDPORT`. | 25602 | #### YAML snippet ```yaml internalApi: host: 0.0.0.0 port: 26502 advertisedHost: 0.0.0.0 advertisedPort: 25602 ``` ### zeebe.broker.data This section allows to configure Zeebe's data storage. Data is stored in "partition folders". A partition folder has the following structure: ``` partitions └── 1 (root partition folder) ├── 1.log ├── 2.log └── snapshots └── └── xx.sst └── runtime └── yy.sst ``` | Field | Description | Example Value | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- | | directory | Specify the directory in which data is stored. This setting can also be overridden using the environment variable ZEEBE_BROKER_DATA_DIRECTORY. | data | | runtimeDirectory | Specify the directory in which runtime is stored. By default runtime is stored in `directory` for data. If runtimeDirectory is configured, then the configured directory will be used. It will have a subdirectory for each partition to store its runtime. There is no need to store runtime in a persistent storage. This configuration allows to split runtime to another disk to optimize for performance and disk usage. Note: If runtime is another disk than the data directory, files need to be copied to data directory while taking snapshot. This may impact disk i/o or performance during snapshotting. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_RUNTIMEDIRECTORY`. | null | | logSegmentSize | The size of data log segment files. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_LOGSEGMENTSIZE`. | 128MB | | snapshotPeriod | How often we take snapshots of streams (time unit). This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_SNAPSHOTPERIOD`. | 5m | #### YAML snippet ```yaml data: directory: data runtimeDirectory: null logSegmentSize: 128MB snapshotPeriod: 5m ``` ### zeebe.broker.data.disk | Field | Description | Example Value | | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | enableMonitoring | Configure disk monitoring to prevent getting into a non-recoverable state due to out of disk space. When monitoring is enabled, the broker rejects commands and pause replication when the required freeSpace is not available. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_DISK_ENABLEMONITORING` | true | | monitoringInterval | Sets the interval at which the disk usage is monitored. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_DISK_MONITORINGINTERVAL` | 1s | #### YAML snippet ```yaml disk: enableMonitoring: true monitoringInterval: 1s ``` ### zeebe.broker.data.disk.freeSpace | Field | Description | Example Value | | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | processing | When the free space available is less than this value, this broker rejects all client commands and pause processing. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_DISK_FREESPACE_PROCESSING` | 2GB | | replication | When the free space available is less than this value, broker stops receiving replicated events. This value must be less than freeSpace.processing. It is recommended to configure free space large enough for at least one log segment and one snapshot. This is because a partition needs enough space to take a new snapshot to be able to compact the log segments to make disk space available again. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_DISK_FREESPACE_REPLICATION` | 1GB | #### YAML snippet ```yaml disk: freeSpace: processing: 2GB replication: 1GB ``` ### zeebe.broker.data.backup Configure backup store. :::note Use the same configuration on all brokers of this cluster. ::: :::caution Backups created with one store are not available or restorable from another store. This is especially relevant if you were using GCS through the S3 compatibility mode and want to switch to the new built-in support for GCS now. Even when the underlying storage bucket is the same, backups from one are not compatible with the other. ::: | Field | Description | Example Value | | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | store | Set the backup store type. Supported values are [NONE, S3, GCS, AZURE, FILESYSTEM]. Default value is NONE. When NONE, no backup store is configured and no backup will be taken. Use S3 to use any S3 compatible storage, including, but not limited to, Amazon S3. Use GCS to use [Google Cloud Storage](https://cloud.google.com/storage/). Use AZURE to employ [Azure Cloud Storage](https://learn.microsoft.com/en-us/azure/storage/common/storage-introduction). Use FILESYSTEM to store backups directly via the filesystem to a particular folder. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_STORE`. | NONE | #### YAML snippet ```yaml backup: store: NONE ``` ### zeebe.broker.data.backup.s3 Configure the following if store is set to s3. :::note You can use any S3 compatible storage, including, but not limited to, Amazon S3. ::: :::note Backup encryption Zeebe does not support backup encryption natively, but it _can_ use encrypted S3 buckets. For AWS S3, this means [enabling default bucket encryption](https://docs.aws.amazon.com/AmazonS3/latest/userguide/default-bucket-encryption.html). Using default bucket encryption gives you control over the encryption keys and algorithms while being completely transparent with Zeebe. Combined with TLS between Zeebe and the S3 API, backups are fully encrypted in transit and at rest. Other S3 compatible services might have similar features that should work as well. ::: :::note Backup compression Backups can be large depending on your usage of Zeebe. To reduce S3 storage costs and upload times, you can enable backup compression. Zeebe compresses backup data immediately before uploading to S3 and buffers the compressed files in a temporary directory. Compression and buffering of compressed files can have a negative effect if Zeebe is heavily resource constrained. You can enable compression by specifying a compression algorithm to use. We recommend using [zstd](https://github.com/facebook/zstd) as it provides a good trade-off between compression ratio and resource usage. More compression algorithms are available; check [commons-compress](https://commons.apache.org/proper/commons-compress/) for a full list. ::: | Field | Description | Example Value | | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | bucketName | Name of the bucket where the backup will be stored. The bucket must be already created. The bucket must not be shared with other zeebe clusters. bucketName must not be empty. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_S3_BUCKETNAME`. | | | endpoint | Configure URL endpoint for the store. If no endpoint is provided, it will be determined based on the configured region. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_S3_ENDPOINT`. | | | region | Configure region. If no region is provided it will be determined as documented by your S3 compatible storage provider. If you use Amazon S3, it will be determined as [documented](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/region-selection.html#automatically-determine-the-aws-region-from-the-environment). This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_S3_REGION` | | | accessKey | Configure access credentials. If either accessKey or secretKey is not provided, it will be determined as [documented](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/credentials.html#credentials-chain), not based on your S3 compatible storage provider. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_S3_ACCESSKEY`. | | | secretKey | This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_S3_SECRETKEY`. | | | apiCallTimeout | Configure a maximum duration for all S3 client API calls. Lower values will ensure that failed or slow API calls don't block other backups but may increase the risk that backups can't be stored if uploading parts of the backup takes longer than the configured timeout. Amazon S3 users can refer [here](https://github.com/aws/aws-sdk-java-v2/blob/master/docs/BestPractices.md#utilize-timeout-configurations). This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_S3_APICALLTIMEOUT`. | PT180S | | forcePathStyleAccess | When enabled, forces the s3 client to use path-style access. By default, the client will automatically choose between path-style and virtual-hosted-style. Should only be enabled if the s3 compatible storage cannot support virtual-hosted-style. Refer to your S3 compatible storage provider or the [Amazon S3 docs](https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-bucket-intro.html) for more information. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_S3_FORCEPATHSTYLEACCESS`. | false | | compression | When set to an algorithm such as 'zstd', enables compression of backup contents. When not set or set to 'none', backup content is not compressed. Enabling compression reduces the required storage space for backups in S3 but also increases the impact on CPU and disk utilization while taking a backup. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_S3_COMPRESSION` | none | | basePath | When set, all objects in the bucket will use this prefix. Must be non-empty and not start or end with '/'. Useful for using the same bucket for multiple Zeebe clusters. In this case, basePath must be unique. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_S3_BASEPATH`. | | maxConcurrentConnections | Maximum number of connections allowed in a connection pool. This is used to restrict the maximum number of concurrent uploads as to avoid connection timeouts when uploading backups with large/many files. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_S3_MAXCONCURRENTCONNECTIONS`. | | connectionAquisitionTimeout | Timeout for acquiring an already-established connection from a connection pool to a remote service. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_S3_CONNECTIONAQUISITIONTIMEOUT`. | | supportLegacyMd5 | Enables the AWS provided `LegacyMd5Plugin` to extend backwards compatibility of the client. Useful when using an S3-compatible object storage as your backup store that is not up to date with latest AWS SDK guidelines. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_S3_SUPPORTLEGACYMD5`. | false | #### YAML snippet ```yaml backup: store: s3 s3: bucketName: null endpoint: null region: null secretKey: null apiCallTimeout: PT180S forcePathStyleAccess: false compression: none basePath: null maxConcurrentConnections: connectionAcquisitionTimeout: supportLegacyMd5: false ``` ### zeebe.broker.data.backup.gcs Configure the following if store is set to GCS. :::note The GCS backup strategy utilizes the [Google Cloud Storage REST API](https://cloud.google.com/storage/docs/request-endpoints). ::: :::note Backup encryption There are multiple [data encryption options](https://cloud.google.com/storage/docs/encryption), some of which are supported by Zeebe: - [Default server-side encryption](https://cloud.google.com/storage/docs/encryption/default-keys) is fully supported. This is enabled by default for all GCS buckets. - [Customer-managed encryption keys](https://cloud.google.com/storage/docs/encryption/customer-managed-keys) are supported if they are [set as the default key](https://cloud.google.com/storage/docs/encryption/using-customer-managed-keys#set-default-key) for your bucket. - [Customer-supplied encryption keys](https://cloud.google.com/storage/docs/encryption/customer-supplied-keys) are not supported. - [Client-side encryption keys](https://cloud.google.com/storage/docs/encryption/client-side-keys) are not supported. ::: | Field | Description | Example Value | | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | bucketName | Name of the bucket where the backup will be stored. The bucket **must already exist**. The bucket must not be shared with other Zeebe clusters unless basePath is also set. Zeebe will check at startup that the specified bucket exists and can be accessed, and log at WARN level if the bucket does not exist. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_GCS_BUCKETNAME`. | | | basePath | When set, all blobs in the bucket will use this prefix. Useful for using the same bucket for multiple Zeebe clusters. In this case, basePath must be unique. Should not start or end with '/' character. Must be non-empty and not consist of only '/' characters. See [Google documentation on naming](https://cloud.google.com/storage/docs/objects#naming). This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_GCS_BASEPATH`. | | | host | When set, this overrides the host that the GCS client connects to. By default, this is not set because the client can automatically discover the correct host to connect to. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_GCS_HOST` | | | auth | Configures which authentication method is used for connecting to GCS. Can be either 'auto' or 'none'. Choosing 'auto' means that the GCS client uses application default credentials which automatically discovers appropriate credentials from the runtime environment: https://cloud.google.com/docs/authentication/application-default-credentials. Choosing 'none' means that no authentication is attempted which is only applicable for testing with emulated GCS. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_GCS_AUTH`. | auto | #### YAML snippet ```yaml backup: store: gcs gcs: bucketName: null basePath: null host: null auth: auto ``` ### zeebe.broker.data.backup.azure Configure the following if store is set to Azure. :::note Backup encryption - [Default server-side encryption](https://learn.microsoft.com/en-us/azure/storage/common/storage-service-encryption?toc=%2Fazure%2Fstorage%2Fblobs%2Ftoc.json&bc=%2Fazure%2Fstorage%2Fblobs%2Fbreadcrumb%2Ftoc.json#about-azure-storage-service-side-encryption) is fully supported, is enabled by default, and can't be switched off. - [Customer-managed encryption keys](https://learn.microsoft.com/en-us/azure/storage/common/customer-managed-keys-overview?toc=/azure/storage/blobs/toc.json&bc=/azure/storage/blobs/breadcrumb/toc.json) are supported if they are configured in the Azure store. - [Customer-provided encryption keys](https://learn.microsoft.com/en-us/azure/storage/blobs/encryption-customer-provided-keys) are not supported. - [Client-side encryption keys](https://learn.microsoft.com/en-us/azure/storage/blobs/client-side-encryption?tabs=dotnet) are not supported. ::: | Field | Description | Example Value | | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | endpoint | Name of the endpoint where the backup will be stored. Sets the blob service endpoint. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_AZURE_ENDPOINT`. | | | accountName | Account name used to connect to the service. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_AZURE_ACCOUNTNAME`. | | | accountKey | Account key used to connect to the service. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_AZURE_ACCOUNTKEY`. | | | connectionString | The [connection string](https://learn.microsoft.com/en-us/azure/storage/common/storage-configure-connection-string?toc=/azure/storage/blobs/toc.json&bc=/azure/storage/blobs/breadcrumb/toc.json) to connect to the service. If this is defined, it will override the account name, account key, and endpoint. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_AZURE_CONNECTIONSTRING`. | | | basePath | Used to define the container name where the blobs will be saved. This value must not be empty. When `basePath` is set, Zeebe will only create and access objects under this path. This can be any string that is a valid [container name](https://learn.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata#container-names), for example the name of your cluster. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_AZURE_BASEPATH`. | | | createContainer | Defines if the container is created initially or if an existing one should be used (if set to `true` and the container already exists, this is not recreated). This configuration is `true` by default and should be generally not used unless some authentication key is being used that does not have container level permissions. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_AZURE_CREATECONTAINER`. | `true` | | sasToken.type | This setting defines the [saas token](https://learn.microsoft.com/en-us/rest/api/storageservices/delegate-access-with-shared-access-signature) to use. These can be of user delegation, service or account type. Note that user delegation and service SAS tokens do not support the creation of containers, therefore `createContainer` configuration will be overridden to `false` if `sasToken.type` is configured either as "delegation" or "service". In this case the user must make sure that the container already exists, or it will lead to a runtime error. The SAS token must be of the following types: "delegation", "service" or "account". This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_AZURE_SASTOKEN_TYPE`. | | | sasToken.value | Specifies the key value of the SAS token. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_AZURE_SASTOKEN_VALUE`. | | #### YAML snippet ```yaml backup: store: azure azure: endpoint: null accountName: null accountKey: null connectionString: null basePath: null createContainer: true sasToken: type: null value: null ``` ### zeebe.broker.data.backup.filesystem To store your backups in the local filesystem, choose the `FILESYSTEM` backup store and specify where to store the backups locally. :::caution Since the durability of the backups are largely dependent on the target file system and underlying storage, it is recommended to use known durable solutions in production, such as S3, GCS, or Azure. To ensure that this can be used properly in production, you must use a POSIX-compliant file system, and at a minimum replicated disks (e.g. RAID configured disks). ::: :::note Backup encryption Zeebe does not support backup encryption natively, but it _can_ use filesystem based encryption. This then is a feature of the filesystem and not Zeebe itself. ::: | Field | Description | Example Value | | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | | basePath | The base path is used to define the parent directory of all create backups and backup-manifest files. **This directory must exist and be writable by the Zeebe Broker**. This setting can also be overridden using the environment variable `ZEEBE_BROKER_DATA_BACKUP_FILESYSTEM_BASEPATH`. | /mnt/backups/zeebe | #### YAML snippet ```yaml zeebe: broker: data: backup: store: FILESYSTEM filesystem: basePath: ``` ### zeebe.broker.cluster This section contains all cluster related configurations, to setup a zeebe cluster. | Field | Description | Example Value | | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | | nodeId | Specifies the unique ID of this broker node in a cluster. The ID should be between 0 and number of nodes in the cluster (exclusive). This setting can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_NODEID`. | 0 | | partitionsCount | Controls the number of partitions, which should exist in the cluster. This can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_PARTITIONSCOUNT`. | 1 | | replicationFactor | Controls the replication factor, which defines the count of replicas per partition. The replication factor cannot be greater than the number of nodes in the cluster. This can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_REPLICATIONFACTOR`. | 1 | | clusterSize | Specifies the zeebe cluster size. This value is used to determine which broker is responsible for which partition. This can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_CLUSTERSIZE`. | 1 | | initialContactPoints | Allows to specify a list of known other nodes to connect to on startup. The contact points of the internal network configuration must be specified. The format is [HOST:PORT]. To guarantee the cluster can survive network partitions, all nodes must be specified as initial contact points. This setting can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_INITIALCONTACTPOINTS` specifying a comma-separated list of contact points. Default is empty list. | [ 192.168.1.22:26502, 192.168.1.32:26502 ] | | clusterId | Unique identifier for cluster. This setting can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_CLUSTERID`. | zeebe-cluster-123 | | clusterName | Allows to specify a name for the cluster. This setting can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_CLUSTERNAME`. | zeebe-cluster | | heartbeatInterval | Configure heartbeatInterval. The leader sends a heartbeat to a follower every heartbeatInterval. Note: This is an advanced setting. This setting can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_HEARTBEATINTERVAL`. | 250ms | | electionTimeout | Configure electionTimeout. If a follower does not receive a heartbeat from the leader with in an election timeout, it can start a new leader election. electionTimeout should be greater than configured heartbeatInterval. When the electionTimeout is large, there will be delay in detecting a leader failure. When the electionTimeout is small, it can lead to false positives when detecting leader failures and thus leading to unnecessary leader changes. If the network latency between the nodes is high, it is recommended to have a higher election latency. Note: This is an advanced setting. This setting can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_ELECTIONTIMEOUT`. | 2500ms | #### YAML snippet ```yaml cluster: nodeId: 0 partitionsCount: 1 replicationFactor: 1 clusterSize: 1 initialContactPoints: [] clusterName: zeebe-cluster heartbeatInterval: 250ms electionTimeout: 2500ms ``` ### zeebe.broker.cluster.raft This section contains all properties required to configure raft. | Field | Description | Example Value | | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | enablePriorityElection | When this flag is enabled, the leader election algorithm attempts to elect the leaders based on a pre-defined priority. As a result, it tries to distributed the leaders uniformly across the brokers. Note that it is only a best-effort strategy. It is not guaranteed to be a strictly uniform distribution. This setting can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_RAFT_ENABLEPRIORITYELECTION`. | true | #### YAML snippet ```yaml cluster: raft: enablePriorityElection: true ``` ### zeebe.broker.cluster.flush | Field | Description | Example Value | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | flush | Configures how often data is explicitly flushed to disk. By default, for a given partition, data is flushed on every leader commit, and every follower append. This is to ensure consistency across all replicas. Disabling this can cause inconsistencies, and at worst, data corruption or data loss scenarios. The default behavior is optimized for safety, and flushing occurs on every leader commit and follower append in a synchronous fashion. You can introduce a delay to reduce the performance penalty of flushing via `delayTime`. | | | enabled | If false, explicit flushing of the Raft log is disabled, and flushing only occurs right before a snapshot is taken. You should only disable explicit flushing if you are willing to accept potential data loss at the expense of performance. Before disabling it, try the delayed options, which provide a trade-off between safety and performance. This setting can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_RAFT_FLUSH_ENABLED`. | true | | delayTime | If the delay is > 0, then flush requests are delayed by at least the given period. It is recommended that you find the smallest delay here with which you achieve your performance goals. It's also likely that anything above 30s is not useful, as this is the typical default flush interval for the Linux OS. This setting can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_RAFT_FLUSH_DELAYTIME`. | 0s | #### YAML snippet ```yaml cluster: flush: enabled: true delayTime: 0s ``` ### zeebe.broker.cluster.membership Configure parameters for SWIM protocol which is used to propagate cluster membership information among brokers and gateways. | Field | Example Value | Description | | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | broadcastUpdates | Configure whether to broadcast member updates to all members. If set to false updates will be gossiped among the members. If set to true the network traffic may increase but it reduce the time to detect membership changes. This setting can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_MEMBERSHIP_BROADCASTUPDATES`. | false | | broadcastDisputes | Configure whether to broadcast disputes to all members. If set to true the network traffic may increase but it reduce the time to detect membership changes. This setting can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_MEMBERSHIP_BROADCASTDISPUTES`. | true | | notifySuspect | Configure whether to notify a suspect node on state changes. This setting can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_MEMBERSHIP_NOTIFYSUSPECT`. | false | | gossipInterval | Sets the interval at which the membership updates are sent to a random member. This setting can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_MEMBERSHIP_GOSSIPINTERVAL`. | 250ms | | gossipFanout | Sets the number of members to which membership updates are sent at each gossip interval. This setting can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_MEMBERSHIP_GOSSIPFANOUT`. | 2 | | probeInterval | Sets the interval at which to probe a random member. This setting can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_MEMBERSHIP_PROBEINTERVAL`. | 1s | | probeTimeout | Sets the timeout for a probe response. This setting can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_MEMBERSHIP_PROBETIMEOUT`. | 100ms | | suspectProbes | Sets the number of probes failed before declaring a member is suspect. This setting can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_MEMBERSHIP_SUSPECTPROBES`. | 3 | | failureTimeout | Sets the timeout for a suspect member is declared dead. This setting can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_MEMBERSHIP_FAILURETIMEOUT`. | 10s | | syncInterval | Sets the interval at which this member synchronizes its membership information with a random member. This setting can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_MEMBERSHIP_SYNCINTERVAL`. | 10s | #### YAML snippet ```yaml membership: broadcastUpdates: false broadcastDisputes: true notifySuspect: false gossipInterval: 250ms gossipFanout: 2 probeInterval: 1s probeTimeout: 100ms suspectProbes: 3 failureTimeout: 10s syncInterval: 10s ``` ### zeebe.broker.cluster.configManager.gossip Configure the parameters used to propagate the dynamic cluster configuration across brokers and gateways. | Field | Description | ExampleValue | | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | | syncDelay | Sets the interval between two synchronization requests to other members of the cluster. This setting can also be overridden using the environment variable ZEEBE_BROKER_CLUSTER_CONFIGMANAGER_GOSSIP_SYNCDELAY | 10s | | syncRequestTimeout | Sets the timeout for the synchronization requests. This setting can also be overridden using the environment variable ZEEBE_BROKER_CLUSTER_CONFIGMANAGER_GOSSIP_SYNCREQUESTTIMEOUT | 2s | | gossipFanout | Sets the number of cluster members the configuration is gossiped to. This setting can also be overridden using the environment variable ZEEBE_BROKER_CLUSTER_CONFIGMANAGER_GOSSIP_GOSSIPFANOUT | 2 | #### YAML snippet ```yaml configManager: gossip: syncDelay: 10s syncRequestTimeout: 2s gossipFanout: 2 ``` ### zeebe.broker.cluster.messageCompression This feature is useful when the network latency between the nodes is very high (for example when nodes are deployed in different data centers). When latency is high, the network bandwidth is severely reduced. Hence enabling compression helps to improve the throughput. :::caution When there is no latency enabling this may have a performance impact. ::: :::note When this flag is enables, you must also enable compression in standalone broker configuration. ::: | Field | Description | Example Value | | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | messageCompression | Configure compression algorithm for all messages sent between the gateway and the brokers. Available options are NONE, GZIP and SNAPPY. This setting can also be overridden using the environment variable `ZEEBE_BROKER_CLUSTER_MESSAGECOMPRESSION`. | NONE | #### YAML snippet ```yaml messageCompression: NONE ``` ### zeebe.broker.threads | Field | Example Value | Description | | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | cpuThreadCount | Controls the number of non-blocking CPU threads to be used. WARNING: You should never specify a value that is larger than the number of physical cores available. Good practice is to leave 1-2 cores for ioThreads and the operating system (it has to run somewhere). For example, when running Zeebe on a machine which has 4 cores, a good value would be 2. This setting can also be overridden using the environment variable `ZEEBE_BROKER_THREADS_CPUTHREADCOUNT`. | 2 | | ioThreadCount | Controls the number of io threads to be used. These threads are used for workloads that write data to disk. While writing, these threads are blocked which means that they yield the CPU. This setting can also be overridden using the environment variable `ZEEBE_BROKER_THREADS_IOTHREADCOUNT`. | 2 | #### YAML snippet ```yaml threads: cpuThreadCount: 2 ioThreadCount: 2 ``` ### zeebe.broker.flowControl.request Replaces the deprecated `zeebe.broker.backpressure` configuration. | Field | Description | Example Value | | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | enabled | Set this to enable flow control for user requests. When enabled the broker rejects user requests when the number of inflight requests is greater than than the "limit". The value of the "limit" is determined based on the configured algorithm. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_REQUEST_ENABLED`. | true | | algorithm | The algorithm configures which algorithm to use for the flow control. It should be one of vegas, aimd, fixed, gradient, or gradient2. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_REQUEST_ALGORITHM`. | aimd | | useWindowed | If enabled, will use the average latencies over a window as the current latency to update the limit. It is not recommended to enable this when the algorithm is aimd. This setting is not applicable to fixed limit. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_REQUEST_USEWINDOWED`. | false | #### YAML snippet ```yaml flowControl: request: enabled: true algorithm: aimd useWindowed: false ``` ### zeebe.broker.flowControl.request.aimd | Field | Description | Example Value | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | requestTimeout | The limit will be reduced if the observed latency is greater than the requestTimeout. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_REQUEST_AIMD_REQUESTTIMEOUT`. | 200ms | | initialLimit | The initial limit to be used when the broker starts. The limit will be reset to this value when the broker restarts. This setting can also be overridden using the environment `ZEEBE_BROKER_FLOWCONTROL_REQUEST_AIMD_INITIALLIMIT`. | 100 | | minLimit | The minimum limit. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_REQUEST_AIMD_MINLIMIT`. | 1 | | maxLimit | The maximum limit. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL__REQUEST_AIMD_MAXLIMIT`. | 1000 | | backoffRatio | The backoffRatio is a double value x such that x is between 0 and 1. It determines the factor by which the limit is decreased. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_REQUEST_AIMD_BACKOFFRATIO`. | 0.9 | #### YAML snippet ```yaml request: algorithm: aimd aimd: requestTimeout: 200ms initialLimit: 100 minLimit: 1 maxLimit: 1000 backoffRatio: 0.9 ``` ### zeebe.broker.flowControl.request.fixed | Field | Description | Example Value | | ----- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | limit | Set a fixed limit. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_REQUEST_FIXED_LIMIT`. | 20 | #### YAML snippet ```yaml request: algorithm: fixed fixed: limit: 20 ``` ### zeebe.broker.flowControl.request.vegas | Field | Description | Example Value | | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | initialLimit | The initial limit to be used when the broker starts. The limit will be reset to this value when the broker restarts. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_REQUEST_VEGAS_INITIALLIMIT`. | 20 | | alpha | The limit is increased if the queue size is less than this value. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_REQUEST_VEGAS_ALPHA`. | 3 | | beta | The limit is decreased if the queue size is greater than this value. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_REQUEST_VEGAS_BETA`. | 6 | #### YAML snippet ```yaml request: algorithm: vegas vegas: initialLimit: 20 alpha: 3 beta: 6 ``` ### zeebe.broker.flowControl.request.gradient | Field | Description | Example Value | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | minLimit | The minimum limit. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_REQUEST_GRADIENT_MINLIMIT`. | 10 | | initialLimit | The initial limit to be used when the broker starts. The limit will be reset to this value when the broker restarts. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_REQUEST_GRADIENT_INITIALLIMIT`. | 20 | | rttTolerance | Tolerance for changes from minimum latency. A value ≥ 1.0 indicating how much change from minimum latency is acceptable before reducing the limit. For example, a value of 2.0 means that a 2x increase in latency is acceptable. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_REQUEST_GRADIENT_RTTTOLERANCE` | 2.0 | #### YAML snippet ```yaml request: algorithm: gradient gradient: minLimit: 10 initialLimit: 20 rttTolerance: 2.0 ``` ### zeebe.broker.flowControl.request.gradient2 | Field | Description | Example Value | | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | minLimit | The minimum limit. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_REQUEST_GRADIENT2_MINLIMIT`. | 10 | | initialLimit | The initial limit to be used when the broker starts. The limit will be reset to this value when the broker restarts. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_REQUEST_GRADIENT2_INITIALLIMIT`. | 20 | | rttTolerance | Tolerance for changes from minimum latency. A value ≥ 1.0 indicating how much change from minimum latency is acceptable before reducing the limit. For example, a value of 2.0 means that a 2x increase in latency is acceptable. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_REQUEST_GRADIENT2_RTTTOLERANCE`. | 2.0 | | longWindow | longWindow is the length of the window (the number of samples) to calculate the exponentially smoothed average latency. This setting can also be overridden using the environment `ZEEBE_BROKER_FLOWCONTROL_REQUEST_GRADIENT2_LONGWINDOW`. | 600 | #### YAML snippet ```yaml request: algorithm: gradient2 gradient2: minLimit: 10 initialLimit: 20 rttTolerance: 2.0 longWindow: 600 ``` ### zeebe.broker.flowControl.write | Field | Description | Example Value | | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | enabled | Set this to enable or disable flow control for all writes. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_WRITE_ENABLED`. | false | | rampUp | Time period during which the write limit gradually increases to the configured limit. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_WRITE_RAMPUP`. | 10s | | limit | The maximum number of record that can be written per second. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_WRITE_LIMIT`. | 1000 | #### YAML snippet ```yaml write: enabled: false rampUp: 10s limit: 1000 ``` ### zeebe.broker.flowControl.write.throttling | Field | Description | Example Value | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- | | enabled | Set this to enable or disable write throttling based on the exporting backlog. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_WRITE_THROTTLING_ENABLED`. | false | | acceptableBacklog | The number of records that can be in the exporting backlog. The write rate is throttled such that the backlog stabilizes around this value when exporting is a bottleneck. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_WRITE_THROTTLING_ACCEPTABLEBACKLOG`. | 100000 | | minimumLimit | The minimum write limit that is guaranteed even when throttling. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_WRITE_THROTTLING_MINIMUMLIMIT`. | 100 | | resolution | The frequency at which the throttling is updated. This setting can also be overridden using the environment variable `ZEEBE_BROKER_FLOWCONTROL_WRITE_THROTTLING_RESOLUTION`. | 15s | #### YAML snippet ```yaml write: throttling: enabled: false acceptableBacklog: 100000 minimumLimit: 100 resolution: 15s ``` ### zeebe.broker.backpressure | Field | Description | Example Value | | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | enabled | Set this to enable or disable backpressure. When enabled the broker rejects user requests when the number of inflight requests is greater than than the "limit". The value of the "limit" is determined based on the configured algorithm. This setting can also be overridden using the environment variable `ZEEBE_BROKER_BACKPRESSURE_ENABLED`. | true | | useWindowed | if enabled - will use the average latencies over a window as the current latency to update the limit. It is not recommended to enable this when the algorithm is aimd. This setting is not applicable to fixed limit. This setting can also be overridden using the environment variable `ZEEBE_BROKER_BACKPRESSURE_USEWINDOWED`. | true | | algorithm | The algorithm configures which algorithm to use for the backpressure. It should be one of vegas, aimd, fixed, gradient, or gradient2. This setting can also be overridden using the environment variable `ZEEBE_BROKER_BACKPRESSURE_ALGORITHM`. | aimd | #### YAML snippet ```yaml backpressure: enabled: true useWindowed: true algorithm: aimd ``` ### zeebe.broker.backpressure.aimd | Field | Description | Example Value | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- | | requestTimeout | The limit will be reduced if the observed latency is greater than the requestTimeout. This setting can also be overridden using the environment variable `ZEEBE_BROKER_BACKPRESSURE_AIMD_REQUESTTIMEOUT`. | 200ms | | initialLimit | The initial limit to be used when the broker starts. The limit will be reset to this value when the broker restarts. This setting can also be overridden using the environment `ZEEBE_BROKER_BACKPRESSURE_AIMD_INITIALLIMIT`. | 100 | | minLimit | The minimum limit. This setting can also be overridden using the environment variable `ZEEBE_BROKER_BACKPRESSURE_AIMD_MINLIMIT`. | 1 | | maxLimit | The maximum limit. This setting can also be overridden using the environment variable `ZEEBE_BROKER_BACKPRESSURE_AIMD_MAXLIMIT`. | 1000 | | backoffRatio | The backoffRatio is a double value x such that x is between 0 and 1. It determines the factor by which the limit is decreased. This setting can also be overridden using the environment variable `ZEEBE_BROKER_BACKPRESSURE_AIMD_BACKOFFRATIO`. | 0.9 | #### YAML snippet ```yaml backpressure: algorithm: aimd aimd: requestTimeout: 200ms initialLimit: 100 minLimit: 1 maxLimit: 1000 backoffRatio: 0.9 ``` ### zeebe.broker.backpressure.fixed | Field | Description | Example Value | | ----- | ------------------------------------------------------------------------------------------------------------------------------ | ------------- | | limit | Set a fixed limit. This setting can also be overridden using the environment variable `ZEEBE_BROKER_BACKPRESSURE_FIXED_LIMIT`. | 20 | #### YAML snippet ```yaml backpressure: algorithm: fixed fixed: limit: 20 ``` ### zeebe.broker.backpressure.vegas | Field | Description | Example Value | | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | initialLimit | The initial limit to be used when the broker starts. The limit will be reset to this value when the broker restarts. This setting can also be overridden using the environment variable `ZEEBE_BROKER_BACKPRESSURE_VEGAS_INITIALLIMIT`. | 20 | | alpha | The limit is increased if the queue size is less than this value. This setting can also be overridden using the environment variable `ZEEBE_BROKER_BACKPRESSURE_VEGAS_ALPHA`. | 3 | | beta | The limit is decreased if the queue size is greater than this value. This setting can also be overridden using the environment variable `ZEEBE_BROKER_BACKPRESSURE_VEGAS_BETA`. | 6 | #### YAML snippet ```yaml backpressure: algorithm: vegas vegas: initialLimit: 20 alpha: 3 beta: 6 ``` ### zeebe.broker.backpressure.gradient | Field | Description | Example Value | | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | minLimit | The minimum limit. This setting can also be overridden using the environment variable `ZEEBE_BROKER_BACKPRESSURE_GRADIENT_MINLIMIT`. | 10 | | initialLimit | The initial limit to be used when the broker starts. The limit will be reset to this value when the broker restarts. This setting can also be overridden using the environment variable `ZEEBE_BROKER_BACKPRESSURE_GRADIENT_INITIALLIMIT`. | 20 | | rttTolerance | Tolerance for changes from minimum latency. A value ≥ 1.0 indicating how much change from minimum latency is acceptable before reducing the limit. For example, a value of 2.0 means that a 2x increase in latency is acceptable. This setting can also be overridden using the environment variable ZEEBE_BROKER_BACKPRESSURE_GRADIENT_RTTTOLERANCE | 2.0 | #### YAML snippet ```yaml backpressure: algorithm: gradient gradient: minLimit: 10 initialLimit: 20 rttTolerance: 2.0 ``` ### zeebe.broker.backpressure.gradient2 | Field | Description | Example Value | | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | minLimit | The minimum limit. This setting can also be overridden using the environment variable `ZEEBE_BROKER_BACKPRESSURE_GRADIENT2_MINLIMIT`. | 10 | | initialLimit | The initial limit to be used when the broker starts. The limit will be reset to this value when the broker restarts. This setting can also be overridden using the environment variable `ZEEBE_BROKER_BACKPRESSURE_GRADIENT2_INITIALLIMIT`. | 20 | | rttTolerance | Tolerance for changes from minimum latency. A value ≥ 1.0 indicating how much change from minimum latency is acceptable before reducing the limit. For example, a value of 2.0 means that a 2x increase in latency is acceptable. This setting can also be overridden using the environment variable `ZEEBE_BROKER_BACKPRESSURE_GRADIENT2_RTTTOLERANCE`. | 2.0 | | longWindow | longWindow is the length of the window (the number of samples) to calculate the exponentially smoothed average latency. This setting can also be overridden using the environment `ZEEBE_BROKER_BACKPRESSURE_GRADIENT2_LONGWINDOW`. | 600 | #### YAML snippet ```yaml backpressure: algorithm: gradient2 gradient2: minLimit: 10 initialLimit: 20 rttTolerance: 2.0 longWindow: 600 ``` ### zeebe.broker.exporters Each exporter should be configured following this template: | Field | Description | Example Value | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- | | exporters | Configure exporters below | | | jarPath | path to the JAR file containing the exporter class. JARs are only loaded once, so you can define two exporters that point to the same JAR, with the same class or a different one, and use args to parametrize its instantiation. | | | className | entry point of the exporter, a class which _must_ extend the io.camunda.zeebe.exporter.Exporter interface. A nested table as "args:" will allow you to inject arbitrary arguments into your class through the use of annotations. These setting can also be overridden using the environment variables "`ZEEBE_BROKER_EXPORTERS_`[exporter name]\_..." | | #### YAML snippet ```yaml zeebe: broker: ... exporters: jarPath: className: ``` ### zeebe.broker.exporters.elasticsearch An example configuration for the Elasticsearch exporter can be found [here](../exporters/elasticsearch-exporter.md#example). ### zeebe.broker.exporters.opensearch (OpenSearch Exporter) An example configuration for the OpenSearch exporter can be found [here](../exporters/opensearch-exporter.md#example). ### zeebe.broker.exporters.camundaExporter (Camunda Exporter) An example configuration for the Camunda exporter can be found [here](../exporters/camunda-exporter.md#example). ### zeebe.broker.processing | Field | Description | Example Value | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- | | maxCommandsInBatch | Sets the maximum number of commands that processed within one batch. The processor will process until no more follow up commands are created by the initial command or the configured limit is reached. By default, up to 100 commands are processed in one batch. Can be set to 1 to disable batch processing. Must be a positive integer number. Note that the resulting batch size will contain more entries than this limit because it includes follow up events. When resulting batch size is too large (see maxMessageSize), processing will be rolled back and retried with a smaller maximum batch size. Lowering the command limit can reduce the frequency of rollback and retry. This setting can also be overridden using the environment variable `ZEEBE_BROKER_PROCESSING_MAXCOMMANDSINBATCH`. | 100 | #### YAML snippet ```yaml processing: maxCommandsInBatch = 100 ``` ### Experimental configuration See the experimental section of the [defaults.yaml](https://github.com/camunda/camunda/blob/main/dist/src/main/config/defaults.yaml). Be aware that all configurations which are part of the experimental section are subject to change and can be dropped at any time. ### Multitenancy configuration For an embedded gateway setup, any gateway property can be passed along to the `StandaloneBroker` by prefixing `zeebe.broker`. #### zeebe.broker.gateway.multitenancy Multi-tenancy in Zeebe can be configured with the following configuration properties. Multi-tenancy is disabled by default. Read more [in the multi-tenancy documentation](/components/concepts/multi-tenancy.md). :::note For now, multi-tenancy is only supported in combination with Identity. To use multi-tenancy, you must set [`authentication.mode`](#zeebebrokergatewaysecurityauthentication) to `'identity'` and specify the `camunda.identity.baseUrl` property or the [corresponding Camunda Identity environment variable](/self-managed/components/management-identity/miscellaneous/configuration-variables.md#core-configuration) as well. ::: :::note If you are using a standalone gateway, refer to the [gateway configuration guide](./gateway.md#zeebegatewaymultitenancy). ::: | Field | Description | Example value | | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- | | enabled | Enable multitenancy in the embedded gateway. This setting can also be overridden using the environment variable `ZEEBE_BROKER_GATEWAY_MULTITENANCY_ENABLED`. | false | ##### YAML snippet ```yaml broker: gateway: multitenancy: enabled: false ``` #### zeebe.broker.gateway.security.authentication | Field | Description | Example value | | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | mode | Controls which authentication mode is active; supported modes are `none` and `identity`. If `identity` is set, authentication will be done using [camunda-identity](/self-managed/components/management-identity/overview.md), which needs to be configured in the corresponding subsection. This setting can also be overridden using the environment variable `ZEEBE_BROKER_GATEWAY_SECURITY_AUTHENTICATION_MODE`. | none | ##### YAML snippet ```yaml security: authentication: mode: none ``` #### zeebe.broker.gateway.security.authentication.identity :::note The Zeebe configuration properties for Camunda Identity are deprecated as of version `8.4.0`. Use the dedicated Camunda Identity properties or the [corresponding environment variables](/self-managed/components/management-identity/miscellaneous/configuration-variables.md#core-configuration). ::: | Field | Description | Example value | | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | | issuerBackendUrl | The URL to the auth provider backend, used to validate tokens. This setting can also be overridden using the environment variable `ZEEBE_BROKER_GATEWAY_SECURITY_AUTHENTICATION_IDENTITY_ISSUERBACKENDURL`. | http://keycloak:18080/auth/realms/camunda-platform | | audience | The required audience of the auth token. This setting can also be overridden using the environment variable `ZEEBE_BROKER_GATEWAY_SECURITY_AUTHENTICATION_IDENTITY_AUDIENCE`. | zeebe-api | | baseUrl | The URL to the Identity instance. This setting can also be overridden using the environment variable `ZEEBE_BROKER_GATEWAY_SECURITY_AUTHENTICATION_IDENTITY_BASEURL`. | http://identity:8084 | ##### YAML snippet ```yaml security: authentication: mode: identity identity: issuerBackendUrl: http://keycloak:18080/auth/realms/camunda-platform audience: zeebe-api type: keycloak ``` ### Console Ping Configuration This feature enables components like the Zeebe Broker, Tasklist, Operate, and Zeebe Gateway to ping Console with license information. #### camunda.console.ping | Field | Description | Example value | | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | | `enabled` | Enables or disables the ping to console feature. Disabled by default. This setting can also be overridden using the environment variable `CAMUNDA_CONSOLE_PING_ENABLED` | `true` | | `endpoint` | Endpoint where pings should be sent. This setting can also be overridden using the environment variable `CAMUNDA_CONSOLE_PING_ENDPOINT`. | `https://console.endpoint.com` | | `clusterName` | Cluster name sent with telemetry. This setting can also be overridden using the environment variable `CAMUNDA_CONSOLE_PING_CLUSTERNAME`. | `test_cluster_name` | | `pingPeriod` | Frequency of pings (e.g., `1s`, `1h`, `1d`). This setting can also be overridden using the environment variable `CAMUNDA_CONSOLE_PING_PINGPERIOD`. | `1h` | | `properties` | Additional properties to include in the ping payload (as key-value pairs). This setting can also be overridden using the environment variable `CAMUNDA_CONSOLE_PING_PROPERTIES`. | `testProperty: 123` | | `retry.maxRetries` | Maximum number of retry attempts after a failed ping. Uses exponential backoff. This setting can also be overridden using the environment variable `CAMUNDA_CONSOLE_PING_MAX_RETRIES`. | `1` | | `retry.minRetryDelay` | Minimum delay between retries. This setting can also be overridden using the environment variable `CAMUNDA_CONSOLE_PING_RETRY_MINRETRYDELAY`. | `1s` | | `retry.maxRetryDelay` | Maximum delay between retries. This setting can also be overridden using the environment variable `CAMUNDA_CONSOLE_PING_ENABLED_RETRY_MAXRETRYDELAY`. | `10s` | | `retry.retryDelayMultiplier` | Multiplier applied to delay between retries. This setting can also be overridden using the environment variable `CAMUNDA_CONSOLE_PING_RETRY_RETRYDELAYMULTIPLIER`. | `2` | ##### YAML snippet ```yaml camunda: console: ping: enabled: true endpoint: https://console.endpoint.com clusterName: test_cluster_name pingPeriod: 1h properties: testProperty: 123 retry: maxRetries: 1 minRetryDelay: 1s maxRetryDelay: 10s retryDelayMultiplier: 2 ``` ### Continuous backups configuration Configuration options for primary storage continuous backups. #### camunda.data.primary-storage.backup | Field | Description | Example value | | :--------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------- | | `continuous` | Enables or disables the continuous backups feature. | `true` | | `required` | Forces the continuous backups feature to be properly configured during broker startup. | `true` | | `schedule` | The interval at which a primary storage backup is taken. Can be a CRON expression, an ISO-8601 duration, or `none`. | `PT12H` | | `checkpoint-interval` | The interval at which checkpoints are ingested into the log stream. Uses an ISO-8601 duration. | `PT5M` | | `offset` | Optional offset for the generated backup identifiers. | `20260215115715` | | `retention.window` | The active window of backups available for restore in the configured backup store. Uses an ISO-8601 duration. | `P1W` | | `retention.cleanup-schedule` | The interval at which the retention mechanism checks for backups outside the active window. Can be a CRON expression, an ISO-8601 duration, or `none`. | `PT1H` | ```yaml camunda: data: primary-storage: backup: required: "false" continuous: "true" schedule: "PT10M" checkpoint-interval: "PT1M" offset: 20260215115715 retention: window: "P1W" cleanup-schedule: "PT1H" ``` ```bash CAMUNDA_DATA_PRIMARYSTORAGE_BACKUP_REQUIRED=false CAMUNDA_DATA_PRIMARYSTORAGE_BACKUP_CONTINUOUS=true CAMUNDA_DATA_PRIMARYSTORAGE_BACKUP_SCHEDULE="PT10M" CAMUNDA_DATA_PRIMARYSTORAGE_BACKUP_CHECKPOINTINTERVAL="PT1M" CAMUNDA_DATA_PRIMARYSTORAGE_BACKUP_OFFSET=20260215115715 CAMUNDA_DATA_PRIMARYSTORAGE_BACKUP_RETENTION_WINDOW="P1W" CAMUNDA_DATA_PRIMARYSTORAGE_BACKUP_RETENTION_CLEANUPSCHEDULE="PT1H" ``` --- ## Configuration(4) Zeebe can be configured through the following: - Configuration files - Environment variables - A mix of both If both configuration files and environment variables are present, environment variables overwrite settings in configuration files. To make small changes to the configuration, we recommend using environment variables. To make big changes to the configuration, we recommend using a configuration file. The configuration is applied during startup of Zeebe. It is not possible to change the configuration at runtime. ## Default configuration The default configuration is located in `config/application.yaml`. This configuration contains the most common configuration settings for a standalone broker. It also lists the corresponding environment variable for each setting. :::note The default configuration is not suitable for a standalone gateway node. To run a standalone gateway node, take a look at [the gateway configuration](gateway.md) or `/config/gateway.default.yaml`. ::: ## Configuration file templates We provide templates that contain all possible configuration settings, along with explanations for each setting, though you may find it easier to search through our [broker](broker.md) and [gateway](gateway.md) configuration documentation to adjust the templates: - [`config/defaults.yaml` Standalone Broker (with embedded gateway)](https://github.com/camunda/camunda/blob/main/dist/src/main/config/defaults.yaml) - Complete configuration template for a standalone broker with embedded gateway. Use this as the basis for a single broker deployment for test or development. - [`config/gateway.default.yaml`](https://github.com/camunda/camunda/blob/main/zeebe/gateway/src/test/resources/configuration/gateway.default.yaml) - Complete configuration template for a standalone gateway. :::note These templates also include the corresponding environment variables to use for every setting. ::: ## Editing the configuration You can either start from scratch or start from the configuration templates listed above. If you use a configuration template and want to uncomment certain lines, make sure to also uncomment their parent elements: ```yaml Valid Configuration zeebe: gateway: network: # host: 0.0.0.0 port: 26500 Invalid configuration # zeebe: # gateway: # network: # host: 0.0.0.0 port: 26500 ``` Uncommenting individual lines is a bit finicky, because YAML is sensitive to indentation. The best way to do it is to position the cursor before the `#` character and delete two characters (the dash and the space). Doing this will consistently give you a valid YAML file. When it comes to editing individual settings, two data types are worth mentioning: - Data size (e.g. `logSegmentSize`) - Human-friendly format: `500MB` (or `KB, GB`) - Machine-friendly format: size in bytes as long - Timeouts/intervals (e.g. `requestTimeout`) - Human-friendly format: `15s` (or `m, h`) - Machine-friendly format: either duration in milliseconds as long, or [ISO-8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations) format (e.g. `PT15S`) ## Passing configuration files to Zeebe Rename the configuration file to `application.yaml` and place it in the following location: ```shell script ./config/application.yaml ``` ### Other ways to specify the configuration file Zeebe uses Spring Boot for its configuration parsing. All other ways to [configure a Spring Boot application](https://docs.spring.io/spring-boot/reference/features/external-config.html) should also work. In particular, you can use: - `SPRING_CONFIG_ADDITIONAL_LOCATION` to specify an additional configuration file. - `SPRING_APPLICATION_JSON` to specify settings in JSON format. Details can be found in the Spring documentation. :::note We recommend not to use `SPRING_CONFIG_LOCATION` as this will replace all existing configuration defaults. When used inappropriately, some features will be disabled or will not be configured properly. ::: If you specify `SPRING_CONFIG_LOCATION`, specify it like this: ```shell script export SPRING_CONFIG_LOCATION='classpath:/,file:./[path to config file]' ``` This will ensure the defaults defined in the classpath resources will be used (unless explicitly overwritten by the configuration file you provide). If you omit the defaults defined in the classpath, some features may be disabled or will not be configured properly. ## Verifying configuration Start Zeebe to verify the configuration was applied. If the configuration could be read, Zeebe will expose it via the monitoring port at [http://localhost:9600/actuator/configprops/zeebe](http://localhost:9600/actuator/configprops/zeebe). This will show you both the resolved configuration and its inputs. :::note Zeebe uses the Spring Boot [configprops](https://docs.spring.io/spring-boot/docs/current/actuator-api/htmlsingle/#configprops) actuator for this, so any documentation listed there applies as well. ::: In some cases of invalid configuration, Zeebe will fail to start and log a warning that explains which configuration setting could not be read. ``` 17:17:38.796 [] [main] ERROR org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter - *************************** APPLICATION FAILED TO START *************************** Description: Failed to bind properties under 'zeebe.broker.network.port-offset' to int: Property: zeebe.broker.network.port-offset Value: false Origin: System Environment Property "ZEEBE_BROKER_NETWORK_PORTOFFSET" Reason: failed to convert java.lang.String to int Action: Update your application's configuration ``` ## Licensing See the [core settings documentation](/self-managed/components/orchestration-cluster/core-settings/configuration/licensing.md). ## Logging Zeebe uses Log4j2 framework for logging. In the distribution and the Docker image, find the default log configuration file in `config/log4j2.xml`. ### Google Stackdriver (JSON) logging To enable Google Stackdriver compatible JSON logging, set the environment variable `ZEEBE_LOG_APPENDER=Stackdriver` before starting Zeebe. ### Change log level dynamically Zeebe brokers expose a [Spring Boot Actuators web endpoint](https://docs.spring.io/spring-boot/docs/current/actuator-api/html/#loggers) for configuring loggers dynamically. To change the log level of a logger, make a `POST` request to the `/actuator/loggers/{logger.name}` endpoint as shown in the example below. Change `io.camunda.zeebe` to the required logger name and `debug` to required log level. ``` curl 'http://localhost:9600/actuator/loggers/io.camunda.zeebe' -i -X POST -H 'Content-Type: application/json' -d '{"configuredLevel":"debug"}' ``` ## Health probes Health probes are set to sensible defaults which cover common use cases. For specific use cases, it might be necessary to customize health probes: - [Gateway health probes](gateway-health-probes.md) ## Experimental configuration options You may have already noticed a special section of Zeebe's configuration templates titled `experimental`. This section refers to settings which are potentially not backwards compatible. In other words, any configuration setting found there may or may not be dropped in any minor version. These settings are there primarily for incubating features and/or very advanced settings for which the team has not found a good general default configuration. Once one is found, or the incubating feature is promoted, the setting(s) may be moved into a different section. Only at that point do they fall under the same backwards compatibility guarantees as the rest of the project. We may choose to drop support for specific experimental configurations in any minor version update. Most users should not have to change anything in this section for a good experience. However, if you have a unique set up, or simply wish to try out new experimental features, it can be worth investigating these (ideally with the guidance of the Zeebe community). ## Logging For details on logging, refer to the Orchestration Cluster [core settings and features](/self-managed/components/orchestration-cluster/core-settings/configuration/logging.md) --- ## Environment variables ## Environment variables for configuration As a Spring Boot application, Zeebe supports any standard [Spring configuration](https://docs.spring.io/spring-boot/reference/features/external-config.html) method. This configuration can be provided as a configuration file, through environment variables, or both. When both sources are used, environment variables have precedence over the configuration file. All available environment variables are documented in the [configuration file templates](configuration.md#configuration-file-templates). ## Environment variables for operators The following environment variables are intended for operators: - `ZEEBE_LOG_LEVEL`: Sets the log level of the Zeebe Logger (default: `info`). - `ZEEBE_LOG_APPENDER`: Sets the console log appender (default: `Console`). We recommend using `Stackdriver` if Zeebe runs on Google Cloud Platform to output JSON formatted log messages. ## Environment variables for developers The following environment variables are intended for developers: - `SPRING_PROFILES_ACTIVE=dev`: If this is set, the broker starts in a temporary folder and all data is cleaned up upon exit. - `ZEEBE_DEBUG=true/false`: Activates a `DebugLogExporter` with default settings. The value of the environment variable toggles pretty printing. :::note It is not recommended to use these settings in production. ::: --- ## Fixed partitioning Starting with 1.2.0, there is a new experimental configuration option which lets you specify a fixed partitioning scheme; this means you can manually configure which partitions belong to which brokers. The partitioning scheme is controlled via a new configuration option under `zeebe.broker.experimental.partitioning`, more specifically `zeebe.broker.experimental.partitioning.scheme`. This option currently takes the following values: - `ROUND_ROBIN`: When set, this applies the round-robin partition distribution, which corresponds to the distribution explained above on this page. _This is the default option, and requires no extra configuration if you want to use it._ - `FIXED`: When set, this applies a manually configured partition distribution, configured separately. To use the `FIXED` partitioning scheme, _you must provide an exhaustive map of all partitions to a set of brokers_. This is achieved via the `zeebe.broker.experimental.partitioning.fixed` configuration option. The example below outlines a cluster of `5` brokers, `3` partitions, and a replication factor of `3`. ```yaml partitioning: scheme: FIXED fixed: - partitionId: 1 nodes: - nodeId: 0 - nodeId: 2 - nodeId: 4 - partitionId: 2 nodes: - nodeId: 1 - nodeId: 3 - nodeId: 4 - partitionId: 3 nodes: - nodeId: 0 - nodeId: 2 - nodeId: 3 ``` This configuration will produce the following distribution: | | Node 0 | Node 1 | Node 2 | Node 3 | Node 4 | | ----------: | :----: | :----: | :----: | :----: | :----: | | Partition 1 | X | | X | | X | | Partition 2 | | X | | X | X | | Partition 3 | X | | X | X | | ## Validation Each broker performs reasonableness checks on the `FIXED` configuration provided. Namely, the configuration must uphold the following conditions: - All partitions _must be explicitly configured_. - All partitions configured must have valid IDs, i.e. between 1 and `zeebe.broker.cluster.partitionsCount`. - All partitions must configure exactly the replicas count, i.e. `zeebe.broker.cluster.replicationFactor`. - All nodes configured for a partition have a valid node ID, i.e. between 0 and `zeebe.broker.cluster.clusterSize - 1`. - If priority election is enabled, all priorities configured for a partition are different. The broker will fail to start if any of these conditions are not met. ## Priority election If you're using the priority election feature, you must also specify the priorities of each broker. In fact, the broker will fail to start if the nodes do not have different priorities, as otherwise you may encounter lengthy election loops. Here is the same example configuration as above, but this time with priorities configured: ```yaml partitioning: scheme: FIXED fixed: - partitionId: 1 nodes: - nodeId: 0 priority: 1 - nodeId: 2 priority: 2 - nodeId: 4 priority: 3 - partitionId: 2 nodes: - nodeId: 1 priority: 1 - nodeId: 3 priority: 3 - nodeId: 4 priority: 2 - partitionId: 3 nodes: - nodeId: 0 priority: 3 - nodeId: 2 priority: 2 - nodeId: 3 priority: 1 ``` :::note The only condition is that the priorities for the nodes of a given partition must be different from one another. We recommend, however, that you use a simple monotonic increase from 1 to the replica count, as shown above. ::: --- ## Gateway health probes The health status for a standalone gateway is available at `{zeebe-gateway}:9600/actuator/health`. The following health indicators are enabled by default: - **Gateway Started** - Checks if the gateway is running (i.e. not currently starting and not yet shut down). - **Gateway Responsive** - Checks if the gateway can handle a request within a given timeout. - **Gateway Cluster Awareness** - Checks if the gateway is aware of other nodes in the cluster. - **Gateway Partition Leader Awareness** - Checks if the gateway is aware of partition leaders in the cluster. - **Disk Space** - Checks that the free disk space is greater than 10 MB. - **Memory** - Checks that at least 10% of max memory (heap) is still available. Health indicators are set to sensible defaults. For specific use cases, it might be necessary to customize health probes. ## Startup probe The started probe is available at `{zeebe-gateway}:9600/actuator/health/startup`. In the default configuration this is merely an alias for the **Gateway Started** health indicator. Other configurations are possible (see below). ## Liveness probe The liveness probe is available at `{zeebe-gateway}:9600/actuator/health/liveness`. It is based on the health indicators mentioned above. In the default configuration, the liveness probe is comprised of the following health indicators: - **Gateway Started** - Checks if the gateway is running (i.e. not currently starting and not yet shut down). - **Liveness Gateway Responsive** - Checks if the gateway can handle a request within an ample timeout, but will only report a `DOWN` health status after the underlying health indicator is down for more than 10 minutes. - **Liveness Gateway Cluster Awareness** - Based on gateway cluster awareness, but will only report a `DOWN` health status after the underlying health indicator is down for more than five minutes. - **Liveness Gateway Partition Leader Awareness** - Based on gateway partition leader awareness, but will only report a `DOWN` health status after the underlying health indicator is down for more than five minutes. - **Liveness Disk Space** - Checks that the free disk space is greater than 1 MB. - **Liveness Memory** - Checks that at least 1% of max memory (heap) is still available. :::note Health indicators with the _liveness_ prefix are intended to be customized for the liveness probe. This allows defining tighter thresholds (e.g. for free memory 1% for liveness vs. 10% for health), as well as adding tolerance for short downtimes (e.g. gateway has no awareness of other nodes in the cluster for more than five minutes). ::: ## Customizing health probes Global settings for all health indicators: - `management.health.defaults.enabled=true` - Enables (default) or disables all health indicators. - `management.endpoint.health.show-details=always/never` - Toggles whether a summary or details (default) of the health indicators will be returned. ### Startup probe Settings for started probe: - `management.endpoint.health.group.startup.show-details=never` - Toggles whether a summary (default) or details of the startup probe will be returned. - `management.endpoint.health.group.startup.include=gatewayStarted` - Defines which health indicators are included in the startup probe. ### Liveness probe Settings for liveness probe: - `management.endpoint.health.group.liveness.show-details=never` - Toggles whether a summary (default) or details of the liveness probe will be returned. - `management.endpoint.health.group.liveness.include=gatewayStarted,livenessGatewayResponsive,livenessGatewayClusterAwareness,livenessGatewayPartitionLeaderAwareness,livenessDiskSpace,livenessMemory` - Defines which health indicators are included in the liveness probe. :::note The individual contributing health indicators of the liveness probe can be configured as well (see below). ::: ### Gateway started Settings for gateway started health indicator: - `management.health.gateway-started.enabled=true` - Enables (default) or disables this health indicator. ### Gateway responsive Settings for gateway responsiveness health indicator: - `management.health.gateway-responsive.enabled=true` - Enables (default) or disables this health indicator. - `management.health.gateway-responsive.requestTimeout=500ms` - Defines the timeout for the request; if the test completes before the timeout, the health status is `UP`, otherwise it is `DOWN`. - `management.health.liveness.gateway-responsive.requestTimeout=5s` - Defines the timeout for the request for liveness probe; if the request completes before the timeout, the health status is `UP`. - `management.health.liveness.gateway-responsive.maxdowntime=10m` - Defines the maximum downtime before the liveness health indicator for responsiveness will flip. ### Gateway cluster awareness Settings for gateway cluster awareness health indicator: - `management.health.gateway-clusterawareness.enabled=true` - Enables (default) or disables this health indicator (and its liveness counterpart). - `management.health.liveness.gateway-clusterawareness.maxdowntime=5m` - Defines the maximum downtime before the liveness health indicator for cluster awareness will flip. In other words, this health indicator will report `DOWN` after the gateway was unaware of other members in the cluster for more than five minutes. ### Gateway partition leader awareness Settings for gateway partition leader awareness health indicator: - `management.health.gateway-partitionleaderawareness.enabled=true` - Enables (default) or disables this health indicator (and its liveness counterpart). - `management.health.liveness.gateway-partitionleaderawareness.maxdowntime=5m` - Defines the maximum downtime before the liveness health indicator for partition leader awareness will flip. In other words, this health indicator will report `DOWN` after the gateway was unaware of partition leaders for more than five minutes. ### Disk space This is arguably the least critical health indicator given the standalone gateway does not write to disk. The only exception may be the writing of log files, which depend on the log configuration. Settings for disk space health indicator: - `management.health.diskspace.enabled=true` - Enables (default) or disables this health indicator (and its liveness counterpart). - `management.health.diskspace.threshold=10MB` - Defines the threshold for the required free disk space. - `management.health.diskspace.path=.` - Defines the path for which the free disk space is examined. - `management.health.liveness.diskspace.threshold=1MB` - Defines the threshold for the required free disk space for liveness. - `management.health.liveness.diskspace.path=.` - Defines the path for which the free disk space for liveness is examined. ### Memory This health indicator examines free memory (heap). Settings for memory health indicator: - `management.health.memory.enabled=true` - Enables (default) or disables this health indicator (and its liveness counterpart). - `management.health.memory.threshold=0.1` - Defines the threshold for the required free memory. The default is 0.1 which is interpreted as 10% of max memory. - `management.health.liveness.memory.threshold=0.01` - Defines the threshold for the required free memory for liveness. The default is 0.01 which is interpreted as 10 of max memory. --- ## Gateway configuration The Zeebe Gateway can be configured similarly to the broker via the `application.yaml` file or environment variables. A complete gateway configuration template is available in the [Zeebe repository](https://github.com/camunda/camunda/blob/main/zeebe/gateway/src/test/resources/configuration/gateway.default.yaml). :::info Configure an embedded gateway When you deploy with Helm (Camunda 8.8+), the gateway runs embedded in the broker by default. In that case, use `zeebe.broker.gateway.*` instead of `zeebe.gateway.*` for any configuration options below, and `ZEEBE_BROKER_GATEWAY_*` instead of `ZEEBE_GATEWAY_*` for environment variables. For a standalone gateway, keep using the `zeebe.gateway.*` / `ZEEBE_GATEWAY_*` prefix. ::: ## Conventions Take the following conventions into consideration when working with the gateway configuration. ### Byte sizes Buffers and data values referencing sizing must be specified as strings and follow the following format: "10U" where U (unit) must be replaced with KB = Kilobytes, MB = Megabytes or GB = Gigabytes. If unit is omitted then the default unit is simply bytes. For example, `sendBufferSize = "16MB"` creates a buffer of 16 Megabytes. ### Time units Timeouts and intervals must be specified either in the standard ISO-8601 format used by `java.time.Duration`, or as strings with the following format: "VU", where: - V is a numerical value (e.g. 1, 5, 10, etc.) - U is the unit, one of: ms = Milliseconds, s = Seconds, m = Minutes, or h = Hours ### Paths Relative paths are resolved relative to the installation directory of the broker. ## Configuration The following sections describe Zeebe Gateway configuration options. Each section includes a table with: - Environment variables - Application properties - A description - Default values The configuration name appears as the section header. Table fields show the property used to set the configuration. For deployments, environment variables are typically easier to use. Table entries use the standalone prefix (`ZEEBE_GATEWAY_*`). For an embedded gateway, replace this prefix with `ZEEBE_BROKER_GATEWAY_*` as described earlier. If you deploy Camunda 8 with Helm (the recommended approach), you can configure the gateway using Helm chart parameters. See the [Zeebe Gateway Helm chart configuration options](https://artifacthub.io/packages/helm/camunda/camunda-platform#zeebe-gateway-parameters). :::note The Zeebe Gateway is a Spring Boot application. In addition to the configuration options documented here, you can use standard Spring Boot properties. For example, the REST server can be configured using `server.*` properties, and the management server (for actuator endpoints) using `management.server.*`. See the [Spring Boot application properties reference](https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html). ::: ### server The `server` configuration allows you to configure the main REST server. Below are a few common ones, but you can find a more exhaustive list [in the official Spring documentation](https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#appendix.application-properties.server). | Field | Description | Example value | | ----- | ------------------------------------------------------------------------------------------------------------------------- | ------------- | | host | Sets the host the REST server binds to. This setting can also be overridden using the environment variable `SERVER_HOST`. | 0.0.0.0 | | port | Sets the port the REST server binds to. This setting can also be overridden using the environment variable `SERVER_PORT`. | 8080 | #### server.compression | Field | Description | Example value | | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | enabled | If true, enables compression of responses for the Orchestration Cluster REST API. This setting can also be overridden using the environment variable `SERVER_COMPRESION_ENABLED`. | false | #### server.ssl Allows you to configure the SSL security for the REST server. | Field | Description | Example value | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | enabled | If true, enables TLS for the Orchestration Cluster REST API. This setting can also be overridden using the environment variable `SERVER_SSL_ENABLED`. | false | | certificate | The path to a PEM encoded certificate. This setting can also be overridden using the environment variable `SERVER_SSL_CERTIFICATE`. | | | certificate-private-key | The path to a PKCS1 or PKCS8 private key for the configured certificate. This setting can also be overridden using the environment variable `SERVER_SSL_CERTIFICATEPRIVATEKEY`. | | #### YAML snippet ```yaml server: host: 0.0.0.0 port: 8080 compression: enabled: true ssl: enabled: true certificate: /path/to/my/cert.pem certificate-private-key: /path/to/my/private.key ``` ### server.servlet | Field | Description | Example value | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- | | context-path | The context path prefix for all REST API requests. For example, if you configure `/zeebe`, then the client's REST address would be `http://localhost:8080/zeebe`. This setting can also be overridden using the environment variable `SERVER_SERVLET_CONTEXTPATH`. | `/` | #### YAML snippet ```yaml server.servlet: context-path: / ``` ### management.server The `management.server` configuration allows you to configure the management server. | Field | Description | Example value | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | host | Sets the host the management server binds to. This setting can also be overridden using the environment variable `MANAGEMENT_SERVER_HOST`. | 0.0.0.0 | | port | Sets the port the management server binds to. This setting can also be overridden using the environment variable `MANAGEMENT_SERVER_PORT`. | 9600 | | base-path | The context path prefix for all management endpoints. For example, if you configure `/zeebe`, your actuator endpoints will be at `http://localhost:9600/zeebe/actuator/configprops`. This setting can also be overridden using the environment variable `MANAGEMENT_SERVER_BASEPATH`. | `/` | #### YAML snippet ```yaml management.server: host: 0.0.0.0 port: 9600 base-path: / ``` ### zeebe.gateway.network The network configuration allows configuration of the host and port details for the gateway. | Field | Description | Example value | | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | host | Sets the host the gateway binds to. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_NETWORK_HOST`. | 0.0.0.0 | | port | Sets the port the gateway binds to. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_NETWORK_PORT`. | 26500 | | minKeepAliveInterval | Sets the minimum keep alive interval. This setting specifies the minimum accepted interval between keep alive pings. This value must be specified as a positive integer followed by 's' for seconds, 'm' for minutes, or 'h' for hours. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_NETWORK_MINKEEPALIVEINTERVAL`. | 30s | | maxMessageSize | Sets the maximum size of the incoming and outgoing messages (i.e. commands and events). Apply the same setting on the broker too, see `ZEEBE_BROKER_NETWORK_MAXMESSAGESIZE`. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_NETWORK_MAXMESSAGESIZE`. | 4MB | | socketReceiveBuffer | Sets the size of the socket's receive buffer for the gateway. If omitted defaults to 1MB. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_NETWORK_SOCKETRECEIVEBUFFER`. | 4MB | | socketSendBuffer | Sets the size of the socket's send buffer for the gateway. If omitted defaults to 1MB. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_NETWORK_SOCKETSENDBUFFER`. | 4MB | #### YAML snippet ```yaml network: host: 0.0.0.0 port: 26500 minKeepAliveInterval: 30s maxMessageSize: 4MB socketReceiveBuffer: 4MB socketSendBuffer: 4MB ``` ### zeebe.gateway.cluster As mentioned, the gateway needs to connect to the Zeebe brokers. It is important to configure the cluster's initial contact point to the Zeebe brokers. You may set only one of the Zeebe brokers, but keep in mind that resiliency will be lower than using all the Zeebe brokers available. The corresponding environment variable is called `ZEEBE_GATEWAY_CLUSTER_INITIALCONTACTPOINTS`. It is necessary to use the same cluster name for the broker and gateway. Otherwise, a connection will not be possible. The related configuration property is `zeebe.gateway.cluster.clusterName` and as an environment variable, it is called `ZEEBE_GATEWAY_CLUSTER_CLUSTERNAME`. If you use the Helm charts, both properties are configured for you already. | Field | Description | Example value | | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | | initialContactPoints | Sets initial contact points (brokers), which the gateway should contact. The contact points of the internal network configuration must be specified. The format is [HOST:PORT]. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_INITIALCONTACTPOINTS` specifying a comma-separated list of contact points. | [ 192.168.1.22:26502, 192.168.1.32:26502 ] | | requestTimeout | Sets the timeout of requests sent to the broker cluster. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_REQUESTTIMEOUT`. | 15s | | clusterName | Sets name of the Zeebe cluster to connect to. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_CLUSTERNAME`. | zeebe-cluster | | memberId | Sets the member ID of the gateway in the cluster. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_MEMBERID`. This is a deprecated property; use `camunda.cluster.gateway-id` instead. | gateway | | host | Sets the host the gateway node binds to for internal cluster communication. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_HOST`. | 0.0.0.0 | | port | Sets the port the gateway node binds to for internal cluster communication. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_PORT`. | 26502 | | advertisedHost | Controls the advertised host; if omitted defaults to the host. This is particularly useful if your gateway stands behind a proxy. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_ADVERTISEDHOST`. | 0.0.0.0 | | advertisedPort | Controls the advertised port; if omitted defaults to the port. This is particularly useful if your gateway stands behind a proxy. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_ADVERTISEDPORT`. | 25602 | #### YAML snippet ```yaml cluster: initialContactPoints: 127.0.0.1:26502 requestTimeout: 15s clusterName: zeebe-cluster memberId: gateway host: 0.0.0.0 port: 26502 advertisedHost: 0.0.0.0 advertisedPort: 25602 ``` ### zeebe.gateway.cluster.membership To configure how the gateway connects and distributes information with other nodes (brokers or gateways) via SWIM, the following properties can be used. It might be useful to increase timeouts for setups that encounter a high latency between nodes. | Field | Description | Example value | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | broadcastUpdates | Configure whether to broadcast member updates to all members. If set to `false`, updates will be gossiped among the members. If set to `true`, the network traffic may increase but reduce the time to detect membership changes. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_MEMBERSHIP_BROADCASTUPDATES`. | false | | broadcastDisputes | Configure whether to broadcast disputes to all members. If set to `true`, the network traffic may increase but reduce the time to detect membership changes. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_MEMBERSHIP_BROADCASTDISPUTES`. | true | | notifySuspect | Configure whether to notify a suspect node on state changes. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_MEMBERSHIP_NOTIFYSUSPECT`. | false | | gossipInterval | Sets the interval at which the membership updates are sent to a random member. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_MEMBERSHIP_GOSSIPINTERVAL`. | 250ms | | gossipFanout | Sets the number of members to which membership updates are sent at each gossip interval. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_MEMBERSHIP_GOSSIPFANOUT`. | 2 | | probeInterval | Sets the interval at which to probe a random member. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_MEMBERSHIP_PROBEINTERVAL`. | 1s | | probeTimeout | Sets the timeout for a probe response. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_MEMBERSHIP_PROBETIMEOUT`. | 100ms | | suspectProbes | Sets the number of probes failed before declaring a member is suspect. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_MEMBERSHIP_SUSPECTPROBES`. | 3 | | failureTimeout | Sets the timeout for a suspect member is declared dead. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_MEMBERSHIP_FAILURETIMEOUT`. | 10s | | syncInterval | Sets the interval at which this member synchronizes its membership information with a random member. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_MEMBERSHIP_SYNCINTERVAL`. | 10s | #### YAML snippet ```yaml membership: broadcastUpdates: false broadcastDisputes: true notifySuspect: false gossipInterval: 250ms gossipFanout: 2 probeInterval: 1s probeTimeout: 100ms suspectProbes: 3 failureTimeout: 10s syncInterval: 10s ``` ### zeebe.gateway.cluster.configManager.gossip Configure the parameters used to propagate the dynamic cluster configuration across brokers and gateways. | Field | Description | ExampleValue | | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | | syncDelay | Sets the interval between two synchronization requests to other members of the cluster. This setting can also be overridden using the environment variable ZEEBE_GATEWAY_CLUSTER_CONFIGMANAGER_GOSSIP_SYNCDELAY | 10s | | syncRequestTimeout | Sets the timeout for the synchronization requests. This setting can also be overridden using the environment variable ZEEBE_GATEWAY_CLUSTER_CONFIGMANAGER_GOSSIP_SYNCREQUESTTIMEOUT | 2s | | gossipFanout | Sets the number of cluster members the configuration is gossiped to. This setting can also be overridden using the environment variable ZEEBE_GATEWAY_CLUSTER_CONFIGMANAGER_GOSSIP_GOSSIPFANOUT | 2 | #### YAML snippet ```yaml configManager: gossip: syncDelay: 10s syncRequestTimeout: 2s gossipFanout: 2 ``` ### zeebe.gateway.cluster.security The cluster security configuration options allow securing communication between the gateway and other nodes in the cluster. :::note You can read more about intra-cluster security on [its dedicated page](../security/secure-cluster-communication.md). ::: | Field | Description | Example value | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | enabled | Enables TLS authentication between this gateway and other nodes in the cluster. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_SECURITY_ENABLED`. | false | | certificateChainPath | Sets the path to the certificate chain file. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_SECURITY_CERTIFICATECHAINPATH`. | | | privateKeyPath | Sets the path to the private key file location. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_SECURITY_PRIVATEKEYPATH`. | | | keyStore | Configures the keystore file containing both the certificate chain and the private key; currently only supports PKCS12 format. | | | keyStore.filePath | The path for keystore file; This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_SECURITY_KEYSTORE_FILEPATH`. | /path/key.pem | | keyStore.password | Sets the password for the keystore file, if not set it is assumed there is no password; This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_SECURITY_KEYSTORE_PASSWORD` | changeme | #### YAML snippet ```yaml security: enabled: false certificateChainPath: null privateKeyPath: null keyStore: filePath: null password: null ``` ### zeebe.gateway.cluster.security.authentication | Field | Description | Example value | | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | mode | Controls which authentication mode is active; supported modes are `none` and `identity`. If `identity` is set, authentication will be done using [camunda-identity](/self-managed/components/management-identity/overview.md), which needs to be configured in the corresponding subsection. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_SECURITY_AUTHENTICATION_MODE`. | none | #### YAML snippet ```yaml security: authentication: mode: none ``` ### zeebe.gateway.cluster.security.authentication.identity :::note The Zeebe configuration properties for Camunda Identity are deprecated as of version `8.4.0`. Use the dedicated Camunda Identity properties or the [corresponding environment variables](/self-managed/components/management-identity/miscellaneous/configuration-variables.md#core-configuration). ::: | Field | Description | Example value | | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | | issuerBackendUrl | The URL to the auth provider backend, used to validate tokens. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_SECURITY_AUTHENTICATION_IDENTITY_ISSUERBACKENDURL`. | http://keycloak:18080/auth/realms/camunda-platform | | audience | The required audience of the auth token. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_SECURITY_AUTHENTICATION_IDENTITY_AUDIENCE`. | zeebe-api | | type | The identity auth type to apply, one of `keycloak` or `auth0`. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_SECURITY_AUTHENTICATION_IDENTITY_TYPE`. | keycloak | | baseUrl | The URL to the Identity instance. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_SECURITY_AUTHENTICATION_IDENTITY_BASEURL`. | http://identity:8084 | #### YAML snippet ```yaml security: authentication: mode: identity identity: issuerBackendUrl: http://keycloak:18080/auth/realms/camunda-platform audience: zeebe-api type: keycloak ``` ### zeebe.gateway.cluster.messageCompression To configure the compression algorithm for all messages sent between the gateway and the brokers, the following property can be set. Available options are NONE, GZIP, and SNAPPY. This feature is useful when the network latency between the nodes is very high (for example, when nodes are deployed in different data centers). When latency is high, the network bandwidth is severely reduced. Therefore, enabling compression helps improve the throughput. You need to decide between reducing bandwidth or reducing resources required for compression. :::caution When this flag is enabled, you must also enable compression in the standalone broker configuration. When there is no latency enabling, this may have a performance impact. ::: | Field | Description | Example value | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | messageCompression | Configure compression algorithm for all messages sent between the gateway and the brokers. Available options are NONE, GZIP, and SNAPPY. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_CLUSTER_MESSAGECOMPRESSION`. | NONE | #### YAML snippet ```yaml messageCompression: NONE ``` ### zeebe.gateway.threads To handle many concurrent incoming requests, the user can do two things: scale the deployed gateways (if the standalone mode is in use), or increase the used resources and threads. The Zeebe Gateway uses one thread by default, but this should be set to a higher number if the gateway doesn’t exhaust its available resources and doesn’t keep up with the load. The corresponding environment variables look like this: `ZEEBE_GATEWAY_THREADS_MANAGEMENTTHREADS`. During benchmarking and when increasing the thread count, it may also make sense to increase the given resources, which are quite small in the Helm chart. For high availability and redundancy, two Zeebe Gateways are deployed by default with the Helm charts. To change that amount, set `zeebe-gateway.replicas=2` to a different number. Increasing the number of gateway replicas to more than one enables the possibility for quick failover; in the case one gateway dies, the remaining gateway(s) can handle the traffic. A separate thread pool is used to run the gRPC business logic. The thread pool is elastic (meaning it will start/stop threads dynamically), but will always keep a minimum number of threads, and only start up to a maximum number of threads. By default, this range is from one thread per core, up to two threads per core. To explore how the gateway behaves, or what it does, metrics can be consumed. By default, the gateway exports Prometheus metrics, which can be scrapped under `:9600/actuator/prometheus`. | Field | Description | Example value | | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | managementThreads | Sets the number of threads the gateway will use to communicate with the broker cluster. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_THREADS_MANAGEMENTTHREADS`. | 1 | | grpcMinThreads | Sets the minimum number of threads in the gRPC thread pool. Only accepts static values; defaults to the number of cores available. | 8 | | grpcMaxThreads | Sets the maximum number of threads in the gRPC thread pool. Only accepts static values; defaults to twice the number of cores available. | 16 | #### YAML snippet ```yaml threads: managementThreads: 1 grpcMinThreads: 8 grpcMaxThreads: 16 ``` ### zeebe.gateway.security The client security configuration options allow securing the communication between a gateway and clients. :::note You can read more about client-gateway security on [its dedicated page](../security/secure-client-communication.md). ::: | Field | Description | Example value | | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | enabled | Enables TLS authentication between clients and the gateway. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_SECURITY_ENABLED`. | false | | certificateChainPath | Sets the path to the certificate chain file. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_SECURITY_CERTIFICATECHAINPATH`. | | | privateKeyPath | Sets the path to the private key file location. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_SECURITY_PRIVATEKEYPATH`. | | #### YAML snippet ```yaml security: enabled: false certificateChainPath: privateKeyPath: ``` ### zeebe.gateway.longPolling It's possible to configure gateway long-polling behavior. Read more on long-polling behavior [here](../../../../../components/concepts/job-workers.md#long-polling). | Field | Description | Example value | | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | enabled | Enables long polling for available jobs. This setting can also be overridden using the environment `variable ZEEBE_GATEWAY_LONGPOLLING_ENABLED`. | true | | timeout | Set the timeout for long polling in milliseconds. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_LONGPOLLING_TIMEOUT`. | 10000 | | probeTimeout | Set the probe timeout for long polling in milliseconds. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_LONGPOLLING_PROBETIMEOUT`. | 10000 | | minEmptyResponses | Set the number of minimum empty responses, a minimum number of responses with jobCount of 0 infers that no job are available. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_LONGPOLLING_MINEMPTYRESPONSES`. | 3 | #### YAML snippet ```yaml longPolling: enabled: true timeout: 10000 probeTimeout: 10000 minEmptyResponses: 3 ``` ### zeebe.gateway.interceptors It is possible to intercept requests in the gateway, which can be configured via environment variables or the `application.yaml` file. For more details, read about [interceptors](/self-managed/components/orchestration-cluster/zeebe/zeebe-gateway/interceptors.md). Each interceptor should be configured with the values described below: Field Description Example value id Identifier for this interceptor. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_INTERCEPTORS_0_ID`. jarPath Path (relative or absolute) to the JAR file containing the interceptor class and its dependencies. All classes must be compiled for the same language version as Zeebe or lower. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_INTERCEPTORS_0_JARPATH`. className Entry point of the interceptor, a class which must: implement io.grpc.ServerInterceptor have public visibility have a public default constructor (i.e. no-arg constructor) This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_INTERCEPTORS_0_CLASSNAME`. #### YAML snippet ```yaml interceptors: id: null jarPath: null className: null ``` ### zeebe.gateway.filters It is possible to filter REST API requests in the gateway, which can be configured via environment variables or the `application.yaml` file. For more details, read about [filters](/self-managed/components/orchestration-cluster/zeebe/zeebe-gateway/filters.md). Each filter should be configured with the values described below: Field Description Example value id Identifier for this filter. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_FILTERS_0_ID`. jarPath Path (relative or absolute) to the JAR file containing the filter class and its dependencies. All classes must be compiled for the same language version as Zeebe or lower. This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_FILTERS_0_JARPATH`. className Entry point of the filter, a class which must: implement jakarta.servlet.Filter have public visibility have a public default constructor (i.e. no-arg constructor) This setting can also be overridden using the environment variable `ZEEBE_GATEWAY_FILTERS_0_CLASSNAME`. #### YAML snippet ```yaml filters: id: null jarPath: null className: null ``` --- ## Priority election Priority election is an alternative to the default raft leader election, where leader election is implemented by a random timer-based algorithm. It aims to achieve a more uniform leader distribution by assigning each node a priority per partition and modifying the election algorithm to ensure nodes with higher priority have a higher chance of becoming leader. ## Configuration Enable priority election by setting `zeebe.broker.cluster.raft.enablePriorityElection=true` in your config or by setting the equivalent environment variable `ZEEBE_BROKER_CLUSTER_RAFT_ENABLEPRIORITYELECTION=true`. If you are using the fixed partitioning scheme (experimental), you may need [additional configuration](fixed-partitioning.md#priority-election). ## Limitations With priority election enabled, election latency and thus failover time increases. The result of a leader election is not deterministic, and priority election can only increase the chance of having a uniform leader distribution, not guarantee it. Factors such as high load can prevent high-priority nodes from becoming the leader. --- ## Camunda Exporter The Camunda Exporter exports Zeebe records directly to Elasticsearch or OpenSearch. Unlike the Elasticsearch and OpenSearch exporters, it exports records in the format required by Operate and Tasklist, so you don’t need to configure additional importers or data transformations. Using the Camunda Exporter can increase process instance throughput and reduce the latency of changes appearing in Operate and Tasklist. :::note When exporting, indexes are created as required and not recreated if they already exist. However, disabling the exporter does not delete indexes. Administrators must handle deletions. You can configure a [retention policy](./camunda-exporter.md?configuration=retention#options) to automatically delete data after a set number of days. ::: ## Configuration Camunda Exporter is enabled by default if secondary storage is configured to use Elasticsearch or OpenSearch. See the properties prefixed with `CAMUNDA_DATA_SECONDARYSTORAGE` in [secondary-storage configuration properties](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#data---secondary-storage). To connect to an external Elasticsearch or OpenSearch cluster, configure the secondary storage endpoint with `camunda.data.secondary-storage.elasticsearch.url` or `camunda.data.secondary-storage.elasticsearch.urls`. If you're using OpenSearch, use `camunda.data.secondary-storage.opensearch.url` or `camunda.data.secondary-storage.opensearch.urls` instead. The Camunda Exporter reuses that secondary storage connection automatically. If you're deploying with Helm, configure the external cluster in your Elasticsearch or OpenSearch values, then set the secondary storage type for Orchestration Cluster. For a complete Helm example, see [configure secondary storage](/self-managed/concepts/secondary-storage/configuring-secondary-storage.md#configuration-options) and [using external Elasticsearch](/self-managed/deployment/helm/configure/database/elasticsearch/using-external-elasticsearch.md). :::info Helm values mapping The option names in the tabs below (for example, `rolloverInterval`) are exporter option names, not top-level Helm values keys. In Helm, configure these as Orchestration Cluster application properties using `orchestration.extraConfiguration` (or `orchestration.configuration`). For example, set history rollover using: - `camunda.data.secondary-storage.elasticsearch.history.rollover-interval` - `camunda.data.secondary-storage.opensearch.history.rollover-interval` ::: ### Options Helm property path prefix for these options: `camunda.data.secondary-storage.{elasticsearch|opensearch}.` Use `url` or `urls` to define the Elasticsearch or OpenSearch endpoint. The `connect` options in this section only fine-tune how Camunda connects to the secondary storage cluster after you configure the endpoint. :::note Please refer to [supported environments](/reference/supported-environments.md#camunda-8-self-managed) to find out which versions of Elasticsearch and/or OpenSearch are supported in a Camunda 8 Self-Managed setup. ::: | Option | Description | Default | | -------------- | ----------------------------------------------------------------------------------------------------------------------------- | --------------------------- | | dateFormat | Defines a custom date format that should be used for fetching date data from the engine (should be the same as in the engine) | yyyy-MM-dd'T'HH:mm:ss.SSSZZ | | socketTimeout | Defines the socket timeout in milliseconds, which is the timeout for waiting for data. | | | connectTimeout | Determines the timeout in milliseconds until a connection is established. | | :::note If you are using `opensearch` on AWS, the AWS SDK's [DefaultCredentialsProvider](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/auth/credentials/DefaultCredentialsProvider.html) is used for authentication. For more details on configuring credentials, refer to the [AWS SDK documentation](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/credentials-chain.html#credentials-default). ::: Helm property path prefix for these options: `camunda.data.secondary-storage.{elasticsearch|opensearch}.` | Option | Description | Default | | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | numberOfShards | The number of [shards](https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules.html#_static_index_settings) used for each created index. | 1 | | numberOfReplicas | The number of shard [replicas](https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules.html#dynamic-index-settings) used for created index. | 0 | | variableSizeThreshold | Defines a threshold for variable size. Variables exceeding this threshold are split into two properties: `FULL_VALUE` (full content, not indexed) and `VALUE` (truncated content, indexed). | 8191 | | shardsByIndexName | A map where the key is the index name and the value is the number of shards, allowing you to override the default `numberOfShards` setting for specific indices. | | | replicasByIndexName | A map where the key is the index name and the value is the number of replicas, allowing you to override the default `numberOfReplicas` setting for specific indices. | | Helm property path prefix for these options: `camunda.data.secondary-storage.{elasticsearch|opensearch}.bulk.` To avoid too many expensive requests to the Elasticsearch/OpenSearch cluster, the exporter performs batch updates by default. The size of the batch, along with how often it should be flushed (regardless of size) can be controlled by configuration. | Option | Description | Default | | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | delay | Delay, in seconds, before force flush of the current batch. This ensures that even when we have low traffic of records, we still export every once in a while. | `1` | | size | The amount of records a batch should have before we flush the batch. | `1000` | With the default configuration, the exporter will aggregate records and flush them to Elasticsearch/OpenSearch: 1. When it has aggregated 1000 records. 2. One seconds have elapsed since the last flush (regardless of how many records were aggregated). Helm property path prefix for these options: `camunda.data.secondary-storage.retention.` A retention policy can be set up to delete old data. When enabled, this creates an Index Lifecycle Management (ILM) Policy that deletes the data after the specified `minimumAge`. All index templates created by this exporter apply the created ILM Policy. | Option | Description | Default | | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | | enabled | If `true` the ILM Policy is created and applied to the index templates. | `false` | | minimumAge | Specifies how old the data must be, before the data is deleted as a duration. | `30d` | | policyName | The name of the created and applied ILM policy. | `camunda-retention-policy` | | usageMetricsMinimumAge | Specifies how old the usage metrics data must be, before the data is deleted as a duration. Applies to `camunda-usage-metric-8.8.0_` and `camunda-usage-metric-tu-8.8.0_` indices. | `730d` | | usageMetricsPolicyName | The name of the created and applied usage metrics ILM policy. | `camunda-usage-metrics-retention-policy` | | applyPolicyJobInterval | The interval at which the ILM policy is periodically applied to all historical indices (starting from version 8.8.1). | `PT1H` | :::note The duration can be specified in days `d`, hours `h`, minutes `m`, seconds `s`, milliseconds `ms`, and/or nanoseconds `nanos`. ::: Helm property path prefix for these options: `camunda.data.secondary-storage.{elasticsearch|opensearch}.history.` For example, `rolloverInterval` maps to: `camunda.data.secondary-storage.{elasticsearch|opensearch}.history.rollover-interval`. To keep the main runtime index performant, documents are periodically moved into historical indices. The history can be configured as follows: | Option | Description | Default | | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | | elsRolloverDateFormat | Defines how date values are formatted for historical indices using Java DateTimeFormatter syntax. If no format is specified, the first date format defined in the field mapping is used. | `date` | | rolloverInterval | The rollover period before an active index is rolled over. This means that `rolloverInterval` is the time gap between updates of historical indexes, therefore for an index `index-abc` and a `rolloverInterval` of 7 days (`7d`), we will have the historical indexes `index-abc-2025-01-01`, `index-abc-2025-01-08`, and so on. The `elsRolloverDateFormat` must have sufficient resolution to compute the `rolloverInterval`. For example, if the `rolloverInterval` is `1h`, then the `elsRolloverDateFormat` should be `yyyy-MM-dd-HH`. Additionally, `rolloverInterval` cannot use seconds (`s`) or minutes (`m`) as units. | `1d` | | usageMetricsRolloverInterval | The rollover period for usage metric indices before an active index is rolled over. Behaves the same way as the `rolloverInterval` but applies only to `usage-metric`, `usage-metric-tu` indices and only values of `1-4w` and `1M` are allowed. | `1M` | | rolloverBatchSize | The maximum number of instances per batch to be archived. | `100` | | waitPeriodBeforeArchiving | Grace period during which completed process instances are excluded from archiving. For example, with a value of `1h`, any process instances completed within the last hour will not be archived. | `1h` | | delayBetweenRuns | Time in milliseconds between archiving runs for completed process instances. | `2000` | | maxDelayBetweenRuns | The maximum delay between archive runs when using an exponential backoff strategy in case of unsuccessful archiving attempts. | `60000` | | processInstanceEnabled | If `true`, enables the archiving of the completed process instances and their related objects. | `true` | | processInstanceRetentionMode | Controls the retention behavior for process instance data in secondary storage. Allowed values: `PI_HIERARCHY`, `PI_HIERARCHY_IGNORE_LEGACY`, `PI`. | `PI_HIERARCHY` | | retention | Refer to [Retention](./camunda-exporter.md?configuration=retention#options) for retention configuration options. | | Helm property path prefix for these options: `camunda.data.secondary-storage.{elasticsearch|opensearch}.batch-operations.` Other miscellaneous properties: | Option | Description | Default | | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | batchOperation.exportItemsOnCreation | Defines whether the pending items of a started batch operation should be exported from the beginning. For very large batch operations involving more than 100,000 process instances, this can cause temporary performance issues due to the high volume of document insertions. If set to `false`, the "has pending batch operations" spinner in the Operate UI will not function properly. | `true` | ## Example Here is an example configuration of the exporter: ```yaml --- exporters: # Camunda Exporter ---------- # An example configuration for the camunda exporter: # # These setting can also be overridden using the environment variables "ZEEBE_BROKER_EXPORTERS_CAMUNDAEXPORTER_..." # To convert a YAML formatted variable to an environment variable, start with the top-level property and separate every nested property with an underscore (_). # For example, the property "zeebe.broker.exporters.camundaexporter.args.index.numberOfShards" would be converted to "ZEEBE_BROKER_EXPORTERS_CAMUNDAEXPORTER_ARGS_INDEX_NUMBEROFSHARDS". # camundaexporter: args: connect: dateFormat: yyyy-MM-dd'T'HH:mm:ss.SSSZZ socketTimeout: 1000 connectTimeout: 1000 bulk: delay: 5 size: 1000 index: numberOfShards: 3 numberOfReplicas: 0 history: elsRolloverDateFormat: "date" rolloverInterval: "1d" rolloverBatchSize: 100 waitPeriodBeforeArchiving: "1h" delayBetweenRuns: 2000 maxDelayBetweenRuns: 60000 processInstanceEnabled: true retention: enabled: false minimumAge: 30d policyName: camunda-retention-policy usageMetricsMinimumAge: 730d usageMetricsPolicyName: camunda-usage-metrics-retention-policy applyPolicyJobInterval: PT1H batchOperation: exportItemsOnCreation: true createSchema: true ``` --- ## Elasticsearch exporter :::note For supported Elasticsearch versions in Camunda 8 Self-Managed, see [supported environments](/reference/supported-environments.md#camunda-8-self-managed). Starting with Camunda 8.8, Camunda uses the [Camunda exporter](/self-managed/components/orchestration-cluster/zeebe/exporters/camunda-exporter.md) to consume new records. Records from 8.7 and earlier are consumed only during migration. The Elasticsearch and OpenSearch exporters remain fully usable after migration (for example, for existing setups, Optimize, or other custom use cases). Their functionality is not limited to the migration period. From 8.9 onward, the Elasticsearch exporter also supports Optimize-focused export filters (for example, variable-name filters, variable-type filters, BPMN process include/exclude, and an Optimize mode flag). For Optimize-specific guidance and recommended settings, see [Camunda 8 system configuration](../../../optimize/configuration/system-configuration-platform-8.md). ::: The Zeebe Elasticsearch exporter acts as a bridge between [Zeebe](https://camunda.com/platform/zeebe/) and [Elasticsearch](https://www.elastic.co/products/elasticsearch) by exporting records written to Zeebe streams as documents into several indices. ## Concept The exporter operates on the idea that it should perform as little as possible on the Zeebe side of things. In other words, you can think of the indexes into which the records are exported as a staging data warehouse. Any enrichment or transformation on the exported data should be performed by your own ETL jobs. When configured to do so, the exporter will automatically create an index per record value type (see the value type in the Zeebe protocol). Each of these indexes has a corresponding pre-defined mapping to facilitate data ingestion for your own ETL jobs. You can find those as templates in [the resources folder of the exporter's source code](https://github.com/camunda/camunda/tree/main/zeebe/exporters/elasticsearch-exporter/src/main/resources). :::note The indexes are created as required, and will not be created twice if they already exist. However, once disabled, they will not be deleted (that is up to the administrator.) Similarly, data is never deleted by the exporter, and must be deleted by the administrator when it is safe to do so. A [retention](#retention) policy can be configured to automatically delete data after a certain number of days. ::: ## Configuration :::note As the exporter is packaged with Zeebe, it is not necessary to specify a `jarPath`. ::: The exporter can be enabled by configuring it with the `classpath` in the broker settings. For a Spring Boot application or Camunda 8 with unified configuration: **Application config (YAML):** ```yaml camunda: data: exporters: elasticsearch: class-name: io.camunda.zeebe.exporter.ElasticsearchExporter args: # Refer to the table below for the available args options ``` **Environment variables:** Set environment variables in the format `CAMUNDA_DATA_EXPORTERS_ELASTICSEARCH_...` (for example, `CAMUNDA_DATA_EXPORTERS_ELASTICSEARCH_URL`). **Helm:** Add the same configuration under `orchestration.configuration` in your `values.yaml` file. :::warning Do not configure both legacy (`zeebe.broker.exporters.*`) and unified (`camunda.data.exporters.*`) exporter properties at the same time. Exporter properties are a breaking-change mapping in unified configuration, and the application fails to start until legacy properties are removed. ::: The exporter can be configured by providing `args`. The table below explains all the different options, and the default values for these options: | Option | Description | Default | | ----------------------- | ---------------------------------------------------------------------------------------- | ----------------------- | | url | Valid URLs as comma-separated string. | `http://localhost:9200` | | request-timeout-ms | Request timeout (in ms) for Elasticsearch. client | `30000` | | index | Refer to [index](#index) for the index configuration options. | | | bulk | Refer to [bulk](#bulk) for the bulk configuration options. | | | retention | Refer to [retention](#retention) for the retention configuration options. | | | authentication | Refer to [authentication](#authentication) for the authentication configuration options. | | | include-enabled-records | If `true` all enabled record types will be exported. | `false` | In most cases, you will not be interested in exporting every single record produced by a Zeebe cluster, but rather only a subset of them. This can also be configured to limit the kinds of records being exported (for example, only events, no commands), and the value type of these records (for example, only job and process values). | Option | Description | Default | | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------- | | prefix | This prefix will be appended to every index created by the exporter; must not contain `_` (underscore). | zeebe-record | | create-template | If `true` missing indexes will be created automatically. | `true` | | index-suffix-date-pattern | This suffix will be appended to every index created by the exporter; The pattern is based on the Java [DateTimeFormatter](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/time/format/DateTimeFormatter.html) and supports the same syntax. This is useful when indexes should be created in a different interval, like hourly instead of daily. | `"yyyy-MM-dd'"` | | number-of-shards | The number of [shards](https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules.html#_static_index_settings) used for each new record index created. | 3 | | number-of-replicas | The number of shard [replicas](https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules.html#dynamic-index-settings) used for each new record index created. | 0 | | command | If `true` command records will be exported | `false` | | event | If `true` event records will be exported | `true` | | rejection | If `true` rejection records will be exported | `false` | | checkpoint | If `true` records related to checkpoints will be exported | `false` | | command-distribution | If `true` records related to command distributions will be exported | `true` | | decision | If `true` records related to decisions will be exported | `true` | | decision-evaluation | If `true` records related to decision evaluations will be exported | `true` | | decision-requirements | If `true` records related to decisionRequirements will be exported | `true` | | deployment | If `true` records related to deployments will be exported | `true` | | deployment-distribution | If `true` records related to deployment distributions will be exported | `true` | | error | If `true` records related to errors will be exported | `true` | | escalation | If `true` records related to escalations will be exported | `true` | | form | If `true` records related to forms will be exported | `true` | | incident | If `true` records related to incidents will be exported | `true` | | job | If `true` records related to jobs will be exported | `true` | | job-batch | If `true` records related to job batches will be exported | `false` | | message | If `true` records related to messages will be exported | `true` | | message-batch | If `true` records related to message batches will be exported | `false` | | message-subscription | If `true` records related to message subscriptions will be exported | `true` | | message-start-event-subscription | If `true` records related to message start event subscriptions will be exported | `true` | | process | If `true` records related to processes will be exported | `true` | | process-event | If `true` records related to process events will be exported | `false` | | process-instance | If `true` records related to process instances will be exported | `true` | | process-instance-batch | If `true` records related to process instances batches will be exported | `false` | | process-instance-creation | If `true` records related to process instance creations will be exported | `true` | | process-instance-migration | If `true` records related to process instance migrations will be exported | `true` | | process-instance-modification | If `true` records related to process instance modifications will be exported | `true` | | process-message-subscription | If `true` records related to process message subscriptions will be exported | `true` | | resource-deletion | If `true` records related to resource deletions will be exported | `true` | | signal | If `true` records related to signals will be exported | `true` | | signal-subscription | If `true` records related to signal subscriptions will be exported | `true` | | timer | If `true` records related to timers will be exported | `true` | | user-task | If `true` records related to user tasks will be exported | `true` | | variable | If `true` records related to variables will be exported | `true` | | variable-document | If `true` records related to variable documents will be exported | `true` | ### Variable-name filters Starting with Camunda 8.9, you can filter exported variable records by variable name. Configuration: ```yaml camunda: data: exporters: elasticsearch: args: index: variable-name-inclusion-start-with: - business_ variable-name-exclusion-start-with: - business_debug ``` The exporter first matches variable names against inclusion rules (if present), then against exclusion rules. If a variable matches both, the exclusion wins. For details on how this interacts with Optimize, see [Camunda 8 system configuration](../../../optimize/configuration/system-configuration-platform-8.md). ### Variable-type filters Variable-type filters let you restrict exported variables by their inferred JSON type, such as `String`, `Number`, `Boolean`, `Object` or `Null`. The exporter first matches variable types against inclusion rules (if present), then against exclusion rules. If a variable type matches both, the exclusion wins. Configuration: ```yaml camunda: data: exporters: elasticsearch: args: index: variable-value-type-inclusion: - Object - String variable-value-type-exclusion: - Object ``` Use this filter to drop large object or array payloads at export time. Type inference is similar to what Optimize uses. For details on which types to include or exclude for reporting, see [Camunda 8 system configuration](../../../optimize/configuration/system-configuration-platform-8.md). ### Scope-aware variable export The Elasticsearch exporter can filter root and local variables differently. - Root variables are created in the process instance scope and are visible throughout the process instance. - Local variables are created in a child scope, such as a sub-process, call activity, or task. They are visible only in that scope and its children. Configure these options under `index`: ```yaml camunda: data: exporters: elasticsearch: args: index: export-local-variables-enabled: true # Root variable name filters root-variable-name-inclusion-exact: [] root-variable-name-inclusion-start-with: [] root-variable-name-inclusion-end-with: [] root-variable-name-exclusion-exact: [] root-variable-name-exclusion-start-with: [] root-variable-name-exclusion-end-with: [] # Local variable name filters local-variable-name-inclusion-exact: [] local-variable-name-inclusion-start-with: [] local-variable-name-inclusion-end-with: [] local-variable-name-exclusion-exact: [] local-variable-name-exclusion-start-with: [] local-variable-name-exclusion-end-with: [] # Root variable type filters root-variable-value-type-inclusion: [] root-variable-value-type-exclusion: [] # Local variable type filters local-variable-value-type-inclusion: [] local-variable-value-type-exclusion: [] ``` Behavior overview: - If `export-local-variables-enabled` is set to `false`, no local variables are exported. - If all `root-*` and `local-*` lists are empty, only the global filters (`variable-name-*` and `variable-value-type-*`) apply. This preserves behavior from earlier versions. - If any root-specific or local-specific list is non-empty, that scope uses both the global filters and the scope-specific filters. - Exclusion filters take precedence over inclusion filters. #### Example configuration Export only selected root variables and exclude temporary local variables: ```yaml index: export-local-variables-enabled: true # Include only specific root variables root-variable-name-inclusion-exact: ["customerId", "orderId"] # Exclude local variables used for temporary processing local-variable-name-exclusion-start-with: ["tmp_", "debug_"] # Export only simple root variable types root-variable-value-type-inclusion: ["String", "Number"] ``` In this example: - Only `customerId` and `orderId` root variables are exported. - Local variables starting with `tmp_` or `debug_` are excluded. - Only root variables of type `String` and `Number` are exported. ### BPMN process filters BPMN process filters control which processes (by `bpmnProcessId`) are exported. All records that carry the given `bpmnProcessId` follow the same decision. Configuration: ```yaml camunda: data: exporters: elasticsearch: args: index: bpmn-process-id-inclusion: - orderProcess bpmn-process-id-exclusion: - debugProcess ``` Processes listed under `inclusion` are candidates; `exclusion` removes any of those candidates again. Some value types that never expose `bpmnProcessId` (for example, `DEPLOYMENT`, `DECISION`) are not affected and remain controlled only via the `index.*` flags. ### Optimize mode With Optimize mode, you can restrict exported records to those used by Optimize, reducing index size. Configuration: ```yaml camunda: data: exporters: elasticsearch: args: index: optimize-mode-enabled: true ``` When enabled, the exporter emits only the value types and intents that Optimize imports. Other value types are dropped unless you explicitly opt in to the legacy behavior (for example, via `include-enabled-records`). Use this flag only if the exporter indices are dedicated to Optimize. For SaaS and Self-Managed recommendations, see [Camunda 8 system configuration](../../../optimize/configuration/system-configuration-platform-8.md). To avoid too many expensive requests to the Elasticsearch cluster, the exporter performs batch updates by default. The size of the batch, along with how often it should be flushed (regardless of size) can be controlled by configuration. | Option | Description | Default | | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | | delay | Delay, in seconds, before force flush of the current batch. This ensures that even when we have low traffic of records, we still export every once in a while. | `5` | | size | The amount of records a batch should have before we flush the batch | `1000` | | memory-limit | The size of the batch, in bytes, before we flush the batch | `10485760` (10 MB) | With the default configuration, the exporter will aggregate records and flush them to Elasticsearch: 1. When it has aggregated 1000 records. 2. When the batch memory size exceeds 10 MB. 3. Five seconds have elapsed since the last flush (regardless of how many records were aggregated). A retention policy can be set up to delete old data. When enabled, this creates an Index Lifecycle Management (ILM) Policy that deletes the data after the specified `minimumAge`. All index templates created by this exporter apply the created ILM Policy. | Option | Description | Default | | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | | enabled | If `true` the ILM Policy is created and applied to the index templates | `false` | | minimum-age | Specifies how old the data must be, before the data is deleted as a duration | `30d` | | policy-name | The name of the created and applied ILM policy | `zeebe-record-retention-policy` | | manage-policy | If `true` the exporter creates, updates, and removes the ILM policy on its own. Set to `false` to leave an externally managed policy untouched (the exporter neither creates nor removes it) | `true` | :::note The duration can be specified in days `d`, hours `h`, minutes `m`, seconds `s`, milliseconds `ms`, and/or nanoseconds `nanos`. ::: :::note Externally managed policy When `manage-policy: false`, the exporter still attaches the policy named by `policy-name` (default `zeebe-record-retention-policy`) to the indices it creates. The policy must already exist in your Elasticsearch cluster under that name when the exporter starts. If the policy does not exist, Elasticsearch silently accepts the dangling `index.lifecycle.name` setting. The exporter starts successfully, but ILM logs `policy_not_found` warnings on each cycle and skips retention actions until the policy is created. Once you create the policy under the configured name, ILM picks it up retroactively on its next cycle. ::: Providing these authentication options will enable Basic authentication on the exporter. | Option | Description | Default | | -------- | ----------------------------- | ------- | | username | Username used to authenticate | N/A | | password | Password used to authenticate | N/A | ## Example The following is an example configuration of the exporter: ```yaml --- camunda: data: exporters: elasticsearch: # Elasticsearch exporter ---------- # An example configuration for the elasticsearch exporter: # # These settings can also be overridden using environment variables "CAMUNDA_DATA_EXPORTERS_ELASTICSEARCH_..." # class-name: io.camunda.zeebe.exporter.ElasticsearchExporter args: # A comma separated list of URLs pointing to the Elasticsearch instances you wish to export to. # For example, if you want to connect to multiple nodes for redundancy: # url: http://localhost:9200,http://localhost:9201 url: http://localhost:9200 bulk: delay: 5 size: 1000 memory-limit: 10485760 retention: enabled: true minimum-age: 30d policy-name: zeebe-records-retention-policy authentication: username: elastic password: changeme index: prefix: zeebe-record create-template: true index-suffix-date-pattern: "yyyy-MM-dd" command: false event: true rejection: false command-distribution: true decision-requirements: true decision: true decision-evaluation: true deployment: true deployment-distribution: true error: true escalation: true form: true incident: true job: true job-batch: false message: true message-start-subscription: true message-subscription: true process: true process-event: false process-instance: true process-instance-creation: true process-instance-migration: true process-instance-modification: true process-message-subscription: true resource-deletion: true signal: true signal-subscription: true timer: true user-task: true variable: true variable-document: true ``` ## Self-signed certificates The Zeebe Elasticsearch exporter does not [currently support](https://github.com/camunda/camunda/issues/9839) connecting to Elasticsearch using self-signed certificates. If you must use self-signed certificates, it is possible to build your own trust store and have the application use it. In this case, it is recommended to create a new custom trust store based on the default one. This way, it will also be able to verify certificates signed using trusted root certificate authorities. 1. First, create a new custom trust store which contains the same data as the default one, using PKCS12 format. To do so, find the location of the default `cacerts` trust store: - On Linux systems, find it at `$JAVA_HOME/lib/security/cacerts`. - For macOS, find it under `$(/usr/libexec/java_home)/jre/lib/security/cacerts`. Once you have the right location, for example, `$JAVA_HOME/lib/security/cacerts`, run the following to create a new trust store: ```sh keytool -importkeystore -srckeystore $JAVA_HOME/lib/security/cacerts -destkeystore zeebeTrustStore.jks -srcstoretype PKCS12 -deststoretype JKS ``` Set any password, so long as it's at least 6 characters. 2. Add your custom certificate to to the new trust store. For example, if your custom certificate is located at `/tmp/myCustomCertificate.pem`: ```sh keytool -import -alias MyCustomCertificate -keystore zeebeTrustStore.jks -file /tmp/myCustomCertificate.pem ``` :::note Replace the `-file` parameter with the actual path to your certificate, and make sure to replace the `-alias` parameter with something descriptive, like `WebServerCertificate`. ::: When prompted to trust the certificate, make sure to answer **yes**. 3. Update the application to use this trust store. First, make sure the file is readable by the application. For example, on Unix systems, run: ```sh chmod a+r zeebeTrustStore.jks ``` Then, specify the following properties when running the application: - `javax.net.ssl.trustStore`: must be set to the path of your custom trust store. - `javax.net.ssl.trustStorePassword`: set to your trust store password. The following example uses a trust store location of `/tmp/zeebeTrustStore.jks`, and a password of `changeme`. When using the official distribution (whether Docker image or the bundled shell scripts), these propertiescan be provided using the following environment variable: ```sh JAVA_OPTS="-Djavax.net.ssl.trustStore=/tmp/zeebeTrustStore.jks -Djavax.net.ssl.trustStorePassword=changeme ${JAVA_OPTS}" ``` :::warning If you're using containers, you will need to mount the trust store to the container such that it can be found by the `java` process. This will depend on your deployment method (for example, Helm chart, Docker Compose). The simplest way is to build a custom image which already contains your trust store, and specifies the environment variable. ::: ## Legacy Zeebe records and Optimize filters With the introduction of the Camunda exporter, the Elasticsearch and OpenSearch exporters no longer export all record types by default. Instead, they will emit only the record value types and intents required by Optimize. To export additional record types, enable the [`include-enabled-records`](#configuration) configuration property. When you enable exporter-side filters (`optimize-mode-enabled`, `variable-name`, `variable-type`, or `bpmn-process-id`), filtering applies only to newly produced records. Existing documents in Elasticsearch or OpenSearch are not rewritten. To export other record types, enable the [include-enabled-records](#configuration) configuration property. :::info Upgrade note (8.8 to 8.9) When upgrading from 8.8 to 8.9, exporter filtering behavior may affect data completeness. See the [Camunda 8 system configuration](../../../optimize/configuration/system-configuration-platform-8.md) for guidance. ::: --- ## Exporters The Orchestration Cluster comes packaged with built-in exporters: - [Elasticsearch](elasticsearch-exporter.md) - [OpenSearch](opensearch-exporter.md) - [Camunda Exporter](camunda-exporter.md) - [RDBMS Exporter](rdbms-exporter.md) This section of the docs explains how these exporters can be [installed](install-zeebe-exporters.md) and configured. For a general overview on the exporters concept, refer to our [exporters concept](../../../../concepts/exporters.md) page. --- ## Install Zeebe exporters Camunda 8 Self-Managed Helm chart supports the addition of Zeebe exporters by using `initContainer`. The following is an example to install the community-supported Zeebe [Hazelcast](https://github.com/camunda-community-hub/zeebe-hazelcast-exporter) exporter. ```yaml extraInitContainers: - name: init-exporters-hazelcast image: busybox:1.35 command: ["/bin/sh", "-c"] args: [ "wget --no-check-certificate https://repo1.maven.org/maven2/io/zeebe/hazelcast/zeebe-hazelcast-exporter/0.8.0-alpha1/zeebe-hazelcast-exporter-0.8.0-alpha1-jar-with-dependencies.jar -O /exporters/zeebe-hazelcast-exporter.jar; ls -al /exporters", ] volumeMounts: - name: exporters mountPath: /exporters/ env: - name: ZEEBE_BROKER_EXPORTERS_HAZELCAST_JARPATH value: /exporters/zeebe-hazelcast-exporter.jar - name: ZEEBE_BROKER_EXPORTERS_HAZELCAST_CLASSNAME value: io.zeebe.hazelcast.exporter.HazelcastExporter - name: ZEEBE_HAZELCAST_REMOTE_ADDRESS value: "{{ .Release.Name }}-hazelcast" ``` This example is downloading the exporters' JAR from a URL and adding the JAR to the `exporters` directory, which will be scanned for JARs and added to the Zeebe Broker classpath. Then, with `environment variables`, you can configure the exporter parameters. :::note During startup, the Zeebe pods will copy the exporters located in `/exporters/` to `/usr/local/zeebe/exporters/`. If the pods have a read-only root file system, a writable volume must be mounted to `/usr/local/zeebe/exporters/`. ::: --- ## OpenSearch exporter :::note For supported OpenSearch versions in Camunda 8 Self-Managed, see [Supported Environments](../../../../../reference/supported-environments.md#camunda-8-self-managed). Starting with Camunda 8.8, Camunda uses the [Camunda exporter](/self-managed/components/orchestration-cluster/zeebe/exporters/camunda-exporter.md) to consume new records. Records from 8.7 and earlier are consumed only during migration. The Elasticsearch and OpenSearch exporters remain fully usable after migration (for example, for existing setups, Optimize, or other custom use cases). Their functionality is not limited to the migration period. From 8.9 onward, the OpenSearch exporter also supports Optimize-focused export filters (for example, variable-name filters, variable-type filters, BPMN process include/exclude, and an Optimize mode flag). For Optimize-specific guidance and recommended settings, see [Camunda 8 system configuration](../../../optimize/configuration/system-configuration-platform-8.md). ::: The Zeebe OpenSearch exporter acts as a bridge between [Zeebe](https://camunda.com/platform/zeebe/) and [OpenSearch](https://opensearch.org) by exporting records written to Zeebe streams as documents into several indices. ## Concept The exporter operates on the idea that it should perform as little as possible on the Zeebe side of things. In other words, you can think of the indexes into which the records are exported as a staging data warehouse. Any enrichment or transformation on the exported data should be performed by your own ETL jobs. When configured to do so, the exporter will automatically create an index per record value type (see the value type in the Zeebe protocol). Each of these indexes has a corresponding pre-defined mapping to facilitate data ingestion for your own ETL jobs. You can find those as templates in this module's resources folder. :::note The indexes are created as required, and will not be created twice if they already exist. However, once disabled, they will not be deleted (that is up to the administrator). Similarly, data is never deleted by the exporter, and must be deleted by the administrator when it is safe to do so. ::: ## Configuration :::note As the exporter is packaged with Zeebe, it is not necessary to specify a `jarPath`. ::: The exporter can be enabled by configuring it with the classpath in the broker settings. For a Spring Boot application or Camunda 8 with unified configuration: **Application config (YAML):** ```yaml camunda: data: exporters: opensearch: class-name: io.camunda.zeebe.exporter.opensearch.OpensearchExporter args: # Refer to the table below for the available args options ``` **Environment variables:** Set environment variables in the format `CAMUNDA_DATA_EXPORTERS_OPENSEARCH_...` (for example, `CAMUNDA_DATA_EXPORTERS_OPENSEARCH_URL`). **Helm:** Add the same configuration under `orchestration.configuration` in your `values.yaml` file. :::warning Do not configure both legacy (`zeebe.broker.exporters.*`) and unified (`camunda.data.exporters.*`) exporter properties at the same time. Exporter properties are a breaking-change mapping in unified configuration, and the application fails to start until legacy properties are removed. ::: The exporter can be configured by providing `args`. The table below explains all the different options, and the default values for these options: | Option | Description | Default | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------- | | url | Valid URLs as a comma-separated string. | `http://localhost:9200` | | request-timeout-ms | Request timeout (in ms) for the OpenSearch client. | `30000` | | index | Refer to [index](#index) for index configuration options, including record/value-type switches, Optimize-focused filters, and the Optimize mode flag. | | | bulk | Refer to [bulk](#bulk) for the bulk configuration options. | | | retention | Refer to [retention](#retention) for the retention configuration options. | | | authentication | Refer to [authentication](#authentication) for the authentication configuration options. | | | aws | Refer to [AWS](#aws) for the AWS configuration options. | | | include-enabled-records | If `true`, exports all enabled record types configured under `index`. If `optimize-mode-enabled` is `true`, Optimize mode takes precedence. Use mainly for migration or compatibility scenarios. | `false` | In most cases, you will not be interested in exporting every single record produced by a Zeebe cluster, but rather only a subset of them. This can also be configured to limit the kinds of records exported (for example, only events, no commands), and the value type of these records (for example, only job and process values). | Option | Description | Default | | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | | prefix | This prefix will be appended to every index created by the exporter; must not contain `_` (underscore). | zeebe-record | | create-template | If `true` missing indexes will be created automatically. | `true` | | number-of-shards | The number of [shards](https://opensearch.org/docs/latest/install-and-configure/configuring-opensearch/index-settings/#static-index-level-index-settings) used for each new record index created. | 3 | | number-of-replicas | The number of shard [replicas](https://opensearch.org/docs/latest/install-and-configure/configuring-opensearch/index-settings/#dynamic-index-level-index-settings) used for each new record index created. | 0 | | command | If `true` command records will be exported | `false` | | event | If `true` event records will be exported | `true` | | rejection | If `true` rejection records will be exported | `false` | | checkpoint | If `true` records related to checkpoints will be exported | `false` | | command-distribution | If `true` records related to command distributions will be exported | `true` | | decision | If `true` records related to decisions will be exported | `true` | | decision-evaluation | If `true` records related to decision evaluations will be exported | `true` | | decision-requirements | If `true` records related to decision requirements will be exported | `true` | | deployment | If `true` records related to deployments will be exported | `true` | | deployment-distribution | If `true` records related to deployment distributions will be exported | `true` | | error | If `true` records related to errors will be exported | `true` | | escalation | If `true` records related to escalations will be exported | `true` | | form | If `true` records related to forms will be exported | `true` | | incident | If `true` records related to incidents will be exported | `true` | | job | If `true` records related to jobs will be exported | `true` | | job-batch | If `true` records related to job batches will be exported | `false` | | message | If `true` records related to messages will be exported | `true` | | message-batch | If `true` records related to message batches will be exported | `false` | | message-subscription | If `true` records related to message subscriptions will be exported | `true` | | message-start-event-subscription | If `true` records related to message start event subscriptions will be exported | `true` | | process | If `true` records related to processes will be exported | `true` | | process-event | If `true` records related to process events will be exported | `false` | | process-instance | If `true` records related to process instances will be exported | `true` | | process-instance-batch | If `true` records related to process instances batches will be exported | `false` | | process-instance-creation | If `true` records related to process instance creations will be exported | `true` | | process-instance-migration | If `true` records related to process instance migrations will be exported | `true` | | process-instance-modification | If `true` records related to process instance modifications will be exported | `true` | | process-message-subscription | If `true` records related to process message subscriptions will be exported | `true` | | resource-deletion | If `true` records related to resource deletions will be exported | `true` | | signal | If `true` records related to signals will be exported | `true` | | signal-subscription | If `true` records related to signal subscriptions will be exported | `true` | | timer | If `true` records related to timers will be exported | `true` | | user-task | If `true` records related to user tasks will be exported | `true` | | variable | If `true` records related to variables will be exported | `true` | | variable-document | If `true` records related to variable documents will be exported | `true` | ### Variable-name filters Starting with Camunda 8.9, you can filter exported variable records by variable name. Configuration: ```yaml camunda: data: exporters: opensearch: args: index: variable-name-inclusion-start-with: - business_ variable-name-exclusion-start-with: - business_debug ``` The exporter first matches variable names against inclusion rules (if present), then against exclusion rules. If a variable matches both, the exclusion wins. For details on how this interacts with Optimize, see [Camunda 8 system configuration](../../../optimize/configuration/system-configuration-platform-8.md). ### Variable-type filters Variable-type filters let you restrict exported variables by their inferred JSON type, such as `String`, `Number`, `Boolean`, `Object` or `Null`. Configuration: ```yaml camunda: data: exporters: opensearch: args: index: variable-value-type-inclusion: - Object - String variable-value-type-exclusion: - Object ``` Use this filter to drop large object or array payloads at export time. Type inference is similar to what Optimize uses. For details on which types to include or exclude for reporting, see [Camunda 8 system configuration](../../../optimize/configuration/system-configuration-platform-8.md). ### Scope-aware variable export The OpenSearch exporter can filter root and local variables differently. - Root variables are created in the process instance scope and are visible throughout the process instance. - Local variables are created in a child scope, such as a sub-process, call activity, or task. They are visible only in that scope and its children. Configure these options under `index`: ```yaml camunda: data: exporters: opensearch: args: index: export-local-variables-enabled: true # Root variable name filters root-variable-name-inclusion-exact: [] root-variable-name-inclusion-start-with: [] root-variable-name-inclusion-end-with: [] root-variable-name-exclusion-exact: [] root-variable-name-exclusion-start-with: [] root-variable-name-exclusion-end-with: [] # Local variable name filters local-variable-name-inclusion-exact: [] local-variable-name-inclusion-start-with: [] local-variable-name-inclusion-end-with: [] local-variable-name-exclusion-exact: [] local-variable-name-exclusion-start-with: [] local-variable-name-exclusion-end-with: [] # Root variable type filters root-variable-value-type-inclusion: [] root-variable-value-type-exclusion: [] # Local variable type filters local-variable-value-type-inclusion: [] local-variable-value-type-exclusion: [] ``` Behavior overview: - If `export-local-variables-enabled` is set to `false`, no local variables are exported. - If all `root-*` and `local-*` lists are empty, only the global filters (`variable-name-*` and `variable-value-type-*`) apply. This preserves behavior from earlier versions. - If any root-specific or local-specific list is non-empty, that scope uses both the global filters and the scope-specific filters. - Exclusion filters take precedence over inclusion filters. #### Example configuration Export only selected root variables and exclude temporary local variables: ```yaml index: export-local-variables-enabled: true # Include only specific root variables root-variable-name-inclusion-exact: ["customerId", "orderId"] # Exclude local variables used for temporary processing local-variable-name-exclusion-start-with: ["tmp_", "debug_"] # Export only simple root variable types root-variable-value-type-inclusion: ["String", "Number"] ``` In this example: - Only `customerId` and `orderId` root variables are exported. - Local variables starting with `tmp_` or `debug_` are excluded. - Only root variables of type `String` and `Number` are exported. ### BPMN process filters BPMN process filters control which processes (by `bpmnProcessId`) are exported. All records that carry the given `bpmnProcessId` follow the same decision. ```yaml camunda: data: exporters: opensearch: args: index: bpmn-process-id-inclusion: - orderProcess bpmn-process-id-exclusion: - debugProcess ``` Processes listed under `inclusion` are candidates; `exclusion` removes any of those candidates again. Some value types that never expose `bpmnProcessId` (for example, `DEPLOYMENT`, `DECISION`) are not affected and remain controlled only via the `index.*` flags. ### Optimize mode With Optimize mode, you can restrict exported records to those used by Optimize, reducing index size. ```yaml camunda: data: exporters: opensearch: args: index: optimize-mode-enabled: true ``` When enabled, the exporter emits only the value types and intents that Optimize imports. Other value types are dropped unless you explicitly opt in to the legacy behavior (for example, via `include-enabled-records`). Use this flag only if the exporter indices are dedicated to Optimize. For SaaS and Self-Managed recommendations, see [Camunda 8 system configuration](../../../optimize/configuration/system-configuration-platform-8.md). To avoid too many expensive requests to the OpenSearch cluster, the exporter performs batch updates by default. The size of the batch, along with how often it should be flushed (regardless of size) can be controlled by configuration. | Option | Description | Default | | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | | delay | Delay, in seconds, before force flush of the current batch. This ensures that even when we have low traffic of records, we still export every once in a while. | `5` | | size | The amount of records a batch should have before we flush the batch. | `1000` | | memory-limit | The size of the batch, in bytes, before we flush the batch. | `10485760` (10 MB) | With the default configuration, the exporter would aggregate records and flush them to OpenSearch either: 1. When it has aggregated 1000 records. 2. When the batch memory size exceeds 10 MB. 3. Five seconds have elapsed since the last flush (regardless of how many records were aggregated). A retention policy can be set up to delete old data. When enabled, this creates an Index State Management (ISM) policy that deletes the data after the specified `minimumAge`. All index templates created by this exporter apply the created ISM policy. | Option | Description | Default | | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | | enabled | If `true` the ISM policy is created and applied to the index templates. | `false` | | minimum-age | Specifies how old the data must be, before the data is deleted as a duration. | `30d` | | policy-name | The name of the created and applied ISM policy. | `zeebe-record-retention-policy` | | policy-description | The description of the created and applied ISM policy. | `Zeebe record retention policy` | | manage-policy | If `true` the exporter creates, updates, and removes the ISM policy on its own. Set to `false` to leave an externally managed policy untouched (the exporter neither creates nor removes it). | `true` | :::note The duration can be specified in days `d`, hours `h`, minutes `m`, seconds `s`, milliseconds `ms`, and/or nanoseconds `nanos`. ::: :::note Externally managed policy When `manage-policy: false`, the exporter still attaches the policy named by `policy-name` (default `zeebe-record-retention-policy`) to the indices it creates. The policy must already exist in your OpenSearch cluster under that name when the exporter starts. If the policy does not exist, OpenSearch's ISM `add` API rejects the call and the exporter fails to start with `OpensearchExporterException: Failed to add policy to indices`. Provision the ISM policy before starting Camunda when using `manage-policy: false`. ::: Providing these authentication options will enable Basic authentication on the exporter. | Option | Description | Default | | -------- | ----------------------------- | ------- | | username | Username used to authenticate | N/A | | password | Password used to authenticate | N/A | When running OpenSearch in AWS, you may require requests to be signed. By enabling AWS in the configuration, a request interceptor will be added to the exporter. This interceptor will take care of signing the requests. Signing requests requires credentials. These credentials are not directly configurable in the exporter. Instead, they are resolved by following the [Default Credential Provider Chain](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html). | Option | Description | Default | | ------------ | --------------------------------------------------------------------------------------- | -------------------------------------------------- | | enabled | Enables AWS request signing | `false` | | service-name | AWS' name of the service to where requests are made. For OpenSearch this should be `es` | `es` | | region | The region this exporter is running in | The value of the `AWS_REGION` environment variable | ## Example The following is an example configuration of the exporter: ```yaml --- camunda: data: exporters: opensearch: # Opensearch exporter ---------- # An example configuration for the opensearch exporter: # # These settings can also be overridden using environment variables "CAMUNDA_DATA_EXPORTERS_OPENSEARCH_..." class-name: io.camunda.zeebe.exporter.opensearch.OpensearchExporter args: # A comma separated list of URLs pointing to the Opensearch instances you wish to export to. # For example, if you want to connect to multiple nodes for redundancy: # url: http://localhost:9200,http://localhost:9201 url: http://localhost:9200 bulk: delay: 5 size: 1000 memory-limit: 10485760 retention: enabled: true minimum-age: 30d policy-name: zeebe-records-retention-policy policy-description: Zeebe records retention policy authentication: username: opensearch password: changeme aws: enabled: true service-name: es region: eu-west-1 index: prefix: zeebe-record create-template: true command: false event: true rejection: false command-distribution: true decision-requirements: true decision: true decision-evaluation: true deployment: true deployment-distribution: true error: true escalation: true form: true incident: true job: true job-batch: false message: true message-start-event-subscription: true message-subscription: true process: true process-event: false process-instance: true process-instance-creation: true process-instance-migration: true process-instance-modification: true process-message-subscription: true resource-deletion: true signal: true signal-subscription: true timer: true user-task: true variable: true variable-document: true ``` ## Self-signed certificates The Zeebe OpenSearch exporter does not [currently support](https://github.com/camunda/camunda/issues/9839) connecting to OpenSearch using self-signed certificates. If you must use self-signed certificates, it is possible to build your own trust store and have the application use it. In this case, it is recommended to create a new custom trust store based on the default one. This way, it will also be able to verify certificates signed using trusted root certificate authorities. 1. First, create a new custom trust store which contains the same data as the default one, using PKCS12 format. To do so, find the location of the default `cacerts` trust store: - On Linux systems, find it at `$JAVA_HOME/lib/security/cacerts`. - For macOS, find it under `$(/usr/libexec/java_home)/jre/lib/security/cacerts`. Once you have the right location, for example, `$JAVA_HOME/lib/security/cacerts`, run the following to create a new trust store: ```sh keytool -importkeystore -srckeystore $JAVA_HOME/lib/security/cacerts -destkeystore zeebeTrustStore.jks -srcstoretype PKCS12 -deststoretype JKS ``` Set any password, so long as it's at least 6 characters. 2. Add your custom certificate to to the new trust store. For example, if your custom certificate is located at `/tmp/myCustomCertificate.pem`: ```sh keytool -import -alias MyCustomCertificate -keystore zeebeTrustStore.jks -file /tmp/myCustomCertificate.pem ``` :::note Replace the `-file` parameter with the actual path to your certificate, and make sure to replace the `-alias` parameter with something descriptive, like `WebServerCertificate`. ::: When prompted to trust the certificate, make sure to answer **yes**. 3. Update the application to use this trust store. First, make sure the file is readable by the application. For example, on Unix systems, run: ```sh chmod a+r zeebeTrustStore.jks ``` Then, specify the following properties when running the application: - `javax.net.ssl.trustStore`: must be set to the path of your custom trust store. - `javax.net.ssl.trustStorePassword`: set to your trust store password. The following example uses a trust store location of `/tmp/zeebeTrustStore.jks`, and a password of `changeme`. When using the official distribution (whether Docker image or the bundled shell scripts), these propertiescan be provided using the following environment variable: ```sh JAVA_OPTS="-Djavax.net.ssl.trustStore=/tmp/zeebeTrustStore.jks -Djavax.net.ssl.trustStorePassword=changeme ${JAVA_OPTS}" ``` :::warning If you're using containers, you will need to mount the trust store to the container such that it can be found by the `java` process. This will depend on your deployment method (for example, Helm chart, Docker Compose). The simplest way is to build a custom image which already contains your trust store, and specifies the environment variable. ::: ## Legacy Zeebe records and Optimize filters With the introduction of the Camunda exporter, the Elasticsearch and OpenSearch exporters no longer export all record types by default. By default, they emit only the record value types and intents required by Optimize. To export additional record types, enable the [`include-enabled-records`](#configuration) configuration property. When you enable exporter-side filters (`optimize-mode-enabled`, `variable-name`, `variable-type`, or `bpmn-process-id`), filtering applies only to newly produced records. Existing documents in Elasticsearch or OpenSearch are not rewritten. To export other record types, enable the [include-enabled-records](#configuration) configuration property. :::info Upgrade note (8.8 to 8.9) When upgrading from 8.8 to 8.9, exporter filtering behavior may affect data completeness. See the [Camunda 8 system configuration](../../../optimize/configuration/system-configuration-platform-8.md) for guidance. ::: --- ## RDBMS Exporter The RDBMS Exporter writes Orchestration Cluster secondary storage data to relational database tables. It is enabled when secondary storage is configured as `rdbms`. ## How it works - The RDBMS Exporter consumes records from the log stream, transforming relevant records and writing them to secondary storage database tables. - Operate and Tasklist query this secondary storage data through the Orchestration Cluster APIs. ## Configuration Configure the exporter through secondary storage settings: - [RDBMS secondary storage configuration](/self-managed/concepts/databases/relational-db/configuration.md) - [Secondary storage properties](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#data---secondary-storage) --- ## Backpressure(Operations) When a broker receives a client request, it is written to the **event stream** first (see section [internal processing](/components/zeebe/technical-concepts/internal-processing.md) for details), and processed later by the stream processor. If the processing is slow or if there are many client requests in the stream, it might take too long for the processor to start processing the command. If the broker keeps accepting new requests from the client, the backlog increases and the processing latency can grow beyond an acceptable time. To avoid such problems, Zeebe employs a backpressure mechanism. When the broker receives more requests than it can process with an acceptable latency, it rejects some requests (see [technical error handling](/apis-tools/zeebe-api/technical-error-handling.md)). Alternatively, [flow control write rate limits](/self-managed/operational-guides/configure-flow-control/configure-flow-control.md) can also be used with static write rate limits or throttling. This prevents the partition from building an excessive backlog of records not exported. ### Terminology - **RTT** - Round-Trip Time, known as the time between when the request is accepted by the broker and when the response to the request is sent back to the gateway. - **Inflight count** - The number of requests accepted by the broker but the response is not yet sent. - **Limit** - Maximum number of flight requests. When the inflight count is above the limit, any new incoming request is rejected. :::note The limit and inflight count are calculated per partition. ::: ### Backpressure algorithms Zeebe uses adaptive algorithms from [concurrency-limits](https://github.com/Netflix/concurrency-limits) to dynamically calculate the limit. Configure Zeebe with one of the backpressure algorithms in the following sections. The default values can be found in the [Zeebe repo](https://github.com/camunda/camunda/blob/main/dist/src/main/config/defaults.yaml) in the `# backpressure` section. #### Fixed limit With **fixed limit**, one can configure a fixed value of the limit. Zeebe operators are recommended to evaluate the latencies observed with different values for limit. Note that with different cluster configurations, you may have to choose different limit values. #### AIMD **Additive increase/multiplicative decrease (AIMD)** calculates the limit based on the configured _requestTimeout_. When the RTT for a request is shorter than _requestTimeout_, the limit is increased by 1. When the RTT is longer than _requestTimeout_, the limit will be reduced according to the configured _backoffRatio_. #### Vegas Vegas is an adaptive limit algorithm based on TCP Vegas congestion control algorithm. Vegas estimates a base latency as the minimum observed latency. This base RTT is the expected latency when there is no load. Whenever the RTT deviates from the base RTT, a new limit is calculated based on the Vegas algorithm. Vegas allows you to configure two parameters - _alpha_ and _beta_. The values correspond to a queue size estimated by the Vegas algorithm based on the observed RTT, base RTT, and current limit. When the queue size is below _alpha_, the limit is increased. When the queue size is above _beta_, the limit is decreased. #### Gradient Gradient is an adaptive limit algorithm that dynamically calculates the limit based on observed RTT. In the gradient algorithm, the limit is adjusted based on the gradient of observed RTT and an observed minimum RTT. If gradient is less than 1, the limit is decreased. Otherwise, the limit is increased. #### Gradient2 Gradient2 is similar to Gradient, but instead of using observed minimum RTT as the base, it uses an exponentially smoothed average RTT. ## Backpressure tuning The goal of backpressure is to keep the processing latency low. The processing latency is calculated as the time between the command is written to the event stream until it is processed. To see how backpressure behaves, run a benchmark on your cluster and observe the following metrics: - `zeebe_stream_processor_latency_bucket` - `zeebe_dropped_request_count_total` - `zeebe_received_request_count_total` - `zeebe_backpressure_requests_limit` You may want to run the benchmark with different loads: 1. With low load - Where the number of requests sent per second is low. 2. With high load - Where the number of requests sent per second is above what Zeebe can process within a reasonable latency. If the value of the limit is small, the processing latency will be small, but the number of rejected requests may be high. If the value of the limit is large, fewer requests may be rejected (depending on the request rate), but the processing latency may increase. When using **fixed limit**, you can run the benchmark with different values for the limit. You can then determine a suitable value for a limit for which the processing latency (`zeebe_stream_processor_latency_bucket`) is within the desired latency. When using **AIMD**, you can configure a `requestTimeout` which corresponds to a desired latency. Note that during high load, AIMD can lead to a processing latency two times more than the configured `requestTimeout`. It is also recommended to configure a `minLimit` to prevent the limit from aggressively dropping during constant high load. When using **Vegas**, you cannot configure the backpressure to a desired latency. Instead, Vegas tries to keep the RTT as low as possible based on the observed minimum RTT. Similar to Vegas, you cannot configure the desired latency in Gradient and Gradient2. They calculated the limit based on the gradient of observed RTT from the expected RTT. The higher the value of _rttTolerance_, the higher deviations are tolerated that results in higher values for limit. If a lot of requests are rejected due to backpressure, it might indicate that the processing capacity of the cluster is not enough to handle the expected throughput. If this is the expected workload, you might consider a different configuration for the cluster, such as provisioning more resources and increasing the number of nodes and partitions. ## Potential issues The rate limiter used by Zeebe to implement backpressure may use `System.nanoTime()` to measure the RTT of requests. In some systems, we've observed consecutive calls to this method can return equal or even decreasing values. [Low clock resolution](https://shipilev.net/blog/2014/nanotrusting-nanotime) and [monotonicity](https://bugs.openjdk.java.net/browse/JDK-6458294) [issues](https://stackoverflow.com/questions/3657289/linux-clock-gettimeclock-monotonic-strange-non-monotonic-behavior) are some of the most likely culprits of this. If this happens, it's recommended to configure the backpressure to use the **fixed** algorithm. Without a clock with sufficient resolution, adaptive backpressure algorithms are not useful. ## Next steps Looking for more information on backpressure? Review our documentation on [internal processing and backpressure](/components/zeebe/technical-concepts/internal-processing.md#handling-backpressure). --- ## Backups(Operations) :::note Refer to our documentation on [backup and restore](/self-managed/operational-guides/backup-restore/backup-and-restore.md) to learn how to take backups without downtime. ::: As Zeebe fully manages the state of your process instances, consider taking backups of Zeebe data; this is crucial to prevent data loss, roll back application-level errors, and more. Zeebe is fault-tolerant and replicates state internally. Backups are only necessary if you'd like to protect against the loss of entire replica sets or data corruption bugs. State of other components, such as Operate and Tasklist, is not managed by Zeebe and must be backed up separately. Taking backups is a manual process that is highly dependent on your infrastructure and deployment. Camunda does not provide an automated backup mechanism or tool. However, we do offer the following guidance to create and execute a successful backup. ## Cold backups Cold backups, also called offline backups, require **downtime**. During the downtime, processes don't make progress and clients can't communicate with Zeebe. To make sure that the downtime doesn't cause issues for your clients, you should test how your clients behave during the downtime, or shut them down as well. ### Shutting down all brokers in the cluster To take a consistent backup, all brokers must be shut down first. As soon as brokers shut down, partitions become unhealthy and clients lose connections to Zeebe or experience full backpressure. To prevent unnecessary failovers during the shutdown process, we recommend shutting down all brokers at the same time instead of a gradual shutdown. Wait for all brokers to fully shut down before proceeding to the next step. ### Creating the backup :::note The `data` folder contains symbolic and hard links which may require special attention when copying, depending on your environment. ::: To create the backup, take the following steps: 1. Each broker has a data folder where all state is persisted. The location of the data folder is [configured](../configuration/configuration.md) via `zeebe.broker.data.directory`. Create a copy of the data folder and store it in a safe location. If you have direct access to the broker, for example in a bare-metal setup, you can do this by creating a tarball like this: `tar caf backup.tar.gz data/`. You may also use filesystem snapshots or [Kubernetes volume snapshots](https://kubernetes.io/docs/concepts/storage/volume-snapshots/) if that fits your environment better 2. Double-check that your tool of choice supports symbolic and hard links. 3. Do not merge or otherwise modify data folders as this might result in data loss and unrestorable backups. 4. Save the broker configuration to ensure the replacement cluster can process the backed-up data. See the following example on how a backup may look: ```bash $ tree zeebe-backup-* zeebe-backup-2021-01-31 ├── zeebe-broker-0-config.yml ├── zeebe-broker-0-data.tar.gz ├── zeebe-broker-1-config.yml ├── zeebe-broker-1-data.tar.gz ├── zeebe-broker-2-config.yml └── zeebe-broker-2-data.tar.gz ``` ### Resuming After taking the backup, brokers can be started again and will automatically resume with processing. ## Restore from backup ### Prepare replacement cluster :::note Caution Always use the same or the next minor version of Zeebe that you were using when taking the backup. Using a different version may result in data corruption or data loss. See the [upgrade guide](/self-managed/upgrade/components/index.md) for more details. ::: Ensure your replacement cluster has the same number of brokers as the old cluster and uses the [same node IDs](setting-up-a-cluster.md#configuration). ### Shutting down all brokers in the replacement cluster Before installing the backup, ensure all brokers are fully shut down. ### Installing the backup To install the backup, take the following steps: 1. Delete the existing data folder on each broker of your replacement cluster. 2. For each broker, copy over the configuration and the data folder. 3. You may need to slightly adjust the configuration for your replacement cluster, for example to update IP addresses. ### Starting the Zeebe cluster After replacing the data folders, brokers can be started again and will automatically resume with processing. --- ## Cluster scaling Zeebe allows scaling an existing cluster by adding or removing brokers and by adding new partitions. Partitions are automatically redistributed over the set of brokers to spread the load evenly. Zeebe provides a REST API to manage the cluster scaling. The cluster management API is a custom endpoint available via [Spring Boot Actuator](https://docs.spring.io/spring-boot/docs/3.1.x/reference/htmlsingle/#actuator.endpoints). This is accessible via the management port of the Zeebe Gateway. :::important - Partition count can only be increased and not decreased. - Backups are disallowed during partition scaling but can be taken before or after. A backup taken before scaling can only be restored to a cluster with the same partition count. After restoring, you can request scaling again to the desired partition count. - When scaling up the number of partitions, consider the resulting RocksDB size per partition. Allocate **at least 32 MB of RocksDB memory per partition** after scaling. For details, see the [resource planning guide](/self-managed/components/orchestration-cluster/zeebe/operations/resource-planning.md). ::: ## Considerations - Scaling operations occur while the cluster remains online. During scaling, data is redistributed and new leaders are elected for affected partitions. - Existing partitions continue processing data, but you may notice temporary performance impacts until scaling completes. Plan scaling ahead of anticipated load increases to minimize disruption. - When adding new partitions or brokers, partitions are redistributed across both old and new brokers. Depending on the number of brokers and partitions, this may increase the load per broker. Use the API endpoints in [dry run](#dry-run) mode to preview partition distribution. - Always take a backup before scaling to ensure you can restore if needed. ## Scale up brokers The following shows how to scale up a Zeebe cluster using an example of scaling from cluster size 3 to cluster size 6. The target partition count is 6. This example assumes the cluster was deployed with the following configurations, depending on what we want to scale: #### Initial State - scale brokers only: - clusterSize 3 - partitionCount 6 - scale brokers and partitions: - clusterSize 3 - partitionCount 3 #### Target state - clusterSize 6 - partitionCount 6 ### 1. Start new brokers If you have deployed Zeebe using [Helm](/self-managed/deployment/helm/install/quick-install.md), you can start new brokers by using the `kubectl scale` command. Otherwise, refer to the corresponding installation methods on how to start a new broker. ``` kubectl scale statefulset camunda --replicas=6 ``` You can see new pods being created when running `kubectl get pods`. The new brokers will be assigned ids `3`, `4`, and `5` respectively. ``` camunda-zeebe-0 1/1 Running 0 3m24s camunda-zeebe-1 1/1 Running 0 3m24s camunda-zeebe-2 1/1 Running 0 3m24s camunda-zeebe-3 0/1 Init:0/1 0 11s camunda-zeebe-4 0/1 Init:0/1 0 11s camunda-zeebe-5 0/1 Init:0/1 0 11s ``` ### 2. Send scale request to the Zeebe Gateway Send a POST request to the Zeebe Gateway's management endpoint to add new brokers to the cluster or redistribute partitions. See the [API reference](#api-reference) for details. If you are running on Kubernetes and haven’t set up Ingress, port-forward to access the Zeebe Gateway on your local machine: ``` kubectl port-forward svc/camunda-zeebe-gateway 9600:9600 ``` Choose the appropriate request depending on whether you are adding new partitions (see section 2.a or 2.b). Verify partition distribution after scaling by calling the endpoints in [dry run](#dry-run) mode. #### 2.a Scale brokers only Run the following to send the request to the Zeebe Gateway: ``` curl -X 'PATCH' \ 'http://localhost:9600/orchestration/actuator/cluster' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{ "brokers": { "add": [3,4,5] } }' ``` Here `3`, `4`, and `5` are the newly-added brokers. #### 2.b Scaling brokers and partitions Run the following to send the request to the Zeebe Gateway to add 3 new brokers to the cluster and set the number of partition to 6. ``` curl -X 'PATCH' \ 'http://localhost:9600/orchestration/actuator/cluster' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{ "brokers": { "add": [3,4,5] }, "partitions": { "count": 6, "replicationFactor": 3 } }' ``` #### 2.c Scaling only partitions If you don't intend to add new brokers to the cluster, you can skip the `"brokers"` section: ``` curl -X 'PATCH' \ 'http://localhost:9600/orchestration/actuator/cluster' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{ "partitions": { "count": 6, "replicationFactor": 3 } }' ``` You can omit `replicationFactor` if you don't want to change it. The response includes a `changeId`, `currentTopology`, planned changes, and the expected topology, as shown below: ``` { "changeId": 2, "currentTopology": [ ... ], "plannedChanges": [ { "operation": "BROKER_ADD", "brokerId": 3 }, { "operation": "BROKER_ADD", "brokerId": 4 }, { "operation": "BROKER_ADD", "brokerId": 5 }, { "operation": "PARTITION_JOIN", "brokerId": 4, "partitionId": 5, "priority": 3 }, { "operation": "PARTITION_LEAVE", "brokerId": 1, "partitionId": 5 }, ... ], "expectedTopology": [ { "id": 1, "state": "ACTIVE", "version": 7, "lastUpdatedAt": "2023-12-22T13:37:43.403615966Z", "partitions": [ { "id": 1, "state": "ACTIVE", "priority": 2 }, { "id": 2, "state": "ACTIVE", "priority": 3 }, { "id": 6, "state": "ACTIVE", "priority": 1 } ] }, ... ] } ``` ### 3. Query the Zeebe Gateway to monitor progress of scaling The scaling operation can take a while because data needs to be moved to the newly-added brokers. Therefore, you have to monitor the status by querying Zeebe and sending the following GET request to the Zeebe Gateway: ``` curl --request GET 'http://localhost:9600/orchestration/actuator/cluster' ``` When the scaling has completed, the `changeId` from the previous response will be marked as completed: ``` { "version": 3, "brokers": [ ... ], "lastChange": { "id": 2, "status": "COMPLETED", "startedAt": "2023-12-22T13:37:43.405094261Z", "completedAt": "2023-12-22T13:40:06.90159187Z" } } ``` ### 4. (Optional) Verify the partitions are distributed to new brokers This step is optional, but it is useful when you are testing to see if scaling works as expected. Port-forward to access the Zeebe Gateway if required: ``` kubectl port-forward svc/camunda-gateway 8080:8080 ``` Run the following command to see the current status of the cluster. If security is enabled, first obtain an access token from your identity provider and export it as `ACCESS_TOKEN`, then include it as a Bearer token in the request header. ``` curl -L 'http://localhost:8080/orchestration/v2/topology' \ -H 'Accept: application/json' ``` The response would show that partitions are distributed to new brokers:
Example response ```json { "brokers": [ { "nodeId": 0, "host": "camunda-zeebe-0.camunda-zeebe", "port": 26501, "partitions": [ { "partitionId": 1, "role": "leader", "health": "healthy" }, { "partitionId": 5, "role": "follower", "health": "healthy" }, { "partitionId": 6, "role": "follower", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 1, "host": "camunda-zeebe-1.camunda-zeebe", "port": 26501, "partitions": [ { "partitionId": 1, "role": "follower", "health": "healthy" }, { "partitionId": 2, "role": "leader", "health": "healthy" }, { "partitionId": 6, "role": "leader", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 2, "host": "camunda-zeebe-2.camunda-zeebe", "port": 26501, "partitions": [ { "partitionId": 1, "role": "follower", "health": "healthy" }, { "partitionId": 2, "role": "follower", "health": "healthy" }, { "partitionId": 3, "role": "leader", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 3, "host": "camunda-zeebe-3.camunda-zeebe", "port": 26501, "partitions": [ { "partitionId": 2, "role": "follower", "health": "healthy" }, { "partitionId": 3, "role": "follower", "health": "healthy" }, { "partitionId": 4, "role": "leader", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 4, "host": "camunda-zeebe-4.camunda-zeebe", "port": 26501, "partitions": [ { "partitionId": 3, "role": "follower", "health": "healthy" }, { "partitionId": 4, "role": "follower", "health": "healthy" }, { "partitionId": 5, "role": "leader", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 5, "host": "camunda-zeebe-5.camunda-zeebe", "port": 26501, "partitions": [ { "partitionId": 4, "role": "follower", "health": "healthy" }, { "partitionId": 5, "role": "follower", "health": "healthy" }, { "partitionId": 6, "role": "follower", "health": "healthy" } ], "version": "8.8.0" } ], "clusterSize": 6, "partitionsCount": 6, "replicationFactor": 3, "gatewayVersion": "8.8.0", "clusterId": "clusterId" } ```
## Scale down We will explain how to scale down a Zeebe cluster via an example of scaling from cluster size 6 to cluster size 3. We assume the cluster is running with 6 brokers. :::warning Scale down can be performed only on brokers, partition count cannot be decreased ::: ### 1. Send the scale request to the Zeebe Gateway Now we should tell Zeebe to move partitions away from the brokers that will be removed. For that, we send a POST request to the Zeebe Gateway's management endpoint. See [API reference](#api-reference) for more details. If you haven't set up Ingress, you can first port-forward to access the Zeebe Gateway in your local machine: ``` kubectl port-forward svc/camunda-zeebe-gateway 9600:9600 ``` ``` curl -X 'PATCH' \ 'http://localhost:9600/orchestration/actuator/cluster' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{ "brokers": { "remove": [3,4,5] } }' ``` Similar to scaling up, the response to this request would contain a `changeId`, `currentTopology`, planned changes, and expected topology. ### 2. Query the Zeebe Gateway to monitor progress of scaling ``` curl --request GET 'http://localhost:9600/orchestration/actuator/cluster' ``` When the scaling has completed, the changeId from the previous response will be marked as completed: ``` { "version": 5, "brokers": [ ... ], "lastChange": { "id": 4, "status": "COMPLETED", "startedAt": "2023-12-22T13:43:05.936882692Z", "completedAt": "2023-12-22T13:43:41.138424552Z" } } ``` ### 3. (Optional) Verify partitions have been moved to the remaining brokers This step is optional, but it is useful when you are testing to see if scaling worked as expected. Run the following command to see the current status of the cluster. If security is enabled, first obtain an access token from your identity provider and export it as `ACCESS_TOKEN`, then include it as a Bearer token in the request header. ``` curl -L 'http://localhost:8080/orchestration/v2/topology' \ -H 'Accept: application/json' ``` The response would show that the partitions are moved away from brokers `3`, `4`, and `5`:
Example response ```json { "brokers": [{ "nodeId": 0, "host": "camunda-zeebe-0.camunda-zeebe.camunda", "port": 26501, "partitions": [{ "partitionId": 1, "role": "leader", "health": "healthy" }, { "partitionId": 2, "role": "follower", "health": "healthy" }, { "partitionId": 3, "role": "follower", "health": "healthy" }, { "partitionId": 4, "role": "leader", "health": "healthy" } { "partitionId": 5, "role": "follower", "health": "healthy" }, { "partitionId": 6, "role": "follower", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 1, "host": "camunda-zeebe-1.camunda-zeebe.camunda", "port": 26501, "partitions": [{ "partitionId": 1, "role": "follower", "health": "healthy" }, { "partitionId": 2, "role": "leader", "health": "healthy" }, { "partitionId": 3, "role": "follower", "health": "healthy" }, { "partitionId": 4, "role": "follower", "health": "healthy" }, { "partitionId": 5, "role": "leader", "health": "healthy" }, { "partitionId": 6, "role": "leader", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 2, "host": "camunda-zeebe-2.camunda-zeebe", "port": 26501, "partitions": [{ "partitionId": 1, "role": "follower", "health": "healthy" }, { "partitionId": 2, "role": "follower", "health": "healthy" }, { "partitionId": 3, "role": "leader", "health": "healthy" }, { "partitionId": 4, "role": "follower", "health": "healthy" }, { "partitionId": 5, "role": "follower", "health": "healthy" }, { "partitionId": 6, "role": "follower", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 3, "host": "camunda-zeebe-3.camunda-zeebe", "port": 26501, "partitions": [], "version": "8.8.0" }, { "nodeId": 4, "host": "camunda-zeebe-4.camunda-zeebe", "port": 26501, "partitions": [], "version": "8.8.0" }, { "nodeId": 5, "host": "camunda-zeebe-5.camunda-zeebe", "port": 26501, "partitions": [], "version": "8.8.0" } ], "clusterSize": 3, "partitionsCount": 6, "replicationFactor": 3, "gatewayVersion": "8.8.0", "clusterId": "clusterId" } ```
### 4. Shut down the brokers when the scaling operation has completed :::danger If you shut down brokers before Zeebe has scaled down and moved all partitions away from the brokers, scaling operation would never complete and may result in data loss. ::: ``` kubectl scale statefulset --replicas=3 ``` When monitoring the pods via `kubectl get pods`, we can see that pods 3, 4, and 5 have been terminated. ``` camunda-zeebe-0 1/1 Running 0 9m55s camunda-zeebe-1 1/1 Running 0 9m55s camunda-zeebe-2 1/1 Running 0 9m50s ``` :::note After scaling down the statefulset, you may have to delete the PVCs manually. ::: ## API reference OpenAPI spec for this API can be found [here](https://github.com/camunda/camunda/blob/main/dist/src/main/resources/api/cluster/cluster-api.yaml). ### Reconfiguration API This API lets you reconfigure a cluster by adding or removing brokers, adding partitions, or changing the `replicationFactor`. You can use this instead of the Scale API. :::note This endpoint does not respect the fixed partitioning scheme configured with `zeebe.broker.experimental.partitioning`. When used, partitions are redistributed using the `ROUND_ROBIN` strategy. ::: #### Request ``` PATCH actuator/cluster { brokers: { add: [] remove: [] count: } { partitions: { count: replicationFactor: } } } ```
Example request ``` curl -X 'PATCH' \ 'http://localhost:9600/orchestration/actuator/cluster' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{ "brokers": { "add": [3,4,5] }, "partitions": { "count": 6, "replicationFactor": 3 } }' ```
##### Dry run You can do a dry run without executing the reconfiguration by setting the `dryRun` request parameter to `true`. By default, `dryRun` is set to `false`. ##### Force :::caution This is a dangerous operation and must be used with caution. Incorrect use may result in split-brain scenarios or an unhealthy, unrecoverable cluster. ::: Usually, changes can only be made when all brokers are up. If some brokers are unreachable, you can remove them from the cluster by setting the `force` request parameter to `true`. This operation is mainly useful for [dual-region setups](/self-managed//concepts/multi-region/dual-region.md). For details, see the [dual-region operational procedure](/self-managed/deployment/helm/operational-tasks/dual-region-ops.md). Deviations from the process may make the cluster unusable. :::note Don’t send more than one `force` request at a time. ::: Example request: ``` curl -X 'PATCH' \ 'http://localhost:9600/orchestration/actuator/cluster?force=true' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{ "brokers": { "remove": [0,2] } }' ``` This operation doesn’t redistribute the partitions from the removed brokers. The resulting cluster has fewer replicas for the affected partitions. ### Scale request API :::note See also the [Reconfiguration API](#reconfiguration-api). ::: Use this endpoint to scale a cluster up or down by changing the cluster size and redistributing partitions. :::note This endpoint does not respect the fixed partitioning scheme configured with `zeebe.broker.experimental.partitioning`. When used, partitions are redistributed using the `ROUND_ROBIN` strategy. ::: #### Request ``` POST actuator/cluster/brokers/ [ , ,.. ] ``` The input is a list of _all_ broker ids that will be in the final cluster after scaling:
Example request ``` curl --request POST 'http://localhost:9600/orchestration/actuator/cluster/brokers' \ -H 'Content-Type: application/json' \ -d '[0, 1, 2, 3]' ```
##### Dry run You can also do a dry run without actually executing the scaling by specifying the request parameter `dryRun` to `true` as follows. By default, `dryRun` is set to false: ``` curl --request POST 'http://localhost:9600/orchestration/actuator/cluster/brokers?dryRun=true' \ -H 'Content-Type: application/json' \ -d '[0, 1, 2, 3]' ``` ##### Replication factor The replication factor for all partitions can be changed with the `replicationFactor` request parameter. If not specified, the replication factor remains unchanged. The new replicas are assigned to the brokers based on the round robin partition distribution strategy. ``` curl --request POST 'http://localhost:9600/orchestration/actuator/cluster/brokers?replicationFactor=4' \ -H 'Content-Type: application/json' \ -d '[0, 1, 2, 3]' ``` ##### Force remove brokers :::caution This is a dangerous operation and must be used with caution. When not used correctly, split-brain scenarios or unhealthy, unrecoverable clusters may result. ::: Usually, changes can only be made to a cluster when all brokers are up. When some brokers are unreachable, you may want to remove them from the cluster. You can force remove a set of brokers by setting the request parameter `force` to `true`. This operation is mainly useful for [dual-region setups](/self-managed//concepts/multi-region/dual-region.md), and additional information can be found in the [dual-region operational procedure](/self-managed/deployment/helm/operational-tasks/dual-region-ops.md). Any deviations from the described process can result in the cluster being unusable. :::note Do not send more than one force request at a time. ::: The following request force removes all brokers that are _not_ provided in the request body: ``` curl --request POST 'http://localhost:9600/orchestration/actuator/cluster/brokers?force=true' \ -H 'Content-Type: application/json' \ -d '[0, 1, 2]' ``` This operation does not re-distribute the partitions that were in the removed brokers. As a result, the resulting cluster will have a reduced number of replicas for the affected partitions. #### Response The response is a JSON object. See detailed specs [here](https://github.com/camunda/camunda/blob/main/dist/src/main/resources/api/cluster/cluster-api.yaml): ``` { changeId: currentTopology: [...] plannedChanges: [...] expectedTopology: [...] } ``` - `changeId`: The ID of the changes initiated to scale the cluster. This can be used to monitor the progress of the scaling operation. The ID typically increases so new requests get a higher ID than the previous one. - `currentTopology`: A list of current brokers and the partition distribution. - `plannedChanges`: A sequence of operations that has to be executed to achieve scaling. - `expectedToplogy`: The expected list of brokers and the partition distribution once the scaling is completed.
Example response ``` { "changeId": 2, "currentTopology": [ { "id": 1, "state": "ACTIVE", "version": 0, "lastUpdatedAt": "-999999999-01-01T00:00:00+18:00", "partitions": [ { "id": 1, "state": "ACTIVE", "priority": 2 }, { "id": 2, "state": "ACTIVE", "priority": 3 }, { "id": 3, "state": "ACTIVE", "priority": 1 }, { "id": 4, "state": "ACTIVE", "priority": 1 }, { "id": 5, "state": "ACTIVE", "priority": 3 }, { "id": 6, "state": "ACTIVE", "priority": 2 } ] }, { "id": 2, "state": "ACTIVE", "version": 0, "lastUpdatedAt": "-999999999-01-01T00:00:00+18:00", "partitions": [ { "id": 1, "state": "ACTIVE", "priority": 1 }, { "id": 2, "state": "ACTIVE", "priority": 2 }, { "id": 3, "state": "ACTIVE", "priority": 3 }, { "id": 4, "state": "ACTIVE", "priority": 2 }, { "id": 5, "state": "ACTIVE", "priority": 1 }, { "id": 6, "state": "ACTIVE", "priority": 3 } ] }, { "id": 0, "state": "ACTIVE", "version": 0, "lastUpdatedAt": "-999999999-01-01T00:00:00+18:00", "partitions": [ { "id": 1, "state": "ACTIVE", "priority": 3 }, { "id": 2, "state": "ACTIVE", "priority": 1 }, { "id": 3, "state": "ACTIVE", "priority": 2 }, { "id": 4, "state": "ACTIVE", "priority": 3 }, { "id": 5, "state": "ACTIVE", "priority": 2 }, { "id": 6, "state": "ACTIVE", "priority": 1 } ] } ], "plannedChanges": [ { "operation": "BROKER_ADD", "brokerId": 3 }, { "operation": "BROKER_ADD", "brokerId": 4 }, { "operation": "BROKER_ADD", "brokerId": 5 }, { "operation": "PARTITION_JOIN", "brokerId": 4, "partitionId": 5, "priority": 3 }, { "operation": "PARTITION_JOIN", "brokerId": 5, "partitionId": 5, "priority": 2 }, { "operation": "PARTITION_LEAVE", "brokerId": 1, "partitionId": 5 }, { "operation": "PARTITION_LEAVE", "brokerId": 2, "partitionId": 5 }, { "operation": "PARTITION_RECONFIGURE_PRIORITY", "brokerId": 0, "partitionId": 5, "priority": 1 }, { "operation": "PARTITION_JOIN", "brokerId": 3, "partitionId": 4, "priority": 3 }, { "operation": "PARTITION_JOIN", "brokerId": 4, "partitionId": 4, "priority": 2 }, { "operation": "PARTITION_JOIN", "brokerId": 5, "partitionId": 4, "priority": 1 }, { "operation": "PARTITION_LEAVE", "brokerId": 1, "partitionId": 4 }, { "operation": "PARTITION_LEAVE", "brokerId": 2, "partitionId": 4 }, { "operation": "PARTITION_LEAVE", "brokerId": 0, "partitionId": 4 }, { "operation": "PARTITION_JOIN", "brokerId": 3, "partitionId": 2, "priority": 1 }, { "operation": "PARTITION_LEAVE", "brokerId": 0, "partitionId": 2 }, { "operation": "PARTITION_JOIN", "brokerId": 3, "partitionId": 3, "priority": 2 }, { "operation": "PARTITION_JOIN", "brokerId": 4, "partitionId": 3, "priority": 1 }, { "operation": "PARTITION_LEAVE", "brokerId": 1, "partitionId": 3 }, { "operation": "PARTITION_LEAVE", "brokerId": 0, "partitionId": 3 }, { "operation": "PARTITION_JOIN", "brokerId": 5, "partitionId": 6, "priority": 3 }, { "operation": "PARTITION_LEAVE", "brokerId": 2, "partitionId": 6 }, { "operation": "PARTITION_RECONFIGURE_PRIORITY", "brokerId": 1, "partitionId": 6, "priority": 1 }, { "operation": "PARTITION_RECONFIGURE_PRIORITY", "brokerId": 0, "partitionId": 6, "priority": 2 } ], "expectedTopology": [ { "id": 1, "state": "ACTIVE", "version": 7, "lastUpdatedAt": "2023-12-22T13:37:43.403615966Z", "partitions": [ { "id": 1, "state": "ACTIVE", "priority": 2 }, { "id": 2, "state": "ACTIVE", "priority": 3 }, { "id": 6, "state": "ACTIVE", "priority": 1 } ] }, { "id": 2, "state": "ACTIVE", "version": 6, "lastUpdatedAt": "2023-12-22T13:37:43.403558726Z", "partitions": [ { "id": 1, "state": "ACTIVE", "priority": 1 }, { "id": 2, "state": "ACTIVE", "priority": 2 }, { "id": 3, "state": "ACTIVE", "priority": 3 } ] }, { "id": 3, "state": "ACTIVE", "version": 8, "lastUpdatedAt": "2023-12-22T13:37:43.401971149Z", "partitions": [ { "id": 2, "state": "ACTIVE", "priority": 1 }, { "id": 3, "state": "ACTIVE", "priority": 2 }, { "id": 4, "state": "ACTIVE", "priority": 3 } ] }, { "id": 4, "state": "ACTIVE", "version": 8, "lastUpdatedAt": "2023-12-22T13:37:43.40214448Z", "partitions": [ { "id": 3, "state": "ACTIVE", "priority": 1 }, { "id": 4, "state": "ACTIVE", "priority": 2 }, { "id": 5, "state": "ACTIVE", "priority": 3 } ] }, { "id": 5, "state": "ACTIVE", "version": 8, "lastUpdatedAt": "2023-12-22T13:37:43.40345971Z", "partitions": [ { "id": 4, "state": "ACTIVE", "priority": 1 }, { "id": 5, "state": "ACTIVE", "priority": 2 }, { "id": 6, "state": "ACTIVE", "priority": 3 } ] }, { "id": 0, "state": "ACTIVE", "version": 8, "lastUpdatedAt": "2023-12-22T13:37:43.403675185Z", "partitions": [ { "id": 1, "state": "ACTIVE", "priority": 3 }, { "id": 5, "state": "ACTIVE", "priority": 1 }, { "id": 6, "state": "ACTIVE", "priority": 2 } ] } ] } ```
The scaling is executed asynchronously. Use the Query API below to monitor the progress. ### Monitoring API The current cluster topology and any ongoing scaling operations can be monitored via this endpoint. #### Request ``` GET actuator/cluster ``` #### Response The response is a JSON object. See detailed specs [here](https://github.com/camunda/camunda/blob/main/dist/src/main/resources/api/cluster/cluster-api.yaml): ``` { version: brokers: [...] lastChange: {} pendingChange: {} } ``` - `version`: The version of current cluster topology. The version is updated when the cluster is scaled up or down. - `brokers`: A list of current brokers and the partition distribution. - `lastChange`: The details about the last completed scaling operation. - `pendingChange`: The details about the ongoing scaling operation.
Example response ``` { "version": 6, "brokers": [ { "id": 1, "state": "ACTIVE", "version": 14, "lastUpdatedAt": "2023-12-22T13:43:29.718491365Z", "partitions": [ { "id": 1, "state": "ACTIVE", "priority": 2 }, { "id": 2, "state": "ACTIVE", "priority": 3 }, { "id": 3, "state": "ACTIVE", "priority": 1 }, { "id": 4, "state": "ACTIVE", "priority": 1 }, { "id": 5, "state": "ACTIVE", "priority": 3 }, { "id": 6, "state": "ACTIVE", "priority": 2 } ] }, { "id": 2, "state": "ACTIVE", "version": 12, "lastUpdatedAt": "2023-12-22T13:43:30.951499449Z", "partitions": [ { "id": 1, "state": "ACTIVE", "priority": 1 }, { "id": 2, "state": "ACTIVE", "priority": 2 }, { "id": 3, "state": "ACTIVE", "priority": 3 }, { "id": 4, "state": "ACTIVE", "priority": 2 }, { "id": 5, "state": "ACTIVE", "priority": 1 }, { "id": 6, "state": "ACTIVE", "priority": 3 } ] }, { "id": 0, "state": "ACTIVE", "version": 16, "lastUpdatedAt": "2023-12-22T13:43:28.482560705Z", "partitions": [ { "id": 1, "state": "ACTIVE", "priority": 3 }, { "id": 2, "state": "ACTIVE", "priority": 1 }, { "id": 3, "state": "ACTIVE", "priority": 2 }, { "id": 4, "state": "ACTIVE", "priority": 3 }, { "id": 5, "state": "ACTIVE", "priority": 2 }, { "id": 6, "state": "ACTIVE", "priority": 1 } ] } ], "lastChange": { "id": 4, "status": "COMPLETED", "startedAt": "2023-12-22T13:43:05.936882692Z", "completedAt": "2023-12-22T13:43:41.138424552Z" }, "pendingChange": { "id": 6, "status": "IN_PROGRESS", "completed": [], "pending": [ { "operation": "BROKER_ADD", "brokerId": 3 }, { "operation": "PARTITION_JOIN", "brokerId": 3, "partitionId": 3, "priority": 2 }, { "operation": "PARTITION_LEAVE", "brokerId": 1, "partitionId": 3 }, { "operation": "PARTITION_RECONFIGURE_PRIORITY", "brokerId": 0, "partitionId": 3, "priority": 1 }, { "operation": "PARTITION_JOIN", "brokerId": 3, "partitionId": 6, "priority": 2 }, { "operation": "PARTITION_LEAVE", "brokerId": 0, "partitionId": 6 }, { "operation": "PARTITION_RECONFIGURE_PRIORITY", "brokerId": 1, "partitionId": 6, "priority": 3 }, { "operation": "PARTITION_RECONFIGURE_PRIORITY", "brokerId": 2, "partitionId": 6, "priority": 1 }, { "operation": "PARTITION_RECONFIGURE_PRIORITY", "brokerId": 1, "partitionId": 5, "priority": 1 }, { "operation": "PARTITION_RECONFIGURE_PRIORITY", "brokerId": 2, "partitionId": 5, "priority": 2 }, { "operation": "PARTITION_RECONFIGURE_PRIORITY", "brokerId": 0, "partitionId": 5, "priority": 3 }, { "operation": "PARTITION_JOIN", "brokerId": 3, "partitionId": 2, "priority": 1 }, { "operation": "PARTITION_LEAVE", "brokerId": 0, "partitionId": 2 }, { "operation": "PARTITION_JOIN", "brokerId": 3, "partitionId": 4, "priority": 3 }, { "operation": "PARTITION_LEAVE", "brokerId": 2, "partitionId": 4 }, { "operation": "PARTITION_RECONFIGURE_PRIORITY", "brokerId": 0, "partitionId": 4, "priority": 2 } ] } } ```
--- ## Disk space Zeebe uses the local disk for storage of its persistent data. Therefore, if the Zeebe Broker runs out of disk space, the system is in an invalid state as the broker cannot update its state. To prevent the system from reaching an unrecoverable state, Zeebe expects a minimum size of free disk space available. If this limit is violated, the broker rejects new requests to allow the operations team to free more disk space, and allows the broker to continue to update its state. Zeebe can be configured with the following settings for the disk usage: - **zeebe.broker.data.disk.enablemonitoring**: Configure if disk usage should be monitored (default: true) - **zeebe.broker.data.disk.monitoringInterval**: The interval in which the disk space usage is checked (default: 1 second) - **zeebe.broker.data.disk.freeSpace.replication**: When the free space available is less than this value, Zeebe pauses receiving replicated events. (default: 1GB) - For **production** use cases, we recommend to increase this value and set it approximately to `number of partitions x logSegmentSize + 1GB`. - **zeebe.broker.data.disk.freeSpace.processing**: When the free space available is less than this value, Zeebe rejects all user commands and pauses processing. (default: 2GB) - This must be greater than `freeSpace.replication`. - For **production** use cases, we recommend increasing this value and setting it at a minimum of `number of partitions x 2 x logSegmentSize + 1GB`. --- ## Health status ## Broker The Zeebe Broker exposes three HTTP endpoints to query its health status: - Startup check - Ready check - Health check ### Startup check Startup check endpoint is exposed via `http://{zeebe-broker-host}:9600/actuator/health/startup`. This endpoint returns a 200 response. If it is not ready, it will return a 503 error. A broker has successfully started when: - The broker has found other brokers in the cluster. - All partitions owned by this broker have started and participate in replication. - Other necessary services have started. A successful startup does not mean the broker is ready to process requests. The broker is ready only after startup has successfully completed. ### Ready check Ready check endpoint is exposed via `http://{zeebe-broker-host}:9600/actuator/health/readiness`. This endpoint returns a 200 response. If it is not ready, it will return a 503 error. A broker is ready when it installs all necessary services to start processing in all partitions. If a broker is ready, it doesn't mean it's the leader for the partitions. It means it is participating in the replication and can be either a leader or a follower of all the partitions that are assigned to it. Once it is ready, it never becomes unready again. A ready check is useful, for example, to use as a `readinessProbe` in a Kubernetes configuration to control when a pod can be restarted for rolling update. Depending on the cluster configuration, restarting one pod before the previous one is ready might make the system unavailable because the quorum of replicas is not available. By configuring a `readinessProbe` that uses the ready check endpoint, we can inform Kubernetes when it is safe to proceed with the rolling update. ### Health check Health check endpoint is exposed via `http://{zeebe-broker-host}:9600/actuator/health/status`. This endpoint returns a 200 response if the broker is healthy. If it is not healthy, it will return a 503 error. A broker is never healthy before it is ready. Unlike ready check, a broker can become unhealthy after it is healthy. Hence, it gives a better status of a running broker. A broker is healthy when it can process processes, accept commands, and perform all its expected tasks. If it is unhealthy, it may mean three things: - **It is only temporarily unhealthy**: For example, due to environmental circumstances such as temporary I/O issues. - **It is partially unhealthy**: One or more partitions could be unhealthy, while the rest of them are able to process processes. - **It is completely dead**: one or more partitions has failed in a non-recoverable way, and human intervention is required to recover. [Metrics](/self-managed/operational-guides/monitoring/metrics.md) give more insight into which partition is healthy or unhealthy. When a broker becomes unhealthy, it's recommended to check the logs to see what went wrong. (The default broker port can be configured using environment variables - respectively `MANAGEMENT_SERVER_PORT` and `MANAGEMENT_SERVER_ADDRESS` - or system properties - respectively `-Dmanagement.server.port=` or `-Dmanagement.server.address=` - to configure them) ## Gateway The Zeebe Gateway exposes three HTTP endpoints to query its health status: - Health status - `http://{zeebe-gateway}:9600/actuator/health` - Startup probe - `http://{zeebe-gateway}:9600/actuator/health/startup` - Liveness probe - `http://{zeebe-gateway}:9600/actuator/health/liveness` (The default port can be changed in the configuration: `{zeebe.gateway.monitoring.port}`) ### Health status The gateway is **healthy** if it: - Started successfully - Has sufficient free memory and disk space to work with - Is able to respond to requests within a defined timeout - Is aware of other nodes in the cluster - Is aware of leaders for partitions - All its partitions are healthy The gateway is **degraded** if it also meets the **healthy** standards above, with the exception that at least **one** partition is healthy instead of **all** partitions. ### Startup probe The gateway starts if it finished its boot sequence successfully and is ready to receive requests. It no longer starts when it initiates the shutdown sequence. The started probe can be used as Kubernetes startup probe. ### Liveness probe The gateway is live if it: - Started successfully - Has a minimal amount of free memory and disk space to work with - Is able to respond to requests within a defined timeout, or misses the timeout for less than 10 minutes - Is aware of other nodes in the cluster, or lost awareness of other nodes for less than five minutes - Is aware of leaders for partitions, or lost awareness of partition leaders for less than five minutes The liveness probe can be used as Kubernetes liveness probe. ### Status responses Each endpoint returns a status which can be one of the following: - `UNKNOWN` (HTTP status code 200) - `UP` (HTTP status code 200) - `DOWN` (HTTP status code 503) - `OUT_OF_SERVICE` (HTTP status code 503) If details are enabled (default), the response will also contain additional details. ### Customization Health indicators are set to sensible defaults. For specific use cases, it might be necessary to [customize health indicators](../configuration/gateway-health-probes.md). --- ## Management API As well as the [REST](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) and [gRPC API](/apis-tools/zeebe-api/grpc.md) for process instance execution, the Zeebe Gateway exposes an HTTP endpoint for cluster management operations. ## About this API This API is not expected to be used by a typical user, but by a privileged user such as a cluster administrator. It is exposed via a different port, and configured using configuration `management.server.port` (or via environment variable `MANAGEMENT_SERVER_PORT`). By default, this is set to `9600`. The API is a custom endpoint available via [Spring Boot Actuator](https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints). :::info For additional configurations such as security, refer to the official [Spring Boot documentation](https://spring.io/guides). ::: ### Operations This API currently supports the following operations: - [Rebalancing](/self-managed/components/orchestration-cluster/zeebe/operations/rebalancing.md) - [Pause and resume exporting](#exporting-api) - [Enable and disable exporter](#exporters-api) ## Exporting API Use the Exporting API for the followings: - As a debugging tool. - When taking a backup of Camunda 8 (see [backup and restore](/self-managed/operational-guides/backup-restore/backup-and-restore.md)). :::warning This endpoint always returns HTTP `200`. Check the `status` field in the response body to determine whether the operation succeeded: `204` indicates success and `500` indicates failure. If the request fails, verify that all brokers are running and retry. ::: The operation requires a complete cluster topology. If a broker is unavailable, the request fails entirely — no partitions are paused or resumed. Retry when all brokers are available. **Success response:** ```json { "body": null, "status": 204, "contentType": null } ``` **Failure response:** ```json { "body": { "message": "Expected 3 members of partition 1 but found 2, current topology: ..." }, "status": 500, "contentType": null } ``` To pause exporting on all partitions, send the following request to the gateway's management endpoint. ``` POST actuator/exporting/pause ``` When all partitions pause exporting, the response contains `"status": 204`. If the request fails, some partitions may have paused exporting. Therefore, it is important to either retry until success or revert the partial pause by resuming exporting. After exporting is paused, it must eventually be resumed. Otherwise, the cluster could become unavailable. To resume exporting, send the following request to the gateway's management endpoint: ``` POST actuator/exporting/resume ``` When all partitions have resumed exporting, the response contains `"status": 204`. If the request fails, only some partitions may have resumed exporting. Therefore, it is important to retry until successful. The soft pause feature can be used when you want to continue exporting records, but don't want to delete those records (log compaction) from Zeebe. This is particularly useful during hot backups. Learn more about [using this feature for hot backups](/self-managed/operational-guides/backup-restore/backup-and-restore.md). ``` POST actuator/exporting/pause?soft=true ``` When all partitions soft pause exporting, the response contains `"status": 204`. If the request fails, some partitions may have soft paused exporting. Therefore, either retry until success or revert the partial soft pause by resuming the export. ## Exporters API The Exporters API allows for enabling, disabling or deleting configured exporters. By default, all configured exporters are enabled. The enable and disable functionality is specifically useful for [dual region deployment](/self-managed/deployment/helm/operational-tasks/dual-region-ops.md) operations. - **Enabled**: Records are exported to the exporter. The log is compacted only after the records are exported. - **Disabled**: Records are _not_ exported to the exporter, and the log is compacted. :::info You can find the OpenAPI spec for this API in the [GitHub repository](https://github.com/camunda/camunda/blob/main/dist/src/main/resources/api/cluster/exporter-api.yaml). ::: :::note The `camunda‐zeebe‐gateway` service on port 9600 exposes the exporter endpoints. ::: When enabling an exporter, the exporter must be already configured in the cluster. To initialize an exporter's state, an existing exporter's ID can be provided in the optional `initializeFrom` field. Both exporters must be of the same type. To enable a previously disabled exporter, send the following request to the gateway's management API: ``` POST actuator/exporters/{exporterId}/enable { initializeFrom: {anotherExporterId} } ``` New records written after the exporter is enabled will be exported to this exporter. To disable an exporter, send the following request to the gateway's management API: ``` POST actuator/exporters/{exporterId}/disable ``` After disabling the exporter, no records will be exported to this exporter. Other exporters continue exporting. To delete an exporter permanently from the system, first remove the configuration of the exporter from the application. Then send the following request to the gateway's management API: ``` DELETE actuator/exporters/{exporterId} ``` If the configuration is deleted, the exporter remains in the system but enters a blocked state. This prevents log compaction and thus increases the disk usage. - To fully remove the exporter, it must be deleted using the Management API to ensure all references to it are removed. - To re-add the exporter, restore its configuration in the application properties and restart the system. Alternatively, if you no longer wish to use an exporter, you can disable it using the management API. The exporter can be re-enabled at any time without requiring a system restart. All requests to change the state of the exporters are processed asynchronously. To monitor the status of the exporters, send the following request to the gateway's management API: ``` GET actuator/exporters/ ``` The response is a JSON object that lists all configured exporters with their status: ```json [ { "exporterId": "elasticsearch0", "status": "ENABLED" }, { "exporterId": "elasticsearch1", "status": "DISABLED" } ] ``` --- ## Network ports The broker cluster sits behind the gateway, which handles all requests (via REST and gRPC servers) from clients/workers and forwards events to brokers. To communicate with clients/workers, the gateway will start two different ports to communicate via its REST API (default port 8080) and gRPC (default port 26500). These are respectively controlled via `server.port: 8080` (REST) and `zeebe.gateway.network.port: 26500` (gRPC). Additionally, it will need to communicate with other nodes (mostly brokers) in the cluster (default port 26502), configured via `zeebe.gateway.cluster.port: 26502`. To join the cluster, it will also need at least one initial contact point, typically a broker, configured via `zeebe.gateway.cluster.initialContactPoints: [127.0.0.1:26502]`. :::note You can use all broker connections instead of one to make the startup process of the Zeebe Gateway more resilient. ::: The relevant [configuration](../configuration/configuration.md) settings are: ``` Config file server: port: 8080 # REST zeebe: gateway: network: port: 26500 # gRPC cluster: port: 26502 initialContactPoints: [127.0.0.1:26502] Environment Variables SERVER_PORT = 8080 ZEEBE_GATEWAY_NETWORK_PORT = 26500 ZEEBE_GATEWAY_CLUSTER_PORT = 26502 ZEEBE_GATEWAY_CLUSTER_INITIALCONTACTPOINTS = 127.0.0.1:26502 ``` The broker needs to receive communication from the gateway and from other brokers. It also exposes a port for monitoring. - `zeebe.broker.network.commandApi.port: 26501`: Gateway-to-broker communication, using an internal SBE (Simple Binary Encoding) protocol. This is the Command API port. This should be exposed to the gateway. - `zeebe.broker.network.internalApi.port: 26502`: Inter-broker clustering using the Gossip and Raft protocols for partition replication, broker elections, topology sharing, and message subscriptions. This should be exposed to other brokers and the gateway. - `zeebe.broker.network.monitoringApi.port: 9600`: Metrics and Readiness Probe. Prometheus metrics are exported on the route `/metrics`. There is a readiness probe on `/ready`. The relevant [configuration](../configuration/configuration.md) settings are: ``` Config file zeebe: broker: network: commandAPI: port: 26501 internalAPI: port: 26502 monitoringApi port: 9600 Environment Variables ZEEBE_BROKER_NETWORK_COMMANDAPI_PORT = 26501 ZEEBE_BROKER_NETWORK_INTERNALAPI_PORT = 26501 ZEEBE_BROKER_NETWORK_MONITORINGAPI_PORT = 26501 ``` --- ## Rebalancing Rebalancing is re-electing partition leaders so they are evenly distributed across all brokers. An even leader distribution is beneficial as all brokers share the work of being partition leaders. Zeebe will, by default, prefer an even leader distribution when electing new leaders, but will not trigger a re-election unless a leader becomes unavailable. When a Zeebe cluster uses an uneven leader distribution, caused by losing a leader and thus electing a suboptimal broker as new leader for example, manually requesting rebalancing can restore the cluster to an even leader distribution. ## Manual rebalancing The gateway exposes an HTTP API to request rebalancing. You can use it by `POST`ing to the `/actuator/rebalance` endpoint on the monitoring port of the gateway: ```bash curl -X POST https://{zeebe-gateway}:9600/actuator/rebalance ``` The result of this operation is always `200 OK` with no body, even when rebalancing is [not supported](#limitations) by the current configuration or when not all leaders have been contacted. Track the rebalancing progress by observing [metrics](/self-managed/operational-guides/monitoring/metrics.md). During the rebalancing, partitions might become unhealthy and can't make progress until a new leader is elected. ### Limitations Manual rebalancing is not guaranteed to succeed in all cases. Rebalancing is only supported under specific configurations, and even when supported, the resulting distribution cannot be guaranteed due to the nature of distributed systems. There are two configurations where manual rebalancing is supported: - **Priority election** with **round-robin distribution** - Priority election and round-robin distribution are enabled by default. - As long as you have not manually disabled priority election or set a fixed distribution, rebalancing is supported. - Brokers are automatically assigned as primary partition leaders during startup, based on cluster size and replication factor. - **Priority election** with **fixed distribution** - Fixed distribution is an experimental configuration that is disabled by default. - Brokers are assigned as primary partition leaders based on the configuration. - Only configurations where a partition designates a single broker as primary partition leader are supported. **Priority election** is controlled by the `zeebe.broker.cluster.raft.enablePriorityElection` config and is enabled by default. Learn more about [priority election](../configuration/priority-election.md). **Partition distribution** is controlled by the `zeebe.broker.experimental.partitioning` config options. The default scheme is `ROUND_ROBIN`. All other configurations are not supported and a manual rebalancing will silently fail. The rebalancing request is successfully completed by the gateway, but leaders will ignore the request and no re-election is triggered. Even when a rebalancing request is handled successfully by all leaders, the result of the re-election process is not guaranteed. Followers that are not fully caught up with the leader cannot be elected as leader. This becomes more likely under high load or with increased network latency between leader and follower. ### Rebalancing impact :::note Rebalancing causes every partition leader to step down simultaneously, triggering a new leader election for each partition. If the desired leader for a partition (the node with the highest priority) is _already_ the leader, rebalancing is a no-op for this partition. If a cluster is already perfectly balanced, a rebalancing call is a no-op. ::: During the election period, the affected partition has no leader. While leaderless, a partition cannot process, export, or accept new commands. In the worst case, when all partitions rebalance at the same time, the entire cluster is temporarily unavailable: no workflow engine processing occurs, no new data becomes visible in the web applications, and no client requests are accepted. This is typically observed externally as: - Increased error rates from clients, as requests are rejected while no leader is available - Increased processing latency, as service tasks complete later and process instances progress more slowly - Increased exporting latency, as new data appears in Operate later than expected ### When to rebalance Before triggering a rebalance, verify that it is likely to succeed. As described in [Limitations](#limitations), rebalancing only works if the desired leader for each partition is not lagging behind the current leader. You can verify this using the `atomix_non_replicated_entries` metric, filtering by the `partition` and `follower` labels to determine how far a replica lags behind. The closer this value is to `0`, the more likely rebalancing is to succeed. If the desired leader has a significant lag, triggering a rebalance will cause a temporary performance drop without achieving a better distribution. :::note If you are using Prometheus, you can query the replication lag for a given partition and desired leader with: ```promql sum(atomix_non_replicated_entries{partition=~"$partition", follower=~"$follower"}) by (partition, follower) ``` Replace `$partition` and `$follower` by the desired combination. If you're using the Zeebe Grafana dashboard, you can already visualize this in the `Raft` section, in a graph named `Non replicated records`. ::: Once you have confirmed that rebalancing is likely to succeed, consider the trade-off: rebalancing can improve long-term cluster performance by achieving an optimal leader distribution, but it causes a temporary performance impact and potential unavailability window. Decide whether the long-term benefit outweighs the short-term disruption. :::warn Rebalance only when the cluster is under low load. To determine this, identify what low load means for your specific scenario. What constitutes low load, and whether rebalancing is appropriate at all in a given situation, is ultimately your decision. ::: For example, you could consider the cluster idle if the total leader append rate across all partitions is low, such as below 64 KB/s. At this level of activity, the impact of a rebalance on your users and applications is minimal. :::note If you are using Prometheus, you can query the total replication rate across all partitions with: ```promql sum(rate(atomix_append_entries_data_rate_total[1m])) ``` This returns the cluster replication rate in bytes per second. You can find this in the Zeebe Grafana dashboard under the `Raft` section, visualized as a graph named `Leader append data rate`. ::: --- ## Resource planning The short answer to “_what resources and configuration will I need to take Zeebe to production?_” is: it depends. While we cannot tell you exactly what you need, we can explain what depends, what it depends on, and how it depends on it. ## Disk space All brokers in a partition use disk space to store the following: - The event log for each partition they participate in. By default, this is a minimum of _128MB_ for each partition, incrementing in 128MB segments. The event log is truncated on a given broker when data has been processed and successfully exported by all loaded exporters. - One periodic snapshot of the running state (in-flight data) of each partition (unbounded, based on in-flight work). Additionally, the leader of a partition also uses disk space to store a projection of the running state of the partition in RocksDB (unbounded, based on in-flight work). To calculate the required amount of disk space, the following "back of the envelope" formula can be used as a starting point: ``` neededDiskSpace = replicatedState + localState replicatedState = totalEventLogSize + totalSnapshotSize totalEventLogSize = followerPartitionsPerNode * eventLogSize * reserveForPartialSystemFailure totalSnapshotSize = partitionsPerNode * singleSnapshotSize * 2 // singleSnapshotSize * 2: // the last snapshot (already replicated) + // the next snapshot (in transit, while it is being replicated) partitionsPerNode = leaderPartitionsPerNode + followerPartitionsPerNode leaderPartitionsPerNode = partitionsCount / numberOfNodes followerPartitionsPerNode = partitionsCount * replicationFactor / numberOfNodes clusterSize = [number of broker nodes] partitionsCount = [number of partitions] replicationFactor = [number of replicas per partition] reserveForPartialSystemFailure = [factor to account for partial system failure] singleSnapshotSize = [size of a single rocks DB snapshot] eventLogSize = [event log size for duration of snapshotPeriod] ``` Some observations on the scaling of the factors above: - `eventLogSize`: This factor scales with the throughput of your system. - `totalSnapshotSize`: This factor scales with the number of in-flight processes. - `reserveForPartialSystemFailure`: This factor is supposed to be a reserve to account for partial system failure (e.g. loss of quorum inside Zeebe cluster, or loss of connection to external system). See the remainder of this document for a further discussion on the effects of partial system failure on Zeebe cluster and disk space provisioning. Many of the factors influencing the above formula can be fine-tuned in the [configuration](../configuration/configuration.md). The relevant configuration settings are: ```yaml Config file zeebe: broker: data: logSegmentSize: 128MB snapshotPeriod: 5m cluster: partitionsCount: 1 replicationFactor: 1 clusterSize: 1 Environment Variables ZEEBE_BROKER_DATA_LOGSEGMENTSIZE = 128MB ZEEBE_BROKER_DATA_SNAPSHOTPERIOD = 5m ZEEBE_BROKER_CLUSTER_PARTITIONSCOUNT = 1 ZEEBE_BROKER_CLUSTER_REPLICATIONFACTOR = 1 ZEEBE_BROKER_CLUSTER_CLUSTERSIZE = 1 ``` Other factors can be observed in a production-like system with representative throughput. By default, this data is stored in the following: - `segments` - The data of the log split into segments. The log is only appended, and its data can be deleted when it becomes part of a new snapshot. - `state` - The active state. Deployed processes, active process instances, etc. Completed process instances or jobs are removed. - `snapshot` - A state at a certain point in time. > **Pitfalls** > > To avoid exceeding your disk space, here are a few pitfalls to avoid: > > - Do not configure an exporter which does not advance its record position (such as the Debug Exporter). If you do configure an exporter, ensure you monitor its availability and health, as well as the availability and health the exporter depends on. This is the Achilles' heel of the cluster. If data cannot be exported, it cannot be removed from the cluster and will accumulate on disk. See _effect of exporters and external system failure_ further on in this document for an explanation and possible buffering strategies. ### Event log The event log for each partition is segmented. By default, the segment size is 128MB. The event log grows over time, unless and until individual event log segments are deleted. An event log segment can be deleted once: - All the events it contains have been processed by exporters. - All the events it contains have been replicated to other brokers. - All the events it contains have been processed. The following conditions inhibit the automatic deletion of event log segments: - A cluster loses its quorum. In this case, events are queued but not processed. Once a quorum is reestablished, events are replicated and eventually event log segments are deleted. - An exporter does not advance its read position in the event log. In this case, the event log grows ad infinitum. An event log segment is not deleted until all the events in it are exported by all configured exporters. This means exporters that rely on side effects, perform intensive computation, or experience backpressure from external storage will cause disk usage to grow, as they delay the deletion of event log segments. Exporting is only performed on the partition leader, but the followers of the partition do not delete segments in their replica of the partition until the leader marks all events in it as unneeded by exporters. We make sure that event log segments are not deleted too early. No event log segment is deleted until a snapshot is taken that includes that segment. When a snapshot is taken, the event log is only deleted up to that point. ### Snapshots The running state of the partition is captured periodically on the leader in a snapshot. By default, this period is every five minutes. This can be changed in the [configuration](../configuration/configuration.md). A snapshot is a projection of all events that represent the current running state of the processes running on the partition. It contains all active data, for example, deployed processes, active process instances, and not yet completed jobs. When the broker writes a new snapshot, it deletes all data on the log which was written before the latest snapshot. :::note We tested the snapshot interval via a Zeebe Chaos experiment. Learn more about this experiment and snapshot intervals in our [Zeebe Chaos blog](https://camunda.github.io/zeebe-chaos/2022/02/01/High-Snapshot-Frequency/#snapshot-interval). ::: ### RocksDB On the lead broker of a partition, the current running state is kept in memory and on disk in RocksDB. In our experience, this grows to 2GB under a heavy load of long-running processes. The snapshots replicated to followers are snapshots of RocksDB. ### Effect of exporters and external system failure If an external system relied on by an exporter fails (for example, if you are exporting data to Elasticsearch and the connection to the Elasticsearch cluster fails), the exporter will not advance its position in the event log, and brokers cannot truncate their logs. The broker event log grows until the exporter is able to reestablish the connection and export the data. To ensure your brokers are resilient in the event of external system failure, give them sufficient disk space to continue operating without truncating the event log until the connection to the external system is restored. ### Effect on exporters of node failure Only the leader of a partition exports events. Only committed events (events that have been replicated) are passed to exporters. The exporter then updates its read position. The exporter read position is only replicated between brokers in the snapshot. It is not itself written to the event log. This means _an exporter’s current position cannot be reconstructed from the replicated event log, only from a snapshot_. When a partition fails over to a new leader, the new leader is able to construct the current partition state by projecting the event log from the point of the last snapshot. The position of exporters cannot be reconstructed from the event log, so it is set to the last snapshot. This means an exporter can see the same events twice in the event of a fail-over. You should assign idempotent ids to events in your exporter if this is an issue for your system. The combination of record position and partition ID is reliable as a unique ID for an event. ### Effect of quorum loss If a partition goes under quorum (for example, if two nodes in a 3-node cluster go down), the leader of the partition continues to accept requests, but these requests are not replicated and are not marked as committed. In this case, they cannot be truncated. This causes the event log to grow. The amount of disk space needed to continue operating in this scenario is a function of the broker throughput and the amount of time to quorum being restored. You should ensure your nodes have sufficient disk space to handle this failure mode. ## Memory Memory usage is determined by the Java heap size (by default, [25% of the maximum RAM](https://docs.oracle.com/en/java/javase/21/gctuning/ergonomics.html#GUID-DA88B6A6-AF89-4423-95A6-BBCBD9FAE781)) and native memory usage (also 25% by default). As a result, the JVM can use up to 50% of the available RAM. Zeebe supports multiple RocksDB memory allocation strategies, configured via the `CAMUNDA_DATA_PRIMARYSTORAGE_ROCKSDB_MEMORYALLOCATIONSTRATEGY` setting in the broker configuration: - `PARTITION` (default): Total RocksDB memory equals the actual number of partitions per broker (from the cluster topology) multiplied by the configured memory limit. - `BROKER`: Total RocksDB memory equals the configured memory limit, regardless of the number of partitions. - `FRACTION`: RocksDB memory is allocated as a fraction of total available memory. When using the `FRACTION` strategy, configure the fraction using `CAMUNDA_DATA_PRIMARYSTORAGE_ROCKSDB_MEMORYFRACTION` (range `[0,1]`). The default is `0.1` (10% of total memory). For the `PARTITION` and `BROKER` strategies, the default value of `CAMUNDA_DATA_PRIMARYSTORAGE_ROCKSDB_MEMORYLIMIT` allocates [512 MB](https://github.com/camunda/camunda/blob/main/dist/src/main/config/defaults.yaml). When hardcoding memory values, consider the following: - Zeebe relies heavily on memory-mapped files, so sufficient OS page cache is required. - Insufficient page cache can lead to degraded I/O performance. The amount of OS page cache needed depends on factors such as the number of partitions on a node and system throughput. For most use cases, reserve 20–30% of total memory for the OS page cache, adjusting as needed based on observed performance. The minimum memory usage (when using the `PARTITION` strategy) is: | Component | Amount | | ------------------- | -----------------------: | | Java Heap | 25% | | Java Native Memory | 25% | | RocksDB | 512MB \* partitionCount | | OS Page Cache | 20-30% | | ------------------- | ------------------------ | | Sum | x MB + 50% of max RAM | | ------------------- | ------------------------ | --- ## Setting up a Zeebe cluster To set up a cluster, you need to adjust the `cluster` section in the Zeebe configuration file. Below is a snippet of the default Zeebe configuration file: ```yaml --- cluster: # This section contains all cluster related configurations, to setup a zeebe cluster # Specifies the unique id of this broker node in a cluster. # The id should be between 0 and number of nodes in the cluster (exclusive). # # This setting can also be overridden using the environment variable ZEEBE_BROKER_CLUSTER_NODEID. nodeId: 0 # Controls the number of partitions, which should exist in the cluster. # # This can also be overridden using the environment variable ZEEBE_BROKER_CLUSTER_PARTITIONSCOUNT. partitionsCount: 1 # Controls the replication factor, which defines the count of replicas per partition. # The replication factor cannot be greater than the number of nodes in the cluster. # # This can also be overridden using the environment variable ZEEBE_BROKER_CLUSTER_REPLICATIONFACTOR. replicationFactor: 1 # Specifies the zeebe cluster size. This value is used to determine which broker # is responsible for which partition. # # This can also be overridden using the environment variable ZEEBE_BROKER_CLUSTER_CLUSTERSIZE. clusterSize: 1 # Allows to specify a list of known other nodes to connect to on startup # The contact points of the internal network configuration must be specified. # The format is [HOST:PORT] # Example: # initialContactPoints : [ 192.168.1.22:26502, 192.168.1.32:26502 ] # # To guarantee the cluster can survive network partitions, all nodes must be specified # as initial contact points. # # This setting can also be overridden using the environment variable ZEEBE_BROKER_CLUSTER_INITIALCONTACTPOINTS # specifying a comma-separated list of contact points. # Default is empty list: initialContactPoints: [] # Allows to specify a name for the cluster # This setting can also be overridden using the environment variable ZEEBE_BROKER_CLUSTER_CLUSTERNAME. # Example: clusterName: zeebe-cluster ``` ## Example In this example, we will set up a Zeebe cluster with five brokers. Each broker needs to get a unique node id. To scale well, we will bootstrap five partitions with a replication factor of three. For more information about this, take a look into the [clustering](/components/zeebe/technical-concepts/clustering.md) section. The clustering setup will look like this: ![cluster](assets/example-setup-cluster.png) ## Configuration The configuration of the first broker could look like this: ```yaml --- cluster: nodeId: 0 partitionsCount: 5 replicationFactor: 3 clusterSize: 5 initialContactPoints: [ ADDRESS_AND_PORT_OF_NODE_0, ADDRESS_AND_PORT_OF_NODE_1, ADDRESS_AND_PORT_OF_NODE_2, ADDRESS_AND_PORT_OF_NODE_3, ADDRESS_AND_PORT_OF_NODE_4, ] ``` For the other brokers, the configuration will slightly change: ```yaml --- cluster: nodeId: NODE_ID partitionsCount: 5 replicationFactor: 3 clusterSize: 5 initialContactPoints: [ ADDRESS_AND_PORT_OF_NODE_0, ADDRESS_AND_PORT_OF_NODE_1, ADDRESS_AND_PORT_OF_NODE_2, ADDRESS_AND_PORT_OF_NODE_3, ADDRESS_AND_PORT_OF_NODE_4, ] ``` Each broker needs a unique node id. The ids should be in the range of zero and `clusterSize - 1`. You need to replace the `NODE_ID` placeholder with an appropriate value. Additionally, the brokers need an initial contact point to start their gossip conversation. Make sure you use the address and **management port** of another broker. You need to replace the `ADDRESS_AND_PORT_OF_NODE_0` placeholder. To guarantee a cluster can properly recover from network partitions, it is currently required that all nodes be specified as initial contact points. It is not necessary for a broker to list itself as an initial contact point, but it is safe to do so, and likely simpler to maintain. ## Partitions bootstrapping On bootstrap, each node will create a partition matrix. This matrix depends on the partitions count, replication factor and the cluster size. If you completed the configuration correctly and used the same values for `partitionsCount`, `replicationFactor`, and `clusterSize` on each node, all nodes will generate the same partition matrix. For the current example, the matrix will look like the following: | | Node 0 | Node 1 | Node 2 | Node 3 | Node 4 | | ----------- | -------- | -------- | -------- | -------- | -------- | | Partition 0 | Leader | Follower | Follower | - | - | | Partition 1 | - | Leader | Follower | Follower | - | | Partition 2 | - | - | Leader | Follower | Follower | | Partition 3 | Follower | - | - | Leader | Follower | | Partition 4 | Follower | Follower | - | - | Leader | The matrix ensures the partitions are well distributed between the different nodes. Furthermore, it guarantees each node knows exactly which partitions it has to bootstrap and for which it will become the leader at first (this could change later, if the node needs to step down for example.) ## Keep alive intervals It's possible to specify how often Zeebe clients should send keep alive pings. By default, the [official Zeebe clients](/apis-tools/working-with-apis-tools.md#official-zeebe-clients) send keep alive pings every 45 seconds. This interval can be configured through the clients' APIs and through the `ZEEBE_KEEP_ALIVE` environment variable. When configuring the clients with the environment variable, the time interval must be expressed a positive amount of milliseconds (e.g., 45000). It's also possible to specify the minimum interval allowed by the gateway before it terminates the connection. By default, gateways terminate connections if they receive more than two pings with an interval less than 30 seconds. This minimum interval can be modified by editing the network section in the respective configuration file or by setting the `ZEEBE_GATEWAY_NETWORK_MINKEEPALIVEINTERVAL` environment variable. --- ## Upgrade Zeebe Zeebe versions can be upgraded: - From any patch version to a newer patch of the same minor version - To any patch of the next minor version. Since Zeebe 8.5, upgrades to a newer version can be rolling or offline. Zeebe 8.4 and older don't contain necessary safety checks that make rolling upgrades safe, and we recommend offline upgrades instead to ensure processing behaves correctly. :::info Review the [upgrade guide](/self-managed/upgrade/components/index.md) for general upgrade procedures you must follow and to check for known issues relating to the specific upgrade you are planning. ::: ## Rolling update A **rolling update** ensures the Zeebe cluster stays available by upgrading brokers and gateways one by one instead of all at once. There are three parts to a rolling update: the Zeebe Broker, Zeebe Gateway, and clients. We recommend upgrading brokers first, then gateways, and finally clients. This ensures clients don't use new APIs that are not yet supported by the brokers or gateways. Because Zeebe is backwards compatible with the previous minor version, upgrading gateways and clients is not strictly necessary and can happen any time after upgrading the brokers. While upgrading brokers, leadership for partitions will rotate which may cause brief unavailability or loss of performance on the affected partitions. The procedure to do a rolling update of Zeebe brokers is the following: 1. Pick the broker with the highest ID that runs the old version. 2. Shut down the broker. 3. Upgrade the broker software to the new version. 4. Start the broker and wait for it to become ready and healthy. 5. Repeat until all brokers are upgraded. Gateways are upgraded with the same procedure, upgrading each replica one by one. Clients can be upgraded according to your requirements and environment, for example by simply deploying a new version of your worker applications. For disaster recovery, you may want to take [backups](/self-managed/operational-guides/backup-restore/backup-and-restore.md) before the upgrade. If you plan to immediately upgrade again, wait to give all brokers a chance to take new snapshots. The snapshot period is five minutes by default but is [configurable via `snapshotPeriod`](../configuration/broker.md#zeebebrokerdata). ### Using Helm If your Zeebe deployment is managed by our [Helm charts](/self-managed/deployment/helm/install/quick-install.md), the rolling update procedure is already automated. :::note Zeebe brokers are managed by a [`StatefulSet`](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies). Zeebe Gateways are managed by a [`Deployment`](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#updating-a-deployment). ::: #### Upgrading brokers Ensure the `StatefulSet` for brokers is ready to do a rolling update by checking that: - The update strategy is `RollingUpdate`. - All replicas are ready. - The version is at least 8.5.0. The following is an example how to verify these properties. Depending on your environment, you may have to adjust these commands slightly. ``` $ kubectl get statefulsets -l app.kubernetes.io/component=zeebe-broker -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.updateStrategy.type}{"\n"}{end}' camunda-platform-zeebe RollingUpdate $ kubectl rollout status statefulset -l app.kubernetes.io/component=zeebe-broker statefulset rolling update complete 3 pods at revision camunda-platform-zeebe-d69689fbc... $ kubectl get services -l app.kubernetes.io/component=zeebe-gateway NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE camunda-platform-zeebe-gateway ClusterIP 10.96.227.153 9600/TCP,26500/TCP,8080/TCP 21m $ kubectl port-forward services/camunda-platform-zeebe-gateway -p 8080:8080 & $ curl localhost:8080/api/v1/topology | jq .brokers[].version && kill %1 8.5.0 8.5.0 8.5.0 ``` To start the rolling update, upgrade the Helm deployment to use a new version of Zeebe. Set `$NEW_ZEEBE_VERSION` to the version you want to upgrade to, for example `8.5.2`. Remember to read the [upgrade guide](/self-managed/upgrade/components/index.md) to check for known issues. Then, start the rolling update with `helm upgrade`. ``` $ export $NEW_ZEEBE_VERSION=8.5.2 $ helm upgrade camunda-platform camunda/camunda-platform --reuse-values --set zeebe.image.tag=$NEW_ZEEBE_VERSION ``` Then, wait for the rolling update to complete: ``` $ kubectl rollout status statefulset -l app.kubernetes.io/component=zeebe-broker Waiting for 3 pods to be ready... Waiting for 2 pods to be ready... Waiting for 1 pods to be ready... statefulset rolling update complete 3 pods at revision camunda-platform-zeebe-5b7f7d6477... ``` When the command finishes, all Zeebe brokers are upgraded to the new version and should be ready. We can verify this by running the command to check versions again: ```shell $ kubectl port-forward services/camunda-platform-zeebe-gateway -p 8080:8080 & $ curl localhost:8080/api/v1/topology | jq .brokers[].version && kill %1 8.5.2 8.5.2 8.5.2 ``` #### Upgrading gateways Ensure the deployment of gateways is ready to do a rolling update by checking that: - All replicas are ready. - The version is at least 8.5. You can use the following command to verify this: ``` $ kubectl rollout status statefulset -l app.kubernetes.io/component=zeebe-gateway NAME READY UP-TO-DATE AVAILABLE AGE camunda-platform-zeebe-gateway 2/2 2 2 4h25m ``` Then, upgrade the version via Helm: ``` $ helm upgrade camunda-platform camunda/camunda-platform --reuse-values --set zeebe-gateway.image.tag=$NEW_ZEEBE_VERSION ``` Wait for the upgrade to complete: ``` $ kubectl rollout status -l app.kubernetes.io/component=zeebe-gateway ``` At this point, both brokers and gateways are upgraded. Your client applications are ready to be upgraded as well and can start using features added in the new version. ### Troubleshooting #### Rolling update is not completing A rolling update can become stuck due to outside interference such as failing Kubernetes pods. To recover from this, the upgrade can be forced by not waiting on each broker to become ready and instead directly upgrading all brokers at once. Assuming you use our Helm charts, this can be done via the following command: ``` $ kubectl delete pod -l app.kubernetes.io/component=zeebe-broker ``` This will recreate all broker pods from scratch, running the new version. Because the upgrade is no longer rolling and all brokers are shut down at the same time, a short downtime is to be expected. #### Failed to install partition If upgraded brokers log the error message `Failed to install partition` and do not become healthy, look for more details to understand if this is caused by the rolling update. If the error is caused by `Cannot upgrade to or from a pre-release version`, Zeebe detected that either the version you started from or the version you upgraded to is a pre-release version. This is not permitted because pre-release versions such as alpha releases are considered unstable and do not guarantee compatibility with any other version. :::note If you attempted to upgrade from a minor release to a pre-release or alpha version, it is possible to roll back to the previous version of Zeebe. Note that version rollbacks are not supported in most other instances. ::: If the log message includes `Snapshot is not compatible with current version`, the rolling update failed and manual recovery is required. :::note This message can also be logged by not yet upgraded brokers, in which case it should resolve itself automatically as soon as the [rolling update completes](#rolling-update-is-not-completing). ::: The exact scenario is further described in the log message and can be one of the following: ##### Snapshot is not compatible with current version: `SkippedMinorVersion` This normally occurs when attempting an upgrade from one minor version to a newer one while skipping minor versions in between. For example, upgrading from 8.5 to 8.7 directly without first upgrading to 8.6. This is not supported and Zeebe refuses to run when detecting this. To recover, you may be able to roll back to the previous version and then upgrade to the next minor version first. Another much more unlikely cause may be that you upgraded multiple times before Zeebe brokers could take snapshots. For example, if you first upgrade from 8.5 to 8.6 and then immediately to 8.7, the upgraded brokers running 8.7 may find snapshots taken by 8.5 and refuse to run. You can recover from this manually. ##### Snapshot is not compatible with current version: `PatchDowngrade` or `MinorDowngrade` These indicate a deliberate version downgrade which Zeebe does not support. If you mistakenly tried to downgrade either the patch or minor version, you can restore by switching to the original version again. :::note This message may show up during a rolling update on not yet upgraded broker. In that case, it is caused by upgraded brokers sharing snapshots with not yet upgraded brokers. This should resolve automatically once the broker is upgraded. ::: If this persists, you can [force the upgrade](#rolling-update-is-not-completing). Alternatively, it is possible to restart Zeebe with the "skipped" minor version. Note that version rollbacks are not supported in most other instances. ## Offline upgrade See the [upgrade guide](/self-managed/upgrade/components/index.md) for specific instructions per Zeebe version. To upgrade a Zeebe cluster, take the following steps: 1. Shut down all Zeebe brokers and other components of the system. 1. Take a [backup](./backups.md) of your Zeebe brokers and Elasticsearch `data` folder if used. 1. Upgrade all Zeebe brokers and gateways to the new version. 1. Restart the system components. ## Partitions admin endpoint This endpoint allows querying the status of the partitions and performing operations to prepare an upgrade. The endpoint is available under `http://{zeebe-broker}:{zeebe.broker.network.monitoringApi.port}/actuator/partitions` (default port: `9600`). It is enabled by default. It can be disabled in the configuration by setting: ``` management.endpoint.partitions.enabled=false ``` ### Query the partition status The status of the partitions can be queried with a `GET` request: ``` /actuator/partitions ``` The response contains all partitions of the broker mapped to the partition-id.
Full Response ``` { "1":{ "role":"LEADER", "snapshotId":"399-1-1601275126554-490-490", "processedPosition":490, "processedPositionInSnapshot":490, "streamProcessorPhase":"PROCESSING" } } ```
--- ## Operating Zeebe in production This chapter covers topics relevant to anyone who wants to operate Zeebe in production. - [Resource planning](resource-planning.md) - Gives an introduction for calculating how many resources need to be provisioned. - [Network ports](network-ports.md) - Discusses which ports are needed to run Zeebe. - [Setting up a Zeebe cluster](setting-up-a-cluster.md) - Quick guide on how to set up a cluster with multiple brokers. - [Health status](health.md) - Lists available high-level health and liveness probes. - [Backpressure](backpressure.md) - Discusses the backpressure mechanism used by Zeebe brokers. - [Disk space](disk-space.md) - Explains how to set limits for the amount of free disk space. Once these limits are undercut, Zeebe degrades gracefully to allow the operations team to provide more disk space. - [Upgrade Zeebe](update-zeebe.md) - Contains information on how to perform a shutdown upgrade. - [Rebalancing](rebalancing.md) - Describes how to rebalance a cluster. --- ## Zeebe on Self-Managed [Zeebe](/components/zeebe/zeebe-overview.md) is the process automation engine component within the Orchestration Cluster. Within this section you will find detailed information about: - [Zeebe Gateway](zeebe-gateway/zeebe-gateway-overview.md) - The Zeebe Gateway is a component of the Zeebe cluster; it can be considered the contact point for the Zeebe cluster which allows Zeebe clients to communicate with Zeebe brokers inside a Zeebe cluster. - [Configuration](configuration/configuration.md) - Explains the configuration options. These configuration options apply to both environments, but not to Camunda 8. In Camunda 8, the configuration is provided for you. - [Security](security/security.md) - Discusses the security aspects of running Zeebe and how to use them. - [Operation](operations/zeebe-in-production.md) - Outlines topics that become relevant when you want to operate Zeebe in production. - [Exporters](exporters/exporters.md) - The Orchestration Cluster includes built-in exporters for [Elasticsearch](exporters/elasticsearch-exporter.md), [OpenSearch](exporters/opensearch-exporter.md), [Camunda Exporter](exporters/camunda-exporter.md), and RDBMS (see [RDBMS configuration](/self-managed/concepts/databases/relational-db/configuration.md)). This section explains how exporters can be configured. For a general overview, refer to our [exporters concept](/self-managed/concepts/exporters.md) page. For broader guidance on secondary storage options, see [secondary storage](/self-managed/concepts/secondary-storage/index.md). :::note New to BPMN and want to learn more before moving forward? [Visit our getting started guide](/components/modeler/bpmn/automating-a-process-using-bpmn.md) to learn about automating a process using BPMN. ::: --- ## Client authorization The Zeebe Gateway supports [Orchestration Cluster Admin](/self-managed/components/orchestration-cluster/admin/overview.md)-based auth token validation. In the Camunda 8 Self-Managed Helm chart, authentication is enabled by default via Camunda Admin. ## Camunda Admin authorization [Orchestration Cluster Admin](/self-managed/components/orchestration-cluster/admin/overview.md)-based OAuth token validation can be enabled by setting `security.authentication.mode` to `identity` and providing the corresponding `camunda.identity.*` properties. You can find more details about these in the [Camunda Management Identity documentation](/self-managed/components/management-identity/miscellaneous/configuration-variables.md#core-configuration). The Camunda 8 Self-Managed Helm chart is already fully preconfigured by default. ### YAML snippet ```yaml security: authentication: mode: identity identity: issuerBackendUrl: http://keycloak:18080/auth/realms/camunda-platform audience: zeebe-api type: keycloak ``` With authentication enabled, every request to the Gateway requires a valid auth token in the `Authorization` header, granting access to the configured `security.authentication.identity.audience`, issued by the configured `security.authentication.identity.issuerBackendUrl`. The `zeebe-api` audience is already pre-configured in Camunda Admin. The authentication could be disabled by setting `security.authentication.mode: none` in the Gateway configuration file or via `ZEEBE_GATEWAY_SECURITY_AUTHENTICATION_MODE=none` as environment variable. ## Client Zeebe clients also provide a way for users to modify request headers, namely to contain access tokens. Users can modify request headers using Zeebe's built-in `OAuthCredentialsProvider`, which uses user-specified credentials to contact an OAuth authorization server. The authorization server should return an access token that is then appended to each request. Although, by default `OAuthCredentialsProvider` is configured with to use a Camunda 8 authorization server, it can be configured to use any user-defined server. Users can also write a custom [CredentialsProvider](https://github.com/camunda/camunda/blob/main/clients/java/src/main/java/io/camunda/client/CredentialsProvider.java). In the following sections, we'll describe the usage of the default `OAuthCredentialsProvider` as well as the `CredentialsProvider` interface that can be extended for implementing a custom provider. ### OAuthCredentialsProvider The `OAuthCredentialsProvider` requires the specification of a client ID and a client secret. These are then used to request an access token from an OAuth 2.0 authorization server through a [client credentials flow](https://tools.ietf.org/html/rfc6749#section-4.4). By default, the authorization server is the one used by Camunda 8, but any other can be used. Using the access token returned by the authorization server, the `OAuthCredentialsProvider` adds it to the gRPC headers of each request as a bearer token. Requests which fail with due to authentication errors (i.e. HTTP 401 or `UNAUTHENTICATED` gRPC code) are seamlessly retried only if a new access token can be obtained. #### Java To use the Zeebe client with Camunda 8, first an `OAuthCredentialsProvider` must be created and configured with the appropriate client credentials. The `audience` should be equivalent to the cluster endpoint without a port number. ```java public class AuthorizedClient { public void main(String[] args) { final OAuthCredentialsProvider provider = new OAuthCredentialsProviderBuilder() .clientId("clientId") .clientSecret("clientSecret") .audience("cluster.endpoint.com") // optional .scope("scope") .build(); final CamundaClient client = new CamundaClientBuilderImpl() .gatewayAddress("cluster.endpoint.com:443") .credentialsProvider(provider) .build(); System.out.println(client.newTopologyRequest().send().join().toString()); } } ``` For security reasons, client secrets should not be hard coded. Therefore, it's recommended to use environment variables to pass client secrets into Zeebe. See [`ZEEBE_*` environment variables](#environment-variables) for the supported variable names. After setting the environment variables corresponding to the properties set on `OAuthCredentialsProviderBuilder` to the correct values, the following would be equivalent to the previous code: ```java public class AuthorizedClient { public void main(final String[] args) { final CamundaClient client = new CamundaClientBuilderImpl() .gatewayAddress("cluster.endpoint.com:443") .build(); System.out.println(client.newTopologyRequest().send().join().toString()); } } ``` The client creates an `OAuthCredentialProvider` with the credentials specified through the environment variables and the audience is extracted from the address specified through the `CamundaClientBuilder`. :::note Zeebe's Java client will not prevent you from adding credentials to requests while using an insecure connection, but you should be aware that doing so will expose your access token by transmitting it in plaintext. ::: #### Environment variables Since there are several environment variables that can be used to configure an `OAuthCredentialsProvider`, we list them here along with their uses: - `ZEEBE_CLIENT_ID` - The client ID used to request an access token from the authorization server. - `ZEEBE_CLIENT_SECRET` - The client secret used to request an access token from the authorization server. - `ZEEBE_TOKEN_AUDIENCE` - The audience for which the token should be valid. - `ZEEBE_TOKEN_SCOPE` - The [OAuth scope](https://oauth.net/2/scope/) which can be set optionally, not sent if left unset. - `ZEEBE_AUTHORIZATION_SERVER_URL` - The URL of the authorization server from which the access token will be requested (by default, configured for Camunda 8). - `ZEEBE_CLIENT_CONFIG_PATH` - The path to a cache file where the access tokens will be stored (by default, it's `$HOME/.camunda/credentials`). ### Custom Credentials provider As previously mentioned, the `CredentialProvider`'s purpose is to modify the HTTP headers with an authorization method. The interface consists of an `applyCredentials(CredentialsApplier)` method and a `shouldRetryRequest(StatusCode)` method. - `applyCredentials(CredentialsApplier)`: Called on every request (both REST and gRPC). The applier lets you add any headers to the request before it's sent. - `shouldRetryRequest(StatusCode)`: Called every time a request completed with a non-successful status code. The `StatusCode` argument lets you inspect the raw HTTP or gRPC code, and provides a convenient method to check the request had wrong credentials (`StatusCode#isUnauthorized`). The following sections implement custom provider in Java: #### Java ```java public class MyCredentialsProvider implements CredentialsProvider { /** * Logs in as demo:demo */ @Override public void applyCredentials(final CredentialsApplier applier) { applier.put("Authorization", "Basic ZGVtbzpkZW1vCg=="); } @Override public boolean shouldRetryRequest(final StatusCode status) { return status.isUnauthorized(); } } ``` After implementing the `CredentialsProvider`, we can provide it when building a client: ```java public class SecureClient { public static void main(final String[] args) { final CamundaClient client = CamundaClient.newClientBuilder().credentialsProvider(new MyCredentialsProvider()).build(); // continue... } } ``` --- ## Secure client communication Zeebe supports transport layer security (TLS v1.3) between the gateway and all the officially supported clients. In this section, we will review how to configure these components. ## Gateway TLS in the gateway is disabled by default. This means that if you are just experimenting with Zeebe or in development, there is no configuration needed. However, if you want to enable authentication, we strongly recommend you enable TLS between the client and the gateway. To do so, you will need to configure both the REST and gRPC parts of the gateway separately. :::note You can use different certificates, or even reuse the same configuration for both protocols, as each are configured independently from the other. ::: ### gRPC You can configure TLS for the gRPC gateway in the `security` section of the configuration files. The following configurations are present in both `gateway.default.yaml` and `config/defaults.yaml`, the file you should edit depends on whether you are using a standalone gateway or an embedded gateway. ```yaml --- security: # Enables TLS authentication between clients and the gateway enabled: false # Sets the path to the certificate chain file certificateChainPath: # Sets the path to the private key file location privateKeyPath: ``` `enabled` should be either `true` or `false`, where true will enable TLS authentication between client and gateway, and false will disable it. `certificateChainPath` and `privateKeyPath` are used to configure the certificate with which the server will authenticate itself. `certificateChainPath` should be a file path pointing to a certificate chain in PEM format representing the server's certificate, and `privateKeyPath` is a file path pointing to the certificate's PKCS8 private key, also in PEM format. Additionally, as you can see in the configuration file, each value can also be configured through an environment variable. The environment variable to use again depends on whether you are using a standalone gateway or an embedded gateway. ### REST The REST server is simply a Spring Boot server, and as such, [any of the common server properties can be applied to it](https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#appendix.application-properties.server). Note that, as of 8.5.0, it is a _reactive_ server, meaning **none of the servlet properties will have any effect**. To enable TLS for the REST server, you will need to configure the properties under the `server.ssl` tree. To enable TLS, you will need to at least specify a certificate and private key. This can be done using a PEM certificate chain file with its private key, as seen in the gRPC section, or via a key store. For example, with a PEM file: ```yaml server: ssl: enabled: true certificate: /path/to/my/certificate.pem certificate-private-key: /path/to/my/private.key ``` [This Spring blog post](https://spring.io/blog/2023/06/07/securing-spring-boot-applications-with-ssl) provides a great tutorial on how to use other options to configure the server security. ## Clients Unlike the Zeebe Gateway, TLS is enabled by default in all of Zeebe's supported clients. The following sections show how to disable or properly configure each client. :::note Disabling TLS should only be done for testing or development. During production deployments, clients and gateways should be properly configured to establish secure connections. ::: ### Java Without any configuration, the client looks in the system's certificate store for a CA certificate with which to validate the Zeebe Gateway's certificate chain. If you wish to use TLS without having to install a certificate in client's system, you can specify a CA certificate: ```java public class SecureClient { public static void main(final String[] args) { final CamundaClient client = CamundaClient.newClientBuilder().caCertificatePath("path/to/certificate").build(); // ... } } ``` Alternatively, use the `ZEEBE_CA_CERTIFICATE_PATH` environment variable to override the code configuration. To disable TLS in a Java client, use the `ZEEBE_INSECURE_CONNECTION` environment variable. To enable an insecure connection, set it to **true**. To use a secure connection, set it to any non-empty value other than **true**. Setting the environment variable to an empty string is equivalent to unsetting it. ## Self signed certificates It may be useful, for testing or development purposes, to use TLS between the client and the gateway; to simplify things, we can use self-signed certificates for this. ### Testing & example To generate your own self-signed certificates for testing/development, you will need `openssl` install on your local machine. Then you can run: ```sh openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 --nodes -addext 'subjectAltName=IP:127.0.0.1' ``` This will generate a new certificate, `cert.pem`, and a new passwordless key, `key.pem`. :::danger Do not use these in production! Again, this is for development and testing purposes only. ::: Then start up your gateway with the certificate and key specified above. For example, if we run a broker with an embedded gateway directly using Docker: ```sh docker run -p 26500:26500 -e ZEEBE_BROKER_NETWORK_HOST=0.0.0.0 -e ZEEBE_BROKER_GATEWAY_SECURITY_ENABLED=true -e ZEEBE_BROKER_GATEWAY_SECURITY_CERTIFICATECHAINPATH=/usr/local/zeebe/cert.pem -e ZEEBE_BROKER_GATEWAY_SECURITY_PRIVATEKEYPATH=/usr/local/zeebe/key.pem --mount type=bind,source="$(pwd)"/cert.pem,target=/usr/local/zeebe/cert.pem --mount type=bind,source="$(pwd)"/key.pem,target=/usr/local/zeebe/key.pem camunda/zeebe ``` There is one caveat: in order for the client to accept this self-signed certificate, you will need to trust it. The simplest way is to specify it as part of the client's configuration. Refer to the documentation above on how to configure your clients. ## Troubleshooting authentication issues Here we will describe a few ways the clients and gateway could be misconfigured and what those errors look like. Hopefully, this will help you recognize these situations and provide an easy fix. :::note `zbctl` is a [community-supported client](https://github.com/camunda-community-hub/zeebe-client-go/blob/main/cmd/zbctl/zbctl.md). ::: ### TLS is enabled in `zbctl` but disabled in the gateway The client will fail with the following error: ``` Error: rpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection error: desc = "transport: authentication handshake failed: tls: first record does not look like a TLS handshake" ``` The following error will be logged by Netty in the gateway: ``` Aug 06, 2019 4:23:22 PM io.grpc.netty.NettyServerTransport notifyTerminated INFO: Transport failed io.netty.handler.codec.http2.Http2Exception: HTTP/2 client preface string missing or corrupt. Hex dump for received bytes: 1603010096010000920303d06091559c43ec48a18b50c028 at io.netty.handler.codec.http2.Http2Exception.connectionError(Http2Exception.java:103) at io.netty.handler.codec.http2.Http2ConnectionHandler$PrefaceDecoder.readClientPrefaceString(Http2ConnectionHandler.java:306) at io.netty.handler.codec.http2.Http2ConnectionHandler$PrefaceDecoder.decode(Http2ConnectionHandler.java:239) at io.netty.handler.codec.http2.Http2ConnectionHandler.decode(Http2ConnectionHandler.java:438) at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:505) at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:444) at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:283) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352) at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1421) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360) at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:930) at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:794) at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:424) at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:326) at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:918) at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.lang.Thread.run(Thread.java:748) ``` **Solution:** Either enable TLS in the gateway as well or specify the `--insecure` flag when using `zbctl`. ### TLS is disabled in `zbctl` but enabled for the gateway `zbctl` will fail with the following error: ``` Error: rpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection closed ``` **Solution:** Either enable TLS in the client by specifying a path to a certificate or disable it in the gateway by editing the appropriate configuration file. ### TLS is enabled for both client and gateway but the CA certificate can't be found `zbctl` will fail with the following error: ``` Error: rpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection error: desc = "transport: authentication handshake failed: x509: certificate signed by unknown authority ``` **Solution:** Either install the CA certificate in the appropriate location for the system or specify a path to certificate using the methods described above. --- ## Secure cluster communication :::note TLS between nodes in the same cluster is disabled by default. ::: Zeebe supports transport layer security (TLS v1.3) between all nodes in a Zeebe cluster. This means it's possible to encrypt all TCP traffic between all nodes of a given cluster. Enabling TLS for cluster communication is an all or nothing feature: either all nodes are configured to use TLS, or none are. It's not currently possible to only configure some nodes to enable TLS. Additionally, a small portion of Zeebe traffic is done over UDP, which is left unencrypted. This is purely used for the nodes to gossip topology information amongst themselves, and no sensitive or user-given data is transmitted this way. ## Configuration To enable TLS for cluster communication, provide a certificate chain and a private key. Two mutually exclusive formats can be provided, and attempting to use both at the same time will result in an error. ### Keystore file A keystore file can be provided. Currently, this only supports PKCS#12. Per the [RFC](https://datatracker.ietf.org/doc/html/rfc7292) it must: - Not have more than **one** certificate-key entry in the file. - Have the same password for the keystore and the key password of the only entry. The certificate will remain the same as the PEM certificate approach, and it should be an x.509 public certificate. The private key must also be generated using PKCS#8. ### PEM certificate and keys The certificate chain file is expected to be a PEM public certificate file, which should contain a x509 public certificate, and may additionally contain an entire certificate chain. If it does include the chain, it should simply be concatenated after the node's certificate. For example, a simple certificate file with only a single certificate: ``` -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- ``` If you wanted to include its signing authority, for example, you would append the contents of the authority's public certificate to the end of the certificate chain file: ``` -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- -----BEGIN TRUSTED CERTIFICATE----- ... -----END TRUSTED CERTIFICATE----- ``` While each node uses the default Java trust store to verify incoming certificates (configurable via `javax.net.ssl.trustStore`), which by default uses the system's root certificates, it's recommended to include the complete certificate chain in the file. These will also be used by each node to verify the other nodes' certificates. :::note More specifically, the certificate chain will be part of the trust store of the node, and will be used to verify other node's certificates. ::: This will allow you to configure each node with a different leaf certificate sharing the same root certificate (or at least an intermediate authority), as long as they're contained in the chain. If all nodes use the same certificate, or if you're certain the certificate is trusted by the root certificates available on each node, it's sufficient for the file to only contain the leaf certificate. The private key file should be a PEM private key file, and should be the one during generation of the node's public certificate. Algorithms supported for the private keys are RSA, DSA, and EC. The private key must be generated using [PKCS8](https://datatracker.ietf.org/doc/html/rfc5208) or [PKCS #1](https://datatracker.ietf.org/doc/html/rfc2437); any other format will not work with Zeebe. If you're unsure what format your private key is, you can quickly run it through the `openssl` utility to convert it to PKCS8: ```shell > openssl pkcs8 -topk8 -nocrypt -in my_private_key -out my_private_pkcs8_key.pem ``` Remove the `-nocrypt` parameter if your private key has a password. If your certificate is already in the right format, it will simply do nothing. See the [OpenSSL manpages](https://www.openssl.org/docs/man1.1.1/man1/openssl-pkcs8.html) for more options. :::caution Note that currently, Zeebe does not support password protected private keys. Since storing the certificates and private keys unencrypted on disk is a security risk, we recommend you use a secret management solution like Vault to inject your certificates in memory at runtime. ::: ## Broker To configure secure communication for a broker, configure its `zeebe.broker.network.security` section, which looks like this: ```yaml security: # Enables TLS authentication between this gateway and other nodes in the cluster # This setting can also be overridden using the environment variable ZEEBE_BROKER_NETWORK_SECURITY_ENABLED. enabled: false # Sets the path to the certificate chain file. # This setting can also be overridden using the environment variable ZEEBE_BROKER_NETWORK_SECURITY_CERTIFICATECHAINPATH. certificateChainPath: # Sets the path to the private key file location # This setting can also be overridden using the environment variable ZEEBE_BROKER_NETWORK_SECURITY_PRIVATEKEYPATH. privateKeyPath: # Configures the keystore file containing both the certificate chain and the private key. # Currently only supports PKCS#12 format. keyStore: # The path for the keystore file # This setting can also be overridden using the environment variable ZEEBE_BROKER_NETWORK_SECURITY_KEYSTORE_FILEPATH filePath: # Sets the password for the keystore file, if not set it is assumed there is no password # This setting can also be overridden using the environment variable ZEEBE_BROKER_NETWORK_SECURITY_KEYSTORE_PASSWORD password: ``` > The `certificateChainPath`, `privateKeyPath` and `keyStore.filePath` can be relative to your broker's working directory, or can be absolute paths. ## Gateway To configure secure communication for a standalone gateway with the rest of the cluster, configure its `zeebe.gateway.cluster.security` section, which looks like this: ```yaml security: # Enables TLS authentication between this gateway and other nodes in the cluster # This setting can also be overridden using the environment variable ZEEBE_GATEWAY_CLUSTER_SECURITY_ENABLED. enabled: false # Sets the path to the certificate chain file. # This setting can also be overridden using the environment variable ZEEBE_GATEWAY_CLUSTER_SECURITY_CERTIFICATECHAINPATH. certificateChainPath: # Sets the path to the private key file location # This setting can also be overridden using the environment variable ZEEBE_GATEWAY_CLUSTER_SECURITY_PRIVATEKEYPATH. privateKeyPath: # Configures the keystore file containing both the certificate chain and the private key. # Currently only supports PKCS#12 format. keyStore: # The path for the keystore file # This setting can also be overridden using the environment variable ZEEBE_GATEWAY_CLUSTER_SECURITY_PKCS12_FILEPATH filePath: # Sets the password for the keystore file, if not set it is assumed there is no password # This setting can also be overridden using the environment variable ZEEBE_GATEWAY_CLUSTER_SECURITY_PKCS12_PASSWORD password: ``` :::note The `certificateChainPath`, `privateKeyPath`, and `keyStore.filePath` can be relative to the gateway's working directory, or can be absolute paths. ::: ## Tasklist and Operate Using the same set of configuration properties, you can configure Tasklist and Operate to enable TLS-secured connectivity within a Camunda 8 cluster. Refer to the documentation on [Tasklist configuration](../../tasklist/tasklist-configuration.md#intra-cluster-secure-connection) and [Operate configuration](../../operate/operate-configuration.md#intra-cluster-secure-connection) for additional details. ## How it works When enabled for each node, communication over TCP between these is securely encrypted using the provided certificates in a client-server model. For example, let's take two nodes (`A` and `B`). When `A` (the client) sends a request to `B` (the server), they perform a TLS handshake, wherein `B`'s certificate is exchanged and verified by `A`. Afterwards, the request is encrypted such that only a node with `B`'s private key may decrypt it (i.e. in this instance, `B`). When the roles are reversed (e.g. `B` sends a request to `A`), the same handshake occurs, but the other way around. As`B` is now the client, and `A` the server, `A`'s certificate is exchanged and verified by `B`. Afterwards, all communication is encrypted and can only be decrypted with `A`'s private key. :::note In this model, only the client verifies the identity of the server, as opposed to mTLS, in which both client and server exchange and verify one another's identities. If you need mTLS, it's currently recommended to explore a solution which provides this transparently like a service mesh (e.g. Linkerd or Istio). ::: ## Self signed certificates If you wish to use self-signed certificates for testing or development purposes, the simplest way is to have all nodes share the same certificate. As aforementioned, the certificate chain configured on a node is also part of its trust store. As such, if all nodes share the same certificate, they will have no trouble verifying the identity of the other nodes. You can still configure a different self-signed certificate for each node, _provided they can be verified by the other nodes' certificate chain_. For example, let's say you have your own root certificate authority you use to sign your own certificates, and one certificate for each node that you signed with that authority. For each node, you can then create a certificate chain file which would consist of the node's public certificate, followed by the root certificate authority's public certificate. Though each node would have a different leaf certificate it uses to identify itself, the other nodes could verify its identity since their certificate chain contains an authority used to sign it. ### Testing & example To generate your own self-signed certificates for testing, you must first create a certificate authority. :::note For this example, whenever you are asked for input, feel free to just press enter and leave the defaults there. ::: ```shell openssl req -config <(printf "[req]\ndistinguished_name=dn\n[dn]\n[ext]\nbasicConstraints=CA:TRUE,pathlen:0") -new -newkey rsa:2048 -nodes -subj "/C=DE/O=Test/OU=Test/ST=BE/CN=cluster.local" -x509 -extensions ext -keyout ca.key -out ca.pem ``` Once we have our certificate authority, we can now generate certificates for each node. Let's say we have a cluster of three nodes, `A`, `B`, and `C`. Take the following steps: 1. Generate a private key for each node: ```shell openssl genpkey -out nodeA.key -algorithm RSA -pkeyopt rsa_keygen_bits:2048 openssl genpkey -out nodeB.key -algorithm RSA -pkeyopt rsa_keygen_bits:2048 openssl genpkey -out nodeC.key -algorithm RSA -pkeyopt rsa_keygen_bits:2048 ``` :::note Generating a private key using RSA and `openssl` will generate a PKCS8 private key by default. ::: 2. Create a certificate signing request (CSR) for each as well: ```shell openssl req -new -key nodeA.key -out nodeA.csr openssl req -new -key nodeB.key -out nodeB.csr openssl req -new -key nodeC.key -out nodeC.csr ``` 3. Create the final certificates for each node: ```shell openssl x509 -req -days 365 -in nodeA.csr -CA ca.pem -CAkey ca.key -set_serial 01 -extfile <(printf "subjectAltName = IP.1:127.0.0.1") -out nodeA.pem openssl x509 -req -days 365 -in nodeB.csr -CA ca.pem -CAkey ca.key -set_serial 01 -extfile <(printf "subjectAltName = IP.1:127.0.0.1") -out nodeB.pem openssl x509 -req -days 365 -in nodeC.csr -CA ca.pem -CAkey ca.key -set_serial 01 -extfile <(printf "subjectAltName = IP.1:127.0.0.1") -out nodeC.pem ``` Make sure to replace `IP.1:127.0.0.1` with the advertised host of the broker. If it's an IP address, then keep the `IP.1` prefix. If it's a hostname/DNS entry, then you can write it out as `DNS.1:advertisedHost`. To be flexible, you can also use a wildcard host. For example, if you're deploying in Kubernetes, you could use `subjectAltName = DNS.1:*.cluster.local"`. You can also omit the whole `-extfile` parameter if you do not wish to use hostname verification at all. 4. Create the certificate chain so that each node is able to verify the identity of the others: ```shell cat nodeA.pem ca.pem > chainNodeA.pem cat nodeB.pem ca.pem > chainNodeB.pem cat nodeC.pem ca.pem > chainNodeC.pem ``` 5. You can now configure each node using its respective final `chainNode*.pem` file and `node*.key` file. For example, if node `A` was a broker: ```yaml security: enabled: true certificateChainPath: chainNodeA.pem privateKeyPath: nodeA.key ``` --- ## Security Zeebe supports the following security features: - **[Client-gateway authorization](client-authorization.md)** - Allows you set up authorization for the client and the gateway. - **[Secure client-gateway communication](secure-client-communication.md)** - Allows you to secure communication between clients and gateways. - **[Secure cluster communication](secure-cluster-communication.md)** - Allows you to secure communication between all nodes in a cluster. --- ## Filters :::danger Filters are only applied to the Orchestration Cluster REST API of the gateway, and do not affect any gRPC calls. For gRPC-related middleware, review the [gateway interceptors documentation](interceptors.md). ::: All communication from a client to a broker must first pass through a gateway. There, it can be filtered before being dispatched. Zeebe provides a way to load arbitrary REST API filters into the gateway. Some typical examples of what you can accomplish with this include: - Enforcing custom authorization rules on incoming calls - Monitoring and logging incoming calls ## Implementing a filter To communicate between client and gateway, Zeebe uses [REST](components/zeebe/technical-concepts/protocols.md). A filter is then implemented as a Jakarta servlet [filter](https://www.javadoc.io/doc/jakarta.servlet/jakarta.servlet-api/6.0.0/jakarta.servlet/jakarta/servlet/Filter.html). An implementation must adhere to the following requirements: - It implements a [filter](https://www.javadoc.io/doc/jakarta.servlet/jakarta.servlet-api/6.0.0/jakarta.servlet/jakarta/servlet/Filter.html) - It has public visibility - It has a public default constructor (for example, no-arg constructor) Consider a filter logging incoming calls as an example: ```java package io.camunda.zeebe.example; /** * A simple filter that logs each incoming call. The class must be public * since we will load it via JAR into the gateway. */ public final class LoggingFilter implements Filter { private static final Logger LOGGER = LoggerFactory.getLogger("LoggingFilter"); @Override public void doFilter( final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { LOGGER.trace("filtered a call"); filterChain.doFilter(servletRequest, servletResponse); } } ``` This example filter will log `filtered a call` at `TRACE` level for each incoming call it filters. This specific filter always dispatches all incoming calls to the target broker, but it is possible to stop the message from being filtered by other filters, and even block it from dispatching to the broker. ## Compiling your filter Our source code for the filter class can now be compiled. There are many ways to do this, but for simplicity we'll use `javac` directly. When compiling your class, ensure all compile-time dependencies are provided. In the example above, you'll need the `jakarta.servlet-api` and `slf4j-api` libraries available when compiling. Since the filter will be running inside the Zeebe Gateway, the language level of the compiled code must be the same as Zeebe's (i.e. currently JDK 21) or lower. This example thus assumes you're using version 21 of `javac`. ```sh # to compile LoggingFilter.java, we'll need to provide the api libraries javac -classpath .:lib/jakarta.servlet-api.jar:lib/slf4j-api.jar ./LoggingFilter.java ``` ## Packaging a filter Next, package the filter class into a fat JAR. Such a JAR must contain all classes (for example, all classes your own classes depend upon at runtime). Like compiling, there are many ways to do this, but we'll use `jar` directly. This means we must define a Java manifest file by hand to place the libraries' classes on the classpath. Similar to your filter class, any libraries you package must be compiled for the same language level as Zeebe's (currently JDK 21) or lower. :::note The file path for `jar` should match the package name. For example, if your package name is `com.example`, you should package `jar` as `jar cvfm LoggingFilter.jar ./MANIFEST.MF ./com/example/*.class ./lib`. ::: ```sh # both runtime libraries and the manifest must be packaged together with the compiled classes jar cvfm LoggingFilter.jar ./MANIFEST.MF ./*.class ./lib # let's verify the contents of the JAR jar tf ./LoggingFilter.jar # META-INF/ # META-INF/MANIFEST.MF # LoggingFilter.java # LoggingFilter$1.class # lib/ # lib/jakarta.servlet-api.jar # lib/slf4j-api.jar # lib/slf4j.jar ``` ## Loading a filter into a gateway A filter can be loaded into your gateway as a fat JAR. For each filter, provide the gateway with: - A filter order index - An identifier to identify this specific filter - Where to find the JAR with the filter class - The [fully qualified name](https://docs.oracle.com/javase/specs/jls/se17/html/jls-6.html#jls-6.7) of the filter class. For example, `com.acme.ExampleFilter`. Continuing with the `LoggingFilter` example, provide these [configurations](/self-managed/components/orchestration-cluster/zeebe/configuration/configuration.md) using a gateway config file, environment variables, or a mix of both. We'll be using a config file in this example. The following gateway config file configures our `LoggingFilter` so it can be loaded into the gateway at start-up: ```yaml zeebe: gateway: ... # allows specifying multiple filters filters: - # identifier, can be used for debugging id: logging-filter # name of our Filter implementation # this must be the fully qualified name of the class className: io.camunda.zeebe.example.LoggingFilter # path to the fat JAR, can be absolute or relative jarPath: /tmp/LoggingFilter.jar # you can add additional filters by listing them - id: ... className: ... jarPath: ... ``` :::note Multiple filters can be configured (for example, `zeebe.gateway.filters` expects a list of filter configurations). The listing order determines the order in which a call is filtered by the different filters. The first filter in the list wraps the second, etc. The first filter is thus the outermost filter. In other words, calls are filtered first by the first listed filter, followed by the second listed filter, etc. ::: This configuration can also be provided using environment variables. Provide an index for the filter in the variable name to distinguish the ordering of the different filters. For example, to configure the `className` of the first filter use `zeebe_gateway_filters_0_className`. Likewise, a second filter's `jarPath` can be configured using `zeebe_gateway_filters_1_jarPath`. ## Class loading [Previously](#packaging-a-filter), we stated that you need to package the filter class into a fat JAR. Although good general advice, this is not entirely true. To understand why, let's discuss how the class loading of your filter works. When your JAR is loaded into the gateway, Zeebe provides a special class loader for it. This class loader isolates your filter from the rest of Zeebe, but it also exposes our own code to your filter. When loading classes for your filter, it will always first look in this special class loader. If it is not available, it will look in Zeebe's main class loader. Therefore, you can access any classes from Zeebe's main class loader when they are not provided by your JAR. For internal class loading, Zeebe will still only look in its main class loader. You can reduce your JAR size by leaving out libraries already provided by Zeebe's class loader. Additionally, if your filter depends on a different version of a class than the one provided by Zeebe, you can provide your own version without having to worry about breaking Zeebe. ## Troubleshooting Here we describe a few common errors to help you recognize these situations and provide a solution. Generally, the gateway will not be able to start up with a misconfigured filter. :::note Environment variables can overwrite your gateway configuration file. The gateway logs the configuration it uses during start-up; use this to verify your configuration. ::: ### java.lang.ClassNotFoundException Your `Filter` implementation could not be found. Ensure you've configured the `className` correctly in the [gateway configuration](#loading-a-filter-into-a-gateway) and your [JAR contains your class](#packaging-a-filter). ### io.camunda.zeebe.gateway.rest.impl.filters.FilterLoadException Something went wrong trying to load your filter. Ensure your [JAR is packaged](#packaging-a-filter) correctly. For example, ensure it contains all runtime dependencies and specifies them in the manifest file's classpath. The exception should provide a clear description, but generally we distinguish the following common cases: **\*Unable to instantiate your class** Ensure your class adheres to the [requirements described above](#implementing-a-filter). **The JAR could not be loaded** Ensure you've configured your filter correctly in the [gateway configuration](#loading-a-filter-into-a-gateway). ### io.camunda.zeebe.util.jar.ExternalJarLoadException The JAR could not be loaded. Ensure you've configured your filter correctly in the [gateway configuration](#loading-a-filter-into-a-gateway). ### java.lang.UnsupportedClassVersionError Your filter has been compiled by a more recent version of the Java Runtime. Ensure your [class is compiled](#packaging-a-filter) with JDK 21. --- ## Interceptors :::danger Interceptors are only applied to the gRPC API of the gateway, and do not affect any REST calls. For REST-related middleware, read the [gateway filters documentation](filters.md). ::: All communication from a client to a broker must first pass through a gateway. There they can be intercepted before being dispatched. Zeebe provides a way to load arbitrary interceptors into the gateway. Some typical examples of what you can accomplish with this include: - Enforcing custom authorization rules on incoming calls - Monitoring and logging of incoming calls (e.g. https://github.com/grpc-ecosystem/java-grpc-prometheus) - Distributed tracing (e.g. https://github.com/open-telemetry/opentelemetry-java-instrumentation) ## Implementing an interceptor For the communication between client and gateway, Zeebe uses the gRPC [protocol](components/zeebe/technical-concepts/protocols.md). An interceptor is thus implemented as a gRPC [ServerInterceptor](https://grpc.github.io/grpc-java/javadoc/io/grpc/ServerInterceptor.html). An implementation must adhere to the following requirements: - It implements [ServerInterceptor](https://grpc.github.io/grpc-java/javadoc/io/grpc/ServerInterceptor.html) - It has public visibility - It has a public default constructor (i.e. no-arg constructor) Let's consider an interceptor that provides logging of incoming calls as an example. Other ServerInterceptor examples can be found in the official grpc-java [examples](https://github.com/grpc/grpc-java/tree/v1.41.0/examples). ```java package io.camunda.zeebe.example; /** * A simple interceptor that logs each incoming call. The class must be public * since we will load it via JAR into the gateway. */ public final class LoggingInterceptor implements ServerInterceptor { private static final Logger LOGGER = LoggerFactory.getLogger("LoggingInterceptor"); @Override public Listener interceptCall( final ServerCall call, final Metadata headers, final ServerCallHandler next) { final var listener = next.startCall(call, headers); return new SimpleForwardingServerCallListener<>(listener) { @Override public void onMessage(final ReqT message) { LOGGER.trace("intercepted a call"); super.onMessage(message); } }; } } ``` This example interceptor will log `"intercepted a call"` at `TRACE` level for each incoming call it intercepted. This specific interceptor always dispatches all incoming calls to the target broker, but it would also be possible to stop the message from interception by other interceptors and even to block it from dispatch to the broker. ## Compiling your interceptor Our source code for the interceptor class can now be compiled. There are many ways to do this, but for simplicity we'll use `javac` directly. When compiling your class, you need to make sure all compile-time dependencies are provided. In the example above, that means we need the `grpc-api` and `slf4j-api` libraries available when compiling. Since the interceptor will be running inside the Zeebe Gateway, the language level of the compiled code must be the same as Zeebe's (i.e. currently JDK 21) or lower. This example thus assumes you're using version 21 of `javac`. ```sh # to compile LoggingInterceptor.java, we'll need to provide the api libraries javac -classpath .:lib/grpc-api.jar:lib/slf4j-api.jar ./LoggingInterceptor.java ``` ## Packaging an interceptor Next, you need to package the interceptor class into a fat JAR. Such a JAR must contain all classes (i.e. including all classes your own classes depend upon at runtime). Like compiling there are many ways to do this, but for simplicity we'll use `jar` directly. Note, that means we have to define a java manifest file by hand, in order to place the libraries' classes on the classpath. Similar to your interceptor class, any libraries you package must be compiled for the same language level as Zeebe's (i.e. currently JDK 21) or lower. :::note The file path for `jar` should match the package name. For example, if your package name is `com.example`, you should package `jar` as `jar cvfm LoggingInterceptor.jar ./MANIFEST.MF ./com/example/*.class ./lib`. ::: ```sh # both runtime libraries and the manifest must be packaged together with the compiled classes jar cvfm LoggingInterceptor.jar ./MANIFEST.MF ./*.class ./lib # let's verify the contents of the JAR jar tf ./LoggingInterceptor.jar # META-INF/ # META-INF/MANIFEST.MF # LoggingInterceptor.java # LoggingInterceptor$1.class # lib/ # lib/grpc-api.jar # lib/grpc.jar # lib/slf4j-api.jar # lib/slf4j.jar ``` ## Loading an interceptor into a gateway An interceptor can be loaded into your gateway as a fat JAR. For each interceptor, you need to provide your gateway with: - An interception order index - An identifier to identify this specific interceptor - Where to find the JAR with the interceptor class - The [fully qualified name](https://docs.oracle.com/javase/specs/jls/se17/html/jls-6.html#jls-6.7) of the interceptor class, e.g. `com.acme.ExampleInterceptor` Let's continue with the LoggingInterceptor example. We can provide these [configurations](/self-managed/components/orchestration-cluster/zeebe/configuration/configuration.md) using a gateway config file, environment variables or a mix of both. We'll be using a config file here. The following gateway config file configures our LoggingInterceptor so it can be loaded into the gateway at start-up. ```yaml zeebe: gateway: ... # allows specifying multiple interceptors interceptors: - # identifier, can be used for debugging id: logging-interceptor # name of our ServerInterceptor implementation # this must be the fully qualified name of the class className: io.camunda.zeebe.example.LoggingInterceptor # path to the fat JAR, can be absolute or relative jarPath: /tmp/LoggingInterceptor.jar # you can add additional interceptors by listing them - id: ... className: ... jarPath: ... ``` Note that multiple interceptors can be configured (i.e. `zeebe.gateway.interceptors` expects a list of interceptor configurations). The listing order determines the order in which a call is intercepted by the different interceptors. The first interceptor in the list wraps the second, etc. The first interceptor is thus the outermost interceptor. In other words, calls are intercepted first by the first listed interceptor, followed by the second listed interceptor, etc. This configuration can also be provided using environment variables. You'll need to provide an index for the interceptor in the variable name, to distinguish the ordering of the different interceptors. For example, to configure the `className` of the first interceptor use: `zeebe_gateway_interceptors_0_className`. Likewise, a second interceptor's `jarPath` can be configured using `zeebe_gateway_interceptors_1_jarPath`. ## About class loading [Previously](#packaging-an-interceptor), we stated that you need to package the interceptor class into a fat JAR. Although good general advice, this is not entirely true. To understand why, let's discuss how the class loading of your interceptor works. When your JAR is loaded into the gateway, Zeebe provides a special class loader for it. This class loader isolates your interceptor from the rest of Zeebe, but it also exposes our own code to your interceptor. When loading classes for your interceptor, it will always first look in this special class loader and only if it is not available it will look in Zeebe's main class loader. In other words, you can access any classes from Zeebe's main class loader when they are not provided by your JAR. For internal class loading, Zeebe will still only look in its main class loader. This means you can reduce your JAR size by leaving out libraries that are already provided by Zeebe's class loader. In addition, if your interceptor depends on a different version of a class than the one provided by Zeebe, then you can provide your own version without having to worry about breaking Zeebe. ## Troubleshooting Here we describe a few common errors. Hopefully, this will help you recognize these situations and provide an easy fix. Generally, the gateway will not be able to start up with a misconfigured interceptor. :::note Environment variables can overwrite your gateway configuration file. The gateway logs the configuration it uses during start-up, use this to verify your configuration. ::: ### java.lang.ClassNotFoundException Your ServerInterceptor implementation could not be found. Make sure you've configured the `className` correctly in the [gateway configuration](#loading-an-interceptor-into-a-gateway) and that your [JAR contains your class](#packaging-an-interceptor). ### io.camunda.zeebe.gateway.interceptors.impl.InterceptorLoadException Something went wrong trying to load your interceptor. Make sure your [JAR is packaged](#packaging-an-interceptor) correctly, i.e. it contains all runtime dependencies and specifies them in the manifest file's classpath. The exception should provide a clear description, but generally we distinguish the following common cases: **\*Unable to instantiate your class** Make sure your class adheres to the [requirements described above](#implementing-an-interceptor). **The JAR could not be loaded** Make sure you've configured your interceptor correctly in the [gateway configuration](#loading-an-interceptor-into-a-gateway). ### io.camunda.zeebe.util.jar.ExternalJarLoadException The JAR could not be loaded: make sure you've configured your interceptor correctly in the [gateway configuration](#loading-an-interceptor-into-a-gateway). ### java.lang.UnsupportedClassVersionError Your interceptor has been compiled by a more recent version of the Java Runtime. Make sure your [class is compiled](#packaging-an-interceptor) with JDK 21. --- ## Job streaming [Job streaming](../../../../../components/concepts/job-workers.md#job-streaming) is a long-lived process designed to reduce the latency involved with re-creating and propagating job workers. When using a reverse proxy or a load balancer between your worker and your gateway, you may need to configure additional parameters to ensure the job stream is not closed unexpectedly. Impacted proxies will see HTTP 504 (gateway timeout) errors returned to the job streaming worker at regular intervals. :::note This configuration is _only_ required for reverse proxies which do not support forwarding HTTP/2 keepalive (on either side). See [this nginx ticket](https://trac.nginx.org/nginx/ticket/1887), for example. Proxies which support forwarding HTTP/2 keepalive do not require any change. ::: The following configuration is recommended for impacted reverse proxies: - On your client, set an explicit stream timeout of one hour. See additional examples in [Java](../../../../../apis-tools/java-client/job-worker.md). - On your reverse proxy, ensure the read response timeout is set to slightly higher than your client (for example, an hour and ten minutes). ## Nginx Nginx is a known proxy which does not support forward HTTP/2 pings from either side as a form of keepalive. To resolve related gateway timeouts, configure an appropriate `grpc_send_timeout` that it is _higher_ than your job worker stream timeout configuration. --- ## Zeebe Gateway The Zeebe Gateway is a component of the Zeebe cluster; it can be considered the contact point for the Zeebe cluster which allows Zeebe clients to communicate with Zeebe brokers inside a Zeebe cluster. For more information about the Zeebe Broker, visit our [additional documentation](../../../../../components/zeebe/technical-concepts/architecture.md#brokers). To summarize, the Zeebe Broker is the main part of the Zeebe cluster, which does all the heavy work like processing, replicating, exporting, and everything based on partitions. The Zeebe Gateway acts as a load balancer and router between Zeebe’s processing partitions. ![Zeebe Gateway overview](assets/zeebe-gateway-overview.png) To interact with the Zeebe cluster, the Zeebe client sends a command to the gateway either as a gRPC message (to port `26500` by default), or a plain HTTP request to its REST API (to port `8080` by default). Given the gateway supports gRPC as well as an OpenAPI spec, the user can use several clients in different languages to interact with the Zeebe cluster. For more information, read our [overview](../../../../../apis-tools/working-with-apis-tools.md). :::note Be aware Zeebe brokers divide data into partitions (shards), and use RAFT for replication. Read more on RAFT [here](../../../../../components/zeebe/technical-concepts/clustering.md#raft-consensus-and-replication-protocol). ::: When the Zeebe Gateway receives a valid message, it is translated to an internal binary format and forwarded to one of the partition leaders inside the Zeebe cluster. The command type and values can determine to which partition the command is forwarded. For example, creating a new process instance is sent in a round-robin fashion to the different partitions. If the command relates to an existing process instance, the command must be sent to the same partition where it was first created (determined by the key). To determine the current leader for the corresponding partition, the gateway must maintain the topology of the Zeebe cluster. The gateway(s) and broker(s) form a cluster using gossip protocol to distribute information. ## Why do we have the Zeebe Gateway and what problems does it solve? The Zeebe Gateway protects the brokers from external sources. It allows the creation of a demilitarized zone ([DMZ]()) and the Zeebe Gateway is the only contact point. The Zeebe Gateway also allows you to easily create clients in your language of choice while keeping the client implementation as thin as possible. The clients can be kept thin, since the gateway takes care of the cluster topology and forwards the requests to the right partitions. There are already several client implementations available, officially-supported, and community-maintained. Check the list [here](../../../../../apis-tools/working-with-apis-tools.md). The gateway can be run and scaled independently of the brokers, which means it translates the messages, distributes them to the correct partition leaders, and separates the concerns of the applications. For example, if your system encounters a spike of incoming requests, and you have set up enough partitions on the broker side up front, but not enough gateways to handle the load, you can easily scale them up. ## Embedded versus standalone The Zeebe Gateway can be run in two different ways: embedded and standalone. Running the gateway in embedded mode means it will run as part of the Zeebe Broker. The broker will accept gRPC client messages via the embedded gateway and distribute the translated requests inside the cluster. This means the request accepted by the embedded gateway does not necessarily go to the same broker, where the embedded gateway is running. The embedded gateway is useful for development and testing purposes, and to reduce the burden of deploying and running multiple applications. For example, in [zeebe-process-test](https://github.com/camunda/zeebe-process-test) an embedded gateway is used to accept the client commands and write directly to the engine. :::note Be aware If the gateway is running in the embedded mode, it will consume resources from the broker, which might impact the performance of the system. ::: Running the gateway in standalone mode means the gateway will be executed as its own application. This is the recommended way for production use cases, and it is the default (and only option) in the Helm charts. As mentioned, this allows separation of concerns, especially as the gateway can be scaled independently of the broker based on the current workload. --- ## Configure the audit log(Audit-log) Configure the audit log in Camunda 8 Self-Managed. ## Configure recorded operations The audit log is an important feature with which you can meet regulatory requirements and maintain operational integrity by accessing a record of operations. These records include who performed the operations, when, and on which entities. The audit log is enabled by default, and the storage it requires may result in increased costs. To mitigate these resource costs, only user operations are tracked by default, not [client](../../../components/zeebe/technical-concepts/architecture.md#clients) operations. To change the default behavior in Camunda 8 Self-Managed, such as to disable the audit log or configure recorded operations, you must configure your deployment: ```yaml camunda: data: audit-log: enabled: true user: categories: [DEPLOYED_RESOURCES, USER_TASKS, ADMIN] # User operations are recorded by default client: categories: [DEPLOYED_RESOURCES, USER_TASKS, ADMIN] # You must opt in to client operations ``` ```bash CAMUNDA_DATA_AUDITLOG_ENABLED=true # User operations are recorded by default CAMUNDA_DATA_AUDITLOG_USER_CATEGORIES_0=DEPLOYED_RESOURCES CAMUNDA_DATA_AUDITLOG_USER_CATEGORIES_1=USER_TASKS CAMUNDA_DATA_AUDITLOG_USER_CATEGORIES_2=ADMIN # You must opt in to client operations CAMUNDA_DATA_AUDITLOG_CLIENT_CATEGORIES_0=DEPLOYED_RESOURCES CAMUNDA_DATA_AUDITLOG_CLIENT_CATEGORIES_1=USER_TASKS CAMUNDA_DATA_AUDITLOG_CLIENT_CATEGORIES_2=ADMIN ``` ```yaml orchestration: extraConfiguration: - file: additional-spring-properties.yaml content: | camunda: data: audit-log: enabled: true user: categories: [DEPLOYED_RESOURCES, USER_TASKS, ADMIN] # User operations are recorded by default client: categories: [DEPLOYED_RESOURCES, USER_TASKS, ADMIN] # You must opt in to client operations ``` See [all configuration options](../../components/orchestration-cluster/core-settings/configuration/properties.md#camundadataaudit-log) to learn more. If you disable the audit log, new operations are no longer recorded. Changing this setting doesn't cause the existing audit log data to be immediately purged. Instead, it will be cleaned up according to the secondary storage retention settings. Until the data is cleaned up, you can continue to access the data in [Operate](../../../components/operate/userguide/audit-operations.md), [Tasklist](../../../components/tasklist/userguide/audit-task-history.md), [Admin](../../../components/admin/audit-operations.md), and the [Search API](/apis-tools/orchestration-cluster-api-rest/specifications/search-audit-logs.api.mdx). ## Configure secondary storage retention With Camunda 8 Self-Managed, you control the [secondary storage retention policy](../../components/orchestration-cluster/core-settings/configuration/properties.md#data---secondary-storage), which applies to audit log records: ```yaml camunda: data: secondary-storage: retention: enabled: true minimum-age: 30d ``` ```bash CAMUNDA_DATA_SECONDARYSTORAGE_RETENTION_ENABLED=true CAMUNDA_DATA_SECONDARYSTORAGE_RETENTION_MINIMUMAGE=30d ``` ```yaml orchestration: retention: enabled: true minimumAge: 30d ``` See [Configure data retention](../../deployment/helm/configure/data-retention.md) for more information about the Helm configuration. --- ## Audit log(Audit-log) Use the [audit log](../../../components/audit-log/overview.md) to access a record of operations, including who performed the operation, when it was performed, and on which entities the operation was performed. ## Impact on secondary storage When the audit log is active, a record is written to [secondary storage](../../concepts/secondary-storage/index.md) for every applicable operation instance. By default, only user operations are tracked, not [client](/components/zeebe/technical-concepts/architecture.md#clients) operations. With this default behavior, you can expect a 3.5% increase in disk usage. :::warning The audit log is enabled by default. Because of the increase in resource usage on secondary storage, you may see increased costs associated with this feature. ::: You can [configure the audit log](./configure.md) to fine tune log thoroughness and resource usage according to your needs. --- ## Management and modeling component authentication in Self-Managed Authentication to the Camunda 8 management and modeling components and their resources is managed by [Management Identity](/self-managed/components/management-identity/overview.md). This includes components such as Web Modeler, Console, and [Optimize](/self-managed/components/optimize/overview.md). ## About Management Identity authentication Management Identity relies on the **OpenID Connect (OIDC)** and **OAuth 2.0** protocols for secure authentication and authorization. It supports three primary scenarios: - Use Keycloak as the default built-in Identity Provider (IdP). - Configure the built-in Keycloak to connect to an external IdP. - Connect directly to an external OIDC IdP. ## Use Keycloak as default (built-in) IdP This is the default authentication setup for Self-Managed installation methods, including [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md), [Helm charts](/self-managed/deployment/helm/index.md) and [Manual installation](/self-managed/deployment/manual/install.md). It comes with a pre-packaged Keycloak instance that acts as the Identity Provider. In this scenario: - **User authentication:** Users log in through the Keycloak's login page. - **Application authentication:** Applications authenticate using Machine-to-Machine (M2M) tokens. - **User management:** Administrators manage users, groups, roles, and permissions within Keycloak. This method is convenient for getting started quickly and is suitable for environments that do not need to integrate with an existing corporate identity management system. :::info For more information, see [connect to an existing Keycloak instance](/self-managed/components/management-identity/configuration/connect-to-an-existing-keycloak.md). ::: ## Connect to an external IdP via Keycloak You can configure the built-in Keycloak to act as an identity broker, connecting to an external corporate Identity Provider. This allows you to leverage your existing user base from providers that support protocols like **SAML**, **LDAP**, or **OpenID Connect**. In this scenario, Keycloak remains the direct IdP for Camunda management and modeling components, but it delegates the authentication process to your configured external provider. - **User authentication:** Users are redirected from Keycloak's login page to your external IdP. - **User management:** Users are managed in your external IdP and federated into Keycloak. - **Application authentication:** Applications use M2M tokens issued by Keycloak. This method is useful when you need to integrate with an IdP that does not use OIDC, or when you want to use Keycloak's advanced features to manage roles and map claims from your external provider. :::info For more information, see [configure an external IdP using Keycloak](/self-managed/components/management-identity/configuration/configure-external-identity-provider.md). ::: ## Connect to an external OIDC IdP You can connect Management Identity to an external Identity Provider (IdP) that supports **OpenID Connect (OIDC)** (e.g., Microsoft Entra ID, Keycloak, Auth0, Okta). In this scenario: - Users are managed in your external IdP. - User groups from your IdP can be used to manage permissions. - Clients for M2M authentication are managed in your external IdP. :::tip Recommendation If you have configured the [authentication to Orchestration Cluster](authentication-to-orchestration-cluster.md#oidc) with an external OIDC provider, we recommend using the same provider for Management Identity. This will create a unified authentication experience and allow for centralized user management across all Camunda 8 components. ::: :::info For more information, see [connect Management Identity to an external IdP](/self-managed/components/management-identity/configuration/connect-to-an-oidc-provider.md). ::: --- ## Orchestration Cluster authentication in Self-Managed Authentication to the Orchestration Cluster components and their resources is managed by [Admin](../../components/orchestration-cluster/admin/overview.md). ## About Orchestration Cluster authentication Orchestration Cluster authentication includes components such as [Zeebe](/components/zeebe/zeebe-overview.md), [Admin](/components/admin/admin-introduction.md), [Operate](/components/operate/operate-introduction.md), [Tasklist](/components/tasklist/introduction-to-tasklist.md), and the [Orchestration Cluster API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). The Orchestration Cluster supports two authentication methods: - [Basic authentication](#basic-authentication) - [OIDC](#oidc) ### Comparison of authentication methods | | **API access** | **Web UI access** | **User management** | | ------------------------ | --------------------- | --------------------- | ------------------------------ | | **Basic authentication** | Username and password | Username and password | Via Admin | | **OIDC** | OAuth 2.0 (via IdP) | OIDC (via IdP) | Via External Identity Provider | Additionally, an [Unprotected API mode](#unprotected-api-mode) is available for development purposes, which can be applied to either method. ## Basic authentication With Basic authentication, Orchestration Cluster components are protected with a username and password. User management is handled within the built-in Admin service. :::note This is the default authentication method for all installation options: [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md), [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md), [Helm charts](/self-managed/deployment/helm/index.md), and [Manual installation](/self-managed/deployment/manual/install.md). ::: ### Example configuration ```yaml CAMUNDA_SECURITY_AUTHENTICATION_METHOD=basic ``` ```yaml camunda.security.authentication.method: basic ``` ```yaml orchestration.security.authentication.method=basic ``` ### Security considerations While Basic authentication provides a simple layer of protection suitable for development or testing environments, it has several security limitations: - **No multi-factor authentication (MFA):** Basic authentication does not support MFA, increasing the risk of unauthorized access through credential stuffing attacks, where attackers use stolen credentials from other services. - **No account locking:** The system does not lock accounts after multiple failed login attempts, leaving it vulnerable to brute-force attacks where an attacker can try to guess passwords repeatedly without being blocked. - **Insecure password recovery:** The password recovery process for administrators can be insecure and may require direct, risky manual intervention with the system. - **No single sign-on (SSO):** It leads to a higher likelihood of weak or reused passwords. #### Mitigation and recommendations For a secure, production-ready setup, Camunda **strongly recommends using [OIDC](#oidc)**. OIDC delegates authentication to a dedicated identity provider (IdP), allowing you to leverage advanced security features such as MFA, SSO, and password policies. ## OIDC With OIDC, authentication is delegated to an [external Identity Provider (IdP)](/components/concepts/access-control/connect-to-identity-provider.md) using OpenID Connect (OIDC). This is the **recommended method for production environments**. - [Users](/components/admin/user.md) are managed in your external IdP and [mapped through rules](/components/concepts/access-control/mapping-rules.md) in Admin. - [User groups](/components/admin/group.md) can be managed in Admin or configured to use groups from your IdP. - [Clients](/components/admin/client.md) are managed in your external IdP and [mapped through rules](/components/concepts/access-control/mapping-rules.md) in Admin. Using OIDC provides several security benefits: - **Centralized user management:** Manage all users and their access from a single, central IdP. - **Single Sign-On (SSO):** Provide a seamless login experience for your users across multiple applications. - **Enhanced security:** Enforce MFA, password rotation policies, and other advanced security measures offered by your IdP. :::info For more information, see [connect Orchestration Cluster Admin to an external IdP](/self-managed/components/orchestration-cluster/admin/connect-external-identity-provider.md). ::: ### Example configuration ```yaml CAMUNDA_SECURITY_AUTHENTICATION_METHOD=oidc ``` ```yaml camunda.security.authentication.method: oidc ``` ```yaml orchestration.security.authentication.method=oidc ``` If OIDC authentication is enabled, additional configuration values must be set. See [supported OIDC configuration properties](../../components/orchestration-cluster/core-settings/configuration/properties.md#oidc-configuration). ## Unprotected API mode In this mode, API access is unprotected with no authentication required for APIs. This mode can be enabled with both Basic authentication and OIDC. By default, [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) and [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md) are configured in unprotected API mode for local development quick start. :::caution This mode should never be used in production environments. ::: :::note If you need to use authorizations for access control, you must protect APIs by disabling the unprotected API mode. To learn more, see [Orchestration Cluster authorization](../../../components/concepts/access-control/authorizations.md). ::: ### Example configuration ```yaml CAMUNDA_SECURITY_AUTHENTICATION_UNPROTECTEDAPI=true ``` ```yaml camunda.security.authentication.unprotected-api: true ``` ```yaml orchestration.security.authentication.unprotectedApi=true ``` --- ## Machine-to-machine (M2M) tokens A **machine-to-machine (M2M)** token is a token requested by one service so it can communicate with another service acting as itself. In [Management Identity](/self-managed/components/management-identity/overview.md), we provide the ability to assign permissions to an application. This functionality allows an application to perform the `client_credentials` flow to retrieve a JWT token with permissions. The token generated can then be used to communicate with other applications in Camunda without the need for user intervention. :::tip Want to learn how to generate an M2M token? Head to our guide, [generating M2M tokens](/self-managed/components/management-identity/authentication.md) to find out more! ::: --- ## Elasticsearch privileges If you implement Camunda 8 with Elasticsearch as a service provider, the following privileges may be required: ## Cluster privileges :::note Running Elasticsearch with limited cluster privileges If an application cannot be granted cluster privileges, the schema manager can be run as a standalone application separate from the main application. In this setup, the single application does not need cluster privileges. ::: - `monitor` - Required to check the Elasticsearch cluster health. This privilege provides read-only cluster operations permissions. - `manage_index_templates` - Creates the necessary index templates when the Orchestration Cluster and Optimize are started for the first time, or when upgrading to a newer version of Camunda 8. Once the index templates are created, you can stop the Component, remove this privilege, and then start Component again. - `manage_ilm` - _Required when index lifecycle management (ILM) is enabled._ Required to create the necessary ILM policies when the Orchestration Cluster is started for the first time, or when upgrading to a newer version of Camunda 8. Once the ILM policies are created, you can stop the Orchestration Cluster, remove this privilege, and then start the Orchestration Cluster again. ### Backup privileges To use the [backup feature](/self-managed/operational-guides/backup-restore/backup-and-restore.md), you must have snapshot privileges. You can provide these privileges to each Component before you create a backup, and then revoke them after the backup has been completed: - `create_snapshot` - Creates a backup, or snapshot, of a running cluster. - `monitor_snapshot` - Provides read-only permissions to list and view snapshot details. ### Upgrade privileges When [upgrading](/self-managed/upgrade/components/index.md) to a newer version of Camunda 8 which requires data migration, the following are required: - `manage_pipeline` - Allows any data transformations to occur when upgrading. - `manage_index_templates` - See [cluster privileges](#cluster-privileges). - `manage_ilm` - _Required when index lifecycle management (ILM) is enabled._ See [cluster privileges](#cluster-privileges). These privileges can be granted temporarily during an upgrade: - Stop the Component, and grant the required privileges - Start the Component and perform the upgrade - Stop the Component when the upgrade is complete, and remove any privileges - Start the Component normally ## Indices privileges The following permissions are required to read, write, view, and update Elasticsearch indices. More information on indices privileges can be found in the [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/security-privileges.html#privileges-list-indices). - `create_index` - `delete_index` - `read` - `write` - `manage` - `manage_ilm` - _Required when index lifecycle management (ILM) is enabled._ --- ## Elasticsearch without cluster privileges If the Camunda single application cannot access Elasticsearch with cluster-level privileges, you can run the schema manager as a standalone application, separate from the main application. ## Standalone schema manager When running the schema manager as a standalone application, cluster-level privileges are required only during schema creation. The single application itself does not need cluster-level privileges. - Database support: This setup supports Elasticsearch only. For OpenSearch, see [Run OpenSearch without cluster privileges](./opensearch-without-cluster-privileges.md). - Privileges required by the single application: The Camunda single application still requires an index-level privilege of at least `manage` to function properly. To run the schema manager as a standalone application: 1. [Initialize the schema manager](#initialize): The database schema must first be initialized. 2. [Start the Camunda single application](#start): Once the schema is initialized, start the application without cluster-level privileges. ### 1. Initialize the schema manager {#initialize} The schema manager is a separate standalone Java application responsible for creating and managing the database schema, and applying database settings (e.g., retention policies). :::note - Initialization requires a user with cluster-level privileges (e.g., `superuser`) in the database. - Initialization needs to be executed only once per installation. ::: #### Configure the schema manager Create a custom configuration for the schema manager with the following values: ```yaml camunda: data: secondary-storage: type: elasticsearch elasticsearch: # Example assuming an existing user called 'camunda-admin' who has 'superuser' privileges username: camunda-admin password: camunda123 url: https://localhost:9200 # If custom SSL configuration is necessary security: enabled: true self-signed: true verify-hostname: false certificate-path: PATH_TO_CA_CERT # Optional, only if ILM is enabled database: retention: enabled: true # Optional, only if legacy Elasticsearch exporter is used zeebe.broker.exporters.elasticsearch: class-name: io.camunda.zeebe.exporter.ElasticsearchExporter args: url: https://localhost:9200 index: createTemplate: true retention: enabled: true # Example assuming an existing user called 'camunda-admin' who has 'superuser' privileges authentication: username: camunda-admin password: camunda123 ``` For additional configuration options, see the [common secondary storage configuration](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#secondary-storage). #### Start the schema manager Using the custom configuration file, start the Java application `schema` (or `schema.bat` on Windows) located in the `bin` folder of the delivered JAR package. The schema manager will create the necessary indices and templates in the database and apply the configured settings. Assuming your custom configuration is saved as `schema-manager.yaml`, you can start the application with the following command: ```shell SPRING_CONFIG_ADDITIONALLOCATION=/path/to/schema-manager.yaml ./bin/schema ``` Verify that the application executed successfully. ### 2. Start the Camunda single application {#start} The Camunda single application can now be started without cluster-level privileges. It will connect to the database and use the schema previously created by the schema manager. #### Elasticsearch user with sufficient privileges Ensure that an Elasticsearch user with sufficient privileges exists. The application requires a database user with at least `manage` privileges on the indices it needs to access. You can either use an existing user with the required privileges or assign the necessary privileges to an example user named `camunda-app` by sending the following request to the Elasticsearch REST API: ```yaml PUT _security/role/read_write_role { "indices": [ { "names": [ "*" ], "privileges": [ "read", "write", "view_index_metadata" ], "allow_restricted_indices": false }, { "names": [ "camunda-*", "operate-*", "tasklist-*", "zeebe-*" ], "privileges": [ "manage" ], "allow_restricted_indices": false } ], "applications": [], "run_as": [], "metadata": {}, "transient_metadata": { "enabled": true } } ``` Next, assign the user to the role defined above. For example, if Elasticsearch is running on Docker, use the following command: ```shell docker exec -t elasticsearch elasticsearch-users useradd camunda-app -p camunda123 docker exec -t elasticsearch elasticsearch-users roles camunda-app -a read_write_role ``` #### Configure the Camunda single application Create a configuration for the Camunda single application with the following values. This essentially disables schema creation for the application. ```yaml camunda: data: secondary-storage: type: elasticsearch elasticsearch: # Example assuming an existing user called 'camunda-app' with the privileges described in 2.1 username: camunda-app password: camunda123 url: https://localhost:9200 # If custom SSL configuration is necessary security: enabled: true self-signed: true verify-hostname: false certificate-path: PATH_TO_CA_CERT database: schema-manager: createSchema: false # only required for upgrades from 8.7 tasklist: zeebe-elasticsearch: username: camunda-app password: camunda123 url: https://localhost:9200 # If custom SSL configuration is necessary ssl: self-signed: true verify-hostname: false certificate-path: PATH_TO_CA_CERT # only required for upgrades from 8.7 operate: zeebe-elasticsearch: # Example assuming an existing user called 'camunda-app' with the privileges described in 2.1 username: camunda-app password: camunda123 url: https://localhost:9200 # If custom SSL configuration is necessary ssl: self-signed: true verify-hostname: false certificate-path: PATH_TO_CA_CERT zeebe.broker.exporters: camundaexporter: class-name: io.camunda.zeebe.exporter.CamundaExporter args: createSchema: false history: # Optional, only if ILM is enabled retention: enabled: true # Optional, only if legacy Elasticsearch exporter is used elasticsearch: class-name: io.camunda.zeebe.exporter.ElasticsearchExporter args: url: https://localhost:9200 index: create-template: false retention: enabled: false manage-policy: false # Example assuming an existing user called 'camunda-app' with the privileges described in 2.1 authentication: username: camunda-app password: camunda123 ``` #### Start the application You can start the application using the custom configuration either from the JAR file or with Helm charts. #### Start the application from the JAR file Start the Java application `camunda` (or `camunda.bat` on Windows), located in the `bin` folder of the delivered JAR package. Assuming the configuration is saved in a file named `application-custom.yaml`, start the application with the following command: ``` SPRING_CONFIG_ADDITIONALLOCATION=/path/to/application-custom.yaml ./bin/camunda ``` #### Starting the application using Helm charts ##### Case 1: Auto-generated app configuration by Helm chart [Spring Boot convention](https://docs.spring.io/spring-boot/reference/features/external-config.html#features.external-config.typesafe-configuration-properties.relaxed-binding.environment-variables) environment variables can be used to override configuration. The following Helm values are needed to disable the schema manager in the Camunda apps. ```yaml # Helm chart values file. orchestration: env: - name: CAMUNDA_DATABASE_SCHEMAMANAGER_CREATESCHEMA value: "false" - name: CAMUNDA_TASKLIST_ELASTICSEARCH_HEALTHCHECKENABLED value: "false" - name: CAMUNDA_OPERATE_ELASTICSEARCH_HEALTHCHECKENABLED value: "false" - name: ZEEBE_BROKER_EXPORTERS_CAMUNDAEXPORTER_ARGS_CREATESCHEMA value: "false" - name: ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_ARGS_INDEX_CREATETEMPLATE value: "false" - name: ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_ARGS_RETENTION_ENABLED value: "false" - name: ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_ARGS_RETENTION_MANAGEPOLICY value: "false" ``` ##### Case 2: Manually-managed app config by the user If the application configurations are managed directly and do not rely on the Helm chart auto-generated configuration. ```yaml # Helm chart values file. orchestration: configuration | [...] # Any other custom config. camunda.database: schema-manager: create-schema: false camunda.tasklist: elasticsearch: health-check-enabled: false camunda.operate: elasticsearch: health-check-enabled: false zeebe.broker.exporters: camundaexporter: class-name: io.camunda.zeebe.exporter.CamundaExporter args: create-schema: false elasticsearch: class-name: io.camunda.zeebe.exporter.ElasticsearchExporter args: index: create-template: false retention: enabled: false manage-policy: false [...] # Any other custom config. ``` ### Minor version upgrades using the standalone schema manager {#minor-upgrades} Prepare a Camunda minor version upgrade by running the standalone schema manager for the target version (`N+1`). This pre-creates or adjusts index templates and mappings. You can then upgrade the Camunda single application, minimizing downtime for upgrades that require only schema adjustments. :::important Upgrading from 8.7 → 8.8 requires migration steps. Follow the relevant guides and plan a maintenance window: - [Components upgrade 8.7 to 8.8](/versioned_docs/version-8.8/self-managed/upgrade/components/870-to-880.md) - [Helm chart upgrade guide: 8.7 to 8.8](/versioned_docs/version-8.8/self-managed/upgrade/helm/870-to-880.md) These steps may require stopping or scaling down the Camunda application before running the migration. ::: If the target upgrade also requires a data or application migration (as documented in [Upgrade to Camunda 8.8](/versioned_docs/version-8.8/self-managed/upgrade/index.md)), follow the migration sequence: 1. Stop the Camunda application (or scale it down) before executing the migration logic. 2. Run the schema manager for version `N+1` with a privileged user if schema changes are part of the upgrade. 3. Execute any required migration tooling or steps described in the upgrade documentation. 4. Start or roll out the Camunda application at version `N+1` with schema creation disabled. If no migration is required, you can keep the application running at version `N` while you run the schema manager for version `N+1`. #### High-level flow 1. Current state: Camunda single application is running at version `N` (for example, 8.7) and processing traffic with its indices in Elasticsearch. 2. Verification: Check the upgrade documentation for version `N → N+1` (for example, 8.7 → 8.8) to determine if migrations are required. - If migrations are not required, continue while keeping `N` running. - If migrations are required, schedule downtime and stop the application before running migration steps. 3. Preparation: Obtain the Camunda distribution for version `N+1`. 4. Run the schema manager for version `N+1` with a configuration that grants the required cluster privileges (see [Initialize the schema manager](#initialize)). Keep the existing application at version `N` running. The schema manager applies any new or updated templates, mappings, and ILM policies (if enabled) required by version `N+1`. 5. Completion check: Wait until the schema manager logs successful completion and exits without errors. 6. Application upgrade: Upgrade or perform a rolling update of the Camunda single application from version `N` to `N+1`, using a configuration that disables schema creation. The new version will reuse the already-prepared indices. #### Example timeline | Time | Action | | ---- | ----------------------------------------------------------------------- | | T0 | App v8.6.X running, serving workload | | T1 | Launch schema manager v8.7.Y with elevated cluster privileges | | T2 | Schema manager completes successfully and exits | | T3 | Upgrade or roll out application to v8.7.Y with schema creation disabled | | T4 | Traffic now served by app v8.7.Y | This staged approach reduces or eliminates downtime for minor upgrades that require only schema adjustments. ### Update index settings with the standalone schema manager {#settings-updates} You can use the standalone schema manager to roll out certain index template setting changes without granting cluster privileges to the continuously running Camunda application. Supported settings (see [configuration references](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#index--retention-settings) and the [Elasticsearch exporter configuration](../../../components/orchestration-cluster/zeebe/exporters/elasticsearch-exporter.md#configuration)): - **numberOfShards** (Operate / Tasklist / Camunda / Zeebe Elasticsearch exporter) — static: applies only to new indices created after the change. Existing indices keep their shard count. - **numberOfReplicas** (Operate / Tasklist / Camunda) — dynamic: applied to existing indices and index templates. - **numberOfReplicas** (Zeebe Elasticsearch exporter) — static: applies only to new indices created after the change. Existing indices keep their replica count. - **templatePriority** (Operate / Tasklist / Camunda / Zeebe Elasticsearch exporter): determines precedence when multiple index templates match. Higher priority templates override lower ones. #### When to use the schema manager for settings updates Use the standalone schema manager if you need to: - Adjust index template-level settings for future indices. - Trigger a global index replicas count change. - Modify index template priority. #### Procedure 1. Prepare a schema manager configuration that includes the new settings. - For Operate and Tasklist version 8.7.11+, set `updateSchemaSettings: true`. Example configuration: ```yaml zeebe.broker.exporters.elasticsearch: class-name: io.camunda.zeebe.exporter.ElasticsearchExporter args: index: create-template: true number-of-shards: 3 # affects only new Zeebe record indices number-of-replicas: 1 # affects only new Zeebe record indices template-priority: 25 # optional, overrides default priority 20 ... # other settings camunda: database: index: number-of-shards: 1 # only new Operate/Tasklist/Camunda indices number-of-replicas: 1 # updates existing Operate/Tasklist/Camunda indices template-priority: 25 # optional, overrides default priority 0 ... # other settings ``` 2. Run the standalone schema manager with a user that has the required cluster privileges (see [Initialize the schema manager](#initialize)). You can keep the Camunda application online without cluster privileges. 3. Check the logs to confirm the schema manager completed successfully. ### Limitations - This feature only works with Elasticsearch installations. - Camunda Optimize cannot be used with this setup. ## Standalone backup application If the Camunda application(s) cannot access Elasticsearch with cluster-level privileges, you can run the backup for Operate and Tasklist data as a standalone application, separate from the main application. Creating a snapshot in Elasticsearch requires `manage_snapshots` cluster-level privileges. These privileges are only needed by the application responsible for creating the backups; the Camunda application(s) do not require cluster-level privileges. - Database support: This setup supports Elasticsearch only. For OpenSearch, see [Run OpenSearch without cluster privileges](./opensearch-without-cluster-privileges.md). - Indices: The standalone application only handles Operate and Tasklist indices. Optimize is not included in this procedure. :::note Before using the standalone backup manager: - A user with cluster-level privileges (including snapshot creation) must be configured in Elasticsearch. A user with the [snapshot_user](https://www.elastic.co/guide/en/elasticsearch/reference/current/built-in-roles.html#:~:text=related%20to%20rollups.-,snapshot_user,-Grants%20the%20necessary) role should be sufficient to run the backup application. However, restoring snapshots also requires index-level permissions. - An [Elasticsearch snapshot repository](https://www.elastic.co/guide/en/elasticsearch/reference/current/snapshot-restore.html) must be configured. ::: ### 1. Configure the backup application Create a custom `backup-manager.yaml` configuration file for the standalone backup application using the following values: ```yaml camunda: data: backup: # Example assuming an existing snapshot repository 'els-test' repository-name: els-test secondary-storage: type: elasticsearch elasticsearch: # Example assuming an existing user called 'camunda-admin' who has 'snapshot_user' privileges username: camunda-admin password: camunda123 url: https://localhost:9200 # If custom SSL configuration is necessary security: enabled: true self-signed: true verify-hostname: false certificate-path: PATH_TO_CA_CERT ``` For additional configuration options, see the [common database configuration guide](../../../components/orchestration-cluster/core-settings/configuration/properties.md#data---secondary-storage). ### 2. Start the backup application Start the Java application `backup-webapps` (or `backup-webapps.bat` on Windows), located in the `bin` folder of the delivered JAR package. This application requires a `` argument—a unique identifier of type `java.lang.Long`, used as part of the snapshot names. To learn more, see the [backup and restore guide](/self-managed/operational-guides/backup-restore/backup-and-restore.md). Assuming your custom configuration is saved in a file named `backup-manager.yaml`, start the application using the following command: ```shell SPRING_CONFIG_ADDITIONALLOCATION=/path/to/backup-manager.yaml ./bin/backup-webapps ``` The standalone application will log the current state of the backup every five seconds until it completes. Verify that the application executed successfully. Example output logs: ``` 11:42:13.713 [main] INFO i.c.a.StandaloneBackupManager - Snapshot observation: 11:42:13.714 [main] INFO i.c.a.StandaloneBackupManager - Indices snapshot is COMPLETED. Details: [GetBackupStateResponseDto{backupId=12345, state=COMPLETED, failureReason='null', details=[GetBackupStateResponseDetailDto{snapshotName='camunda_webapps_12345_snapshot_part_1_of_7', state='SUCCESS', startTime=2025-06-25T11:42:08.495+02:00, failures=null}, GetBackupStateResponseDetailDto{snapshotName='camunda_webapps_12345_snapshot_part_2_of_7', state='SUCCESS', startTime=2025-06-25T11:42:08.695+02:00, failures=null}, GetBackupStateResponseDetailDto{snapshotName='camunda_webapps_12345_snapshot_part_3_of_7', state='SUCCESS', startTime=2025-06-25T11:42:08.897+02:00, failures=null}, GetBackupStateResponseDetailDto{snapshotName='camunda_webapps_12345_snapshot_part_4_of_7', state='SUCCESS', startTime=2025-06-25T11:42:08.897+02:00, failures=null}, GetBackupStateResponseDetailDto{snapshotName='camunda_webapps_12345_snapshot_part_5_of_7', state='SUCCESS', startTime=2025-06-25T11:42:08.897+02:00, failures=null}, GetBackupStateResponseDetailDto{snapshotName='camunda_webapps_12345_snapshot_part_6_of_7', state='SUCCESS', startTime=2025-06-25T11:42:09.097+02:00, failures=null}, GetBackupStateResponseDetailDto{snapshotName='camunda_webapps_12345_snapshot_part_7_of_7', state='SUCCESS', startTime=2025-06-25T11:42:09.097+02:00, failures=null}]}] 11:42:13.714 [main] INFO i.c.a.StandaloneBackupManager - Backup with id:[12345] is completed! ``` The backup manager creates a backup of Elasticsearch data. The backup includes several Elasticsearch snapshots containing sets of Camunda, Operate and Tasklist indices. For example, a backup with an ID of `123` might contain the following Elasticsearch snapshots: ``` camunda_webapps_123_8.8.0_part_1_of_7 camunda_webapps_123_8.8.0_part_2_of_7 camunda_webapps_123_8.8.0_part_3_of_7 camunda_webapps_123_8.8.0_part_4_of_7 camunda_webapps_123_8.8.0_part_5_of_7 camunda_webapps_123_8.8.0_part_6_of_7 camunda_webapps_123_8.8.0_part_7_of_7 ``` Once completed, you can proceed with step 7 of the [backup procedure](/self-managed/operational-guides/backup-restore/backup-and-restore.md#backup-process). ### Limitations - This feature only works for installations using Elasticsearch. - Camunda Optimize data cannot be backed up with this setup. - Some operations that are supported by the backup actuator API are not supported by this feature. As a workaround, you can use the Elasticsearch API as follows: #### List the snapshots of a backup ``` GET /_snapshot//*__* ``` #### Delete the snapshots of a backup :::warning Make sure the `` you provide is not a single digit integer, otherwise the following command will delete more snapshots than desired. ::: ``` DELETE /_snapshot//*__* ``` --- ## OpenSearch privileges If you implement Camunda 8 with OpenSearch as a service provider, you must configure OpenSearch with the following [permissions](https://opensearch.org/docs/latest/security/access-control/permissions/) and [default action groups](https://opensearch.org/docs/latest/security/access-control/default-action-groups/) in mind: Action groups are a set of permissions. Permissions have the `cluster|indices` prefix; all others are action groups. ## Cluster - `cluster_monitor` - Necessary for health check. - `cluster:admin/reindex/rethrottle` - Necessary to archive and migrate indices. - `cluster_manage_pipelines` - Necessary to migrate indices. - `manage_snapshots` - Necessary to take backups. - `indices:admin/index_template/put` - Necessary to create and manage index schema on start up and migration. - `indices:admin/index_template/get` - Necessary to create and manage index schema on start up and migration. - `indices:admin/index_template/delete` - Necessary to create and manage index schema on start up and migration. - `indices:data/write/reindex` - Necessary to reindex during archiving. Required to move data from runtime indices to dated indices. - `indices:data/read/scroll` - Necessary to scroll through data when reading large result sets. - `indices:data/read/scroll/clear` - Necessary to search with paging. - `cluster_manage_templates` - Necessary to create and manage index schema on start up. - `cluster_manage_index_templates` - Necessary to create and manage index schema on start up. ## Indices - `data_access` - Necessary to query and read. - `get` - Necessary to read. - `delete` - Necessary to create, archive, and migrate data. - `create_index` - Necessary to create index schema and archive. - `search` - Necessary to query. - `manage` - Necessary to create index schema, archive, and migrate. ## Index state management Add in the cluster section of permissions for using index state management (ISM): - `cluster:admin/opendistro/ism/managedindex/add` - `cluster:admin/opendistro/ism/managedindex/change` - `cluster:admin/opendistro/ism/managedindex/remove` - `cluster:admin/opendistro/ism/policy/write` - `cluster:admin/opendistro/ism/policy/get` - `cluster:admin/opendistro/ism/policy/delete` --- ## OpenSearch without cluster privileges If the Camunda single application cannot access OpenSearch with cluster-level privileges (for example, due to restrictive IAM or fine‑grained access control policies), you can run the schema manager as a standalone application, separate from the main application. This approach mirrors the Elasticsearch procedure, but uses OpenSearch-specific configuration and privileges. For Elasticsearch, see [Elasticsearch without cluster privileges](./elasticsearch-without-cluster-privileges.md). ## Standalone schema manager When you run the schema manager as a standalone application, it requires cluster-level privileges only during schema creation and settings updates. The Camunda application then runs with minimal privileges (primarily index-level permissions, with one specific cluster-level requirement for clearing scrolls due to the OpenSearch security model). - Database support: This setup is supported only for OpenSearch installations (Elasticsearch procedure uses a different configuration). - Required privileges: The Camunda application requires the `manage` index-level privilege, and the `indices:data/read/scroll/clear` cluster permission to operate (see [OpenSearch privileges](./opensearch-privileges.md)). To run the schema manager as a standalone application: 1. [Initialize the schema manager](#initialize): Create templates, indices (as required), and apply retention ISM policies. 2. [Start the Camunda single application](#start): Run without cluster-level privileges using a restricted user. ### Initialize the schema manager {#initialize} The schema manager is a separate Java application responsible for creating and managing the database schema and applying index template settings (for example, shard/replica counts and retention policies). :::note - Initialization requires a user with cluster-level privileges. For example, use an OpenSearch administrative role with access to action groups such as `cluster_manage_index_templates`, `cluster_monitor`, and the index template CRUD permissions listed in [OpenSearch privileges](./opensearch-privileges.md). - Initialization needs to be executed only once per installation (and again for minor upgrades requiring schema adjustments). ::: #### Configure the schema manager settings Create a custom configuration file for the schema manager with the following values: ```yaml camunda: data: secondary-storage: type: opensearch opensearch: # Example assuming an existing privileged user 'camunda-admin' username: camunda-admin password: camunda123 url: https://localhost:9200 # If custom SSL configuration is necessary security: enabled: true self-signed: true verify-hostname: false certificate-path: PATH_TO_CA_CERT # Optional if retention / ISM policies are enabled via schema manager database: retention: enabled: true # Optional only if legacy OpenSearch exporter (pre Camunda Exporter adoption) is used zeebe.broker.exporters.opensearch: class-name: io.camunda.zeebe.exporter.opensearch.OpensearchExporter args: url: https://localhost:9200 index: createTemplate: true retention: enabled: true authentication: username: camunda-admin password: camunda123 ``` For additional configuration options, review the [secondary storage configuration](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#secondary-storage) and the [OpenSearch exporter configuration](/self-managed/components/orchestration-cluster/zeebe/exporters/opensearch-exporter.md). #### Start the schema manager Start the `schema` Java application (or `schema.bat` on Windows) from the `bin` directory by using your custom configuration file. Assuming your configuration is saved as `schema-manager-opensearch.yaml`: ```bash SPRING_CONFIG_ADDITIONALLOCATION=/path/to/schema-manager-opensearch.yaml ./bin/schema ``` Wait for successful completion (application exits cleanly) before moving to step 2. ### Start the Camunda single application {#start} Start the application with a less privileged OpenSearch user. Most operations require only index-level privileges (`manage` for required indices), but `indices:data/read/scroll/clear` must be assigned as a cluster permission. OpenSearch treats the permission to clear scrolls as a cluster-wide action because scroll IDs are not bound to a specific index endpoint in the request URL. #### OpenSearch user with required privileges Create or reuse a user with at least the following index privileges on all Camunda indices: - `manage` (covers create index, mappings updates, search, read, write) You can create a role using OpenSearch Security plugin APIs (for IAM roles on AWS OpenSearch Service, please refer to [Identity and Access Management in Amazon OpenSearch Service](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/ac.html)). Example role definition: ```bash curl -XPUT https://localhost:9200/_plugins/_security/api/roles/camunda_app_role \ -H 'Content-Type: application/json' \ -u admin:admin \ -d '{ "cluster_permissions": [ "indices:data/read/scroll/clear" ], "index_permissions": [ { "index_patterns": [ "zeebe-*", "operate-*", "tasklist-*", "camunda-*" ], "allowed_actions": [ "indices:data/write/*", "indices:data/read/*", "indices:admin/create", "indices:admin/shards/search_shards" ] } ] }' ``` Then assign the role to the user (example using Security plugin user API): ```bash curl -XPUT https://localhost:9200/_plugins/_security/api/internalusers/camunda-app \ -H 'Content-Type: application/json' \ -u admin:admin \ -d '{"password": "camunda123", "opendistro_security_roles": ["camunda_app_role"]}' ``` #### Configure the Camunda single application Disable schema creation in the application configuration so it reuses what the standalone schema manager prepared: ```yaml camunda: data: secondary-storage: type: opensearch opensearch: # Example restricted user 'camunda-app' username: camunda-app password: camunda123 url: https://localhost:9200 security: enabled: true self-signed: true verify-hostname: false certificate-path: PATH_TO_CA_CERT database: schema-manager: createSchema: false tasklist: opensearch: health-check-enabled: false # Only required for upgrades from 8.7 (legacy component configs) zeebe-elasticsearch: # legacy key retained for backward compatibility username: camunda-app password: camunda123 url: https://localhost:9200 ssl: self-signed: true verify-hostname: false certificate-path: PATH_TO_CA_CERT operate: opensearch: health-check-enabled: false # Only required for upgrades from 8.7 (legacy component configs) zeebe-elasticsearch: # legacy key retained for backward compatibility username: camunda-app password: camunda123 url: https://localhost:9200 ssl: self-signed: true verify-hostname: false certificate-path: PATH_TO_CA_CERT zeebe.broker.exporters: camundaexporter: class-name: io.camunda.zeebe.exporter.CamundaExporter args: createSchema: false history: retention: enabled: true # Only if ISM retention enabled globally opensearch: class-name: io.camunda.zeebe.exporter.opensearch.OpensearchExporter args: url: https://localhost:9200 index: createTemplate: false retention: enabled: false managePolicy: false authentication: username: camunda-app password: camunda123 ``` #### Start from the JAR distribution ```bash SPRING_CONFIG_ADDITIONALLOCATION=/path/to/application-opensearch.yaml ./bin/camunda ``` #### Start using Helm charts If using Helm, disable schema creation via environment variables: ```yaml # values.yaml snippet orchestration: env: - name: CAMUNDA_DATABASE_SCHEMAMANAGER_CREATESCHEMA value: "false" - name: CAMUNDA_TASKLIST_OPENSEARCH_HEALTHCHECKENABLED value: "false" - name: CAMUNDA_OPERATE_OPENSEARCH_HEALTHCHECKENABLED value: "false" - name: ZEEBE_BROKER_EXPORTERS_CAMUNDAEXPORTER_ARGS_CREATESCHEMA value: "false" - name: ZEEBE_BROKER_EXPORTERS_OPENSEARCH_ARGS_INDEX_CREATETEMPLATE value: "false" - name: ZEEBE_BROKER_EXPORTERS_OPENSEARCH_ARGS_RETENTION_ENABLED value: "false" ``` Or when managing configuration manually: ```yaml # values.yaml snippet (manual configuration block) orchestration: configuration: | camunda.database: schema-manager: create-schema: false camunda.tasklist.opensearch: health-check-enabled: false camunda.operate.opensearch: health-check-enabled: false zeebe.broker.exporters: camundaexporter: class-name: io.camunda.zeebe.exporter.CamundaExporter args: create-schema: false opensearch: class-name: io.camunda.zeebe.exporter.opensearch.OpensearchExporter args: index: create-template: false retention: enabled: false manage-policy: false ``` ### Minor version upgrades with the standalone schema manager {#minor-upgrades} For a minor upgrade (N → N+1), pre-run the standalone schema manager of version N+1 with a privileged user to apply new templates/mappings. Then upgrade the application with schema creation disabled. If the upgrade requires a data/application migration (see [Upgrade overview](/versioned_docs/version-8.8/self-managed/upgrade/index.md)): 1. Stop or scale down the application. 2. Run schema manager (version N+1) with elevated privileges. 3. Execute migration tooling. 4. Start application at version N+1. If no migration is required you can keep N serving traffic while running the schema manager for N+1. ### Update index settings with the standalone schema manager {#settings-updates} You can roll out index template setting changes (shards, replicas, template priority) via the standalone schema manager without providing cluster privileges to the running application. Supported settings (see [configuration references](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#index--retention-settings) and [OpenSearch exporter configuration](/self-managed/components/orchestration-cluster/zeebe/exporters/opensearch-exporter.md)): - numberOfShards (new indices only; Operate / Tasklist / Camunda / Zeebe exporter) - numberOfReplicas (dynamic for Operate / Tasklist / Camunda; static for Zeebe exporter indices) - templatePriority (precedence when multiple templates match) Procedure: 1. For Operate and Tasklist 8.7.11+, set `updateSchemaSettings: true` if applicable. 2. Run schema manager with privileged user while application remains online. 3. Verify successful completion. ### Limitations - This procedure applies to OpenSearch installations only. - Optimize supports OpenSearch (see release notes). However, you cannot include Optimize in this reduced-privilege model if it requires additional cluster features, such as advanced aggregations. Evaluate required privileges before including Optimize. - Does not remove the need for appropriate ISM permissions if retention is enabled. ## Standalone backup application Snapshot backups require the `manage_snapshots` cluster-level privilege. If the main application cannot hold this privilege, run a separate backup application with an elevated user. - Database support: Supported only for OpenSearch installations in this procedure. - Indices covered: Operate and Tasklist indices (Optimize indices excluded from this simplified procedure; handle separately if used). Before running the standalone backup manager: 1. Ensure snapshot repository is configured (for example, S3, filesystem) with appropriate permissions. 2. Ensure elevated user has `manage_snapshots` and access to index patterns. 3. Prepare backup configuration file referencing OpenSearch connection under `camunda.data.secondary-storage.opensearch`. (Backup command usage mirrors Elasticsearch steps; adjust endpoints to OpenSearch.) ### High-level flow recap | Step | Action | | ---- | -------------------------------------------------------------------- | | 1 | Run the privileged schema manager to prepare templates and indices | | 2 | Start the application with a restricted user | | 3 | (Upgrade) Run the next version of the schema manager with privileges | | 4 | Upgrade the application with schema creation disabled | | 5 | (Optional) Run the standalone backup application | This staged approach reduces or eliminates downtime for minor upgrades and isolates cluster-level privileges to short-lived administrative tasks rather than long-running services. --- ## Overview(Databases) Camunda applications depend on a secondary storage backend to read workflow and decision data exported from the Zeebe engine. This storage layer can use either a document-store backend or a relational database (RDBMS), depending on your requirements. For an architectural explanation of how secondary storage fits into Camunda 8, see the [secondary storage overview](/self-managed/concepts/secondary-storage/index.md). ## App database support | App | RDBMS | Non-SQL | | --------------------- | ----- | ------- | | Orchestration Cluster | Yes | Yes | | Optimize | No | Yes | | Web Modeler | Yes | No | | Management Identity | Yes | No | Use this matrix as a compatibility summary for the main Camunda components and their supported database backends. ## Document-store backends Camunda supports document-store backends such as Elasticsearch and OpenSearch. These systems are optimized for high-volume ingestion and flexible search queries. Related documentation: - [Elasticsearch privileges](/self-managed/concepts/databases/elasticsearch/elasticsearch-privileges.md) - [OpenSearch privileges](/self-managed/concepts/databases/elasticsearch/opensearch-privileges.md) - [Elasticsearch exporter](/self-managed/components/orchestration-cluster/zeebe/exporters/elasticsearch-exporter.md) - [OpenSearch exporter](/self-managed/components/orchestration-cluster/zeebe/exporters/opensearch-exporter.md) ## Relational databases (RDBMS) Camunda also supports several relational databases for secondary storage, enabling the Orchestration Cluster API, Operate, Tasklist, and Admin to run without Elasticsearch or OpenSearch. RDBMS and document-store backends are both valid secondary storage options. Select based on your workload, operational model, and platform standards. A full list of supported vendors and versions, JDBC driver information, and component compatibility is published in the [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md). For configuration details in Helm deployments, see the [RDBMS configuration guide](/self-managed/deployment/helm/configure/database/rdbms.md). ### RDBMS behavior and limitations When using an RDBMS as secondary storage, keep the following limitations in mind: - **Length limits for user-defined values:** Most user-defined strings that are exported to secondary storage or stored in identity management are length-limited. See [string length limits for user-defined values](#string-length-limits-for-user-defined-values). - **Variable comparisons:** For String and JSON variables, comparison operators (`equals`, `notEquals`, `in`, `notIn`) only consider the first 8191 characters (or 4000 characters with Oracle). `LIKE` comparisons are not affected. - **Sorting may differ by vendor:** Because collation behavior varies across database vendors, results sorted by string fields may differ between systems. #### String length limits for user-defined values Camunda limits most user-defined strings before they are exported to secondary storage or stored in identity management. | Scope | Limit | Notes | | ------------------------------------------ | --------------------- | ---------------------------------------------------------------------------------------------------- | | Elasticsearch/OpenSearch secondary storage | **32,768 characters** | This matches the practical limit for the relevant Elasticsearch / OpenSearch string fields. | | RDBMS secondary storage | **256 characters** | This limit is chosen to align exported field lengths with the supported RDBMS schema. | | Identity objects | **256 characters** | This limit applies independently of the secondary storage backend used by the Orchestration Cluster. | These limits are enforced using Java string length semantics rather than raw UTF-8 byte counts. In practice, most common Latin, CJK, and Arabic characters count as one character, while characters represented as surrogate pairs in Java count as two. Because of that, the effective visible-character limit can be lower for some inputs. The limits apply to the following user-defined fields: - BPMN element IDs and names - DMN element IDs and names - Job worker types in task definitions - Variable names - Resource names of deployed BPMN, DMN, and form files - Form IDs - Message names and correlation keys - Identity object identifiers and names Special case for DMN on RDBMS: if a rule does not define its own ID, Camunda generates one from the decision ID, decision version, and rule index. In that case, keep the decision ID at **235 characters or fewer** so the generated rule ID stays within the RDBMS limit. These limits are currently not configurable. In particular, the RDBMS-backed limits are tied to the database schema and do not increase automatically if you change application configuration. #### Working with variables When retrieving variables through the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md), the following comparison operators only apply to the first 4000 characters of large String or JSON variables: - equals - notEquals - in - notIn This ensures consistent performance on large datasets. For details, see the [get variable specification](/apis-tools/orchestration-cluster-api-rest/specifications/get-variable.api.mdx). ## Related database topics These pages provide deeper detail for operators, DBAs, and administrators: - [RDBMS benchmark results](/self-managed/concepts/secondary-storage/rdbms-benchmark-results.md) - [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) - [Configure RDBMS in Helm](/self-managed/deployment/helm/configure/database/rdbms.md) - [Elasticsearch privileges](/self-managed/concepts/databases/elasticsearch/elasticsearch-privileges.md) - [OpenSearch privileges](/self-managed/concepts/databases/elasticsearch/opensearch-privileges.md) - [Configure the RDBMS exporter](/self-managed/concepts/databases/relational-db/configuration.md) :::note For guidance on coordinating backups between Zeebe (primary storage) and your configured secondary storage backend—whether Elasticsearch, OpenSearch, or an RDBMS—see the [backup and restore overview](/self-managed/operational-guides/backup-restore/backup-and-restore.md). ::: --- ## RDBMS configuration overview Camunda can use a relational database (RDBMS) as the secondary storage backend for Operate, Tasklist, Identity, and the Camunda REST API. This page explains how RDBMS configuration works at the application level. If you are deploying with Helm, see: - [RDBMS configuration in Helm](/self-managed/deployment/helm/configure/database/rdbms.md) - [Access native SQL and Liquibase scripts](/self-managed/deployment/helm/configure/database/access-sql-liquibase-scripts.md) For supported database vendors and versions, see the [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md). :::tip Need end-to-end guidance? For a unified setup guide covering provisioning, topology decisions, driver management, and backup strategies across both Orchestration Cluster and Web Modeler, see the [end-to-end RDBMS setup guide](/self-managed/concepts/databases/relational-db/rdbms-setup-guide.md). This guide is useful both when starting a new setup and when harmonizing existing component configurations. ::: ## Enable RDBMS as secondary storage To activate an RDBMS backend, configure two components: 1. **Enable the RDBMS exporter in Zeebe**, which streams workflow data to the database. 2. **Configure the application layer** (Operate, Tasklist, Identity, REST API) to use RDBMS for secondary storage. Example configuration: ```yaml # Enable the RDBMS exporter in Zeebe zeebe: broker: exporters: rdbms: className: camunda.data.exporters.rdbms.className # Configure secondary storage for Camunda applications camunda: data: secondary-storage: type: rdbms rdbms: url: jdbc:postgresql://localhost:5432/camunda username: camunda password: camunda ``` The RDBMS exporter can be used alongside other exporters, but enabling multiple exporters may affect performance. ## Schema management Camunda uses Liquibase to automatically create and update the database schema on startup. Liquibase creates two internal management tables: - `DATABASECHANGELOG` - `DATABASECHANGELOGLOCK` These tables must not be modified or deleted. For Helm deployments requiring manual schema control or access to vendor-specific SQL, see: **[Access SQL and Liquibase scripts](/self-managed/deployment/helm/configure/database/access-sql-liquibase-scripts.md)**. ### Configure table prefix To add a prefix to all Camunda-managed database tables: ```yaml camunda.data.secondary-storage.rdbms.prefix: c8_ ``` ## Disable automatic schema creation If your organization manages schema manually: ```yaml camunda.data.secondary-storage.rdbms.auto-ddl: false ``` SQL scripts for manual schema creation are documented in the Liquibase/SQL access guide linked above. ## Database privileges The configured database user must have the following privileges on all Camunda tables: - SELECT - INSERT - UPDATE - DELETE ### Additional privileges for automatic schema management If Liquibase schema management is enabled, the following privileges must be granted before the first startup: - CREATE - ALTER - DROP ### Additional privilege for purge operations If using the RDBMS purge feature, the following privilege is required: - TRUNCATE ## History cleanup The RDBMS exporter performs automatic history cleanup using two mechanisms: 1. **TTL-based marking** Finished process instances and related data are marked for deletion after their configured history TTL expires. 2. **Periodic cleanup job** A scheduled cleanup process deletes marked data in batches, adjusting its interval dynamically: - If no data is deleted → interval doubles (up to `max-history-cleanup-interval`) - If the batch limit is reached → interval halves (down to `min-history-cleanup-interval`) - Otherwise → the interval remains unchanged ## Database driver Camunda images include JDBC drivers for all supported databases except Oracle and MySQL. If you use one of these databases, you must provide the driver yourself. ### Docker Compose When running Camunda with Docker Compose, mount the driver into `/driver-lib`: ```yaml services: camunda: image: camunda/camunda: volumes: - /driver-lib:/driver-lib ``` Place the driver JAR directly inside the mounted directory (not in subfolders). ### Helm If you are using the Helm charts, refer to the database configuration guide for the supported driver configuration options: - [Helm database configuration](../../../../self-managed/deployment/helm/configure/database/index.md) ## Database configuration RDBMS configuration properties are defined under: ```yaml camunda.data.secondary-storage.rdbms.* ``` | Property | Description | Default | | ----------------------- | ---------------------------------------------------------------- | ------- | | `url` | JDBC connection URL | _empty_ | | `user` | Username for the connection | _empty_ | | `password` | Password for the connection | _empty_ | | `auto-ddl` | Enables Liquibase schema management | `true` | | `prefix` | Optional table name prefix | `""` | | `database-vendor-id` | Manually override vendor detection (`postgres`, `mariadb`, etc.) | _empty_ | | `ddl-lock-wait-timeout` | Max time Liquibase can hold a lock on the database | PT15M | ## Connection pool configuration Camunda uses HikariCP for JDBC connection pooling. The following properties can be adjusted: | Property name | Description | Default | | ------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------- | | `camunda.data.secondary-storage.rdbms.connection-pool.maximum-pool-size` | Maximum number of simultaneous connections | 10 | | `camunda.data.secondary-storage.rdbms.connection-pool.minimum-idle` | Minimum number of idle connections | 2 | | `camunda.data.secondary-storage.rdbms.connection-pool.idle-timeout` | Timeout (ms) before closing an idle connection | 600000 | | `camunda.data.secondary-storage.rdbms.connection-pool.max-lifetime` | Maximum lifetime (ms) of each connection before it is closed and replaced | 1800000 | | `camunda.data.secondary-storage.rdbms.connection-pool.connection-timeout` | Maximum time (ms) the application waits for a connection from the pool | 30000 | ## Exporter configuration The RDBMS exporter is automatically enabled when: ```yaml camunda.data.secondary-storage.type: rdbms ``` The following additional configuration options are available under `camunda.data.secondary-storage.rdbms`: ### Exporter performance settings | Property name | Description | Default | | ------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ------- | | `flush-interval` | Maximum time a record waits in the flush queue before being flushed and committed to the database | PT0.5S | | `max-queue-size` | Maximum number of records allowed in the flush queue before a forced flush | 1000 | | `queue-memory-limit` | Maximum memory usage (MB) allowed for queued records before a forced flush | 20 | | `export-batch-operation-items-on-creation` | If due items should be exported at the beginning of a batch operation or only after processing | true | | `insert-batching.max-audit-log-insert-batch-size` | Maximum number of rows to batch into a single insert statement into the AUDIT_LOG table | 50 | | `insert-batching.max-flow-node-insert-batch-size` | Maximum number of rows to batch into a single insert statement into the FLOW_NODE table | 25 | | `insert-batching.max-job-insert-batch-size` | Maximum number of rows to batch into a single insert statement into the JOB table | 25 | | `insert-batching.max-variable-insert-batch-size` | Maximum number of rows to batch into a single insert statement into the VARIABLE table | 25 | ## History cleanup The RDBMS exporter provides automatic history cleanup, which works in two stages: 1. **TTL marking** When a root process instance finishes, the entire process instance hierarchy (the root and any child process instances started via Call Activities), and all related data are marked for deletion once the root instance's time-to-live expires. 2. **Periodic cleanup job** A scheduled cleanup job deletes marked records in batches and adjusts future intervals dynamically: - If no records are deleted → interval doubles (up to `max-history-cleanup-interval`) - If the batch size is fully used → interval halves (down to `min-history-cleanup-interval`) - Otherwise → interval remains unchanged - additionally the cleanup is only allowed to take a maximum of `max-history-cleanup-usage` execution time. This will not cut off the current execution, but it will affect the interval for the next one. ### History cleanup configuration RDBMS history configuration properties are defined under: ```yaml camunda.data.secondary-storage.rdbms.history.* ``` | Property name | Description | Default | | ---------------------------------------------- | ----------------------------------------------------------------------------------------------- | ---------- | | `default-history-ttl` | TTL for finished process instances and related data (ISO-8601 duration) | P30D | | `default-batch-operation-ttl` | TTL for batch operation history | P5D | | `batch-operation-cancel-process-instance-ttl` | TTL for cancel-process-instance batch operations | P5D | | `batch-operation-migrate-process-instance-ttl` | TTL for migrate-process-instance batch operations | P5D | | `batch-operation-modify-process-instance-ttl` | TTL for modify-process-instance batch operations | P5D | | `batch-operation-resolve-incident-ttl` | TTL for resolve-incident batch operations | P5D | | `historyCleanupBatchSize` | Maximum number of entries deleted per cleanup run | 1000 | | `min-history-cleanup-interval` | Minimum duration between cleanup runs (ISO-8601 duration) | PT1M | | `max-history-cleanup-interval` | Maximum duration between cleanup runs (ISO-8601 duration) | PT60M | | `max-history-cleanup-usage` | Maximum percentage of usage time the history cleanup is allowed to use (values between 0 and 1) | 0.25 (25%) | | `history-cleanup-process-instance-batch-size` | Number of process instances to be cleaned per cleanup run | 500 | | `history-cleanup-batch-size` | Number of rows to be cleaned per cleanup run in each table | 10000 | | `usage-metrics-ttl` | TTL for usage metrics | P730D | | `usage-metrics-cleanup` | Interval between usage metrics cleanup runs (ISO-8601 duration) | PT24H | ## Exporter cache configuration | Property name | Description | Default | | -------------------------------- | ---------------------------------------------------------------- | ------- | | `process-cache.max-size` | Maximum number of process definitions held in the exporter cache | 1000 | | `batch-operation-cache.max-size` | Maximum number of cached batch operations | 1000 | ## Multi-region support The RDBMS Exporter currently has no multi-region support. Only one RDBMS Exporter instance and one JDBC database connection can be configured per Orchestration Cluster. :::note Multi-region support for the RDBMS Exporter is not planned at this time. For multi-region setups, multi-region replication must be handled within the RDBMS itself, for example using a managed database service such as AWS Aurora. ::: ## Usage with AWS Aurora PostgreSQL Camunda supports **PostgreSQL** as a secondary storage backend. AWS Aurora PostgreSQL is a PostgreSQL-compatible managed service and is expected to work when configured like a standard PostgreSQL database. In addition to the standard PostgreSQL JDBC driver, you can use the **AWS Advanced JDBC Wrapper** to take advantage of Aurora-specific features such as improved failover handling and IAM-based authentication. To use the AWS JDBC wrapper, configure the JDBC URL as follows: ```yaml camunda: data: secondary-storage: type: rdbms rdbms: url: jdbc:aws-wrapper:postgresql://aurora-host:5432/camunda username: camunda password: camunda ``` The AWS JDBC wrapper supports standard username/password authentication as well as IAM-based authentication. To use IAM authentication, enable the corresponding wrapper plugin and configure a database user without a password that has the required IAM permissions: ```yaml camunda: data: secondary-storage: type: rdbms rdbms: url: jdbc:aws-wrapper:postgresql://aurora-host:5432/camunda?wrapperPlugins=iam username: camunda ``` The AWS JDBC wrapper JAR is shipped with the Camunda distribution, alongside most of the other JDBC drivers. There is no need to provide it separately. --- ## Relational databases Relational database (RDBMS) support is a core capability of the Orchestration Cluster in Camunda 8 Self-Managed. RDBMS is a standard secondary storage option for Orchestration Cluster installations, alongside Elasticsearch and OpenSearch. With RDBMS secondary storage, the RDBMS exporter writes orchestration data to your relational database, and the Orchestration Cluster API queries it. Operate, Tasklist, and Admin use that API rather than querying the database directly. In this context: - [Primary storage](/reference/glossary.md#primary-storage) is the broker-managed store for workflow execution state management. - [Secondary storage](/reference/glossary.md#secondary-storage) is an external store used for indexing, querying, analytics, and retention. For a deeper conceptual comparison, see [understanding primary vs secondary storage](/self-managed/concepts/secondary-storage/no-secondary-storage.md#about-this-mode). ```mermaid graph LR exporter["RDBMS Exporter"] api["Orchestration Cluster API"] rdbms["RDBMS\n(secondary storage)"] exporter -->|"Write"| rdbms api -->|"Query"| rdbms style exporter fill:#e4eef8,stroke:#2272c9,color:#14082c style api fill:#e4eef8,stroke:#2272c9,color:#14082c style rdbms fill:#fde8da,stroke:#fc5d0d,color:#14082c ``` ## Start here - New to RDBMS in Camunda: [End-to-end RDBMS setup guide](/self-managed/concepts/databases/relational-db/rdbms-setup-guide.md) - Need supported versions and compatibility details: [RDBMS version support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) - Need application-level settings and behavior: [RDBMS configuration overview](/self-managed/concepts/databases/relational-db/configuration.md) ## Deployment guides - Helm deployments: [Configure RDBMS in Helm](/self-managed/deployment/helm/configure/database/rdbms.md) - Manual deployments: [Configure RDBMS for manual installation](/self-managed/deployment/manual/rdbms/configuration.md) ## Related concepts - [Secondary storage](/self-managed/concepts/secondary-storage/index.md) - [Databases overview](/self-managed/concepts/databases/overview.md) --- ## End-to-end RDBMS setup guide This guide provides a unified approach to configuring relational databases for Camunda 8 across the Orchestration Cluster (Zeebe, Operate, Tasklist, Identity) and Web Modeler. It answers the key setup questions and links to detailed, component-specific configuration reference. :::info - For **Orchestration Cluster configuration reference**, see [RDBMS configuration overview](/self-managed/concepts/databases/relational-db/configuration.md). - For **Web Modeler configuration reference**, see [Web Modeler database configuration](/self-managed/components/hub/configuration/database.md). - For **supported vendors and versions**, see the [RDBMS version support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md). - For **deployment-specific setup**, see [Helm RDBMS configuration](/self-managed/deployment/helm/configure/database/rdbms.md) or [manual RDBMS configuration](/self-managed/deployment/manual/rdbms/configuration.md). ::: ## Step 1: Decide on topology Before provisioning, choose whether to use a **shared database** or **separate databases** for each component. | Aspect | Shared database | Separate databases | | ------------ | ----------------------------------------------------------------------- | ----------------------------------------------------------------- | | **Use case** | Small deployments, unified DBA team | Large production, multi-team environments | | **Setup** | Single instance with different schemas/databases for OC and Web Modeler | Independent instances per component | | **Pros** | Simplified administration, single backup policy | Independent scaling, isolated credentials, easier troubleshooting | | **Cons** | Shared resources, requires schema/database separation | Additional operational overhead, higher infrastructure costs | Both topologies are fully supported. Choose based on your organizational model and scaling needs. ## Step 2: Provision the database ### Prerequisites - **Supported RDBMS**: PostgreSQL (recommended), MariaDB, MySQL, SQL Server, Oracle, or H2 (development only). - **Versions**: See the [RDBMS version support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md). - **Network and credentials**: Ensure reachable database and user with DDL permissions (CREATE TABLE, ALTER TABLE) for schema initialization. - **SSL/TLS**: Optional but recommended. See [Web Modeler SSL configuration](/self-managed/components/hub/configuration/database.md#configuring-ssl-for-the-database-connection) for guidance on JDBC URL parameters. ### Create database and user Choose your topology (shared or separate) and database vendor: ```sql -- Shared topology: single database for both OC and Web Modeler CREATE DATABASE camunda ENCODING 'UTF8'; CREATE USER camunda WITH PASSWORD 'your-secure-password'; GRANT CONNECT ON DATABASE camunda TO camunda; GRANT USAGE ON SCHEMA public TO camunda; GRANT CREATE ON DATABASE camunda TO camunda; -- Separate topology: independent instances -- CREATE DATABASE camunda_oc ENCODING 'UTF8'; -- CREATE USER camunda_oc WITH PASSWORD 'oc-password'; -- GRANT CONNECT ON DATABASE camunda_oc TO camunda_oc; -- GRANT USAGE ON SCHEMA public TO camunda_oc; -- GRANT CREATE ON DATABASE camunda_oc TO camunda_oc; ``` ```sql -- Shared topology CREATE DATABASE camunda CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER camunda@'%' IDENTIFIED BY 'your-secure-password'; GRANT ALL PRIVILEGES ON camunda.* TO camunda@'%'; FLUSH PRIVILEGES; -- Separate topology: repeat with different database and user names ``` For SQL Server and Oracle, follow your database vendor's user provisioning guidelines. The user must have CREATE TABLE and ALTER permissions. Consult your DBA and the detailed setup guides linked below. ## Step 3: Configure connections and authentication Configuration is component-specific but follows consistent principles across OC and Web Modeler. ### Orchestration Cluster connection In your `values.yaml`: ```yaml orchestration: data: secondaryStorage: type: rdbms rdbms: url: jdbc:postgresql://postgres.example.com:5432/camunda_oc username: camunda_oc secret: existingSecret: rdbms-credentials existingSecretKey: password ``` For full Helm reference, see [RDBMS configuration in Helm](/self-managed/deployment/helm/configure/database/rdbms.md). Set environment variables before starting the Orchestration Cluster: ```bash export CAMUNDA_DATA_SECONDARY_STORAGE_TYPE=rdbms export CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_URL="jdbc:postgresql://postgres.example.com:5432/camunda_oc" export CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_USERNAME="camunda_oc" export CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_PASSWORD="your-secure-password" ``` For full manual setup, see [RDBMS configuration for manual installations](/self-managed/deployment/manual/rdbms/configuration.md). ### Web Modeler connection Web Modeler uses Spring Boot datasource configuration (separate from Orchestration Cluster). In your `values.yaml`: ```yaml webModeler: restapi: externalDatabase: enabled: true url: jdbc:postgresql://postgres.example.com:5432/camunda_wm username: camunda_wm secret: inlineSecret: your-secure-password # Or use existingSecret for production ``` For full Web Modeler reference, see [Web Modeler database configuration](/self-managed/components/hub/configuration/database.md). Set environment variables before starting Web Modeler: ```bash export SPRING_DATASOURCE_URL="jdbc:postgresql://postgres.example.com:5432/camunda_wm" export SPRING_DATASOURCE_USERNAME="camunda_wm" export SPRING_DATASOURCE_PASSWORD="your-secure-password" ``` ### Secrets management For production, use a secrets store (Kubernetes Secrets, Vault, AWS Secrets Manager) instead of inline passwords. **Kubernetes Secrets example**: ```bash kubectl create secret generic rdbms-credentials \ --from-literal=user=camunda_oc \ --from-literal=password=your-very-secure-password ``` Then reference it in `values.yaml`: ```yaml orchestration: data: secondaryStorage: rdbms: secret: existingSecret: rdbms-credentials existingSecretKey: password ``` ### Aurora IAM authentication If using Amazon Aurora PostgreSQL, configure IAM database authentication for enhanced security (no stored passwords). **Orchestration Cluster (Helm)**: ```yaml orchestration: data: secondaryStorage: type: rdbms rdbms: url: "jdbc:postgresql://aurora-cluster.123456789012.us-east-1.rds.amazonaws.com:5432/camunda?sslmode=require" username: db_user # IAM database user # No password needed; IAM token generated at runtime # Requires IAM role attached to pod (IRSA or Karpenter) ``` **Web Modeler (Helm)**: ```yaml webModeler: restapi: externalDatabase: enabled: true url: "jdbc:aws-wrapper:postgresql://aurora-cluster.123456789012.us-east-1.rds.amazonaws.com:5432/camunda?wrapperPlugins=iam" username: db_user # IAM database user # No password needed; IAM token generated at runtime ``` For detailed Aurora setup, see [Orchestration Cluster RDBMS configuration](/self-managed/concepts/databases/relational-db/configuration.md) and [Web Modeler configuration](/self-managed/components/hub/configuration/database.md). ## Step 4: JDBC driver management Camunda bundles JDBC drivers for PostgreSQL, MariaDB, SQL Server, and H2. **You must provide a user-supplied driver for Oracle and MySQL.** For detailed driver provisioning strategies (init containers, custom images, volume mounts), see: - **Helm**: [JDBC driver management in Helm](/self-managed/deployment/helm/configure/database/rdbms-jdbc-drivers.md) - **Manual**: [Manual installation driver setup](/self-managed/deployment/manual/rdbms/configuration.md#jdbc-driver-management) - **Web Modeler**: [Web Modeler database configuration](/self-managed/components/hub/configuration/database.md) ## Step 5: Schema management **Orchestration Cluster uses Liquibase** → automatically creates and updates schema on startup (configurable via `autoDDL: true/false`). **Web Modeler uses Flyway** → migrations applied automatically on startup; **manual DBA execution not supported**. This is the key difference: Orchestration Cluster schema can be managed manually by a DBA if preferred, while Web Modeler schema is automatic only. For access to SQL/Liquibase scripts or manual DBA procedures, see [Access SQL and Liquibase scripts](/self-managed/deployment/helm/configure/database/access-sql-liquibase-scripts.md). ## Step 6: Validation ### Orchestration Cluster checklist 1. Confirm the RDBMS exporter is enabled. 2. Check logs for Liquibase initialization: `[INFO] io.camunda.application.commons.rdbms.MyBatisConfiguration - Initializing Liquibase for RDBMS` 3. Verify database schema was initialized. See [access SQL and Liquibase scripts](/self-managed/deployment/helm/configure/database/access-sql-liquibase-scripts.md) for the complete table list for your database platform. 4. Run [RDBMS validation tests](/self-managed/deployment/helm/configure/database/validate-rdbms.md). ### Web Modeler checklist 1. Confirm the database connection is configured. 2. Check logs for Flyway schema initialization. 3. Verify database contains tables (Flyway creates these automatically on startup). 4. Test the health endpoint: `/health`. ### Common issues | Issue | Resolution | | ------------------ | ------------------------------------------------------------------------------------------------------------------------ | | Schema not created | Grant `CREATE TABLE`, `ALTER TABLE` permissions to the database user. | | Connection timeout | Check firewall rules, security groups, and VPC peering. Verify network connectivity. | | TLS/SSL error | Verify database certificate and adjust JDBC SSL mode parameters. See component-specific configuration pages for details. | | Driver not found | Load user-supplied drivers via init container, custom image, or volume mount. | For detailed troubleshooting, see: - [RDBMS troubleshooting](/self-managed/deployment/helm/configure/database/rdbms-troubleshooting.md) - [Web Modeler database troubleshooting](/self-managed/components/hub/troubleshooting/troubleshoot-database-connection.md) ## Step 7: Backup and restore Both components require RDBMS backups: - **Orchestration Cluster**: Zeebe exports data to RDBMS; backups are DBA responsibility. - **Web Modeler**: All data stored in RDBMS; backups are DBA responsibility. Use vendor-native tools: PostgreSQL (`pg_dump`), MariaDB/MySQL (`mysqldump`), SQL Server (native backup), Oracle (RMAN). Test restore procedures in non-production environments regularly. ## Related guides - [Secondary storage overview](/self-managed/concepts/secondary-storage/index.md) - [RDBMS example deployment](/self-managed/deployment/helm/install/helm-with-rdbms.md) - [Operations and maintenance](/self-managed/deployment/manual/rdbms/operations.md) --- ## RDBMS version support policy Confirm which relational databases and versions Camunda supports for **Camunda 8 Self-Managed**, including lifecycle rules, managed PostgreSQL guidance, JDBC driver responsibilities, and component compatibility. ## Scope and applicability This policy applies to: - **Camunda 8 Self-Managed** deployments. It covers relational databases used for: - [Secondary storage for the Orchestration Cluster](/self-managed/concepts/secondary-storage/index.md) - [Web Modeler](/self-managed/components/hub/configuration/database.md) :::info Camunda follows an **"all LTS versions"** rule for database support. All listed database versions are official **LTS releases still supported by the vendor**. Camunda tests against both the **oldest** and **newest** supported version of each database in CI. ::: ## Supported RDBMS The following relational databases are officially supported when used as an RDBMS backend (including as secondary storage where applicable): | Database | Supported versions | | ------------------------ | ------------------ | | PostgreSQL | 15, 16, 17, 18 | | Amazon Aurora PostgreSQL | 15, 16, 17 | | MariaDB | 10.11, 11.4, 11.8 | | MySQL | 8.4 | | Microsoft SQL Server | 2022, 2025 | | Oracle | 19c, 26ai | | H2 | 2.4 | :::info Changes to supported versions are announced in the [release notes](/reference/announcements-release-notes/8100/8100-announcements.md). ::: ### Recommended database versions For **new deployments**, standardize on **one of the latest two supported versions** of a given database whenever possible. Older supported versions remain valid for existing deployments but are not recommended for new deployments. ## Managed PostgreSQL services Camunda supports PostgreSQL as a database engine, not individual managed service implementations. Managed PostgreSQL services are supported when: - They are fully compatible with the PostgreSQL versions listed in this policy, and - The service provider guarantees compatibility and support for those versions. Provider-specific operational behavior and service characteristics remain the responsibility of the service provider. Amazon Aurora PostgreSQL is listed separately because it is explicitly tested by Camunda, while other managed PostgreSQL services are supported based on compatibility guarantees provided by the service provider. ## New version support New database versions are added based on the following criteria: - **Vendor release:** The version must be officially released by the database vendor. - **Availability window:** The version must be available at least three months before the next Camunda minor release. - **Validation:** The version must be tested and validated across relevant Camunda components. If the availability window is missed, support is deferred to a subsequent minor release. ## Version deprecation and removal Versions are deprecated and removed using the following rules: - **Vendor end of support (EoS):** Versions are deprecated when vendor support has ended or is scheduled to end within six months. - **Removal:** Deprecated versions may be removed in a subsequent Camunda minor release once vendor support has ended. - **Advance notice:** Deprecations and removals are communicated in advance via release notes. - **Exceptions:** Support may be extended in exceptional cases, but any extension is explicitly documented and time-bound. Camunda may remove support for a version if it contains known issues that prevent reliable operation. ## Database-specific support notes ### PostgreSQL Camunda supports multiple active PostgreSQL major versions concurrently. ### MariaDB Camunda supports **MariaDB LTS releases only**. ### MySQL Camunda supports **MySQL LTS releases only**. ### Microsoft SQL Server Camunda supports SQL Server versions that are in mainstream or extended vendor support. ### Oracle Database Camunda supports **Oracle LTS releases**. ### H2 H2 is supported for development, testing, and evaluation only. Production use is not recommended. For Camunda Orchestration Cluster secondary storage, H2 is a single-broker option only: - Multi-broker clusters with H2 are not a valid architecture. - H2 does not provide a shared database across brokers. - In-memory H2 is ephemeral and does not survive restarts. - File-based H2 persists on local disk and is suitable for local/dev usage only. ## Supported JDBC driver versions Camunda bundles JDBC drivers for databases where redistribution is permitted and expects you to provide drivers where licensing or distribution constraints apply (for example, Oracle). Bundled driver versions are not pinned as a formal support guarantee. Each bundled driver is tested in CI against every supported version of the corresponding database listed above, and the exact version may change between Camunda patch releases as dependencies are updated. To inspect the precise driver version bundled in a given Camunda release, see the `lib/` directory of the distribution archive. ### Bundled drivers The following JDBC drivers and wrappers are included in the Camunda application images. Each is known to be compatible with every supported version of the corresponding database listed above: | Database / platform | Driver artifact | Notes | | :----------------------- | :----------------------------------------------- | :------------------------------------------------------------- | | PostgreSQL | `org.postgresql:postgresql` | Bundled in Camunda images. | | MariaDB | `org.mariadb.jdbc:mariadb-java-client` | Bundled in Camunda images. | | Microsoft SQL Server | `com.microsoft.sqlserver:mssql-jdbc` | Bundled in Camunda images (JRE 11). | | H2 | `com.h2database:h2` | Bundled in Camunda images. | | Amazon Aurora (AWS JDBC) | `software.amazon.jdbc:aws-advanced-jdbc-wrapper` | JDBC wrapper for AWS Aurora; requires a supported base driver. | ### User-supplied drivers The following databases require you to provide a compatible JDBC driver at runtime: | Database | Driver artifact | Tested version | Notes | | :------- | :-------------------------------- | :------------- | :---------------------------------------------------------------------- | | Oracle | `com.oracle.database.jdbc:ojdbc*` | 23.7.0.25.01 | Must be provided by you. May be OS/architecture-specific (amd64/arm64). | | MySQL | `com.mysql:mysql-connector-j` | 9.5.0 | Must be provided by you. | :::info Camunda validates driver compatibility in CI by testing against the oldest and newest supported database versions. A single driver version is expected to work across the supported database versions listed on this page. For deployment instructions, see [loading JDBC drivers into pods](/self-managed/deployment/helm/configure/database/rdbms.md#bundled-vs-custom-jdbc-drivers). ::: ## JDBC driver policy - Camunda supports the latest vendor-compatible JDBC driver for each supported RDBMS. - You are responsible for providing JDBC drivers when required (for example, Oracle or MySQL). - Driver versions are not pinned unless a specific version is required for compatibility or stability reasons. ## Component support matrix This table shows RDBMS support status by component (including RDBMS as secondary storage where applicable): | Component | Support status | Notes | | :------------------------ | :----------------- | :------------------------------------------------------------------------------------------------ | | **Orchestration Cluster** | ✅ Fully supported | Supports RDBMS as secondary storage. | | Tasklist UI | ✅ Fully supported | All functionality available. | | Operate UI | ✅ Fully supported | All functionality available. | | Optimize | ❌ Not supported | Out of scope for RDBMS support. | | Web Modeler | ✅ Fully supported | See [Web Modeler database configuration](/self-managed/components/hub/configuration/database.md). | | Identity | ✅ Fully supported | All functionality available. | | Management API (REST API) | ✅ Fully supported | All functionality available. | :::note "Orchestration Cluster" refers to the secondary storage of the Orchestration Cluster. UI products are listed separately because their RDBMS support and maturity can differ by the alpha release. ::: ## Known limitations When using RDBMS (including as secondary storage), be aware of the following limitations: ### ID size limits Most user-defined strings exported to RDBMS-backed secondary storage are limited to **256 characters**. This includes BPMN and DMN IDs and names, job worker types, variable names, resource names, form IDs, and message names or correlation keys. If a DMN rule does not define its own ID, Camunda generates one from the decision ID, decision version, and rule index. In that case, keep the decision ID at **235 characters or fewer** so the generated rule ID stays within the RDBMS limit. Identity objects are also limited to **256 characters**, regardless of which secondary storage backend the Orchestration Cluster uses. For the full backend comparison, including the Elasticsearch/OpenSearch limit of 32,768 characters and details on how length is counted, see [string length limits for user-defined values](/self-managed/concepts/databases/overview.md#string-length-limits-for-user-defined-values). ### Variable comparison limits When retrieving variables through the REST API, the following comparison operators only apply to the first **4000 characters** (or **8191 characters**, depending on the database vendor) of large String or JSON variables: - `equals` - `notEquals` - `in` - `notIn` The `LIKE` comparison operator is not affected by this limitation. ### Collation and sorting behavior Because collation behavior varies across database vendors, results sorted by string fields may differ between systems. Ensure your application accounts for potential sorting variations when migrating between different RDBMS vendors. ## Installation and deployment guides For hands-on instructions to deploy Camunda with RDBMS, start with: - [End-to-end RDBMS setup guide](/self-managed/concepts/databases/relational-db/rdbms-setup-guide.md) - Unified guide for provisioning, topology decisions, driver management, and backup strategies. Then choose your deployment pattern: - [Secondary storage architecture](/self-managed/reference-architecture/reference-architecture.md#secondary-storage-architecture) - Canonical backend trade-offs and production architecture guidance. - [Production architecture with RDBMS](/self-managed/deployment/manual/rdbms/rdbms-production-architecture.md) - Manual deployment topology guidance for RDBMS. - [Manual installation with RDBMS](/self-managed/deployment/manual/rdbms/index.md) - Entry point for manual installation, configuration, and operations. - [RDBMS example deployment for Helm](/self-managed/deployment/helm/install/helm-with-rdbms.md) - Kubernetes/Helm-based example walkthrough. --- ## Document handling configuration in Camunda 8 Run :::note [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md) can be used for local development only, and none of the storage options below are suitable for production. ::: If no storage configuration is provided, the default document storage is **in-memory**. It means that documents will be lost when the application is stopped. To change this to a different storage method, use the environment variables in the sections below. No additional configuration is required for the **in-memory** storage. To set what storage should be used, accepted values for `DOCUMENT_DEFAULT_STORE_ID` are `aws`, `inmemory`, `gcp` (for Google Cloud Platform), `azure` (for Azure Blob Storage), and `local` (for local storage). ## Storage options By using **external cloud file bucket storage** with [**AWS S3**](https://aws.amazon.com/s3/), documents can be stored in a secure and scalable way. Buckets are integrated per cluster to ensure proper isolation and environment-specific management. | Credentials variable | Required | Description | | ----------------------- | -------- | ----------------------------------------------------------------------------------------------------- | | `AWS_ACCESS_KEY_ID` | Yes | Access key ID used to interact with AWS S3 buckets. | | `AWS_SECRET_ACCESS_KEY` | Yes | The AWS secret access key associated with the `AWS_ACCESS_KEY_ID`. This will be used to authenticate. | | `AWS_REGION` | Yes | Region where the bucket is. | | Store variable | Required | Description | | -------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `DOCUMENT_STORE_AWS_BUCKET` | Yes | Specifies the name of the AWS S3 bucket where documents are stored. | | `DOCUMENT_STORE_AWS_CLASS` | Yes | io.camunda.document.store.aws.AwsDocumentStoreProvider | | `DOCUMENT_STORE_AWS_BUCKET_PATH` | No | Defines the folder-like path within the S3 bucket where documents are stored. This helps organize files within the bucket. For example, `documents/invoices`. If not provided, the application logic assumes a default value of `""`. | | `DOCUMENT_STORE_AWS_BUCKET_TTL` | No | Represents the time-to-live (TTL) for documents stored in the S3 bucket. This could be used to set an expiration policy, meaning documents will be deleted automatically after a specified duration. If not provided, the application logic ignores this. | **Example:** ``` AWS_ACCESS_KEY_ID=AWSACCESSKEYID AWS_REGION=eu-north-1 AWS_SECRET_ACCESS_KEY=AWSSECRETACCESSKEYGOESHERE DOCUMENT_STORE_AWS_BUCKET=test-bucket DOCUMENT_STORE_AWS_BUCKET_PATH=test/path DOCUMENT_STORE_AWS_BUCKET_TTL=5 DOCUMENT_STORE_AWS_CLASS=io.camunda.document.store.aws.AwsDocumentStoreProvider DOCUMENT_DEFAULT_STORE_ID=aws ``` ## AWS API client permission requirements To ensure seamless integration and functionality of document handling with AWS services, the API client utilized must be configured with the appropriate permissions. The following AWS Identity and Access Management (IAM) permissions are necessary for the execution of operations related to document handling: | Permission | Description | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `s3:DeleteObject` | This permission authorizes the API client to remove objects from the specified S3 bucket. | | `s3:GetObject` | This permission is required to retrieve contents and metadata of objects from Amazon S3. The API client will utilize this permission to download or access the contents of the documents that have been uploaded to the bucket. | | `s3:ListBucket` | This permission allows the application to verify it has access to the specified S3 bucket. Lack of this permission does not prevent the application from starting, but it logs a warning on application start-up. | | `s3:PutObject` | To upload documents to an Amazon S3 bucket, the API client must have this permission. | By using **external cloud file bucket storage** with [**Google Cloud Platform (GCP)**](https://cloud.google.com/storage), documents can be stored in a secure and scalable way. Buckets are integrated per cluster to ensure proper isolation and environment-specific management. | Credentials variable | Required | Description | | -------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------- | | `GOOGLE_APPLICATION_CREDENTIALS` | Yes | Specifies the file path to a JSON key file that contains authentication credentials for a Google Cloud service account. | | Store variable | Required | Description | | --------------------------- | -------- | ------------------------------------------------------------------------------- | | `DOCUMENT_STORE_GCP_BUCKET` | Yes | Defines the name of the Google Cloud Storage bucket where documents are stored. | | `DOCUMENT_STORE_GCP_CLASS` | Yes | io.camunda.document.store.gcp.GcpDocumentStoreProvider | **Example:** ``` DOCUMENT_STORE_GCP_CLASS=io.camunda.document.store.gcp.GcpDocumentStoreProvider DOCUMENT_STORE_GCP_BUCKET=test-bucket DOCUMENT_DEFAULT_STORE_ID=gcp GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json ``` ## GCP API client permission requirements To ensure seamless integration and functionality of document handling with GCP services, the API client utilized must be configured with the appropriate permissions. The following permissions are necessary for the execution of operations related to document handling: | Permission | Description | | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `storage.buckets.get` | This permission allows the application to verify it has access to the specified bucket. Lack of this permission does not prevent the application from starting, but it logs a warning on application start-up. | | `storage.objects.get` | This permission allows the API client to retrieve objects from Google Cloud Storage. It is vital for downloading or accessing the contents of stored objects. | | `storage.objects.create` | With this permission, the API client can upload new objects to a bucket. It is essential for adding new documents to the storage. | | `storage.objects.update` | This permission enables the API client to update contents and metadata of existing objects within a bucket. | | `storage.objects.delete` | This permission grants the API client the ability to delete objects from a bucket. | | `iam.serviceAccounts.signBlob` | This permission allows the service account to sign data as part of the process to create secure, signed URLs for accessing uploaded documents. | By using **external cloud file bucket storage** with [**Azure Blob Storage**](https://azure.microsoft.com/en-us/products/storage/blobs), documents can be stored in a secure and scalable way. Azure Blob Storage supports two authentication methods: connection string and DefaultAzureCredential (Managed Identity). #### Prerequisites - An Azure Storage account with a Blob container. - For connection string authentication: The connection string from the Azure portal (**Settings > Access keys**). - For Managed Identity/DefaultAzureCredential authentication: The `Storage Blob Data Contributor` RBAC role assigned on the storage account. | Store variable | Required | Description | | ---------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `DOCUMENT_STORE_AZURE_CLASS` | Yes | `io.camunda.document.store.azure.AzureBlobDocumentStoreProvider` | | `DOCUMENT_STORE_AZURE_CONTAINER` | Yes | Name of the Azure Blob Storage container. | | `DOCUMENT_STORE_AZURE_CONNECTION_STRING` | Conditional | Azure Storage connection string. Required unless using DefaultAzureCredential. | | `DOCUMENT_STORE_AZURE_ENDPOINT` | Conditional | Storage account endpoint (e.g. `https://myaccount.blob.core.windows.net`). Required when using DefaultAzureCredential/Managed Identity. | | `DOCUMENT_STORE_AZURE_CONTAINER_PATH` | No | Optional path/prefix within the container. | **Example (connection string):** ``` DOCUMENT_STORE_AZURE_CLASS=io.camunda.document.store.azure.AzureBlobDocumentStoreProvider DOCUMENT_STORE_AZURE_CONTAINER=my-container DOCUMENT_STORE_AZURE_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=myaccount;AccountKey=...;EndpointSuffix=core.windows.net DOCUMENT_DEFAULT_STORE_ID=azure ``` **Example (DefaultAzureCredential/Managed Identity):** ``` DOCUMENT_STORE_AZURE_CLASS=io.camunda.document.store.azure.AzureBlobDocumentStoreProvider DOCUMENT_STORE_AZURE_CONTAINER=my-container DOCUMENT_STORE_AZURE_ENDPOINT=https://myaccount.blob.core.windows.net DOCUMENT_DEFAULT_STORE_ID=azure ``` ## Azure API client permission requirements To ensure seamless integration and functionality of document handling with Azure Blob Storage, the identity used must be assigned the `Storage Blob Data Contributor` RBAC role on the storage account. **In-memory** storage can be used to store documents during the application's runtime. When the application is stopped, documents are lost. In-memory storage is not suitable for production use, as pods and memory are not shared across components. Files stored in memory are not persisted and will be lost on application restart. If no configuration is provided for at least one storage type, and no `DOCUMENT_DEFAULT_STORE_ID` is set, in-memory is used as the default storage type. If the configuration for another storage type has been provided (`DOCUMENT_STORE_AWS_BUCKET`, `DOCUMENT_STORE_AWS_BUCKET_PATH`, etc.), in-memory storage must be set explicitly to be used. To use the in-memory store when an alternate configuration has been provided, take the following steps: 1. Set `DOCUMENT_STORE_INMEMORY_CLASS=io.camunda.document.store.inmemory.InMemoryDocumentStoreProvider`. 2. Set `DOCUMENT_DEFAULT_STORE_ID=inmemory`. | Store variable | Required | Description | | ------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | `DOCUMENT_STORE_INMEMORY_CLASS` | Yes | The class for instantiating the in-memory store. This must always be `io.camunda.document.store.inmemory.InMemoryDocumentStoreProvider`. | **Example:** ``` DOCUMENT_STORE_INMEMORY_CLASS=io.camunda.document.store.inmemory.InMemoryDocumentStoreProvider DOCUMENT_DEFAULT_STORE_ID=inmemory ``` **Local storage** can be configured for a cluster to store documents in a local folder. It can be used only for local development with [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md). Local storage is not suitable for production use, as pods and file paths are not shared across components. This prevents components like Tasklist and Zeebe from accessing the same data. Files are stored locally, and their retention must be managed manually. | Store variable | Required | Description | | ---------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | `DOCUMENT_STORE_LOCAL_CLASS` | Yes | The class for instantiating the local store. This must always be `io.camunda.document.store.localstorage.LocalStorageDocumentStoreProvider` | | `DOCUMENT_STORE_LOCAL_PATH` | Yes | The path to the directory which will host the uploaded files. | **Example:** ``` DOCUMENT_STORE_LOCAL_CLASS=io.camunda.document.store.localstorage.LocalStorageDocumentStoreProvider DOCUMENT_STORE_LOCAL_PATH=/usr/local/camunda DOCUMENT_DEFAULT_STORE_ID=local ``` --- ## Document handling configuration in Docker Compose :::note None of the storage options below with Docker Compose are suitable for production. ::: If no storage configuration is provided, the default document storage is **in-memory**. It means that documents will be lost when the application is stopped. To change this to a different storage method, use the environment variables in the section below for every component using it (Zeebe and Tasklist). No additional configuration is required for the **in-memory** storage. To set what storage should be used, accepted values for `DOCUMENT_DEFAULT_STORE_ID` are `aws`, `inmemory`, `gcp` (for Google Cloud Platform), `azure` (for Azure Blob Storage), and `local` (for local storage). When using [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md), Tasklist and Zeebe run in separate containers and do not share memory or volumes, which introduces certain limitations. While the document handling feature will still work, the environment variable below must be set for all components that use it (Zeebe and Tasklist). In this topology, using in-memory or local storage means components cannot access the same data, so documents uploaded by Zeebe may not be visible to Tasklist. This limitation does not apply when using cloud storage options like AWS or GCP, where documents are always stored in a shared, centralized location. By using **external cloud file bucket storage** with [**AWS S3**](https://aws.amazon.com/s3/), documents can be stored in a secure, and scalable way. Buckets are integrated per cluster to ensure proper isolation and environment-specific management. | Credentials variable | Required | Description | | ----------------------- | -------- | ----------------------------------------------------------------------------------------------------- | | `AWS_ACCESS_KEY_ID` | Yes | Access key ID used to interact with AWS S3 buckets. | | `AWS_SECRET_ACCESS_KEY` | Yes | The AWS secret access key associated with the `AWS_ACCESS_KEY_ID`. This will be used to authenticate. | | `AWS_REGION` | Yes | Region where the bucket is. | | Store variable | Required | Description | | -------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `DOCUMENT_STORE_AWS_BUCKET` | Yes | Specifies the name of the AWS S3 bucket where documents are stored. | | `DOCUMENT_STORE_AWS_CLASS` | Yes | io.camunda.document.store.aws.AwsDocumentStoreProvider | | `DOCUMENT_STORE_AWS_BUCKET_PATH` | No | Defines the folder-like path within the S3 bucket where documents are stored. This helps organize files within the bucket. For example, `documents/invoices`. If not provided, the application logic assumes a default value of `""`. | | `DOCUMENT_STORE_AWS_BUCKET_TTL` | No | Represents the time-to-live (TTL) for documents stored in the S3 bucket. This could be used to set an expiration policy, meaning documents will be deleted automatically after a specified duration. If not provided, the application logic ignores this. | **Example:** ``` AWS_ACCESS_KEY_ID=AWSACCESSKEYID AWS_REGION=eu-north-1 AWS_SECRET_ACCESS_KEY=AWSSECRETACCESSKEYGOESHERE DOCUMENT_STORE_AWS_BUCKET=test-bucket DOCUMENT_STORE_AWS_BUCKET_PATH=test/path DOCUMENT_STORE_AWS_BUCKET_TTL=5 DOCUMENT_STORE_AWS_CLASS=io.camunda.document.store.aws.AwsDocumentStoreProvider DOCUMENT_DEFAULT_STORE_ID=aws ``` ## AWS API client permission requirements To ensure seamless integration and functionality of document handling with AWS services, the API client utilized must be configured with the appropriate permissions. The following AWS Identity and Access Management (IAM) permissions are necessary for the execution of operations related to document handling: | Permission | Description | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `s3:DeleteObject` | This permission authorizes the API client to remove objects from the specified S3 bucket. | | `s3:GetObject` | This permission is required to retrieve contents and metadata of objects from Amazon S3. The API client will utilize this permission to download or access the contents of the documents that have been uploaded to the bucket. | | `s3:ListBucket` | This permission allows the application to verify it has access to the specified S3 bucket. Lack of this permission does not prevent the application from starting, but it logs a warning on application start-up. | | `s3:PutObject` | To upload documents to an Amazon S3 bucket, the API client must have this permission. | By using **external cloud file bucket storage** with [**Google Cloud Platform (GCP)**](https://cloud.google.com/storage), documents can be stored in a secure, and scalable way. Buckets are integrated per cluster to ensure proper isolation and environment-specific management. | Credentials variable | Required | Description | | -------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------- | | `GOOGLE_APPLICATION_CREDENTIALS` | Yes | Specifies the file path to a JSON key file that contains authentication credentials for a Google Cloud service account. | | Store variable | Required | Description | | --------------------------- | -------- | ------------------------------------------------------------------------------- | | `DOCUMENT_STORE_GCP_BUCKET` | Yes | Defines the name of the Google Cloud Storage bucket where documents are stored. | | `DOCUMENT_STORE_GCP_CLASS` | Yes | io.camunda.document.store.gcp.GcpDocumentStoreProvider | **Example:** ``` DOCUMENT_STORE_GCP_CLASS=io.camunda.document.store.gcp.GcpDocumentStoreProvider DOCUMENT_STORE_GCP_BUCKET=test-bucket DOCUMENT_DEFAULT_STORE_ID=gcp GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json ``` ## GCP API client permission requirements To ensure seamless integration and functionality of document handling with GCP services, the API client utilized must be configured with the appropriate permissions. The following permissions are necessary for the execution of operations related to document handling: | Permission | Description | | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `storage.buckets.get` | This permission allows the application to verify it has access to the specified bucket. Lack of this permission does not prevent the application from starting, but it logs a warning on application start-up. | | `storage.objects.get` | This permission allows the API client to retrieve objects from Google Cloud Storage. It is vital for downloading or accessing the contents of stored objects. | | `storage.objects.create` | With this permission, the API client can upload new objects to a bucket. It is essential for adding new documents to the storage. | | `storage.objects.update` | This permission enables the API client to update contents and metadata of existing objects within a bucket. | | `storage.objects.delete` | This permission grants the API client the ability to delete objects from a bucket. | | `iam.serviceAccounts.signBlob` | This permission allows the service account to sign data as part of the process to create secure, signed URLs for accessing uploaded documents. | By using **external cloud file bucket storage** with [**Azure Blob Storage**](https://azure.microsoft.com/en-us/products/storage/blobs), documents can be stored in a secure and scalable way. Azure Blob Storage supports two authentication methods: connection string and DefaultAzureCredential (Managed Identity). #### Prerequisites - An Azure Storage account with a Blob container. - For connection string authentication: The connection string from the Azure portal (**Settings > Access keys**). - For Managed Identity/DefaultAzureCredential authentication: The `Storage Blob Data Contributor` RBAC role assigned on the storage account. | Store variable | Required | Description | | ---------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `DOCUMENT_STORE_AZURE_CLASS` | Yes | `io.camunda.document.store.azure.AzureBlobDocumentStoreProvider` | | `DOCUMENT_STORE_AZURE_CONTAINER` | Yes | Name of the Azure Blob Storage container. | | `DOCUMENT_STORE_AZURE_CONNECTION_STRING` | Conditional | Azure Storage connection string. Required unless using DefaultAzureCredential. | | `DOCUMENT_STORE_AZURE_ENDPOINT` | Conditional | Storage account endpoint (e.g. `https://myaccount.blob.core.windows.net`). Required when using DefaultAzureCredential/Managed Identity. | | `DOCUMENT_STORE_AZURE_CONTAINER_PATH` | No | Optional path/prefix within the container. | **Example (connection string):** ``` DOCUMENT_STORE_AZURE_CLASS=io.camunda.document.store.azure.AzureBlobDocumentStoreProvider DOCUMENT_STORE_AZURE_CONTAINER=my-container DOCUMENT_STORE_AZURE_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=myaccount;AccountKey=...;EndpointSuffix=core.windows.net DOCUMENT_DEFAULT_STORE_ID=azure ``` **Example (DefaultAzureCredential/Managed Identity):** ``` DOCUMENT_STORE_AZURE_CLASS=io.camunda.document.store.azure.AzureBlobDocumentStoreProvider DOCUMENT_STORE_AZURE_CONTAINER=my-container DOCUMENT_STORE_AZURE_ENDPOINT=https://myaccount.blob.core.windows.net DOCUMENT_DEFAULT_STORE_ID=azure ``` ## Azure API client permission requirements To ensure seamless integration and functionality of document handling with Azure Blob Storage, the identity used must be assigned the `Storage Blob Data Contributor` RBAC role on the storage account. This role grants the following required permissions: | Permission | Description | | ------------------------------------------------------------------------ | -------------------------------- | | `Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read` | Read blob content and metadata. | | `Microsoft.Storage/storageAccounts/blobServices/containers/blobs/write` | Create or update blobs. | | `Microsoft.Storage/storageAccounts/blobServices/containers/blobs/delete` | Delete blobs from the container. | **In-memory** storage can be used to store documents during the application's runtime. When the application is stopped, documents are lost. In-memory storage is not suitable for production use, as pods and memory are not shared across components. Files stored in memory are not persisted and will be lost on application restart. If no configuration is provided for at least one storage type, and no `DOCUMENT_DEFAULT_STORE_ID` is set, in-memory is used as the default storage type. If the configuration for another storage type has been provided (`DOCUMENT_STORE_AWS_BUCKET`, `DOCUMENT_STORE_AWS_BUCKET_PATH`, etc.), in-memory storage must be set explicitly to be used. To use the in-memory store when an alternate configuration has been provided, take the following steps: 1. Set `DOCUMENT_STORE_INMEMORY_CLASS=io.camunda.document.store.inmemory.InMemoryDocumentStoreProvider`. 2. Set `DOCUMENT_DEFAULT_STORE_ID=inmemory`. | Store variable | Required | Description | | ------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | `DOCUMENT_STORE_INMEMORY_CLASS` | Yes | The class for instantiating the in-memory store. This must always be `io.camunda.document.store.inmemory.InMemoryDocumentStoreProvider`. | **Example:** ``` DOCUMENT_STORE_INMEMORY_CLASS=io.camunda.document.store.inmemory.InMemoryDocumentStoreProvider DOCUMENT_DEFAULT_STORE_ID=inmemory ``` | Store variable | Required | Description | | ---------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `DOCUMENT_STORE_LOCAL_CLASS` | Yes | The class for instantiating the local store. This must always be `io.camunda.document.store.localstorage.LocalStorageDocumentStoreProvider` | | `DOCUMENT_STORE_LOCAL_PATH` | Yes | The path to the directory which will host the uploaded files. **Use `/usr/local/camunda/documents` as it is pre-created with the right permissions for the process user.** | **Example:** ``` DOCUMENT_STORE_LOCAL_CLASS=io.camunda.document.store.localstorage.LocalStorageDocumentStoreProvider DOCUMENT_STORE_LOCAL_PATH=/usr/local/camunda/documents DOCUMENT_DEFAULT_STORE_ID=local ``` --- ## Document handling configuration in Helm Helm offers external cloud file bucket storage options (recommended for production use when deploying with Helm) and in-memory storage (not suitable for production use): - By using **external cloud file bucket storage options**, documents can be stored in a secure, and scalable way. Buckets are integrated per cluster to ensure proper isolation and environment-specific management. The following file bucket storage options are supported: - [**Google Cloud Platform (GCP)**](https://cloud.google.com/storage) - [**AWS S3**](https://aws.amazon.com/s3/) - [**Azure Blob Storage**](https://azure.microsoft.com/en-us/products/storage/blobs) - **In-memory** storage can be used to store documents during the application's runtime. When the application is stopped, documents are lost. In-memory storage is not suitable for production use, as pods and memory are not shared across components. Files stored in memory are not persisted and will be lost on application restart. If no storage configuration is provided, the default document storage is in-memory. However, in-memory storage is not supported in production environments, so a configuration update is required. This is because memory is not shared between pods or components, preventing services like Tasklist and Zeebe from accessing the same data. Additionally, all uploaded files are lost when the application restarts. To change the storage to **Google Cloud Platform**, **AWS S3**, or **Azure Blob Storage**, update the `values.yaml` file with the storage configuration parameters. Below is an example of storage configuration. While this example mixes GCP, AWS, and in-memory, this example represents part of the [default Helm chart values](https://github.com/camunda/camunda-platform-helm/blob/main/charts/camunda-platform-8.7/values.yaml). This example demonstrates the current default values and what they would need to change to enable the storage type of their preference. :::note Azure Blob Storage configuration differs from AWS and GCP. Only the connection string secret is managed in `values.yaml` under `global.documentStore.type.azure`. All other Azure configuration (container name, class, endpoint, etc.) must be provided via [`extraConfiguration`](/self-managed/deployment/helm/configure/application-configs.md). See the [Azure Blob Storage configuration](#azure-blob-storage-configuration) section below for details. ::: ``` # Global configuration for variables which can be accessed by all sub charts global: ## Document Store Configuration documentStore: activeStoreId: "inmemory" type: aws: ## @param global.documentStore.type.aws.enabled Enable AWS document store configuration. enabled: false ## @param global.documentStore.type.aws.storeId Custom prefix for AWS. Default will generate env vars containing 'storeId' such as DOCUMENT_STORE_AWS_CLASS. storeId: "AWS" ## @param global.documentStore.type.aws.region AWS region for the S3 bucket. (example: us-east-1) region: "" ## @param global.documentStore.type.aws.bucket Name of the AWS S3 bucket. bucket: "your-aws-bucket" ## @param global.documentStore.type.aws.bucketPath [string, nullable] (Optional) Path/prefix within the S3 bucket. bucketPath: "" ## @param global.documentStore.type.aws.bucketTtl [int, nullable] (Optional) Time-to-live for documents in the S3 bucket (number in days). bucketTtl: 0 ## @param global.documentStore.type.aws.class Fully qualified class name for the AWS document store provider. class: "io.camunda.document.store.aws.AwsDocumentStoreProvider" ## @param global.documentStore.type.aws.existingSecret Reference to an existing Kubernetes secret containing AWS credentials. existingSecret: "aws-credentials" ## @param global.documentStore.type.aws.accessKeyIdKey Key within the AWS credentials secret for AWS_ACCESS_KEY_ID. accessKeyIdKey: "awsAccessKeyId" ## @param global.documentStore.type.aws.secretAccessKeyKey Key within the AWS credentials secret for AWS_SECRET_ACCESS_KEY. secretAccessKeyKey: "awsSecretAccessKey" gcp: ## @param global.documentStore.type.gcp.enabled Enable GCP document store configuration. enabled: false ## @param global.documentStore.type.gcp.storeId Custom prefix for GCP. Default will generate env vars containing 'storeId' such as DOCUMENT_STORE_GCP_CLASS. storeId: "GCP" ## @param global.documentStore.type.gcp.bucket Name of the GCP bucket. bucket: "your-gcp-bucket" ## @param global.documentStore.type.gcp.class Fully qualified class name for the GCP document store provider. class: "io.camunda.document.store.gcp.GcpDocumentStoreProvider" ## @param global.documentStore.type.gcp.existingSecret Reference to an existing Kubernetes secret containing GCP credentials. existingSecret: "gcp-credentials" ## @param global.documentStore.type.gcp.credentialsKey Key in the GCP credentials secret that contains the service-account JSON. credentialsKey: "service-account.json" ## @param global.documentStore.type.gcp.mountPath Mount path for the GCP credentials secret. mountPath: "/var/secrets/gcp" ## @param global.documentStore.type.gcp.fileName The file name for the GCP credentials JSON. fileName: "service-account.json" inmemory: ## @param global.documentStore.type.inmemory.enabled Enable in-memory document store configuration. enabled: true ## @param global.documentStore.type.inmemory.storeId Custom prefix for in-memory. Default will generate env vars containing 'storeId' such as DOCUMENT_STORE_INMEMORY_CLASS. storeId: "INMEMORY" ## @param global.documentStore.type.inmemory.class Fully qualified class name for the in-memory document store provider. class: "io.camunda.document.store.inmemory.InMemoryDocumentStoreProvider" ``` ## Azure Blob Storage configuration Azure Blob Storage uses a different configuration pattern than AWS and GCP. Only the connection string secret is managed via `values.yaml` under `global.documentStore.type.azure`. All other configuration (container name, class, endpoint, etc.) must be provided by the user via `orchestration.extraConfiguration` and `connectors.extraConfiguration`. This follows the same [`extraConfiguration` pattern](/self-managed/deployment/helm/configure/application-configs.md) used by other application-level settings in the 8.9+ chart. ### Prerequisites - An Azure Storage account with a Blob container. - For connection string authentication: The connection string from the Azure portal (**Settings > Access keys**). - For Managed Identity/DefaultAzureCredential authentication: The `Storage Blob Data Contributor` RBAC role assigned on the storage account. ### Authentication options Azure Blob Storage supports two authentication methods: 1. **Connection string** — simplest setup. The connection string is injected as a secret via `global.documentStore.type.azure.connectionString.secret`. 2. **DefaultAzureCredential** (recommended for AKS): Uses Workload Identity or Managed Identity. Set the `endpoint` in `extraConfiguration` instead of providing a connection string. Requires the `Storage Blob Data Contributor` RBAC role on the storage account. ### Connection string authentication This example uses a connection string stored in a Kubernetes secret. ```yaml global: documentStore: activeStoreId: "azure" type: azure: connectionString: secret: existingSecret: "azure-storage-credentials" existingSecretKey: "connection-string" orchestration: extraConfiguration: - file: azure-documentstore.yaml content: | camunda: document: store: azure: class: io.camunda.document.store.azure.AzureBlobDocumentStoreProvider container: my-container connectors: extraConfiguration: - file: azure-documentstore.yaml content: | camunda: document: store: azure: class: io.camunda.document.store.azure.AzureBlobDocumentStoreProvider container: my-container ``` ### Managed Identity/DefaultAzureCredential When using AKS Workload Identity or Managed Identity, omit the connection string secret and set the `endpoint` instead: ```yaml global: documentStore: activeStoreId: "azure" # No connectionString secret needed — DefaultAzureCredential handles auth orchestration: extraConfiguration: - file: azure-documentstore.yaml content: | camunda: document: store: azure: class: io.camunda.document.store.azure.AzureBlobDocumentStoreProvider container: my-container endpoint: https://myaccount.blob.core.windows.net connectors: extraConfiguration: - file: azure-documentstore.yaml content: | camunda: document: store: azure: class: io.camunda.document.store.azure.AzureBlobDocumentStoreProvider container: my-container endpoint: https://myaccount.blob.core.windows.net ``` --- ## Document handling configuration Camunda supports multiple storage options for handling documents in Self-Managed environments. Depending on your deployment setup and production requirements, you can choose from cloud-based, local, or in-memory storage methods. The following section outlines supported storage options, their intended use cases, and configuration guidance. Jump to the environment configuration options below: - [Camunda 8 Run](/self-managed/concepts/document-handling/configuration/camunda-8-run.md) - [Docker Compose](/self-managed/concepts/document-handling/configuration/docker.md) - [Helm](/self-managed/concepts/document-handling/configuration/helm.md) :::note Camunda 8 Run is a fast way for users to test the capabilities of the platform, but it is not necessarily scalable. Docker Compose is not recommended for use in production. Therefore, Helm is currently the **only** option to deploy the platform in a Self-Managed environment for production. ::: ## Supported storage options - By using **external cloud file bucket storages**, documents can be stored in a secure, and scalable way. Buckets are integrated per cluster to ensure proper isolation and environment-specific management. The following file bucket storages are supported: - [**Google Cloud Platform (GCP)**](https://cloud.google.com/storage) - [**AWS S3**](https://aws.amazon.com/s3/) - [**Azure Blob Storage**](https://azure.microsoft.com/en-us/products/storage/blobs) - Configuring these storages is supported in [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md), [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md), and [Helm](/self-managed/deployment/helm/install/quick-install.md). - **Local storage** can be configured for a cluster to store documents in a local folder. - It can be used only for local development with [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md). - Local storage is not suitable for production use, as pods and file paths are not shared across components. This prevents components like Tasklist and Zeebe from accessing the same data. Files are stored locally, and their retention must be managed manually. - If you're using a container image and a mounted volume for your storage, you can use the path `/usr/local/camunda/documents` as the mount path, as it will already have the right permissions for the Camunda process to read and write to it. - **In-memory** storage can be used to store documents during the application's runtime. When the application is stopped, documents are lost. - It can be used with [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md), [Docker Compose](/self-managed/quickstart/developer-quickstart/docker-compose.md) and [Helm](/self-managed/deployment/helm/install/quick-install.md). - In-memory storage is not suitable for production use, as pods and memory are not shared across components. Files stored in memory are not persisted and will be lost on application restart. ## Storage policies - **Maximum upload size for one or multiple files**: 10 MB - **File expiration time/time-to-live (TTL) policy**: With Self-Managed, users may define their own lifecycle policies. A custom expiration date can be specified via metadata for each document. The [document upload API](/apis-tools/orchestration-cluster-api-rest/specifications/create-document.api.mdx) allows this. For forms, this defaults to the cluster configuration as there is no set custom TTL for forms. --- ## Getting started with document handling Camunda 8 Self-Managed supports document storage and management using Camunda Forms, connectors, Tasklist, and the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/specifications/create-document.api.mdx). You can store, track, and manage binary data, like PDFs, images and other file types, across development and production environments. Storage must be configured according to your infrastructure and operational needs. :::note For SaaS-focused document handling content, visit our [SaaS documentation](/components/document-handling/getting-started.md). ::: ## Use cases and capabilities Document handling can be beneficial for different process use cases, such as uploading a document to a BPMN process, displaying and downloading a document, sending a document to an external system via a connector, and automating documents with [intelligent document processing](/components/hub/workspace/modeler/idp/idp-example.md). Step through all of these capabilities in the [use cases section](/components/document-handling/getting-started.md). ## Storage integration and configuration You can configure document storage based on your deployment setup and production requirements. Supported options include an external cloud storage such as Google Cloud Platform (GCP) or AWS S3, local file storage or an in-memory storage. Learn how to set it up in the [storage configuration guide](/self-managed/concepts/document-handling/configuration/index.md). --- ## Camunda exporters As Zeebe processes jobs and workflows, or performs internal maintenance (for example, Raft failover), it produces an ordered stream of records. :::note Exporters are not available in Camunda 8 Software-as-a-Service (SaaS). ::: ![record-stream](img/exporters-stream.png) Although clients cannot directly inspect this stream, Zeebe can load user-defined code, known as an exporter, to process each record. An exporter provides a single entry point to handle every record written to the stream. Exporters can be used for various purposes: - Persist historical data by pushing it to an external data warehouse - Export records to visualization tools (for example, [zeebe-simple-monitor](https://github.com/camunda-community-hub/zeebe-simple-monitor)) Zeebe loads exporters only if they are configured in the main Zeebe YAML configuration file. Exporters are initialized when Zeebe starts. Exporters receive only records produced after they are configured. Camunda 8 Self-Managed ships several built-in exporters, including the [Camunda exporter](../components/orchestration-cluster/zeebe/exporters/camunda-exporter.md), [Elasticsearch](../components/orchestration-cluster/zeebe/exporters/elasticsearch-exporter.md), [OpenSearch](../components/orchestration-cluster/zeebe/exporters/opensearch-exporter.md), and the RDBMS exporter (see [RDBMS configuration](/self-managed/concepts/databases/relational-db/configuration.md)). Use a custom exporter only when you need a different target system or behavior. Zeebe manages data deletion through two distinct mechanisms to reduce disk usage: 1. Internal state deletion: Zeebe automatically deletes data from its internal state (RocksDB) when it's no longer operationally required, such as when a process instance completes. This deletion is independent of exporters. 2. Log stream compaction: The event log stream (which stores all runtime and historical records) is compacted based on positions acknowledged by exporters. Each exporter acknowledges the position of the last record it has successfully processed. The stream processor also marks the position it has processed. Log compaction then occurs up to the lowest acknowledged position across all exporters and the stream processor, ensuring the log can be safely truncated without losing data that exporters haven't yet consumed. :::note If no exporters are configured, Zeebe automatically deletes data when it's no longer needed. To retain historical data, you must configure an exporter to stream records to an external system. ::: All exporters—whether loaded from an external JAR or not—interact with the broker through the [exporter interface](https://github.com/camunda/camunda/blob/main/zeebe/exporter-api/src/main/java/io/camunda/zeebe/exporter/api/Exporter.java). ## Loading Exporters are loaded during broker startup, before any processing begins. The broker validates each exporter configuration during loading and will fail to start if: - An exporter ID is not unique - The exporter references a non-existent or inaccessible JAR - The specified class does not exist or can't be instantiated - The exporter throws an exception in its `Exporter#configure` method This validation step allows exporters to perform lightweight configuration checks. During this phase, the context provides a partition ID value of `Context#NULL_PARTITION_VALUE`. At runtime, this will be replaced with the actual partition ID. :::note Zeebe instantiates the exporter for validation and then discards it. Exporters should avoid heavy computations during instantiation. ::: ### Metrics The Micrometer [MeterRegistry](https://docs.micrometer.io/micrometer/reference/concepts/registry.html) is available via the `Exporter#configure(Context)` method for exporters to record metrics: ```java public class SomeExporter implements Exporter { @Override public void configure(final Context context) { // ... registry = context.getMeterRegistry(); // ... } public void flush() { try (final var ignored = Timer.resource(registry, "meter.name")) { exportBulk(); } } } ``` When an exporter is validated, it receives an in-memory register that is discarded afterward. :::note Zeebe creates an isolated class loader for each JAR referenced in exporter configurations. If the same JAR is used by multiple exporters, they will share the same class loader. This design allows different exporters to depend on the same third-party libraries without concerns about version conflicts or class name collisions. System classes and those bundled with the Zeebe JAR are loaded via the system class loader. ::: Exporter-specific configuration is defined in the `[exporters.args]` nested map. This map is passed as a `Map` to the exporter's `Exporter#configure(Configuration)` method using the [Configuration](https://github.com/camunda/camunda/tree/main/zeebe/exporter-api/src/main/java/io/camunda/zeebe/exporter/api/context/Configuration.java) object. Configuration takes place in two phases: once during broker startup and again each time a partition elects a new leader. ## Processing At any given time, there is exactly one leader node for each partition. When a node becomes the leader for a partition, it starts an instance of the [exporter stream processor](https://github.com/camunda/camunda/tree/main/zeebe/broker/src/main/java/io/camunda/zeebe/broker/exporter/stream/ExporterDirector.java). This stream processor creates exactly one instance of each configured exporter and forwards every record on the stream to each exporter in sequence. :::note This means there is exactly one instance of each exporter per partition. For example, if you have four partitions and four processing threads, potentially four instances of your exporter may run simultaneously. ::: Zeebe guarantees at-least-once delivery semantics. This means that each record will be seen by an exporter at least once, but possibly more. Duplicate delivery can occur in scenarios such as: - Reprocessing after Raft failover (i.e., leader re-election) - Errors occurring before the exporter updates its position To reduce duplicates, the stream processor tracks the position of the last successfully exported record for each exporter. Because the stream is an ordered sequence of records with monotonically increasing positions, tracking the position is sufficient. Exporters set this position once they can ensure the corresponding record was exported successfully. :::note Although Zeebe minimizes duplicate record delivery, exporters must be designed to handle duplicates. Export operations must be idempotent. This can be implemented within the exporter, but if exporting to an external system, it's recommended to handle deduplication there to minimize load on Zeebe. Refer to the exporter-specific documentation for implementation details. ::: ### Error handling If an error occurs during the `Exporter#open(Context)` phase, the stream processor fails and is restarted. This may resolve transient issues automatically. In the worst case, no exporters will run until the errors are resolved. If an error occurs during the `Exporter#close` phase, it is logged, but other exporters are still allowed to finish their work and shut down gracefully. If an error occurs during record processing, the same record is retried continuously until the error no longer occurs. In the worst case, a single failing exporter can block all exporters for that partition. Currently, exporters are expected to implement their own retry and error-handling strategies—though this behavior may evolve in future Zeebe versions. ### Performance impact Each loaded exporter introduces some performance overhead. A slow exporter will slow down all other exporters for the same partition and, in extreme cases, may block a processing thread entirely. To avoid performance bottlenecks, exporters should be kept as simple and lightweight as possible. Any heavy data transformation or enrichment should be delegated to external systems. :::warning When you enable or change exporter-side filters on an existing cluster, the exported record stream can change shape. If another component (such as Optimize) relies on a previously unfiltered sequence, this may lead to gaps or inconsistencies unless you follow the recommended upgrade flow. For Optimize‑specific guidance and examples, see the [Camunda 8 system configuration](../components/optimize/configuration/system-configuration-platform-8.md) documentation. ::: ## Purging The data purge feature allows you to delete all historical (and runtime) data from your cluster. Therefore every exporter needs to implement the `Exporter#purge` method. When a purge is happening, the `Exporter#purge` method is called. This method: - Deletes all data exported so far. - Is blocking and only returns when all data has been deleted. - May be retried and therefore must be idempotent. When the purge cluster operation is executed, the following steps are taken: - All nodes leave existing partitions resulting in a cluster with no partitions. This means all exporters are closed. At this point all runtime data is deleted. - For all previously configured exporters, the following steps are executed sequentially for each exporter: - It is configured via `Exporter#configure(Context)` - The `Exporter#purge` method is called. - The exporter is closed via `Exporter#close`. - Partitions are bootstrapped and nodes rejoin the partitions with the same configuration as before the purge. :::note `Exporter#open(Context)` is not called during the purge operation. ::: When an exporter is purged, it is expected to delete all data, but not schemas. In the case that an exporter exports to a database, only the records are deleted, not the tables themselves. :::note All resources required for purging need to be closed afterwards to avoid memory leaks. ::: ## Custom exporter to filter specific records The exporter interface supports record filtering through the [`Context#RecordFilter`](https://github.com/camunda/camunda/blob/main/zeebe/exporter-api/src/main/java/io/camunda/zeebe/exporter/api/context/Context.java) interface. At a high level, filtering happens in two phases: - Metadata-level filtering via `acceptType`, `acceptValue`, and `acceptIntent`, which runs before records are deserialized and is very cheap. - Record-level filtering via `acceptRecord(Record)`, which can inspect the fully deserialized record value when you need richer conditions (for example, inspecting variables or BPMN process IDs). Valid record types and value types can be found in the [protocol definition](https://github.com/camunda/camunda/blob/main/zeebe/protocol/src/main/resources/protocol.xml), while intents are listed in the [Intent enum class](https://github.com/camunda/camunda/blob/main/zeebe/protocol/src/main/java/io/camunda/zeebe/protocol/record/intent/Intent.java). For example, you can implement a custom exporter that only exports records with: - Record type: `EVENT` - Value type: `JOB` - Intent: `CREATED` ```java 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; } @Override public boolean acceptIntent(Intent intent) { return intent == JobIntent.CREATED; } } ``` You can then set this filter in the `Exporter#configure` method of your custom exporter: ```java public class CustomExporter implements Exporter { // ... private Controller controller; @Override public void open(final Controller controller) { this.controller = controller; // ... } @Override public void configure(final Context context) { // ... context.setFilter(new CustomExporterFilter()); } @Override public void export(final Record record) { // ... // after handling the record, acknowledge the position controller.updateLastExportedRecordPosition(record.getPosition()); } // ... } ``` :::note - After handling the record, you must acknowledge the position by calling `controller.updateLastExportedRecordPosition(record.getPosition())`. Because the stream is an ordered sequence of records with monotonically increasing positions, tracking the position is sufficient. Exporters set this position once they can ensure the corresponding record was exported successfully. - All filter methods are combined with logical `AND`. A record is exported only if it passes `acceptType`, `acceptValue`, `acceptIntent`, and (when implemented) `acceptRecord`. In simple cases you can implement only the metadata methods; use `acceptRecord` when you need to inspect full record values. ::: ### Listen to expired messages with a custom filter You can also create a custom filter to listen to expired messages. This can be useful if you want to take specific actions on messages that have expired, such as logging them or re-publishing them. For example, if you want to allow exporting only message events with `EXPIRED` intent, follow the steps below: 1. Implement the `RecordFilter` interface: ```java public class MessageExpiredExporterFilter implements RecordFilter { @Override public boolean acceptType(RecordType recordType) { return recordType == RecordType.EVENT; } @Override public boolean acceptValue(ValueType valueType) { return valueType == ValueType.MESSAGE; } @Override public boolean acceptIntent(Intent intent) { if (intent instanceof MessageIntent messageIntent) { return messageIntent == MessageIntent.EXPIRED; } return true; } } ``` :::note This filter will only accept records of type `EVENT`, value type `MESSAGE`, and intent `EXPIRED`. To accept more record types, value types, and intents, modify the `acceptType`, `acceptValue`, and `acceptIntent` methods accordingly. ::: 2. Set the `MessageExpiredExporterFilter` filter in the `Exporter#configure` method of your custom exporter: ```java public class MessageExpiredExporter implements Exporter { // ... private Controller controller; @Override public void open(final Controller controller) { this.controller = controller; // ... } @Override public void configure(final Context context) { // ... context.setFilter(new MessageExpiredExporterFilter()); } @Override public void export(final Record record) { // ... // after handling the record, acknowledge the position controller.updateLastExportedRecordPosition(record.getPosition()); } // ... } ``` :::info Messages with zero TTL will also be exported with this filter. If a message with zero TTL is republished after expiration, it will immediately expire again, causing the exporter to receive it again. This creates an infinite loop of republishing and expiring the same message, potentially leading to the engine blocked from processing other records. To avoid this, check the message TTL before republishing it. ::: 3. (Optional) By default, the exporter will not receive the full message body, only the message key with the empty message body is exported. To receive the full message body with the expired message, enable it via YAML configuration or environment variable. ```sh ZEEBE_BROKER_EXPERIMENTAL_FEATURES_ENABLEMESSAGEBODYONEXPIRED=true ``` ```yaml zeebe: broker: experimental: features: enableMessageBodyOnExpired: true ``` :::caution Enabling the full message body for expired messages can impact the performance of message expiration. - When this feature flag is enabled, every deleted message is appended to the Zeebe engine's record stream **including the full message body**. - Because each expired message now carries its entire payload, the expiration checker's write buffer fills up faster. As a result, the checker requires more time (or more roundtrips) to process the same number of expired messages. - This can lead to an increasing backlog of messages waiting to be expired. For finer control over the expiration checker's behavior, see the [message TTL checker configuration](https://github.com/camunda/camunda/blob/main/dist/src/main/config/broker.yaml.template#L1223). ::: ### Optimize-oriented built-in filters Camunda‑maintained exporters for Elasticsearch and OpenSearch include built‑in filters that reduce the volume of data exported when their indices are used primarily by Optimize. These filters can: - Restrict exported variables (by name pattern or inferred value type). - Include or exclude whole processes (based on `bpmnProcessId`). - Enable an Optimize mode that exports only the record types and intents Optimize actually consumes. - Control variable scope for root and local variables, including options to drop all local variables or apply different filters to each scope. For concrete arguments, examples, and version details, see: - [Elasticsearch exporter](../components/orchestration-cluster/zeebe/exporters/elasticsearch-exporter.md#configuration) - [OpenSearch exporter](../components/orchestration-cluster/zeebe/exporters/opensearch-exporter.md#configuration) - [Camunda 8 system configuration](../components/optimize/configuration/system-configuration-platform-8.md) ## Schema In earlier Camunda versions, some upgrades required manual data migrations. These migrations often introduced: - Operational downtime - Risk of human error - Complex backup and rollback procedures Starting with Camunda 8.8, exporters are designed to support upgrades without requiring data migrations. This approach reduces complexity, minimizes downtime, and enables faster, more reliable releases. --- ## Dual-region Camunda 8 can be deployed in a dual-region configuration with certain [limitations](#camunda-8-dual-region-limitations). Starting with version 8.9, this setup runs **active-active** by default when using the v2 REST API and Tasklist V2 — both regions process data and serve user traffic. Deployments still on v1 APIs operate in a hybrid mode: active-active data replication with active-passive user traffic routing (see [Active-active](#active-active)). :::important **Both regions must be fully operational at all times.** With v2 APIs, both regions also serve user traffic simultaneously. With v1 APIs, one region serves user traffic (primary) while the other only processes and replicates data (secondary). ::: :::caution Before implementing a dual-region setup, ensure you understand the topic, the [limitations](#camunda-8-dual-region-limitations) of dual-region setup, and the general [considerations](#platform-considerations) of operating a dual-region setup. ::: ## Architecture overview The dual-region setup is active-active by default with v2 APIs (8.9+), and a hybrid active-active data / active-passive user-traffic architecture with v1 APIs: | **Component** | **Mode** | **Both Regions Running** | **User Traffic** | **RPO** | | --------------------------------------------: | ------------------------------------------------------- | ------------------------ | ----------------------------------------- | ------- | | **Orchestration Cluster** | | ✅ Required | | | | Zeebe | Active-active | ✅ Required | Both regions process data | 0 | | Admin | Active-active | ✅ Required | Cluster-level identity | 0 | | Operate | Active-active with v2 API (active-passive with v1) | ✅ Required | Both regions serve users with v2 API | 0 | | Tasklist | Active-active with Tasklist V2 (active-passive with v1) | ✅ Required | Both regions serve users with Tasklist V2 | 0 | | **Elasticsearch** | Active-active | ✅ Required | Data replicated to both | 0 | :::important **All components in both regions must be fully operational at all times.** "Passive" refers only to user traffic routing, not system operation. Both regions actively participate in data processing and replication. ::: ## Traffic routing and terminology ### Primary and secondary regions With v2 APIs (default in 8.9+), **both regions serve user traffic simultaneously** — there is no primary/secondary distinction at the UI layer. Load can be distributed across regions via DNS or load balancer. The primary/secondary terminology only applies to deployments still using v1 APIs: - **Primary region**: Serves user traffic (UI access, API calls). - **Secondary region**: Fully operational but does not serve user traffic under normal conditions. In both cases, both regions are operationally active with all components running and replicating data. ### User traffic management With v2 APIs (default in 8.9+), user traffic can be served from both regions simultaneously. Distribute traffic via DNS, load balancer, or network routing policies as appropriate for your setup. With v1 APIs, you must route user traffic exclusively to the primary region. Methods include: - DNS configuration - Load balancer settings - Network routing policies If the primary region fails in a v1-API setup, traffic must be redirected manually to the secondary region. ## Active-active vs active-passive comparison - **Active-active** setups distribute user traffic across multiple regions simultaneously, with all regions processing requests. - **Active-passive** setups designate one region for user traffic while keeping backup regions on standby. - **Camunda's approach** depends on the API version in use: - **Data layer (all setups)**: Active-active replication ensures zero data loss (RPO = 0). - **User interface layer with v2 APIs (default in 8.9+)**: Active-active — both regions serve user traffic; consistency is maintained because all writes flow through the Camunda Exporter, not region-local stores. - **User interface layer with v1 APIs**: Active-passive routing prevents conflicts caused by region-local v1 state (batch operations, task assignments). ## Disclaimer :::caution Running dual-region setups requires developing, testing, and executing custom [operational procedures](/self-managed/deployment/helm/operational-tasks/dual-region-ops.md) matching your environments. This page outlines key points to consider. ::: :::info Active-Active Starting in Camunda 8.8, the **v2 REST API** removed previous region-specific limitations. In current releases, Tasklist also uses only the Orchestration Cluster REST API, so user task operations are no longer tied to the legacy Tasklist V1 behavior. These improvements also make a user-facing **active-active** setup feasible. Starting with version 8.9, this configuration will become the default for dual-region deployments. This note serves as an early indication that **active-active** functionality is already supported. ::: ## Dual-region architecture The dual-region architecture consists of two regions in a Kubernetes-based installation. Each region has a Kubernetes cluster with all Camunda 8 components fully operational. - With v2 APIs (default in 8.9+), both regions serve user traffic simultaneously. - With v1 APIs, **Region 0** acts as the primary region serving user traffic and **Region 1** acts as the secondary region, fully operational but not serving user traffic. Both regions actively participate in data processing and replication. :::note The visual representation shows both regions as operational. Any grayed-out appearance in the diagram represents user traffic routing, not system operational status. All components in both regions must be running and operational. ::: The Orchestration Cluster consists of multiple components: - Zeebe stretches across regions using the [Raft protocol](), allowing communication and data replication between all brokers. - Zeebe exports data to Elasticsearch instances in both regions using the [Camunda Exporter](/self-managed/components/orchestration-cluster/zeebe/exporters/camunda-exporter.md). - Using the new exporters ensures that Operate and Tasklist data is the same in both regions. See [active-active](#active-active). - Admin is embedded in the Orchestration Cluster and provides cluster-level identity management. ### User traffic With v2 APIs (default in 8.9+), both regions serve user traffic simultaneously, and traffic can be distributed via DNS, load balancer, or network routing policies. With v1 APIs, the system uses active-passive user traffic routing. You must designate one region as the primary and route all user traffic to it. The secondary region remains fully operational but does not serve user requests (see [Active-active](#active-active)). Traffic management responsibilities (v1-API setups): - Configure DNS to route to primary region. - Implement health checks and failover procedures. - Manually redirect traffic during primary region failure in combination with the [operational failover procedure](/self-managed/deployment/helm/operational-tasks/dual-region-ops.md#failover). :::warning Operation requirement Traffic redirection must be performed as part of the complete failover procedure. Redirecting traffic without following the operational procedure can lead to system inconsistencies and data issues. ::: ### Components The currently supported Camunda 8 Self-Managed components are: - Orchestration Cluster - Zeebe (process automation engine) - Admin - Operate - Tasklist - Elasticsearch (database) #### Component requirements | Component | Mode | Requirement | Function | Data loss risk | | --------------------------------------------: | ------------------------------------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------- | | **Orchestration Cluster** | | | | | | Zeebe | Active-active | All brokers in both regions must run | Leaders and followers distributed across regionsContinuous replication via Raft protocolBoth regions required for quorum maintenance | Can handle region failure without data loss when properly configured | | Admin | Active-active | Embedded in the Orchestration cluster | Admin provides unified, cluster-level identity management and authorization | Can handle region failure without data loss | | Operate | Active-active with v2 API (active-passive with v1) | Embedded in the Orchestration cluster | Both regions maintain synchronized data stateBoth regions serve users when using v2 API**Region-specific data**: Uncompleted batch operations only when using v1 API | Data loss possible only if using v1 API, as changes are isolated to the initiated region. | | Tasklist | Active-active with Tasklist V2 (active-passive with v1) | Embedded in the Orchestration cluster | Both regions maintain synchronized data stateBoth regions serve users when using Tasklist V2**Region-specific data**: Task assignments only when using v1 API | Data loss possible only if using v1 API, as changes are isolated to the initiated region. | | **Elasticsearch** | Active-active | Both clusters must run | Independent clusters in each regionZeebe exports identical data to both continuously and directlyData consistency maintained through Zeebe's dual export mechanism, not Elasticsearch replicationThe clusters do not communicate with each other—replication happens at the Zeebe level | Zeebe exporters may fail globally if secondary ES is down | ## Requirements and limitations ### Installation environment Two Kubernetes clusters are required for the Helm chart installation. :::note OpenSearch is **not supported** in dual-region configurations. ::: :::note RDBMS (relational database) secondary storage is **not supported** in dual-region configurations. ::: #### Network requirements - Kubernetes clusters, services, and pods must not have overlapping CIDRs. Each cluster must use distinct CIDRs that do not conflict or overlap with those of any other cluster to avoid routing issues. - The regions (for example, two Kubernetes clusters) must be able to communicate with each other (for example, via VPC peering). See [example implementation](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/dual-region.md) for AWS EKS. - Kubernetes services in one cluster must be resolvable and reachable from the other cluster and vice-versa. This is essential for proper communication and functionality across regions: - For AWS EKS setups, ensure DNS chaining is configured. Refer to the [Amazon Elastic Kubernetes Service (EKS) setup guide](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/dual-region.md). - For OpenShift, [Submariner](https://docs.redhat.com/en/documentation/red_hat_advanced_cluster_management_for_kubernetes/2.11/html/networking/networking#submariner) is recommended for handling multi-cluster networking. Refer to the [OpenShift dual-region setup guide](/self-managed/deployment/helm/cloud-providers/openshift/dual-region.md). - Maximum network round trip time (**RTT**) between regions should not exceed **100 ms**. - Required open ports between the two regions: - **9200** for Elasticsearch (for cross-region data pushed by Zeebe). - **26500** for communication to the Zeebe Gateway from clients/workers. - **26501** and **26502** for communication between Zeebe brokers and the Zeebe Gateway. ### Zeebe cluster configuration The following Zeebe brokers and replication configuration are supported: - `clusterSize` must be a multiple of **2** and at least **4** to evenly distribute brokers across the two regions. - `replicationFactor` must be **4** to ensure even partition distribution across regions. - `partitionCount` is unrestricted but should be chosen based on workload requirements. See [understanding sizing and scalability behavior](../../../components/best-practices/architecture/sizing-your-environment.md#understanding-sizing-and-scalability-behavior). For more details on partition distribution, see [documentation on partitions](../../../components/zeebe/technical-concepts/partitions.md). Zeebe creates partitions in a [round-robin fashion](/components/zeebe/technical-concepts/partitions.md#partition-distribution). The Helm charts ensures that all brokers with even numbers (0, 2, 4, 6, ...) are created in the same region. The brokers with uneven numbers (1, 3, 5, 7, ...) are created in the other region. This numbering and the round-robin partition distribution assures the even replication across the two regions. #### Scaling Zeebe cluster Follow the [Cluster Scaling steps](../../components/orchestration-cluster/zeebe/operations/cluster-scaling.md) respecting the [Zeebe cluster configuration](#zeebe-cluster-configuration). - The cluster should be evenly scaled, keeping the regions balanced with the same number of brokers. ### Camunda 8 dual-region limitations | **Aspect** | **Details** | | :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Installation methods | For Kubernetes we recommend using a dual-region Kubernetes setup with the [Camunda Helm chart](/self-managed/deployment/helm/install/quick-install.md) installed in two Kubernetes clusters.For other platforms, using alternative installation methods (for example, docker-compose) is not covered by our guides. | | Traffic Management | **Data Layer**: Active-active replication with zero RPO (all setups).**User Traffic**: Active-active with v2 APIs (8.9+ default); active-passive routing with v1 APIs to prevent region-local conflicts.**All Components**: Must be operational in both regions. | | Management Identity Support | Management Identity, including multi-tenancy and role-based access control (RBAC), is currently unavailable in this setup. Multi-tenancy and RBAC are supported using the Orchestration Cluster level Admin. | | Optimize Support | Not supported (requires Management Identity with specific configuration). | | Connectors Deployment | Connectors can be deployed in a dual-region setup, but attention to [idempotency](../../../components/connectors/use-connectors/inbound.md#creating-the-connector-event) is required to avoid event duplication. In a dual-region setup, you'll have two connector deployments, so using message idempotency is critical. | | Connectors | If you are running Connectors and have a process with an inbound connector deployed in a dual-region setup, consider the following: when you want to delete the process deployment, delete it via Operate, otherwise the inbound connector won't deregister.if you have multiple Operate instances running, then perform the delete operation in both instances. This is a [known limitation](https://github.com/camunda/camunda/issues/17762). | | Zeebe Cluster Scaling | Supported. See [Zeebe cluster configuration](#zeebe-cluster-configuration) | | Web Modeler | Web Modeler is a standalone component that is not covered in this guide. Modelling applications can operate independently outside of the orchestration clusters. Web Modeler also has a dependency on Management Identity. | ### Infrastructure and deployment platform considerations Multi-region setups come with inherent complexities, and it is essential to fully understand these challenges before selecting a dual-region configuration. The following areas must be managed independently, and are not controlled by Camunda or covered by our guides: - **Kubernetes cluster management**: Managing multiple Kubernetes clusters and their deployments across regions - **Monitoring and alerting**: Dual-region monitoring and alerting with cross-region correlation - **Cost implications**: Increased costs of multiple clusters and cross-region traffic - **Network reliability**: Data consistency and synchronization challenges (for example, brought in by the increased latency) - Bursts of increased latency can already have an impact - **Traffic management**: Managing DNS and incoming traffic routing - **Security**: Ensuring consistent security policies and network controls across regions - **Backup and disaster recovery**: Coordinating backup strategies across regions :::tip Operational Readiness Before implementing dual-region, ensure your organization has: - Experience managing multi-cluster Kubernetes environments - Established procedures for cross-region networking and security - Monitoring and alerting systems capable of cross-region correlation - Defined RTO/RPO requirements and tested recovery procedures ::: ### Upgrade considerations Follow the upgrade recommendations provided in the [Camunda Helm chart](/self-managed/upgrade/helm/index.md) and the [component-specific upgrade guides](/self-managed/upgrade/components/index.md). The general procedure outlined in the [upgrade overview](/self-managed/upgrade/index.md) also applies. Before starting, always create a [Camunda-supported backup](/self-managed/operational-guides/backup-restore/backup-and-restore.md). For dual-region setups, use a **staged upgrade approach**: upgrade one region at a time. Upgrading both regions simultaneously can cause a **loss of quorum** in Zeebe partitions if brokers in both regions are upgraded at once. To prevent this, complete the upgrade in one region before proceeding with the other, ensuring that only one Zeebe Broker is updated during each phase. However, for certain **minor version upgrades**, simultaneous upgrades of both regions may be required to complete migration steps successfully. Always consult the release notes and migration instructions for your specific version before proceeding. ## Region loss In a dual-region setup, loss of either region affects Camunda 8's processing capability due to quorum requirements. When a region becomes unavailable, the Zeebe cluster loses quorum (half of brokers unreachable) and **immediately stops processing** new data. This affects all components as they cannot update or process new processes until the failover procedure completes. :::warning Immediate Impact Region failure results in **immediate service interruption**: - No new process instances can start - Running process instances are suspended - User interfaces become unavailable if primary region is lost ::: See the [operational procedure](/self-managed/deployment/helm/operational-tasks/dual-region-ops.md) for recovery steps from region loss and re-establishment procedures. :::caution You must monitor for region failures and execute the necessary [operational procedures](/self-managed/deployment/helm/operational-tasks/dual-region-ops.md) to ensure smooth recovery and failover. ::: ### Primary region loss If the primary region is lost: - **Service disruption**: User traffic is unavailable - **Zeebe halt**: Processing stops due to quorum loss - **Data loss**: Region-specific data (batch operations, task assignments) is lost **only for deployments still using v1 APIs**. With v2 REST API and Tasklist V2, all data is replicated via the Camunda Exporter and survives region loss (see [Active-active](#active-active)). #### Recovery steps for primary region loss 1. **Temporary recovery:** Follow the [operational procedure](/self-managed/deployment/helm/operational-tasks/dual-region-ops.md#failover-phase) for temporary recovery to restore functionality and unblock the process automation engine (zeebe). 2. **Traffic rerouting:** With v2 APIs (default in 8.9+), remove the failed region from serving traffic (for example, via DNS or load balancer health checks). With v1 APIs, redirect user traffic to the secondary region (now primary). 3. **Data and task management**: - Reassign uncompleted tasks lost from the previous primary region. - Recreate batch operations in Operate. 4. **Permanent region setup:** Follow the [operational procedure](/self-managed/deployment/helm/operational-tasks/dual-region-ops.md#failback-phase) to create a new secondary region. ### Secondary region loss If the secondary region is lost: - **Zeebe halt**: Processing stops due to quorum loss. - **No user impact**: Traffic continues to be served by the primary region during recovery. #### Recovery steps for secondary region loss 1. **Temporary recovery:** Follow the [operational procedure](/self-managed/deployment/helm/operational-tasks/dual-region-ops.md#failover) to temporarily recover and restore processing. 2. **Permanent region setup:** Follow the [operational procedure](/self-managed/deployment/helm/operational-tasks/dual-region-ops.md#failback) to create a new secondary region. :::note Unlike primary region loss, no user-facing data is lost and no traffic rerouting is necessary. ::: ### Disaster recovery Based on all the limitations and requirements outlined in this article, you can consider the **Recovery Point Objective (RPO)** and **Recovery Time Objective (RTO)** in case of a disaster recovery to help with the risk assessment. The **RPO** is the maximum tolerable data loss measured in time. The **RTO** is the time to restore services to a functional state. For Operate, Tasklist, and Zeebe, the **RPO** is **0**. The **RTO** can be considered for the **failover** and **failback** procedures, both of which result in a functional state. - **failover** has an **RTO** of **< 1** minute to restore a functional state, excluding DNS reconfiguration and Networking considerations. - **failback** has an **RTO** of **5 + X** minutes to restore a functional state, where X is the time it takes to back up and restore Elasticsearch. This timing is highly dependent on the setup and chosen [Elasticsearch backup type](https://www.elastic.co/guide/en/elasticsearch/reference/current/snapshots-register-repository.html#ess-repo-types). During our automated tests, the reinstallation and reconfiguration of Camunda 8 takes 5 minutes. This can serve as a general guideline for the time required, though your experience may vary depending on your available resources and familiarity with the operational procedure. :::info The **Recovery Time Objective (RTO)** estimates are based on our internal tests and should be considered approximate. Actual times may vary depending on the specific manual steps and conditions during the recovery process. ::: ## Further resources - Familiarize yourself with our [Amazon Elastic Kubernetes Service (EKS) setup guide](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/dual-region.md). This showcases an example blueprint setup in AWS that utilizes the managed EKS and VPC peering for a dual-region setup with Terraform. - The concepts in the guide are mainly cloud-agnostic, and the guide can be adopted by other cloud providers. - Familiarize yourself with the [operational procedure](/self-managed/deployment/helm/operational-tasks/dual-region-ops.md) to understand how to proceed in the case of a total region loss and how to prepare yourself to ensure smooth operations. --- ## Configure secondary storage Configure secondary storage to enable features such as Operate, Tasklist, Identity, and search-based REST APIs in Camunda Self-Managed environments. Use "secondary storage" as the general concept. The backend can be a supported RDBMS or a document-store backend such as Elasticsearch or OpenSearch, depending on your deployment requirements. ## Configuration options You can configure secondary storage using Helm charts, Docker Compose, or manual configuration files. Camunda uses the `data.secondary-storage` configuration to define which secondary storage backend supports Orchestration Cluster web applications and APIs (for example, Operate, Tasklist, Identity, and search endpoints). :::note For the latest list of supported relational databases and versions, see the [RDBMS version support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md). ::: When deploying with Helm, set the secondary storage type, connection details, and exporter settings in your `values.yaml` file. ```yaml orchestration: exporters: camunda: enabled: false rdbms: enabled: true data: secondaryStorage: type: rdbms rdbms: url: jdbc:postgresql://hostname:5432/camunda username: camunda secret: existingSecret: camunda-db-secret existingSecretKey: password ``` More information about RDBMS in the Camunda Helm chart can be found on this [configuration page](/self-managed/deployment/helm/configure/database/rdbms.md). If you choose Elasticsearch as the secondary storage backend, configure it as follows: ```yaml global: elasticsearch: enabled: true external: true auth: username: elastic secret: existingSecret: camunda-db-secret existingSecretKey: password url: protocol: http host: hostname port: 443 orchestration: data: secondaryStorage: type: elasticsearch ``` More information about Elasticsearch in the Camunda Helm chart can be found in [using external Elasticsearch](/self-managed/deployment/helm/configure/database/elasticsearch/using-external-elasticsearch.md). To explicitly disable secondary storage (for example, when running only the Zeebe engine), set: ```yaml global: noSecondaryStorage: true ``` When this flag is set, all secondary-storage-dependent components are automatically disabled. If you’re using Docker Compose, configure your environment variables within the relevant service definition. The examples below use the `CAMUNDA_DATA_SECONDARY_STORAGE_*` naming family consistently. ```yaml environment: - CAMUNDA_DATA_SECONDARY_STORAGE_TYPE=rdbms - CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_URL=jdbc:postgresql://postgres:5432/camunda - CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_USERNAME=camunda - CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_PASSWORD=camunda ``` If you choose Elasticsearch as the secondary storage backend: ```yaml environment: - CAMUNDA_DATA_SECONDARY_STORAGE_TYPE=elasticsearch - CAMUNDA_DATA_SECONDARY_STORAGE_ELASTICSEARCH_URL=http://elasticsearch:9200 ``` If you choose OpenSearch as the secondary storage backend: ```yaml environment: - CAMUNDA_DATA_SECONDARY_STORAGE_TYPE=opensearch - CAMUNDA_DATA_SECONDARY_STORAGE_OPENSEARCH_URL=http://opensearch:9200 ``` To disable secondary storage: ```yaml environment: - CAMUNDA_DATA_SECONDARY_STORAGE_TYPE=none ``` For end-to-end backend-specific examples, including PostgreSQL, MariaDB, MySQL, Oracle, SQL Server, H2, Elasticsearch, and OpenSearch, see the [Docker Compose developer quickstart](/self-managed/quickstart/developer-quickstart/docker-compose.md). In Self-Managed or Camunda 8 Run deployments, you can also configure storage directly in the `application.yaml` file: ```yaml data: secondary-storage: type: rdbms rdbms: url: jdbc:h2:file:./camunda-data/h2db username: sa password: ``` If you choose Elasticsearch as the secondary storage backend: ```yaml data: secondary-storage: type: elasticsearch elasticsearch: url: http://localhost:9200/ ``` ## Choosing a storage backend - Local testing or Camunda 8 Run quickstart: H2 is fast, lightweight, and runs entirely in memory or file-based. - Production workloads: Use a supported RDBMS or document-store backend. Choose based on operational needs and validate with [benchmarking and sizing guidance](/components/best-practices/architecture/sizing-your-environment.md). - Debugging and troubleshooting: H2 or PostgreSQL are often easier to inspect and visualize. ### H2 limitations Use H2 only for development, testing, and evaluation. - H2 is non-production only. - H2 in-memory mode does not persist data across restarts. - H2 file-based mode persists only to local node disk and is intended for local/dev usage. - H2 does not provide a shared database across brokers. - Multi-broker clusters with H2 are not a valid architecture; query results can be broker-local and incomplete. - H2 has limited concurrency and scalability compared to external production backends. For Helm deployments, if you choose H2 you must run a single broker (`clusterSize: 1`, `partitionCount: 1`, `replicationFactor: 1`). For multi-broker Helm clusters, use a shared external backend (for example, PostgreSQL). ### Migration from invalid H2 setups If you currently run an invalid H2 topology, use one of these paths: 1. Local/dev only: Move to file-based H2 with a single broker. 2. Shared or clustered deployment: Move to an external persistent backend (for example, PostgreSQL, MariaDB, MySQL, Oracle, SQL Server, Elasticsearch, or OpenSearch according to support and architecture). :::note Starting in 8.9, H2 is the default secondary storage for lightweight Camunda 8 Run setups and quickstarts. H2 remains suitable for local testing, demos, and file-based setups, but it is not recommended for production workloads where persistence, scaling, and full analytics are required. For production use, Orchestration Cluster applications and APIs (including Operate, Tasklist, Identity, and search endpoints) should run against a persistent secondary storage backend such as a supported RDBMS or a document-store backend (Elasticsearch/OpenSearch). Both are valid production choices when supported for your deployment. Consult the [RDBMS version support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) when choosing a relational database, and [supported environments](/reference/supported-environments.md) for Elasticsearch/OpenSearch versions. ::: :::note Switching between secondary storage backend families (document-store and RDBMS) is not a supported in-place migration path. Plan migration as a fresh secondary-store setup, and validate the procedure in a non-production environment before rollout. For upgrade planning, see [prepare for upgrade](/self-managed/upgrade/prepare-for-upgrade.md). ::: ## Run without secondary storage If you want to run the Zeebe engine without secondary storage or web applications, you can use **no secondary storage** mode. In this mode: - Operate, Tasklist, Identity, Optimize, and the REST API are disabled. - The Zeebe engine and primary storage remain active for process execution. - This configuration is best suited for local development or minimal-resource environments. See [run without secondary storage](./no-secondary-storage.md) for configuration examples and limitations. --- ## Secondary storage Camunda uses a layered storage model that separates workflow execution data from data used by web applications and APIs. ## About secondary storage Secondary storage is one of the two complementary layers in Camunda’s data model: | Layer | Purpose | Technologies you can use | | :---------------- | :-------------------------------------------------------------------------------------------------------------------------------- | :-------------------------- | | Primary storage | Persists real-time workflow execution state managed by [Zeebe](/self-managed/components/orchestration-cluster/zeebe/overview.md). | RocksDB (embedded in Zeebe) | | Secondary storage | Stores workflow, decision, and task data for querying, visualization, and API access. | Document-store or RDBMS | :::note Secondary storage is not a duplicate of primary data. It represents exported workflow and decision data optimized for querying and visualization. ::: ### Supported storage options Camunda supports multiple secondary storage backends. For the latest list of supported database versions, see the [RDBMS version support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md). Both document-store and RDBMS backends are valid secondary storage choices in Self-Managed deployments. Support maturity can vary by product area and version (for example, the Orchestration Cluster API, Operate, Tasklist, Admin, or Optimize), so confirm current compatibility details before choosing a backend. | Database type | Availability | Use case | | :--------------------- | :------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Document-store (ES/OS) | General availability | Secondary storage for indexing, search, and analytics. | | RDBMS | 8.9+ | Secondary storage for relational database deployments. See the [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) for supported vendors and versions. | :::info OpenSearch support Camunda 8 supports both [Amazon OpenSearch](https://aws.amazon.com/opensearch-service) and the open-source [OpenSearch](https://opensearch.org/) distribution. ::: ```mermaid graph TD subgraph oc["Orchestration Cluster"] broker["Zeebe Broker"] exp_rdbms["RDBMS Exporter"] exp_camunda["Camunda Exporter\n(for Orchestration Cluster on ES/OS)"] exp_opt["Elasticsearch/OpenSearch Exporter\n(for Optimize)"] api["Orchestration Cluster API"] apps["Operate · Tasklist · Admin"] end rdbms["RDBMS\n(PostgreSQL, Oracle, MariaDB, MySQL)"] es["Elasticsearch/OpenSearch"] opt["Optimize\n(document-store required)"] broker -->|"Enable for RDBMS backend"| exp_rdbms broker -.->|"Enable for document-store backend"| exp_camunda broker -.->|"Enable when Optimize is deployed"| exp_opt exp_rdbms -->|"Write"| rdbms exp_camunda -->|"Write"| es exp_opt -.->|"Write"| es apps -->|"Use"| api api -->|"Query selected backend"| rdbms api -.->|"Query selected backend"| es opt -.->|"Read/Write (if deployed)"| es style broker fill:#e4eef8,stroke:#2272c9,color:#14082c style exp_rdbms fill:#e4eef8,stroke:#2272c9,color:#14082c style exp_camunda fill:#e4eef8,stroke:#2272c9,color:#14082c style exp_opt fill:#e4eef8,stroke:#2272c9,color:#14082c style api fill:#e4eef8,stroke:#2272c9,color:#14082c style apps fill:#e4eef8,stroke:#2272c9,color:#14082c style rdbms fill:#fde8da,stroke:#fc5d0d,color:#14082c style es fill:#fde8da,stroke:#fc5d0d,color:#14082c style oc fill:#f0f5ff,stroke:#2272c9 style opt fill:#e8fdf1,stroke:#10c95d,color:#14082c ``` :::note Starting in 8.9, Camunda 8 Run and default lightweight installs use H2 as the default secondary storage. Elasticsearch remains a supported alternative in Camunda 8 Run. OpenSearch and RDBMS-based secondary storage are supported in Self-Managed deployments. Enable the backend you need explicitly when required. H2 is a convenience default for local development, testing, and evaluation. It is not a production reference architecture and is not a valid backend for multi-broker Helm clusters. ::: 1. The Zeebe Broker executes workflow instances and stores state in primary storage. 1. Exporters, running as part of Zeebe, write orchestration data to the configured secondary storage backend and can write to multiple targets when needed. 1. Operate, Tasklist, and Admin use the Orchestration Cluster API, which reads data from the configured secondary storage backend. ## Choosing a secondary storage backend Camunda supports multiple secondary storage backends, and the right choice depends on your workload and operational constraints. For guidance on supported vendors, versions, and configuration, see: - [Secondary storage configuration](/self-managed/concepts/secondary-storage/configuring-secondary-storage.md) - [RDBMS configuration](/self-managed/deployment/helm/configure/database/rdbms.md) - [RDBMS version support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) - [RDBMS benchmark results](./rdbms-benchmark-results.md) :::note The documentation is intentionally descriptive rather than prescriptive. Use benchmarking and sizing based on your own workload to choose the secondary storage backend that best meets your requirements. ::: Learn how to configure secondary storage in Self-Managed environments using Helm, Docker, or manual deployment. Configure secondary storage :::note Although you should use secondary storage in nearly all production environments, you can choose to disable secondary storage in limited scenarios, such as lightweight development environments, specialized technical use cases, or resource-constrained deployments. See [run without secondary storage](no-secondary-storage.md). ::: ## Manage secondary storage Learn about best practices for data management, backups, and monitoring to ensure data integrity and performance. Effective secondary storage management ensures stability, scalability, and data integrity across your Camunda environment. By following Camunda best practices, you can avoid data corruption, maintain compliance, and ensure your orchestration environment remains performant and reliable. Manage secondary storage ## Benchmark results Review current benchmark results and caveats for PostgreSQL-based secondary storage. RDBMS benchmark results --- ## Manage secondary storage Manage your secondary storage carefully to maintain data integrity, performance, and system stability. For definitions and conceptual context, see [secondary storage](/reference/glossary.md#secondary-storage). ## Modifying secondary storage data :::warning You should never manually modify data stored in secondary storage unless instructed by Camunda Support during an active support case. Do not make direct edits to data in secondary storage outside of explicit Camunda Support guidance. ::: ### Risks of manual modification Unsupervised changes to secondary storage data can lead to severe issues, such as the following: | Risk | Description | | :------------------------- | :------------------------------------------------------------------------------------------------------------- | | Data loss | Manual edits may delete or overwrite essential records that cannot be recovered without backups. | | Data corruption | Structural or value changes can leave indices or tables in inconsistent states, leading to application errors. | | Unsupported system states | Unapproved modifications may break compatibility with upgrades, patches, or new features. | | Troubleshooting challenges | Custom edits make it difficult for support engineers to diagnose and resolve issues. | | Security vulnerabilities | Unauthorized changes can expose sensitive data or weaken access controls. | | Compliance issues | Altered records may violate internal or external data-integrity regulations. | ## Configuring capacity and redundancy Secondary storage configuration depends on the backend you choose (for example, a document-store backend such as Elasticsearch/OpenSearch, or an RDBMS). Both are valid options in supported scenarios. Use the documentation for your selected backend and validate decisions against your expected workload. :::note Backend selection and sizing should be based on benchmarking and realistic workload expectations. Prefer configuration choices that you can validate with measured throughput, latency, and retention needs. ::: ### Document-store backends (Elasticsearch/OpenSearch): shards and replicas :::warning When Elasticsearch/OpenSearch Exporter indices and Orchestration Cluster indices share the same Elasticsearch or OpenSearch cluster, they must use different index prefixes. One prefix must not be the beginning of the other (for example, avoid `custom` and `custom-zeebe` together because `custom*` matches both). Do not use `operate`, `tasklist`, or `camunda` as the full exporter prefix, and do not use `zeebe-record` as the Orchestration Cluster index prefix, as `zeebe-record` is the default prefix for Elasticsearch/OpenSearch Exporter indices. For detailed requirements, configuration examples, and common mistakes, see [Index prefix configuration](/self-managed/deployment/helm/configure/database/elasticsearch/configure-elasticsearch-prefix-indices.md#index-prefix-configuration). ::: If you use [Elasticsearch/OpenSearch](/reference/glossary.md#elasticsearchopensearch) as your secondary storage backend, configure shards and replicas to support resilience and scalability. #### Shards Define shard count according to your data size and expected growth. - Start with **1–5 primary shards** per index. - More shards can improve scalability but increase management complexity. - Avoid over-sharding, which can reduce performance and add unnecessary overhead. #### Replicas - **Single-node clusters:** Do not configure replicas. Replicas provide redundancy only when distributed across multiple nodes. On a single node, replicas remain unassigned and may prevent the cluster from reporting as healthy. - **Multi-node clusters:** Configure at least **one replica per index**. Replicas ensure fault tolerance by keeping data available if one node fails. ## Backups Regular backups of your secondary storage are critical for disaster recovery and business continuity. - Follow the official Camunda backup procedure step by step. - Schedule backups regularly based on data volume and business requirements. - Periodically test restore operations to confirm that your backups are valid and usable. ## Index templates If you use [Elasticsearch/OpenSearch](/reference/glossary.md#elasticsearchopensearch), Camunda uses index templates to define settings and mappings for indices. To prevent issues: - Avoid using custom index templates that conflict with Camunda’s defaults. Templates with higher priority may override Camunda mappings and cause incorrect index creation. - Do not delete or modify existing Camunda index templates without explicit guidance from Camunda Support. If your provider includes predefined wildcard index templates, set a higher priority for the Camunda templates to prevent conflicts. ## Monitoring secondary storage health Use the [Data Layer Dashboard](/self-managed/operational-guides/monitoring/metrics.md) to monitor performance and detect issues with your secondary storage integration. For example: - Track exporter and indexing latency. - Detect shard or replica imbalances for document-store backends (Elasticsearch/OpenSearch). - Identify degraded query performance early across secondary storage backends (document-store or RDBMS). ## Advanced: Elasticsearch deprecation logging If you are using Elasticsearch version ≥8.16.0, you may want to turn off deprecation logging to reduce log noise: ```bash curl -X PUT "http://localhost:9200/_cluster/settings" \ -H "Content-Type: application/json" \ -d '{ "persistent": { "logger.org.elasticsearch.deprecation": "OFF" } }' ``` This is optional but can help keep logs clean in production environments. --- ## Run without secondary storage Use **no secondary storage** mode to run Zeebe clusters with only the process engine and its primary storage layer. :::warning Disabling secondary storage removes key Orchestration Cluster capabilities, including Operate, Tasklist, Identity, and search-based REST endpoints. This mode is suitable only for lightweight development, testing, or specialized technical use cases. ::: ## About this mode Typically, you should use secondary storage in nearly all production environments to enable monitoring, analytics, querying, and human-task management through Orchestration Cluster applications. You should **only** disable/run without secondary storage in limited scenarios, such as lightweight development environments, specialized technical use cases, or resource-constrained deployments. - In this mode, Operate, Tasklist, Identity, and web-based APIs are automatically disabled. - For Helm deployments, Optimize is also disabled by default when secondary storage is not configured. - For Docker or manual deployments, you must **explicitly disable Optimize** in your configuration, as it cannot function without secondary storage. This setup provides core process execution and orchestration capabilities through Zeebe, but excludes the full Camunda experience, such as analytics, search, and human-task management. ## Enable **no secondary storage** mode You can enable this mode in several ways depending on your deployment method. To disable secondary storage in Helm-based installations, set the following flag in your `values.yaml` file: ```yaml global: noSecondaryStorage: true ``` When this value is set, the Helm charts automatically disable all components that depend on secondary storage. To disable secondary storage in Camunda 8 Run or other manual setups, set the following property in your configuration file: ```yaml spring: profiles: active: broker,standalone camunda: data: secondary-storage: type: none ``` Or use environment variables: ```yaml SPRING_PROFILES_ACTIVE=broker,standalone CAMUNDA_DATA_SECONDARYSTORAGE_TYPE=none ``` If brokers and gateways run separately, apply the same configuration for gateways: ```yaml spring: profiles: active: gateway,standalone camunda: data: secondary-storage: type: none ``` ```bash SPRING_PROFILES_ACTIVE=gateway,standalone CAMUNDA_DATA_SECONDARYSTORAGE_TYPE=none ``` In a Docker Compose setup, you can disable secondary storage by setting the following environment variable for the relevant service: ```yaml environment: - CAMUNDA_DATA_SECONDARYSTORAGE_TYPE=none ``` ## Authentication Authentication works with no secondary storage mode. OIDC authentication is configured the same way as with secondary storage enabled. For details, see [Orchestration Cluster authentication](/self-managed/concepts/authentication/authentication-to-orchestration-cluster.md). :::note Basic authentication with no secondary storage If you use Basic authentication, you must also enable unprotected API mode because Basic auth requires access to user data in secondary storage. ```yaml global: noSecondaryStorage: true orchestration: security: authentication: method: basic unprotectedApi: true authorizations: enabled: false ``` This configuration should **only be used for development** or testing environments, as the unprotected API mode disables authentication checks on API endpoints. ::: ## Components and features disabled If secondary storage is disabled, the following components and features are unavailable: | Category | Component or feature | Behavior | | :--------------- | :--------------------------------------------------------------------------------- | :--------------------- | | Web applications | Operate, Tasklist, Admin UI, Optimize, Play (Modeler Play tab) | Disabled | | APIs & services | Orchestration Cluster REST API (search endpoints), batch operations, usage metrics | Return `403 Forbidden` | | Data & storage | Secondary storage exporters, Schema Manager, secondary storage backups | Disabled | :::note - Outbound connectors remain supported. However, inbound connectors and any features that require process definition lookup are unavailable. - Custom exporters (for example, Kafka, Prometheus, or MongoDB) continue to work as they interact directly with the engine’s primary storage. ::: ## Error handling and API responses If you attempt to access a disabled feature, the system returns a descriptive error response. For example: ```json { "status": 403, "detail": "This endpoint requires secondary storage, but none is set. Configure it using the 'camunda.data.secondary-storage.type' property.", "instance": "/v2/decision-instances/search" } ``` - At startup, affected components log a warning and shut down gracefully. - SDKs and clients will also return a `403 Forbidden` error when interacting with unsupported endpoints. ## Limitations and considerations Using this mode significantly reduces Camunda’s capabilities: | Limitation | Impact | | ------------------------------- | -------------------------------------------------------------- | | No visual monitoring | Operate, Tasklist, and the Identity UI are unavailable. | | No historical data or analytics | Optimize, dashboards, and audit records cannot be accessed. | | Limited API access | Most search and query endpoints return `403 Forbidden`. | | Reduced observability | Built-in metrics and secondary storage exporters are disabled. | | No human task management | Tasklist and identity-based task assignment are unavailable. | :::note You can still deploy and execute BPMN and DMN processes using the Zeebe client or REST API endpoints that only rely on the engine. ::: ## When to use this mode :::caution Before using this mode in production, consult your Camunda support or field team to ensure it meets your requirements. ::: Use this mode only in limited, specialized scenarios, such as the following: | Scenario | Description | | :------------------------------------------ | :-------------------------------------------------------------------------------------------- | | Local development with Camunda 8 Run | Run Zeebe locally without secondary storage components for lightweight testing and iteration. | | Resource-constrained environments | Deploy only the Zeebe engine when secondary storage or web applications cannot be supported. | | Temporary migration or diagnostic scenarios | Minimal orchestration functionality during transition or troubleshooting. | | Custom monitoring or exporter development | Use custom exporters or external observability tools instead of secondary storage. | --- ## RDBMS benchmarking results This page summarizes current benchmark results for using PostgreSQL as Orchestration Cluster secondary storage. Use these results as directional guidance, not strict guarantees. Actual performance depends on process complexity, hardware, database configuration, retention settings, and query patterns. ## Test scope and environment Current published results are based on PostgreSQL benchmarking only. ### Scenarios tested - **Single-task workload**: A process with one task. - **Multi-step timer workload**: A 10-task process with multiple timers. - **Complex business workload**: A process with call activities, subprocesses, and DMN. ### Baseline setup used in benchmark discussions - Orchestration cluster: three nodes, three partitions. - Orchestration resources: 3.5 CPU and 2 GB RAM per node. - PostgreSQL: single-node containerized setup, typically 3-6 CPU and 6-8 GB RAM. - Retention baseline used in several tests: TTL 60 minutes. Published results currently reflect short-retention benchmark windows. ## Key results summary ### Write throughput - In scale tests, RDBMS write behavior was described as approximately linear with additional cluster capacity. ### Scenario observations - **Single-task workload**: Stable exporter behavior under low-complexity flow patterns. - **Multi-step timer workload**: Stable exporter behavior in multi-step flows, with engine pressure becoming visible before exporter pressure in some runs. - **Complex business workload**: Stable exporter behavior in complex business flows, where engine and workload characteristics can dominate end-to-end throughput. ### Data availability and cleanup - History cleanup overhead was minimal in short-retention scenarios and did not impact write throughput. With longer retention periods, allocate additional database resources for cleanup operations. ### Read/query behavior Read performance is currently the main trade-off for RDBMS secondary storage in query-heavy workloads: - Key-based access patterns scale better. - Broad filters, sorting, and statistics/count queries can degrade with data growth. - Statistics queries for process and dashboard views are known sensitive paths in the Orchestration Cluster API, whether called directly through the API or indirectly by Operate dashboards. Recommended mitigation: - Validate your most common read queries with production-like data. - Add and tune indexes for high-frequency filters used in your environment. - Re-evaluate index strategy as data volume and query patterns evolve. ### RDBMS vs Elasticsearch/OpenSearch comparison - With the same hardware setup, RDBMS write performance was observed at roughly **~70%** of Elasticsearch/OpenSearch write performance across tested scenarios. - API read performance (including Operate dashboards) with RDBMS can be significantly slower than Elasticsearch/OpenSearch on large datasets, especially for complex queries, multi-field filtering, and sorting. ## Choosing RDBMS secondary storage Choose RDBMS secondary storage when: - You already operate relational databases at scale. - You prefer operational consistency with existing database tooling. - Workloads are low-to-moderate and read/query pressure is manageable. Prefer Elasticsearch/OpenSearch when: - You need high-throughput, high-volume execution at scale. - You rely on heavy filtering/sorting/search workloads. - You require Optimize without introducing a second storage technology. ## Caveats and interpretation guidance - These results are from controlled benchmark scenarios and should not be interpreted as guaranteed production numbers. - Results can change as product and exporter optimizations evolve. - Always validate final sizing with production-like workload tests. --- ## Deploy to Amazon ECS This guide walks you through deploying the [Camunda 8 Orchestration Cluster](/reference/glossary.md#orchestration-cluster) and Connectors on AWS Elastic Container Service (ECS) using Fargate and Aurora PostgreSQL, and verifying that all components are working. :::tip New to AWS ECS? If you are new to AWS ECS or Terraform, consider reviewing the [AWS ECS documentation](https://docs.aws.amazon.com/ecs/) and [Terraform documentation](https://developer.hashicorp.com/terraform/docs) before proceeding with this guide. ::: ## Prerequisites - **AWS account** – An AWS account to provision resources with permissions for **ecs**, **iam**, **elasticloadbalancing**, **kms**, **logs**, and **rds** services. - For detailed permissions, refer to this [example policy](https://github.com/camunda/camunda-deployment-references/tree/main/aws/containers/ecs-single-region-fargate/example/policy.json). - **Terraform** – Infrastructure as code tool (v1.7 or later). [Install Terraform](https://developer.hashicorp.com/terraform/install). - **AWS CLI** – Command-line tool to manage AWS resources, used for `local-exec` to trigger the initial Aurora PostgreSQL user seeding. [Install AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html). For the exact tool versions used during testing, refer to the repository's [.tool-versions](https://github.com/camunda/camunda-deployment-references/blob/main/.tool-versions) file. ### Considerations :::warning Running this guide incurs costs on your AWS account, primarily for ECS and Aurora. Use the AWS [pricing calculator](https://calculator.aws/#/) to estimate costs for your region. ::: If you want a simpler setup, consider using [Camunda 8 SaaS](https://accounts.camunda.io/signup). - Unlike our other guides, which usually separate infrastructure setup from the deployment of Camunda 8, this is not the case with ECS. Since the infrastructure is largely managed by AWS, deploying Camunda 8 and provisioning the required AWS resources happens in a single step. - This guide focuses on AWS ECS with Fargate but can work with managed instances for more predictable performance. You can find more information about how to migrate from Fargate to managed instances from the [AWS migration guide](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/migrate-fargate-to-managed-instances.html). - This guide relies on a shared [multi-AZ replicated](https://docs.aws.amazon.com/efs/latest/ug/efs-replication.html) EFS network disk. - Cost and performance may differ from a related Kubernetes setup with block storage. - The EFS volume is shared among all brokers to support the native ECS Service capabilities. - AWS does not support block storage options in combination with ECS Services and Fargate. For a detailed overview, have a look at the [AWS documentation](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using_data_volumes.html). - Scaling is a manual process as it requires invoking the [cluster scaling API](/self-managed/components/orchestration-cluster/zeebe/operations/cluster-scaling.md) for joining and removing a [Zeebe Broker](../../../../../components/zeebe/technical-concepts/architecture.md#brokers). Autoscaling may not have effects as the brokers have to be explicitly joined into the [Zeebe cluster](../../../../../components/zeebe/technical-concepts/clustering.md) or when removed result in partitions or data becoming inaccessible. - A node-id provider is integrated into Zeebe that assigns an available node-id based on Zeebe cluster information, instead of relying on a statically-configured node-id. - This guide focuses on Aurora PostgreSQL for the secondary datastorage as it is a newly supported offering by Camunda 8 and potentially more familiar for customers. - You may still use Elasticsearch or OpenSearch but need to adjust the required configuration. More information about the configuration can be found in [our documentation](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md#data---secondary-storage). - Examples for how to deploy AWS OpenSearch can be found in other existing reference architectures for AWS. :::warning Reference architectures and examples provided in this guide are not turnkey modules. Camunda recommends cloning the repository and modifying it locally. You are responsible for operating and maintaining the infrastructure. Camunda updates the reference architecture over time, and changes may not be backward compatible. You can use these updates to upgrade your customized codebase as needed. ::: ### Outcome The result is a fully functioning Camunda Orchestration Cluster deployed in a high-availability setup using AWS ECS with Fargate and a managed Aurora PostgreSQL instance using IAM authentication. All ECS tasks share a single EFS volume dedicated to Camunda. #### Architecture The architecture outlined below describes a standard Zeebe three-node deployment, distributed across three [availability zones](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/) within a single AWS region. It includes a managed Aurora PostgreSQL instance deployed under the same conditions. This approach ensures high availability and redundancy in case of a zone failure. _Infrastructure diagram for the Orchestration Cluster ECS architecture (click the image to view the PDF version)_ [![AWS ECS Architecture](./assets/architecture.jpg)](./assets/architecture.pdf) After completing this guide, you will have: - A [Virtual Private Cloud](https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html) (VPC), which is a logically isolated virtual network. - _For simplification the private and public were not visualized in the diagram above._ - A [Private Subnet](https://docs.aws.amazon.com/vpc/latest/userguide/configure-subnets.html), which does not have direct internet access. - [Elastic Container Service (ECS) Cluster](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/clusters.html) - ECS Services for the Orchestration Cluster and Connectors - These spawn ECS tasks running on [Fargate](https://aws.amazon.com/fargate/) - [Elastic File System (EFS)](https://aws.amazon.com/efs/) as primary datastore for the Zeebe cluster - [Aurora PostgreSQL](https://aws.amazon.com/rds/aurora/) as secondary datastore - A [Public Subnet](https://docs.aws.amazon.com/vpc/latest/userguide/configure-subnets.html), which has internet access via an [Internet Gateway](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html). - (Optional) An [Application Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html) (ALB) to expose web interfaces such as Operate, Tasklist, Connectors, and the Orchestration Cluster REST API. This uses sticky sessions, as requests are otherwise distributed round-robin across ECS instances. - (Optional) A [Network Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/introduction.html) (NLB) to expose the gRPC endpoint of the Zeebe Gateway, if external applications need to connect. - [Security Groups](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-security-groups.html) to control network traffic to and from the ECS instances. - An [Internet Gateway](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html) to route traffic between the VPC and the internet. - An [S3 bucket](https://aws.amazon.com/s3/) used by the Orchestration Cluster’s ECS-specific node-id provider. - A versioning-enabled [S3 bucket](https://aws.amazon.com/s3/) for backups. - Use a separate bucket for backups. The node-id bucket has versioning disabled because frequent metadata changes would incur additional cost without any benefit. - [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/) for application credentials and optional container registry credentials. - [AWS CloudWatch](https://aws.amazon.com/cloudwatch/) for logs. - [ECS Service Connect](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/service-connect.html) to connect ECS services directly with each other. - [IAM authentication](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html) to connect the Orchestration Cluster with the Aurora PostgreSQL cluster. Both subnet types are distributed across three availability zones in a single AWS region, supporting a high-availability architecture. You can also scale this setup to a single ECS task. In that case, a zone failure makes the environment unavailable. ## Configure AWS and initialize Terraform :::note Terraform infrastructure example We do not recommend using the following Terraform-based infrastructure as a module, since we cannot guarantee compatibility. Instead, we suggest reusing or extending components of the Terraform example to ensure alignment with your environment. ::: ### Obtain a copy of the reference architecture Start by downloading a copy of the reference architecture from the GitHub repository. This content will be used throughout the rest of the guide. The reference architectures are versioned according to Camunda releases (e.g., stable/8.x). The reference architecture repository allows you to reuse and extend the provided Terraform examples. This flexible implementation avoids the constraints of relying on third-party-maintained Terraform modules: ```bash reference https://github.com/camunda/camunda-deployment-references/tree/main/aws/containers/ecs-single-region-fargate/procedure/get-your-copy.sh ``` With the reference architecture in place, you can proceed with the remaining steps in this documentation. Make sure you're in the correct directory before continuing with the instructions. ### Terraform prerequisites To manage Camunda 8 infrastructure on AWS using Terraform, you need to configure Terraform's backend to store the state file remotely in an S3 bucket. This provides secure, persistent primary storage for your infrastructure. :::note Advanced users may choose to configure a different backend. The setup described here is a recommended starting point for new users. ::: #### Set up AWS authentication The [AWS Terraform provider](https://registry.terraform.io/providers/hashicorp/aws/latest/docs) is required to provision resources in AWS. Before using the provider, you must authenticate it with your AWS credentials. :::caution Ownership of the created resources Any user who creates AWS resources retains administrative access to them. For better control and security, it is recommended to create a dedicated [AWS IAM user](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users.html) specifically for Terraform. This ensures the resources are properly managed and owned by a single identity. ::: You can customize the region and authentication settings as needed. Terraform supports multiple [authentication methods](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#authentication-and-configuration), including: - For development or testing, you can use the [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html). If your AWS CLI is already configured, Terraform will automatically detect and use those credentials. To configure the AWS CLI: ```bash aws configure ``` Enter your `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, region, and output format. These can be retrieved from the [AWS Console](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html). - For production environments, it is recommended to use a dedicated IAM user. Create [access keys](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html) for that user via the AWS console, and export them as environment variables: `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. #### Create an S3 bucket for Terraform state management Before initializing Terraform, you must create an S3 bucket to store the state file. This is essential for collaborative work and helps prevent issues such as state file corruption. Begin by setting your preferred AWS region as an environment variable to avoid repeating it in every command: ```bash export AWS_REGION= ``` Replace `` with your chosen AWS region (e.g., `eu-central-1`). Next, follow these steps to create an S3 bucket with versioning enabled: 1. Open your terminal and ensure that the AWS CLI is installed and properly configured. 2. Run the following command to create an S3 bucket for storing your Terraform state. Be sure to choose a unique bucket name, and ensure that the `AWS_REGION` environment variable is already set: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/common/procedure/s3-bucket/s3-bucket-creation.sh ``` 3. Enable versioning on the S3 bucket to track changes and protect the state file from accidental deletions or overwrites: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/common/procedure/s3-bucket/s3-bucket-versioning.sh ``` 4. Secure the bucket by blocking public access: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/common/procedure/s3-bucket/s3-bucket-private.sh ``` 5. Verify versioning is enabled on the bucket: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/common/procedure/s3-bucket/s3-bucket-verify.sh ``` The S3 bucket is now ready to securely store your Terraform state files, with versioning enabled for added protection. #### Initialize Terraform Once authentication is configured, you can initialize your Terraform project. Earlier, you created a dedicated S3 bucket (`S3_TF_BUCKET_NAME`) for storing the state file. In this step, Terraform will use that bucket along with a specific key to manage your infrastructure state. Initialize the backend and download the required provider plugins: :::note Make sure you are in the `terraform/cluster` subfolder: `camunda-deployment-references/aws/containers/ecs-single-region-fargate/terraform/cluster`. ::: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/common/procedure/s3-bucket/s3-bucket-tf-init.sh ``` Terraform will now use the S3 bucket to manage the state file, ensuring remote and persistent storage. ## Terraform setup The root workspace houses the overall implementation to keep things configurable and interchangeable as needed. While each Camunda component is kept as a separate module to abstract the need of each component as it's required with their base setup. If wanting to deploy multiple Camunda 8 setups, it may make sense to abstract the root workspace to a common module as well to allow easier scaling. If not otherwise indicated, the `.tf` file is corresponding to the [root workspace path](https://github.com/camunda/camunda-deployment-references/tree/main/aws/containers/ecs-single-region-fargate/terraform/cluster). ### Elastic Container Service `ecs.tf` contains the ECS cluster, which is just a logical component to group ECS resources. `../../modules/ecs/fargate/orchestration-cluster` is the main component `Orchestration Cluster` of Camunda and contains the definitions for: - ECS Service and task definition - Defines the base setup for the Orchestration Cluster, including the node ID provider, EFS configuration, and initial cluster endpoints. - Automatically sets the Zeebe cluster size based on the task count. - Resolves initial contact points using DNS with multiple A records instead of requiring explicit Zeebe Broker addresses. - Task-specific IAM role - Grants access to AWS services required by this component, such as the S3 bucket and Aurora PostgreSQL. - S3 bucket - Used by the ECS-specific node ID provider. - CloudWatch log group - Used for Orchestration Cluster logs. - Can be shared with other Camunda components that have a one-to-one relationship with the Orchestration Cluster, such as Connectors. - Networking configuration - Integrates with ECS Service Connect and Amazon Route 53 to enable access from within the VPC, including from resources outside the ECS cluster (for example, EC2 instances or Kubernetes clusters). - Load balancer configuration - Adds listener rules to a shared load balancer for the Orchestration Cluster and Connectors. - EFS file system The base terraform documentation for this module can be found [alongside the repository](https://github.com/camunda/camunda-deployment-references/tree/main/aws/modules/ecs/fargate/orchestration-cluster). `../../modules/ecs/fargate/connectors` is a secondary component `Connectors` and contains the definitions for: - ECS Service and Task definition - Task specific IAM role to allow access to AWS services isolated to this component - Load Balancer related configurations to add listener rules to a shared Load Balancer between Orchestration Cluster and Connectors The base terraform documentation for this module can be found [alongside the repository](https://github.com/camunda/camunda-deployment-references/tree/main/aws/modules/ecs/fargate/connectors). `camunda.tf` contains the module invocations with an example base configuration for the Orchestration Cluster and Connectors: - Aurora PostgreSQL configuration with the [AWS JDBC Wrapper](https://github.com/aws/aws-advanced-jdbc-wrapper) that comes as part of the Camunda distribution - Basic authentication Admin setup - Admin user with random password - Connectors user with random password configured and pre-configured for Connectors to consume to connect to the Orchestration Cluster In `camunda.tf` you can pass in any configuration adjustment required for the component or increase the resources. A few configuration options as mentioned above are kept as part of the modules to ensure the user can't interfere with the base setup. If you need to adjust those, then you have to adjust those in your copy of the modules. ### Aurora PostgreSQL :::info Optional module If you do not want to use this module, you can skip this section. However, you will need to adjust the remaining steps to remove any references to it. If you choose not to use this module, you must provide your own PostgreSQL, Elasticsearch or OpenSearch instance and make required config adjustments. Additionally, be sure to delete the `postgres.tf` and `postgres_seed.tf` file in your reference copy—otherwise, the resources defined in it will still be created. Additionally, remove the references for the IAM access in `iam.tf` following the `RDS IAM Auth Support` section. ::: `postgres.tf` provisions an Aurora PostgreSQL cluster with a pre-created `camunda` database and admin credentials saved in the AWS Secrets Manager. This Aurora PostgreSQL cluster is used as secondary storage for the Orchestration Cluster. `postgres_seed.tf` provisions a temporary ECS task to pre-seed the database for IAM authentication to work. The Aurora PostgreSQL cluster is not easily accessible from the outside as we don't expose it, therefore a workaround is required to do the initial user creation for the IAM authentication to work instead of using hard-coded username/password combinations. If you're fine with username/password, you can remove the `postgres_seed.tf` and reuse the admin user that was created on creation to configure the Orchestration Cluster to consume said user. ### Miscellaneous Resources `registry-auth.tf` contains the basics to create a secret via the AWS Secrets Manager for any kind of registry to access the Camunda images or bypass rate limitations. `lb.tf` contains the creation of the main Network Load Balancer (NLB) and the Application Load Balancer (ALB). `iam.tf` contains various IAM roles and policies. `secrets.tf` contains the creation of random passwords and storage in AWS Secrets Manager. `s3.tf` contains a bucket for backup purposes with versioning and encryption enabled. Access is handled through IAM role policies. ### Advanced Topics #### Camunda components configuration The Terraform implementation does not abstract any configuration and anything you need to configure for the Camunda components can be found within their own documentation. Camunda components can be configured for example via environment variables or an application YAML. ##### Environment Variables The base configuration is done via environment variables and defined directly as is in the invocation of the module. An alternative approach, still with environment variables, could be to load them from an external file. Example: ```bash title="orchestration-cluster-env" KEY=VALUE KEY2=VALUE2 KEY3=${template} ``` The file `orchestration-cluster-env` could then be loaded in Terraform via: ```hcl locals { # Combine with templatefile to replace dynamic values derived from Terraform env_lines = split("\n", templatefile("orchestration-cluster-env", { template = "SOME_TF_VALUE" })) # Splits KEY=VALUE into expected JSON env_kv_pairs = [ for line in local.env_lines : { name = trim(split("=", line)[0], " ") value = trim(join("=", slice(split("=", line), 1, length(split("=", line)))), " ") } if length(split("=", line)) > 1 # Filter out lines without '=' ] } ``` This can then be passed to the module invocation ```hcl environment_variables = local.env_kv_pairs # or mixed with the concat function ``` ##### Application YAML 1. This can either be baked into a custom image permanently by you 2. Pull application YAML on startup via init container from external store or integrate in Terraform Example: ```hcl init_container_enabled = true init_container_image = "public.ecr.aws/amazonlinux/amazonlinux:minimal" init_container_command = ["sh", "-c", "curl -fsSL https://example.com/additional-properties.yaml -o /config/additional-properties.yaml"] ``` Add this as part of your module environment usages to let Spring know to load the additional file. ```hcl { name = "SPRING_CONFIG_IMPORT" value = "file:./config/additional-properties.yaml" } ``` `/config` is a shared ephemeral volume between the init container and the running container. #### Terraform Configuration The base terraform documentation for the Orchestration can be found [alongside the repository](https://github.com/camunda/camunda-deployment-references/tree/main/aws/modules/ecs/fargate/orchestration-cluster) as well as for [Connectors](https://github.com/camunda/camunda-deployment-references/tree/main/aws/modules/ecs/fargate/connectors). Some common topics to potentially change: ##### Resources ```hcl # both modules task_cpu = 4096 task_cpu_architecture = "X86_64" task_memory = 8192 ``` The EFS file system uses the `elastic` throughput mode by default. This mode automatically scales for most workloads. If you need a fixed throughput configuration, adjust the `efs_throughput_mode` and `efs_provisioned_throughput_in_mibps` variables. Example: ```hcl # Orchestration Cluster efs_throughput_mode = "provisioned" efs_provisioned_throughput_in_mibps = 50 ``` ##### Camunda ###### Image ```hcl # both modules image = "camunda/camunda:VERSION" # "camunda/connectors-bundle:VERSION" ``` You could supply your custom registry and version this way like: ```hcl image = "ghcr.io/NAMESPACE/IMAGE_NAME:VERSION" ``` ###### Sizing ```hcl # both modules task_desired_count = X # in case of Orchestration Cluster automatically changes the `camunda.cluster.size` ``` ###### Wait for ready This flag ensures that Terraform waits until the ECS service is successfully deployed. It is useful when other components, such as Connectors, depend on the Orchestration Cluster, because it prevents them from being deployed before the cluster is ready. If you disable this flag, Terraform deploys all resources at once without waiting for service readiness. ```hcl # both modules wait_for_steady_state = true ``` #### Aurora PostgreSQL initial user seeding When wanting to use IAM authentication to simplify the authentication between Orchestration Cluster and Aurora PostgreSQL cluster then an initial seeding of the database is required to have a passwordless user with the `rds_iam` role assigned. More information about IAM authentication with Aurora can be found in the [AWS documentation](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.DBAccounts.html#UsingWithRDS.IAMDBAuth.DBAccounts.PostgreSQL). Terraform itself can't do this initial seeding as the Aurora PostgreSQL is not publicly exposed, so a workaround is needed. In our reference architecture to keep things simple and working ideally immediately, a local execution was used that triggers a one time seeding job to do the required steps as the ECS has access to Aurora PostgreSQL cluster. As previously mentioned, if you don't want to do this local execution, you can delete the `postgres_seed.tf` and remove the `depends_on` in `camunda.tf`. Alternatives are for example: - Externally supplied PostgreSQL - Rely on username / password of e.g. admin user - Manual seeding via an EC2 instance or ECS task in the same VPC or a connected VPN - Temporarily exposing the Aurora PostgreSQL cluster (not recommended) - AWS Lambda function that does the seeding - AWS Step function that does the seeding It was implemented as a `local exec` with an ECS task since we wanted to provide a fully working reference end-to-end while still relying on something like IAM authentication. #### Rolling deployments The Orchestration Cluster is stateful and overprovisioning will not help the deployment to reach a ready state quicker as we're limited by the Zeebe node-ids and brokers only becoming ready when successfully joining a cluster. Therefore, the Orchestration Cluster does a deployment of maximum `100%` of tasks and minimum `66%` to ensure quorum is kept. If using smaller task sizes, you may have to consider using `service_force_new_deployment = true` to force a new deployment as otherwise the minimum and maximum task size will block a successful update. For the Connectors task, it's kept at a maximum of `200%` and minimum of `50%` as the application is stateless and can therefore scale above the initial target during upgrades. ## Execution :::note Secret management We strongly recommend managing sensitive information using a secure secrets management tool such as HashiCorp Vault. For guidance on injecting secrets into Terraform via Vault, refer to the [Terraform Vault Secrets Injection Guide](https://developer.hashicorp.com/terraform/tutorials/secrets/secrets-vault). ::: :::info Terraform Flow Due to the `postgres_seed.tf` it is required that the machine executing it has the `AWS CLI` installed and configured to be able to start and wait for the seeding task to have finished. If that is not wanted or can't be done then please either execute it as two steps with manual seeding, fallback to username/password or supply a pre-configured secondary storage as previously mentioned. ::: 1. Open a terminal in the reference directory containing `config.tf` and the other `.tf` files. 2. Perform a final initialization to apply any changes made throughout this guide: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/common/procedure/s3-bucket/s3-bucket-tf-init.sh#L7 ``` 3. Plan the configuration files: ```bash terraform plan -out cluster.plan # describe what will be created ``` 4. After reviewing the plan, you can confirm and apply the changes: ```bash terraform apply cluster.plan # apply the creation ``` Terraform will now provision the Amazon ECS resources and the Aurora PostgreSQL cluster with all necessary configurations. This process may take approximately 20–30 minutes to complete. The Terraform flow is as follows: - Creation of the VPC and related resources, among that IAM roles - Creation of the Aurora Postgres Cluster within the VPC - Creation of the temporary Aurora Postgres seeding task and wait for it to finish - Creation of the Orchestration Cluster and wait for it to be ready - Creation of the Connectors and wait for it to be ready ## Verify connectivity to Camunda 8 Using Terraform, you can obtain the HTTP endpoint of the Application Load Balancer and interact with Camunda through the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). :::warning HTTPS To keep dependencies minimal and non-blocking for a quick start, this reference architecture omits a custom domain and TLS configuration. You can easily add TLS by attaching an AWS Certificate Manager (ACM) certificate to the Application Load Balancer (ALB). For details, see the AWS documentation on [creating an HTTPS listener](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html). Information on configuring a custom domain and understanding the ALB DNS name is available in the [Application Load Balancer documentation](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/application-load-balancers.html#dns-name). Without these additions, information is transmitted in plaintext and is therefore insecure. ::: 1. Navigate to the Terraform folder: ```sh cd terraform ``` 2. Retrieve the Application Load Balancer output: ```sh terraform output -raw alb_endpoint ``` The ALB exposes both the Orchestration and Connectors through the same port and uses listener rules with weights to determine the path they're on. - ALB:80 - `/*` routes to the Orchestration Cluster UI/REST API - `/connectors*` routes to the Connectors - ALB:9600 (optional - not recommended to be exposed publicly) - `/*` routes to the Orchestration Cluster - Connectors has the management port with the web server combined by default - NLB:26500 (TCP) - Exposes the Orchestration Cluster - Zeebe Gateway with gRPC 3. Access the URL of `alb_endpoint` which should present you a login screen. The admin user name as pre-configured in `camunda.tf` is `admin` and the password is randomly generated and can be retrieved via: ```sh terraform output -raw admin_user_password ``` 4. Use the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) to communicate with Camunda: Follow the example in the [Orchestration Cluster REST API documentation](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md) to authenticate and retrieve the cluster topology. ## Troubleshooting ### Logs Logs are by default exported to CloudWatch unless configured otherwise by you. Those are both visible in the CloudWatch dashboard and the ECS Service alongside each task. ### Accessing task or management API ECS tasks are not easily accessible without workarounds, some options are the following: - EC2 / ECS debug instance / task within the same VPC to try to ping and use the [management API](/self-managed/components/orchestration-cluster/zeebe/operations/management-api.md) - AWS VPN connected to the VPC - Lambda functions - Step functions - Temporarily exposing the management API - Temporarily set `task_enable_execute_command` to `true` and redeploy to allow accessing the running container ```sh aws ecs execute-command \ --cluster $ECS_CLUSTER \ --task $ECS_TASK_ID \ --container orchestration-cluster \ --command "/bin/sh" \ --interactive ``` You can find more information about `AWS ECS Exec` within the [AWS documentation](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-exec-run.html). For general troubleshooting assistance, consult the [operational guides troubleshooting documentation](/self-managed/operational-guides/troubleshooting.md). ## Operations ### Backup and restore The general [backup and restore procedure](/self-managed/operational-guides/backup-restore/backup-and-restore.md) applies. The backup process, itself, doesn't require changes. Restoring, however, introduces additional complexity because each broker's data directory (persistent volume) must be restored in a coordinated manner. To support this, an init container is introduced as part of the Orchestration Cluster, responsible for restoring the data directory for the broker running in that task. This mechanism corresponds to the step of [restoring the Zeebe Cluster](/self-managed/operational-guides/backup-restore/rdbms/restore.md#step-1-restore-zeebe-from-its-primary-storage-backup). This approach is implemented in the example module. Set the `restore_enabled` parameter to `true` to enable it. You can optionally provide the `restore_backup_id` parameter to target a specific backup (see [restore options when using RDBMS](/self-managed/operational-guides/backup-restore/rdbms/restore.md#restore-options)). On startup, the init container leverages the node-id provider to determine its broker ID in alignment with the other tasks. It restores the partitions associated with that broker, then blocks execution until all brokers have completed their restore operations. Afterward, the init container exits, allowing the Orchestration Cluster container to start. You must configure the init container and the Orchestration Cluster container identically. If you use environment variables, this requirement is automatically satisfied. If configuration is distributed through other mechanisms, those must also be explicitly applied to the init container. As long as the `restore_enabled` parameter remains set to `true`, the init container remains part of the task definition. After the backup has been successfully restored, subsequent executions will effectively be no-ops until the parameter is removed. :::note Camunda recommends restoring to a fresh cluster rather than reusing an existing one. A newly created cluster already has an empty S3 bucket and EFS volume, so no additional cleanup is needed. If you choose to restore into an existing cluster instead, you must manually ensure the S3 bucket configured for the node ID provider is empty and the EFS volume is fully cleared before starting the restore. ::: ## Next steps After setting up your cluster, many users typically do the following: - [Connect to an identity provider](/self-managed/components/orchestration-cluster/admin/connect-external-identity-provider.md) – integrate with an external identity system for authentication. - [Add TLS](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html) and configure a [custom domain](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/application-load-balancers.html#dns-name) for the Application Load Balancer (ALB). --- ## Amazon ECS Deploy the Camunda 8 Orchestration Cluster and Connectors to Amazon Elastic Container Service (ECS) to benefit from containerization without having to manage Kubernetes infrastructure. ## Get started Get started with Amazon ECS and Fargate: Deploy the Orchestration Cluster to Amazon ECS ## Learn the fundamentals This deployment targets a single AWS Region with multiple Availability Zones, running the Orchestration Cluster and Connectors on Amazon ECS with Fargate. Other dependencies include: - [Amazon EFS](https://aws.amazon.com/efs/) as primary storage - [Aurora PostgreSQL](https://aws.amazon.com/rds/aurora/) as secondary storage - [Amazon S3](https://aws.amazon.com/s3/) for node ID metadata and backups For more implementation details, read the [Architecture](./aws-ecs#architecture) section of our deployment guide. ## Dynamic node ID provider Camunda 8 is designed for Kubernetes StatefulSet deployments where each broker manages data on dedicated disk storage. Amazon ECS presents a challenge, as tasks are stateless by design and typically rely on external databases for state management. To deploy Camunda 8 to Amazon ECS, we introduce a dynamic node ID provider service backed by Amazon S3. This service enables each ECS task to assume the role of a Camunda 8 broker and safely manage the corresponding data in a dedicated directory on a shared EFS disk. The node ID provider operates using a lease mechanism stored in Amazon S3. A task acquires a broker role when it obtains the lease for a specific node ID. If the lease cannot be renewed, the task shuts down immediately to maintain data integrity. ## Amazon ECS vs. Amazon EKS If you're already invested in Kubernetes tooling and patterns and you want to remain in that environment, Amazon EKS might be a better option for you. More reasons to consider deploying to Amazon EKS over Amazon ECS include: - **Multi-region support:** When deploying to Amazon ECS, the only supported and tested pattern is a single AWS Region with multiple Availability Zones (multi‑AZ). All core pieces of the Orchestration Cluster are expected to live in one region. - **Performance:** The Amazon ECS with Fargate deployment uses Amazon EFS as primary storage. This may result in higher costs and lower performance than a Kubernetes setup with block storage. - **Scaling:** The autoscaling feature of ECS is not currently supported. Check out our [Amazon EKS deployment guide](../../../helm/cloud-providers/amazon/amazon-eks/amazon-eks.md) if a Kubernetes environment better suits your needs. ## Explore further resources --- ## Camunda Docker images Camunda provides [official Docker images](https://hub.docker.com/u/camunda) for all major components. ## Docker images vs. Docker Compose Docker images are suitable for production deployments. By contrast, the provided [Docker Compose files](../../quickstart/developer-quickstart/docker-compose/) are intended only for quick start, development, and testing. For production, we recommend using [Kubernetes with Helm](../helm/install/quick-install.md). Advanced users can create their own hardened Docker Compose configuration, but this requires additional effort. ## Platform support - Use the `linux/amd64` or `linux/arm64` image for production environments. - All images are publicly accessible. Docker images are supported for production only on Linux systems. Windows and macOS are supported for development environments only. ## Docker images and configuration references | Component | Docker image | Configuration docs | | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | | Orchestration Cluster | [camunda/camunda](https://hub.docker.com/r/camunda/camunda) | [Environment variables](../../components/orchestration-cluster/overview/) | | Management Identity | [camunda/identity](https://hub.docker.com/r/camunda/identity) | [Management Identity configuration](../../components/management-identity/configuration/identity-configuration-overview/) | | Optimize | [camunda/optimize](https://hub.docker.com/r/camunda/optimize) | [Optimize configuration](../../components/optimize/overview/) | | Connectors | [camunda/connectors](https://hub.docker.com/r/camunda/connectors) | [Connectors configuration](../../components/connectors/overview/) | | Connectors Bundle | [camunda/connectors-bundle](https://hub.docker.com/r/camunda/connectors-bundle) | [Connectors configuration](../../components/connectors/overview/) | | Web Modeler | [camunda/web-modeler-restapi](https://hub.docker.com/r/camunda/web-modeler-restapi)[camunda/web-modeler-websockets](https://hub.docker.com/r/camunda/web-modeler-websockets) | [Web Modeler configuration](../../components/hub/configuration/modeler-configuration.md) | | Console | [camunda/console](https://hub.docker.com/r/camunda/console) | [Console configuration](../../components/hub/configuration/configuration.md) | --- ## Helm chart parameters Helm chart parameters let you configure the components and behavior of your Camunda Self-Managed installation. The main way to customize these parameters is by using a `values.yaml` file. In Helm charts, the `values.yaml` file defines configuration for your deployment. To tailor your installation to your needs, you can override parameters in this file or provide your own values file. It's best practice to keep the original `values.yaml` unchanged and maintain a separate file with your custom settings. :::tip Templating support Some values in `values.yaml` support Go template expressions (for example, `{{ .Release.Name }}`). Values that support templating are marked with "Supports templating" in their description in `values.yaml`. This includes `podLabels`, `podAnnotations`, and `global.ingress.host`, among others. ::: The following tables show the **top-level configuration sections** in `values.yaml`. Each section controls a specific area of the chart. ### Global and orchestration cluster configuration | Section | Purpose | | --------------- | ------------------------------------------------------- | | `global` | Configures shared settings that apply across components | | `orchestration` | Configures orchestration cluster settings | For pod-level networking options such as `dnsPolicy`, `dnsConfig`, and `orchestration.hostNetwork`, see [configure pod networking](/self-managed/deployment/helm/configure/pod-networking.md). ### Other Camunda applications | Section | Purpose | | ------------ | --------------------------------------------------- | | `console` | Configures the Camunda Self-Managed Console service | | `connectors` | Configures the Connector runtime | | `identity` | Configures the Management Identity service | | `optimize` | Configures the Optimize web application | | `webModeler` | Configures the Web Modeler service | ### Bitnami subcharts Bitnami subcharts are best suited for development and testing environments unless your operations team has experience managing Bitnami chart deployments in production. For production environments, deploy infrastructure services separately from the Camunda Helm charts. This lets you use your preferred deployment methods, leverage managed services (for example, Amazon OpenSearch Service), and manage infrastructure lifecycles independently of Camunda. See [deploy required dependencies with Kubernetes operators](/self-managed/deployment/helm/configure/operator-based-infrastructure.md) for instructions on deploying PostgreSQL, Elasticsearch, and Keycloak using official operators instead of Bitnami subcharts. - `elasticsearch`: Provides an embedded Elasticsearch backend (Bitnami subchart). This can be used as a secondary storage backend for evaluations. See [secondary storage](/reference/glossary.md#secondary-storage) and [document-store backends (Elasticsearch/OpenSearch)](/reference/glossary.md#elasticsearchopensearch). - `identityKeycloak`: Provides an embedded Keycloak service for Management Identity (Bitnami subchart). - `identityPostgresql`: Provides an embedded PostgreSQL database for Management Identity (Bitnami subchart). - `webModelerPostgresql`: Provides an embedded PostgreSQL database for Web Modeler (Bitnami subchart). :::note The Helm chart supports embedded Elasticsearch for evaluations. For production, configure the secondary storage backend that fits your requirements. Depending on the component, topology, and version, you can use a document-store backend (Elasticsearch/OpenSearch) or an RDBMS-based secondary store. See [RDBMS configuration](/self-managed/concepts/databases/relational-db/configuration.md) and the glossary entry [RDBMS](/reference/glossary.md#rdbms). ::: #### Bitnami subcharts guidance **Development and testing environments**: Bitnami subcharts provide ready-to-use infrastructure components that you can deploy with Camunda applications using minimal configuration. **Production environments**: Camunda recommends deploying infrastructure services separately from the Camunda Helm charts. This approach lets you: - Use your preferred deployment method and operational tooling - Leverage managed services such as AWS RDS, Azure Database, or Google Cloud SQL - Manage infrastructure lifecycle independently of Camunda applications - Implement your organization's security, backup, and monitoring standards If you use Bitnami subcharts in production, consider [Bitnami Premium images](/self-managed/deployment/helm/configure/registry-and-images/install-bitnami-enterprise-images.md) for enhanced security patches and vendor support. Operational expertise with Bitnami chart production deployments is recommended. ### Observability | Section | Purpose | | -------------------------- | ---------------------------------------------------------- | | `prometheusServiceMonitor` | Creates a Prometheus `ServiceMonitor` resource for metrics | ## Where to find all chart parameters For a full list of supported Helm chart parameters, including default values and descriptions, see [Helm chart parameters on Artifact Hub](https://artifacthub.io/packages/helm/camunda/camunda-platform/#parameters). Check this page when installing or upgrading to ensure you use the latest options for your chart version. ### Provided values files In addition to the default `values.yaml`, the Helm chart repository includes several additional values files for special use cases. You can use these files individually or combine them with your own overrides. | File | Purpose | | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `values.yaml` | The default configuration. Includes all chart parameters with baseline values. | | `values-local.yaml` | Optimized for local development (for example, kind or Minikube). Adjusts resource requests and limits for smaller environments. | | `values-enterprise.yaml` | Switches Bitnami subcharts to Camunda Enterprise images. For Camunda Enterprise customers only. | | `values-bitnami-legacy.yaml` | Uses the archived Bitnami open-source images for subcharts instead of the default ones. Deprecated; see [bitnami/containers#83267](https://github.com/bitnami/containers/issues/83267). Provided only as a temporary transition option. | | `values-latest.yaml` | Tracks the latest versions of applications and subcharts. This may include breaking changes and is intended for early testing. | | `values-digest.yaml` | Uses the latest snapshot images referenced by digest (for internal development only). | ### Creating your own values files To customize parameters, create an override file (for example, `my-overrides.yaml`) with custom settings. This approach is recommended over editing `values.yaml` directly. ### Combining multiple values files Helm lets you specify multiple values files. You can layer them to build the configuration you need: ```bash helm install camunda camunda/camunda-platform \ -f values.yaml \ -f values-enterprise.yaml \ -f my-overrides.yaml ``` If the same parameter is defined in more than one file, the value in the last file listed takes precedence. In the example above, settings from `my-overrides.yaml` override values from both `values-enterprise.yaml` and `values.yaml`. --- ## Amazon EKS Amazon Elastic Kubernetes Service ([Amazon EKS](https://aws.amazon.com/eks/)) is a managed container service to run and scale Kubernetes applications in the cloud or on-premises. Camunda 8 Self-Managed can be deployed on any Kubernetes cluster using [Helm charts](/self-managed/deployment/helm/install/quick-install.md). Deployment requirements, including cloud provider-specific information, is available on our [Kubernetes deployment reference](/self-managed/reference-architecture/kubernetes.md). --- ## Dual-region setup (EKS) :::caution Review our [dual-region concept documentation](/self-managed/concepts/multi-region/dual-region.md) before continuing to understand the current limitations and restrictions of this blueprint setup. ::: This guide explains how to deploy two [Amazon Web Services (AWS) Elastic Kubernetes Service (EKS) clusters](https://docs.aws.amazon.com/eks/latest/userguide/what-is-eks.html) in a dual-region setup using Terraform, a widely used Infrastructure as Code (IaC) tool. The dual-region EKS clusters serve as the infrastructure foundation for running Camunda 8 with disaster recovery capabilities. For advanced EKS scenarios, see the [Amazon EKS documentation](https://docs.aws.amazon.com/eks/latest/userguide/). :::tip New to Terraform or Infrastructure as Code? Start with the [Terraform IaC documentation](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/infrastructure-as-code) and try the [interactive quick start](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/infrastructure-as-code#quick-start). Additionally, review the [single-region EKS Terraform setup](./terraform-setup.md) for the essentials of setting up an Amazon EKS cluster and configuring AWS IAM permissions. ::: ## Requirements - **AWS account** – Required to create AWS resources. See [What is an AWS account?](https://docs.aws.amazon.com/accounts/latest/reference/accounts-welcome.html). - **AWS CLI** – Command-line tool to manage AWS resources. [Install AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html). - **Terraform** – IaC tool used to provision resources. [Install Terraform](https://developer.hashicorp.com/terraform/downloads). - **kubectl** – CLI for interacting with Kubernetes clusters. [Install kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl). - **Helm** – Package manager for Kubernetes. [Install Helm](https://helm.sh/docs/intro/install/). - **AWS service quotas** – Verify your quotas before deployment: - At least 6 Elastic IPs (three per availability zone, per region). - Adequate quotas for **VPCs, EC2 instances, and storage** in both regions. - Request increases if needed via the AWS console. You pay only for used resources. See [AWS service quotas](https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html) and [Amazon EC2 service quotas](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-resource-limits.html). For the tool versions used in testing, see the repository's [.tool-versions](https://github.com/camunda/camunda-deployment-references/blob/main/.tool-versions) file. It contains an up-to-date list of versions used for testing. ### Considerations This setup provides a solid starting point for running Camunda 8 on AWS in a dual-region configuration. It is not optimized for peak performance. Use it as a foundation you can extend and adapt for production with [Infrastructure as Code (IaC) tools](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/infrastructure-as-code). - To test or develop against Camunda 8, consider signing up for our [SaaS offering](https://camunda.com/platform/). - If you already have two Amazon EKS clusters (peered together) and an Amazon S3 bucket, skip ahead to [deploy Camunda 8 via Helm charts](#3-deploy-camunda-8-via-helm-charts). :::warning Reference architectures and examples provided in this guide are not turnkey modules. Camunda recommends cloning the repository and modifying it locally. You are responsible for operating and maintaining the infrastructure. Camunda updates the reference architecture over time and changes may not be backward compatible. You can use these updates to upgrade your customized codebase as needed. ::: :::danger Cost management This guide will incur costs on your cloud provider account, specifically for the managed Kubernetes service, running Kubernetes nodes in EC2, Elastic Block Storage (EBS), traffic between regions, and S3. For more details, see [AWS EKS pricing](https://aws.amazon.com/eks/pricing/) and the [AWS Pricing Calculator](https://calculator.aws/#/). Costs vary by region. ::: ### Outcome _Infrastructure diagram for a dual-region EKS setup (click on the image to open the PDF version)_ [![Infrastructure Diagram EKS Dual-Region](./assets/eks-dual-region.jpg)](./assets/eks-dual-region.pdf) After completing this guide, you will have: - Two Amazon EKS Kubernetes clusters in two different geographic regions with each four nodes ready for the Camunda 8 dual-region installation. - The [EBS CSI driver](https://docs.aws.amazon.com/eks/latest/userguide/ebs-csi.html) installed and configured, which is used by the Camunda 8 Helm chart to create [persistent volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/). - A [VPC peering](https://docs.aws.amazon.com/vpc/latest/peering/what-is-vpc-peering.html) between the two EKS clusters, allowing cross-cluster communication between different regions. - An [Amazon Simple Storage Service](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html) (S3) bucket for [Elasticsearch backups](https://www.elastic.co/guide/en/elasticsearch/reference/current/repository-s3.html). - The [Elastic Cloud on Kubernetes (ECK) operator](https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-overview.html) installed in both clusters for managing Elasticsearch lifecycle. - ECK-managed Elasticsearch clusters running in both regions. :::note This guide uses the ECK operator as the preferred approach for managing Elasticsearch. You are not limited to this approach; you can use managed Elasticsearch services or other Elasticsearch instances. If you choose an alternative, you are responsible for adapting the scripts and configuration accordingly. ::: ## 1. Configure AWS and apply Terraform ### Obtain a copy of the reference architecture The first step is to download a copy of the reference architecture from the [GitHub repository](https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-dual-region/). This material will be used throughout the rest of this documentation. The provided reference architecture repository allows you to directly reuse and extend the existing Terraform example base. This sample implementation is flexible to extend to your own needs without the potential limitations of a Terraform module maintained by a third party. ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-dual-region/procedure/get-your-copy.sh ``` With the reference architecture copied, you can proceed with the remaining steps outlined in this documentation. Ensure that you are in the correct directory before continuing with further instructions. ### Export environment variables To streamline the execution of the subsequent commands, it is recommended to export multiple environment variables within your terminal. There are two regions (`REGION_0` and `REGION_1`), each with its own Kubernetes cluster (`CLUSTER_0` and `CLUSTER_1`). It is recommended to save these changes for future interactions with the dual-region setup. 1. Navigate to the `procedure` folder within your cloned repository: ```bash cd aws/kubernetes/eks-dual-region/procedure ``` 2. The folder provides a helper script [export_environment_prerequisites.sh](https://github.com/camunda/camunda-deployment-references/tree/main/aws/kubernetes/eks-dual-region/procedure/export_environment_prerequisites.sh) to export various environment variables to ease the interaction with a dual-region setup. Consider permanently changing this file for future interactions. 3. Adjust the environment variable values within the script to your needs. :::caution You have to choose unique namespaces for Camunda 8 installations. The namespace for Camunda 8 installation in the cluster of region 0 (`CAMUNDA_NAMESPACE_0`), needs to have a different name from the namespace for Camunda 8 installation in the cluster of region 1 (`CAMUNDA_NAMESPACE_1`). This is required for proper traffic routing between the clusters. For example, you can install Camunda 8 into `CAMUNDA_NAMESPACE_0` in `CLUSTER_0`, and `CAMUNDA_NAMESPACE_1` on the `CLUSTER_1`, where `CAMUNDA_NAMESPACE_0` != `CAMUNDA_NAMESPACE_1`. Using the same namespace names on both clusters won't work as CoreDNS won't be able to distinguish between traffic targeted at the local and remote cluster. ::: 4. Execute the script via the following command: ```bash . ./export_environment_prerequisites.sh ``` The dot is required to export those variables to your shell and not a spawned subshell. ```bash reference https://github.com/camunda/camunda-deployment-references/tree/main/aws/kubernetes/eks-dual-region/procedure/export_environment_prerequisites.sh ``` ### Dual-region EKS cluster module setup This module provides the foundational configuration for AWS access and Terraform usage. We use [Terraform modules](https://developer.hashicorp.com/terraform/language/modules) to abstract resources into reusable components and simplify infrastructure management. The [Camunda AWS EKS cluster module](https://github.com/camunda/camunda-deployment-references/tree/main/aws/modules/eks-cluster/) is publicly available and serves as a robust starting point for deploying Amazon EKS clusters. Review the module before implementation to understand its structure and capabilities. The module is locally sourced in your clone. Any changes you make to the module in your repository take effect immediately in your setup. #### Terraform files overview From your cloned repository, navigate to `aws/kubernetes/eks-dual-region/terraform/clusters`. This contains the Terraform base configuration for the dual-region setup. ##### config.tf This file contains the [backend](https://developer.hashicorp.com/terraform/language/backend) and [provider](https://developer.hashicorp.com/terraform/language/providers/configuration) configuration, meaning where to store the [Terraform state](https://developer.hashicorp.com/terraform/language/state) and which providers to use, their versions, and potential credentials. The important part of `config.tf` is the initialization of two AWS providers, as you need one per region and this is a limitation by AWS given everything is scoped to a region. ##### clusters.tf This file is using [Terraform modules](https://developer.hashicorp.com/terraform/language/modules), which allows abstracting resources into reusable components. The Terraform modules of [AWS EKS](https://github.com/camunda/camunda-deployment-references/tree/main/aws/modules/eks-cluster) are an example implementation and can be used for development purposes or as a starting reference. This contains the declaration of the two clusters. One of them has an explicit provider declaration, as otherwise everything is deployed to the default AWS provider, which is limited to a single region. ##### vpc-peering.tf For a multi-region setup, you need to have the [virtual private cloud (VPC)](https://aws.amazon.com/vpc/) peered to route traffic between regions using private IPv4 addresses and not publicly route the traffic and expose it. For more information, review the [AWS documentation on VPC peering](https://docs.aws.amazon.com/vpc/latest/peering/what-is-vpc-peering.html). For dual- or tri-region setups, VPC peering is preferred over [transit gateways](https://aws.amazon.com/transit-gateway/) as it is simpler to manage and has fewer AWS limitations. Other AWS networking options remain valid - choose what best fits your environment. For a complete comparison, review the [AWS documentation](https://docs.aws.amazon.com/whitepapers/latest/building-scalable-secure-multi-vpc-network-infrastructure/transit-vpc-solution.html#peering-vs). The previously mentioned [Camunda module](https://github.com/camunda/camunda-deployment-references/tree/main/aws/modules/eks-cluster) will automatically create a VPC per cluster. This file covers the VPC peering between the two VPCs and allow any traffic between those two by adjusting each cluster's security groups. ##### s3.tf For Elasticsearch, an S3 bucket is required to allow [creating and restoring snapshots](https://www.elastic.co/guide/en/elasticsearch/reference/current/repository-s3.html). There are [alternative ways](https://www.elastic.co/guide/en/elasticsearch/reference/current/snapshot-restore.html), but since this is focused on AWS, it makes sense to remain within the same cloud environment. This file covers the declaration of an S3 bucket to use for the backups. Additionally, a service account with access to use within the Kubernetes cluster to configure Elasticsearch to access the S3 bucket. ##### output.tf [Terraform outputs](https://developer.hashicorp.com/terraform/language/values/outputs) allow you to reuse generated values in future steps. For example, the access keys of the service account with S3 access. ##### variables.tf This file contains various variable definitions for both [local](https://developer.hashicorp.com/terraform/language/values/locals) and [input](https://developer.hashicorp.com/terraform/language/values/variables) types. The difference is that input variables require you to define the value on execution. While local variables are permanently defined, they are namely for code duplication purposes and readability. #### Preparation 1. Adjust any values in the [variables.tf](https://github.com/camunda/camunda-deployment-references/tree/main/aws/kubernetes/eks-dual-region/terraform/clusters/variables.tf) file to your liking. For example, the target regions and their name or CIDR blocks of each cluster. 2. Make sure that any adjustments are reflected in your [environment variables](#export-environment-variables) to ease the [cluster configuration](#2-preparation-for-camunda-8-installation). 3. Set up the authentication for the `AWS` provider. :::note The [AWS Terraform provider](https://registry.terraform.io/providers/hashicorp/aws/latest/docs) is required to create resources in AWS. You must configure the provider with the proper credentials before using it. You can further change the region and other preferences and explore different [authentication](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#authentication-and-configuration) methods. There are several ways to authenticate the `AWS` provider: - (Recommended) Use the [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html) to configure access. Terraform will automatically default to AWS CLI configuration when present. - Set environment variables `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`, which can be retrieved from the [AWS Console](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html). ::: ### Terraform prerequisites To manage the infrastructure for Camunda 8 on AWS using Terraform, we need to set up Terraform's backend to store the state file remotely in an S3 bucket. This ensures secure and persistent storage of the state file. :::note Advanced users may want to handle this part differently and use a different backend. The backend setup provided is an example for new users. ::: #### Set up AWS authentication The [AWS Terraform provider](https://registry.terraform.io/providers/hashicorp/aws/latest/docs) is required to create resources in AWS. Before you can use the provider, you must authenticate it using your AWS credentials. :::caution Ownership of the created resources A user who creates resources in AWS will always retain administrative access to those resources, including any Kubernetes clusters created. It is recommended to create a dedicated [AWS IAM user](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users.html) for Terraform purposes, ensuring that the resources are managed and owned by that user. ::: You can further change the region and other preferences and explore different [authentication](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#authentication-and-configuration) methods: - For development or testing purposes you can use the [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html). If you have configured your AWS CLI, Terraform will automatically detect and use those credentials. To configure the AWS CLI: ```bash aws configure ``` Enter your `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, region, and output format. These can be retrieved from the [AWS Console](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html). - For production environments, we recommend the use of a dedicated IAM user. Create [access keys](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html) for the new IAM user via the console, and export them as `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. #### Create an S3 bucket for Terraform state management Before setting up Terraform, you need to create an S3 bucket that will store the state file. This is important for collaboration and to prevent issues like state file corruption. To start, set the region as an environment variable upfront to avoid repeating it in each command: ```bash export AWS_REGION= ``` Replace `` with your chosen AWS region (for example, `eu-central-1`). Now, follow these steps to create the S3 bucket with versioning enabled: 1. Open your terminal and ensure the AWS CLI is installed and configured. 2. Run the following command to create an S3 bucket for storing your Terraform state. Make sure to use a unique bucket name and set the `AWS_REGION` environment variable beforehand: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/common/procedure/s3-bucket/s3-bucket-creation.sh ``` 3. Enable versioning on the S3 bucket to track changes and protect the state file from accidental deletions or overwrites: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/common/procedure/s3-bucket/s3-bucket-versioning.sh ``` 4. Secure the bucket by blocking public access: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/common/procedure/s3-bucket/s3-bucket-private.sh ``` 5. Verify versioning is enabled on the bucket: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/common/procedure/s3-bucket/s3-bucket-verify.sh ``` This S3 bucket will now securely store your Terraform state files with versioning enabled. #### Initialize Terraform Once your authentication is set up, you can initialize your Terraform project. The previous steps configured a dedicated S3 Bucket (`S3_TF_BUCKET_NAME`) to store your state, and the following creates a bucket key that will be used by your configuration. Configure the backend and download the necessary provider plugins: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/common/procedure/s3-bucket/s3-bucket-tf-init.sh ``` Terraform will connect to the S3 bucket to manage the state file, ensuring remote and persistent storage. ### Create the EKS clusters :::note Secret management We strongly recommend managing sensitive information using a secure secrets management solution like HashiCorp Vault. For details on how to inject secrets directly into Terraform via Vault, see the [Terraform Vault Secrets Injection Guide](https://developer.hashicorp.com/terraform/tutorials/secrets/secrets-vault). ::: 1. Open a terminal in the reference folder where `config.tf` and other `.tf` files are. ```bash cd ./aws/kubernetes/eks-dual-region/terraform/clusters/ ``` 2. Perform a final initialization for anything changed throughout the guide: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/common/procedure/s3-bucket/s3-bucket-tf-init.sh#L7 ``` 3. Plan the configuration files: ```bash terraform plan -out cluster.plan # describe what will be created ``` 4. After reviewing the plan, you can confirm and apply the changes. ```bash terraform apply cluster.plan # apply the creation ``` Terraform will now create the Amazon EKS clusters with all the necessary configurations. The completion of this process may require approximately 20-30 minutes for each component. ## 2. Preparation for Camunda 8 installation Now that you have created two peered Kubernetes clusters with Terraform, you will need to configure various components to make the dual-region setup work. ### Access the created EKS clusters To ease working with two clusters, create or update your local `kubeconfig` to contain those new contexts. Using an alias for those new clusters allows you to directly use kubectl and Helm with a particular cluster. Update or create your kubeconfig via the [AWS CLI](https://docs.aws.amazon.com/eks/latest/userguide/create-kubeconfig.html): ```bash # the alias allows for easier context switching in kubectl aws eks --region $REGION_0 update-kubeconfig --name $CLUSTER_0 --alias $CLUSTER_0 aws eks --region $REGION_1 update-kubeconfig --name $CLUSTER_1 --alias $CLUSTER_1 ``` The region and name must align with the values you have defined in Terraform. ### Configure DNS chaining DNS chaining allows for easier communication between the two clusters by forwarding DNS queries from the region 0 cluster to the region 1 cluster and vice versa. You are configuring the CoreDNS from the cluster in **Region 0** to resolve certain namespaces via **Region 1** instead of using the in-cluster DNS server. Camunda applications (e.g. Zeebe brokers) to resolve DNS record names of Camunda applications running in another cluster. #### CoreDNS configuration 1. Expose `kube-dns`, the in-cluster DNS resolver via an internal load-balancer in each cluster: ```bash kubectl --context $CLUSTER_0 apply -f https://raw.githubusercontent.com/camunda/camunda-deployment-references/refs/heads/main/aws/kubernetes/eks-dual-region/procedure/manifests/internal-dns-lb.yml kubectl --context $CLUSTER_1 apply -f https://raw.githubusercontent.com/camunda/camunda-deployment-references/refs/heads/main/aws/kubernetes/eks-dual-region/procedure/manifests/internal-dns-lb.yml ``` 2. Execute the script [generate_core_dns_entry.sh](https://github.com/camunda/camunda-deployment-references/tree/main/aws/kubernetes/eks-dual-region/procedure/generate_core_dns_entry.sh) in the folder `aws/kubernetes/eks-dual-region/procedure/` of the repository to help you generate the CoreDNS config. Make sure that you have previously exported the [environment variables](#export-environment-variables) since the script builds on top of it. ```bash ./generate_core_dns_entry.sh ``` 3. The script will retrieve the IPs of the load balancer via the AWS CLI and return the required config change. 4. The script prints the `kubectl edit` commands to change the DNS settings of each cluster inline. Copy the statement between the placeholders to edit the CoreDNS configmap in cluster 0 and cluster 1, depending on the placeholder. An alternative to inline editing is to create two copies of the file [./manifests/coredns.yml](https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-dual-region/procedure/manifests/coredns.yml), one for each cluster. Add the section generated by the script to each file. Apply the changes to each cluster with e.g. `kubectl --context cluster-london -n kube-system apply -f file.yml`. Replace the `context` parameter with your current values.
Example output :::caution For illustration purposes only. These values will not work in your environment. ::: ```bash ./generate_core_dns_entry.sh Please copy the following between ### Cluster 0 - Start ### and ### Cluster 0 - End ### and insert it at the end of your CoreDNS configmap in Cluster 0 kubectl --context cluster-london -n kube-system edit configmap coredns ### Cluster 0 - Start ### camunda-paris.svc.cluster.local:53 { errors cache 30 forward . 10.202.19.54 10.202.53.21 10.202.84.222 { force_tcp } } ### Cluster 0 - End ### Please copy the following between ### Cluster 1 - Start ### and ### Cluster 1 - End ### and insert it at the end of your CoreDNS configmap in Cluster 1 kubectl --context cluster-paris -n kube-system edit configmap coredns ### Cluster 1 - Start ### camunda-london.svc.cluster.local:53 { errors cache 30 forward . 10.192.27.56 10.192.84.117 10.192.36.238 { force_tcp } } ### Cluster 1 - End ### ```
Full configmap example :::caution For illustration purposes only. This file will not work in your environment. ::: ```yaml title="coredns-cm-london.yml" apiVersion: v1 kind: ConfigMap metadata: labels: eks.amazonaws.com/component: coredns k8s-app: kube-dns name: coredns namespace: kube-system data: Corefile: | .:53 { errors health { lameduck 5s } ready kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa } prometheus :9153 forward . /etc/resolv.conf cache 30 loop reload loadbalance } camunda-paris.svc.cluster.local:53 { errors cache 30 forward . 10.202.19.54 10.202.53.21 10.202.84.222 { force_tcp } } ```
5. Check that CoreDNS has reloaded for the changes to take effect before continuing. Make sure it contains `Reloading complete`: ```bash kubectl --context $CLUSTER_0 logs -f deployment/coredns -n kube-system kubectl --context $CLUSTER_1 logs -f deployment/coredns -n kube-system ``` #### Verify DNS chaining The script [test_dns_chaining.sh](https://github.com/camunda/camunda-deployment-references/tree/main/aws/kubernetes/eks-dual-region/procedure/test_dns_chaining.sh) within the folder `aws/kubernetes/eks-dual-region/procedure/` of the repository will help to test that the DNS chaining is working by using nginx pods and services to ping each other. 1. Execute the [test_dns_chaining.sh](https://github.com/camunda/camunda-deployment-references/tree/main/aws/kubernetes/eks-dual-region/procedure/test_dns_chaining.sh). Make sure you have previously exported the [environment variables](#export-environment-variables) as the script builds on top of it. ```bash ./test_dns_chaining.sh ``` 2. Watch how a nginx pod and service will be deployed per cluster. It will wait until the pods are ready and finally ping from nginx in cluster 0 the nginx in cluster 1 and vice versa. If it fails to contact the other nginx five times, it will fail. ### Configure a high-performance StorageClass Camunda 8 requires high IOPS for performance-critical components such as Zeebe. To achieve this, use AWS `gp3` volumes instead of the default `gp2`. This step defines a custom `StorageClass` that: - Uses `gp3` EBS volumes with optimized IOPS and throughput. - Sets a `Retain` reclaim policy. - Uses `WaitForFirstConsumer` volume binding. - Becomes the default `StorageClass` for the cluster. #### Apply the StorageClass Run the following script from the context of the `aws/kubernetes/eks-dual-region/procedure/` folder to apply the new [storage class](https://github.com/camunda/camunda-deployment-references/tree/main/aws/kubernetes/eks-dual-region/procedure/manifests/storage-class.yml) and set it as default: ```bash reference https://github.com/camunda/camunda-deployment-references/tree/main/aws/kubernetes/eks-dual-region/procedure/storageclass-configure.sh ``` To verify completion of the operation, run: ```bash reference https://github.com/camunda/camunda-deployment-references/tree/main/aws/kubernetes/eks-dual-region/procedure/storageclass-verify.sh ``` You must apply the custom `StorageClass` before installing the Camunda Helm chart so that PersistentVolumeClaims (PVCs) are provisioned with the correct performance characteristics. ## 3. Deploy Camunda 8 via Helm charts :::warning Migration from Bitnami Elasticsearch to ECK in dual-region There is currently no dedicated migration procedure for moving from the Bitnami Elasticsearch subchart to the ECK operator in a dual-region setup. If you need to perform this migration, follow the [single-region migration procedure](/self-managed/deployment/helm/operational-tasks/migration-from-bitnami/bitnami-to-operators.md) and apply it individually to each region. ::: ### Deploy Elasticsearch using ECK Elasticsearch is managed using the [Elastic Cloud on Kubernetes (ECK)](https://www.elastic.co/guide/en/cloud-on-k8s/current/index.html) operator instead of the Camunda Helm chart's built-in Elasticsearch subchart. This provides automated lifecycle management and built-in security with auto-generated credentials. For more details on ECK-based deployments, see [Elasticsearch deployment in the operator-based infrastructure guide](/self-managed/deployment/helm/configure/operator-based-infrastructure.md#elasticsearch-deployment). #### Create the S3 backup secret for Elasticsearch Elasticsearch requires an S3 bucket for data backup and restore procedures during regional failback. The ECK operator injects these credentials into the Elasticsearch keystore at startup. You can pull the data from Terraform since you exposed those via `output.tf`. 1. From the Terraform code location `aws/kubernetes/eks-dual-region/terraform/clusters`, execute the following to export the access keys to environment variables: ```bash export AWS_ACCESS_KEY_ES=$(terraform output -raw s3_aws_access_key) export AWS_SECRET_ACCESS_KEY_ES=$(terraform output -raw s3_aws_secret_access_key) ``` 2. From the folder `aws/kubernetes/eks-dual-region/procedure` of the repository, execute the script [create_elasticsearch_secrets.sh](https://github.com/camunda/camunda-deployment-references/tree/main/aws/kubernetes/eks-dual-region/procedure/create_elasticsearch_secrets.sh). This creates the ECK-compatible `elasticsearch-env-secret` in both regions with the S3 credentials. Those have previously been defined and exported via the [environment variables](#export-environment-variables). ```bash ./create_elasticsearch_secrets.sh ``` 3. Unset environment variables to reduce the risk of potential exposure: ```bash unset AWS_ACCESS_KEY_ES unset AWS_SECRET_ACCESS_KEY_ES ``` :::caution Bucket vulnerable to region outages The Elasticsearch backup [bucket is tied to a specific region](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingBucket.html). If that region becomes unavailable and you want to restore to a different region or S3 services remain disrupted, you must create a new bucket in another region and reconfigure your Elasticsearch cluster to use the new bucket. ::: #### Deploy the ECK operator and elasticsearch clusters Before deploying the Elasticsearch cluster, install the ECK operator and its Custom Resource Definitions (CRDs) in both clusters. The ECK operator manages the lifecycle of Elasticsearch resources in Kubernetes. Run [deploy.sh](https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/elasticsearch/deploy.sh) from `generic/kubernetes/operator-based/elasticsearch/`: ```bash cd generic/kubernetes/operator-based/elasticsearch export ELASTICSEARCH_CLUSTER_FILE="elasticsearch-cluster-dual-region.yml" KUBE_CONTEXT=$CLUSTER_0 ./deploy.sh KUBE_CONTEXT=$CLUSTER_1 ./deploy.sh cd - ``` This performs the following actions: - Installs the ECK CRDs. - Deploys the operator to the `elastic-system` namespace. - Waits for operator readiness. - Creates the Elasticsearch cluster in both regions using the ECK operator.
Review the Elasticsearch deploy.sh script ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/elasticsearch/deploy.sh ```
The dual-region Elasticsearch cluster manifest is located at `generic/kubernetes/operator-based/elasticsearch/elasticsearch-cluster-dual-region.yml`.
Review the Elasticsearch cluster configuration ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/elasticsearch/elasticsearch-cluster-dual-region.yml ```
For more details on the ECK operator deployment, see the [operator-based infrastructure guide](/self-managed/deployment/helm/configure/operator-based-infrastructure.md#elasticsearch-deployment). #### Synchronize Elasticsearch passwords across regions Each ECK-managed Elasticsearch cluster auto-generates its own `elasticsearch-es-elastic-user` secret with a unique password. For Zeebe exporters to authenticate against the remote region's Elasticsearch, each region needs access to the other region's password. Run [sync_elasticsearch_passwords.sh](https://github.com/camunda/camunda-deployment-references/tree/main/aws/kubernetes/eks-dual-region/procedure/sync_elasticsearch_passwords.sh) from `aws/kubernetes/eks-dual-region/procedure` to create cross-region password secrets: ```bash cd aws/kubernetes/eks-dual-region/procedure ./sync_elasticsearch_passwords.sh cd - ``` This script reads the ECK-generated passwords and creates region-specific secrets in both regions: - `elasticsearch-es-password-region-0`: password from region 0's Elasticsearch - `elasticsearch-es-password-region-1`: password from region 1's Elasticsearch These secrets are referenced by the Zeebe exporter configuration in `camunda-values.yml` to authenticate against Elasticsearch in each region. ### Camunda 8 Helm chart prerequisites Within the cloned repository, navigate to `aws/kubernetes/eks-dual-region/helm-values`. This contains a dual-region example setup. The approach is to work with layered Helm values files: - Have a base `camunda-values.yml` that is generally applicable for both Camunda installations - Two overlays that are for region 0 and region 1 installations ##### camunda-values.yml This forms the base layer that contains the basic required setup, which applies to both regions. Key changes of the dual-region setup: - `global.multiregion.regions: 2` - Indicates the use for two regions - `global.security.authentication.method: basic` - Uses Basic authentication for inter-component communication since Management Identity (Keycloak) is not deployed in dual-region. - `global.identity.auth.enabled: false` - Management Identity is not currently supported. For more details, see the [limitations section](/self-managed/concepts/multi-region/dual-region.md#limitations) on the dual-region concept page. - `identity.enabled: false` - Management Identity is currently not supported. - `optimize.enabled: false` - Optimize is not currently supported and depends on Management Identity. - `orchestration.exporters.zeebe.enabled: false` - Disables the automatic Elasticsearch Exporter configuration in the Helm chart. This exporter was previously used with Optimize and earlier setups. - `orchestration.exporters.camunda.enabled: false` - Disables the automatic Camunda Exporter configuration in the Helm chart. Values are supplied manually through environment variables. - `orchestration.env` - `CAMUNDA_CLUSTER_INITIALCONTACTPOINTS` - These are the contact points for the brokers to know how to form the cluster. Find more information on what the variable means in [setting up a cluster](../../../../../components/orchestration-cluster/zeebe/operations/setting-up-a-cluster.md). - `CAMUNDA_DATA_EXPORTERS_CAMUNDAREGION0_ARGS_CONNECT_URL` - The Elasticsearch endpoint for region 0 (the ECK-managed **headless** service, for example: `elasticsearch-es-masters` for a cluster named `elasticsearch`). The headless service is required because VPN/peering routes pod IPs, not ClusterIPs — so DNS must return individual pod addresses that are routable cross-cluster. You can discover the exact service name with `kubectl get svc -n `. - `CAMUNDA_DATA_EXPORTERS_CAMUNDAREGION1_ARGS_CONNECT_URL` - The Elasticsearch endpoint for region 1 (the ECK-managed **headless** service, for example: `elasticsearch-es-masters` for a cluster named `elasticsearch`). - `CAMUNDA_DATA_EXPORTERS_CAMUNDAREGION0_CLASSNAME` - `io.camunda.exporter.CamundaExporter` explicitly creates the new Camunda Exporter. - `CAMUNDA_DATA_EXPORTERS_CAMUNDAREGION1_CLASSNAME` - `io.camunda.exporter.CamundaExporter` explicitly creates the new Camunda Exporter. - `CAMUNDA_DATA_EXPORTERS_CAMUNDAREGION0_ARGS_CONNECT_USERNAME` / `CAMUNDA_DATA_EXPORTERS_CAMUNDAREGION0_ARGS_CONNECT_PASSWORD` - Elasticsearch authentication credentials for region 0. The password is sourced from the `elasticsearch-es-password-region-0` secret created by the [password synchronization step](#synchronize-elasticsearch-passwords-across-regions). - `CAMUNDA_DATA_EXPORTERS_CAMUNDAREGION1_ARGS_CONNECT_USERNAME` / `CAMUNDA_DATA_EXPORTERS_CAMUNDAREGION1_ARGS_CONNECT_PASSWORD` - Elasticsearch authentication credentials for region 1. The password is sourced from the `elasticsearch-es-password-region-1` secret. - A cluster of eight Zeebe brokers (four in each of the regions) is recommended for the dual-region setup - `orchestration.clusterSize: 8` - `orchestration.partitionCount: 8` - `orchestration.replicationFactor: 4` - Elasticsearch is managed via the ECK operator and configured through a separate manifest (`elasticsearch-cluster-dual-region.yml`), not via the Helm chart's built-in Elasticsearch subchart. The Elasticsearch overlay (`camunda-elastic-values.yml`) disables the built-in Bitnami subchart and configures the **local** Elasticsearch connection (URL and authentication) for components that **read** data (orchestration secondary storage, Optimize). This is distinct from the cross-cluster Elasticsearch exporter URLs configured via environment variables above, which handle **writing** data across regions. ##### region0/camunda-values.yml This overlay contains the multi-region identification for the cluster in region 0. ##### region1/camunda-values.yml This overlay contains the multi-region identification for the cluster in region 1. ### Configure Zeebe environment variables :::caution You must change the following environment variables for Zeebe. The default values will not work for you and are only for illustration. ::: The base `camunda-values.yml` in `aws/kubernetes/eks-dual-region/helm-values` requires adjustments before installing the Helm chart: - `CAMUNDA_CLUSTER_INITIALCONTACTPOINTS` - `CAMUNDA_DATA_EXPORTERS_CAMUNDAREGION0_ARGS_CONNECT_URL` - `CAMUNDA_DATA_EXPORTERS_CAMUNDAREGION1_ARGS_CONNECT_URL` 1. The bash script [generate_zeebe_helm_values.sh](https://github.com/camunda/camunda-deployment-references/tree/main/aws/kubernetes/eks-dual-region/procedure/generate_zeebe_helm_values.sh) in the repository folder `aws/kubernetes/eks-dual-region/procedure/` helps generate those values. You only have to copy and replace them within the base `camunda-values.yml`. It uses the exported environment variables of the [export environment variables](#export-environment-variables) section for namespaces and regions. The script derives the number of Zeebe Brokers from your Helm values. For a dual-region setup, the default is `8` (four brokers per region). ```bash ./generate_zeebe_helm_values.sh ```
Example output :::danger For illustration purposes only. These values will not work in your environment. ::: ```bash ./generate_zeebe_helm_values.sh Use the following to set the environment variable CAMUNDA_CLUSTER_INITIALCONTACTPOINTS in the base Camunda Helm chart values file for Zeebe: - name: CAMUNDA_CLUSTER_INITIALCONTACTPOINTS value: camunda-zeebe.camunda-london.svc.cluster.local:26502,camunda-zeebe.camunda-paris.svc.cluster.local:26502 Use the following to set the environment variable CAMUNDA_DATA_EXPORTERS_CAMUNDAREGION0_ARGS_CONNECT_URL in the base Camunda Helm chart values file for Zeebe: - name: CAMUNDA_DATA_EXPORTERS_CAMUNDAREGION0_ARGS_CONNECT_URL value: http://elasticsearch-es-masters.camunda-london.svc.cluster.local:9200 Use the following to set the environment variable CAMUNDA_DATA_EXPORTERS_CAMUNDAREGION1_ARGS_CONNECT_URL in the base Camunda Helm chart values file for Zeebe. - name: CAMUNDA_DATA_EXPORTERS_CAMUNDAREGION1_ARGS_CONNECT_URL value: http://elasticsearch-es-masters.camunda-paris.svc.cluster.local:9200 ```
2. As the script suggests, replace the environment variables within `camunda-values.yml`. :::note The script generates only the environment-specific values (initial contact points and Elasticsearch URLs). The authentication variables (`CAMUNDA_DATA_EXPORTERS_*_ARGS_CONNECT_USERNAME` / `CAMUNDA_DATA_EXPORTERS_*_ARGS_CONNECT_PASSWORD`) use the cross-region secrets created in the [password synchronization step](#synchronize-elasticsearch-passwords-across-regions) and do not require modification. ::: ### Install Camunda 8 using Helm From the terminal context of `aws/kubernetes/eks-dual-region/helm-values`, and ensure you have previously exported the [environment variables](#export-environment-variables), execute the following: ```bash helm install $CAMUNDA_RELEASE_NAME $HELM_CHART_REF \ --version $HELM_CHART_VERSION \ --kube-context $CLUSTER_0 \ --namespace $CAMUNDA_NAMESPACE_0 \ -f camunda-values.yml \ -f ../../../../generic/kubernetes/operator-based/elasticsearch/camunda-elastic-values.yml \ -f region0/camunda-values.yml helm install $CAMUNDA_RELEASE_NAME $HELM_CHART_REF \ --version $HELM_CHART_VERSION \ --kube-context $CLUSTER_1 \ --namespace $CAMUNDA_NAMESPACE_1 \ -f camunda-values.yml \ -f ../../../../generic/kubernetes/operator-based/elasticsearch/camunda-elastic-values.yml \ -f region1/camunda-values.yml ``` ## 4. Verify connectivity to Camunda 8 :::info Authentication changes in 8.8+ Starting from version 8.8, the Orchestration Cluster is configured by default with [Admin](/self-managed/components/orchestration-cluster/admin/overview.md) and is protected by Basic authentication using `demo:demo` as the default username and password. ::: 1. Open a terminal and port-forward the Zeebe Gateway via `kubectl` from one of your clusters. Zeebe is stretching over both clusters and is `active-active`, meaning it doesn't matter which Zeebe Gateway to use to interact with your Zeebe cluster. ```bash kubectl --context "$CLUSTER_0" -n $CAMUNDA_NAMESPACE_0 port-forward services/$CAMUNDA_RELEASE_NAME-zeebe-gateway 8080:8080 ``` 2. Open another terminal and use e.g. `cURL` to print the Zeebe cluster topology: ``` # authentication may vary depending on your setup, the following is just an example call. curl -u demo:demo -L -X GET 'http://localhost:8080/v2/topology' \ -H 'Accept: application/json' ``` 3. Make sure that your output contains all eight brokers from the two regions:
Example output ```json reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/dual-region/procedure/check-zeebe-cluster-topology-output.json ```
## Next steps After successfully deploying Camunda 8 in a dual-region setup, consider the following next steps: - [Dual-region operational procedures](/self-managed/deployment/helm/operational-tasks/dual-region-ops.md) - Learn how to perform failover and failback operations. --- ## Install Camunda 8 on an EKS cluster This guide provides a comprehensive walkthrough for installing the Camunda 8 Helm chart on your existing AWS Kubernetes EKS cluster. It also includes instructions for setting up optional DNS configurations and other optional AWS-managed services, such as OpenSearch and PostgreSQL. Lastly you'll verify that the connection to your Self-Managed Camunda 8 environment is working. :::note Using Amazon Aurora PostgreSQL as secondary storage Use this page for the EKS cluster, networking, Ingress, and AWS-managed services. Then continue with [RDBMS example deployment](/self-managed/deployment/helm/install/helm-with-rdbms.md) and [configure RDBMS in Helm](/self-managed/deployment/helm/configure/database/rdbms.md). If you use Amazon OpenSearch Service for secondary storage, continue with the default path in this guide. ::: ## Requirements - A Kubernetes cluster; see the [eksctl](./eksctl.md) or [Terraform](./terraform-setup.md) guide. - [Helm](https://helm.sh/docs/intro/install/) - [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) to interact with the cluster. - [jq](https://jqlang.github.io/jq/download/) to interact with some variables. - [GNU envsubst](https://www.man7.org/linux/man-pages/man1/envsubst.1.html) to generate manifests. - (optional) Domain name/[hosted zone](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/hosted-zones-working-with.html) in Route53. This allows you to expose Camunda 8 and connect via community-supported [zbctl](https://github.com/camunda-community-hub/zeebe-client-go/blob/main/cmd/zbctl/zbctl.md) or [Camunda Modeler](https://camunda.com/download/modeler/). - A namespace to host Camunda. For the tool versions used, check the [.tool-versions](https://github.com/camunda/camunda-deployment-references/blob/main/.tool-versions) file in the repository. It contains an up-to-date list of versions that we also use for testing. ### Considerations While this guide is primarily tailored for UNIX systems, it can also be run under Windows by utilizing the [Windows Subsystem for Linux](https://learn.microsoft.com/windows/wsl/about). Multi-tenancy is disabled by default and is not covered further in this guide. If you decide to enable it, you may use the same PostgreSQL instance and add an extra database for multi-tenancy purposes. :::caution Optimize compatibility with OpenSearch **Migration:** The migration step will be disabled during the installation. For more information, refer to [using Amazon OpenSearch Service](/self-managed/deployment/helm/configure/database/using-external-opensearch.md). ::: ## Architecture In addition to the infrastructure diagram provided in the [Terraform setup guide](./terraform-setup.md), this section installs Camunda 8 following the architecture described in the [reference architecture](/self-managed/reference-architecture/reference-architecture.md). The architecture includes the following core components: - **Orchestration Cluster**: Core process execution engine (Zeebe, Operate, Tasklist, and Admin) - **Web Modeler and Console**: Management and design tools (Web Modeler, Console, and Management Identity) To demonstrate how to deploy with a custom domain, the following stack is also included: - **cert-manager**: Automates TLS certificate management with [Let's Encrypt](https://letsencrypt.org/) - **external-dns**: Manages DNS record in Route53 for domain ownership confirmation - **ingress-nginx**: Provides HTTP/HTTPS load balancing and routing to Kubernetes services ## Export environment variables To streamline the execution of the subsequent commands, it is recommended to export multiple environment variables. ### Export the AWS region and Helm chart version The following are the required environment variables with some example values: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/procedure/setting-region.sh ``` ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/chart-env.sh ``` - `CAMUNDA_NAMESPACE` is the Kubernetes namespace where Camunda will be installed. - `CAMUNDA_RELEASE_NAME` is the name of the Helm release associated with this Camunda installation. ### Export database values When using either standard authentication (network based or username and password) or IRSA authentication, specific environment variables must be set with valid values. Follow the guide for either [eksctl](./eksctl.md#configuration-1) or [Terraform](./terraform-setup.md#export-values-for-the-helm-chart) to set them correctly. Verify the configuration of your environment variables by running the following loop: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/procedure/check-env-variables.sh ``` ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region-irsa/procedure/check-env-variables.sh ``` ## (Optional) Ingress Setup :::info Domain or domainless installation If you do not have a domain name, external access to Camunda 8 web endpoints from outside the AWS VPC will not be possible. In this case, you may skip the DNS setup and proceed directly to [deploying Camunda 8 via Helm charts](#deploy-camunda-8-via-helm-charts). Alternatively, you can use `kubectl port-forward` to access Camunda without a domain or Ingress configuration. For more information, see the [kubectl port-forward documentation](https://kubernetes.io/docs/reference/kubectl/generated/kubectl_port-forward/). Throughout the rest of this installation guide, we will refer to configurations as **"With domain"** or **"Without domain"** depending on whether the application is exposed via a domain. ::: In this section, we provide an optional setup guide for configuring an Ingress with TLS and DNS management, allowing you to access your application through a specified domain. If you haven't set up an Ingress, refer to the [Kubernetes Ingress documentation](https://kubernetes.io/docs/concepts/services-networking/ingress/) for more details. In Kubernetes, an Ingress is an API object that manages external access to services in a cluster, typically over HTTP, and can also handle TLS encryption for secure connections. To monitor your Ingress setup using Amazon CloudWatch, you may also find the official AWS guide on [monitoring nginx workloads with CloudWatch Container Insights and Prometheus](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/ContainerInsights-Prometheus-Sample-Workloads-nginx.html) helpful. Additionally, for detailed steps on exposing Kubernetes applications with the nginx ingress controller, refer to the [official AWS tutorial](https://aws.amazon.com/fr/blogs/containers/exposing-kubernetes-applications-part-3-nginx-ingress-controller/). ### Export Values Set the following values for your Ingress configuration: ```shell reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/export-ingress-setup-vars.sh ``` Additionally, obtain these values by following the guide for either [eksctl](./eksctl.md) or [Terraform](./terraform-setup.md), as they will be needed in later steps: - `EXTERNAL_DNS_IRSA_ARN` - `CERT_MANAGER_IRSA_ARN` - `REGION` ### ingress-nginx [Ingress-nginx](https://github.com/kubernetes/ingress-nginx) is an open-source Kubernetes Ingress controller that provides a way to manage external access to services within a Kubernetes cluster. It acts as a reverse proxy and load balancer, routing incoming traffic to the appropriate services based on rules defined in the Ingress resource. The following installs `ingress-nginx` in the `ingress-nginx` namespace via Helm. For more configuration options, consult the [Helm chart](https://github.com/kubernetes/ingress-nginx/tree/main/charts/ingress-nginx). ```shell reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/procedure/install-ingress-nginx.sh ``` ### external-dns [External-dns](https://github.com/kubernetes-sigs/external-dns) is a Kubernetes add-on that automates the management of DNS records for external resources, such as load balancers or Ingress controllers. It monitors the Kubernetes resources and dynamically updates the DNS provider with the appropriate DNS records. The following installs `external-dns` in the `external-dns` namespace via Helm. For more configuration options, consult the [Helm chart](https://github.com/kubernetes-sigs/external-dns/tree/master/charts/external-dns). Consider setting `domainFilters` via `--set` to restrict access to certain hosted zones. :::tip Make sure to have `EXTERNAL_DNS_IRSA_ARN` exported prior by either having followed the [eksctl](./eksctl.md#policy-for-external-dns) or [Terraform](./terraform-setup.md#outputs) guide. ::: :::danger Uniqueness of txtOwnerId for DNS If you are already running `external-dns` in a different cluster, ensure each instance has a **unique** `txtOwnerId` for the TXT record. Without unique identifiers, the `external-dns` instances will conflict and inadvertently delete existing DNS records. In the example below, it's set to `external-dns` and should be changed if this identifier is already in use. Consult the [documentation](https://kubernetes-sigs.github.io/external-dns/v0.15.0/#note) to learn more about DNS record ownership. ::: ```shell reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/procedure/install-external-dns.sh ``` ### cert-manager [Cert-manager](https://cert-manager.io/) is an open-source Kubernetes add-on that automates the management and issuance of TLS certificates. It integrates with various certificate authorities (CAs) and provides a straightforward way to obtain, renew, and manage SSL/TLS certificates for your Kubernetes applications. To simplify the installation process, it is [recommended](https://cert-manager.io/docs/installation/helm/#3-install-customresourcedefinitions) to install the cert-manager `CustomResourceDefinition` resources before installing the chart. This separate step allows for easy uninstallation and reinstallation of cert-manager without deleting any custom resources that have been installed. ```shell reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/install-cert-manager-crds.sh ``` The following installs `cert-manager` in the `cert-manager` namespace via Helm. For more configuration options, consult the [Helm chart](https://artifacthub.io/packages/helm/cert-manager/cert-manager). The supplied settings also configure `cert-manager` to ease the certificate creation by setting a default issuer, which allows you to add a single annotation on an Ingress to request the relevant certificates. :::tip Make sure to have `CERT_MANAGER_IRSA_ARN` exported prior by either having followed the [eksctl](./eksctl.md#policy-for-cert-manager) or [Terraform](./terraform-setup.md#outputs) guide. ::: ```shell reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/procedure/install-cert-manager.sh ``` Create a `ClusterIssuer` via `kubectl` to enable cert-manager to request certificates from [Let's Encrypt](https://letsencrypt.org/): ```shell reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/procedure/install-cert-manager-issuer.sh ``` ## Identity Provider (IdP) setup ## Deploy Camunda 8 via Helm charts For more configuration options, refer to the [Helm chart documentation](https://artifacthub.io/packages/helm/camunda/camunda-platform#parameters). Additionally, explore our existing resources on the [Camunda 8 Helm chart](/self-managed/deployment/helm/install/quick-install.md) and [guides](/self-managed/deployment/helm/configure/index.md). Depending of your installation path, you may use different settings. For easy and reproducible installations, we will use yaml files to configure the chart. ### 1. Create the `values.yml` file Start by creating a `values.yml` file to store the configuration for your environment. This file will contain key-value pairs that will be substituted using `envsubst`. You can find a reference example of this file here: :::note Database initialization prerequisite If you're using an external Aurora PostgreSQL database, you must create the individual component databases (Identity and Web Modeler) before installing the Helm chart. This initialization step is covered in the infrastructure setup guides: - **Terraform**: See [Configure the database and associated access](./terraform-setup.md#configure-the-database-and-associated-access) in the Terraform setup guide. - **eksctl**: See [Create the databases](./eksctl.md#create-the-databases) in the eksctl guide. Without this step, Management Identity and Web Modeler will fail to connect to their databases. ::: The following makes use of the [combined Ingress setup](/self-managed/deployment/helm/configure/ingress/ingress-setup.md#combined-ingress-setup) by deploying a single Ingress for all HTTP components and a separate Ingress for the gRPC endpoint. :::info Cert-manager annotation for domain installation The annotation `kubernetes.io/tls-acme=true` will be [interpreted by cert-manager](https://cert-manager.io/docs/usage/ingress/) and automatically results in the creation of the required certificate request, easing the setup. ::: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/helm-values/values-domain.yml ``` :::danger Exposure of the Zeebe Gateway Service For production-grade security, keep the Zeebe Gateway on a private network (no public Ingress) and access it only from internal workloads or through a secure VPN connection. This limits the attack surface and ensures workflow and job traffic remain inside your trusted network boundary. See the [VPN module setup](./terraform-setup.md#vpn-module-setup) for guidance on establishing secure remote access to a private EKS cluster. Additionally, implement fine-grained [Kubernetes NetworkPolicies](https://kubernetes.io/docs/concepts/services-networking/network-policies/) to explicitly allow only required internal components to initiate connections to the Zeebe Gateway Service. Deny all other Ingress traffic at the network layer to reduce blast radius if another workload in the cluster is compromised. ::: #### Reference the credentials in secrets Before installing the Helm chart, create Kubernetes secrets to store the database authentication credentials. To create the secrets, run the following commands: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/procedure/create-external-db-secrets.sh ``` ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/helm-values/values-no-domain.yml ``` #### Reference the credentials in secrets Before installing the Helm chart, create Kubernetes secrets to store the database authentication credentials. To create the secrets, run the following commands: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/procedure/create-external-db-secrets.sh ``` The following makes use of the [combined Ingress setup](/self-managed/deployment/helm/configure/ingress/ingress-setup.md#combined-ingress-setup) by deploying a single Ingress for all HTTP components and a separate Ingress for the gRPC endpoint. :::info Cert-manager annotation for domain installation The annotation `kubernetes.io/tls-acme=true` will be [interpreted by cert-manager](https://cert-manager.io/docs/usage/ingress/) and automatically results in the creation of the required certificate request, easing the setup. ::: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region-irsa/helm-values/values-domain.yml ``` :::danger Exposure of the Zeebe Gateway Service For production-grade security, keep the Zeebe Gateway on a private network (no public Ingress) and access it only from internal workloads or through a secure VPN connection. This limits the attack surface and ensures workflow and job traffic remain inside your trusted network boundary. See the [VPN module setup](./terraform-setup.md#vpn-module-setup) for guidance on establishing secure remote access to a private EKS cluster. Additionally, implement fine-grained [Kubernetes NetworkPolicies](https://kubernetes.io/docs/concepts/services-networking/network-policies/) to explicitly allow only required internal components to initiate connections to the Zeebe Gateway Service. Deny all other Ingress traffic at the network layer to reduce blast radius if another workload in the cluster is compromised. ::: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region-irsa/helm-values/values-no-domain.yml ``` ### 2. Configure your deployment #### Enable Enterprise components Web Modeler, Console, and Management Identity are not enabled by default in this deployment. To enable these enterprise components in an OIDC-enabled full cluster, first deploy the required infrastructure (PostgreSQL, Elasticsearch/OpenSearch, and an IdP, such as Keycloak) using the official operators, then apply the Helm values examples shown in [deploy required dependencies with operators](/self-managed/deployment/helm/configure/operator-based-infrastructure.md). #### Secondary storage options This guide includes a managed Amazon OpenSearch example path for secondary storage. Choose the backend that fits your requirements: - **Managed OpenSearch**: Use the managed Amazon OpenSearch domain provisioned in the [eksctl](./eksctl.md) or [Terraform](./terraform-setup.md) setup. - **Amazon Aurora PostgreSQL**: Use Aurora PostgreSQL as secondary storage for the Orchestration Cluster. Follow this EKS guide for the cluster, networking, Ingress, and optional AWS services, then continue with [RDBMS example deployment](/self-managed/deployment/helm/install/helm-with-rdbms.md) for the Helm workflow and [configure RDBMS in Helm](/self-managed/deployment/helm/configure/database/rdbms.md) for the values reference. RDBMS as secondary storage disables Optimize unless you also deploy Elasticsearch or OpenSearch alongside it. #### Advanced: Use Helm-chart Elasticsearch instead of managed OpenSearch For advanced deployments, you can disable managed OpenSearch and enable the Elasticsearch deployment from the Camunda Helm chart: :::caution Deprecated path The Helm-chart Elasticsearch deployment uses deprecated Bitnami subcharts. Prefer managed Elasticsearch/OpenSearch services for long-term deployments, or deploy [Elastic Cloud on Kubernetes (ECK)](/self-managed/deployment/helm/configure/operator-based-infrastructure.md#elasticsearch-deployment) if you need operator-based Elasticsearch with automated scaling, upgrades, and built-in security. :::
Show configuration changes to disable external OpenSearch usage ```yaml global: elasticsearch: enabled: true opensearch: enabled: false elasticsearch: enabled: true ```
#### Use internal PostgreSQL instead of the managed Aurora If you prefer not to use an external PostgreSQL service, you can switch to the internal PostgreSQL deployment. In this case, you will need to configure the Helm chart as follows and remove certain configurations related to the external database and service account: :::tip Alternative: Operator-based PostgreSQL deployment Instead of using Bitnami subcharts for internal PostgreSQL, consider using [CloudNativePG operator](/self-managed/deployment/helm/configure/operator-based-infrastructure.md#postgresql-deployment) for production-grade PostgreSQL clusters with automated backup, monitoring, and scaling capabilities. :::
Show configuration changes to disable external database usage ```yaml webModelerPostgresql: enabled: true webModeler: # Remove this part # restapi: # externalDatabase: # url: jdbc:aws-wrapper:postgresql://${DB_HOST}:5432/${DB_WEBMODELER_NAME} # user: ${DB_WEBMODELER_USERNAME} # ... identity: # Remove this part # externalDatabase: # enabled: true # host: ${DB_HOST} # port: 5432 # username: ${DB_IDENTITY_USERNAME} # database: ${DB_IDENTITY_NAME} # ... ```
#### Fill your deployment with actual values Once you've prepared the `values.yml` file, run the following `envsubst` command to substitute the environment variables with their actual values: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/assemble-envsubst-values.sh ``` :::note Web Modeler SMTP secret If you plan to enable Web Modeler, create the SMTP secret required for email notifications ([see how it's used by Web Modeler](/self-managed/components/hub/configuration/modeler-configuration.md#smtp--email)): ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/procedure/create-webmodeler-secret.sh ``` ::: ### 3. Install Camunda 8 using Helm Now that the `generated-values.yml` is ready, you can install Camunda 8 using Helm. Run the following command: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/install-chart.sh ``` This command: - Installs (or upgrades) Camunda using the Helm chart. - Substitutes the appropriate version using the `$CAMUNDA_HELM_CHART_VERSION` environment variable. - Applies the configuration from `generated-values.yml`. You can track the progress of the installation using the following command: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/check-deployment-ready.sh ```
Understand how each component interacts with IRSA #### Web Modeler As the Web Modeler REST API uses PostgreSQL, configure the `restapi` to use IRSA with Amazon Aurora PostgreSQL. Check the [Web Modeler database configuration](../../../../../components/hub/configuration/database.md#running-web-modeler-on-amazon-aurora-postgresql) for more details. Web Modeler already comes fitted with the [aws-advanced-jdbc-wrapper](https://github.com/awslabs/aws-advanced-jdbc-wrapper) within the Docker image. #### Identity Identity uses PostgreSQL, and `identity` is configured to use IRSA with Amazon Aurora PostgreSQL. Check the [Identity database configuration](/self-managed/components/management-identity/miscellaneous/configuration-variables.md#running-identity-on-amazon-aurora-postgresql) for more details. Identity includes [aws-advanced-jdbc-wrapper](https://github.com/awslabs/aws-advanced-jdbc-wrapper) within the Docker image. :::info Keycloak with IRSA If you deploy Keycloak via the Keycloak Operator and want it to use IRSA for database access, refer to the [official Keycloak documentation](https://www.keycloak.org/server/db#preparing-keycloak-for-amazon-aurora-postgresql) for instructions on configuring Amazon Aurora PostgreSQL with a custom JDBC wrapper. ::: #### Amazon OpenSearch Service ##### Internal database configuration The default setup is sufficient for Amazon OpenSearch Service clusters without **fine-grained access control**. Fine-grained access control adds another layer of security to OpenSearch, requiring you to add a mapping between the IAM role and the internal OpenSearch role. Visit the [AWS documentation](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/fgac.html) on fine-grained access control. There are different ways to configure the mapping within Amazon OpenSearch Service: - Via a [Terraform module](https://registry.terraform.io/modules/idealo/opensearch/aws/latest) in case your OpenSearch instance is exposed. - Via the [OpenSearch dashboard](https://opensearch.org/docs/latest/security/access-control/users-roles/). - Via the **REST API**. To authorize the IAM role in OpenSearch for access, follow these steps: Use the following `curl` command to update the OpenSearch internal database and authorize the IAM role for access. Replace placeholders with your specific values: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region-irsa/setup-opensearch-fgac.yml#L28-L48 ``` - Replace `OPENSEARCH_MASTER_USERNAME` and `OPENSEARCH_MASTER_PASSWORD` with your OpenSearch domain admin credentials. - Replace `OPENSEARCH_HOST` with your OpenSearch endpoint URL. - Replace `OPENSEARCH_ROLE_ARN` with the IAM role name created by Terraform, which is output by the `opensearch_role` module. :::note Security of Basic authentication usage **This example uses Basic authentication (username and password), which may not be the best practice for all scenarios, especially if fine-grained access control is enabled.** The endpoint used in this example is not exposed by default, so consult your OpenSearch documentation for specifics on enabling and securing this endpoint. ::: Ensure that the `iam_role_arn` of the previously created `opensearch_role` is assigned to an internal role within Amazon OpenSearch Service. For example, `all_access` on the Amazon OpenSearch Service side is a good candidate, or if required, extra roles can be created with more restrictive access.
## Verify connectivity to Camunda 8 First, we need an OAuth client to be able to connect to the Camunda 8 cluster. ### Generate an M2M token using Identity Generate an M2M token by following the steps outlined in the [Identity getting started guide](/self-managed/components/management-identity/overview.md), along with the [incorporating applications documentation](/self-managed/components/management-identity/application-user-group-role-management/applications.md). Below is a summary of the necessary instructions: 1. Open Identity in your browser at `https://${CAMUNDA_DOMAIN}/managementidentity`. You will be redirected to your IdP and prompted to log in. 2. Log in with the initial user `admin` (defined in `identity.firstUser` of the values file). Retrieve the generated password (created earlier when running the secret creation script) from the Kubernetes secret and use it to authenticate: ```shell kubectl get secret identity-secret-for-components \ --namespace "$CAMUNDA_NAMESPACE" \ -o jsonpath='{.data.identity-first-user-password}' | base64 -d; echo ``` 3. Select **Add application** and select **M2M** as the type. Assign a name like "test." 4. Select the newly created application. Then, select **Access to APIs > Assign permissions**, and select the **Orchestration API** with "read" and "write" permission. 5. Retrieve the `client-id` and `client-secret` values from the application details ```shell export ZEEBE_CLIENT_ID='client-id' # retrieve the value from the identity page of your created m2m application export ZEEBE_CLIENT_SECRET='client-secret' # retrieve the value from the identity page of your created m2m application ``` 6. Open the Orchestration Cluster Admin in your browser at `https://${CAMUNDA_DOMAIN}/admin` and log in with the user `admin` (defined in `identity.firstUser` of the values file). 7. In the Admin navigation menu, select **Roles**. 8. Either select an existing role (for example, **Admin**) or [create a new role](/components/admin/role.md) with the appropriate permissions for your use case. 9. In the selected role view, open the **Clients** tab and click **Assign client**. 10. Enter the client ID of your application created in Management Identity (for example, `test`) and click **Assign client** to save. This operation links the OIDC client to the role's permissions in the Orchestration Cluster, granting the application access to the cluster resources. For more information about managing roles and clients, see [Roles](/components/admin/role.md#manage-clients). Identity and the Orchestration cluster must be port-forwarded to be able to connect to the cluster. If using Keycloak via the Keycloak Operator, you also need to port-forward the Keycloak service. ```shell kubectl port-forward "services/$CAMUNDA_RELEASE_NAME-identity" 8085:80 --namespace "$CAMUNDA_NAMESPACE" kubectl port-forward "services/$CAMUNDA_RELEASE_NAME-zeebe-gateway" 8080:8080 --namespace "$CAMUNDA_NAMESPACE" # If using Keycloak Operator: kubectl port-forward "services/keycloak-service" 18080:18080 --namespace "$CAMUNDA_NAMESPACE" ``` 1. Open Identity in your browser at `http://localhost:8085/managementidentity`. You will be redirected to your IdP and prompted to log in. 2. Log in with the initial user `admin` (defined in `identity.firstUser` of the values file). Retrieve the generated password (created earlier when running the secret creation script) from the Kubernetes secret and use it to authenticate: ```shell kubectl get secret identity-secret-for-components \ --namespace "$CAMUNDA_NAMESPACE" \ -o jsonpath='{.data.identity-first-user-password}' | base64 -d; echo ``` 3. Select **Add application** and select **M2M** as the type. Assign a name like "test". 4. Select the newly created application. Then, select **Access to APIs > Assign permissions**, and select the **Orchestration API** with "read" and "write" permission. 5. Retrieve the `client-id` and `client-secret` values from the application details ```shell export ZEEBE_CLIENT_ID='client-id' # retrieve the value from the identity page of your created m2m application export ZEEBE_CLIENT_SECRET='client-secret' # retrieve the value from the identity page of your created m2m application ``` 6. Open the Orchestration Cluster Admin in your browser at `http://localhost:8080/admin` and log in with the user `admin` (defined in `identity.firstUser` of the values file). 7. In the Admin navigation menu, select **Roles**. 8. Either select an existing role (for example, **Admin**) or [create a new role](/components/admin/role.md) with the appropriate permissions for your use case. 9. In the selected role view, open the **Clients** tab and click **Assign client**. 10. Enter the client ID of your application created in Management Identity (for example, `test`) and click **Assign client** to save. This operation links the OIDC client to the role's permissions in the Orchestration Cluster, granting the application access to the cluster resources. For more information about managing roles and clients, see [Roles](/components/admin/role.md#manage-clients). ### Use the token For a detailed guide on generating and using a token, consult the relevant documentation on [authenticating with the Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md). Export the following environment variables: ```shell reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/export-verify-zeebe-domain.sh ``` This requires to port-forward the Zeebe Gateway to be able to connect to the cluster. ```shell kubectl port-forward "services/$CAMUNDA_RELEASE_NAME-zeebe-gateway" 8080:8080 --namespace "$CAMUNDA_NAMESPACE" ``` Export the following environment variables: ```shell reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/export-verify-zeebe-local.sh ``` Generate a temporary token to access the Orchestration Cluster REST API, then capture the value of the `access_token` property and store it as your token. Use the stored token (referred to as `TOKEN` in this case) to interact with the Orchestration Cluster REST API and display the cluster topology: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/check-zeebe-cluster-topology.sh ``` ...and results in the following output:
Example output ```json reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/check-zeebe-cluster-topology-output.json ```
Follow our existing [Modeler guide on deploying a diagram](/self-managed/components/modeler/desktop-modeler/deploy-to-self-managed.md). Below are the helper values required to be filled in Modeler: The following values are required for the OAuth authentication: - **Cluster endpoint:** `https://zeebe-$CAMUNDA_DOMAIN`, replacing `$CAMUNDA_DOMAIN` with your domain - **Client ID:** Retrieve the client ID value from the identity page of your created M2M application - **Client Secret:** Retrieve the client secret value from the Identity page of your created M2M application - **OAuth Token URL:** Your IdP's token endpoint (for example, `https://$CAMUNDA_DOMAIN/auth/realms/camunda-platform/protocol/openid-connect/token` for Keycloak), replacing `$CAMUNDA_DOMAIN` with your domain - **Audience:** `orchestration-api`, the default for Camunda 8 Self-Managed This requires port-forwarding the Zeebe Gateway to be able to connect to the cluster: ```shell kubectl port-forward "services/$CAMUNDA_RELEASE_NAME-zeebe-gateway" 26500:26500 --namespace "$CAMUNDA_NAMESPACE" ``` The following values are required for OAuth authentication: - **Cluster endpoint:** `http://localhost:26500` - **Client ID:** Retrieve the client ID value from the identity page of your created M2M application - **Client Secret:** Retrieve the client secret value from the Identity page of your created M2M application - **OAuth Token URL:** Your IdP's token endpoint (for example, `http://keycloak-service:18080/auth/realms/camunda-platform/protocol/openid-connect/token` for Keycloak Operator) - **Audience:** `orchestration-api`, the default for Camunda 8 Self-Managed
## Advanced topics The following are some advanced configuration topics to consider for your cluster: - [Cluster autoscaling](https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/cloudprovider/aws/README.md) To get more familiar with our product stack, visit the following topics: - [Operate](/components/operate/operate-introduction.md) - [Tasklist](/components/tasklist/introduction-to-tasklist.md) - [Optimize](/components/optimize/what-is-optimize.md) --- ## Deploy an EKS cluster with eksctl This guide explores the streamlined process of deploying Camunda 8 Self-Managed on Amazon Elastic Kubernetes Service (EKS) using the `eksctl` command-line tool. [Eksctl](https://eksctl.io/) is a common CLI tool for quickly creating and managing your Amazon EKS clusters and is [officially endorsed](https://docs.aws.amazon.com/eks/latest/userguide/getting-started-eksctl.html) by Amazon. While this guide is suitable for testing purposes, building a robust, scalable, and reproducible infrastructure is better achieved using Infrastructure as Code (IaC) tools like those described in the [Terraform guide](./terraform-setup.md), which offers more flexibility and control over your cloud environment. This guide provides a user-friendly approach for setting up and managing Amazon EKS clusters. It covers everything from the prerequisites, such as AWS IAM role configuration, to creating a fully functional Amazon EKS cluster and a managed Aurora PostgreSQL instance. Ideal for those seeking a practical and efficient method to deploy Camunda 8 on AWS, this guide provides detailed instructions for setting up the necessary environment and AWS IAM configurations. ## Prerequisites - An [AWS account](https://docs.aws.amazon.com/accounts/latest/reference/accounts-welcome.html) is required to create resources within AWS. - [kubectl (1.33+)](https://kubernetes.io/docs/tasks/tools/#kubectl), a CLI tool to interact with the cluster. - [AWS CLI (2.23+)](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html), a CLI tool for creating AWS resources. - [eksctl (0.215+)](https://eksctl.io/getting-started/), a CLI tool for creating and managing Amazon EKS clusters. - This guide uses GNU/Bash for all the shell commands listed. ### Considerations This is a basic setup to get started with Camunda 8 but does not reflect a high performance setup. For a better starting point towards production, we recommend utilizing [Infrastructure as Code tooling](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/infrastructure-as-code) and following our [Terraform guide](./terraform-setup.md). We refer to this architecture as the **standard installation**, which can be set up with or without a **domain** ([Ingress](https://docs.aws.amazon.com/eks/latest/userguide/alb-ingress.html)). The standard installation utilizes a username and password connection for the Camunda components (or simply relies on network isolation for certain components). This option is straightforward and easier to implement, making it ideal for environments where simplicity and rapid deployment are priorities, or where network isolation provides sufficient security. **Secondary storage options:** This guide includes a managed OpenSearch example path. RDBMS (PostgreSQL, MySQL, MariaDB, or Oracle) is also a supported secondary storage backend for the Orchestration Cluster. If you prefer to use RDBMS instead of OpenSearch, you can configure this during the Helm installation step. See [configure RDBMS in Helm](/self-managed/deployment/helm/configure/database/rdbms.md) for details. To try out Camunda 8 or develop against it, consider signing up for our [SaaS offering](https://camunda.com/platform/), or if you already have an Amazon EKS cluster, consider skipping to the [Helm guide](./eks-helm.md). While the guide is primarily tailored for UNIX systems, it can also be run under Windows by utilizing the [Windows Subsystem for Linux](https://learn.microsoft.com/windows/wsl/about). :::danger Cost management Following this guide will incur costs on your Cloud provider account, namely for the managed Kubernetes service, running Kubernetes nodes in EC2, Elastic Block Storage (EBS), and Route53. More information can be found on [AWS](https://aws.amazon.com/eks/pricing/) and their [pricing calculator](https://calculator.aws/#/) as the total cost varies per region. ::: ### Outcome This guide results in the following: - An Amazon EKS Kubernetes cluster running the latest Kubernetes version with four nodes ready for Camunda 8 installation. - Installed and configured [EBS CSI driver](https://docs.aws.amazon.com/eks/latest/userguide/ebs-csi.html), which is used by the Camunda 8 Helm chart to create [persistent volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/). - A [managed Aurora PostgreSQL 17.x](https://aws.amazon.com/rds/aurora/) instance that will be used by the Camunda 8 components. - A [managed OpenSearch domain](https://aws.amazon.com/opensearch-service/) created and configured as a secondary storage option for Camunda. Note: This guide’s example provisions a managed OpenSearch domain. Depending on the components you run and your requirements, you can instead configure an RDBMS-based secondary storage backend for supported components. See [configure RDBMS in Helm](/self-managed/deployment/helm/configure/database/rdbms.md) for details. - [IAM Roles for Service Accounts](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) (IRSA) configured and [Pod Identities](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html). - This simplifies the setup by not relying on explicit credentials, but instead allows creating a mapping between IAM roles and Kubernetes service accounts based on a trust relationship. A [blog post](https://aws.amazon.com/blogs/containers/diving-into-iam-roles-for-service-accounts/) by AWS visualizes this on a technical level. - This allows a Kubernetes service account to temporarily impersonate an AWS IAM role to interact with AWS services like S3, RDS, or Route53 without supplying explicit credentials. - [AWS Quotas](https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html) - Ensure at least **3 Elastic IPs** (one per availability zone). - Verify quotas for **VPCs, EC2 instances, and storage**. - Request increases if needed via the AWS console ([guide](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-resource-limits.html)). Costs apply only for resources you provision and use. This basic cluster setup is required to continue with the Helm setup as described in our [AWS Helm guide](./eks-helm.md). ## 1. Configure AWS and eksctl ### Set up AWS authentication Use the [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html) to run the following commands: ```bash # set your region export AWS_REGION="eu-central-1" aws configure ``` Enter your `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`, and output format. These can be retrieved from the [AWS Console](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html). :::caution Ownership of the created resources A user who creates resources in AWS will always retain administrative access to those resources, including any Kubernetes clusters. It is recommended to create a dedicated [AWS IAM user](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users.html) to ensure that the resources are managed and owned by that specific user. This ensures that the user maintains admin access to Kubernetes and associated resources unless those resources are explicitly deleted. [Create access keys](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html) for the new IAM user via the console and export them as `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` variables to use with the AWS CLI and `eksctl`. ::: ### Set up eksctl [eksctl](https://eksctl.io/) is a tool that allows the creation of clusters via a single command, but does not support all configuration options. This setup supplies a YAML file that can be used with the CLI to create the cluster preconfigured with various settings. Review the [installation guide](https://eksctl.io/installation/) for additional details. ### Configure your infrastructure In this guide, we will set up multiple environment variables to configure the components. Each component starts with a section that configures the different variables according to your needs. ## 2. EKS cluster ### Configuration ```shell ##### Kubernetes parameters # The name used for the Kubernetes cluster export CLUSTER_NAME=camunda-cluster # Your standard region that you host AWS resources in export REGION="$AWS_REGION" # Multi-zones, derived from the region export ZONES="${REGION}a ${REGION}b ${REGION}c" # The AWS Account ID export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) # CIDR range used for the VPC subnets export CIDR=10.192.0.0/16 # Optional # Default node type for the Kubernetes cluster export NODE_TYPE=m7i.xlarge # Initial node count to create the cluster with export NODE_COUNT=4 ``` ### Kubernetes secret encryption The following enables [envelope encryption](https://aws.amazon.com/about-aws/whats-new/2020/03/amazon-eks-adds-envelope-encryption-for-secrets-with-aws-kms/) to add another layer of protection to your Kubernetes secrets. We recommend enabling KMS encryption as a first step in creating the cluster. Enabling this configuration afterward can take up to 45 minutes. The KMS key is required in the [eksctl cluster YAML](#eksctl-cluster-yaml). Create AWS KMS Key via the aws-cli. For additional settings, visit the [documentation](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/kms/create-key.html). ```shell export KMS_ARN=$(aws kms create-key \ --description "Kubernetes Encryption Key" \ --query "KeyMetadata.Arn" \ --output text) ``` The variable `KMS_ARN` contains the required output. It should look something like this: `arn:aws:kms:eu-central-1:1234567890:key/aaaaaaa-bbbb-cccc-dddd-eeeeeeee`. For more information concerning the KMS encryption, refer to the [eksctl documentation](https://eksctl.io/usage/kms-encryption/). ### Create the cluster using eksctl Execute the following script, which creates a file called `cluster.yaml` with the following contents: ```shell cat <./cluster.yaml --- apiVersion: eksctl.io/v1alpha5 metadata: name: ${CLUSTER_NAME:-camunda-cluster} # e.g. camunda-cluster region: ${REGION:-eu-central-1} # e.g. eu-central-1 version: "1.33" availabilityZones: - ${REGION:-eu-central-1}c # e.g. eu-central-1c, the minimal is two distinct Availability Zones (AZs) within the region - ${REGION:-eu-central-1}b - ${REGION:-eu-central-1}a cloudWatch: clusterLogging: {} iam: vpcResourceControllerPolicy: true addons: - name: vpc-cni resolveConflicts: overwrite version: latest useDefaultPodIdentityAssociations: true - name: kube-proxy resolveConflicts: overwrite version: latest useDefaultPodIdentityAssociations: true - name: aws-ebs-csi-driver resolveConflicts: overwrite version: latest useDefaultPodIdentityAssociations: true - name: coredns resolveConflicts: overwrite version: latest useDefaultPodIdentityAssociations: true - name: eks-pod-identity-agent version: latest kind: ClusterConfig kubernetesNetworkConfig: ipFamily: IPv4 managedNodeGroups: - amiFamily: AmazonLinux2023 desiredCapacity: ${NODE_COUNT:-4} # number of default nodes spawned if no cluster autoscaler is used disableIMDSv1: true iam: withAddonPolicies: albIngress: true autoScaler: true cloudWatch: true ebs: true awsLoadBalancerController: true instanceSelector: {} instanceTypes: - ${NODE_TYPE:-m6i.xlarge} # node type that is selected as default labels: alpha.eksctl.io/cluster-name: ${CLUSTER_NAME:-camunda-cluster} # e.g. camunda-cluster alpha.eksctl.io/nodegroup-name: services maxSize: 10 # maximum node pool size for cluster autoscaler minSize: 1 # minimum node pool size for cluster autoscaler name: services privateNetworking: true releaseVersion: "" securityGroups: withLocal: null withShared: null ssh: allow: false publicKeyPath: "" tags: alpha.eksctl.io/nodegroup-name: services alpha.eksctl.io/nodegroup-type: managed volumeIOPS: 3000 volumeSize: 80 volumeThroughput: 125 volumeType: gp3 privateCluster: enabled: false skipEndpointCreation: false vpc: autoAllocateIPv6: false cidr: ${CIDR:-10.192.0.0/16} clusterEndpoints: privateAccess: false publicAccess: true manageSharedNodeSecurityGroupRules: true nat: gateway: HighlyAvailable secretsEncryption: keyARN: ${KMS_ARN} autoModeConfig: enabled: false EOF ``` With eksctl you can execute the previously created file as follows and takes 25-30 minutes. ```shell cat cluster.yaml eksctl create cluster --config-file cluster.yaml ``` ### (Optional) IAM access management Kubernetes access is divided into two distinct layers. The **first layer** involves **AWS IAM permissions**, which enable basic Amazon EKS functionalities such as using the Amazon EKS UI and generating Amazon EKS access through the AWS CLI. The **second layer** provides **cluster access**, determining the user's permissions within the Kubernetes cluster. As a result, we must initially grant the user adequate AWS IAM permissions and subsequently assign them a specific role within the Kubernetes cluster for proper access management.
First Layer: IAM Permissions A minimum set of permissions is required to gain access to an Amazon EKS cluster. These two permissions allow a user to execute `aws eks update-kubeconfig` to update the local `kubeconfig` with cluster access to the Amazon EKS cluster. The policy should look as follows and can be restricted further to specific Amazon EKS clusters if required: ```json cat <./policy-eks.json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "eks:DescribeCluster", "eks:ListClusters" ], "Resource": "*" } ] } EOF ``` Via the AWS CLI, you can run the following to create the policy above in IAM. ```shell aws iam create-policy --policy-name "BasicEKSPermissions" --policy-document file://policy-eks.json ``` The created policy `BasicEKSPermissions` has to be assigned to a group, a role, or a user to work. Consult the [AWS documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_manage-attach-detach.html#add-policy-cli) to find the correct approach for you.
Second Layer: Cluster Access By default, the user creating the Amazon EKS cluster has admin access. To allow other users to access it, we have to adjust the `aws-auth` configmap. This can either be done manually via `kubectl` or via `eksctl`. In the following sections, we explain how to do this. #### eksctl With `eksctl`, you can create an AWS IAM user to Kubernetes role mapping with the following command: ```shell eksctl create iamidentitymapping \ --cluster=$CLUSTER_NAME \ --region=$REGION \ --arn arn:aws:iam::0123456789:user/ops-admin \ --group system:masters \ --username admin ``` - `arn` is the identifier of your user. - `group` is the Kubernetes role and as an example `system:masters` is a Kubernetes group for the admin role. - `username` is either the username itself or the role name. It can also be any arbitrary value as it is used for the audit logs to identify the operation owner. Example: ```shell eksctl create iamidentitymapping \ --cluster=$CLUSTER_NAME \ --region=$REGION \ --arn arn:aws:iam::0123456789:user/ops-admin \ --group system:masters \ --username admin ``` More information about usage and other configuration options can be found in the [eksctl documentation](https://eksctl.io/usage/iam-identity-mappings/). #### kubectl The same can also be achieved by using `kubectl` and manually adding the mapping as part of the `mapRoles` or `mapUsers` section. ```shell kubectl edit configmap aws-auth -n kube-system ``` For detailed examples, review the [documentation provided by AWS](https://docs.aws.amazon.com/eks/latest/userguide/auth-configmap.html).
### Access the created EKS cluster Access the Amazon EKS cluster via the `AWS CLI` using the following command: ```shell aws eks --region "$REGION" update-kubeconfig --name "$CLUSTER_NAME" --alias "$CLUSTER_NAME" ``` After updating the kubeconfig, verify your connection to the cluster with `kubectl`: ```shell kubectl get nodes ``` Create a namespace for Camunda: ```shell export CAMUNDA_NAMESPACE="camunda" kubectl create namespace "$CAMUNDA_NAMESPACE" ``` In the remainder of the guide, we reference the `CAMUNDA_NAMESPACE` variable as the namespace to create some required resources in the Kubernetes cluster, such as secrets or one-time setup jobs. ### Check existing StorageClasses We recommend using **gp3** volumes with Camunda 8 (see [volume performance](./amazon-eks.md#volume-performance)). It may be necessary to create the `gp3` StorageClass, as the default configuration only includes **gp2**. For detailed information, refer to the [AWS documentation](https://aws.amazon.com/ebs/general-purpose/). :::danger Reclaim policy Using `reclaimPolicy: Delete` can cause **permanent data loss** if a PVC is deleted. Consider using `Retain` for production. See [troubleshooting](/self-managed/operational-guides/troubleshooting.md#zeebe-data-loss-after-pvc-deletion) for details. ::: To see the available StorageClasses in your Kubernetes cluster, including which one is set as default, use the following command: ```bash kubectl describe storageclass ``` To check if `gp3` is set as the default StorageClass, look for the annotation `storageclass.kubernetes.io/is-default-class: "true"` in the output of the previous command. If `gp3` is not installed, or is not set as the default StorageClass, complete the following steps to install it and set it as default: 1. Create the `gp3` StorageClass: ```shell cat << EOF | kubectl apply -f - --- apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: ebs-sc annotations: storageclass.kubernetes.io/is-default-class: "true" provisioner: ebs.csi.aws.com parameters: type: gp3 reclaimPolicy: Retain # CRITICAL: Prevents data loss when PVCs are deleted volumeBindingMode: WaitForFirstConsumer EOF ``` This manifest defines an `ebs-sc` StorageClass to be created. This StorageClass uses the `ebs.csi.aws.com` provisioner, which is supplied by the **aws-ebs-csi-driver** addon installed during cluster creation. For more information, refer to the [official AWS documentation](https://docs.aws.amazon.com/eks/latest/userguide/ebs-csi.html). 2. Modify the `gp2` StorageClass to mark it as a non-default StorageClass: ```shell kubectl patch storageclass gp2 -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}' ``` 3. Verify the changes by running the `kubectl get storageclass` command. After executing these commands, you will have a `gp3` StorageClass set as the default and the `gp2` StorageClass marked as non-default, provided that **gp2** was already present. ### Domain deployment requirements If you plan to deploy Camunda using an external domain associated with an external certificate, you will need to set up some IAM policies to allow both **external-dns** and **cert-manager** to interact with Route 53, which controls the DNS. By default, the cluster uses **Pod Identity** to manage IAM roles for your applications. This means that service accounts are associated with IAM roles, allowing your pods to securely access AWS resources without hardcoding credentials. For more information on configuring Pod Identity, refer to the [AWS documentation](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html). #### Enable OIDC and IAM roles for Service Accounts (IRSA) To [enable OpenID Connect (OIDC) and IAM Roles for Service Accounts (IRSA)](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html) on your cluster, complete the following steps: 1. Determine the OIDC issuer ID for your cluster. First, ensure that your EKS cluster is set up with an OIDC provider. The following command should show you the OIDC issuer: ```bash export oidc_id=$(aws eks describe-cluster --name "$CLUSTER_NAME" --query "cluster.identity.oidc.issuer" --output text | cut -d '/' -f 5) echo "$oidc_id" ``` Determine whether an IAM OIDC provider with your cluster’s issuer ID is already in your account: ```bash aws iam list-open-id-connect-providers | grep $oidc_id | cut -d "/" -f4 ``` If output is returned, an IAM OIDC provider is already set up for your cluster, so you can skip the next step. If no output is returned, you will need to set up an IAM OIDC provider for your cluster. 1. Create an IAM OIDC identity provider for your cluster with the following command: ```bash eksctl utils associate-iam-oidc-provider --region "$REGION" --cluster "$CLUSTER_NAME" --approve ``` #### Policy for external-dns The following instructions are based on the [external-dns](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/aws.md) guide concerning the AWS setup and only covers the required IAM setup. The Helm chart will be installed in the [follow-up guide](./eks-helm.md). The following relies on the previously mentioned feature around IAM Roles for Service Accounts (IRSA) to simplify the external-dns setup. The IAM policy document below allows external-dns to update Route53 resource record sets and hosted zones. You need to create this policy in AWS IAM first. In our example, we will call the policy `AllowExternalDNSUpdates`. You may fine-tune the policy to permit updates only to explicit Hosted Zone IDs. ```shell cat <./policy-dns.json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["route53:ChangeResourceRecordSets"], "Resource": ["arn:aws:route53:::hostedzone/*"] }, { "Effect": "Allow", "Action": [ "route53:ListHostedZones", "route53:ListResourceRecordSets", "route53:ListTagsForResource" ], "Resource": ["*"] } ] } EOF ``` Create AWS IAM policy with the AWS CLI: ```shell aws iam create-policy --policy-name "AllowExternalDNSUpdates" --policy-document file://policy-dns.json # example: arn:aws:iam::XXXXXXXXXXXX:policy/AllowExternalDNSUpdates export EXTERNAL_DNS_POLICY_ARN=$(aws iam list-policies \ --query 'Policies[?PolicyName==`AllowExternalDNSUpdates`].Arn' \ --output text) echo "EXTERNAL_DNS_POLICY_ARN=$EXTERNAL_DNS_POLICY_ARN" ``` The `EXTERNAL_DNS_POLICY_ARN` will be used in the next step to create a role mapping between the Kubernetes Service Account and AWS IAM Service Account. Use `eksctl` to create the required role mapping for external-dns: ```shell eksctl create iamserviceaccount \ --cluster $CLUSTER_NAME \ --name "external-dns" \ --namespace "external-dns" \ --attach-policy-arn $EXTERNAL_DNS_POLICY_ARN \ --role-name="external-dns-irsa" \ --role-only \ --approve ``` ```shell export EXTERNAL_DNS_IRSA_ARN=$(aws iam list-roles \ --query "Roles[?RoleName=='external-dns-irsa'].Arn" \ --output text) echo "EXTERNAL_DNS_IRSA_ARN=$EXTERNAL_DNS_IRSA_ARN" ``` The variable `EXTERNAL_DNS_IRSA_ARN` contains the `arn` (it should look like this: `arn:aws:iam::XXXXXXXXXXXX:role/external-dns-irsa`). Alternatively, you can deploy the Helm chart first and then use `eksctl` with the option `--override-existing-serviceaccounts` instead of `--role-only` to reconfigure the created service account. #### Policy for cert-manager The following instructions are taken from the [cert-manager](https://cert-manager.io/docs/configuration/acme/dns01/route53/) guide concerning the AWS setup and only covers the required IAM setup. The Helm chart will be installed in the [follow-up guide](./eks-helm.md). The following relies on the previously mentioned feature around IAM Roles for Service Accounts (IRSA) to simplify the cert-manager setup. The IAM policy document below allows cert-manager to update Route53 resource record sets and hosted zones. You need to create this policy in AWS IAM first. In our example, we call the policy `AllowCertManagerUpdates`. If you prefer, you may fine-tune the policy to permit updates only to explicit Hosted Zone IDs. ```shell cat <./policy-cert.json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "route53:GetChange", "Resource": "arn:aws:route53:::change/*" }, { "Effect": "Allow", "Action": [ "route53:ChangeResourceRecordSets", "route53:ListResourceRecordSets" ], "Resource": "arn:aws:route53:::hostedzone/*" }, { "Effect": "Allow", "Action": "route53:ListHostedZonesByName", "Resource": "*" } ] } EOF ``` Create AWS IAM policy with the AWS CLI: ```shell aws iam create-policy --policy-name "AllowCertManagerUpdates" --policy-document file://policy-cert.json # example: arn:aws:iam::XXXXXXXXXXXX:policy/AllowCertManagerUpdates export CERT_MANAGER_POLICY_ARN=$(aws iam list-policies \ --query 'Policies[?PolicyName==`AllowCertManagerUpdates`].Arn' \ --output text) echo "CERT_MANAGER_POLICY_ARN=$CERT_MANAGER_POLICY_ARN" ``` The `CERT_MANAGER_POLICY_ARN` is used in the next step to create a role mapping between the Amazon EKS Service Account and the AWS IAM Service Account. Use `eksctl` to create the required role mapping for cert-manager: ```shell eksctl create iamserviceaccount \ --cluster=$CLUSTER_NAME \ --name="cert-manager" \ --namespace="cert-manager" \ --attach-policy-arn=$CERT_MANAGER_POLICY_ARN \ --role-name="cert-manager-irsa" \ --role-only \ --approve ``` ```shell export CERT_MANAGER_IRSA_ARN=$(aws iam list-roles \ --query "Roles[?RoleName=='cert-manager-irsa'].Arn" \ --output text) echo "CERT_MANAGER_IRSA_ARN=$CERT_MANAGER_IRSA_ARN" ``` The variable `CERT_MANAGER_IRSA_ARN` will contain the `arn` (it should look like this: `arn:aws:iam::XXXXXXXXXXXX:role/cert-manager-irsa`). Alternatively, you can deploy the Helm chart first and then use `eksctl` with the option `--override-existing-serviceaccounts` instead of `--role-only` to reconfigure the created service account. ## 3. PostgreSQL database Creating a PostgreSQL database can be accomplished through various methods, such as using the AWS Management Console or the AWS CLI. This guide focuses on providing a reproducible setup using the CLI. For information on creating PostgreSQL using the UI, refer to the [AWS documentation](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_GettingStarted.CreatingConnecting.PostgreSQL.html). :::info Optional service If you don't want to use the Amazon RDS Aurora managed service for PostgreSQL, you can skip this section. However, note that you may need to adjust the following instructions to remove references to it. If you choose not to use this service, you'll need to either provide a managed PostgreSQL service or use the internal deployment by the Camunda Helm chart in Kubernetes. ::: The following components use the PostgreSQL database: - Keycloak - Identity - Web Modeler ### Configuration ```shell ##### Postgres parameters # Name for the Postgres DB cluster and instance export RDS_NAME=camunda-postgres # Postgres DB admin username export AURORA_USERNAME=secret_user # Postgres DB password of the admin user export AURORA_PASSWORD=camundarocks123 # The PostgreSQL version export POSTGRESQL_VERSION=17.5 # For each database, we need to generate a username, password and database name export DB_KEYCLOAK_NAME="keycloak_db" export DB_KEYCLOAK_USERNAME="keycloak-pg" export DB_KEYCLOAK_PASSWORD="CHANGE-ME-PLEASE" export DB_IDENTITY_NAME="identity_db" export DB_IDENTITY_USERNAME="identity-pg" export DB_IDENTITY_PASSWORD="CHANGE-ME-PLEASE" export DB_WEBMODELER_NAME="webmodeler_db" export DB_WEBMODELER_USERNAME="webmodeler-pg" export DB_WEBMODELER_PASSWORD="CHANGE-ME-PLEASE" ``` ### Step-by-step setup 1. Identify the VPC associated with the Amazon EKS cluster: ```shell export VPC_ID=$(aws ec2 describe-vpcs \ --query "Vpcs[?Tags[?Key=='alpha.eksctl.io/cluster-name']|[?Value=='$CLUSTER_NAME']].VpcId" \ --output text) echo "VPC_ID=$VPC_ID" ``` The variable `VPC_ID` contains the output value required for the next step (the value should look like this: `vpc-1234567890`). 2. Create a security group within the VPC to allow connections to the Aurora PostgreSQL instance: ```shell export GROUP_ID_AURORA=$(aws ec2 create-security-group \ --group-name aurora-postgres-sg \ --description "Security Group to allow the Amazon EKS cluster $CLUSTER_NAME to connect to Aurora PostgreSQL $RDS_NAME" \ --vpc-id $VPC_ID \ --query 'GroupId' \ --output text) echo "GROUP_ID_AURORA=$GROUP_ID_AURORA" ``` The variable `GROUP_ID_AURORA` contains the output (the value should look like this: `sg-1234567890`). 3. Create a security Ingress rule to allow access to PostgreSQL: ```shell aws ec2 authorize-security-group-ingress \ --group-id $GROUP_ID_AURORA \ --protocol tcp \ --port 5432 \ --cidr $CIDR ``` 4. Retrieve subnets of the VPC to create a database subnet group: ```shell export SUBNET_IDS=$(aws ec2 describe-subnets \ --filter Name=vpc-id,Values=$VPC_ID \ --query "Subnets[?Tags[?Key=='aws:cloudformation:logical-id']|[?contains(Value, 'Private')]].SubnetId" \ --output text | expand -t 1) echo "SUBNET_IDS=$SUBNET_IDS" ``` The variable `SUBNET_IDS` contains the output values of the private subnets (the value should look like this: `subnet-0123456789 subnet-1234567890 subnet-9876543210`). 5. Create a database subnet group to associate PostgreSQL within the existing VPC: ```shell aws rds create-db-subnet-group \ --db-subnet-group-name camunda-postgres \ --db-subnet-group-description "Subnet for Camunda PostgreSQL $RDS_NAME" \ --subnet-ids $(echo "$SUBNET_IDS") ``` 6. Create a PostgreSQL cluster within a private subnet of the VPC: For the latest Camunda-supported PostgreSQL engine version, check our [documentation](../../../../../../reference/supported-environments.md#camunda-8-self-managed). ```shell aws rds create-db-cluster \ --db-cluster-identifier $RDS_NAME \ --engine aurora-postgresql \ --engine-version $POSTGRESQL_VERSION \ --master-username $AURORA_USERNAME \ --master-user-password $AURORA_PASSWORD \ --vpc-security-group-ids $GROUP_ID_AURORA \ --availability-zones $(echo $ZONES) \ --db-subnet-group-name camunda-postgres ``` More configuration options can be found in the [AWS documentation](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/rds/create-db-cluster.html). 7. Wait for the PostgreSQL cluster to be ready: ```shell aws rds wait db-cluster-available \ --db-cluster-identifier $RDS_NAME ``` 8. Create a database instance within the DB cluster: Ensure that the `engine-version` matches the previously created PostgreSQL cluster. ```shell aws rds create-db-instance \ --db-instance-identifier $RDS_NAME \ --db-cluster-identifier $RDS_NAME \ --engine aurora-postgresql \ --engine-version $POSTGRESQL_VERSION \ --no-publicly-accessible \ --db-instance-class db.t3.medium ``` More configuration options can be found in the [AWS documentation](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/rds/create-db-instance.html). 9. Wait for changes to be applied: ```shell aws rds wait db-instance-available \ --db-instance-identifier $RDS_NAME ``` This command will wait until the instance is ready. ### Create the databases Now that you have a database, you need to create dedicated databases for each Camunda component along with associated users that have configured access. We will also use this step to verify connectivity to the database from the created EKS cluster. The creation of the databases will be performed by spawning a pod job in the Kubernetes cluster, using the main user to create the different databases. 1. Retrieve the writer endpoint of the DB cluster: ```shell export DB_HOST=$(aws rds describe-db-cluster-endpoints \ --db-cluster-identifier $RDS_NAME \ --query "DBClusterEndpoints[?EndpointType=='WRITER'].Endpoint" \ --output text) echo "DB_HOST=$DB_HOST" ``` 2. Create a secret that references the environment variables: ```bash kubectl create secret generic setup-db-secret --namespace "$CAMUNDA_NAMESPACE" \ --from-literal=AURORA_ENDPOINT="$DB_HOST" \ --from-literal=AURORA_PORT="5432" \ --from-literal=AURORA_DB_NAME="postgres" \ --from-literal=AURORA_USERNAME="$AURORA_USERNAME" \ --from-literal=AURORA_PASSWORD="$AURORA_PASSWORD" \ --from-literal=DB_KEYCLOAK_NAME="$DB_KEYCLOAK_NAME" \ --from-literal=DB_KEYCLOAK_USERNAME="$DB_KEYCLOAK_USERNAME" \ --from-literal=DB_KEYCLOAK_PASSWORD="$DB_KEYCLOAK_PASSWORD" \ --from-literal=DB_IDENTITY_NAME="$DB_IDENTITY_NAME" \ --from-literal=DB_IDENTITY_USERNAME="$DB_IDENTITY_USERNAME" \ --from-literal=DB_IDENTITY_PASSWORD="$DB_IDENTITY_PASSWORD" \ --from-literal=DB_WEBMODELER_NAME="$DB_WEBMODELER_NAME" \ --from-literal=DB_WEBMODELER_USERNAME="$DB_WEBMODELER_USERNAME" \ --from-literal=DB_WEBMODELER_PASSWORD="$DB_WEBMODELER_PASSWORD" ``` This command creates a secret named `setup-db-secret` and dynamically populates it with the values from your environment variables. After running the above command, you can verify that the secret was created successfully by using: ```bash kubectl get secret setup-db-secret -o yaml --namespace "$CAMUNDA_NAMESPACE" ``` This should display the secret with the base64 encoded values. 3. Save the following manifest to a file, for example, `setup-postgres-create-db.yml`: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/setup-postgres-create-db.yml ``` 4. Apply the manifest: ```bash kubectl apply -f setup-postgres-create-db.yml --namespace "$CAMUNDA_NAMESPACE" ``` Once the secret is created, the **Job** manifest from the previous step can consume this secret to securely access the database credentials. 5. Once the job is created, monitor its progress using: ```bash kubectl get job/create-setup-user-db --namespace "$CAMUNDA_NAMESPACE" --watch ``` Once the job shows as `Completed`, the users and databases will have been successfully created. 6. View the logs of the job to confirm that the users were created and privileges were granted successfully: ```bash kubectl logs job/create-setup-user-db --namespace "$CAMUNDA_NAMESPACE" ``` 7. Cleanup the resources: ```bash kubectl delete job create-setup-user-db --namespace "$CAMUNDA_NAMESPACE" kubectl delete secret setup-db-secret --namespace "$CAMUNDA_NAMESPACE" ``` Running these commands will clean up both the job and the secret, ensuring that no unnecessary resources remain in the cluster. ## 4. OpenSearch domain Creating an OpenSearch domain can be accomplished through various methods, such as using the AWS Management Console or the AWS CLI. This guide focuses on providing a reproducible setup using the CLI. For information on creating an OpenSearch domain using the UI, refer to the [AWS OpenSearch documentation](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/create-managed-domain.html). The resulting OpenSearch domain is intended for use with Camunda, the following components utilize OpenSearch: - Orchestration Cluster (Zeebe, Operate, Tasklist, Identity) - Optimize :::info Optional service If you don't want to use the Amazon OpenSearch managed service, you can skip this section. However, note that you may need to adjust the following instructions to remove references to it. If you choose not to use this service, you can either: - Provide a managed OpenSearch or Elasticsearch service, or use the internal deployment by the Camunda Helm chart in Kubernetes. - Use RDBMS (PostgreSQL, MySQL, MariaDB, or Oracle) as the secondary storage backend for the Orchestration Cluster. See [configure RDBMS in Helm](/self-managed/deployment/helm/configure/database/rdbms.md) for details. ::: :::note Migration to OpenSearch is not supported Using Amazon OpenSearch Service requires [setting up a new Camunda installation](/self-managed/setup/overview.md). Migration from previous Camunda versions or Elasticsearch environments is currently not supported. Switching between Elasticsearch and OpenSearch, in either direction, is also not supported. ::: ### Configuration ```shell ##### OpenSearch parameters # Name for the OpenSearch domain export OPENSEARCH_NAME=camunda-opensearch ``` :::caution Network based security The standard deployment for OpenSearch relies on the first layer of security, which is the Network. While this setup allows easy access, it may expose sensitive data. To enhance security, consider implementing IAM Roles for Service Accounts (IRSA) to restrict access to the OpenSearch cluster, providing a more secure environment. For more information, see the [Amazon OpenSearch Service fine-grained access control documentation](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/fgac.html#fgac-access-policies). ::: ### Step-by-step setup 1. Identify the VPC associated with the Amazon EKS cluster: ```shell export VPC_ID=$(aws ec2 describe-vpcs \ --query "Vpcs[?Tags[?Key=='alpha.eksctl.io/cluster-name']|[?Value=='$CLUSTER_NAME']].VpcId" \ --output text) echo "VPC_ID=$VPC_ID" ``` The variable `VPC_ID` contains the output value required for the next steps (the value should look like this: `vpc-1234567890`). 2. Create a security group within the VPC to allow connections to the OpenSearch domain: ```shell export GROUP_ID_OPENSEARCH=$(aws ec2 create-security-group \ --group-name opensearch-sg \ --description "Security Group to allow internal connections From EKS $CLUSTER_NAME to OpenSearch $OPENSEARCH_NAME" \ --vpc-id $VPC_ID \ --query 'GroupId' \ --output text) echo "GROUP_ID_OPENSEARCH=$GROUP_ID_OPENSEARCH" ``` The variable `GROUP_ID_OPENSEARCH` contains the output (the value should look like this: `sg-1234567890`). 3. Create a security Ingress rule to allow access to OpenSearch over HTTPS (port 443) from within the VPC: ```shell aws ec2 authorize-security-group-ingress \ --group-id $GROUP_ID_OPENSEARCH \ --protocol tcp \ --port 443 \ --cidr $CIDR ``` Ensure that the CIDR range is appropriate for your environment. OpenSearch uses `443` as the https transport port. 4. Retrieve the private subnets of the VPC: ```shell export SUBNET_IDS=$(aws ec2 describe-subnets \ --filter Name=vpc-id,Values=$VPC_ID \ --query "Subnets[?Tags[?Key=='aws:cloudformation:logical-id']|[?contains(Value, 'Private')]].SubnetId" \ --output text | expand -t 1) # format it with coma export SUBNET_IDS=$(echo "$SUBNET_IDS" | sed 's/ /,/g') echo "SUBNET_IDS=$SUBNET_IDS" ``` The variable `SUBNET_IDS` now contains the output values of the private subnets (the value should look like this: `subnet-0123456789 subnet-1234567890`). 5. Create the OpenSearch domain: ```shell aws opensearch create-domain --domain-name $OPENSEARCH_NAME \ --engine-version OpenSearch_2.19 \ --cluster-config "InstanceType=m7i.large.search,InstanceCount=3,ZoneAwarenessEnabled=true,ZoneAwarenessConfig={AvailabilityZoneCount=3}" \ --node-to-node-encryption-options Enabled=true \ --ebs-options "EBSEnabled=true,VolumeType=gp3,VolumeSize=50,Iops=3000,Throughput=125" \ --encryption-at-rest-options Enabled=true \ --access-policies "{ \"Version\": \"2012-10-17\", \"Statement\": [{\"Effect\": \"Allow\", \"Principal\": { \"AWS\": \"*\" }, \"Action\": \"es:*\", \"Resource\": \"arn:aws:es:$REGION:*:domain/$OPENSEARCH_NAME/*\" }]}" \ --vpc-options "SubnetIds=${SUBNET_IDS},SecurityGroupIds=${GROUP_ID_OPENSEARCH}" ``` - **Domain Name**: `$OPENSEARCH_NAME` is the name of the OpenSearch domain being created. - **Engine Version**: Uses OpenSearch version `2.19`. - **Cluster Configuration**: - `InstanceType=m7i.large.search` specifies the instance type for the domain. - `InstanceCount=3` creates a cluster with 3 instances. - `ZoneAwarenessEnabled=true` and `ZoneAwarenessConfig={AvailabilityZoneCount=3}` enable zone awareness and spread the instances across 3 availability zones to improve fault tolerance. - **Node-to-Node Encryption**: Encryption for traffic between nodes in the OpenSearch cluster is enabled (`Enabled=true`). - **EBS Options**: - `EBSEnabled=true` enables Elastic Block Store (EBS) for storage. - `VolumeType=gp3` specifies the volume type as `gp3` with 50 GiB of storage. - `Iops=3000` and `Throughput=125` set the IOPS and throughput for the storage. - **Encryption at Rest**: Data stored in the domain is encrypted at rest (`Enabled=true`). - **Access Policies**: The default access policy allows all actions (`es:*`) on resources within the domain for any AWS account (`"Principal": { "AWS": "*" }`). This is scoped to the OpenSearch domain resources using the `arn:aws:es:$REGION:*:domain/$OPENSEARCH_NAME/*` resource ARN. - **VPC Options**: The domain is deployed within the specified VPC, restricted to the provided subnets (`SubnetIds=${SUBNET_IDS}`) and associated security group (`SecurityGroupIds=${GROUP_ID_OPENSEARCH}`). This configuration creates a secure OpenSearch domain with encryption both in transit (between nodes) and at rest, zonal fault tolerance, and sufficient storage performance using `gp3` volumes. The access is restricted to resources in the VPC of the EKS cluster and is governed by the specified security group. :::tip The instance type `m7i.large.search` in the above example is a suggestion, and can be changed depending on your needs. ::: 6. Wait for the OpenSearch domain to be active: ```shell while [ "$(aws opensearch describe-domain --domain-name $OPENSEARCH_NAME --query 'DomainStatus.Processing' --output text)" != "False" ]; do echo "Waiting for OpenSearch domain to become availablen this can up to take 20-30 minutes..."; sleep 30; done && echo "OpenSearch domain is now available\!" ``` 7. Retrieve the endpoint of the OpenSearch domain: ```shell export OPENSEARCH_HOST=$(aws opensearch describe-domains --domain-names $OPENSEARCH_NAME --query "DomainStatusList[0].Endpoints.vpc" --output text) echo "OPENSEARCH_HOST=$OPENSEARCH_HOST" ``` This endpoint will be used to connect to your OpenSearch domain. ### Verify connectivity from within the EKS cluster To verify that the OpenSearch domain is accessible from within your Amazon EKS cluster, follow these steps: 1. Deploy a temporary pod to test connectivity: Create a temporary pod using the `amazonlinux` image in the `camunda` namespace, install `curl`, and test the connection to OpenSearch—all in a single command: ```bash kubectl run amazonlinux-opensearch -n camunda --rm -i --tty --image amazonlinux -- sh -c "curl -XGET https://$OPENSEARCH_HOST/_cluster/health" ``` 2. Verify the response: If everything is set up correctly, you should receive a response from the OpenSearch service indicating its health status. You have successfully set up an OpenSearch domain that is accessible from within your Amazon EKS cluster. For further details, refer to the [OpenSearch documentation](https://opensearch.org/docs/latest/index/). ## 5. Install Camunda 8 using the Helm chart Now that you've exported the necessary values, you can proceed with installing Camunda 8 using Helm charts. Follow the guide [Camunda 8 on Kubernetes](./eks-helm.md) for detailed instructions on deploying the platform to your Kubernetes cluster. --- ## IAM Roles for Service Accounts (IRSA) ## IRSA configuration validation of a Camunda 8 helm deployment The [c8-sm-checks](self-managed/operational-guides/troubleshooting.md#anomaly-detection-scripts) utility is designed to validate IAM Roles for Service Accounts ([IRSA](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html)) configuration in EKS Kubernetes clusters on AWS. It ensures that key components in a Camunda 8 deployment, such as PostgreSQL and OpenSearch, are properly configured to securely interact with AWS resources via the appropriate IAM roles. ### IRSA check script The `/checks/kube/aws-irsa.sh` script verifies IRSA setup in your AWS Kubernetes environment by performing two types of checks: 1. **Configuration Verification**: Ensures key IRSA configurations are correctly set, using specific checks on IAM roles, policies, and mappings to service accounts. 2. **Namespace Commands and Job Execution**: Runs commands within the specified namespace using Kubernetes jobs (if necessary) to verify network and access configurations. This utility is non-intrusive and will not alter any deployment settings. If the `-s` flag is provided, the script skips spawning debugging pods for network flow verification, which can be helpful if pod creation is restricted or not required for troubleshooting. :::info Compatibility with Helm Deployments The script relies on Helm chart values and is compatible only with deployments installed or updated through standard Helm commands. It will not work with other deployment methods, such as those using `helm template` (e.g., [ArgoCD](https://argo-cd.readthedocs.io/en/latest/faq/#after-deploying-my-helm-application-with-argo-cd-i-cannot-see-it-with-helm-ls-and-other-helm-commands)). Compatibility is confirmed for [Camunda Helm chart releases version 11 and above](https://artifacthub.io/packages/helm/camunda/camunda-platform). ::: #### Key features - **Helm values retrieval**: Extracts deployment values using Helm to ensure all required configurations are set. - **EKS and OIDC configuration check**: Confirms that EKS is configured with IAM and OIDC, matching the minimum required version for IRSA compatibility. - **Service account role validation**: For each specified component, verifies that the service account exists and has the correct IAM role annotations. - **Network access verification**: Ensures that PostgreSQL (Aurora) or OpenSearch instances are accessible from within the cluster. This step involves an `nmap` scan through a Kubernetes job. Use the `-s` option to skip this step if network flow verification is unnecessary. - **IRSA value check**: Validates that the Helm deployment values are correctly configured to use IRSA for secure service interactions with AWS. - **Aurora PostgreSQL and OpenSearch IAM configuration**: Confirms that these services support IAM login, ensuring secure access configurations. - **Access and Trust Policy verification**: Checks that access and trust policies are correctly set. Note that the script performs basic checks; if issues arise with these policies, further manual verification may be needed. - **Service Account Role association test**: Tests that the IAM role association with the service account is functioning as expected by spawning a job with the specified service account and validating the resulting ARN. This step can also be skipped using the `-s` option. - **OpenSearch Access Policy check**: Validates that the OpenSearch access policy is configured correctly to support secure connections from the cluster. #### Example usage You can find the complete usage details in the [c8-sm-checks repository](https://github.com/camunda/c8-sm-checks/). Below is a quick reference for common usage options: ```bash Usage: ./checks/kube/aws-irsa.sh [-h] [-n NAMESPACE] [-e EXCLUDE_COMPONENTS] [-p] [-l] [-s] Options: -h Display this help message -n NAMESPACE Specify the namespace to use (required) -e EXCLUDE_COMPONENTS Comma-separated list of Components to exclude from the check (reference of the component is the root key used in the chart) -p Comma-separated list of Components to check IRSA for PostgreSQL (overrides default list: identityKeycloak,identity,webModeler) -l Comma-separated list of Components to check IRSA for OpenSearch (overrides default list: orchestration,optimize) -s Disable pod spawn for IRSA and connectivity verification. By default, the script spawns jobs in the specified namespace to perform IRSA checks and network connectivity tests. These jobs use the amazonlinux:latest image and scan with nmap to verify connectivity. ``` **Example Command:** ```bash ./checks/kube/aws-irsa.sh -n camunda-primary -p "identity,webModeler" -l "orchestration" ``` In this example, the script will check **`identity`** and **`webModeler`** components (references of the component name in the helm chart) for Aurora PostgreSQL access and only **`orchestration`** for OpenSearch access in the `camunda-primary` namespace. #### Script output overview The script offers detailed output to confirm that each component is properly configured for IRSA. Below is an outline of the checks it performs and the expected output format: **Example Output:** ``` [OK] AWS CLI version 2.15.20 is compatible and user is logged in. [OK] AWS environment detected. Proceeding with the script. [INFO] Chart camunda-platform is deployed in namespace camunda-primary. [INFO] Retrieved values for Helm deployment: camunda-platform-11.0.1. [FAIL] The service account keycloak-sa does not have a valid eks.amazonaws.com/role-arn annotation. You must add it in the chart, see https://docs.camunda.io/docs/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/eks-helm/ [FAIL] RoleArn name for component 'identityKeycloak' is empty. Skipping verification. ``` The script highlights errors with the `[FAIL]` prefix, and these are directed to `stderr` for easier filtering. We recommend capturing `stderr` output to quickly identify failed configurations. If the script returns a false positive—indicating success when issues are actually present—manually review each output line to ensure reported configuration details (like Role ARNs or annotations) are accurate. For example, ensure that each service account has the correct Role ARN and associated permissions to avoid undetected issues. ### Advanced troubleshooting for IRSA configuration The troubleshooting script provides essential checks but may not capture all potential issues, particularly those related to IAM policies and configurations. If IRSA is not functioning as expected and no errors are flagged by the script, follow the steps below for deeper troubleshooting. #### Spawn a debug pod to simulate the pod environment To troubleshoot in an environment identical to your pod, deploy a debug pod with the necessary service account. Here are examples of debug manifests you can customize for your needs: - [OpenSearch client pod](https://github.com/camunda/camunda-deployment-references/blob/main/aws/modules/fixtures/opensearch-client.yml) - [PostgreSQL client pod](https://github.com/camunda/camunda-deployment-references/blob/main/aws/modules/fixtures/postgres-client.yml) 1. Adapt the manifests to use the specific `serviceAccountName` (e.g., `aurora-access-sa`) you want to test. 2. Insert a sleep timer in the command to allow time to exec into the pod for live debugging. 3. Create the pod with the `kubectl apply` command: ```bash kubectl apply -f debug-client.yaml ``` 4. Once the pod is running, connect to it with a bash shell (make sure to adjust the app label with your value): ```bash kubectl exec -it $(kubectl get pods -l app=REPLACE-WITH-LABEL -o jsonpath='{.items[0].metadata.name}') -- /bin/bash ``` 5. Inside the pod, display all environment variables to check for IAM and AWS configurations: ```bash env ``` This command will print out all environment variables, including those related to IRSA. Inside the pod, validate that key environment variables are correctly injected: - `AWS_WEB_IDENTITY_TOKEN_FILE`: Path to the token (JWT) file for WebIdentity. - `AWS_ROLE_ARN`: ARN of the associated IAM role. - `AWS_REGION`, `AWS_STS_REGIONAL_ENDPOINTS`, and other AWS configuration variables. To ensure that IRSA and role associations are functioning: - Check that the expected `AWS_ROLE_ARN` and token are present. - Decode the JWT token to validate the correct trust relationship with the service account and namespace. #### Verify OpenSearch fine-grained access control (fgac) configuration For OpenSearch clusters, ensure [fine-grained access control](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/fgac.html) is set up to allow the role’s access to the cluster. If you deployed OpenSearch with the [terraform reference architecture implementation for EKS](terraform-setup.md), fgac should already be configured. For manual deployments, follow the process outlined in the [OpenSearch configuration guide](terraform-setup.md#configure-opensearch-fine-grained-access-control) to apply similar controls. #### Confirm PostgreSQL IAM role access Verify that PostgreSQL roles are correctly configured to support IAM-based authentication. The database user should have the `rds_iam` role to allow IAM authentication. If the setup was automated with the [terraform reference architecture implementation for EKS](terraform-setup.md), the necessary access configuration should already be in place. For manual configurations, refer to [PostgreSQL configuration instructions](terraform-setup.md#configure-the-database-and-associated-access). To test connectivity: - Run a manual connection test using the [PostgreSQL client manifest](https://raw.githubusercontent.com/camunda/camunda-deployment-references/refs/heads/main/aws/modules/fixtures/postgres-client.yml). - Use `psql` within the pod to verify the correct roles are assigned. Run: ```bash SELECT * FROM pg_roles WHERE rolname=''; ``` Confirm that `rds_iam` is listed among the assigned roles. #### Validate IAM Policies for each role Both trust and permission policies are crucial in configuring IAM Roles for Service Accounts (IRSA) in AWS. Each IAM role should have policies that precisely permit necessary actions and correctly trust the relevant Kubernetes service accounts associated with your components. ##### AssumeRole policies In AWS, [AssumeRole](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html) allows a user or service to assume a role and temporarily gain permissions to execute specific actions. Each role needs an **AssumeRole policy** that precisely matches AWS requirements for the specific services and actions your components perform. For each IAM role, ensure the **trust policy** includes: 1. The correct `Service` field, allowing the pod’s service account to assume the role. 2. An `Action` for `sts:AssumeRoleWithWebIdentity`, as IRSA uses WebIdentity to enable IAM role assumption. Verify that the policy is configured according to [AWS’s role trust policy guidelines](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_principal.html) for Kubernetes IRSA. ##### Trust policies For each role, verify that the [trust policy syntax is correct](https://aws.amazon.com/fr/blogs/security/how-to-use-trust-policies-with-iam-roles/), allowing the appropriate service accounts to assume the role. Refer to AWS’s [trust policy validation tool](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_policy-validator.html) for [accurate syntax and configuration](https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-reference-policy-checks.html). ##### Permission policies Each IAM role should also have appropriate permission policies attached. These policies define what actions the role can perform on AWS resources. Verify that permission policies: - Are configured correctly to allow the necessary operations for your resources (e.g., read and write access to S3 buckets or access to RDS). - Align with your security model by only granting the minimum required permissions. The AWS’s [policy simulator](https://policysim.aws.amazon.com/) is a valuable tool for testing how permissions are applied and for spotting misconfigurations. #### If issues persist If issues remain unresolved, compare your configuration with Camunda’s [reference architecture](terraform-setup.md) deployed with Terraform. This setup has been validated to work with IRSA and contains the correct permissions. By comparing it to your setup, you may identify discrepancies that are causing your issues. ## Instance Metadata Service (IMDS) [Instance Metadata Service](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html) is a default fallback for the AWS SDK due to the [default credentials provider chain](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/credentials-chain.html). Within the context of Amazon EKS, it means a pod will automatically assume the role of a node. This can hide many problems, including whether IRSA was set up correctly or not, since it will fall back to IMDS in case of failure and hide the actual error. If nothing within your cluster relies on the implicit node role, Camunda recommends disabling it by configuring the `http_put_response_hop_limit` to 1. This decreases the default value from two to one, so pods are not allowed to assume the role of the node. ### Configure IMDS hop limit **For new node groups** that use a Terraform module such as the [Amazon EKS module](https://registry.terraform.io/modules/terraform-aws-modules/eks/aws/latest), you can define the following: ```json eks_managed_node_group_defaults { metadata_options = { http_put_response_hop_limit = 1 } } ``` :::note Enabled by default in the terraform reference architecture of EKS In the [reference architecture with terraform](terraform-setup.md), this setting is configured like that by default. ::: **For existing worker node instances**, you can modify the hop limit using the AWS CLI: ```bash aws ec2 modify-instance-metadata-options \ --instance-id \ --http-put-response-hop-limit 1 ``` Replace `` with your actual EC2 instance ID. You'll need to run this command for each worker node in your cluster. More details can be found in the [AWS documentation on modifying IMDS for existing instances](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-IMDS-existing-instances.html#modify-PUT-response-hop-limit). Overall, this will disable the role assumption of the node for the Kubernetes pod. ## Backup-related When implementing [backup and restore procedures](/self-managed/operational-guides/backup-restore/backup-and-restore.md) for **Elasticsearch** in your **Camunda** deployment, you can leverage **AWS IAM Roles for Service Accounts (IRSA)** to securely access **S3 buckets**. ### Bitnami Elasticsearch chart configuration Camunda’s Helm chart uses the [Bitnami Elasticsearch chart](https://artifacthub.io/packages/helm/bitnami/elasticsearch) as a sub-chart. If you are using this setup, IRSA can be integrated for backup operations. Following the [AWS IRSA documentation](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html), create an IAM role mapped to a Kubernetes service account with the required permissions for S3, as detailed in the [Elasticsearch documentation](https://www.elastic.co/docs/deploy-manage/tools/snapshot-and-restore/s3-repository#repository-s3-permissions). Additionally, ensure Elasticsearch is configured to recognize the IRSA token. The [Elasticsearch documentation](https://www.elastic.co/docs/deploy-manage/tools/snapshot-and-restore/s3-repository#iam-kubernetes-service-accounts) outlines this requirement for official Elasticsearch images. Once the IRSA setup is complete, configure the Bitnami Elasticsearch chart in your Camunda Helm chart by adjusting your `values.yaml` as follows: ```yaml elasticsearch: enabled: true master: serviceAccount: create: true annotations: eks.amazonaws.com/role-arn: arn:aws:iam:::role/ initScripts: irsa_access_init_script.sh: | #!/bin/sh mkdir -p "/opt/bitnami/elasticsearch/config/repository-s3" ln -s $AWS_WEB_IDENTITY_TOKEN_FILE "/opt/bitnami/elasticsearch/config/repository-s3/aws-web-identity-token-file" ``` The values `` and `` are based on the [AWS IRSA documentation](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html). The init script remains consistent across environments, as the Elasticsearch S3 plugin expects the credentials at the fixed path `repository-s3`. This path is **not configurable**. :::info `$AWS_WEB_IDENTITY_TOKEN_FILE` is automatically injected into the pod by EKS when the pod is using a service account annotated with a valid `eks.amazonaws.com/role-arn`. ::: --- ## Deploy an EKS cluster with Terraform This guide explains how to provision an [Amazon Web Services (AWS) Elastic Kubernetes Service (EKS) cluster](https://docs.aws.amazon.com/eks/latest/userguide/what-is-eks.html) using Terraform, a widely used Infrastructure as Code (IaC) tool. The EKS cluster serves as the infrastructure foundation for running Camunda 8. Camunda recommends this approach for a robust, sustainable setup. For a quicker trial or proof of concept, or if the Terraform module doesn’t meet your needs, see [Deploy an EKS cluster with eksctl](./eksctl.md). For advanced EKS scenarios, see the [Amazon EKS documentation](https://docs.aws.amazon.com/eks/latest/userguide/). :::tip New to Terraform or Infrastructure as Code? Start with the [Terraform IaC documentation](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/infrastructure-as-code) and try the [interactive quick start](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/infrastructure-as-code#quick-start). ::: ## Requirements - **AWS account** – Required to create AWS resources. See [What is an AWS account?](https://docs.aws.amazon.com/accounts/latest/reference/accounts-welcome.html). - **AWS CLI** – Command-line tool to manage AWS resources. [Install AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html). - **Terraform** – IaC tool used to provision resources. [Install Terraform](https://developer.hashicorp.com/terraform/downloads). - **kubectl** – CLI for interacting with Kubernetes clusters. [Install kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl). - **jq** – Lightweight JSON processor. [Download jq](https://jqlang.github.io/jq/download/). - **IAM Roles for Service Accounts (IRSA)** – Configure IRSA to map IAM roles to Kubernetes service accounts. This removes the need for long-lived credentials and lets Kubernetes services assume IAM roles to interact with AWS services (for example, S3, RDS, Route 53). - See the AWS [IRSA deep dive](https://aws.amazon.com/blogs/containers/diving-into-iam-roles-for-service-accounts/). - IRSA is recommended as an [EKS best practice](https://aws.github.io/aws-eks-best-practices/security/docs/iam/). - **AWS service quotas** – Verify your quotas before deployment: - At least 3 Elastic IPs (one per availability zone). - Adequate quotas for **VPCs, EC2 instances, and storage**. - Request increases if needed via the AWS console. You pay only for used resources. See [AWS service quotas](https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html) and [Amazon EC2 service quotas](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-resource-limits.html) - **Shell** – Examples use GNU Bash. For the tool versions used in testing, see the repository’s [.tool-versions](https://github.com/camunda/camunda-deployment-references/blob/main/.tool-versions) file. It contains an up-to-date list of versions used for testing. ### Considerations #### General considerations This setup provides a solid starting point for running Camunda 8 on AWS. It is not optimized for peak performance. Use it as a foundation you can extend and adapt for production with [Infrastructure as Code (IaC) tools](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/infrastructure-as-code). If Terraform feels complex at first, try the [eksctl guide](./eksctl.md) for a simpler entry point, which shows the resources created and how they interact with each other. - To test or develop against Camunda 8, consider signing up for our [SaaS offering](https://camunda.com/platform/). - If you already have an Amazon EKS cluster, skip ahead to the [Helm setup guide](./eks-helm.md) to deploy Camunda 8. #### Security considerations The following security considerations were flagged by [Trivy](https://trivy.dev/) to simplify adoption and development. Review and address these before production. You can look up each ID in the [Aqua vulnerability database](https://avd.aquasec.com/). ``` AVD-AWS-0040 #(CRITICAL): Public cluster access is enabled. AVD-AWS-0041 #(CRITICAL): Cluster allows access from a public CIDR: 0.0.0.0/0 AVD-AWS-0104 #(CRITICAL): Security group rule allows egress to multiple public internet addresses. AVD-AWS-0343 #(MEDIUM): Cluster does not have Deletion Protection enabled AVD-AWS-0178 #(MEDIUM): VPC does not have VPC Flow Logs enabled. AVD-AWS-0038 #(MEDIUM): Control plane scheduler logging is not enabled. AVD-AWS-0077 #(MEDIUM): Cluster instance has very low backup retention period. AVD-AWS-0133 #(LOW): Instance does not have performance insights enabled. ``` :::warning Reference architectures and examples provided in this guide are not turnkey modules. Camunda recommends cloning the repository and modifying it locally. You are responsible for operating and maintaining the infrastructure. Camunda updates the reference architecture over time and changes may not be backward compatible. You can use these updates to upgrade your customized codebase as needed. ::: :::danger Cost management This guide will incur costs on your cloud provider account, specifically for the managed Kubernetes service, running Kubernetes nodes in EC2, Elastic Block Storage (EBS), and Route 53. For more details, see [AWS EKS pricing](https://aws.amazon.com/eks/pricing/) and the [AWS Pricing Calculator](https://calculator.aws/#/). Costs vary by region. ::: ### Variants We support two variants of this architecture: - **Standard installation** - Uses username and password connection for the Camunda components (or relies on network isolation for specific components). This option is straightforward and easier to implement, making it ideal for environments where simplicity and rapid deployment are priorities, or where network isolation provides sufficient security. - **IRSA** (IAM Roles for Service Accounts) - Uses service accounts to perform authentication with IAM policies. This approach offers stronger security and better integration with AWS services, as it eliminates the need to manage credentials manually. It is especially beneficial in environments with strict security requirements, where fine-grained access control and dynamic role-based access are essential. #### How to choose - If you prefer a simpler setup with Basic authentication or network isolation, and your security needs are moderate, the **standard installation** is a suitable choice. - If you require enhanced security, dynamic role-based access management, and want to leverage AWS’s identity services for fine-grained control, the **IRSA** variant is the better option. Both can be set up with or without a **Domain** ([Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/)). ### Outcome _Infrastructure diagram for a single region EKS setup (click on the image to open the PDF version)_ [![Infrastructure Diagram EKS Single-Region](./assets/eks-single-region.jpg)](./assets/eks-single-region.pdf) After completing this guide, you will have: - An Amazon EKS Kubernetes cluster running with four nodes ready for Camunda 8 installation. - The [EBS CSI driver](https://docs.aws.amazon.com/eks/latest/userguide/ebs-csi.html), installed and configured. This is used by the Camunda 8 Helm chart to create [persistent volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/). - (Optional) A managed [Aurora PostgreSQL](https://aws.amazon.com/rds/postgresql/) instance for Camunda. - (Optional) A managed [Amazon OpenSearch Service](https://aws.amazon.com/opensearch-service/) domain created and configured for use with Camunda. - (Optional) [IRSA](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) configured so Kubernetes workloads can assume IAM roles without stored credentials. ## 1. Configure AWS and initialize Terraform ### Obtain a copy of the reference architecture The first step is to download a copy of the reference architecture from the [GitHub repository](https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/). This material will be used throughout the rest of this documentation. The reference architectures are versioned using the same Camunda versions (`stable/8.x`). The provided reference architecture repository allows you to directly reuse and extend the existing Terraform example base. This sample implementation is flexible to extend to your own needs without the potential limitations of a Terraform module maintained by a third party. ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/procedure/get-your-copy.sh ``` ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region-irsa/procedure/get-your-copy.sh ``` With the reference architecture copied, you can proceed with the remaining steps outlined in this documentation. Ensure that you are in the correct directory before continuing with further instructions. #### Initialize Terraform Once your authentication is set up, you can initialize your Terraform project. The previous steps configured a dedicated S3 Bucket (`S3_TF_BUCKET_NAME`) to store your state, and the following creates a bucket key that will be used by your configuration. Configure the backend and download the necessary provider plugins: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/common/procedure/s3-bucket/s3-bucket-tf-init.sh ``` Terraform will connect to the S3 bucket to manage the state file, ensuring remote and persistent storage. ### EKS cluster module setup This module provides the foundational configuration for AWS access and Terraform usage. We use [Terraform modules](https://developer.hashicorp.com/terraform/language/modules) to abstract resources into reusable components and simplify infrastructure management. The [Camunda AWS EKS cluster module](https://github.com/camunda/camunda-deployment-references/tree/main/aws/modules/eks-cluster/) is publicly available and serves as a robust starting point for deploying an Amazon EKS cluster. Review the module before implementation to understand its structure and capabilities. The module is locally sourced in your clone. Any changes you make to the module in your repository take effect immediately in your setup. #### Set up the EKS cluster module 1. Go to the [reference architecture directory of the cloned repository](#obtain-a-copy-of-the-reference-architecture). Verify the layout, and switch into the cluster module: ```bash cd ./aws/kubernetes/eks-single-region(-irsa)/terraform/ ls # Example output: # cluster vpn cd cluster ``` 1. Review `cluster.tf`. It references the local Terraform module and contains a basic cluster setup that you can adjust to your needs. The file is available here: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/terraform/cluster/cluster.tf ``` ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region-irsa/terraform/cluster/cluster.tf ``` 1. Configure user access to the cluster. By default, the user who creates the Amazon EKS cluster has administrative access.
Grant cluster access to other users If you want to grant access to other users, you can configure this by using the `access_entries` input. Amazon EKS access management is divided into two distinct layers: - The **first layer** involves **AWS IAM permissions**, which allow basic Amazon EKS functionalities such as interacting with the Amazon EKS UI and generating EKS access through the AWS CLI. The module handles this part for you by creating the necessary IAM roles and policies. - The **second layer** controls **cluster access** within Kubernetes, defining the user's permissions inside the cluster (for example, policy association). This can be configured directly through the module's `access_entries` input. To manage user access, use the `access_entries` configuration: ```hcl access_entries = { example = { kubernetes_groups = [] principal_arn = "" policy_associations = { example = { policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy" access_scope = { namespaces = ["default"] type = "namespace" } } } } } ``` In this configuration: - Replace `principal_arn` with the ARN of the IAM user or role. - Use `policy_associations` to define policies for fine-grained access control. For a full list of available policies, refer to the [AWS EKS Access Policies documentation](https://docs.aws.amazon.com/eks/latest/userguide/access-policies.html).
1. Customize the cluster setup. The module offers various input options that allow you to further customize the cluster configuration. For a comprehensive list of available options and detailed usage instructions, refer to the [EKS module documentation](https://github.com/camunda/camunda-deployment-references/blob/main/aws/modules/eks-cluster/README.md). :::note Private cluster By default, this cluster is accessible from the internet. To restrict access, set `private_vpc = true`. This creates a private cluster that is only accessible through the [private subnets](https://docs.aws.amazon.com/vpc/latest/userguide/configure-subnets.html) of your VPC. Optionally, you can expose a public Ingress by setting the `expose_public_elb` variable in the EKS module. ⚠️ Because private subnets are not reachable from the internet, you must connect your network to the cluster using either a [bastion host](https://docs.aws.amazon.com/mwaa/latest/userguide/tutorials-private-network-bastion.html) or a Client VPN. See the [VPN module setup](#vpn-module-setup) to configure an [AWS Client VPN endpoint](https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/cvpn-getting-started.html) for secure access to the private cluster. ::: ### PostgreSQL module setup :::info Optional module If you don't want to use this module, you can skip this section. However, you may need to adjust the remaining instructions to remove references to this module. If you choose not to use this module, you must either provide a managed PostgreSQL service or use the internal deployment by the Camunda Helm chart in Kubernetes. Additionally, you must delete the `db.tf` file in the `terraform/cluster` directory of your chosen reference. Otherwise, it will create the resources. ::: In the [reference architecture](/self-managed/reference-architecture/reference-architecture.md), PostgreSQL database is required for Web Modeler and Management Identity. These components require persistent storage for user data, configuration, and authentication information. If you deploy Keycloak inside the cluster (for example, via the Keycloak Operator), it also needs a PostgreSQL database. :::note Management Identity and multi-tenancy Management Identity also requires PostgreSQL, but only when multi-tenancy is enabled, which is not used in this reference architecture. For more information, see [Multi-tenancy](/components/concepts/multi-tenancy.md). ::: We separated the cluster and PostgreSQL modules to offer you more customization options. #### Set up the Aurora PostgreSQL module 1. Go to the [reference architecture directory of the cloned repository](#obtain-a-copy-of-the-reference-architecture). Verify the layout and switch into the cluster module. For simplicity, the PostgreSQL file is located in the cluster module: ```bash cd ./aws/kubernetes/eks-single-region(-irsa)/terraform/ ls # Example output: # cluster vpn cd cluster ``` 1. The `db.tf` file references the local Terraform module and contains a basic Aurora PostgreSQL setup that you can adjust to your needs. The file is available here: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/terraform/cluster/db.tf ``` In addition to using standard username and password authentication, you can opt to use [**IRSA (IAM Roles for Service Accounts)**](https://aws.amazon.com/blogs/opensource/introducing-fine-grained-iam-roles-service-accounts/) for secure, role-based access to your Aurora database. This method allows your EKS workloads to assume IAM roles without needing to manage AWS credentials directly. :::note Using IRSA is optional. If preferred, you can continue using traditional password-based authentication for database access. ::: If you choose to use IRSA, you'll need to take note of the **IAM role** created for Aurora and the **AWS Account ID**, as these will be used later to annotate the Kubernetes service account. ##### Aurora IRSA role and policy The Aurora module uses outputs from the EKS cluster module to configure the IRSA role and policy. Below are the required parameters: Here's how to define the IAM role trust policy and access policy for Aurora: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region-irsa/terraform/cluster/db.tf ``` Once the IRSA configuration is complete, ensure you **record the IAM role name** (from the `iam_aurora_role_name` configuration), it is required to annotate the Kubernetes service account in the next step. 1. Customize the Aurora cluster setup through various input options. Refer to the [Aurora module documentation](https://github.com/camunda/camunda-deployment-references/blob/main/aws/modules/aurora/README.md) for more details on other customization options. ### OpenSearch module setup :::info Optional module If you don't want to use this module, you can skip this section. However, you may need to adjust the remaining instructions to remove references to this module. If you choose not to use this module, you can either: - Provide a managed Elasticsearch or OpenSearch service, or deploy Elasticsearch in your cluster via ECK. - Use RDBMS (PostgreSQL, MySQL, MariaDB, Oracle) as the secondary storage backend for the Orchestration Cluster. See [configure RDBMS in Helm](/self-managed/deployment/helm/configure/database/rdbms.md) for details. Additionally, you must delete the `opensearch.tf` file within the `terraform/cluster` directory of your chosen reference as it will otherwise create the resources. ::: The OpenSearch module creates an OpenSearch domain intended for Camunda. OpenSearch is a powerful alternative to Elasticsearch. For more information on using OpenSearch with Camunda, refer to the [Camunda documentation](/self-managed/deployment/helm/configure/database/using-external-opensearch.md). :::note Secondary storage is configurable. Depending on the components you run and your requirements, you can use a document-store backend (Elasticsearch/OpenSearch) or an RDBMS-based secondary store for supported components. See [configure RDBMS in Helm](/self-managed/deployment/helm/configure/database/rdbms.md) for configuration guidance and limitations. ::: :::note Migration to OpenSearch is not supported Using Amazon OpenSearch Service requires [setting up a new Camunda installation](/self-managed/setup/overview.md). Migration from previous Camunda versions using Elasticsearch environments is currently not supported. Switching between Elasticsearch and OpenSearch, in either direction, is also not supported. ::: #### Set up the OpenSearch domain module 1. Go to the [reference architecture directory of the cloned repository](#obtain-a-copy-of-the-reference-architecture). Verify the layout and switch into the cluster module. For simplicity, the OpenSearch file is located in the cluster module: ```bash cd ./aws/kubernetes/eks-single-region(-irsa)/terraform/ ls # Example output: # cluster vpn cd cluster ``` 1. The `opensearch.tf` references the local Terraform module and contains a basic AWS OpenSearch setup that you can adjust to your needs. The file is available here: :::caution Network based security The standard deployment for OpenSearch relies on the first layer of security, which is the Network. While this setup allows easy access, it may expose sensitive data. To enhance security, consider implementing IAM Roles for Service Accounts (IRSA) to restrict access to the OpenSearch cluster, providing a more secure environment. For more information, see the [Amazon OpenSearch Service Fine-Grained Access Control documentation](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/fgac.html#fgac-access-policies). ::: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/terraform/cluster/opensearch.tf ``` In addition to standard authentication, which uses anonymous users and relies on the network for access control, you can also use [**IRSA (IAM Roles for Service Accounts)**](https://aws.amazon.com/blogs/opensource/introducing-fine-grained-iam-roles-service-accounts/) to securely connect to OpenSearch. IRSA enables your Kubernetes workloads to assume IAM roles without managing AWS credentials directly. :::note Using IRSA is optional. If you prefer, you can continue using password-based access to your OpenSearch domain. ::: If you choose to use IRSA, you'll need to take note of the **IAM role name** created for OpenSearch and the **AWS Account ID**, as these will be required later to annotate the Kubernetes service account. ##### OpenSearch IRSA role and policy To configure IRSA for OpenSearch, the OpenSearch module uses outputs from the EKS cluster module to define the necessary IAM role and policies. Here's an example of how to define the IAM role trust policy and access policy for OpenSearch, this configuration will deploy an OpenSearch domain with advanced security enabled: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region-irsa/terraform/cluster/opensearch.tf ``` Once the IRSA configuration is complete, ensure you **record the IAM role name** (from the `iam_opensearch_role_name` configuration), it is required to annotate the Kubernetes service account in the next step. As the OpenSearch domain has advanced security enabled and [fine-grained access control](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/fgac.html), we will later use your provided master username (`advanced_security_master_user_name`) and password (`advanced_security_master_user_password`) to perform the initial setup of the security component, allowing the created IRSA role to access the domain. 1. Customize the cluster setup using various input options. For a full list of available parameters, see the [OpenSearch module documentation](https://github.com/camunda/camunda-deployment-references/blob/main/aws/modules/opensearch/README.md). :::tip The instance type `m7i.large.search` in the above example is a suggestion, and can be changed depending on your needs. ::: ### Create the EKS cluster, PostgreSQL, and OpenSearch domain After configuring the `cluster` module (which includes EKS, PostgreSQL, and OpenSearch), use Terraform to plan and create the resources. :::note Secret management We strongly recommend managing sensitive information such as the OpenSearch, Aurora username and password using a secure secrets management solution like HashiCorp Vault. For details on how to inject secrets directly into Terraform via Vault, see the [Terraform Vault Secrets Injection Guide](https://developer.hashicorp.com/terraform/tutorials/secrets/secrets-vault). ::: 1. Open a terminal in the chosen reference folder where `config.tf` and other `.tf` files are. ```bash cd ./aws/kubernetes/eks-single-region(-irsa)/terraform/ ls # Example output: # cluster vpn cd cluster ``` 2. Perform a final initialization for anything changed throughout the guide: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/common/procedure/s3-bucket/s3-bucket-tf-init.sh#L7 ``` 3. Plan the configuration files: ```bash terraform plan -out cluster.plan # describe what will be created ``` 4. After reviewing the plan, you can confirm and apply the changes. ```bash terraform apply cluster.plan # apply the creation ``` Terraform will now create the Amazon EKS cluster with all the necessary configurations. The completion of this process may require approximately 20-30 minutes for each component. #### Define outputs Terraform lets you define outputs, which make it easier to retrieve important values generated during execution, such as database endpoints and other configuration details needed for the Helm setup. Each module definition set up in the reference contains an output definition at the end of the file. You can adjust them as needed. Outputs let you easily referenc values such as the **cert-manager** ARN, **external-dns** ARN, and the endpoints for **PostgreSQL** and **OpenSearch** in subsequent steps or scripts, streamlining your deployment process. ### VPN module setup You can optionally set up an AWS VPN endpoint to access a private cluster. This step is only required if you configured a private cluster. Using a VPN offers a flexible and secure way to connect to the private subnets within your VPC. It can be used either by a user to access cluster resources or to enable cross-site communications via [PrivateLink](https://docs.aws.amazon.com/vpc/latest/privatelink/what-is-privatelink.html). This module focuses on user access. _Infrastructure diagram for a single region EKS setup with VPN (click on the image to open the PDF version)_ [![Infrastructure Diagram EKS Single-Region VPN](./assets/eks-single-region-vpn.jpg)](./assets/eks-single-region-vpn.pdf) AWS VPN technology is compatible with OpenVPN clients. It uses [x509 certificates](https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/mutual.html) for mutual authentication and source verification. The encryption provided by these certificates ensures that traffic can securely transit over the internet to the AWS VPN endpoint, which performs NAT and routes the traffic directly into the private subnets. This VPN endpoint then becomes the sole access point to the private cluster. #### Retrieve the VPC cluster ID To create the VPN Endpoint in your cluster's VPC, you need to retrieve the VPC ID using [Terraform outputs](https://developer.hashicorp.com/terraform/language/values/outputs) from the [EKS cluster module](#eks-cluster-module-setup). Follow these steps: 1. Go to the [reference architecture directory of the cloned repository](#obtain-a-copy-of-the-reference-architecture) and switch into the `cluster` module directory: ```bash cd ./aws/kubernetes/eks-single-region/terraform/ ls # Example output: # cluster vpn cd cluster ``` 2. Export the [VPC ID](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc) to an environment variable: ```bash export CLUSTER_VPC_ID="$(terraform output -raw vpc_id)" echo "CLUSTER_VPC_ID=$CLUSTER_VPC_ID" ``` #### Set up the VPN module From the parent directory of your cluster module, go to the `vpn` directory which holds the VPN endpoint configuration. This setup creates a Certificate Authority (CA) for AWS VPN to perform encryption and mutual client authentication. For simplicity, the CA and generated certificates are stored in the project’s Terraform state (`tfstate`). You may customize this as needed. 1. Review `config.tf`, which configures the S3 backend for Terraform state management: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/terraform/vpn/config.tf ``` 2. Review `vpn.tf`, which defines the VPC Client endpoint configuration: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/terraform/vpn/vpn.tf ``` This VPN Client Endpoint follows [AWS best practices and constraints](https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/what-is-best-practices.html): - Uses a client CIDR range that does not overlap with the VPC CIDR or any manually added VPN route table routes. - Implements [split-tunnel routing](https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/split-tunnel-vpn.html), so only traffic for the VPC goes through the VPN, minimizing bandwidth use. - Supports IPv4 only and is bound to the VPC’s private subnets. **Initialize and configure Terraform**: 1. Set your Terraform state key, initialize Terraform with the S3 [backend](#create-an-s3-bucket-for-terraform-state-management), and download necessary provider plugins: ```bash export S3_TF_BUCKET_KEY_VPN="camunda-terraform/vpn.tfstate" echo "Storing cluster terraform state in s3://$S3_TF_BUCKET_NAME/$S3_TF_BUCKET_KEY_VPN" terraform init -backend-config="bucket=$S3_TF_BUCKET_NAME" -backend-config="key=$S3_TF_BUCKET_KEY_VPN" -backend-config="region=$S3_TF_BUCKET_REGION" ``` Terraform will connect to the S3 bucket to manage the state file, ensuring remote and persistent storage. 2. For each client connecting to the cluster, assign a unique name in `client_key_names` to simplify certificate revocation. 3. By default, VPN access is allowed from any IP address. Restrict access by adjusting the `vpn_allowed_cidr_blocks` variable. 4. Review and adjust the configuration for your network design. 5. For customization options, see the [VPN module documentation](https://github.com/camunda/camunda-deployment-references/blob/main/aws/modules/vpn/README.md). #### Outputs The module stores certificates and VPN client configurations in the Terraform state. The next section explains how to retrieve and use client configurations. #### Execution 1. Generate a Terraform plan for the VPN Client endpoint configuration. This will use the private subnets of the designated VPC: ```bash # describe what will be created terraform plan -out vpn.plan \ -var vpc_id="$CLUSTER_VPC_ID" ``` 2. Review and apply the plan to create the resources: ```bash terraform apply vpn.plan # creates the resources ``` Creating the VPN Client endpoint typically takes about 10 minutes. After completion, the Terraform output `vpn_client_configs` provides the client configuration files. ## 2. Preparation for Camunda 8 installation ### Access to the private network using the VPN This section applies if you have previously created a private cluster and want to access it using the [VPN module configured earlier](#vpn-module-setup). 1. Navigate to the VPN module directory (`vpn`): ```bash pwd # Example output: # ./camunda-deployment-references/aws/kubernetes/eks-single-region(-irsa)/terraform/vpn/ ``` 2. Generate your client’s VPN configuration file. This file is compatible with [OpenVPN (ovpn)](https://openvpn.net/) format: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/common/procedure/vpn/gather-vpn-config.sh ``` 3. Import the generated configuration file (`my-client.ovpn`) into an OpenVPN client: - _(preferred)_ [Official AWS VPN Client](https://docs.aws.amazon.com/vpn/latest/clientvpn-user/connect-aws-client-vpn-connect.html) - [Other OpenVPN Clients](https://docs.aws.amazon.com/vpn/latest/clientvpn-user/connect.html) 4. Once the VPN client is connected, you will have secure access to the VPC’s private network. ### Access the created EKS cluster You can gain access to the Amazon EKS cluster via the `AWS CLI` using the following command: ```bash export CLUSTER_NAME="$(terraform console << ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/procedure/export-helm-values.sh ``` ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region-irsa/procedure/export-helm-values.sh ``` :::note IRSA users To authenticate and authorize access to PostgreSQL and OpenSearch, **you do not need to export the PostgreSQL or OpenSearch passwords**, IRSA will handle the authentication. **However**, you will still need to export the relevant usernames and other settings to Helm. ::: Ensure that you use the actual values you passed to the Terraform module during the setup of PostgreSQL and OpenSearch. ### Configure the database and associated access As you now have a database, you need to create dedicated databases for each Camunda component and an associated user that have a configured access. Follow these steps to create the database users and configure access. You can access the created database in the following ways: 1. **Bastion host:** Set up a bastion host within the same network to securely access the database. 2. **Pod within the EKS cluster:** Deploy a pod in your EKS cluster equipped with the necessary tools to connect to the database. See [Access internal infrastructure](#access-internal-infrastructure). 3. **VPN:** Establish a VPN connection to the VPC where the Aurora Cluster resides, allowing secure access from your local machine or another network. The choice depends on your infrastructure setup and security preferences. In this guide, we'll use a pod within the EKS cluster to configure the database. 1. In your terminal, set the necessary environment variables that will be substituted in the setup manifest: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/procedure/vars-create-db.sh ``` A **Kubernetes job** will connect to the database and create the necessary users with the required privileges. The script installs the necessary dependencies and runs SQL commands to create the IRSA user and assign it the correct roles and privileges. 2. Create a secret that references the environment variables: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/procedure/create-setup-db-secret.sh ``` This command creates a secret named `setup-db-secret` and dynamically populates it with the values from your environment variables. After running the above command, you can verify that the secret was created successfully by using: ```bash kubectl get secret setup-db-secret -o yaml --namespace "$CAMUNDA_NAMESPACE" ``` This should display the secret with the base64 encoded values. ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region-irsa/procedure/create-setup-db-secret.sh ``` This command creates a secret named `setup-db-secret` and dynamically populates it with the values from your environment variables. After running the above command, you can verify that the secret was created successfully by using: ```bash kubectl get secret setup-db-secret -o yaml --namespace "$CAMUNDA_NAMESPACE" ``` This should display the secret with the base64 encoded values. 3. Save the following manifest to a file, for example, `setup-postgres-create-db.yml`. ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region/setup-postgres-create-db.yml ``` ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region-irsa/setup-postgres-create-db.yml ``` 4. Apply the manifest: ```bash kubectl apply -f setup-postgres-create-db.yml --namespace "$CAMUNDA_NAMESPACE" ``` Once the secret is created, the **Job** manifest from the previous step can consume this secret to securely access the database credentials. 5. Once the job is created, monitor its progress using: ```bash kubectl get job/create-setup-user-db --namespace "$CAMUNDA_NAMESPACE" --watch ``` Once the job shows as `Completed`, the users and databases will have been successfully created. 6. View the logs of the job to confirm that the users were created and privileges were granted successfully: ```bash kubectl logs job/create-setup-user-db --namespace "$CAMUNDA_NAMESPACE" ``` 7. Clean up the resources: ```bash kubectl delete job create-setup-user-db --namespace "$CAMUNDA_NAMESPACE" kubectl delete secret setup-db-secret --namespace "$CAMUNDA_NAMESPACE" ``` Running these commands cleans up both the job and the secret, ensuring that no unnecessary resources remain in the cluster. ### Configure OpenSearch fine grained access control As you now have an OpenSearch domain, you need to configure the related access for each Camunda component. You can access the created OpenSearch domain in the following ways: 1. **Bastion host:** Set up a bastion host within the same network to securely access the OpenSearch domain. 2. **Pod within the EKS cluster:** Deploy a pod in your EKS cluster equipped with the necessary tools to connect to the OpenSearch domain. See [Access internal infrastructure](#access-internal-infrastructure). 3. **VPN:** Establish a VPN connection to the VPC where the OpenSearch domain resides, allowing secure access from your local machine or another network. The choice depends on your infrastructure setup and security preferences. In this tutorial, we'll use a pod within the EKS cluster to configure the domain. The standard installation comes already pre-configured, and no additional steps are required. 1. In your terminal, set the necessary environment variables that will be substituted in the setup manifest: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region-irsa/procedure/vars-create-os.sh ``` A **Kubernetes job** will connect to the OpenSearch dommain and configure it. 1. Create a secret that references the environment variables: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region-irsa/procedure/create-setup-os-secret.sh ``` This command creates a secret named `setup-os-secret` and dynamically populates it with the values from your environment variables. After running the above command, you can verify that the secret was created successfully by using: ```bash kubectl get secret setup-os-secret -o yaml --namespace "$CAMUNDA_NAMESPACE" ``` This should display the secret with the base64 encoded values. 1. Save the following manifest to a file, for example, `setup-opensearch-fgac.yml`. ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/kubernetes/eks-single-region-irsa/setup-opensearch-fgac.yml ``` 1. Apply the manifest: ```bash kubectl apply -f setup-opensearch-fgac.yml --namespace "$CAMUNDA_NAMESPACE" ``` Once the secret is created, the **Job** manifest from the previous step can consume this secret to securely access the OpenSearch domain credentials. 1. Once the job is created, monitor its progress using: ```bash kubectl get job/setup-opensearch-fgac --namespace "$CAMUNDA_NAMESPACE" --watch ``` Once the job shows as `Completed`, the OpenSearch domain is configured correctly for fine grained access control. 1. View the logs of the job to confirm that the privileges were granted successfully: ```bash kubectl logs job/setup-opensearch-fgac --namespace "$CAMUNDA_NAMESPACE" ``` 1. Clean up the resources: ```bash kubectl delete job setup-opensearch-fgac --namespace "$CAMUNDA_NAMESPACE" kubectl delete secret setup-os-secret --namespace "$CAMUNDA_NAMESPACE" ``` Running these commands will clean up both the job and the secret, ensuring that no unnecessary resources remain in the cluster. ### Access internal infrastructure :::warning Not recommended in production These approaches are intended for **development and troubleshooting purposes only**. For a production cluster, use a proper VPN or other secure access methods. ::: Some infrastructure components, such as OpenSearch dashboards or Aurora PostgreSQL databases, are accessible only from inside the Virtual Private Cloud (VPC). You can use a temporary pod as a _jump host_ to tunnel traffic to these components. ---
Generic approach using a jump host **Component connection details** Export `REMOTE_HOST`, `REMOTE_PORT`, and `LOCAL_PORT` with the component-specific values: | Component | `REMOTE_HOST` | `REMOTE_PORT` | `LOCAL_PORT` | | -------------------- | ------------------ | -------------- | ------------ | | OpenSearch dashboard | `$OPENSEARCH_HOST` | `443` | `9200` | | Aurora PostgreSQL | `$AURORA_ENDPOINT` | `$AURORA_PORT` | `5432` | 1. Run a socat pod to create a TCP tunnel: ```bash kubectl --namespace $CAMUNDA_NAMESPACE run my-jump-pod -it \ --image=alpine/socat \ --tty --rm \ --restart=Never \ --expose=true --port=$REMOTE_PORT -- \ tcp-listen:$REMOTE_PORT,fork,reuseaddr \ tcp-connect:$REMOTE_HOST:$REMOTE_PORT ``` :::tip How it works [socat](http://www.dest-unreach.org/socat/) (_SOcket CAT_) is a command-line tool that relays data between two network endpoints. In this command: - `tcp-listen:$REMOTE_PORT,fork,reuseaddr` listens on the specified port in the pod and can handle multiple connections. - `tcp-connect:$REMOTE_HOST:$REMOTE_PORT` forwards all incoming traffic to the internal component endpoint. Combined with `kubectl port-forward` (step 2), the flow is: ``` Local Client → localhost:$LOCAL_PORT → port-forward → my-jump-pod:$REMOTE_PORT → socat → Remote Component ``` This setup lets you securely reach internal components without exposing them publicly. ::: 2. Port-forward the pod to your local machine: ```bash kubectl port-forward --namespace $CAMUNDA_NAMESPACE pod/my-jump-pod $LOCAL_PORT:$REMOTE_PORT ``` 3. Connect to the component: _OpenSearch example:_ ```bash https://localhost:$LOCAL_PORT/_dashboards ``` Accept the insecure connection if prompted. _Aurora PostgreSQL example:_ ```bash PGPASSWORD=$AURORA_PASSWORD psql -h localhost -p $LOCAL_PORT -U $AURORA_USERNAME -d ```
## 3. Install Camunda 8 using the Helm chart Now that you've exported the necessary values, you can proceed with installing Camunda 8 using Helm charts. Follow the guide [Camunda 8 on Kubernetes](./eks-helm.md) for detailed instructions on deploying the platform to your Kubernetes cluster. --- ## Dual-region ROSA HCP Cluster with Terraform This guide provides a detailed tutorial for deploying two [Red Hat OpenShift on AWS (ROSA) cluster with Hosted Control Plane (HCP)](https://docs.redhat.com/en/documentation/red_hat_openshift_service_on_aws_classic_architecture/4/html/architecture/index.html) in two different [regions](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/). It is specifically tailored for deploying Camunda 8 using Terraform, a widely-used Infrastructure as Code (IaC) tool, details of the High Level design are available in the generic [Red Hat OpenShift dual-region for Camunda 8 guide](/self-managed/deployment/helm/cloud-providers/openshift/dual-region.md). We recommend this guide for building a robust and sustainable infrastructure that needs to survive a region lost. This guide aims to help you leverage IaC to streamline and reproduce your cloud infrastructure setup. While it covers the essentials for deploying an ROSA HCP cluster, for more advanced use cases, please refer to the official [Red Hat OpenShift on AWS Documentation](https://docs.redhat.com/en/documentation/red_hat_openshift_service_on_aws/4). :::tip If you are completely new to Terraform and the idea of IaC, read through the [Terraform IaC documentation](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/infrastructure-as-code) and give their [interactive quick start](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/infrastructure-as-code#quick-start) a try for a basic understanding. ::: ## Requirements - A [Red Hat Account](https://www.redhat.com/) to create the Red Hat OpenShift cluster. - An [AWS account](https://docs.aws.amazon.com/accounts/latest/reference/accounts-welcome.html) to create any resources within AWS. - [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html), a CLI tool for creating AWS resources. - [Terraform](https://developer.hashicorp.com/terraform/downloads) - [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) to interact with the cluster. - [ROSA CLI](https://docs.redhat.com/en/documentation/red_hat_openshift_service_on_aws_classic_architecture/4/html/getting_started/rosa-quickstart-guide-ui.html) to interact with the cluster. - [jq](https://jqlang.github.io/jq/download/) to interact with some Terraform variables. - This guide uses GNU/Bash for all the shell commands listed. For the tool versions used, check the [.tool-versions](https://github.com/camunda/camunda-deployment-references/blob/main/.tool-versions) file in the repository. It contains an up-to-date list of versions that we also use for testing. ### Considerations This setup provides a foundational starting point for working with Camunda 8, though it is not optimized for peak performance. It serves as a solid initial step in preparing a production environment by leveraging [Infrastructure as Code (IaC) tools](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/infrastructure-as-code). Terraform can initially appear complex. If you're new to it, you might want to start by considering [deployment in a single region](terraform-setup.md) first. :::warning Cost management Following this guide will incur costs on your cloud provider account and your Red Hat account, specifically for the managed OpenShift service, OpenShift worker nodes running in EC2, the hosted control plane, Elastic Block Storage (EBS), and Route 53. For more details, refer to [ROSA AWS pricing](https://aws.amazon.com/rosa/pricing/) and the [AWS Pricing Calculator](https://calculator.aws/#/) as total costs vary by region. ::: ### Outcome _Infrastructure diagram for a dual region ROSA setup (click on the image to open the PDF version)_ [![Infrastructure Diagram ROSA Dual-Region](./assets/rosa-dual-region.jpg)](./assets/rosa-dual-region.pdf) Following this tutorial and steps will result in: - Two [Red Hat OpenShift with Hosted Control Plane](https://www.redhat.com/en/topics/containers/what-are-hosted-control-planes#rosa-with-hcp) clusters running the latest ROSA version, each with six nodes ready for Camunda 8 installation in separate regions. - The [EBS CSI driver](https://docs.aws.amazon.com/eks/latest/userguide/ebs-csi.html) installed and configured, enabling the Camunda 8 Helm chart to create [persistent volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/). - [VPC Peering](https://docs.aws.amazon.com/vpc/latest/peering/what-is-vpc-peering.html) configured to enable cross-region cluster communication. - An [Amazon Simple Storage Service](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html) (S3) bucket for [Elasticsearch backups](https://www.elastic.co/guide/en/elasticsearch/reference/current/repository-s3.html). - [Red Hat OpenShift Advanced Cluster Management](https://www.redhat.com/en/technologies/management/advanced-cluster-management) used to manage the two clusters and configure Submariner. - [Submariner](https://docs.redhat.com/en/documentation/red_hat_advanced_cluster_management_for_kubernetes/2.2/html/manage_cluster/submariner) configured on the two clusters to enable cross-namespace and cross-cluster network communication. ## 1. Configure AWS and initialize Terraform ### Obtain a copy of the reference architecture The first step is to download a copy of the reference architecture from the [GitHub repository](https://github.com/camunda/camunda-deployment-references/blob/main/aws/openshift/rosa-hcp-dual-region/). This material will be used throughout the rest of this documentation, the reference architecture is versioned using the same Camunda versions (`stable/8.x`). ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/openshift/rosa-hcp-dual-region/procedure/get-your-copy.sh ``` With the reference architecture copied, you can proceed with the remaining steps outlined in this documentation. Ensure that you are in the correct directory before continuing with further instructions. ### OpenShift clusters module setup This module sets up the foundational configuration for ROSA HCP and Terraform usage. We will leverage [Terraform modules](https://developer.hashicorp.com/terraform/language/modules), which allow us to abstract resources into reusable components, simplifying infrastructure management. The [Camunda-provided module](https://github.com/camunda/camunda-deployment-references/tree/main/aws/openshift/rosa-hcp-dual-region/) is publicly available and serves as a starting point for deploying Red Hat OpenShift clusters on AWS using a Hosted Control Plane. It is highly recommended to review this module before implementation to understand its structure and capabilities. Please note that this module is based on the official [ROSA HCP Terraform module documentation](https://docs.openshift.com/rosa/rosa_hcp/terraform/rosa-hcp-creating-a-cluster-quickly-terraform.html). It is presented as an example for running Camunda 8 in ROSA. **For production or advanced use cases or custom setups, we encourage you to use the [official module](https://docs.openshift.com/rosa/rosa_hcp/terraform/rosa-hcp-creating-a-cluster-quickly-terraform.html)**, which includes vendor-supported features. Configure `CLUSTER_0_REGION` and `CLUSTER_1_REGION` with the target regions respectively. ```bash # Set the region, adjust as needed export CLUSTER_0_REGION="us-east-1" export CLUSTER_1_REGION="us-east-2" ``` Verify your AWS quotas for each region: ```bash rosa verify quota --region="$CLUSTER_0_REGION" rosa verify quota --region="$CLUSTER_1_REGION" ``` :::note This may fail due to organizational policies. ::: #### Set up the ROSA clusters module The dual-cluster setup requires managing two distinct clusters in different regions. For the simplicity of usage, we will manage the two clusters using a single module, therefore this guide uses a dedicated [aws terraform provider](https://registry.terraform.io/providers/hashicorp/aws/latest/docs) for each region. 1. Ensure you are in the [reference architecture directory of the cloned repository](#obtain-a-copy-of-the-reference-architecture): `./aws/openshift/rosa-hcp-dual-region/terraform/`. Then, navigate into the `clusters` module: ```bash ls # Example output: # clusters peering backup_bucket cd clusters ``` 2. Configure your topology deployment, as you will use multiple regions, specify `CLUSTER_0_REGION` and `CLUSTER_1_REGION` with the target regions respectively. ```bash # set the region, adjust to your needs export CLUSTER_0_REGION="us-east-1" export CLUSTER_1_REGION="us-east-2" # ensure bucket variables are set export S3_TF_BUCKET_REGION="" export S3_TF_BUCKET_NAME="my-rosa-dual-tf-state" ``` 3. Ensure that your `RHCS_TOKEN` is defined and valid (otherwise, renew it on [OpenShift Cluster Management API Token](https://console.redhat.com/openshift/token/rosa)): ```bash rosa login --token="$RHCS_TOKEN" ``` 4. Review the module configuration file `config.tf`. This configuration will use the previously created S3 bucket for storing the Terraform state file: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/openshift/rosa-hcp-dual-region/terraform/clusters/config.tf ``` 5. Review the file named `cluster_region_0.tf` in the same directory. This file describes the cluster of the region 0, you may want to customize the `locals` variables with parameters of your choice, those are described in the next steps. ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/openshift/rosa-hcp-dual-region/terraform/clusters/cluster_region_0.tf ``` 6. Do the same review with `cluster_region_1.tf` and adjust it to your needs. This file describes the cluster of the region 1: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/openshift/rosa-hcp-dual-region/terraform/clusters/cluster_region_1.tf ``` 7. After setting up the terraform files and ensuring your AWS authentication is configured, initialize your Terraform project, then, initialize Terraform to configure the backend and download necessary provider plugins: ```bash export S3_TF_BUCKET_KEY_CLUSTERS="camunda-terraform/clusters.tfstate" echo "Storing clusters terraform state in s3://$S3_TF_BUCKET_NAME/$S3_TF_BUCKET_KEY_CLUSTERS" terraform init -backend-config="bucket=$S3_TF_BUCKET_NAME" -backend-config="key=$S3_TF_BUCKET_KEY_CLUSTERS" -backend-config="region=$S3_TF_BUCKET_REGION" ``` **For each cluster's file:** :::note Configure each cluster - Customize the cluster name, availability zones, with the values of your choice. - Additionally, provide a secure username and password for the cluster administrator. We strongly recommend managing sensitive information using a secure secrets management solution like HashiCorp Vault. For details on how to inject secrets directly into Terraform via Vault, see the [Terraform Vault Secrets Injection Guide](https://developer.hashicorp.com/terraform/tutorials/secrets/secrets-vault). - By default, a cluster is accessible from the internet. If you prefer to restrict access, please refer to the [official documentation of the module](https://registry.terraform.io/modules/terraform-redhat/rosa-hcp/rhcs/latest#input_private). ::: 1. Configure each cluster by editing the beginning of their respective files in the `locals` section: - Each cluster should have a unique, non-overlapping CIDR block to ensure proper functioning of the Submariner overlay network (as referenced in the [Submariner documentation](https://submariner.io/0.8/getting-started/architecture/globalnet)). This is essential for successful inter-cluster communication using the Submariner underlay network. If you can't fullfill this requirement, you may need to implement a [Submariner Global Private Network](https://submariner.io/0.8/getting-started/architecture/globalnet/). 1. Configure user access to the clusters. By default, the user who creates an OpenShift cluster has administrative access. If you want to grant access to other users, follow the [Red Hat documentation for granting admin rights to users](https://docs.openshift.com/rosa/cloud_experts_tutorials/cloud-experts-getting-started/cloud-experts-getting-started-admin-rights.html) when the cluster will be created. 1. Customize the clusters setup. The module offers various input options that allow you to further customize the cluster configuration. For a comprehensive list of available options and detailed usage instructions, refer to the [ROSA module documentation](https://github.com/camunda/camunda-deployment-references/blob/main/aws/modules/rosa-hcp/README.md). :::caution Camunda Terraform module This ROSA module is based on the [official Red Hat Terraform module for ROSA HCP](https://registry.terraform.io/modules/terraform-redhat/rosa-hcp/rhcs/latest). Please be aware of potential differences and choices in implementation between this module and the official one. Consult the [Camunda ROSA module documentation](https://github.com/camunda/camunda-deployment-references/blob/main/aws/modules/rosa-hcp/README.md) for more information. ::: #### Define outputs **Terraform** allows you to define outputs, which make it easier to retrieve important values generated during execution, such as cluster endpoints and other necessary configurations for Helm setup. Each module that you have previously set up contains an output definition at the end of the file. You can adjust them to your needs. #### Execution 1. Plan the configuration files: ```bash # describes what will be created terraform plan -out clusters.plan \ -var cluster_0_region="$CLUSTER_0_REGION" \ -var cluster_1_region="$CLUSTER_1_REGION" ``` 1. After reviewing the plan, you can confirm and apply the changes. ```bash # creates the resources terraform apply clusters.plan ``` Terraform will now create the OpenShift clusters with all the necessary configurations. The completion of this process may require approximately 20-30 minutes. ### Region peering module setup This section outlines the process of setting up communication between two different AWS regions hosting each cluster. To achieve this, we will make use of the Amazon [Virtual Private Cloud Peering connection](https://docs.aws.amazon.com/vpc/latest/peering/what-is-vpc-peering.html). The VPC peering connection enables two VPCs in different regions to connect and exchange traffic as if they were part of the same network, while maintaining security through the application of appropriate security groups. Please note that, once the VPC peering is created, it becomes a dependency of the cluster, therefore it must be destroyed before removing the cluster's VPCs. #### Retrieve the peering cluster variables To create the peering between each cluster’s VPC, you need to gather some information using the [terraform outputs](https://developer.hashicorp.com/terraform/language/values/outputs) of the [OpenShift clusters module setup](#openshift-clusters-module-setup). Follow these steps: 1. First, go in the clusters module directory ```bash ls # Example output: # clusters peering backup_bucket cd clusters ``` 1. Then for each cluster, save the associated [VPC ID](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc): ```bash export CLUSTER_0_VPC_ID="$(terraform output -raw cluster_0_vpc_id)" echo "CLUSTER_0_VPC_ID=$CLUSTER_0_VPC_ID" export CLUSTER_1_VPC_ID="$(terraform output -raw cluster_1_vpc_id)" echo "CLUSTER_1_VPC_ID=$CLUSTER_1_VPC_ID" ``` #### Set up the peering module In the parent directory where your clusters module reside (`clusters`), navigate to the directory called `peering` which contains the VPC peering configuration: We'll re-use the previously configured S3 bucket to store the state of the peering configuration. Begin by reviewing up the `config.tf` that configures S3 backend for managing the Terraform state: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/openshift/rosa-hcp-dual-region/terraform/peering/config.tf ``` Alongside the `config.tf` file, review the file called `peering.tf` used to reference the peering configuration:
Show peering.tf reference ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/openshift/rosa-hcp-dual-region/terraform/peering/peering.tf ```
One cluster will be referenced as the **owner**, and the other as the **accepter**. This designation is used solely for networking purposes and does not imply any dependency between the two clusters. #### Initialize Terraform Once the `.tf` files are set up, configure the backend for Terraform and set the S3 bucket key for the peering state and initialize Terraform to configure the backend and download the necessary provider plugins: ```bash # ensure bucket variables are set export S3_TF_BUCKET_REGION="" export S3_TF_BUCKET_NAME="my-rosa-dual-tf-state" export S3_TF_BUCKET_KEY_PEERING="camunda-terraform/peering.tfstate" echo "Storing terraform state in s3://$S3_TF_BUCKET_NAME/$S3_TF_BUCKET_KEY_PEERING" terraform init -backend-config="bucket=$S3_TF_BUCKET_NAME" -backend-config="key=$S3_TF_BUCKET_KEY_PEERING" -backend-config="region=$S3_TF_BUCKET_REGION" ``` This command connects Terraform to the S3 bucket for managing the state file, ensuring remote and persistent storage. #### Execution 1. Navigate to the `peering` directory where the `config.tf` file and other `.tf` files are located. Ensure that you have performed [the previously retrieval of the VPC values](#retrieve-the-peering-cluster-variables). 1. Run the following command to generate a plan for the VPC peering configuration. It will connect with peering the previously retrieved VPCs of each cluster: ```bash terraform plan -out peering.plan \ -var cluster_0_region="$CLUSTER_0_REGION" \ -var cluster_0_vpc_id="$CLUSTER_0_VPC_ID" \ -var cluster_1_region="$CLUSTER_1_REGION" \ -var cluster_1_vpc_id="$CLUSTER_1_VPC_ID" ``` 1. After reviewing the execution plan, apply the configuration to create the VPC peering connection: ```bash terraform apply peering.plan ``` This command will initiate the creation of the peering connection, enabling communication between the two clusters. For more details, consult the official [AWS VPC Peering documentation](https://docs.aws.amazon.com/vpc/latest/peering/what-is-vpc-peering.html). ### S3 backup bucket module setup This section outlines the process of creating a [S3 bucket](https://aws.amazon.com/en/s3/) that will be used to to [perform backups of the elasticsearch cluster](https://www.elastic.co/guide/en/elasticsearch/reference/current/snapshot-restore.html) used by Camunda 8. Read more about the [failover procecure](/self-managed/deployment/helm/operational-tasks/dual-region-ops.md#procedure). The S3 bucket is set up following best practices, including encryption, logging, and versioning. These configurations can be customized to suit your specific requirements. #### Set up the bucket module In the parent directory where your other modules reside (`clusters` and `peering`), navigate to the directory called `backup_bucket` for the S3 configuration: ```bash ls # Example output: # clusters peering backup_bucket cd backup_bucket ``` We'll re-use the previously configured S3 bucket to store the state of the backup bucket configuration. Begin by reviewing the `config.tf` file to use the S3 backend for managing the Terraform state: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/openshift/rosa-hcp-dual-region/terraform/backup_bucket/config.tf ``` Finally, review the file called `backup_bucket.tf`, that describes the elastic backup bucket configuration: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/openshift/rosa-hcp-dual-region/terraform/backup_bucket/backup_bucket.tf ``` This bucket configuration follows [multiple best practices](https://docs.aws.amazon.com/AmazonS3/latest/userguide/security-best-practices.html). We encourage you to review the implementation and adjust it according to your specific requirements. #### Initialize Terraform Once the `.tf` files are set up, configure the backend for Terraform and set the S3 bucket key for the peering state and initialize Terraform to configure the backend and download the necessary provider plugins: ```bash # ensure bucket variables are set export S3_TF_BUCKET_REGION="" export S3_TF_BUCKET_NAME="my-rosa-dual-tf-state" # set the region of the bucket export BACKUP_BUCKET_REGION="us-east-1" export S3_TF_BUCKET_KEY_BUCKET="camunda-terraform/backup-bucket.tfstate" echo "Storing terraform state in s3://$S3_TF_BUCKET_NAME/$S3_TF_BUCKET_KEY_BUCKET" terraform init -backend-config="bucket=$S3_TF_BUCKET_NAME" -backend-config="key=$S3_TF_BUCKET_KEY_BUCKET" -backend-config="region=$S3_TF_BUCKET_REGION" ``` This command connects Terraform to the S3 bucket for managing the state file, ensuring remote and persistent storage. The `BACKUP_BUCKET_REGION` will define the region of the bucket, you can pick one of your cluster region. #### Execution 1. Navigate to the `backup_bucket` directory where the `config.tf` file and other `.tf` files are located. 1. Run the following command to generate a plan for the S3 bucket configuration. You can edit the default bucket name using `-var=bucket_name=nameOfBucket` ```bash terraform plan -out backup-bucket.plan \ -var backup_bucket_region="$BACKUP_BUCKET_REGION" ``` 1. After reviewing the execution plan, apply the configuration to create the VPC peering connection: ```bash terraform apply backup-bucket.plan # apply the creation ``` This command will initiate the creation of the backup bucket. 1. You will need to store the following secret variables to set up the dual-region installation of Camunda: ```bash export AWS_ACCESS_KEY_ES=$(terraform output -raw s3_aws_access_key) export AWS_SECRET_ACCESS_KEY_ES=$(terraform output -raw s3_aws_secret_access_key) export AWS_ES_BUCKET_NAME=$(terraform output -raw s3_bucket_name) export AWS_ES_BUCKET_REGION="$BACKUP_BUCKET_REGION" echo "AWS_ACCESS_KEY_ES=$AWS_ACCESS_KEY_ES" echo "AWS_SECRET_ACCESS_KEY_ES=$AWS_SECRET_ACCESS_KEY_ES" echo "AWS_ES_BUCKET_NAME=$AWS_ES_BUCKET_NAME" echo "AWS_ES_BUCKET_REGION=$AWS_ES_BUCKET_REGION" ``` Ensure these variables are securely stored, as they will be needed later in the process. ### Reference files You can find the reference files used on [this page](https://github.com/camunda/camunda-deployment-references/tree/main/aws/openshift/rosa-hcp-dual-region/) ## 2. Preparation for Camunda 8 installation ### Access the created OpenShift clusters You can now access the created OpenShift clusters. 1. Verify that you are in the [OpenShift clusters module](#openshift-clusters-module-setup) directory `clusters`: ```bash pwd # Example output: # ./camunda-deployment-references/aws/openshift/rosa-hcp-dual-region/terraform/clusters/ ``` 1. Set up the required environment variables from the OpenShift terraform module: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/openshift/rosa-hcp-dual-region/procedure/gather-cluster-login-id.sh ``` 1. Give cluster administrator role to the created user for each cluster: ```bash # Cluster 1 rosa grant user cluster-admin --cluster="$CLUSTER_0_NAME" --user="$CLUSTER_0_ADMIN_USERNAME" # Cluster 2 rosa grant user cluster-admin --cluster="$CLUSTER_1_NAME" --user="$CLUSTER_1_ADMIN_USERNAME" ``` 1. Log in to the OpenShift clusters and configure the kubeconfig contexts: ```bash # Cluster 1 oc config delete-context "$CLUSTER_0_NAME" || true oc login -u "$CLUSTER_0_ADMIN_USERNAME" "$CLUSTER_0_API_URL" -p "$CLUSTER_0_ADMIN_PASSWORD" oc config rename-context $(oc config current-context) "$CLUSTER_0_NAME" # Cluster 2 oc config delete-context "$CLUSTER_1_NAME" || true oc login -u "$CLUSTER_1_ADMIN_USERNAME" "$CLUSTER_1_API_URL" -p "$CLUSTER_1_ADMIN_PASSWORD" oc config rename-context $(oc config current-context) "$CLUSTER_1_NAME" ``` 1. Verify your connection to the clusters with `oc`: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/openshift/rosa-hcp-dual-region/procedure/verify-cluster-nodes.sh ``` In the remainder of the guide, different namespaces will be created following the needs of the dual-region architecture. ## 3. Next installation steps The next steps are generic and referenced in the **[Generic OpenShift Dual-Region for Camunda 8 guide](/self-managed/deployment/helm/cloud-providers/openshift/dual-region.md#setup-advanced-cluster-management-and-submariner)**. It includes: - Installation of Advanced Cluster Management - Installation of Submariner - Preparation of the configuration for Camunda 8 on dual-region - Installation of Camunda 8 in OpenShift dual-region. ## 4. Deletion of Terraform Resources ### Deletion of the VPC peering The VPC peering module can be deleted once it is no longer in use and has no dependencies. Once the VPC peering is deleted, the clusters will not be able to communicate with each other anymore. To delete the module, follow these steps: 1. Before proceeding with the deletion of the VPC peering module, ensure that the necessary variables, which were provided during the module creation, are still available. To verify that the required variables are correctly defined, repeat the steps from the [retrieve the VPC peering cluster variables](#retrieve-the-peering-cluster-variables) section. 1. Ensure that the `CLUSTER_0_REGION` and `CLUSTER_1_REGION` variables are defined correctly: ```bash # set the region, adjust to your needs export CLUSTER_0_REGION="us-east-1" export CLUSTER_1_REGION="us-east-2" ``` 1. Navigate to the VPC peering module directory `peering` where the VPC peering module configuration is located. 1. Execute the following command to plan the destruction of the VPC peering module. Ensure the correct variables are passed in, as shown below: ```bash terraform plan -destroy \ -var cluster_0_region="$CLUSTER_0_REGION" \ -var cluster_0_vpc_id="$CLUSTER_0_VPC_ID" \ -var cluster_1_region="$CLUSTER_1_REGION" \ -var cluster_1_vpc_id="$CLUSTER_1_VPC_ID" \ -out destroy-peering.plan ``` This command will generate a plan to destroy the resources and save it in a file called `destroy-peering.plan`. 1. After reviewing the destruction plan, apply the changes to delete the VPC peering module resources by running: ```bash terraform apply destroy-peering.plan ``` Once the `apply` command completes successfully, the VPC peering module and associated resources will be deleted. ### Deletion of the clusters The clusters can be deleted once they are no longer in use and have no dependencies. Since clusters rely on the VPC Peering connection, the Peering module must be deleted first. 1. Before proceeding with cluster deletion, ensure that the VPC Peering has been successfully removed by following the steps in the [deletion of the VPC Peering](#deletion-of-the-vpc-peering) section. 1. Go to the directory `clusters` where the clusters configurations are managed. 1. Execute the following command to generate a plan for deleting the clusters, ensuring the correct variables are passed: ```bash terraform plan -destroy \ -var cluster_0_region="$CLUSTER_0_REGION" \ -var cluster_1_region="$CLUSTER_1_REGION" \ -out destroy-clusters.plan ``` This command will generate a plan to destroy the clusters and save it in a file called `destroy-clusters.plan`. 1. After reviewing the destruction plan, apply the changes to delete the cluster resources by running: ```bash terraform apply destroy-clusters.plan ``` Once the `apply` command completes successfully, the clusters and associated resources will be deleted. ### Deletion of the S3 backup bucket The S3 backup bucket can be deleted once it is no longer in use and has no dependencies. To delete the bucket, follow these steps: 1. Navigate to the `backup_bucket` directory created during the [S3 backup bucket module setup](#s3-backup-bucket-module-setup). This directory contains the configuration for managing the S3 bucket. 1. Run the Terraform destroy plan: Execute the following Terraform command to plan the destruction of the S3 bucket and other resources: ```bash terraform plan -destroy -out destroy-bucket.plan ``` This command will generate a plan to destroy the resources and output it into a file called `destroy-bucket.plan`. 1. After reviewing the plan, apply the changes to delete the resources with the following command: ```bash terraform apply destroy-bucket.plan ``` Once the `apply` command is successfully completed, the S3 bucket and associated resources will be deleted. --- ## Deploy a ROSA HCP Cluster with Terraform This guide provides a detailed tutorial for deploying a [Red Hat OpenShift on AWS (ROSA) cluster with Hosted Control Plane (HCP)](https://docs.redhat.com/en/documentation/red_hat_openshift_service_on_aws_classic_architecture/4/html/architecture/index.html) capabilities. It is specifically tailored for deploying Camunda 8 using Terraform, a widely-used Infrastructure as Code (IaC) tool. We recommend this guide for building a robust and sustainable infrastructure. However, if you are looking for a quicker trial or proof of concept, or if your needs aren't fully met by our module, consider following the official [ROSA Quickstart Guide](https://docs.redhat.com/en/documentation/red_hat_openshift_service_on_aws_classic_architecture/4/html/getting_started/rosa-quickstart-guide-ui.html). This guide aims to help you leverage IaC to streamline and reproduce your cloud infrastructure setup. While it covers the essentials for deploying an ROSA HCP cluster, for more advanced use cases, please refer to the official [Red Hat OpenShift on AWS Documentation](https://docs.redhat.com/en/documentation/red_hat_openshift_service_on_aws/4). :::tip If you are completely new to Terraform and the idea of IaC, read through the [Terraform IaC documentation](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/infrastructure-as-code) and give their [interactive quick start](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/infrastructure-as-code#quick-start) a try for a basic understanding. ::: ## Requirements - A [Red Hat Account](https://www.redhat.com/) to create the Red Hat OpenShift cluster. - An [AWS account](https://docs.aws.amazon.com/accounts/latest/reference/accounts-welcome.html) to create any resources within AWS. - [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html), a CLI tool for creating AWS resources. - [AWS Quotas](https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html) _(only applies to [ROSA](https://www.redhat.com/en/technologies/cloud-computing/openshift/aws) or OpenShift deployed on AWS)_ - Ensure at least **3 Elastic IPs** (one per availability zone). - Verify quotas for **VPCs, EC2 instances, and storage**. - Request increases if needed via the AWS console ([guide](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-resource-limits.html)); costs apply only to resources used. - [Terraform](https://developer.hashicorp.com/terraform/downloads) - [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) to interact with the cluster. - [ROSA CLI](https://docs.redhat.com/en/documentation/red_hat_openshift_service_on_aws_classic_architecture/4/html/getting_started/rosa-quickstart-guide-ui.html) to interact with the cluster. - [jq](https://jqlang.github.io/jq/download/) to interact with some Terraform variables. - This guide uses GNU/Bash for all the shell commands listed. For the tool versions used, check the [.tool-versions](https://github.com/camunda/camunda-deployment-references/blob/main/.tool-versions) file in the repository. It contains an up-to-date list of versions that we also use for testing. ### Considerations This setup provides a foundational starting point for working with Camunda 8, though it is not optimized for peak performance. It serves as a solid initial step in preparing a production environment by leveraging [Infrastructure as Code (IaC) tools](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/infrastructure-as-code). Terraform can initially appear complex. If you're new to it, you might want to start by considering trying out the [Red Hat OpenShift on AWS UI-based tutorial](https://docs.redhat.com/en/documentation/red_hat_openshift_service_on_aws_classic_architecture/4/html/getting_started/rosa-getting-started.html). This guide will show you what resources are created and how they interact with each other. If you require managed services for PostgreSQL Aurora or OpenSearch, you can refer to the definitions provided in the [EKS setup with Terraform](../amazon-eks/terraform-setup.md) guide. However, please note that these configurations may need adjustments to fit your specific requirements and have not been tested. This guide uses integrated Helm chart database services in its example path (PostgreSQL and Elasticsearch), but you can choose another supported secondary storage backend for the Orchestration Cluster, including RDBMS (PostgreSQL, MySQL, MariaDB, or Oracle) — see [configure RDBMS in Helm](/self-managed/deployment/helm/configure/database/rdbms.md). For testing Camunda 8 or developing against it, you might consider signing up for our [SaaS offering](https://camunda.com/platform/). If you already have a Red Hat OpenShift cluster on AWS, you can skip ahead to the [Helm setup guide](/self-managed/deployment/helm/cloud-providers/openshift/redhat-openshift.md). To keep this guide concise, we provide links to additional documentation covering best practices, allowing you to explore each topic in greater depth. :::danger Cost management Following this guide will incur costs on your cloud provider account and your Red Hat account, specifically for the managed OpenShift service, OpenShift worker nodes running in EC2, the hosted control plane, Elastic Block Storage (EBS), and Route 53. For more details, refer to [ROSA AWS pricing](https://aws.amazon.com/rosa/pricing/) and the [AWS Pricing Calculator](https://calculator.aws/#/) as total costs vary by region. ::: ### Variants Unlike the [EKS Terraform setup](../amazon-eks/terraform-setup.md), we currently support only one main variant of this setup: - The **standard installation** uses a username and password connection for Camunda components (or relies solely on network isolation for certain components). This option is straightforward and easier to implement, making it ideal for environments where simplicity and rapid deployment are priorities, or where network isolation provides adequate security. - The second variant, **IRSA** (IAM Roles for Service Accounts), may work but has not been tested. If you’re interested in setting it up, please refer to the EKS guide as a foundational resource. ### Outcome _Infrastructure diagram for a single region ROSA setup (click on the image to open the PDF version)_ [![Infrastructure Diagram ROSA Single-Region](./assets/rosa-single-region.jpg)](./assets/rosa-single-region.pdf) Following this tutorial and steps will result in: - A [Red Hat OpenShift with Hosted Control Plane](https://www.redhat.com/en/topics/containers/what-are-hosted-control-planes#rosa-with-hcp) cluster running the latest ROSA version with six nodes ready for Camunda 8 installation. - The [EBS CSI driver](https://docs.aws.amazon.com/eks/latest/userguide/ebs-csi.html) is installed and configured, which is used by the Camunda 8 Helm chart to create [persistent volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/). ## 1. Configure AWS and initialize Terraform ### Obtain a copy of the reference architecture The first step is to download a copy of the reference architecture from the [GitHub repository](https://github.com/camunda/camunda-deployment-references/blob/main/aws/openshift/rosa-hcp-single-region/). This material will be used throughout the rest of this documentation, the reference architecture is versioned using the same Camunda versions (`stable/8.x`). ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/openshift/rosa-hcp-single-region/procedure/get-your-copy.sh ``` With the reference architecture copied, you can proceed with the remaining steps outlined in this documentation. Ensure that you are in the correct directory before continuing with further instructions. ### OpenShift cluster module setup This module sets up the foundational configuration for ROSA HCP and Terraform usage. We will leverage [Terraform modules](https://developer.hashicorp.com/terraform/language/modules), which allow us to abstract resources into reusable components, simplifying infrastructure management. The [Camunda-provided module](https://github.com/camunda/camunda-deployment-references/tree/main/aws/openshift/rosa-hcp-single-region/terraform/cluster/) is publicly available and serves as a robust starting point for deploying a Red Hat OpenShift cluster on AWS using a Hosted Control Plane. It is highly recommended to review this module before implementation to understand its structure and capabilities. :::note This module is based on the official [ROSA HCP Terraform module documentation](https://docs.openshift.com/rosa/rosa_hcp/terraform/rosa-hcp-creating-a-cluster-quickly-terraform.html). It is presented as an example for running Camunda 8 in ROSA. **For production or advanced use cases or custom setups, we encourage you to use the [official module](https://docs.openshift.com/rosa/rosa_hcp/terraform/rosa-hcp-creating-a-cluster-quickly-terraform.html)**, which includes vendor-supported features. ::: Verify your AWS quotas: ```bash rosa verify quota --region="$AWS_REGION" ``` :::note This may fail due to organizational policies. ::: #### Set up the ROSA cluster module 1. Ensure you are in the [reference architecture directory of the cloned repository](#obtain-a-copy-of-the-reference-architecture): `./aws/openshift/rosa-hcp-single-region/terraform/`. Then, navigate into the `cluster` module: ```bash ls # Example output: # cluster vpn cd cluster ``` 2. Review the module configuration file `config.tf`. This configuration will use the previously created S3 bucket for storing the Terraform state file: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/openshift/rosa-hcp-single-region/terraform/cluster/config.tf ``` 3. Edit the `cluster.tf` file in the same directory as your `config.tf` file: :::note Configure your cluster Customize the cluster name, availability zones, with the values you previously retrieved from the Red Hat Console. Additionally, provide a secure username and password for the cluster administrator. Ensure that you have set the environment variable `RHCS_TOKEN` with your [OpenShift Cluster Management API Token](https://console.redhat.com/openshift/token/rosa). ::: :::note Private cluster By default, this cluster is accessible from the internet. If you prefer to restrict access, set `locals.rosa_private_cluster = true`. This will create a [private cluster](https://cloud.redhat.com/experts/rosa/private-link/) that is only accessible through the [private subnets](https://docs.aws.amazon.com/vpc/latest/userguide/configure-subnets.html) of your VPC. Optionally, you can still expose a public ingress: [learn more](https://cloud.redhat.com/experts/rosa/private-link/public-ingress/). ⚠️ Since private subnets are not reachable from the internet, you'll need to establish a connection between your network and the cluster. This can be done using a [bastion host](https://docs.aws.amazon.com/mwaa/latest/userguide/tutorials-private-network-bastion.html) or a Client VPN. See the [VPN module setup](#vpn-module-setup) to configure an [AWS VPN Endpoint](https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/cvpn-getting-started.html) for secure access to the private cluster. ::: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/openshift/rosa-hcp-single-region/terraform/cluster/cluster.tf ``` :::caution Camunda Terraform module This ROSA module is based on the [official Red Hat Terraform module for ROSA HCP](https://registry.terraform.io/modules/terraform-redhat/rosa-hcp/rhcs/latest). Please be aware of potential differences and choices in implementation between this module and the official one. We invite you to consult the [Camunda ROSA module documentation](https://github.com/camunda/camunda-deployment-references/blob/main/aws/modules/rosa-hcp/README.md) for more information. ::: 4. After setting up the terraform files and ensuring your AWS authentication is configured, initialize your Terraform project, then, initialize Terraform to [configure the backend](#create-an-s3-bucket-for-terraform-state-management) and download necessary provider plugins: ```bash export S3_TF_BUCKET_KEY_CLUSTER="camunda-terraform/cluster.tfstate" echo "Storing cluster terraform state in s3://$S3_TF_BUCKET_NAME/$S3_TF_BUCKET_KEY_CLUSTER" terraform init -backend-config="bucket=$S3_TF_BUCKET_NAME" -backend-config="key=$S3_TF_BUCKET_KEY_CLUSTER" -backend-config="region=$S3_TF_BUCKET_REGION" ``` Terraform will connect to the S3 bucket to manage the state file, ensuring remote and persistent storage. 5. Configure user access to the cluster. By default, the user who creates the OpenShift cluster has administrative access. If you want to grant access to other users, follow the [Red Hat documentation for granting admin rights to users](https://docs.openshift.com/rosa/cloud_experts_tutorials/cloud-experts-getting-started/cloud-experts-getting-started-admin-rights.html) when the cluster is created. 6. Customize the cluster setup. The module offers various input options that allow you to further customize the cluster configuration. For a comprehensive list of available options and detailed usage instructions, refer to the [ROSA module documentation](https://github.com/camunda/camunda-deployment-references/blob/main/aws/modules/rosa-hcp/README.md). #### Define outputs **Terraform** allows you to define outputs, which make it easier to retrieve important values generated during execution, such as cluster endpoints and other necessary configurations for Helm setup. Each module that you have previously set up contains an output definition at the end of the file. You can adjust them to your needs. #### Execution :::note Secret management We strongly recommend managing sensitive information (for example, the administrator username and password) using a secure secrets management solution like HashiCorp Vault. For details on how to inject secrets directly into Terraform via Vault, see the [Terraform Vault Secrets Injection Guide](https://developer.hashicorp.com/terraform/tutorials/secrets/secrets-vault). ::: 1. Plan the configuration files: ```bash terraform plan -out cluster.plan # describe what will be created ``` 2. After reviewing the plan, you can confirm and apply the changes. ```bash terraform apply cluster.plan # creates the resources ``` Terraform will now create the OpenShift cluster with all the necessary configurations. The completion of this process may require approximately 20-30 minutes for each component. ### VPN module setup This section guides you through setting up an AWS VPN Endpoint to access a private cluster. This step is **optional** and only necessary if you have configured a **private cluster**. Using a VPN offers a flexible and secure way to connect to the private subnets within your VPC. It can be used either by a user to access cluster resources or to enable cross-site communications via [PrivateLink](https://docs.aws.amazon.com/vpc/latest/privatelink/what-is-privatelink.html). This module focuses on user access. _Infrastructure diagram for a single region ROSA setup with VPN (click on the image to open the PDF version)_ [![Infrastructure Diagram ROSA Single-Region VPN](./assets/rosa-single-region-vpn.jpg)](./assets/rosa-single-region-vpn.pdf) AWS VPN technology is compatible with OpenVPN clients. It uses [x509 certificates](https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/mutual.html) for mutual authentication and source verification. The encryption provided by these certificates ensures that traffic can securely transit over the internet to the AWS VPN endpoint, which performs NAT and routes the traffic directly into the private subnets. This VPN endpoint thus becomes the sole access point to the private cluster. #### Retrieve the VPC cluster ID To create the VPN Endpoint in your cluster’s VPC, you need to retrieve the VPC ID using [Terraform outputs](https://developer.hashicorp.com/terraform/language/values/outputs) from the [OpenShift cluster module](#openshift-cluster-module-setup). Follow these steps: 1. Ensure you are in the [reference architecture directory of the cloned repository](#obtain-a-copy-of-the-reference-architecture): `./aws/openshift/rosa-hcp-single-region/terraform/`. Navigate to the `cluster` module directory inside your reference architecture repository, for example: ```bash ls # Example output: # cluster vpn cd cluster ``` 2. Export the [VPC ID](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc) to an environment variable: ```bash export CLUSTER_VPC_ID="$(terraform output -raw vpc_id)" echo "CLUSTER_VPC_ID=$CLUSTER_VPC_ID" ``` #### Set up the VPN module From the parent directory containing your cluster module, go to the `vpn` directory which holds the VPN endpoint configuration. This setup creates a Certificate Authority (CA) for AWS VPN to perform encryption and mutual client authentication. For simplicity, the CA and generated certificates are stored in the project’s Terraform state (`tfstate`). You may customize this as needed. Start by reviewing the `config.tf` file that configures the S3 backend for Terraform state management: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/openshift/rosa-hcp-single-region/terraform/vpn/config.tf ``` Then, review `vpn.tf`, which describes the VPC Client Endpoint configuration: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/openshift/rosa-hcp-single-region/terraform/vpn/vpn.tf ``` This VPN Client Endpoint follows [AWS best practices and constraints](https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/what-is-best-practices.html): - Uses a client CIDR range that does not overlap with the VPC CIDR or any manually added VPN route table routes. - Implements [split-tunnel routing](https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/split-tunnel-vpn.html), so only traffic destined for the VPC goes through the VPN, minimizing bandwidth use. - Supports IPv4 only and is bound to the VPC’s private subnets. 1. Set your Terraform state key and initialize Terraform with the S3 [backend](#create-an-s3-bucket-for-terraform-state-management) and download necessary provider plugins: ```bash export S3_TF_BUCKET_KEY_VPN="camunda-terraform/vpn.tfstate" echo "Storing cluster terraform state in s3://$S3_TF_BUCKET_NAME/$S3_TF_BUCKET_KEY_VPN" terraform init -backend-config="bucket=$S3_TF_BUCKET_NAME" -backend-config="key=$S3_TF_BUCKET_KEY_VPN" -backend-config="region=$S3_TF_BUCKET_REGION" ``` Terraform will connect to the S3 bucket to manage the state file, ensuring remote and persistent storage. 2. For each client connecting to the cluster, assign a unique name in `client_key_names` to simplify certificate revocation. 3. By default, VPN access is allowed from any IP address. You may restrict access by adjusting the `vpn_allowed_cidr_blocks` variable. 4. Network designs vary; please review and adjust the configuration to fit your topology. 5. Customize the VPN module by referring to the [VPN module documentation](https://github.com/camunda/camunda-deployment-references/blob/main/aws/modules/vpn/README.md). #### Outputs The module stores certificates and VPN client configurations in the Terraform state. The next section explains how to retrieve and use client configurations. #### Execution 1. Generate a Terraform plan for the VPN Client Endpoint configuration. This will use the private subnets of the designated VPC: ```bash # describe what will be created terraform plan -out vpn.plan \ -var vpc_id="$CLUSTER_VPC_ID" ``` 2. Review and apply the plan to create the resources: ```bash terraform apply vpn.plan # creates the resources ``` Creation of the VPN Client Endpoint typically takes about 10 minutes. After completion, the client configurations will be available in the Terraform output `vpn_client_configs`. ### Reference files Depending on the installation path you have chosen, you can find the reference files used on this page: - **Standard installation:** [Reference Files](https://github.com/camunda/camunda-deployment-references/tree/main/aws/openshift/rosa-hcp-single-region/) ## 2. Preparation for Camunda 8 installation ### Access to the private network using the VPN This section applies if you have previously created a private cluster and want to access it using the [VPN module configured earlier](#vpn-module-setup). 1. Navigate to the VPN module directory (`vpn`): ```bash pwd # Example output: # ./camunda-deployment-references/aws/openshift/rosa-hcp-single-region/terraform/vpn/ ``` 2. Generate your client’s VPN configuration file. This file is compatible with [OpenVPN (ovpn)](https://openvpn.net/) format: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/common/procedure/vpn/gather-vpn-config.sh ``` 3. Import the generated configuration file (`my-client.ovpn`) into an OpenVPN client: - _(preferred)_ [Official AWS VPN Client](https://docs.aws.amazon.com/vpn/latest/clientvpn-user/connect-aws-client-vpn-connect.html) - [Other OpenVPN Clients](https://docs.aws.amazon.com/vpn/latest/clientvpn-user/connect.html) 4. Once the VPN client is connected, you will have secure access to the VPC’s private network. ### Access the created OpenShift cluster You can access the created OpenShift cluster using the following steps: 1. Verify that you are in the [OpenShift clusters module](#openshift-clusters-module-setup) directory `clusters`: ```bash pwd # Example output: # ./camunda-deployment-references/aws/openshift/rosa-hcp-single-region/terraform/cluster/ ``` 2. Set up the required environment variables from the OpenShift terraform module: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/openshift/rosa-hcp-single-region/procedure/gather-cluster-login-id.sh ``` 3. If you want to give cluster administrator access to the created user, this is not required for a standard installation but can be useful for debugging: ```shell rosa grant user cluster-admin --cluster="$CLUSTER_NAME" --user="$CLUSTER_ADMIN_USERNAME" ``` 4. Log in to the OpenShift cluster: ```shell oc login -u "$CLUSTER_ADMIN_USERNAME" "$CLUSTER_API_URL" -p "$CLUSTER_ADMIN_PASSWORD" ``` Clean up and configure the kubeconfig context: ```shell oc config rename-context $(oc config current-context) "$CLUSTER_NAME" oc config use-context "$CLUSTER_NAME" ``` 5. Verify your connection to the cluster with `oc`: ```shell oc get nodes ``` 6. Create a project for Camunda using `oc`: ```shell export CAMUNDA_NAMESPACE="camunda" oc new-project "$CAMUNDA_NAMESPACE" ``` In the remainder of the guide, the `CAMUNDA_NAMESPACE` variable represents the namespace used to create required resources in the Kubernetes cluster, such as secrets and one-time setup jobs. ## 3. Install Camunda 8 using the Helm chart Now that you've exported the necessary values, you can proceed with installing Camunda 8 using Helm charts. Follow the guide [Camunda 8 on OpenShift](/self-managed/deployment/helm/cloud-providers/openshift/redhat-openshift.md) for detailed instructions on deploying the platform to your OpenShift cluster. --- ## Install Camunda 8 on an AKS cluster This guide provides a comprehensive walkthrough for installing the Camunda 8 Helm chart on your existing Azure Kubernetes Service (AKS) cluster and confirming that it is working as intended. ## Requirements - A Kubernetes cluster; refer to the [Terraform guide](./terraform-setup.md) for details. - [Helm](https://helm.sh/docs/intro/install/) - [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) to interact with the cluster. - [jq](https://jqlang.github.io/jq/download/) to interact with some variables. - [GNU envsubst](https://www.man7.org/linux/man-pages/man1/envsubst.1.html) to generate manifests. - A namespace to host Camunda; in this guide we will reference `camunda` as the target namespace. - (optional) Custom domain name/[DNS zone](https://learn.microsoft.com/en-us/azure/dns/dns-zones-records) in Azure DNS. This allows you to expose Camunda 8 endpoints and connect via community-supported [zbctl](https://github.com/camunda-community-hub/zeebe-client-go/blob/main/cmd/zbctl/zbctl.md) or [Camunda Modeler](https://camunda.com/download/modeler/). - (optional) Permissions to install Kubernetes operators (cluster-admin or equivalent) to deploy infrastructure services such as Elasticsearch, PostgreSQL, and Keycloak. This guide installs them directly from source to provide full control over versions and configuration. For the tool versions used, check the [.tool-versions](https://github.com/camunda/camunda-deployment-references/blob/main/.tool-versions) file in the related repository. This contains an up-to-date list of versions we also use for testing. ## Architecture In addition to the infrastructure diagram provided in the [Terraform setup guide](./terraform-setup.md), this section installs Camunda 8 following the architecture described in the [reference architecture](/self-managed/reference-architecture/reference-architecture.md). The architecture includes the following core components: - **Orchestration Cluster**: Core process execution engine (Zeebe, Operate, Tasklist, and Admin) - **Web Modeler and Console**: Management and design tools (Web Modeler, Console, and Management Identity) To demonstrate how to deploy with a custom domain, the following stack is also included: - **cert-manager**: Automates TLS certificate management with [Let's Encrypt](https://letsencrypt.org/) - **external-dns**: Manages DNS records in Azure DNS for domain ownership confirmation - **ingress-nginx**: Provides HTTP/HTTPS load balancing and routing to Kubernetes services ### Considerations While this guide is primarily tailored for UNIX systems, it can also be run under Windows by utilizing the [Windows Subsystem for Linux](https://learn.microsoft.com/windows/wsl/about). Multi-tenancy is disabled by default and is not covered further in this guide. If you decide to enable it, you may use the same PostgreSQL instance and add an extra database for multi-tenancy purposes. [Workload Identities](https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview) offer a way to connect to Azure-managed PostgreSQL. This is not yet supported by Camunda. ### Secondary storage This guide supports two [secondary storage](/self-managed/concepts/secondary-storage/index.md) backends. If you have not chosen a variant yet, refer to the [Terraform setup guide](./terraform-setup.md#variants) for details. | Variant | Secondary storage | Optimize | Reference architecture | | ----------------- | ---------------------------------------------------------------------------------------------------------------------- | ------------- | ------------------------- | | **Elasticsearch** | [Elasticsearch via ECK](/self-managed/deployment/helm/configure/operator-based-infrastructure.md#deploy-elasticsearch) | Supported | `aks-single-region` | | **RDBMS** | [Azure Database for PostgreSQL](/self-managed/deployment/helm/configure/database/rdbms.md) | Not available | `aks-single-region-rdbms` | :::note Select a variant using the **Elasticsearch**/**RDBMS** tabs throughout this guide. All tabbed sections will switch together automatically. ::: ## Export environment variables To streamline execution of the following commands, we recommend exporting multiple environment variables. ### Export the Helm chart version The following are the required environment variables with some example values: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/chart-env.sh ``` ### Export database values When using standard authentication (username and password), specific environment variables must be set with valid values. Follow the guide for [Terraform](./terraform-setup.md#set-up-azure-authentication) to set them correctly. Verify the configuration of your environment variables by running the following loop: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/procedure/check-env-variables.sh ``` The RDBMS variant requires additional orchestration database variables (`DB_ORCHESTRATION_NAME`, `DB_ORCHESTRATION_USERNAME`, `DB_ORCHESTRATION_PASSWORD`) on top of the base configuration: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region-rdbms/procedure/check-env-variables.sh ``` ## (Optional) Ingress Setup :::info Domain or domainless installation If you do not have a domain name, external access to Camunda 8 web endpoints from outside the Azure Virtual Network will not be possible. In this case, skip the DNS setup and proceed directly to [deploying Camunda 8 via Helm charts](#deploy-camunda-8-via-helm-charts). Alternatively, you can use `kubectl port-forward` to access Camunda without a domain or Ingress configuration. For more information, see the [kubectl port-forward documentation](https://kubernetes.io/docs/reference/kubectl/generated/kubectl_port-forward/). Throughout the rest of this guide, we refer to configurations as **"With domain"** or **"Without domain"**, depending on whether the application is exposed through a domain. ::: In this section, we provide an optional setup guide for configuring an Ingress with TLS and DNS management, allowing you to access your application through a specified domain. If you haven't set up an Ingress, refer to the [Kubernetes Ingress documentation](https://kubernetes.io/docs/concepts/services-networking/ingress/) for more details. In Kubernetes, an Ingress is an API object that manages external access to services in a cluster, typically over HTTP, and can also handle TLS encryption for secure connections. To monitor your Ingress setup using Azure Monitor, you may find the official guide on [monitoring ingress controllers with Azure Monitor and Prometheus](https://learn.microsoft.com/en-us/azure/azure-monitor/containers/container-insights-prometheus-integration) helpful. Additionally, for detailed steps on exposing Kubernetes applications with the nginx ingress controller on Azure, refer to the [official Azure tutorial](https://learn.microsoft.com/en-us/azure/aks/ingress-basic). ### Export Values Set the following values for your Ingress configuration: ```shell reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/export-ingress-setup-vars.sh ``` Additionally, before proceeding, export the following environment variables. These will be used throughout this guide for configuring DNS, certificates, and other Azure resources: ```shell reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/procedure/export-domain-setup-vars.sh ``` These variables will be referenced in later steps, so make sure they are set in your current shell session before continuing. ### ingress-nginx [Ingress-nginx](https://github.com/kubernetes/ingress-nginx) is an open-source Kubernetes Ingress controller that provides a way to manage external access to services within a Kubernetes cluster. It acts as a reverse proxy and load balancer, routing incoming traffic to the appropriate services based on rules defined in the Ingress resource. The following installs `ingress-nginx` in the `ingress-nginx` namespace via Helm. For more configuration options, consult the [Helm chart](https://github.com/kubernetes/ingress-nginx/tree/main/charts/ingress-nginx). ```shell reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/procedure/install-ingress-nginx.sh ``` For a step-by-step walkthrough (and the full list of Azure-specific annotations) see the Microsoft Learn article [“Create an unmanaged NGINX ingress controller in AKS.”](https://learn.microsoft.com/en-us/troubleshoot/azure/azure-kubernetes/load-bal-ingress-c/create-unmanaged-ingress-controller) ### external-dns [External-dns](https://github.com/kubernetes-sigs/external-dns) is a Kubernetes add-on that automates the management of DNS records for external resources, such as load balancers or Ingress controllers. It monitors the Kubernetes resources and dynamically updates the DNS provider with the appropriate DNS records. The following installs `external-dns` in the `external-dns` namespace via Helm. For more configuration options, consult the [Helm chart](https://github.com/kubernetes-sigs/external-dns/tree/master/charts/external-dns). Consider setting `domainFilters` via `--set` to restrict access to certain hosted zones. To enable external-dns to work with Azure Managed Identities, create the Kubernetes secret directly using the exported environment variables: ```shell reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/procedure/external-dns-azure-config.sh ``` :::danger Uniqueness of txtOwnerId for DNS If you are already running `external-dns` in a different cluster, ensure each instance has a **unique** `txtOwnerId` for the TXT record. Without unique identifiers, the `external-dns` instances will conflict and inadvertently delete existing DNS records. In the example below, it's set to `external-dns` and should be changed if this identifier is already in use. Consult the [documentation](https://kubernetes-sigs.github.io/external-dns/v0.15.0/#note) to learn more about DNS record ownership. ::: ```shell reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/procedure/install-external-dns.sh ``` ### cert-manager [Cert-manager](https://cert-manager.io/) is an open-source Kubernetes add-on that automates the management and issuance of TLS certificates. It integrates with various certificate authorities (CAs) and provides a straightforward way to obtain, renew, and manage SSL/TLS certificates for your Kubernetes applications. To simplify the installation process, it is [recommended](https://cert-manager.io/docs/installation/helm/#3-install-customresourcedefinitions) to install the cert-manager `CustomResourceDefinition` resources before installing the chart. This separate step allows for easy uninstallation and reinstallation of cert-manager without deleting any custom resources that have been installed. ```shell reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/install-cert-manager-crds.sh ``` The following installs `cert-manager` in the `cert-manager` namespace via Helm. For more configuration options, consult the [Helm chart](https://artifacthub.io/packages/helm/cert-manager/cert-manager). The supplied settings also configure `cert-manager` to ease the certificate creation by setting a default issuer, which allows you to add a single annotation on an Ingress to request the relevant certificates. ```shell reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/procedure/install-cert-manager.sh ``` Create a `ClusterIssuer` via `kubectl` to enable cert-manager to request certificates from [Let's Encrypt](https://letsencrypt.org/): After exporting the above values, follow up with: ```shell reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/procedure/install-cert-manager-issuer.sh ``` ## Identity Provider (IdP) setup ## Deploy Camunda 8 via Helm charts For more configuration options, refer to the [Helm chart documentation](https://artifacthub.io/packages/helm/camunda/camunda-platform#parameters). Additionally, explore our existing resources in the [Camunda 8 Helm chart](/self-managed/deployment/helm/install/quick-install.md) and [related guides](/self-managed/deployment/helm/configure/index.md). Depending on your installation path, you may use different settings. For easy and reproducible installations, we will use YAML files to configure the chart. ### 1. Create the `values.yml` file Start by creating a `values.yml` file to store the configuration for your environment. This file will contain key-value pairs that will be substituted using `envsubst`. You can find a reference example of this file here: :::note Database initialization prerequisite If you're using an external Azure Database for PostgreSQL, you must create the individual component databases (Identity and Web Modeler) before installing the Helm chart. This initialization step is covered in the [Configure the database and associated access](./terraform-setup.md#configure-the-database-and-associated-access) section of the Terraform setup guide. Without this step, Management Identity and Web Modeler will fail to connect to their databases. ::: The following makes use of the [combined Ingress setup](/self-managed/deployment/helm/configure/ingress/ingress-setup.md#combined-ingress-setup) by deploying a single Ingress for all HTTP components and a separate Ingress for the gRPC endpoint. :::info Cert-manager annotation for domain installation The annotation `kubernetes.io/tls-acme=true` will be [interpreted by cert-manager](https://cert-manager.io/docs/usage/ingress/) and automatically results in the creation of the required certificate request, easing the setup. ::: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/helm-values/values-domain.yml ``` The RDBMS values file disables Elasticsearch and Optimize, and configures PostgreSQL as the secondary storage for the orchestration cluster: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region-rdbms/helm-values/values-domain.yml ``` :::danger Exposure of the Zeebe Gateway Service For secure operation, do not publicly expose the Zeebe Gateway Service. Keep it reachable only within your Azure Virtual Network (for example, by deploying it without a public Ingress) and access it from internal services or over a private network extension such as an Azure VPN or ExpressRoute connection. This reduces external attack surface while preserving controlled operational access. Additionally, implement fine-grained [Kubernetes NetworkPolicies](https://kubernetes.io/docs/concepts/services-networking/network-policies/) to explicitly allow only required internal components to initiate connections to the Zeebe Gateway Service. Deny all other Ingress traffic at the network layer to reduce blast radius if another workload in the cluster is compromised. ::: #### Reference the credentials in secrets Before installing the Helm chart, create Kubernetes secrets to store the database authentication credentials. To create the secrets, run the following commands: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/procedure/create-external-db-secrets.sh ``` The RDBMS variant creates an additional `orchestration-postgres-secret` for the secondary storage database: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region-rdbms/procedure/create-external-db-secrets.sh ``` ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/helm-values/values-no-domain.yml ``` The RDBMS values file disables Elasticsearch and Optimize, and configures PostgreSQL as the secondary storage for the orchestration cluster: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region-rdbms/helm-values/values-no-domain.yml ``` #### Reference the credentials in secrets Before installing the Helm chart, create Kubernetes secrets to store the database authentication credentials. To create the secrets, run the following command for your selected variant: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/procedure/create-external-db-secrets.sh ``` The RDBMS variant creates an additional `orchestration-postgres-secret` for the secondary storage database: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region-rdbms/procedure/create-external-db-secrets.sh ``` ### 2. Configure your deployment #### Enable Enterprise components Web Modeler, Console, and Management Identity are not enabled by default in this deployment. To enable these enterprise components in an OIDC-enabled full cluster, first deploy the required infrastructure (PostgreSQL, Elasticsearch/OpenSearch, and an IdP, such as Keycloak) using the official operators, then apply the Helm values examples shown in [deploy required dependencies with operators](/self-managed/deployment/helm/configure/operator-based-infrastructure.md). ### 3. Deploy prerequisite services Before deploying Camunda, you need to deploy the infrastructure services it depends on. The core infrastructure (Elasticsearch) can be deployed using Kubernetes operators as described in [Deploy infrastructure with Kubernetes operators](/self-managed/deployment/helm/configure/operator-based-infrastructure.md). - **Elasticsearch** (this guide): Deployed via [ECK (Elastic Cloud on Kubernetes)](https://www.elastic.co/guide/en/cloud-on-k8s/current/index.html) - **RDBMS** (alternative): Use a managed Azure Database for PostgreSQL or another supported RDBMS engine — see the [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) All deploy scripts are located in `generic/kubernetes/operator-based/`. Review each script before executing to understand the deployment steps, and adapt the operator Custom Resource configurations for your specific requirements (resource limits, storage, replicas, etc.). :::note Working directory All commands in this guide assume you are at the **repository root** (the directory created by `get-your-copy.sh`). The deploy commands below use subshells `(cd ... && ./deploy.sh)` to preserve your working directory. ::: ##### Deploy Elasticsearch {#deploy-elasticsearch} If your organization does not want to use a managed Elasticsearch service, ECK Operator is an option. In this guide, we default to the ECK Operator deployment of Elasticsearch. :::note RDBMS alternative For those who prefer a relational database, RDBMS (for example, [Azure Database for PostgreSQL](https://azure.microsoft.com/products/postgresql)) is a supported alternative to Elasticsearch for the Orchestration Cluster's secondary storage. See the [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) for supported engines. To use RDBMS instead, skip this section and see [configure RDBMS in Helm](/self-managed/deployment/helm/configure/database/rdbms.md). ::: :::warning Production Elasticsearch recommendation For production workloads, we recommend using an externally managed Elasticsearch service (for example, [Elastic Cloud on Azure](https://azuremarketplace.microsoft.com/en-us/marketplace/apps/elastic.ec-azure-pp)). Terraform support for Elastic Cloud on Azure can be restrictive but remains a viable option. ::: #### Merge operator overlays into values Once the operator-managed services are running, merge the corresponding Helm values overlays into your `values.yml` file. These overlays configure Camunda components to use the external operator-managed services instead of embedded subcharts. Merge the **Elasticsearch** overlay: ```bash yq '. *+ load("generic/kubernetes/operator-based/elasticsearch/camunda-elastic-values.yml")' values.yml > values-merged.yml && mv values-merged.yml values.yml ``` The RDBMS variant does **not** require Elasticsearch or the ECK Operator. The Helm values already configure PostgreSQL as the secondary storage, and Elasticsearch is disabled. Skip directly to [filling your deployment with actual values](#fill-your-deployment-with-actual-values). #### Fill your deployment with actual values {#fill-your-deployment-with-actual-values} Once you've prepared the `values.yml` file, run the following `envsubst` command to substitute the environment variables with their actual values: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/assemble-envsubst-values.sh ``` :::note Web Modeler SMTP secret If you plan to enable Web Modeler, create the SMTP secret required for email notifications ([see how it's used by Web Modeler](/self-managed/components/hub/configuration/modeler-configuration.md#smtp--email)): ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/procedure/create-webmodeler-secret.sh ``` ::: ### 4. Install Camunda 8 using Helm Now that the `generated-values.yml` is ready, you can install Camunda 8 using Helm. Run the following command: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/install-chart.sh ``` This script: - Installs (or upgrades) Camunda using the Helm chart. - Substitutes the appropriate version using the `$CAMUNDA_HELM_CHART_VERSION` environment variable. - Applies the configuration from `generated-values.yml`. You can track the progress of the installation using the following command: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/check-deployment-ready.sh ``` ## Verify connectivity to Camunda 8 First, we need an OAuth client to be able to connect to the Camunda 8 cluster. ### Generate an M2M token using Identity Generate an M2M token by following the steps outlined in the [Identity getting started guide](/self-managed/components/management-identity/overview.md), along with the [incorporating applications documentation](/self-managed/components/management-identity/application-user-group-role-management/applications.md). Below is a summary of the necessary instructions: 1. Open Identity in your browser at `https://${CAMUNDA_DOMAIN}/managementidentity`. You will be redirected to your IdP and prompted to log in. 2. Log in with the initial user `admin` (defined in `identity.firstUser` of the values file). Retrieve the generated password (created earlier when running the secret creation script) from the Kubernetes secret and use it to authenticate: ```shell kubectl get secret identity-secret-for-components \ --namespace "$CAMUNDA_NAMESPACE" \ -o jsonpath='{.data.identity-first-user-password}' | base64 -d; echo ``` 3. Select **Add application** and select **M2M** as the type. Assign a name like "test." 4. Select the newly created application. Then select **Access to APIs > Assign permissions** and assign the **Orchestration API** "read" and "write" permissions. 5. Retrieve the `client-id` and `client-secret` values from the application details ```shell export ZEEBE_CLIENT_ID='client-id' # retrieve the value from the identity page of your created m2m application export ZEEBE_CLIENT_SECRET='client-secret' # retrieve the value from the identity page of your created m2m application ``` 6. Open the Orchestration Cluster Admin in your browser at `https://${CAMUNDA_DOMAIN}/admin` and log in with the user `admin` (defined in `identity.firstUser` of the values file). 7. In the Admin navigation menu, select **Roles**. 8. Either select an existing role (for example, **Admin**) or [create a new role](/components/admin/role.md) with the appropriate permissions for your use case. 9. In the selected role view, open the **Clients** tab and click **Assign client**. 10. Enter the client ID of your application created in Management Identity (for example, `test`) and click **Assign client** to save. This operation links the OIDC client to the role's permissions in the Orchestration Cluster, granting the application access to the cluster resources. For more information about managing roles and clients, see [Roles](/components/admin/role.md#manage-clients). Identity and the Orchestration cluster must be port-forwarded to be able to connect to the cluster. If using Keycloak via the Keycloak Operator, you also need to port-forward the Keycloak service. ```shell kubectl port-forward "services/$CAMUNDA_RELEASE_NAME-identity" 8085:80 --namespace "$CAMUNDA_NAMESPACE" kubectl port-forward "services/$CAMUNDA_RELEASE_NAME-zeebe-gateway" 8080:8080 --namespace "$CAMUNDA_NAMESPACE" # If using Keycloak Operator: kubectl port-forward "services/keycloak-service" 18080:18080 --namespace "$CAMUNDA_NAMESPACE" ``` 1. Open Identity in your browser at `http://localhost:8085/managementidentity`. You will be redirected to your IdP and prompted to log in. 2. Log in with the initial user `admin` (defined in `identity.firstUser` of the values file). Retrieve the generated password (created earlier when running the secret creation script) from the Kubernetes secret and use it to authenticate: ```shell kubectl get secret identity-secret-for-components \ --namespace "$CAMUNDA_NAMESPACE" \ -o jsonpath='{.data.identity-first-user-password}' | base64 -d; echo ``` 3. Select **Add application** and select **M2M** as the type. Assign a name like "test". 4. Select the newly created application. Then select **Access to APIs > Assign permissions** and assign the **Orchestration API** "read" and "write" permissions. 5. Retrieve the `client-id` and `client-secret` values from the application details ```shell export ZEEBE_CLIENT_ID='client-id' # retrieve the value from the identity page of your created m2m application export ZEEBE_CLIENT_SECRET='client-secret' # retrieve the value from the identity page of your created m2m application ``` 6. Open the Orchestration Cluster Admin in your browser at `http://localhost:8080/admin` and log in with the user `admin` (defined in `identity.firstUser` of the values file). 7. In the Admin navigation menu, select **Roles**. 8. Either select an existing role (for example, **Admin**) or [create a new role](/components/admin/role.md) with the appropriate permissions for your use case. 9. In the selected role view, open the **Clients** tab and click **Assign client**. 10. Enter the client ID of your application created in Management Identity (for example, `test`) and click **Assign client** to save. This operation links the OIDC client to the role's permissions in the Orchestration Cluster, granting the application access to the cluster resources. For more information about managing roles and clients, see [Roles](/components/admin/role.md#manage-clients). ### Use the token For a detailed guide on generating and using a token, consult the relevant documentation on [authenticating with the Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md). Export the following environment variables: ```shell reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/export-verify-zeebe-domain.sh ``` This requires port-forwarding the Zeebe Gateway to connect to the cluster. ```shell kubectl port-forward "services/$CAMUNDA_RELEASE_NAME-zeebe-gateway" 8080:8080 --namespace "$CAMUNDA_NAMESPACE" ``` Export the following environment variables: ```shell reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/export-verify-zeebe-local.sh ``` Generate a temporary token to access the Orchestration Cluster REST API, then capture the value of the `access_token` property and store it as your token. Use the stored token (referred to as `TOKEN` in this case) to interact with the Orchestration Cluster REST API and display the cluster topology: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/check-zeebe-cluster-topology.sh ``` ...and results in the following output:
Example output ```json reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/check-zeebe-cluster-topology-output.json ```
Follow our existing [Modeler guide on deploying a diagram](/self-managed/components/modeler/desktop-modeler/deploy-to-self-managed.md). Below are the helper values required to be filled in Modeler: The following values are required for the OAuth authentication: - **Cluster endpoint:** `https://zeebe-$CAMUNDA_DOMAIN`, replacing `$CAMUNDA_DOMAIN` with your domain - **Client ID:** Retrieve the client ID value from the identity page of your created M2M application - **Client Secret:** Retrieve the client secret value from the Identity page of your created M2M application - **OAuth Token URL:** Your IdP's token endpoint (for example, `https://$CAMUNDA_DOMAIN/auth/realms/camunda-platform/protocol/openid-connect/token` when using Keycloak), replacing `$CAMUNDA_DOMAIN` with your domain - **Audience:** `orchestration-api`, the default for Camunda 8 Self-Managed This requires port-forwarding the Zeebe Gateway to be able to connect to the cluster: ```shell kubectl port-forward "services/$CAMUNDA_RELEASE_NAME-zeebe-gateway" 26500:26500 --namespace "$CAMUNDA_NAMESPACE" ``` The following values are required for OAuth authentication: - **Cluster endpoint:** `http://localhost:26500` - **Client ID:** Retrieve the client ID value from the identity page of your created M2M application. - **Client Secret:** Retrieve the client secret value from the Identity page of your created M2M application. - **OAuth Token URL:** Your IdP's token endpoint (for example, `http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token` when using Keycloak with port-forwarding) - **Audience:** `orchestration-api`, the default for Camunda 8 Self-Managed.
## Advanced topics The following are some advanced configuration topics to consider for your cluster: - [Camunda production installation guide with Kubernetes and Helm](versioned_docs/version-8.7/self-managed/operational-guides/production-guide/helm-chart-production-guide.md) - [Cluster autoscaling](https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/cloudprovider/azure/README.md) - [Configure RDBMS secondary storage](/self-managed/deployment/helm/configure/database/rdbms.md) - [Secondary storage concepts](/self-managed/concepts/secondary-storage/index.md) To get more familiar with our product stack, visit the following topics: - [Operate](/components/operate/operate-introduction.md) - [Tasklist](/components/tasklist/introduction-to-tasklist.md) - [Optimize](/components/optimize/what-is-optimize.md) (Elasticsearch variant only) --- ## Microsoft AKS Azure Kubernetes Service ([Microsoft AKS](https://azure.microsoft.com/products/kubernetes-service/)) is a managed container orchestration service for deploying and scaling Kubernetes applications in the cloud. Camunda 8 Self-Managed can be deployed on any supported Kubernetes cluster using [Helm charts](/self-managed/deployment/helm/install/quick-install.md). A list of supported environments is available on our [supported environments page](../../../../../../reference/supported-environments.md). Deployment requirements, including cloud provider-specific information, are available in our [Kubernetes deployment reference](/self-managed/reference-architecture/kubernetes.md). ## Guides --- ## Deploy an AKS cluster with Terraform (advanced) This guide provides a detailed tutorial for deploying an Azure Kubernetes Service (AKS) cluster, tailored specifically for deploying Camunda 8 using Terraform, a popular Infrastructure as Code (IaC) tool. This guide is designed to help you leverage the power of Infrastructure as Code (IaC) to streamline and reproduce your cloud infrastructure setup. By walking through the essentials of setting up an AKS cluster, and provisioning managed Azure resources such as Azure Database for PostgreSQL, this guide demonstrates how to use Terraform with Azure. It makes the process accessible even to those new to Terraform or IaC concepts. It utilizes Azure-managed services where available, offering these as optional components for added convenience and maintainability. :::tip If you are completely new to Terraform and the concept of IaC, consider reading the [Terraform IaC documentation](https://developer.hashicorp.com/terraform/tutorials/azure-get-started/infrastructure-as-code) and trying the [interactive quick start](https://developer.hashicorp.com/terraform/tutorials/azure-get-started/infrastructure-as-code#quick-start) for a basic understanding. ::: ## Requirements - An [Azure subscription](https://azure.microsoft.com/free/) and the necessary permissions to create any resource within Azure. - [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli), a CLI tool for creating and managing Azure resources. - [Terraform](https://developer.hashicorp.com/terraform/downloads) for provisioning infrastructure as code. - [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) to interact with your AKS cluster. - [jq](https://stedolan.github.io/jq/download/) to parse and manipulate JSON (e.g. Terraform outputs). - (optional) Custom domain name/[DNS zone](https://learn.microsoft.com/en-us/azure/dns/dns-zones-records) in Azure DNS. This allows you to expose Camunda 8 endpoints to an external network via the configured ingress. - **Azure service quotas** - Check your quotas for **Virtual Networks**, **vCPU cores**, and **Storage Accounts** in the target region: [Azure subscription and service limits](https://learn.microsoft.com/azure/azure-resource-manager/management/azure-subscription-service-limits). - If you reach a limit, you can [request a quota increase through the Azure portal](https://learn.microsoft.com/en-us/azure/extended-zones/request-quota-increase). - This guide uses **GNU Bash** for all shell commands. For the exact tool versions we’ve tested against, see the [.tool-versions](https://github.com/camunda/camunda-deployment-references/blob/main/.tool-versions) file in the repository. ### Considerations This setup provides a basic foundation for getting started with Camunda 8 on AKS, but it is not fully optimized for performance. It serves as a good starting point for building out a production-ready environment by incorporating [IaC tooling](https://developer.hashicorp.com/terraform/tutorials/azure-get-started/infrastructure-as-code). To try out Camunda 8 or for development purposes, consider signing up for our [SaaS offering](https://camunda.com/platform/). If you already have an AKS cluster, you can skip ahead to the [Helm guide](./aks-helm.md). To keep this guide simple and focused, certain best practices are referenced via links to additional documentation, allowing you to explore each area in more detail when you're ready. :::warning Reference architectures are not intended to be consumed exactly as described. The examples provided in this guide are not packaged as a reusable Terraform module. It is recommended that you clone the repository and make any necessary modifications locally. This approach allows you to extend and customize the codebase according to your specific needs. However, note that maintaining the infrastructure is your responsibility. Camunda will continue to update and improve the reference architecture, and these updates may not be backward compatible. You may incorporate updates into your customized codebase as needed. ::: :::danger Cost management Following this guide will incur costs on your Azure account, including charges for Azure Kubernetes Service (AKS), the compute (virtual machine instances) for the underlying nodes, Azure Managed Disks for persistent volumes, and Azure DNS zones for domain resolution. For more information, refer to the [AKS pricing page](https://azure.microsoft.com/pricing/details/kubernetes-service/) and the [Azure pricing calculator](https://azure.microsoft.com/pricing/calculator/), as costs depend on region and configuration choices. ::: ### Variants This guide supports two secondary storage variants for the Orchestration Cluster. Choose the one that fits your requirements: | Aspect | Elasticsearch | RDBMS (PostgreSQL) | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | | Secondary storage | [Elasticsearch via ECK Operator](/self-managed/deployment/helm/configure/operator-based-infrastructure.md#deploy-elasticsearch) | [Azure Database for PostgreSQL](/self-managed/deployment/helm/configure/database/rdbms.md) | | Optimize | Supported | Not available (requires Elasticsearch) | | Infrastructure | AKS + PostgreSQL + Elasticsearch cluster | AKS + PostgreSQL only (lighter footprint) | | Orchestration database | Not required | Additional `camunda_orchestration` database | For more details on secondary storage, see [Secondary storage concepts](/self-managed/concepts/secondary-storage/index.md). :::note Select a variant using the **Elasticsearch**/**RDBMS** tabs throughout this guide. All tabbed sections will switch together automatically. ::: #### How to choose - If you need **Optimize** for analytics or prefer the proven Elasticsearch-based setup, choose the **Elasticsearch** variant. - If you want a **lighter infrastructure** without managing an Elasticsearch cluster and do not need Optimize, choose the **RDBMS** variant. RDBMS secondary storage is available as of Camunda 8.9. #### Security The following security considerations were relaxed to streamline adoption and development. These should be reassessed and hardened before deploying to production. The following items were identified using [Trivy](https://trivy.dev/) and can be looked up in the [Aqua vulnerability database](https://avd.aquasec.com/). These concessions are intentional in this reference infrastructure to simplify onboarding, allow internal-only access, and minimize friction during evaluation. They are not appropriate for production and must be revisited. This section explains common security findings in Azure deployments and provides guidance on how to address them.
AVD-AZU-0047 (CRITICAL): Security group rule allows unrestricted ingress from any IP address #### Reasoning This rule permits inbound traffic from `0.0.0.0/0`, meaning any external source can reach the AKS subnet. It may expose workloads or future public IPs to unsolicited access, increasing the risk of compromise—especially if internal services are misconfigured. #### Potential resolution - Restrict incoming traffic to specific IP addresses or CIDR ranges that need access. - For management access, limit SSH/RDP to your company's IP ranges. - Use just-in-time access for administrative purposes. - Implement a bastion host/jump box for secure access. - Consider using [Azure Private Link](https://learn.microsoft.com/en-us/azure/private-link/private-link-overview) for private connectivity. > **Note:** This doesn't affect the AKS control plane directly, but still weakens the overall network boundary.
AVD-AZU-0041 (CRITICAL): Cluster does not limit API access to specific IP addresses #### Reasoning This finding shows that your Kubernetes cluster's API server is accessible from any IP address. The API server is the control plane for Kubernetes and unrestricted access increases the risk of unauthorized access and potential attacks. #### Potential resolution - Configure `authorized_ip_ranges` in `api_server_access_profile` to restrict API server access. ([Review the related documentation](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/kubernetes_cluster#api_server_access_profile)). - Enable private cluster mode with `private_cluster_enabled = true`. ([Review the related documentation](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/kubernetes_cluster#private_cluster_enabled)). - Create an `azurerm_private_endpoint` for the AKS Private Link service. ([Review the related documentation](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint)). - Enable Azure AD–based RBAC via `role_based_access_control { azure_active_directory { ... } }`. ([Review the related documentation](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/kubernetes_cluster#role_based_access_control)). - Use `azurerm_network_security_group` and `azurerm_network_security_rule` to restrict access to the control-plane subnet. ([NSG](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_security_group), [rule](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_security_rule)). > **Note:** While open API access simplifies testing and development, production clusters should always restrict API server access to known IP ranges.
AVD-AZU-0013 (CRITICAL): Vault network ACL does not block access by default #### Reasoning This finding indicates that your Azure Key Vault network access controls are not configured to deny access by default. This means that unless specifically restricted, traffic can reach your Key Vault from any source. #### Potential resolution - Enable the ["Deny" default action](https://learn.microsoft.com/en-us/azure/key-vault/general/how-to-azure-key-vault-network-security) for network ACLs. - Allow [specific IP ranges or virtual networks](https://learn.microsoft.com/en-us/azure/key-vault/general/how-to-azure-key-vault-network-security). - Use a [Private Endpoint](https://learn.microsoft.com/en-us/azure/key-vault/general/private-link-service) for Key Vault access. - Use [service endpoints](https://learn.microsoft.com/en-us/azure/key-vault/general/overview-vnet-service-endpoints) to limit Azure service access. - Enable [Soft Delete](https://learn.microsoft.com/en-us/azure/key-vault/general/soft-delete-overview) and [Purge Protection](https://learn.microsoft.com/en-us/azure/key-vault/general/soft-delete-overview) for recovery and data safety. > **Note:** Default deny configurations provide better security posture but may complicate initial setup and testing. For automated testing environments, clearly document these exceptions.
AVD-AZU-0040 (MEDIUM): Cluster does not have logging enabled via OMS Agent #### Reasoning This finding indicates that comprehensive logging is not enabled on your Kubernetes cluster using the OMS (Operations Management Suite) Agent. Without proper logging, you have limited visibility into cluster operations, making it difficult to detect and respond to security incidents. #### Potential resolution - Enable [Azure Monitor for containers](https://learn.microsoft.com/en-us/azure/azure-monitor/containers/container-insights-overview) on your AKS cluster. - Configure the [OMS Agent](https://learn.microsoft.com/en-us/azure/azure-monitor/containers/container-insights-onboard#enable-using-azure-resource-manager-template-or-terraforms) to collect container logs and metrics. - Set up a [Log Analytics workspace](https://learn.microsoft.com/en-us/azure/azure-monitor/logs/log-analytics-workspace-overview) for centralized log storage. - Create [custom queries and alerts](https://learn.microsoft.com/en-us/azure/azure-monitor/logs/log-analytics-tutorial) based on collected logs. - Consider implementing [Microsoft Defender for Cloud](https://learn.microsoft.com/en-us/azure/defender-for-cloud/defender-for-containers-introduction) (formerly Azure Security Center) for enhanced monitoring and threat detection. > **Note:** While disabling logging simplifies testing environments and reduces costs, production environments should always have comprehensive logging enabled. For testing purposes, consider using a shared Log Analytics workspace with appropriate retention policies.
### Outcome _Infrastructure diagram for a single-region AKS setup (click on the image to open the PDF version)_ [![Infrastructure Diagram AKS Single-Region](./assets/aks-single-region.jpg)](./assets/aks-single-region.pdf) :::note The vnet and the subnets are sized according to standard Azure recommendations by default. Due to Azure CNI, every pod will get assigned a real internal IP. While the defaults are more than sufficient for this guide, if you expect a large number of pods in a single subnet, consider using a larger subnet for AKS like /23 or /22. ::: ## 1. Configure Azure and initialize Terraform ### Obtain a copy of the reference architecture The first step is to download a copy of the reference architecture from the [GitHub repository](https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/). Select the variant matching your [secondary storage choice](#variants). This material will be used throughout the rest of this documentation. The reference architectures are versioned using the same Camunda versions (`stable/8.x`). The provided reference architecture repository allows you to directly reuse and extend the existing Terraform example base. This sample implementation is flexible to extend to your own needs without the potential limitations of a Terraform module maintained by a third party. ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/procedure/get-your-copy.sh ``` ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region-rdbms/procedure/get-your-copy.sh ``` With the reference architecture copied, you can proceed with the remaining steps outlined in this documentation. Ensure that you are in the correct directory before continuing with further instructions. ### Terraform prerequisites To manage the infrastructure for Camunda 8 on Azure using Terraform, we need to set up Terraform's backend to store the state file remotely in an Azure Storage Account. This ensures secure and persistent storage of the state file. :::note Advanced users may want to handle this part differently and use a different backend. The backend setup provided is an example for new users. ::: #### Set up Azure authentication The [Azure Terraform provider](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs) requires authentication using Azure credentials before it can create resources. For all environments, create a dedicated Azure AD service principal and assign only the necessary permissions. You can create and assign roles via the [Azure Portal](https://portal.azure.com/) or with the Azure CLI. To log in using an existing Azure Service Principal, you need the `appId` and `tenant` values associated with the Service Principal. These credentials allow Terraform to authenticate and provision resources in your Azure subscription. If you need help finding your tenant ID, refer to [Find your Azure subscription tenant ID](https://learn.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id). Use the following command to log in (you will be prompted for the password): ```bash az login --service-principal \ -u \ --tenant ``` Replace ``, ``, and `` with the actual values of your Service Principal. Also, ensure that you export the `` by running the below command after logging in as the SP, as it will be needed [in the next step](#creating-terraformtfvars). ```shell export AZURE_SP_ID=$(az account show --query user.name -o tsv) ``` To create a new service principal and assign it the required permissions: Feel free to change the example name. ```bash az ad sp create-for-rbac \ --name "camunda-tf-sp" \ --role Contributor \ --scopes /subscriptions/ ``` This will return a JSON object with `appId`, `password`, and `tenant`. These values are required for login using the service principal: ```bash az login --service-principal \ -u \ --tenant ``` You will be prompted to enter the password interactively. Replace ``, ``, and `` with the actual values of your Service Principal. Also, ensure that you export the `` by running the below command after logging in as the SP, as it will be needed [in the next step](#creating-terraformtfvars). ```shell export AZURE_SP_ID=$(az account show --query user.name -o tsv) ``` #### Creating terraform.tfvars To configure your deployment, create a `terraform.tfvars` file in the root of the reference architecture folder (`aks-single-region` or `aks-single-region-rdbms`). This file defines critical environment-specific settings like your Azure subscription and the Service Principal used for authentication. Follow the guide below to get the necessary values: First, set the following environment variables: ```shell # The domain name that your Azure DNS zone manages export TLD= # The resource group that your Azure DNS zone belongs to export AZURE_DNS_RESOURCE_GROUP= ``` Then, run the following script to create the `terraform.tfvars` file: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/procedure/tfvars-domain.sh ``` ##### subscription_id This value specifies the Azure Subscription ID in which all infrastructure will be deployed, including the AKS cluster, PostgreSQL Flexible Server, and Key Vault. To retrieve your current subscription ID, you can run the following command: ##### terraform_sp_app_id This is the Application (client) ID of the Azure Service Principal that Terraform uses to configure Role-Based Access Control (RBAC). By providing this ID, Terraform ensures that the Service Principal has the necessary access rights to manage and provision resources within your Azure subscription. ##### dns_zone_id This value specifies the full Azure resource ID of the DNS Zone used for managing your custom domain. It is **required** if you are deploying Camunda 8 with a domain name. Terraform uses this value to grant the necessary role-based access control (RBAC) permissions to the `external-dns` Kubernetes add-on, allowing it to create and update DNS records dynamically within your Azure DNS Zone. If this value is missing or incorrect, `external-dns` will not have permission to manage records, and DNS entries for your Camunda 8 endpoints will not be created. Run the following script to create the `terraform.tfvars` file: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/procedure/tfvars-no-domain.sh ``` ##### subscription_id This value specifies the Azure Subscription ID in which all infrastructure will be deployed, including the AKS cluster, PostgreSQL Flexible Server, and Key Vault. To retrieve your current subscription ID, you can run the following command: ##### terraform_sp_app_id This is the Application (client) ID of the Azure Service Principal that Terraform uses to configure Role-Based Access Control (RBAC). By providing this ID, Terraform ensures that the Service Principal has the necessary access rights to manage and provision resources within your Azure subscription. #### Create an Azure Storage Account for Terraform state management Before setting up Terraform, you should create an Azure Storage Account and container to store the state file. This is important for collaboration and to prevent issues like state file corruption. This should be in a separate resource group from the main infrastructure. To start, set the required values as environment variables upfront to avoid repeating them in each command: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/common/procedure/storage-account/storage-account-env-vars.sh ``` Define the value for `AZURE_LOCATION` with your chosen Azure region (for example, `westeurope`). :::warning For production deployments, always use custom names for your Azure Storage Account and related resources. Storage Account names must be **globally unique** across all Azure subscriptions. The provided example (`export AZURE_STORAGE_ACCOUNT_NAME="camundatfstate"`) is for demonstration only and may not be available. Choose a name that is unique and follows your organization's naming conventions to avoid deployment failures. ::: Now, follow these steps to create the storage account with versioning enabled: 1. Run the following script to create a storage account and container for storing your Terraform state. Make sure that you have chosen a globally unique name for the storage account before: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/common/procedure/storage-account/storage-account-creation.sh ``` 2. Enable blob versioning to track changes and protect the state file from accidental deletions or overwrites: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/common/procedure/storage-account/storage-account-versioning.sh ``` 3. Verify versioning is enabled on the blob container: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/common/procedure/storage-account/storage-account-verify.sh ``` This Azure Storage Account will now securely store your Terraform state files with versioning enabled. #### Initialize Terraform Once your authentication is set up, you can initialize your Terraform project. The [previous steps](#create-an-azure-storage-account-for-terraform-state-management) configured a dedicated Azure Storage Account and container (`AZURE_STORAGE_ACCOUNT_NAME`, `AZURE_STORAGE_CONTAINER_NAME`) to store your state. Configure the backend and download the necessary provider plugins: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/common/procedure/storage-account/storage-account-tf-init.sh ``` Terraform will connect to the Azure storage container to manage the state file, ensuring remote and persistent storage. ### Terraform setup This reference architecture uses [Terraform modules](https://developer.hashicorp.com/terraform/language/modules) to deploy all required Azure infrastructure for running Camunda 8 in a production-grade AKS environment. It includes: - A Virtual Network (VNet) and three subnets (AKS, database, private endpoint) - Network Security Group (NSG) for AKS - Azure Kubernetes Service (AKS) cluster with system and user node pools across 3 AZs - Azure PostgreSQL Flexible Server with high availability and private endpoint - Azure Key Vault with encryption key and a user-assigned managed identity for AKS secrets (KMS) #### 1. Main configuration The main deployment logic is defined in [`main.tf`](https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/main.tf). It instantiates all modules and exposes several **customizable values** via the `locals` block: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/main.tf ``` :::warning Azure Key Vault naming Azure Key Vault names must be **globally unique** across all Azure subscriptions. In the linked script, you are prompted to provide a resource prefix, and the Key Vault will be created as `resource_prefix-kv`. Be sure to choose a prefix that results in a unique Key Vault name, or override it in the KMS module to avoid deployment failures. ::: The modules deployed are: - `network` ([network.tf](https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/network.tf)): Virtual network, AKS subnet, DB subnet, and private endpoint subnet - `kms` ([kms.tf](https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/kms.tf)): Key Vault, encryption key, and UAMI for AKS secret encryption - `aks` ([aks.tf](https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/aks.tf)): Cluster deployment with system and user node pools across AZs - `postgres_db` ([db.tf](https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/db.tf)): High-availability PostgreSQL Flexible Server, private DNS, and endpoint #### 2. AKS module This module exposes a customizable **kubernetes_version** value via the `locals` block: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/aks.tf#L1-L3 ``` #### 3. PostgreSQL module This module exposes several **customizable values** via the `locals` block: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/db.tf#L1-L18 ``` These values control database user setup, naming, and passwords. Sensitive values are used by downstream provisioning jobs and Helm secrets. The RDBMS variant extends the base PostgreSQL configuration with an additional **orchestration database** used as the secondary storage for Operate, Tasklist, and the Orchestration Cluster REST API: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region-rdbms/db.tf#L1-L26 ``` In addition to the Identity and Web Modeler databases, this variant creates: - A `camunda_orchestration` database for RDBMS secondary storage - A dedicated `orchestration_db` user with a randomly generated password This module is **enabled by default**. To opt out, you must: - Remove the `db.tf` file from the root - Manually provide credentials and PostgreSQL endpoints for the Helm chart :::tip Alternative: Operator-based PostgreSQL deployment If your organization does not want to use a managed Azure Database for PostgreSQL service, CloudNativePG is an option. For more details on the PostgreSQL deployment with CloudNativePG Operator, see [PostgreSQL deployment in the operator-based infrastructure guide](/self-managed/deployment/helm/configure/operator-based-infrastructure.md#postgresql-deployment) for a production-grade setup with automated scaling, upgrades, and built-in security. ::: ### Execution :::note Secret management We strongly recommend managing sensitive information such as the PostgreSQL username and password using a secure secrets management solution like HashiCorp Vault. For details on how to inject secrets directly into Terraform via Vault, see the [Terraform Vault Secrets Injection Guide](https://developer.hashicorp.com/terraform/tutorials/secrets/secrets-vault). ::: 1. Open a terminal in the chosen reference folder where `config.tf` and other `.tf` files are located. 2. Plan the configuration files: ```bash terraform plan -out cluster.plan # describe what will be created ``` 3. After reviewing the plan, you can confirm and apply the changes: ```bash terraform apply cluster.plan # apply the creation ``` Terraform will now create the AKS cluster with all the necessary configurations. The completion of this process may require approximately 20–30 minutes. ## 2. Preparation for Camunda 8 installation ### Access the created AKS cluster You can gain access to the AKS cluster using the `Azure CLI` with the following command: ```bash # Extract values from terraform output RESOURCE_GROUP=$(terraform output -raw resource_group_name) CLUSTER_NAME=$(terraform output -raw aks_cluster_name) # Echo the values to verify they are not empty echo "RESOURCE_GROUP: $RESOURCE_GROUP" echo "CLUSTER_NAME: $CLUSTER_NAME" # Get credentials using Azure CLI az aks get-credentials \ --resource-group "$RESOURCE_GROUP" \ --name "$CLUSTER_NAME" \ --overwrite-existing ``` After updating the kubeconfig, you can verify your connection to the cluster with `kubectl`: ```bash kubectl get nodes ``` Create a namespace for Camunda: ```bash export CAMUNDA_NAMESPACE="camunda" kubectl create namespace "$CAMUNDA_NAMESPACE" ``` In the remainder of the guide, we reference the `camunda` namespace to create some required resources in the Kubernetes cluster, such as secrets or one-time setup jobs. ### Configure a high-performance StorageClass Camunda 8 requires high IOPS for performance-critical components like **Zeebe**, so it is important to use Azure **PremiumV2** disks rather than the default `Standard_LRS`. :::danger Reclaim policy Using `reclaimPolicy: Delete` can cause **permanent data loss** if a PVC is deleted. Consider using `Retain` for production. See [troubleshooting](/self-managed/operational-guides/troubleshooting.md#zeebe-data-loss-after-pvc-deletion) for details. ::: This step defines a custom `StorageClass` that: - Uses **PremiumV2_LRS** Azure Managed Disks - Sets a **`Retain`** reclaim policy to prevent data loss - Uses `WaitForFirstConsumer` volume binding - Becomes the default StorageClass for the cluster #### Apply the StorageClass Run the following script to apply the new storage class and set it as default: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/procedure/storageclass-configure.sh ``` To verify completion of the operation, run: ```bash ./procedure/storageclass-verify.sh ```
Show script procedure/storageclass-verify.sh ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/procedure/storageclass-verify.sh ```
This must be applied **before installing the Camunda Helm chart** so that PersistentVolumeClaims (PVCs) are provisioned with the correct performance characteristics. ### Configure the database and associated access As you now have a database, you need to create dedicated databases for each Camunda component and an associated user that has configured access. Follow these steps to create the database users and configure access. Due to the tight NSG rules in this example, the only way to access the database is through the AKS cluster. 1. In your terminal, set the necessary environment variables that will be substituted in the setup manifest: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/procedure/vars-create-db.sh ``` The RDBMS variant exports additional environment variables for the orchestration database: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region-rdbms/procedure/vars-create-db.sh ``` A **Kubernetes job** will connect to the database and create the necessary users with the required privileges. The script installs the necessary dependencies and runs SQL commands to create the users and assign them the correct roles and privileges. 2. Create a secret that references the environment variables: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/procedure/create-setup-db-secret.sh ``` The RDBMS variant includes additional orchestration database credentials in the secret: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region-rdbms/procedure/create-setup-db-secret.sh ``` This command creates a secret named `setup-db-secret` and dynamically populates it with the values from your environment variables. After running the above command, you can verify that the secret was created successfully by using: ```bash kubectl get secret setup-db-secret -o yaml --namespace "$CAMUNDA_NAMESPACE" ``` This should display the secret with the base64 encoded values. 3. Apply the following manifest to set up the DB: ```bash kubectl apply -f ./manifests/setup-postgres-create-db.yml --namespace "$CAMUNDA_NAMESPACE" ```
Show manifest setup-postgres-create-db.yml ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region/manifests/setup-postgres-create-db.yml ```
The RDBMS variant manifest creates an additional `camunda_orchestration` database and user for the secondary storage:
Show manifest setup-postgres-create-db.yml ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/azure/kubernetes/aks-single-region-rdbms/manifests/setup-postgres-create-db.yml ```
Once the secret is created, the **Job** manifest from the previous step can consume this secret to securely access the database credentials. 4. Once the job is created, monitor its progress using: ```bash kubectl get job/create-setup-user-db --namespace "$CAMUNDA_NAMESPACE" --watch ``` Once the job shows as `Completed`, the users and databases will have been successfully created. 5. View the logs of the job to confirm that the users were created and privileges were granted successfully: ```bash kubectl logs job/create-setup-user-db --namespace "$CAMUNDA_NAMESPACE" ``` 6. Clean up the resources: ```bash kubectl delete job create-setup-user-db --namespace "$CAMUNDA_NAMESPACE" kubectl delete secret setup-db-secret --namespace "$CAMUNDA_NAMESPACE" ``` Running these commands cleans up both the job and the secret, ensuring that no unnecessary resources remain in the cluster. ## 3. Install Camunda 8 using the Helm chart Now that you've exported the necessary values, you can proceed with installing Camunda 8 using Helm charts. Follow the guide [Camunda 8 on Kubernetes](./aks-helm.md) for detailed instructions on deploying the platform to your Kubernetes cluster. --- ## Google GKE Google Kubernetes Engine ([GKE](https://cloud.google.com/kubernetes-engine)) is a managed container service to run and scale Kubernetes applications in the cloud or on-premises. Camunda 8 Self-Managed can be deployed on any Kubernetes cluster using [Helm charts](/self-managed/deployment/helm/install/quick-install.md). Deployment requirements, including cloud provider-specific information, is available on our [Kubernetes deployment reference](/self-managed/reference-architecture/kubernetes.md). ### Google Cloud load balancer Camunda Identity management endpoints, such as the health check endpoint, do not run on port 80. As a result, when using a Google Cloud Load Balancer, you may need a [custom health check configuration](https://cloud.google.com/kubernetes-engine/docs/how-to/ingress-configuration#direct_health). Here’s an example of a `BackendConfig` you can apply: ```yaml apiVersion: cloud.google.com/v1 kind: BackendConfig metadata: name: camunda-identity spec: healthCheck: timeoutSec: 3 type: HTTP requestPath: /actuator/health/readiness port: 82 ``` When using [container-native load balancing](https://cloud.google.com/kubernetes-engine/docs/how-to/container-native-load-balancing), the load balancer sends traffic to an endpoint in a network endpoint group; hence, the `targetPort` should match the `containerPort` as the load balancer sends probes to the Pod's IP address directly. ```yaml apiVersion: cloud.google.com/v1 kind: BackendConfig metadata: name: camunda-identity spec: healthCheck: timeoutSec: 3 type: HTTP requestPath: /actuator/health/readiness port: 8082 ``` Finally, in your Helm values, assign the `BackendConfig` to the Identity service. ```yaml identity: service: annotations: cloud.google.com/backend-config: '{"default": "camunda-identity"}' ``` --- ## Cloud providers In this section, find details on cloud providers, including Amazon, Google, Microsoft, and Red Hat OpenShift. --- ## Deploy Camunda 8 to a local kind cluster With this guide, you'll deploy Camunda 8 Self-Managed to a local Kubernetes cluster using [kind (Kubernetes in Docker)](https://kind.sigs.k8s.io/). The setup is optimized for learning, development, and testing, with reduced resource requirements suitable for a personal machine. While this guide uses kind, the same concepts apply to other local Kubernetes tools, like [K3s](https://k3s.io/), [minikube](https://minikube.sigs.k8s.io/), or [MicroK8s](https://microk8s.io/). :::warning Local development only This setup is intended for **local development only** and shouldn't be used in production environments. For production deployments, follow our [cloud provider guides](/self-managed/deployment/helm/cloud-providers/index.md). ::: If you encounter issues during deployment, refer to the [Troubleshooting](#troubleshooting) section. ## Deployment modes You can choose from two deployment modes: | Mode | Access | Requirements | Use case | | ------------- | ----------------------------- | ------------------------------- | ------------------------------------- | | **Domain** | `https://camunda.example.com` | mkcert, hosts file modification | Full TLS setup, realistic environment | | **No-domain** | `localhost` via port-forward | hosts file modification | Quick setup, minimal configuration | ### How domain mode works In domain mode, you'll simulate a production-like environment locally with: - **Local DNS resolution** - You'll configure your machine's `/etc/hosts` file to resolve `camunda.example.com` to `127.0.0.1`. - Inside the cluster, you'll configure CoreDNS to rewrite DNS queries for this domain to the Ingress controller, allowing pods to communicate with each other using the same domain name. - **TLS certificates with mkcert** - You'll use [mkcert](https://github.com/FiloSottile/mkcert) to generate locally-trusted certificates by installing a local Certificate Authority (CA) in your system's trust store. This allows your browser to trust the self-signed certificates without security warnings. - You'll also mount the CA certificate in the pods that need to make HTTPS calls to other Camunda components. :::note Firefox compatibility mkcert requires [NSS](https://github.com/FiloSottile/mkcert#supported-root-stores) to work properly with Firefox. Without it, Firefox will display certificate errors. We recommend using **Chrome** or **Chromium-based browsers** for the best experience with domain mode. ::: ## Secondary storage options In addition to the deployment mode, you must choose a [secondary storage](/self-managed/concepts/secondary-storage/index.md) backend. The `SECONDARY_STORAGE` environment variable controls which backend the deployment scripts use. You must set it explicitly before running any deployment command — there is no default. For production backend trade-offs between Elasticsearch/OpenSearch and RDBMS, see [secondary storage architecture](/self-managed/reference-architecture/reference-architecture.md#secondary-storage-architecture). This kind guide is for local development and testing only. | Value | Operators deployed | Components | | --------------- | ------------------------------------- | ----------------------------------------------------------- | | `elasticsearch` | ECK, CloudNativePG, Keycloak Operator | Full platform including Optimize | | `postgres` | CloudNativePG, Keycloak Operator | All components except Optimize (uses PostgreSQL RDBMS mode) | The `postgres` option uses [RDBMS secondary storage](/self-managed/concepts/secondary-storage/index.md) (available since 8.9), which replaces Elasticsearch with PostgreSQL for Operate, Tasklist, and the Orchestration Cluster REST API. This results in a lighter deployment with fewer operators and lower resource consumption. Optimize is not available in this mode because it depends on Elasticsearch's aggregation API for analytics — this is an architectural constraint, not a temporary gap. Once you've chosen your backend, export the variable so all subsequent commands pick it up automatically: ```bash export SECONDARY_STORAGE=postgres # or: elasticsearch ``` ## Prerequisites Before you begin, you'll need: - Terminal access with administrator/sudo privileges for modifying the hosts file (`/etc/hosts`) - A container runtime with at least 4 CPU cores and 8 GB RAM available: - [Docker Desktop](https://www.docker.com/products/docker-desktop) - [Docker Engine](https://docs.docker.com/engine/install/) - [Podman](https://podman.io/docs/installation) - [kind](https://kind.sigs.k8s.io/docs/user/quick-start/#installation) - [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) - [Helm](https://helm.sh/docs/intro/install/) - [envsubst](https://www.gnu.org/software/gettext/manual/html_node/envsubst-Invocation.html) (Domain mode only; part of the `gettext` package) - [mkcert](https://github.com/FiloSottile/mkcert#installation) (Domain mode only) :::tip You can also use [asdf](https://asdf-vm.com/) to install the tools, with the versions defined in [.tool-versions](https://github.com/camunda/camunda-deployment-references/blob/main/.tool-versions). ::: ## Outcome By the end of this tutorial, you'll have: - A local Kubernetes cluster running with kind. This includes one control plane and two worker nodes. - A Contour Ingress controller deployed for routing traffic (domain mode only). - TLS certificates configured with mkcert (domain mode only). - Prerequisite services deployed via Kubernetes operators: - Elasticsearch via ECK (used as secondary storage in this guide; RDBMS is a supported alternative — see [configure RDBMS in Helm](/self-managed/deployment/helm/configure/database/rdbms.md)) - PostgreSQL (CloudNativePG) - Keycloak (Keycloak Operator) - Camunda 8 Self-Managed fully deployed and accessible, connected to the operator-managed services. :::info Other installation profiles With this guide, you deploy the full Camunda 8 platform with all components. For lighter setups or specific use cases, see the [Helm installation guide](/self-managed/deployment/helm/install/quick-install.md), which covers different installation profiles, such as core only, with Connectors, and with Web Modeler. ::: ## Download the reference architecture Download the reference architecture files you'll use throughout this guide: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/local/kubernetes/kind-single-region/procedure/get-your-copy.sh ``` You'll run all subsequent commands from `camunda-deployment-references/local/kubernetes/kind-single-region/`. Before proceeding, take some time to explore the repository structure and understand the configuration files, scripts, and Helm values. This will help you understand what each step does and how to customize the deployment for your needs. :::tip Quick setup with Makefile The reference architecture includes a `Makefile` with useful commands to automate the entire deployment process. If you [exported `SECONDARY_STORAGE`](#secondary-storage-options) above, you can omit it from these commands. Otherwise, set it inline: ```bash # PostgreSQL secondary storage (lighter, no Optimize) SECONDARY_STORAGE=postgres make domain.init # With TLS (requires mkcert) SECONDARY_STORAGE=postgres make no-domain.init # With port-forward # Elasticsearch secondary storage (full platform with Optimize) SECONDARY_STORAGE=elasticsearch make domain.init # With TLS (requires mkcert) SECONDARY_STORAGE=elasticsearch make no-domain.init # With port-forward ``` To clean up, use `make domain.clean` or `make no-domain.clean` respectively. Run `make help` to see all available targets, or consult the [Makefile](https://github.com/camunda/camunda-deployment-references/blob/main/local/kubernetes/kind-single-region/Makefile) directly. The following sections detail each step if you prefer to run them manually or want to understand the process. If you've used the quick setup commands above, you can skip ahead to [Accessing Camunda 8](#accessing-camunda-8). ::: ## Create the Kubernetes cluster First, run the cluster creation script: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/local/kubernetes/kind-single-region/procedure/cluster-create.sh ``` This script: 1. Creates a kind cluster named `camunda-platform-local`. 2. Waits until all nodes are ready. 3. Creates the `camunda` namespace. In the output, you should see three nodes in `Ready` state.
Review the cluster configuration The cluster includes one control plane node and two worker nodes with HTTP (80) and HTTPS (443) port mappings for Ingress: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/local/kubernetes/kind-single-region/configs/kind-cluster-config.yaml ```
Now that you've created the cluster, you'll need to choose a deployment mode for the next steps: - [Domain mode](#domain-mode-deployment) - [No-domain mode](#no-domain-mode-deployment) ## Domain mode deployment {#domain-mode-deployment} This section covers the full domain mode setup with TLS certificates and Ingress. If you'd prefer a simpler setup without domain configuration, skip to [No-domain mode deployment](#no-domain-mode-deployment). ### Deploy the Ingress controller Deploy the [Contour Ingress controller](https://projectcontour.io/) to handle incoming traffic: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/local/kubernetes/kind-single-region/procedure/contour-deploy.sh ``` This script: 1. Installs Contour via Helm. 2. Configures Envoy to run on the control plane node with `hostNetwork: true`. 3. Waits until the Envoy deployment is ready. Verify the Ingress controller is running: ```bash kubectl get pods -n projectcontour ``` ### Configure DNS resolution For pods inside the cluster to resolve `camunda.example.com`, configure CoreDNS to rewrite DNS queries: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/local/kubernetes/kind-single-region/procedure/coredns-config.sh ``` This configuration rewrites DNS queries for `camunda.example.com` and `zeebe-camunda.example.com` to the Contour Envoy service (`contour-envoy.projectcontour.svc.cluster.local`), allowing pods to reach Camunda services using the same domain names as external clients.
Review the CoreDNS configuration ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/local/kubernetes/kind-single-region/configs/coredns-configmap-contour.yaml ```
### Configure your hosts file Run the hosts file configuration script to resolve `camunda.example.com` locally. The script requires `sudo` privileges, so you'll need to provide your password: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/local/kubernetes/kind-single-region/procedure/hosts-add.sh ``` This adds the following entries to your `/etc/hosts` file: ``` 127.0.0.1 camunda.example.com 127.0.0.1 zeebe-camunda.example.com ``` ### Generate TLS certificates Generate locally-trusted TLS certificates, using [mkcert](https://github.com/FiloSottile/mkcert): ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/local/kubernetes/kind-single-region/procedure/certs-generate.sh ``` Using certificates from real certificate authorities (CAs) for local development can be dangerous or impossible, for hosts like `localhost` or `127.0.0.1`, and self-signed certificates cause trust errors. mkcert solves this by automatically creating and installing a local CA in the system root store and generating locally-trusted certificates. The certificate generation script: 1. Installs the mkcert CA in your system trust store (first run only). 2. Generates certificates for `camunda.example.com`, `zeebe-camunda.example.com` and `*.camunda.example.com`. 3. Stores certificates in `.certs/`. ### Create Kubernetes secrets for TLS 1. Create the TLS secret in Kubernetes. The Ingress controller will use this to serve HTTPS traffic for `camunda.example.com`. This also creates a `camunda-keycloak-tls` secret for the Keycloak Ingress: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/local/kubernetes/kind-single-region/procedure/certs-create-secret.sh ``` 2. Then, create a ConfigMap with the CA certificate for pods that need to trust it: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/local/kubernetes/kind-single-region/procedure/certs-create-ca-configmap.sh ``` ### Deploy prerequisite services Before deploying Camunda, you need to deploy the external services it depends on. These dependencies are deployed using Kubernetes operators as described in [Deploy infrastructure with Kubernetes operators](/self-managed/deployment/helm/configure/operator-based-infrastructure.md): - Elasticsearch via [ECK (Elastic Cloud on Kubernetes)](https://www.elastic.co/guide/en/cloud-on-k8s/current/index.html) — used as secondary storage in this guide. - PostgreSQL via [CloudNativePG](https://cloudnative-pg.io/) - Keycloak via the [Keycloak Operator](https://www.keycloak.org/operator/installation) :::note Secondary storage alternatives This guide uses Elasticsearch (via ECK) as the secondary storage backend. RDBMS (PostgreSQL, MySQL, MariaDB, Oracle) is a supported alternative for the Orchestration Cluster. To use RDBMS instead, skip the Elasticsearch operator deployment and see [configure RDBMS in Helm](/self-managed/deployment/helm/configure/database/rdbms.md). ::: Run the operator deployment script, specifying the domain deployment mode: ```bash CAMUNDA_MODE=domain ./procedure/operators-deploy.sh ``` This script installs each operator and its custom resources, then waits for all instances to be ready. When `SECONDARY_STORAGE=postgres`, the ECK operator and Elasticsearch cluster are skipped, and an additional PostgreSQL cluster (`pg-camunda`) is deployed for RDBMS secondary storage. ### Deploy Camunda 8 Deploy Camunda 8 with the domain mode Helm values. The deployment script layers the [operator-based Helm values](https://github.com/camunda/camunda-deployment-references/tree/main/generic/kubernetes/operator-based) to connect Camunda to the external PostgreSQL and Keycloak instances, and to the secondary storage backend you selected: ```bash ./procedure/camunda-deploy-domain.sh ``` The script selects the appropriate Helm values based on your [exported `SECONDARY_STORAGE`](#secondary-storage-options) value. With `elasticsearch`, it includes the Elasticsearch values overlay. With `postgres`, it includes the RDBMS values overlay and disables Elasticsearch and Optimize.
Deploy script source ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/local/kubernetes/kind-single-region/procedure/camunda-deploy-domain.sh ```
:::note Using RDBMS instead of Elasticsearch If you chose RDBMS as your secondary storage backend, skip the Elasticsearch overlay merge below and follow the [configure RDBMS in Helm](/self-managed/deployment/helm/configure/database/rdbms.md) guide to configure the Orchestration Cluster components. ::: This uses the following Helm values:
Domain mode Helm values (kind-specific) ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/local/kubernetes/kind-single-region/helm-values/values-domain.yml ```
Operator-based Helm values (external Elasticsearch, PostgreSQL, Keycloak) The deployment script layers the following shared operator values before the kind-specific values: - [`camunda-elastic-values.yml`](https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/elasticsearch/camunda-elastic-values.yml): Connects Camunda to the ECK-managed Elasticsearch (Elasticsearch secondary storage only). - [`camunda-rdbms-values.yml`](https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/postgresql/camunda-rdbms-values.yml): Configures PostgreSQL RDBMS as secondary storage and disables Elasticsearch and Optimize (PostgreSQL secondary storage only). - [`camunda-keycloak-domain-values.yml`](https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/keycloak/camunda-keycloak-domain-values.yml): Connects Camunda to the operator-managed Keycloak (domain mode). - [`camunda-identity-values.yml`](https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/postgresql/camunda-identity-values.yml): Configures Identity to use the CloudNativePG PostgreSQL. - [`camunda-webmodeler-values.yml`](https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/postgresql/camunda-webmodeler-values.yml): Configures Web Modeler to use the CloudNativePG PostgreSQL.
mkcert CA trust configuration ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/local/kubernetes/kind-single-region/helm-values/values-mkcert.yml ```
--- ## No-domain mode deployment {#no-domain-mode-deployment} This section covers the simplified setup using port-forwarding without TLS. ### Deploy prerequisite services Before deploying Camunda, you need to deploy the external services it depends on. These dependencies are deployed using Kubernetes operators as described in [Deploy infrastructure with Kubernetes operators](/self-managed/deployment/helm/configure/operator-based-infrastructure.md): - Elasticsearch via [ECK (Elastic Cloud on Kubernetes)](https://www.elastic.co/guide/en/cloud-on-k8s/current/index.html) — used as secondary storage in this guide - PostgreSQL via [CloudNativePG](https://cloudnative-pg.io/) - Keycloak via the [Keycloak Operator](https://www.keycloak.org/operator/installation) Run the operator deployment script, specifying the no-domain deployment mode: ```bash CAMUNDA_MODE=no-domain ./procedure/operators-deploy.sh ``` This script installs each operator and its custom resources, then waits for all instances to be ready. When `SECONDARY_STORAGE=postgres`, the ECK operator and Elasticsearch cluster are skipped, and an additional PostgreSQL cluster (`pg-camunda`) is deployed for RDBMS secondary storage. ### Configure your hosts file for Keycloak The Keycloak operator configures Keycloak with `keycloak-service` as its hostname. The JWT tokens issued by Keycloak use this hostname in the `iss` claim. To ensure your browser can resolve this hostname during the OIDC login flow, add the following entry to your `/etc/hosts` file: ```text 127.0.0.1 keycloak-service ``` Alternatively, you can run `make hosts.add-keycloak` to add this entry automatically. After adding this entry and deploying Camunda 8 in the next step, you'll be able to reach Keycloak at `http://keycloak-service:18080/auth`. **Why port `18080`?** The Keycloak instance is configured to listen on port `18080` (via `httpPort` in the operator CR) to avoid conflicts with the Zeebe Gateway HTTP endpoint, which uses port `8080` locally. ### Deploy Camunda 8 Deploy Camunda 8 with the no-domain mode Helm values. The deployment script layers the [operator-based Helm values](https://github.com/camunda/camunda-deployment-references/tree/main/generic/kubernetes/operator-based) to connect Camunda to the external PostgreSQL and Keycloak instances, and to the secondary storage backend you selected: ```bash ./procedure/camunda-deploy-no-domain.sh ``` The script selects the appropriate Helm values based on your [exported `SECONDARY_STORAGE`](#secondary-storage-options) value. With `elasticsearch`, it includes the Elasticsearch values overlay. With `postgres`, it includes the RDBMS values overlay and disables Elasticsearch and Optimize.
Deploy script source ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/local/kubernetes/kind-single-region/procedure/camunda-deploy-no-domain.sh ```
:::note Using RDBMS instead of Elasticsearch If you chose RDBMS as your secondary storage backend, skip the Elasticsearch overlay merge below and follow the [configure RDBMS in Helm](/self-managed/deployment/helm/configure/database/rdbms.md) guide to configure the Orchestration Cluster components. ::: This uses the following Helm values:
No-domain mode Helm values (kind-specific) ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/local/kubernetes/kind-single-region/helm-values/values-no-domain.yml ```
Operator-based Helm values (external Elasticsearch, PostgreSQL, Keycloak) The deployment script layers the following shared operator values before the kind-specific values: - [`camunda-elastic-values.yml`](https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/elasticsearch/camunda-elastic-values.yml): Connects Camunda to the ECK-managed Elasticsearch (Elasticsearch secondary storage only). - [`camunda-rdbms-values.yml`](https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/postgresql/camunda-rdbms-values.yml): Configures PostgreSQL RDBMS as secondary storage and disables Elasticsearch and Optimize (PostgreSQL secondary storage only). - [`camunda-keycloak-no-domain-values.yml`](https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/keycloak/camunda-keycloak-no-domain-values.yml): Connects Camunda to the operator-managed Keycloak (no-domain mode). - [`camunda-identity-values.yml`](https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/postgresql/camunda-identity-values.yml): Configures Identity to use the CloudNativePG PostgreSQL. - [`camunda-webmodeler-values.yml`](https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/postgresql/camunda-webmodeler-values.yml): Configures Web Modeler to use the CloudNativePG PostgreSQL.
## Verify deployment Monitor the deployment progress: ```bash kubectl get pods -n camunda -w ``` Wait until all pods show `Running` status. This may take 5–10 minutes depending on your internet connection and system resources. You can also use the deployment readiness check script from the root directory of the `camunda-deployment-references` repository. This script requires [jq](https://jqlang.github.io/jq/) to be installed: ```bash export CAMUNDA_NAMESPACE=camunda ``` ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/check-deployment-ready.sh ``` Finally, verify the Helm release: ```bash helm list -n camunda ``` ## Accessing Camunda 8 :::note Optimize is only available with Elasticsearch secondary storage. If you deployed with `SECONDARY_STORAGE=postgres`, Optimize is disabled. ::: | Component | URL | Availability | | ------------------------------ | ---------------------------------------------- | ------------------------------------ | | Operate | https://camunda.example.com/operate | All | | Tasklist | https://camunda.example.com/tasklist | All | | Admin | https://camunda.example.com/admin | All | | Management Identity | https://camunda.example.com/managementidentity | All | | Optimize | https://camunda.example.com/optimize | Elasticsearch secondary storage only | | Orchestration Cluster REST API | https://camunda.example.com/ | All | | Keycloak | https://camunda.example.com/auth | All | :::note Optimize is only available with Elasticsearch secondary storage. If you deployed with `SECONDARY_STORAGE=postgres`, Optimize is disabled and its port-forward is skipped automatically. ::: Start port-forwarding to access the services: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/local/kubernetes/kind-single-region/procedure/port-forward.sh ``` :::tip Localhost development with kubefwd For a richer localhost experience, and to avoid managing many individual port-forward commands, you can use [kubefwd](https://github.com/txn2/kubefwd) to forward all services in the target namespace and make them resolvable by their in-cluster DNS names on your workstation. This requires `sudo` to bind privileged ports and modify `/etc/hosts`: ```shell sudo kubefwd services -n "camunda" ``` Now, you can reach services directly, for example: - **Management Identity**: `http://camunda-identity/managementidentity` - **Keycloak**: `http://keycloak-service:18080/auth` - **Zeebe Gateway gRPC**: `camunda-zeebe-gateway:26500` You can still use localhost ports if you prefer traditional port-forwarding. Stop kubefwd with **Ctrl+C** when finished. Be aware kubefwd modifies your `/etc/hosts` temporarily, then restores the file when it exits. ::: | Component | URL | Availability | | -------------------- | ---------------------------------- | ------------------------------------ | | Zeebe Gateway (gRPC) | localhost:26500 | All | | Zeebe Gateway (HTTP) | http://localhost:8080/ | All | | Operate | http://localhost:8080/operate | All | | Tasklist | http://localhost:8080/tasklist | All | | Admin | http://localhost:8080/admin | All | | Management Identity | http://localhost:8085 | All | | Optimize | http://localhost:8083 | Elasticsearch secondary storage only | | Web Modeler | http://localhost:8070 | All | | Console | http://localhost:8087 | All | | Connectors | http://localhost:8088 | All | | Keycloak | http://keycloak-service:18080/auth | All | :::tip Connecting to the Orchestration Cluster To interact with the Orchestration Cluster via Zeebe Gateway using the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) or a local client/worker, connect to `localhost:26500` (gRPC) or `http://localhost:8080` (REST). ::: At any time, run `kubectl get services -n camunda` to get a full list of deployed Camunda components and their network properties. ### Default credentials #### Camunda admin - **Username**: `admin` - **Password**: Run the following script ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/local/kubernetes/kind-single-region/procedure/get-password.sh ``` #### Keycloak admin The Keycloak operator generates a separate set of admin credentials stored in the `keycloak-initial-admin` secret. These credentials are different from the Camunda admin credentials and are used to access the Keycloak administration console. ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/local/kubernetes/kind-single-region/procedure/get-keycloak-password.sh ``` ## Cleanup :::warning Destructive action This will permanently delete all Camunda 8 data in the local development cluster. ::: If you used the Makefile for setup, you can use the corresponding clean command: ```bash # Domain mode make domain.clean # No-domain mode make no-domain.clean ``` Alternatively, you can clean up manually: ```bash # Delete cluster kind delete cluster --name camunda-platform-local # (domain mode) Remove hosts entries (requires sudo) sudo sed -i '/camunda.example.com/d' /etc/hosts # (no-domain mode) Remove Keycloak hosts entry (requires sudo) sudo sed -i '/keycloak-service/d' /etc/hosts # (domain mode) Clean certificates rm -rf .certs ``` ## Troubleshooting ### Pods don't start Check the pod status and events: ```bash kubectl get pods -n camunda kubectl describe pod -n camunda kubectl logs -n camunda ``` ### Browser shows certificate errors (Domain mode) Ensure the mkcert CA is installed: ```bash mkcert -install ``` Regenerate certificates: ```bash ./procedure/certs-generate.sh ./procedure/certs-create-secret.sh kubectl rollout restart deployment -n camunda ``` ### Ingress doesn't work (Domain mode) Check the Ingress controller status: ```bash kubectl get pods -n projectcontour kubectl get ingress -n camunda ``` ### Insufficient resources Ensure your container runtime has enough resources allocated (4+ CPU cores, 8GB+ RAM). - **Docker Desktop**: Check the [Resources settings](https://docs.docker.com/desktop/settings-and-maintenance/settings/#resources) - **Docker Engine**: Configure the [Docker daemon](https://docs.docker.com/engine/daemon/) (configuration varies by OS) - **Podman**: Resources are managed by your system; ensure sufficient resources are available ## Next steps - [Getting started guide](/guides/getting-started-orchestrate-human-tasks.md): Deploy your first process - [Camunda APIs](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md): Explore the REST APIs - [Production Helm deployment](/self-managed/deployment/helm/install/production/index.md): Learn more about Helm chart configuration for production - [Cloud provider guides](/self-managed/deployment/helm/cloud-providers/index.md): Deploy to cloud platforms like AWS, Azure, or GCP --- ## Red Hat OpenShift Dual-Region This guide is designed to assist users aiming to deploy Camunda 8 in a dual-region setup on Red Hat OpenShift. The primary goal is to configure and integrate **two OpenShift clusters** for use in the dual-region reference architecture. This setup leverages specific components to address key challenges, and users are encouraged to exercise discretion regarding their use, learn more about [Advanced Cluster Management](https://www.redhat.com/en/resources/advanced-cluster-management-kubernetes-datasheet). While this guide does not cover exhaustive configurations, it aims to provide the key steps needed to achieve the desired outcome. To enable intercommunication between regions, we will utilize [ACM Advanced Cluster Management](https://www.redhat.com/en/technologies/management/advanced-cluster-management) and [Submariner](https://docs.redhat.com/en/documentation/red_hat_advanced_cluster_management_for_kubernetes/2.2/html/manage_cluster/submariner). Failover will be managed through DNS configurations, while access to the platform will be ensured via OpenShift Routes. Additionally, depending on whether your OpenShift clusters are managed on a cloud provider or deployed on-premises, certain aspects of the configuration might require adaptation. :::caution Dual-region limits and constraints Please review our [dual-region concept documentation](/self-managed/concepts/multi-region/dual-region.md) to understand the limitations and constraints of this blueprint before proceeding. ::: ## High Level Design _Infrastructure diagram for a OpenShift dual-region setup (click on the image to open the PDF version)_ [![Infrastructure Diagram OpenShift Dual-Region](./assets/openshift-dual-region.jpg)](./assets/openshift-dual-region.pdf) This High-Level Design describes how the following critical components interact to achieve a Camunda 8 deployment across two regions: - An S3-compatible solution is used for taking snapshots of the Elasticsearch database. - A DNS entry with an associated domain is used to enable Camunda 8 failover and reroute traffic from one cluster to the other. - Firewall and networking components are configured to allow unrestricted communication between the two clusters. - Local storage is provided on each OpenShift cluster for persistent data requirements. - A non-overlapping network configuration is implemented on the OpenShift clusters. This is a mandatory requirement, as outlined in the [Submariner overlapping guide](https://submariner.io/0.8/getting-started/architecture/globalnet/). - [Red Hat OpenShift Advanced Cluster Management](https://www.redhat.com/en/technologies/management/advanced-cluster-management) is used to manage the two clusters and configure Submariner. - [Submariner](https://docs.redhat.com/en/documentation/red_hat_advanced_cluster_management_for_kubernetes/2.2/html/manage_cluster/submariner) is configured on the two clusters to enable cross-namespace and cross-cluster network communication. ## Requirements - You need access to the [Advanced Cluster Management operator](https://www.redhat.com/en/technologies/management/advanced-cluster-management) and the [Submariner operator](https://catalog.redhat.com/software/container-stacks/detail/5f0c67b7ce85fb9e399f3a12). - The clusters must be separated by a reasonable latency as outlined in the [installation environment guidelines](/self-managed/concepts/multi-region/dual-region.md#installation-environment). - Each of your OpenShift clusters must meet at least the minimum capacity requirements for a cluster. Refer to the [cluster specification guide](./redhat-openshift.md#cluster-specification) for details on resource allocation and infrastructure needs. - Administrative privileges are required for both clusters to perform configurations and operator deployments. - A reliable means of communication between the two clusters is necessary. Ensure that each cluster can establish network connections with the other. - The version of your OpenShift clusters must be included in the [supported versions list](./redhat-openshift.md#supported-versions). - Review the [requirements of OpenShift Advanced Cluster Management](https://docs.redhat.com/en/documentation/red_hat_advanced_cluster_management_for_kubernetes/2.12/html/install/installing#installing) if it's not already configured. - Review the [requirements of OpenShift Submariner](https://docs.redhat.com/en/documentation/red_hat_advanced_cluster_management_for_kubernetes/2.12/html/networking/networking#submariner) if it's not already configured. - Review the [requirements of Submariner](https://submariner.io/getting-started/#prerequisites) if it's not already configured, especially the flows to open. ### CLI Requirements In addition to the general prerequisites outlined above, the following CLI tools are required for interacting with the clusters and deploying Camunda 8, these are the same CLI tools required as mentioned in the [OpenShift Guide](redhat-openshift.md#requirements): - [subctl](https://github.com/submariner-io/subctl) a CLI tool used to interact with [Submariner](https://submariner.io/). ### OpenShift clusters The architecture of your OpenShift clusters may vary depending on your specific configuration. This guide assumes a generic deployment on OpenShift. If you are interested in a tested topology, follow the [ROSA Dual-Region Guide](/self-managed/deployment/helm/cloud-providers/amazon/openshift/terraform-setup-dual-region.md). #### Cluster requirements To deploy infrastructure components successfully, it’s essential that the OpenShift clusters you use have a `cluster-admin` role, which grants full privileges within the cluster. This level of access is necessary for the proper setup and configuration of the infrastructure components. However, **Camunda 8 deployment does not require cluster-admin access**. For the best security practice and to follow the principle of least privilege, it is recommended to deploy Camunda 8 with a **standard, limited user**. This approach helps minimize security risks by restricting unnecessary administrative privileges. ## Setup Advanced Cluster Management and Submariner In order to achieve cross-cluster communication, we use the recommanded [Submariner](https://docs.redhat.com/en/documentation/red_hat_advanced_cluster_management_for_kubernetes/2.2/html/manage_cluster/submariner) solution that is designed to securely connect workloads across multiple Kubernetes clusters. The installation of Submariner on OpenShift differs from the standard installation as it requires the usage of [Red Hat Advanced Cluster Management for Kubernetes](https://www.redhat.com/en/technologies/management/advanced-cluster-management). :::note Alternative installations You may achieve the same installation without the usage of Red Hat Advanced Cluster Management for Kubernetes, however, this guide will focus on the official supported deployment of Submariner. ::: ### Advanced Cluster Management If Red Hat Advanced Cluster Management is not enabled on your cluster, you need to enable it. The following steps are an extract from the [official instructions](https://docs.redhat.com/en/documentation/red_hat_advanced_cluster_management_for_kubernetes/2.12/html/install/installing), which you may want to refer for details of the implementation and associated customizations. :::caution Non-production use only The following installation instructions for Advanced Cluster Management are intended for non-production environments. For a production setup, please consult the [official Red Hat Advanced Cluster Management guide](https://docs.redhat.com/en/documentation/red_hat_advanced_cluster_management_for_kubernetes/2.12/html/install/installing). ::: :::note Designation of the clusters in ACM The cluster of region 0 is referred to as `local-cluster` in **ACM**. This designation cannot be changed, as it is a constant name used to reference the [managed hub cluster](https://open-cluster-management.io/docs/concepts/cluster-inventory/managedcluster/). Later in this guide, we will refer to it as **first cluster**. ::: 0. This part of the guide uses a generic reference architecture, you can find all the [acm files here](https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/acm/). 1. Reference each cluster context name and ensure that each cluster's context name matches the corresponding cluster name. If the context name does not match, you will need to rename it to follow this guide. ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main//generic/openshift/dual-region/procedure/set-cluster-names.sh ``` 2. The following manifest will create a namespace for the management cluster, enable the [open-cluster-management operator](https://open-cluster-management.io/) and the [associated subscription](https://docs.openshift.com/container-platform/4.17/operators/admin/olm-adding-operators-to-cluster.html#olm-installing-operator-from-operatorhub-using-cli_olm-adding-operators-to-a-cluster). ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/acm/install-manifest.yml ``` Save this manifest as `install-manifest.yml` then apply it to enable ACM: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/acm/install-acm.sh ``` Verify that the installation succeeded: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/acm/verify-acm.sh ``` 3. With the ACM operator now enabled on the first cluster, the next step is to create the [Multicluster Global Hub](https://docs.redhat.com/en/documentation/red_hat_advanced_cluster_management_for_kubernetes/2.12/html-single/install/index#installing-from-the-cli). This feature allows you to import and manage one or more hub clusters from a single central hub cluster. In this setup, the first cluster will act as the central hub, managing the second cluster. This capability enables the deployment and management of components on the second cluster directly from the first cluster. ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/acm/multi-cluster-hub.yml ``` :::caution Known issue: may not work correctly with the manifest The creation of the MultiClusterHub using the manifest can sometimes remain stuck in the installation phase when created this way. To avoid this issue, you can follow the [official instructions in the OpenShift UI Console](https://docs.redhat.com/en/documentation/red_hat_advanced_cluster_management_for_kubernetes/2.12/html/install/installing#installing-from-the-operatorhub). ::: Save this manifest as `multi-cluster-hub.yml` then apply it to enable ACM: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/acm/install-multi-cluster-hub.sh ``` Wait until the status shows as "Running." This process can take up to 10 minutes: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/acm/verify-multi-cluster-hub.sh ``` :::caution Security consideration - A ServiceAccount with a ClusterRoleBinding automatically gives cluster administrator privileges to Red Hat Advanced Cluster Management and to any user credentials with access to the namespace where you install Red Hat Advanced Cluster Management (`open-cluster-management` here), [learn more about this on the official documentation](https://docs.redhat.com/en/documentation/red_hat_advanced_cluster_management_for_kubernetes/2.12/html/install/installing#installing-from-the-operatorhub). - A namespace called `local-cluster` is reserved for the Red Hat Advanced Cluster Management hub cluster when it is self-managed. This is the only local-cluster namespace that can exist. - :warning: For security reasons, do not give access to the `local-cluster` namespace to any user that is not a cluster-administrator. ::: 4. With the MultiClusterHub created, the last step is to create a `ManagedClusterSet` which is a group of managed clusters. With a `ManagedClusterSet`, you can manage access to all of the managed clusters in the group together ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/acm/managed-cluster-set.yml ``` Save this manifest as `managed-cluster-set.yml` then apply it to enable ACM: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/acm/install-managed-cluster-set.sh ``` Verify that the ManagedClusterSet has been created, at this step, only `local-cluster` will be listed: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/acm/verify-managed-cluster-set.sh ``` 5. After creating the Managed Cluster Set, the next step is to import clusters into the set. - To import a cluster, you need to template the manifest for each cluster. Save the following file as `managed-cluster.yml.tpl`: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/acm/managed-cluster.yml.tpl ``` - The cluster’s associated addon configuration will be managed using the following manifest. Save it as `klusterlet-config.yml.tpl`: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/acm/klusterlet-config.yml.tpl ``` - To import a cluster, you must store the associated authentication token. Save the following file as `auto-import-cluster-secret.yml.tpl`: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/acm/auto-import-cluster-secret.yml.tpl ``` - If running on Red Hat OpenShift Service on AWS (ROSA), the following addition is required to ensure certificates are trusted. Save the following file as `klusterlet-global-config.yml`: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/acm/klusterlet-global-config.yml ``` - Finally, import the target cluster into the Managed Cluster Set and verify that they can be reached and managed successfully: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/acm/initiate-cluster-set.sh ``` - Once all the clusters are imported, verify that all of them are available and reachable: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/acm/verify-managed-cluster-set.sh ``` ### Submariner The [architecture of Submariner](https://submariner.io/getting-started/architecture/) comprises several components working together to enable direct networking between Pods and Services across different Kubernetes clusters. The following diagram illustrates the interaction between the two clusters: _Infrastructure diagram of Submariner setup_ ![Infrastructure diagram of Submariner setup](./assets/submariner-hld.jpg) - Traffic sent from one broker to another cluster can be encrypted by the [Gateway Engine](https://submariner.io/getting-started/architecture/gateway-engine/). In OpenShift, the IPSec protocol is used on port `4500/UDP`, utilizing the [Libreswan](https://libreswan.org/) implementation. - A dedicated node in each cluster assumes the [Broker Role](https://submariner.io/getting-started/architecture/broker/), facilitating the exchange of metadata between Gateway Engines in participating clusters. This component is **not responsible for transmitting data**, unlike the Gateway Engine, which handles data transmission between internal networks of different clusters. High availability can be achieved by adding a second dedicated node. - Service discovery is managed internally by the [Lighthouse project](https://submariner.io/getting-started/architecture/service-discovery/). - The [Route Agent component](https://submariner.io/getting-started/architecture/route-agent/) runs on every node in each participating cluster. It sets up the necessary host network elements on top of the existing Kubernetes CNI plugin. :::note Handling overlapping CIDRs This guide does not cover handling overlapping CIDRs. However, this can be achieved using the [Globalnet Controller](https://submariner.io/getting-started/architecture/globalnet/). ::: Installing Submariner in OpenShift **requires** [Advanced Cluster Management](#advanced-cluster-management) to be configured, with each cluster added to the management cluster. 0. This part of the guide uses a generic reference architecture, you can find all the [submariner files here](https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/submariner/). 1. Ensure cluster context names match: _(Skip this step if already completed as part of [Advanced Cluster Management](#advanced-cluster-management).)_ Verify that each cluster's context name matches its corresponding cluster name. If the context name does not match, rename it to align with this guide. ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main//generic/openshift/dual-region/procedure/set-cluster-names.sh ``` 2. Verify dedicated broker nodes: Confirm that each cluster has nodes labeled for Submariner gateway functionality: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main//generic/openshift/dual-region/procedure/submariner/list-nodes-brokers.sh ``` If no nodes are labeled, you need to label at least one node in each cluster. For better reliability, consider dedicating a node as the broker. **Assigning broker node labels:** Select the first node and apply the required label: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main//generic/openshift/dual-region/procedure/submariner/label-nodes-brokers.sh ``` 3. Deployment of Submariner on the clusters: - Save the following file as `submariner.yml.tpl`: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/submariner/submariner.yml.tpl ``` :::note Cluster naming In this example, the first cluster is referenced as `local-cluster`. This is because the first cluster is used as the management cluster in this minimal setup. If your cluster is named differently, you may need to adapt this file to match your actual cluster name. ::: - Then apply it on the management cluster: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main//generic/openshift/dual-region/procedure/submariner/install-submariner.sh ``` - Wait for the brokers to become ready. This may take up to 10 minutes. You can check the broker status using the following command: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main//generic/openshift/dual-region/procedure/submariner/verify-submariner.sh ``` 4. After deploying Submariner, check that the clusters can communicate with each other by using the `subctl` utility. Keep in mind that it might take several minutes before all status indicators turn green. If you don’t have the `subctl` CLI installed, you can follow the [installation instructions here](https://submariner.io/operations/deployment/) or execute the following commands: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main//generic/openshift/dual-region/procedure/submariner/install-subctl.sh ``` Now, verify communication between the clusters with the following script: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main//generic/openshift/dual-region/procedure/submariner/verify-subctl.sh ``` If everything is set up correctly, you should observe in the output of each cluster context the following statuses: - Gateway's status: `All connections (1) are established` - Connection's status: `connected 10.406614ms (RTT)`
Example Submariner check successfull output ```text reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/submariner/output.txt ```
For more comprehensive details regarding the verification tests for Submariner using subctl, please refer to the [official documentation](https://submariner.io/operations/deployment/subctl/#verify). **Debugging the Submariner setup:** If you are experiencing connectivity issues, we recommend spawning a pod in the `default` namespace that contains networking debugging tools. You can find an [example here](https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/submariner/debug-utils-submariner.yml). With this pod, you will be able to check flow openings, service resolution, and other network-related aspects. Troubleshooting requires examining all the underlying mechanisms of Submariner. Therefore, we also encourage you to read the [Submariner troubleshooting guide](https://submariner.io/operations/troubleshooting/). ## Deploying Camunda 8 via Helm charts in a dual-region setup :::info Migration from Bitnami Elasticsearch to ECK in dual-region There is currently no dedicated migration procedure for moving from the Bitnami Elasticsearch subchart to the ECK operator in a dual-region setup. If you need to perform this migration, follow the [single-region migration procedure](/self-managed/deployment/helm/operational-tasks/migration-from-bitnami/bitnami-to-operators.md) and apply it individually to each region. ::: The installation of Camunda 8 in OpenShift across dual regions requires a functioning Submariner setup connecting two OpenShift clusters. ### Verify the pre-requisites Before proceeding with the installation, ensure the required information is available and configured in your terminal for later use. Review and adjust the following environment script to match your specific configuration: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/chart-env.sh ``` _If you are unsure about the values of the backup bucket, please refer to the [S3 backup bucket module setup](/self-managed/deployment/helm/cloud-providers/amazon/openshift/terraform-setup-dual-region.md#s3-backup-bucket-module-setup) as a reference for implementation._ Save the file as `chart-env.sh`, replace the placeholders with your values, and then source the file: ```bash source chart-env.sh ``` ### Set up namespaces Submariner requires that each cluster has both namespaces present. Create the required namespaces in both clusters: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/setup-namespaces.sh ``` Save it as `setup-namespaces.sh`, and execute it: ```bash chmod +x setup-namespaces.sh ./setup-namespaces.sh ``` ### Create Elasticsearch S3 backup secrets Elasticsearch will need an S3 bucket for data backup and restore procedure, required during a regional failback. For this, you will need to configure a Kubernetes secret to not expose those in cleartext. If you don't have access to an S3 bucket, you can adapt the backup method to use an [alternative Elasticsearch backup solution](https://www.elastic.co/guide/en/elasticsearch/reference/current/snapshot-restore.html). However, this guide focuses solely on S3 snapshots. :::caution Bucket vulnerable to region outages The Elasticsearch backup [bucket is tied to a specific region](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingBucket.html). If that region becomes unavailable and you want to restore to a different region or S3 services remain disrupted, you must create a new bucket in another region and reconfigure your Elasticsearch cluster to use the new bucket. ::: The following script creates the ECK-compatible secure settings secrets for S3 backup access in both regions. These secrets must exist before the Elasticsearch cluster is deployed, as the ECK Elasticsearch CRD references them for keystore injection at startup. ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/create-elasticsearch-secrets.sh ``` Save it as `create-elasticsearch-secrets.sh`, and execute it: ```bash chmod +x create-elasticsearch-secrets.sh ./create-elasticsearch-secrets.sh ``` ### Deploy the ECK operator and Elasticsearch clusters Before deploying the Elasticsearch cluster, install the ECK operator and its Custom Resource Definitions (CRDs) in both clusters. The ECK operator manages the lifecycle of Elasticsearch resources in Kubernetes. Run [deploy.sh](https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/elasticsearch/deploy.sh) from `generic/kubernetes/operator-based/elasticsearch/`: ```bash cd generic/kubernetes/operator-based/elasticsearch export ELASTICSEARCH_CLUSTER_FILE="elasticsearch-cluster-dual-region.yml" CAMUNDA_NAMESPACE=$CAMUNDA_NAMESPACE_0 KUBE_CONTEXT=$CLUSTER_0 ./deploy.sh CAMUNDA_NAMESPACE=$CAMUNDA_NAMESPACE_1 KUBE_CONTEXT=$CLUSTER_1 ./deploy.sh cd - ``` This performs the following actions: - Installs the ECK CRDs. - Deploys the operator to the `elastic-system` namespace. - Waits for operator readiness. - Creates the Elasticsearch cluster in both regions using the ECK operator.
Review the Elasticsearch deploy.sh script ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/elasticsearch/deploy.sh ```
The dual-region Elasticsearch cluster manifest is located at `generic/kubernetes/operator-based/elasticsearch/elasticsearch-cluster-dual-region.yml`.
Review the Elasticsearch cluster configuration ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/elasticsearch/elasticsearch-cluster-dual-region.yml ```
For more details on the ECK operator deployment, see the [operator-based infrastructure guide](/self-managed/deployment/helm/configure/operator-based-infrastructure.md#elasticsearch-deployment). ### Synchronize Elasticsearch passwords across regions Each ECK-managed Elasticsearch cluster auto-generates its own `elasticsearch-es-elastic-user` secret with a unique password. For Zeebe exporters to authenticate against the remote region's Elasticsearch, each region needs access to the other region's password. The following script reads the ECK-generated passwords and creates region-specific secrets in both regions: - `elasticsearch-es-password-region-0`: password from region 0's Elasticsearch - `elasticsearch-es-password-region-1`: password from region 1's Elasticsearch These secrets are referenced by the Zeebe exporter configuration in the values files to authenticate against Elasticsearch in each region. ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/sync-elasticsearch-passwords.sh ``` Save it as `sync-elasticsearch-passwords.sh`, and execute it: ```bash chmod +x sync-elasticsearch-passwords.sh ./sync-elasticsearch-passwords.sh ``` ### Configure your deployment for each region Before deploying, some values in the value files need to be updated. To assist with generating these values, save the following Bash script as `generate-zeebe-helm-values.sh`:
Show `generate-zeebe-helm-values.sh` script reference ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/generate-zeebe-helm-values.sh ```
Then, source the output of the script. By doing so, we can reuse the values later for substitution, instead of manually adjusting the values files. The script derives the number of Zeebe Brokers from your Helm values. For a dual-region setup, the default is `8` (four brokers per region): ``` chmod +x generate-zeebe-helm-values.sh CLUSTER_0='local-cluster' source ./generate-zeebe-helm-values.sh ``` :::note Submariner service naming For those unfamiliar with the Submariner DNS convention, please consult the [official documentation](https://submariner.io/operations/usage/#service-discovery-for-services-deployed-to-multiple-clusters). In this deployment, we are utilizing the service discovery to have Elasticsearch and Zeebe brokers accessible from one cluster to another. Consequently, the values generated by the previous script will not be local services but use `svc.clusterset.local`. Make sure the variable `CLUSTER_0` is set to the name of your first cluster. In this example, the value `local-cluster` is used to maintain consistency with the previous [advanced cluster management step](#advanced-cluster-management). ::: #### Helm values Create `values-region-0.yml` and `values-region-1.yml` to store each region's configuration. These files will contain key-value pairs that will be substituted using `envsubst`. Throughout this guide, you will add and merge values into these files to configure your deployment according to your requirements. - Save the following file as both `values-region-0.yml` and `values-region-1.yml` to serve as the base configuration: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/helm-values/values-base.yml ``` :::warning Merging YAML files This guide references multiple configuration files that need to be merged into a single YAML file. Be cautious to avoid duplicate keys when merging the files. Additionally, pay close attention when copying and pasting YAML content. Ensure that the separator notation `---` does not inadvertently split the configuration into multiple documents. We strongly recommend double-checking your YAML file before applying it. You can use tools like [yamllint.com](https://www.yamllint.com/) or the [YAML Lint CLI](https://github.com/adrienverge/yamllint) if you prefer not to share your information online. ::: Set up the region ID using a unique integer for each region: - Add the following YAML configuration to `values-region-0.yml`: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/helm-values/values-region-0.yml ``` - Add the following YAML configuration to `values-region-1.yml`: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/helm-values/values-region-1.yml ``` **Security Context Constraints (SCCs)** The process of deploying applications in an OpenShift cluster can be influenced by its Security Context Constraints (SCCs) configuration. By default, OpenShift comes with more restrictive SCCs. For the purposes of this guide, which focuses on multi-region deployment, we assume this to be the standard setup. For custom configurations or specific requirements, please refer to the [installation guide for OpenShift](redhat-openshift.md#security-context-constraints-sccs) which details the various available SCC options. Additionally, Elasticsearch is managed using the ECK operator, so the orchestration cluster needs the **local** Elasticsearch connection details (URL and authentication) to read data. This is distinct from the cross-cluster Elasticsearch exporter URLs configured via environment variables in the [previous step](#configure-your-deployment-for-each-region), which handle **writing** data across regions via Submariner DNS. Merge the ECK overlay into your values files: - Add the following ECK Elasticsearch overlay configuration to both `values-region-0.yml` and `values-region-1.yml`: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/elasticsearch/camunda-elastic-values.yml ``` #### Fill your deployment with actual values Once you've prepared each region's values file (`values-region-0.yml` and `values-region-1.yml`), substitute the environment variables with their actual values: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/assemble-envsubst-values.sh ``` ### Install Camunda 8 using Helm With the value files for each region configured, you can now install Camunda 8 using Helm. Execute the following commands: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/install-chart.sh ``` This command: - Installs (or upgrades) Camunda using the Helm chart on each cluster. - Substitutes the appropriate version using the `$CAMUNDA_HELM_CHART_VERSION` environment variable. - Applies the configuration from the value file. :warning: **Installation is not complete yet:** at this stage, the two Camunda 8 deployments cannot communicate. You need to follow the next step to complete the installation. :::note This guide uses `helm upgrade --install` as it runs install on initial deployment and upgrades future usage. This may make it easier for future [Camunda 8 Helm upgrades](/self-managed/upgrade/helm/index.md) or any other component upgrades. ::: #### Export Camunda 8 services using Submariner Once Camunda is deployed across the two clusters, the next step is to expose each service to Submariner so it can be resolved by the other cluster. The following script exports all services for both clusters, then waits for the Submariner Lighthouse controller to create `ServiceImport` resources and propagate `*.svc.clusterset.local` DNS records. This wait is critical. Without it, Zeebe brokers may fail to discover cross-cluster peers during startup, leading to topology initialization failures. ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/export-services-submariner.sh ``` :::note The DNS propagation timeout defaults to 300 seconds and can be customized via the `DNS_WAIT_TIMEOUT` environment variable. ::: Alternatively, you can manage each service individually using the `ServiceExport` Custom Resource Definition (CRD). If you do so manually, ensure you wait for the corresponding `ServiceImport` resources to appear before proceeding.
Example of the ServiceExport manifest ```yaml apiVersion: multicluster.x-k8s.io/v1alpha1 kind: ServiceExport metadata: name: elasticsearch-es-http # name of the ECK-managed Elasticsearch service to export namespace: $CAMUNDA_NAMESPACE_0 # must match the namespace where the Elasticsearch Service exists ```
For each cluster, verify the status of the exported services with this script: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/verify-exported-services.sh ``` To monitor the progress of the installation, save and execute the following script: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/dual-region/procedure/check-deployment-ready.sh ``` Save it as `check-deployment-ready.sh`, make it executable, and run it: ```bash chmod +x check-deployment-ready.sh ./check-deployment-ready.sh ``` ## Verify connectivity to Camunda 8 :::info Authentication changes in 8.8+ Starting from version 8.8, the Orchestration Cluster is configured by default with [Admin](/self-managed/components/orchestration-cluster/admin/overview.md) and is protected by Basic authentication using `demo:demo` as the default username and password. ::: The following script will port-forward the Zeebe Gateway via `kubectl` from one of your clusters. Zeebe is stretching over both clusters and is `active-active`, meaning it doesn't matter which Zeebe Gateway to use to interact with your Zeebe cluster. ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/dual-region/procedure/check-zeebe-cluster-topology.sh ``` Make sure that your output contains all eight brokers from the two regions:
Example output ```json reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/dual-region/procedure/check-zeebe-cluster-topology-output.json ```
## Failover Consult the generic [dual-region failover procedure](/self-managed/deployment/helm/operational-tasks/dual-region-ops.md). ## Pitfalls to avoid For general deployment pitfalls, visit the [deployment troubleshooting guide](self-managed/operational-guides/troubleshooting.md). --- ## Red Hat OpenShift Red Hat OpenShift, a Kubernetes distribution maintained by [Red Hat](https://www.redhat.com/en/technologies/cloud-computing/openshift), provides options for both managed and on-premises hosting. Deploying Camunda 8 on Red Hat OpenShift is supported using Helm, given the appropriate configurations. However, it's important to note that the [Security Context Constraints (SCCs)](#security-context-constraints-sccs) and [Routes](./redhat-openshift.md?current-ingress=openshift-routes#using-openshift-routes) configurations might require slight deviations from the guidelines provided in the [general Helm deployment guide](/self-managed/deployment/helm/install/quick-install.md). Additional information and a high-level overview of Kubernetes as the upstream project is available on our [Kubernetes deployment reference](/self-managed/reference-architecture/kubernetes.md). ## Requirements - [Helm](https://helm.sh/docs/intro/install/) - [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) to interact with the cluster. - [jq](https://jqlang.github.io/jq/download/) to interact with some variables. - [GNU envsubst](https://www.man7.org/linux/man-pages/man1/envsubst.1.html) to generate manifests. - [oc (version supported by your OpenShift)](https://docs.openshift.com/container-platform/4.17/cli_reference/openshift_cli/getting-started-cli.html) to interact with OpenShift. - A namespace to host Camunda. - Permissions to install Kubernetes operators (cluster-admin or equivalent) for deploying the infrastructure services (Elasticsearch, PostgreSQL, Keycloak). These operators can also be installed via the [OpenShift OperatorHub](https://docs.openshift.com/container-platform/latest/operators/understanding/olm-understanding-operatorhub.html), but this guide installs them directly from source for full control over versions and configuration. For the tool versions used, check the [.tool-versions](https://github.com/camunda/camunda-deployment-references/blob/main/.tool-versions) file in the repository. It contains an up-to-date list of versions that we also use for testing. ## Architecture This section installs Camunda 8 following the architecture described in the [reference architecture](/self-managed/reference-architecture/reference-architecture.md). The architecture includes the following core components: - **Orchestration Cluster**: Core process execution engine (Zeebe, Operate, Tasklist, and Admin) - **Web Modeler and Console**: Management and design tools (Web Modeler, Console, and Management Identity) Infrastructure components are deployed using **official Kubernetes operators** as described in [Deploy infrastructure with Kubernetes operators](/self-managed/deployment/helm/configure/operator-based-infrastructure.md): - **[Elasticsearch with ECK](#deploy-elasticsearch)**: Document-store example path used in this guide for secondary storage - **[PostgreSQL with CloudNativePG](#deploy-postgresql)**: Deployed via [CloudNativePG](https://cloudnative-pg.io/) for Identity and Web Modeler databases - **[Keycloak](#deploy-keycloak) (optional)**: Deployed via the [Keycloak Operator](https://www.keycloak.org/operator/installation) as an identity provider for Single Sign-On (SSO) For OpenShift deployments, the following OpenShift-specific configurations are also included: - **OpenShift Routes**: Native OpenShift way to expose services externally (alternative to standard Kubernetes Ingress) - **Security Context Constraints (SCCs)**: Security framework for controlling pod and container permissions ## Identity Provider (IdP) setup ## Deploy Camunda 8 via Helm charts ### Obtain a copy of the reference architecture All configuration files, deployment scripts, and Helm values referenced in this guide are available in the [Camunda deployment references](https://github.com/camunda/camunda-deployment-references) repository. ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/single-region/get-your-copy.sh ``` This places you at the repository root, from which both directories are accessible: - `generic/kubernetes/operator-based/` — operator deployment scripts (Elasticsearch, PostgreSQL, Keycloak) - `generic/openshift/single-region/` — OpenShift-specific Helm values and procedures ### Environment setup Source the environment variables required by the deployment scripts: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/0-set-environment.sh ``` :::note Ensure you source this file before running any deployment or configuration commands in the following sections. ::: ### Configure your deployment Start by copying the base Helm values file from the cloned repository into a working `values.yml` at the repository root: ```bash cp generic/openshift/single-region/helm-values/base.yml values.yml ``` This file contains key-value pairs that will be substituted using `envsubst`. Over this guide, you will merge additional overlays into this file to configure your deployment.
Review the base Helm values ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/single-region/helm-values/base.yml ```
:::danger Merging YAML files This guide references multiple configuration files that need to be merged into a single YAML file. Be cautious to avoid duplicate keys when merging the files. Additionally, pay close attention when copying and pasting YAML content. Ensure that the separator notation `---` does not inadvertently split the configuration into multiple documents. We strongly recommend double-checking your YAML file before applying it. You can use tools like [yamllint.com](https://www.yamllint.com/) or the [YAML Lint CLI](https://github.com/adrienverge/yamllint) if you prefer not to share your information online. ::: #### Configuring the Ingress Before exposing services outside the cluster, we need an Ingress component. Here's how you can configure it: [Routes](https://docs.openshift.com/container-platform/latest/networking/routes/route-configuration.html) expose services externally by linking a URL to a service within the cluster. OpenShift supports both the [standard Kubernetes Ingress and routes](https://docs.openshift.com/container-platform/latest/networking/ingress-operator.html), giving cluster users the flexibility to choose. The presence of routes is rooted in their specification predating Ingress. The functionality of routes differs from Ingress; for example, unlike Ingress, routes don't allow multiple services to be linked to a single route or the use of paths. To use these routes for the Zeebe Gateway, configure this through Ingress as well. #### Setting Up the application domain for Camunda 8 The route created by OpenShift will use a domain to provide access to the platform. By default, you can use the OpenShift applications domain, but any other domain supported by the router can also be used. To retrieve the OpenShift applications domain (used as an example here), run the following command and define the route domain that will be used for the Camunda 8 deployment: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/single-region/procedure/setup-application-domain.sh ``` If you choose to use a custom domain instead, ensure it is supported by your router configuration and replace the example domain with your desired domain. For more details on configuring custom domains in OpenShift, refer to the official [custom domain OpenShift documentation](https://docs.openshift.com/dedicated/applications/deployments/osd-config-custom-domains-applications.html). #### Checking if HTTP/2 is enabled As the Zeebe Gateway also uses `gRPC` (which relies on `HTTP/2`), [HTTP/2 Ingress Connectivity must be enabled](https://docs.openshift.com/container-platform/latest/networking/ingress-operator.html#nw-http2-haproxy_configuring-ingress). To check if HTTP/2 is already enabled on your OpenShift cluster, run the following command: ```bash oc get ingresses.config/cluster -o json | jq '.metadata.annotations."ingress.operator.openshift.io/default-enable-http2"' ``` Alternatively, if you use a dedicated IngressController for the deployment: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/single-region/procedure/get-ingress-http2-status.sh ``` - If the output is `"true"`, it means HTTP/2 is enabled. - If the output is `null` or empty, HTTP/2 is not enabled.
Enable HTTP/2 If HTTP/2 is not enabled, you can enable it by running the following command: **IngressController configuration:** ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/single-region/procedure/enable-ingress-http2.sh ``` **Global cluster configuration:** ```bash oc annotate ingresses.config/cluster ingress.operator.openshift.io/default-enable-http2=true ``` This will add the necessary annotation to [enable HTTP/2 for Ingress in your OpenShift cluster](https://docs.openshift.com/container-platform/latest/networking/ingress-operator.html#nw-http2-haproxy_configuring-ingress) globally on the cluster.
ROSA HCP — additional steps for ALPN h2 These steps are required only on **Red Hat OpenShift Service on AWS – Hosted Control Planes (ROSA HCP)** managed clusters. Self-managed OpenShift clusters where the cluster-wide `ingress.operator.openshift.io/default-enable-http2=true` annotation is honored do **not** need this workaround. On ROSA HCP, the `ingress-config-validation.managed.openshift.io` admission webhook denies the cluster-wide annotation, and the per-`IngressController` annotation alone does not make HAProxy advertise ALPN `h2` on the default certificate path. As a result, gRPC clients (Zeebe) fail with `No ALPN negotiated`. The OpenShift router advertises ALPN `h2` on a per-SNI basis through a `crt-list` entry that HAProxy generates only for Routes that carry an explicit `spec.tls.certificate`. In other words, the gRPC Route must reference a TLS Secret in the Camunda namespace; the default `secretName: '-'` (Ingress-Operator-managed) is not enough on ROSA HCP. To fix this, copy the router default wildcard TLS Secret from `openshift-ingress` into the Camunda namespace, then point the gRPC Ingress to it: 1. Copy the router wildcard certificate Secret into the Camunda namespace: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/single-region/procedure/copy-router-tls-secret.sh ``` ```bash export CAMUNDA_NAMESPACE="camunda" export CAMUNDA_PLATFORM_ROUTER_TLS_SECRET="camunda-platform-router-tls" ./generic/openshift/single-region/procedure/copy-router-tls-secret.sh ``` 2. After you have merged the OpenShift overlay into your local `values.yml` (see the _Configure Route TLS_ section below), override `orchestration.ingress.grpc.tls.secretName` in that `values.yml`. The default value `'-'` lets the Ingress Operator manage the certificate automatically; on ROSA HCP, replace it with the Secret you just created: ```bash yq -i ".orchestration.ingress.grpc.tls.secretName = \"$CAMUNDA_PLATFORM_ROUTER_TLS_SECRET\"" \ values.yml ``` This keeps the upstream `orchestration-route.yml` overlay file untouched so future updates can be pulled cleanly. After applying both steps, the auto-generated Route for the Zeebe gRPC Ingress will carry an inlined `spec.tls.certificate`, HAProxy will emit a per-SNI `[alpn h2,http/1.1]` `crt-list` entry, and gRPC clients will negotiate `h2` successfully.
#### Configure Route TLS Configure the Zeebe Gateway to use an encrypted TLS connection. In OpenShift, the connection from HAProxy to the Zeebe Gateway service can use HTTP/2 only for re-encrypt or pass-through routes, not for edge-terminated or insecure routes. 1. **Zeebe cluster:** at least one [TLS secret](https://kubernetes.io/docs/concepts/configuration/secret/#tls-secrets) is required for the Zeebe Gateway **service**; a second TLS secret for the **route** is optional and depends on your OpenShift flavor: - The first TLS secret is issued to the Zeebe Gateway Service Name. This must use the [PKCS #8 syntax](https://en.wikipedia.org/wiki/PKCS_8) or [PKCS #1 syntax](https://en.wikipedia.org/wiki/PKCS_1) as Zeebe only supports these, referenced as `camunda-platform-internal-service-certificate`. This certificate is also used in the other components such as Operate, Tasklist. In the example below, a TLS certificate is generated for the Zeebe Gateway service with an [annotation](https://docs.openshift.com/container-platform/latest/security/certificates/service-serving-certificate.html). The generated certificate will be in the form of a secret. Another option is [Cert Manager](https://docs.openshift.com/container-platform/latest/security/cert_manager_operator/index.html). For more details, review the [OpenShift documentation](https://docs.openshift.com/container-platform/latest/networking/routes/secured-routes.html#nw-ingress-creating-a-reencrypt-route-with-a-custom-certificate_secured-routes).
PKCS #8, PKCS #1 syntax PKCS #1 private key encoding. PKCS #1 produces a PEM block that contains the private key algorithm in the header and the private key in the body. A key that uses this can be recognised by its BEGIN RSA PRIVATE KEY or BEGIN EC PRIVATE KEY header. NOTE: This encoding is not supported for Ed25519 keys. Attempting to use this encoding with an Ed25519 key will be ignored and default to PKCS #8. PKCS #8 private key encoding. PKCS #8 produces a PEM block with a static header and both the private key algorithm and the private key in the body. A key that uses this encoding can be recognised by its BEGIN PRIVATE KEY header. [PKCS #1, PKCS #8 syntax definition from cert-manager](https://cert-manager.io/docs/reference/api-docs/#cert-manager.io/v1.PrivateKeyEncoding)
- The second TLS secret is optional and applies only to the exposed Route. By default, `orchestration-route.yml` ships with `orchestration.ingress.grpc.tls.secretName: '-'`, which lets the OpenShift Ingress Operator manage the Route TLS certificate automatically by using the cluster's default wildcard. This is the recommended setup on self-managed OpenShift. If you want to terminate the Route with your own custom certificate, for example, the same TLS secret used for Ingress, set `orchestration.ingress.grpc.tls.secretName` to the name of a TLS secret in the Camunda namespace. The Zeebe Gateway Ingress is configured as a [Re-encrypt Route](https://docs.openshift.com/container-platform/latest/networking/routes/route-configuration.html#nw-ingress-creating-a-route-via-an-ingress_route-configuration) in either case. On ROSA HCP, an explicit per-Route certificate is required for ALPN `h2` to work. See the _ROSA HCP — additional steps for ALPN h2_ section above. To configure the orchestration cluster securely, it's essential to set up a secure communication configuration between pods: - We enable gRPC Ingress for the Zeebe Pod, which sets up a secure proxy that we'll use to communicate with the Zeebe cluster. To avoid conflicts with other services, we use a specific domain (`zeebe-$CAMUNDA_DOMAIN`) for the gRPC proxy, different from the one used by other services (`$CAMUNDA_DOMAIN`). We also note that the port used for gRPC is `443`. - We mount the **Service Certificate Secret** (`camunda-platform-internal-service-certificate`) to the Zeebe pod and configure a secure TLS connection. Merge the orchestration route overlay into your `values.yml` file: ```bash yq '. *+ load("generic/openshift/single-region/helm-values/orchestration-route.yml")' values.yml > values-merged.yml && mv values-merged.yml values.yml ```
Review the orchestration route configuration ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/single-region/helm-values/orchestration-route.yml ```
The actual configuration properties can be reviewed [in the Zeebe Gateway configuration documentation](/self-managed/components/orchestration-cluster/zeebe/configuration/gateway.md). 1. **Connectors:** merge the connectors route overlay: ```bash yq '. *+ load("generic/openshift/single-region/helm-values/connectors-route.yml")' values.yml > values-merged.yml && mv values-merged.yml values.yml ```
Review the connectors route configuration ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/single-region/helm-values/connectors-route.yml ```
The actual configuration properties can be reviewed [in the connectors configuration documentation](/self-managed/components/connectors/connectors-configuration.md#zeebe-broker-connection). 1. **TLS for internal applications:** Configure all other applications running inside the cluster and connecting to the Zeebe Gateway to also use TLS. 1. **Domain configuration:** Set up the global configuration to enable the single Ingress definition with the host. Merge the domain overlay: ```bash yq '. *+ load("generic/openshift/single-region/helm-values/domain.yml")' values.yml > values-merged.yml && mv values-merged.yml values.yml ```
Review the domain configuration ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/single-region/helm-values/domain.yml ```
[Routes](https://docs.openshift.com/container-platform/latest/networking/routes/route-configuration.html) serve as OpenShift's default Ingress implementation. If you find that its features aren't suitable for your needs, or if you prefer to use a Kubernetes-native Ingress controller, such as the [ingress-nginx controller](https://github.com/kubernetes/ingress-nginx), [you have that option](https://www.redhat.com/en/blog/a-guide-to-using-routes-ingress-and-gateway-apis-in-kubernetes-without-vendor-lock-in). For guidance on installing an Ingress controller, you can refer to the [Ingress Setup documentation](/self-managed/deployment/helm/configure/ingress/ingress-setup.md). :::note Difference between ingress-nginx and NGINX Ingress Do not confuse the [ingress-nginx controller](https://github.com/kubernetes/ingress-nginx) with the [NGINX Ingress Controller](https://www.redhat.com/en/blog/using-nginx-ingress-controller-red-hat-openshift) that is endorsed by Red Hat for usage with OpenShift. Despite very similar names, they are two different products. If you should decide to use the Red Hat endorsed [NGINX Ingress Controller](https://www.redhat.com/en/blog/using-nginx-ingress-controller-red-hat-openshift), you would require additional adjustments done to the Camunda 8 Ingress objects and the NGINX Ingress Controller itself to make `gRPC` and `HTTP/2` connections work. In that case, please refer to the [example and the prerequisites](https://github.com/nginxinc/kubernetes-ingress/blob/main/examples/ingress-resources/grpc-services/README.md). ::: If you do not have a domain name or do not intend to use one for your Camunda 8 deployment, external access to Camunda 8 web endpoints from outside the OpenShift cluster will not be possible. However, you can use `kubectl port-forward` to access Camunda without a domain name or Ingress configuration. For more information, refer to the [kubectl port-forward documentation](https://kubernetes.io/docs/reference/kubectl/generated/kubectl_port-forward/). To make this work, you will need to configure the deployment to reference `localhost` with the forwarded port. Merge the no-domain overlay into your `values.yml` file: ```bash yq '. *+ load("generic/openshift/single-region/helm-values/no-domain.yml")' values.yml > values-merged.yml && mv values-merged.yml values.yml ```
Review the no-domain configuration ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/single-region/helm-values/no-domain.yml ```
:::info Keycloak issuer and localhost hostname alignment When running without a domain using the Keycloak Operator, Console validates the JWT issuer claim against the configured Keycloak base URL. To keep token issuance consistent and avoid mismatches, the chart configuration sets Keycloak's hostname to its Kubernetes Service name when operating locally. This means that during port-forwarding you may need to map the service hostname to `127.0.0.1` so that browser redirects and token issuer values align. Add (or update) the following entry in your `/etc/hosts` file while developing locally: ```text 127.0.0.1 keycloak-service ``` After adding this entry, you can reach Keycloak at: `http://keycloak-service:18080/auth` **Why port `18080`?** The Keycloak Operator deploys the Keycloak service on port `18080`. We forward that port to the same local port (`18080`) to keep the JWT issuer URL consistent and avoid token validation mismatches. This constraint does not apply when a proper domain and Ingress are configured (the public FQDN is then used as the issuer and no hosts file changes are needed). :::
#### Configuring the Security Context Constraints Depending on your OpenShift cluster's Security Context Constraints (SCCs) configuration, the deployment process may vary. By default, OpenShift employs more restrictive SCCs. The Helm chart must assign `null` to the user running all components and dependencies. The `global.compatibility.openshift.adaptSecurityContext` variable in your values.yaml can be used to set the following possible values: - `force`: The `runAsUser` and `fsGroup` values will be null in all components. - `disabled`: The `runAsUser` and `fsGroup` values will not be modified (default). ```bash yq '. *+ load("generic/openshift/single-region/helm-values/scc.yml")' values.yml > values-merged.yml && mv values-merged.yml values.yml ```
Review the restrictive SCC configuration ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/single-region/helm-values/scc.yml ```
To use permissive SCCs, simply install the charts as they are. Follow the [general Helm deployment guide](/self-managed/deployment/helm/install/quick-install.md). ```bash yq '. *+ load("generic/openshift/single-region/helm-values/no-scc.yml")' values.yml > values-merged.yml && mv values-merged.yml values.yml ```
Review the permissive SCC configuration ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/single-region/helm-values/no-scc.yml ```
#### Enable Enterprise components Some components are not enabled by default in this deployment. For more information on how to configure and enable these components, refer to [configuring Enterprise components and connectors](/self-managed/deployment/helm/install/quick-install.md#configuring-enterprise-components-and-connectors). ### Deploy prerequisite services Before deploying Camunda, you need to deploy the infrastructure services it depends on. The core infrastructure (Elasticsearch and PostgreSQL) is deployed using Kubernetes operators as described in [Deploy infrastructure with Kubernetes operators](/self-managed/deployment/helm/configure/operator-based-infrastructure.md). Keycloak can optionally be deployed as your OIDC provider: - **Elasticsearch**: Deployed via [ECK (Elastic Cloud on Kubernetes)](https://www.elastic.co/guide/en/cloud-on-k8s/current/index.html) — one option for secondary storage - **PostgreSQL**: Deployed via [CloudNativePG](https://cloudnative-pg.io/) - **Keycloak** _(optional)_: Deployed via the [Keycloak Operator](https://www.keycloak.org/operator/installation) — can be replaced with any OIDC-compatible IdP All deploy scripts are located in `generic/kubernetes/operator-based/`. Review each script before executing to understand the deployment steps, and adapt the operator Custom Resource configurations for your specific requirements (resource limits, storage, replicas, etc.). :::note Working directory All commands in this guide assume you are at the **repository root** (the directory created by `get-your-copy.sh`). The deploy commands below use subshells `(cd ... && ./deploy.sh)` to preserve your working directory. ::: #### Deploy Elasticsearch {#deploy-elasticsearch} #### Deploy PostgreSQL {#deploy-postgresql} Deploy PostgreSQL clusters using the CloudNativePG operator: ```bash CLUSTER_FILTER="pg-identity,pg-webmodeler" (cd generic/kubernetes/operator-based/postgresql && ./deploy.sh) ``` This script installs the CNPG operator (auto-detecting OpenShift to apply SCC patches), creates secrets, deploys the specified PostgreSQL clusters, and waits for readiness. The following PostgreSQL clusters are created: - **pg-identity**: Database for Camunda Identity component - **pg-webmodeler**: Database for Web Modeler component (remove from configuration if not needed)
Review the PostgreSQL cluster configuration ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/postgresql/postgresql-clusters.yml ```
For more details on the PostgreSQL deployment, see [PostgreSQL deployment in the operator-based infrastructure guide](/self-managed/deployment/helm/configure/operator-based-infrastructure.md#postgresql-deployment). #### Deploy Keycloak (optional) {#deploy-keycloak} If you choose Keycloak as your identity provider (IdP), deploy it using the Keycloak Operator. First, deploy its PostgreSQL database, then deploy the Keycloak operator and instance. If you use an external OIDC provider instead, skip this section. Deploy Keycloak with OpenShift Routes: ```bash # Deploy the PostgreSQL database for Keycloak CLUSTER_FILTER=pg-keycloak (cd generic/kubernetes/operator-based/postgresql && ./deploy.sh) # Deploy Keycloak export KEYCLOAK_CONFIG_FILE="keycloak-instance-domain-openshift.yml" (cd generic/kubernetes/operator-based/keycloak && ./deploy.sh) ```
Review the OpenShift Keycloak instance configuration ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/keycloak/keycloak-instance-domain-openshift.yml ```
Deploy Keycloak with nginx-ingress: ```bash # Deploy the PostgreSQL database for Keycloak CLUSTER_FILTER=pg-keycloak (cd generic/kubernetes/operator-based/postgresql && ./deploy.sh) # Deploy Keycloak export KEYCLOAK_CONFIG_FILE="keycloak-instance-domain-nginx.yml" (cd generic/kubernetes/operator-based/keycloak && ./deploy.sh) ```
Review the nginx Keycloak instance configuration ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/keycloak/keycloak-instance-domain-nginx.yml ```
Deploy Keycloak without external access: ```bash # Deploy the PostgreSQL database for Keycloak CLUSTER_FILTER=pg-keycloak (cd generic/kubernetes/operator-based/postgresql && ./deploy.sh) # Deploy Keycloak export KEYCLOAK_CONFIG_FILE="keycloak-instance-no-domain.yml" (cd generic/kubernetes/operator-based/keycloak && ./deploy.sh) ```
Review the no-domain Keycloak instance configuration ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/keycloak/keycloak-instance-no-domain.yml ```
For more details on the Keycloak deployment, see [Keycloak deployment in the operator-based infrastructure guide](/self-managed/deployment/helm/configure/operator-based-infrastructure.md#keycloak-deployment). #### Merge operator overlays into values Once the operator-managed services are running, merge the corresponding Helm values overlays into your `values.yml` file. These overlays configure Camunda components to use the external operator-managed services instead of embedded subcharts. Merge the **Elasticsearch** overlay: ```bash yq '. *+ load("generic/kubernetes/operator-based/elasticsearch/camunda-elastic-values.yml")' values.yml > values-merged.yml && mv values-merged.yml values.yml ```
Review the Elasticsearch Helm overlay ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/elasticsearch/camunda-elastic-values.yml ```
Merge the **Identity PostgreSQL** overlay: ```bash yq '. *+ load("generic/kubernetes/operator-based/postgresql/camunda-identity-values.yml")' values.yml > values-merged.yml && mv values-merged.yml values.yml ```
Review the Identity PostgreSQL Helm overlay ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/postgresql/camunda-identity-values.yml ```
If **Web Modeler** is enabled, also merge the **Web Modeler PostgreSQL** overlay: ```bash yq '. *+ load("generic/kubernetes/operator-based/postgresql/camunda-webmodeler-values.yml")' values.yml > values-merged.yml && mv values-merged.yml values.yml ```
Review the Web Modeler PostgreSQL Helm overlay ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/postgresql/camunda-webmodeler-values.yml ```
Merge the **Keycloak** overlay _(optional — only if Keycloak was deployed as your IdP; choose the appropriate variant for your setup)_: ```bash yq '. *+ load("generic/kubernetes/operator-based/keycloak/camunda-keycloak-domain-values.yml")' values.yml > values-merged.yml && mv values-merged.yml values.yml ```
Review the Keycloak domain Helm overlay ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/keycloak/camunda-keycloak-domain-values.yml ```
```bash yq '. *+ load("generic/kubernetes/operator-based/keycloak/camunda-keycloak-domain-values.yml")' values.yml > values-merged.yml && mv values-merged.yml values.yml ```
Review the Keycloak domain Helm overlay ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/keycloak/camunda-keycloak-domain-values.yml ```
```bash yq '. *+ load("generic/kubernetes/operator-based/keycloak/camunda-keycloak-no-domain-values.yml")' values.yml > values-merged.yml && mv values-merged.yml values.yml ```
Review the Keycloak no-domain Helm overlay ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/keycloak/camunda-keycloak-no-domain-values.yml ```
#### Fill your deployment with actual values If **Web Modeler** is enabled, create the SMTP secret: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/single-region/procedure/create-webmodeler-secret.sh ``` :::note Database and authentication secrets are automatically managed by the operators: - **PostgreSQL credentials**: Created by CloudNativePG via `set-secrets.sh` - **Keycloak admin credentials** _(optional)_: Created by the Keycloak Operator - **Elasticsearch credentials**: Created by ECK - **Identity secrets**: Created by the operator-based deployment scripts Only the SMTP password for Web Modeler needs to be created manually. ::: Once you've prepared the `values.yml` file with all overlays merged, run the following `envsubst` command to substitute the environment variables with their actual values: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/single-region/procedure/assemble-envsubst-values.sh ``` ### Install Camunda 8 using Helm Now that the `generated-values.yml` is ready, you can install Camunda 8 using Helm. The following are the required environment variables with some example values: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/single-region/procedure/chart-env.sh ``` - `CAMUNDA_NAMESPACE` is the Kubernetes namespace where Camunda will be installed. - `CAMUNDA_RELEASE_NAME` is the name of the Helm release associated with this Camunda installation. Then run the following command: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/openshift/single-region/procedure/install-chart.sh ``` This command: - Installs (or upgrades) Camunda using the Helm chart. - Substitutes the appropriate version using the `$CAMUNDA_HELM_CHART_VERSION` environment variable. - Applies the configuration from `generated-values.yml`. You can track the progress of the installation using the following command: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/check-deployment-ready.sh ``` ## Verify connectivity to Camunda 8 First, we need an OAuth client to be able to connect to the Camunda 8 cluster. ### Generate an M2M token using Identity Generate an M2M token by following the steps outlined in the [Identity getting started guide](/self-managed/components/management-identity/overview.md), along with the [incorporating applications documentation](/self-managed/components/management-identity/application-user-group-role-management/applications.md). Below is a summary of the necessary instructions: 1. Open Identity in your browser at `https://${CAMUNDA_DOMAIN}/managementidentity`. You will be redirected to your IdP and prompted to log in. 2. Log in with the initial user `admin`. This username is defined by the `identity.firstUser.username` value in your Helm chart configuration. Retrieve the auto-generated password from the Kubernetes secret: ```shell kubectl get secret identity-secret-for-components \ --namespace "$CAMUNDA_NAMESPACE" \ -o jsonpath='{.data.identity-first-user-password}' | base64 -d; echo ``` 3. Select **Add application** and select **M2M** as the type. Assign a name like "test." 4. Select the newly created application. Then, select **Access to APIs > Assign permissions**, and select the **Orchestration API** with "read" and "write" permission. 5. Retrieve the `client-id` and `client-secret` values from the application details ```shell export ZEEBE_CLIENT_ID='client-id' # retrieve the value from the identity page of your created m2m application export ZEEBE_CLIENT_SECRET='client-secret' # retrieve the value from the identity page of your created m2m application ``` 6. Open the Orchestration Cluster Admin in your browser at `https://${CAMUNDA_DOMAIN}/admin` and log in with the user `admin` (defined in `identity.firstUser` of the values file). 7. In the Admin navigation menu, select **Roles**. 8. Either select an existing role (for example, **Admin**) or [create a new role](/components/admin/role.md) with the appropriate permissions for your use case. 9. In the selected role view, open the **Clients** tab and click **Assign client**. 10. Enter the client ID of your application created in Management Identity (for example, `test`) and click **Assign client** to save. This operation links the OIDC client to the role's permissions in the Orchestration Cluster, granting the application access to the cluster resources. For more information about managing roles and clients, see [Roles](/components/admin/role.md#manage-clients). Admin and the Orchestration cluster must be port-forwarded to be able to connect to the cluster. If using Keycloak via the Keycloak Operator, you also need to port-forward the Keycloak service. ```shell kubectl port-forward "services/$CAMUNDA_RELEASE_NAME-identity" 8085:80 --namespace "$CAMUNDA_NAMESPACE" kubectl port-forward "services/$CAMUNDA_RELEASE_NAME-zeebe-gateway" 8080:8080 --namespace "$CAMUNDA_NAMESPACE" # If using Keycloak Operator: kubectl port-forward "services/keycloak-service" 18080:18080 --namespace "$CAMUNDA_NAMESPACE" ``` 1. Open Identity in your browser at `http://localhost:8085/managementidentity`. You will be redirected to your IdP and prompted to log in. 2. Log in with the initial user `admin`. This username is defined by the `identity.firstUser.username` value in your Helm chart configuration. Retrieve the auto-generated password from the Kubernetes secret: ```shell kubectl get secret identity-secret-for-components \ --namespace "$CAMUNDA_NAMESPACE" \ -o jsonpath='{.data.identity-first-user-password}' | base64 -d; echo ``` 3. Select **Add application** and select **M2M** as the type. Assign a name like "test." 4. Select the newly created application. Then, select **Access to APIs > Assign permissions**, and select the **Orchestration API** with "read" and "write" permission. 5. Retrieve the `client-id` and `client-secret` values from the application details ```shell export ZEEBE_CLIENT_ID='client-id' # retrieve the value from the identity page of your created m2m application export ZEEBE_CLIENT_SECRET='client-secret' # retrieve the value from the identity page of your created m2m application ``` 6. Open the Orchestration Cluster Admin in your browser at `http://localhost:8080/admin` and log in with the user `admin` (defined in `identity.firstUser` of the values file). 7. In the Admin navigation menu, select **Roles**. 8. Either select an existing role (for example, **Admin**) or [create a new role](/components/admin/role.md) with the appropriate permissions for your use case. 9. In the selected role view, open the **Clients** tab and click **Assign client**. 10. Enter the client ID of your application created in Management Identity (for example, `test`) and click **Assign client** to save. This operation links the OIDC client to the role's permissions in the Orchestration Cluster, granting the application access to the cluster resources. For more information about managing roles and clients, see [Roles](/components/admin/role.md#manage-clients). ### Use the token For a detailed guide on generating and using a token, consult the relevant documentation on [authenticating with the Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md). Export the following environment variables: ```shell reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/export-verify-zeebe-domain.sh ``` This requires to port-forward the Zeebe Gateway to be able to connect to the cluster. ```shell kubectl port-forward "services/$CAMUNDA_RELEASE_NAME-zeebe-gateway" 8080:8080 --namespace "$CAMUNDA_NAMESPACE" ``` Export the following environment variables: ```shell reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/export-verify-zeebe-local.sh ``` Generate a temporary token to access the Orchestration Cluster REST API, then capture the value of the `access_token` property and store it as your token. Use the stored token (referred to as `TOKEN` in this case) to interact with the Orchestration Cluster REST API and display the cluster topology: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/check-zeebe-cluster-topology.sh ``` ...and results in the following output:
Example output ```json reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/single-region/procedure/check-zeebe-cluster-topology-output.json ```
Follow our existing [Modeler guide on deploying a diagram](/self-managed/components/modeler/desktop-modeler/deploy-to-self-managed.md). Below are the helper values required to be filled in Modeler: The following values are required for the OAuth authentication: - **Cluster endpoint:** `https://zeebe-$CAMUNDA_DOMAIN`, replacing `$CAMUNDA_DOMAIN` with your domain - **Client ID:** Retrieve the client ID value from the identity page of your created M2M application - **Client Secret:** Retrieve the client secret value from the Identity page of your created M2M application - **OAuth Token URL:** Your IdP's token endpoint (for example, `https://$CAMUNDA_DOMAIN/auth/realms/camunda-platform/protocol/openid-connect/token` when using Keycloak), replacing `$CAMUNDA_DOMAIN` with your domain - **Audience:** `orchestration-api`, the default for Camunda 8 Self-Managed This requires port-forwarding the Zeebe Gateway to be able to connect to the cluster. Desktop Modeler communicates via gRPC, so only port `26500` needs to be forwarded. If you also need REST API access (port `8080`), refer to the [REST API section above](#use-the-token). ```shell kubectl port-forward "services/$CAMUNDA_RELEASE_NAME-zeebe-gateway" 26500:26500 --namespace "$CAMUNDA_NAMESPACE" ``` The following values are required for OAuth authentication: - **Cluster endpoint:** `http://localhost:26500` - **Client ID:** Retrieve the client ID value from the identity page of your created M2M application - **Client Secret:** Retrieve the client secret value from the Identity page of your created M2M application - **OAuth Token URL:** Your IdP's token endpoint (for example, `http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token` when using Keycloak with port-forwarding) - **Audience:** `orchestration-api`, the default for Camunda 8 Self-Managed
## Pitfalls to avoid For general deployment pitfalls, visit the [deployment troubleshooting guide](/self-managed/operational-guides/troubleshooting.md). ### Persistent volume reclaim policy OpenShift StorageClasses often default to a `Delete` reclaim policy, which means persistent volume data is permanently deleted when a PVC is removed. This can lead to complete and unrecoverable data loss for Orchestration Cluster brokers. Ensure your StorageClass uses a `Retain` reclaim policy for production deployments. Verify your configuration: ```bash oc get storageclass # RECLAIMPOLICY should show "Retain", not "Delete" ``` For more details, including accepted alternatives to a cluster-wide `Retain` policy, see the [production install guide](/self-managed/deployment/helm/install/production/index.md#persistent-volume-reclaim-policy). ### Security Context Constraints (SCCs) [Security Context Constraints (SCCs)](https://docs.openshift.com/container-platform/latest/authentication/managing-security-context-constraints.html) are a set of conditions that a pod must adhere to in order to be accepted into the system. They define the security conditions under which a pod operates. Similar to how roles control user permissions, SCCs regulate the permissions of deployed applications, both at the pod and container level. It's generally recommended to deploy applications with the most restrictive SCCs possible. If you're unfamiliar with security context constraints, you can refer to the [OpenShift documentation](https://docs.openshift.com/container-platform/latest/authentication/managing-security-context-constraints.html). #### Restrictive SCCs The following represents the most restrictive SCCs that can be used to deploy Camunda 8. Note that in OpenShift 4.10, these are equivalent to the built-in `restricted` SCCs (which are the default SCCs). ```yaml Allow Privileged: false Default Add Capabilities: Required Drop Capabilities: KILL, MKNOD, SYS_CHROOT, SETUID, SETGID Allowed Capabilities: Allowed Seccomp Profiles: Allowed Volume Types: configMap, downwardAPI, emptyDir, persistentVolumeClaim, projected, secret Allow Host Network: false Allow Host Ports: false Allow Host PID: false Allow Host IPC: false Read Only Root Filesystem: false Run As User Strategy: MustRunAsRange SELinux Context Strategy: MustRunAs FSGroup Strategy: MustRunAs Supplemental Groups Strategy: RunAsAny ``` When using these SCCs, be sure not to specify _any_ `runAsUser` or `fsGroup` values in either the pod or container security context. Instead, allow OpenShift to assign arbitrary IDs. :::note If you are providing the ID ranges yourself, you can also configure the `runAsUser` and `fsGroup` values accordingly. ::: The Camunda Helm chart can be deployed to OpenShift with a few modifications, primarily revolving around your desired security context constraints. #### Non-root SCCs If you intend to deploy Camunda 8 while restricting applications from running as root (e.g., using the `nonroot` built-in SCCs), you'll need to configure each pod and container to run as a non-root user. For example, when deploying Zeebe using a stateful set, you would include the following YAML, replacing `1000` with the desired user ID: ```yaml spec: template: spec: securityContext: runAsUser: 1000 containers: securityContext: runAsUser: 1000 ``` :::note As the container user in OpenShift is always part of the root group, defining a `fsGroup` for any Camunda 8 application pod security context is unnecessary. ::: This configuration is necessary for all Camunda 8 applications, as well as related ones (e.g., Keycloak, PostgreSQL, etc.). It's particularly crucial for stateful applications that will write to persistent volumes, but it's also generally a good security practice. #### Permissive SCCs If you deploy Camunda 8 (and related infrastructure) with permissive SCCs out of the box, there's nothing specific for you to configure. Here, permissive SCCs refer to those where the strategy for `RunAsUser` is defined as `RunAsAny` (including root). ### Writing pod permissions for logs OpenShift security policies often restrict writing to files within containers. This can cause Camunda pods to fail to write to the filesystem, which is typically required for writing log in files. Instead, we configure the environment to output logs to `stdout` and `stderr` only, which are supported by OpenShift logging infrastructure. For Camunda components (except Identity), this can be done by setting the environment variable in the chart values: ```yaml zeebe/tasklist/operate/etc: env: - name: CAMUNDA_LOG_FILE_APPENDER_ENABLED value: "false" ``` This will disable the file appender and ensure logs are visible via the container's log output. --- ## Add custom Kubernetes manifests in Helm charts Add extra Kubernetes manifests to the Camunda 8 [Helm chart](/self-managed/deployment/helm/install/quick-install.md) by defining them in the `values.yaml` file. Use this to include resources such as ConfigMaps, Deployments, or Services. ## Configuration Define extra manifests in your `values.yaml` file under `global.extraManifests`. The key accepts a list of Kubernetes manifests. ```yaml global: extraManifests: - | apiVersion: v1 kind: ConfigMap metadata: name: example-cm-one data: test: test-one - | apiVersion: v1 kind: ConfigMap metadata: name: example-cm-two data: test: test-two ``` For more information, see the Kubernetes [object documentation](https://kubernetes.io/docs/concepts/overview/working-with-objects/). ### Manipulate manifests If you need to adjust rendered manifests directly (for example, when the chart template does not support a feature), use [Helm Post Rendering](https://helm.sh/docs/topics/advanced/#post-rendering). Post rendering lets you manipulate, configure, or validate manifests before Helm installs them. Use post rendering for quick workarounds, but also consider raising a feature request for your use case. ## Best practices - **Keep it simple**: Use clear and concise YAML syntax to avoid complexity. - **Use comments**: Include comments in your YAML file to explain the purpose of each manifest. - **Test thoroughly**: Ensure that all added manifests are correctly formatted and functional before deployment. ## Troubleshooting - **Syntax errors**: Check for syntax errors such as indentation issues or missing colons in key-value pairs. - **Manifest validation**: Verify that each manifest is valid and correctly formatted according to Kubernetes specifications. --- ## Configure component configuration This page explains how to configure Camunda components in Helm charts. For most use cases, use `.extraConfiguration` to add or override properties while keeping the chart-provided defaults. Use `.configuration` only when you intentionally want to replace the entire default application configuration file. For the complete list of configuration options per component, see the [Self-Managed Components documentation](../../../components/orchestration-cluster/overview.md) (this is where each component documents its supported application configuration). ## Prerequisites - A deployed Camunda Helm chart release. - Access to the `values.yaml` file. - Basic understanding of Spring Boot configuration (`application.yaml` or `application.properties`). ## Configuration ### Parameters | Key | Type | Description | | ------------------------------------ | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `.extraConfiguration` | list | **Recommended:** Additional configuration entries layered on top of the default configuration. Each entry has a `file` (filename), `content` (file contents), and optionally `springImport` (boolean, default `true`). See [how it works per component](#how-extraconfiguration-works-per-component). | | `.configuration` | string | **Advanced:** Full application configuration file content (for example, the full contents of `application.yaml`). Using this **replaces** the component's default application configuration. | ### Configuration options Two Helm values are available for component configuration: - `.extraConfiguration` - `.configuration` :::tip Which should I use? - Use `.extraConfiguration`. It keeps the chart-provided defaults intact and lets you add/override only the keys you need. - Use `.configuration` only if you intentionally want to take full control of the application's configuration file. It **overwrites** the default config and can affect startup behavior and upgrades. ::: #### componentName.extraConfiguration Use `.extraConfiguration` to add or override configuration **without replacing** the component's default configuration. This option accepts an **ordered list** of entries, where each entry specifies a `file` name and its `content`. Entries are processed in order, so **later entries override earlier ones** for duplicate keys — mimicking Spring Boot's `spring.config.import` semantics. :::info Recommended default Start with `.extraConfiguration` whenever possible. It is safer for upgrades because the Helm chart can continue to evolve its defaults while you only maintain the deltas you actually care about. ::: ```yaml identity: extraConfiguration: - file: logging-debug.yaml content: | logging: level: ROOT: DEBUG io.camunda.identity: DEBUG - file: logging-production.yaml content: | logging: level: ROOT: INFO io.camunda.identity: INFO ``` In this example, `logging-production.yaml` is applied after `logging-debug.yaml`, so the final effective log level is `INFO` (last writer wins). :::note Why an ordered list? Previous versions of the Helm chart used a map (key-value pairs) for `extraConfiguration`. Maps in Go (and Helm) do not guarantee iteration order. Since configuration layering is order-dependent, `extraConfiguration` now uses an array to ensure entries are always applied in the order you define them. ::: #### componentName.configuration Use `.configuration` to define an application configuration file directly in `values.yaml`. :::caution Advanced option (overwrites defaults) When you set `.configuration`, the Helm chart renders **your content as the entire application configuration file** for that component. This means: - You are responsible for including any settings that the chart normally provides by default. - During upgrades, configuration format or default changes may require you to update your configuration before the component can start. Prefer `.extraConfiguration` unless you specifically need to replace the full file. ::: For example, `application.yaml`: ```yaml orchestration: configuration: |- camunda: # Orchestration cluster settings database: data: snapshot-period: "5m" primary-storage: disk: free-space: processing: "2GB" replication: "1GB" secondary-storage: autoconfigure-camunda-exporter: true type: "elasticsearch" elasticsearch: url: "http://camunda-elasticsearch:9200" cluster-name: "elasticsearch" username: "" password: "${VALUES_ELASTICSEARCH_PASSWORD:}" index-prefix: "" number-of-replicas: "1" ``` ### Default properties The `helm template` command can show you the application's default configuration as rendered by the chart. - Use this output as a **reference** to discover the right keys and defaults. - Only copy the full content into `.configuration` if you intentionally want to **replace** the default config (advanced). Keep the original `values.yaml` unchanged and maintain a separate file with your custom settings. For details, see [Creating your own values files](self-managed/deployment/helm/chart-parameters.md#creating-your-own-values-files). To generate the default configuration, replace `` with your release name and run: ```bash helm template \ -f values.yaml \ camunda/camunda-platform \ --show-only templates/operate/configmap.yaml ``` The `--show-only` flag prints the `configmap`. - If you are using `.extraConfiguration`, use the rendered `application.yml` to identify the correct keys and then add only the overrides you need. - If you are using `.configuration`, copy the full `application.yml` content, modify it, and place it under the appropriate `.configuration` key in `values.yaml`. ### How extraConfiguration works per component Camunda components use different runtimes and configuration mechanisms. The Helm chart handles `extraConfiguration` differently for each, so the user-facing `values.yaml` API remains consistent while the underlying behavior adapts to what each application actually supports. #### Spring Boot components **Applies to:** Identity, Connectors, Orchestration, Web Modeler REST API Spring Boot components support loading multiple configuration files via `spring.config.import`. Each `extraConfiguration` entry is mounted as an **individual file** in the container's config directory and imported by Spring at startup. Order is preserved by the array in the values.yaml. Spring applies files in import order, with later files overriding earlier ones. ```yaml identity: extraConfiguration: - file: custom-logging.yaml content: | logging: level: ROOT: DEBUG - file: custom-cache.yaml content: | spring: cache: type: caffeine ``` Both files are mounted and imported separately. No merging happens at Helm template time. ##### Excluding files from `spring.config.import` By default, every `extraConfiguration` entry is added to Spring Boot's `spring.config.import` property. This works for Spring-compatible files (YAML, properties) but causes Spring Boot to fail on startup when non-Spring files (such as Log4j2 XML configs) are provided. To mount a file into the container **without** adding it to `spring.config.import`, set `springImport: false` on the entry: ```yaml connectors: extraConfiguration: - file: log4j2.xml springImport: false content: |- ... - file: custom-application.yaml content: | my.custom.property: value ``` In this example, `log4j2.xml` is mounted in the config directory but not imported by Spring. `custom-application.yaml` is both mounted and imported (the default behavior when `springImport` is omitted or set to `true`). #### Node.js Components **Applies to:** Console Console is a Node.js application that reads only two configuration files: `application.yaml` (the main config) and `application-override.yaml` (a single override file). It does not support loading multiple override files. Because of this constraint, the Helm chart **merges all `extraConfiguration` entries at template rendering time** into a single `application-override.yaml` using deep merge. Later entries override earlier ones for duplicate keys. ```yaml console: extraConfiguration: - file: feature-flags.yaml content: | camunda: console: features: newDashboard: true - file: logging.yaml content: | camunda: console: logging: level: INFO ``` The Helm chart merges both entries and renders a single `application-override.yaml` in the ConfigMap: ```yaml # Rendered application-override.yaml camunda: console: features: newDashboard: true logging: level: INFO ``` :::note `console.overrideConfiguration` is the old way of overriding the default application configuration for Console. It has been deprecated. Please convert to using `console.extraConfiguration`. ::: #### Custom configuration loading **Applies to:** Optimize Optimize uses its own configuration loader (not standard Spring Boot conventions). It reads only two files: `environment-config.yaml` (main config) and `application-ccsm.yaml` (Identity/auth config, loaded when the `ccsm` Spring profile is active). It does **not** scan its config directory for additional files. Starting with **Camunda 8.9**, Optimize also loads any additional configuration files that are referenced via Spring's `spring.config.import` or `spring.config.location` properties. This allows you to use `optimize.extraConfiguration` to configure Optimize-native settings in addition to Spring-only settings. In the Helm chart, each `optimize.extraConfiguration` entry is rendered as a separate file in the Optimize config directory. Spring imports these files in the order you define them, and Optimize's configuration loader applies them in the **same order**. For duplicate keys, later entries override earlier ones. ```yaml optimize: extraConfiguration: - file: custom-zeebe.yaml content: | zeebe: partitionCount: 6 - file: custom-es.yaml content: | es: connection: nodes: - host: "custom-es-host" httpPort: 9200 ``` Both files remain separate in the Optimize config directory and are loaded at runtime in the order you define. :::caution The `content` must be valid YAML. If invalid YAML is provided, Helm will fail during template rendering with a parse error. This is intentional and prevents deploying a broken configuration. ::: ### Summary | Component | Runtime | Config format | How `extraConfiguration` is applied | | --------------------- | ------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Identity | Spring Boot | YAML | Individual files mounted, imported via `spring.config.import` (use `springImport: false` to skip import) | | Connectors | Spring Boot | YAML | Individual files mounted, imported via `spring.config.import` (use `springImport: false` to skip import) | | Orchestration Cluster | Spring Boot | YAML | Individual files mounted, imported via `spring.config.import` (use `springImport: false` to skip import) | | Web Modeler REST API | Spring Boot | YAML | Individual files mounted, imported via `spring.config.import` (use `springImport: false` to skip import) | | Console | Node.js | YAML | Merged at template time into single `application-override.yaml` | | Optimize | Java (custom) | YAML | Loaded at runtime from `environment-config.yaml` plus any files imported via `spring.config.import` / `spring.config.location` (rendered from `optimize.extraConfiguration`); later imports override earlier ones. | ## Practical example: migrating from environment variables to a configuration file This example shows how to convert a Zeebe backup configuration from environment variables to the `application.yaml` file format. ### Step 1: Review the existing environment variable configuration To configure Zeebe backups, earlier charts required environment variables: ```yaml zeebe: clusterSize: "1" enabled: true partitionCount: "1" replicationFactor: "1" env: - name: ZEEBE_BROKER_DATA_BACKUP_S3_BUCKETNAME value: zeebebackuptest - name: ZEEBE_BROKER_DATA_BACKUP_S3_REGION value: us-east-1 - name: ZEEBE_BROKER_DATA_BACKUP_STORE value: S3 - name: ZEEBE_BROKER_DATA_BACKUP_S3_ENDPOINT value: http://loki-minio.monitoring.svc.cluster.local:9000 - name: ZEEBE_BROKER_DATA_BACKUP_S3_ACCESSKEY value: supersecretaccesskey - name: ZEEBE_BROKER_DATA_BACKUP_S3_SECRETKEY value: supersecretkey - name: ZEEBE_BROKER_DATA_BACKUP_S3_APICALLTIMEOUT value: PT180S - name: ZEEBE_BROKER_DATA_BACKUP_S3_BASEPATH value: zeebebackup ``` ### Step 2: Generate the default configuration file Run the following command to render the default configuration file and fill in Helm values: ```bash helm template \ -f values.yaml \ camunda/camunda-platform \ --show-only templates/zeebe/configmap.yaml ``` The output includes an `application.yml` section similar to: ```yaml zeebe: broker: exporters: elasticsearch: className: "io.camunda.zeebe.exporter.ElasticsearchExporter" args: url: "http://RELEASE-elasticsearch:9200" index: prefix: "zeebe-record" gateway: enable: true network: port: 26500 security: enabled: false authentication: mode: none network: host: 0.0.0.0 commandApi: port: 26501 internalApi: port: 26502 monitoringApi: port: "9600" cluster: clusterSize: "1" replicationFactor: "1" partitionsCount: "1" clusterName: RELEASE-zeebe threads: cpuThreadCount: "3" ioThreadCount: "3" ``` ### Step 3: Map environment variables to configuration properties For each environment variable, find the corresponding property in the [Zeebe configuration](/self-managed/components/orchestration-cluster/zeebe/configuration/broker.md). For example, the environment variable `ZEEBE_BROKER_DATA_BACKUP_S3_BUCKETNAME` maps to the property `zeebe.broker.data.backup.s3.bucketName`, documented under [Zeebe S3 Backup](/self-managed/components/orchestration-cluster/zeebe/configuration/broker.md#zeebebrokerdatabackups3). Add the property to the configuration file. Add the `data` section under `zeebe.broker`: ```yaml zeebe: broker: data: backup: s3: bucketName: "zeebebackuptest" exporters: elasticsearch: className: "io.camunda.zeebe.exporter.ElasticsearchExporter" args: url: "http://RELEASE-elasticsearch:9200" index: prefix: "zeebe-record" gateway: enable: true network: port: 26500 security: enabled: false authentication: mode: none network: host: 0.0.0.0 commandApi: port: 26501 internalApi: port: 26502 monitoringApi: port: "9600" cluster: clusterSize: "1" replicationFactor: "1" partitionsCount: "1" clusterName: RELEASE-zeebe threads: cpuThreadCount: "3" ioThreadCount: "3" ``` ### Step 4: Repeat for all environment variables Follow the same process for each environment variable. The resulting configuration looks like this: ```yaml zeebe: broker: data: backup: store: "S3" s3: bucketName: "zeebebackuptest" region: "us-east-1" endpoint: "http://loki-minio.monitoring.svc.cluster.local:9000" accessKey: "supersecretaccesskey" secretKey: "supersecretkey" apiCallTimeout: "PT180S" basePath: "zeebebackup" exporters: elasticsearch: className: "io.camunda.zeebe.exporter.ElasticsearchExporter" args: url: "http://RELEASE-elasticsearch:9200" index: prefix: "zeebe-record" gateway: enable: true network: port: 26500 security: enabled: false authentication: mode: none network: host: 0.0.0.0 commandApi: port: 26501 internalApi: port: 26502 monitoringApi: port: "9600" cluster: clusterSize: "1" replicationFactor: "1" partitionsCount: "1" clusterName: RELEASE-zeebe threads: cpuThreadCount: "3" ioThreadCount: "3" ``` ### Step 5: Add the configuration to `values.yaml` Prefer adding the new settings via `zeebe.extraConfiguration` so you only maintain the keys you changed and keep the chart-provided defaults. For example: ```yaml zeebe: extraConfiguration: - file: backup-s3.yaml content: |- zeebe: broker: data: backup: store: "S3" s3: bucketName: "zeebebackuptest" region: "us-east-1" endpoint: "http://loki-minio.monitoring.svc.cluster.local:9000" accessKey: "supersecretaccesskey" secretKey: "supersecretkey" apiCallTimeout: "PT180S" basePath: "zeebebackup" ``` :::caution Advanced alternative: `zeebe.configuration` overwrites defaults If you intentionally want to fully control Zeebe's `application.yml`, place the full configuration under `zeebe.configuration`. This replaces the chart's default configuration and may require updates during upgrades. ::: ```yaml zeebe: configuration: |- zeebe: broker: data: backup: store: "S3" s3: bucketName: "zeebebackuptest" region: "us-east-1" endpoint: "http://loki-minio.monitoring.svc.cluster.local:9000" accessKey: "supersecretaccesskey" secretKey: "supersecretkey" apiCallTimeout: "PT180S" basePath: "zeebebackup" exporters: elasticsearch: className: "io.camunda.zeebe.exporter.ElasticsearchExporter" args: url: "http://RELEASE-elasticsearch:9200" index: prefix: "zeebe-record" # Example: exporter-side filters for Optimize (Camunda 8.9+) # bpmnProcessIdExclusion: # - technicalProcess # variableNameInclusionStartWith: # - businessTotal gateway: enable: true network: port: 26500 security: enabled: false authentication: mode: none network: host: 0.0.0.0 commandApi: port: 26501 internalApi: port: 26502 monitoringApi: port: "9600" cluster: clusterSize: "1" replicationFactor: "1" partitionsCount: "1" clusterName: RELEASE-zeebe threads: cpuThreadCount: "3" ioThreadCount: "3" ``` The commented `variable-name` and `bpmn-process-id` sections above only illustrate where to configure exporter-side filters for Optimize in Camunda 8.9 and later. For the complete list of available options, their semantics, and upgrade behavior, see: - [Elasticsearch exporter](../../../../components/orchestration-cluster/zeebe/exporters/elasticsearch-exporter/) - [OpenSearch exporter](../../../../components/orchestration-cluster/zeebe/exporters/opensearch-exporter/) ## Connectors configuration :::info Connectors are **enabled by default** in Camunda 8.8. This section covers configuration options for Connectors, including how to disable them if needed. ::: The Connector runtime is enabled by default. To use connectors, install connector element templates. For details, see [Manage connector templates in Web Modeler](/components/connectors/manage-connector-templates.md) or [Configuring templates in Desktop Modeler](/components/modeler/desktop-modeler/element-templates/configuring-templates.md). For the full list of options, see the [Connectors Helm values](https://artifacthub.io/packages/helm/camunda/camunda-platform#connectors-parameters). ### Disable Connectors To disable Connectors, set `connectors.enabled: false` when deploying the Helm chart: ```yaml connectors: enabled: false ``` ### Polling authentication mode Connectors use the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) to fetch process definitions that contain inbound connectors. Depending on your Camunda architecture, choose one of the following values for the `inbound.mode` parameter: - `disabled` — Polling from the Orchestration Cluster is disabled. The connector runtime supports only outbound interactions, such as HTTP REST calls. - `credentials` — The connector runtime authenticates to the Orchestration Cluster REST API with basic HTTP authentication. - `oauth` — _(Recommended, and enabled by default)_ The connector runtime authenticates to the Orchestration Cluster REST API with OAuth 2.0. Camunda uses Keycloak as the default OAuth provider. ## Troubleshooting ### Conflicting options If both an environment variable and a configuration file define the same option, the environment variable takes precedence. Example: ```yaml zeebe: env: - name: ZEEBE_BROKER_DATA_BACKUP_S3_BUCKETNAME value: "zeebetest1" configuration: | zeebe: broker: data: backup: s3: bucketName: "zeebeOtherBucketName" ... ``` Here, the bucket name is set twice. The environment variable `zeebetest1` overrides the configuration file `zeebeOtherBucketName`. See [Spring externalized configuration](https://docs.spring.io/spring-boot/docs/1.5.6.RELEASE/reference/html/boot-features-external-config.html) for precedence details. ### Limitations Setting the `configuration` option replaces the entire contents of the application's configuration file. During upgrades, if the `configuration` option remains and there are breaking changes to the configuration file format or required defaults, this can prevent the component from starting. - Use `extraConfiguration` by default so you only maintain a small set of overrides. - The `configuration` option is an advanced override: if the file format or defaults change, the component may fail to start until you update your full configuration. - Forgetting to wrap multiline values with (`|`) in Helm can cause parse errors. - Mixing `env` and `configuration` for the same property without realizing precedence can lead to unexpected results. ## Migrate extraConfiguration from 8.8 to 8.9 In Camunda 8.8 and earlier, `.extraConfiguration` was a **map** where each key was a filename and each value was the file content: ```yaml # 8.8 format (map) identity: extraConfiguration: custom-logging.yaml: | logging: level: ROOT: DEBUG io.camunda.identity: DEBUG custom-cache.yaml: | spring: cache: type: caffeine ``` Starting with Camunda 8.9, `.extraConfiguration` is an **ordered list** of entries, where each entry has a `file` (filename) and `content` (file contents): ```yaml # 8.9 format (ordered list) identity: extraConfiguration: - file: custom-logging.yaml content: | logging: level: ROOT: DEBUG io.camunda.identity: DEBUG - file: custom-cache.yaml content: | spring: cache: type: caffeine ``` ### Why this changed Maps in Go templates (used by Helm) do not guarantee iteration order. Since configuration layering is order-dependent — later entries override earlier ones for the same keys — the map format could produce unpredictable results. The ordered list format ensures entries are always applied in the sequence you define them. ### Migration steps For every component where you use `extraConfiguration`, convert each map entry to a list entry: 1. **Identify all components** in your `values.yaml` that use `extraConfiguration` (for example, `identity`, `orchestration`, `connectors`, `optimize`, `console`, `webModeler`). 2. **Convert each map entry to a list entry.** For every key-value pair in the old map, create a list item with `file:` set to the former key and `content:` set to the former value. **Before (8.8):** ```yaml orchestration: extraConfiguration: backup-s3.yaml: | zeebe: broker: data: backup: store: "S3" s3: bucketName: "my-bucket" custom-threads.yaml: | zeebe: broker: threads: cpuThreadCount: "4" ``` **After (8.9):** ```yaml orchestration: extraConfiguration: - file: backup-s3.yaml content: | zeebe: broker: data: backup: store: "S3" s3: bucketName: "my-bucket" - file: custom-threads.yaml content: | zeebe: broker: threads: cpuThreadCount: "4" ``` 3. **Order entries intentionally.** If two entries set the same configuration key, the **last entry in the list wins**. Arrange entries so that your highest-priority overrides appear last. 4. **Validate your configuration** before upgrading by running `helm template` with your updated values file and verifying the rendered ConfigMaps: ```bash helm template my-release camunda/camunda-platform \ -f values-8.9.yaml \ --show-only templates/orchestration/configmap.yaml ``` 5. **Proceed with the upgrade** using the updated values file. See the [8.8 to 8.9 upgrade guide](/self-managed/upgrade/helm/880-to-890.md) for additional steps. :::caution The old map format is **not supported** in Camunda 8.9. If you upgrade without converting to the list format, Helm will fail during template rendering. ::: ## References For more details on where to find configuration options for specific components, see the following pages: - [Components (Self-Managed)](../../../components/orchestration-cluster/overview.md) - [Zeebe Broker](/self-managed/components/orchestration-cluster/zeebe/configuration/broker.md) - [Zeebe Gateway](/self-managed/components/orchestration-cluster/zeebe/configuration/gateway.md) - [Operate](/self-managed/components/orchestration-cluster/operate/operate-configuration.md) - [Tasklist](/self-managed/components/orchestration-cluster/tasklist/tasklist-configuration.md) - [Web Modeler](/self-managed/components/hub/configuration/modeler-configuration.md) - [Console](/self-managed/components/hub/configuration/configuration.md) - [Connectors](/self-managed/components/connectors/connectors-configuration.md) - [Identity](/self-managed/components/management-identity/miscellaneous/configuration-variables.md) - [Optimize](/self-managed/components/optimize/configuration/system-configuration.md) --- ## Set up the Helm chart with basic authentication By default, Camunda 8 Self-Managed uses Basic authentication for all components deployed through the Helm chart. This method requires no additional configuration and is ideal for local or development environments. :::note Because Basic authentication is enabled by default, components that depend on Management Identity (which implements OIDC/OAuth authentication) are disabled by default. These components include: - Management Identity - Console - Web Modeler - Keycloak - Optimize ::: In this guide, you'll learn how to: - Deploy the Orchestration Cluster and Connectors with Basic authentication. - Add additional components, using a hybrid approach, combining Basic authentication and OIDC. ## Enable the Orchestration Cluster and Connectors The Orchestration Cluster and Connectors are enabled, by default, with Basic authentication. No additional configuration is required—simply deploy the Helm chart, and these components will be available. ### Default users Two users are created by default: | Username | Password | Role | Description | | ----------- | ----------- | ------------ | ----------------------------------------------------------------------------------- | | `demo` | `demo` | `admin` | Initial administrative user | | `connector` | `connector` | `connectors` | Used by the Connectors component to authenticate with the Orchestration Cluster API | For details on configuring initial users and their roles, see [Orchestration Cluster Admin initialization](/self-managed/components/orchestration-cluster/admin/overview.md#option-3-configuration). :::note Helm arrays In Helm, arrays must be overwritten in full. If you change these configuration settings, keep in mind that the default array must be configured in your custom `values.yaml` if you want to keep those users and role assignments. For example, when adding the user `foo` or assigning roles to `foo`, keep also the values for the demo and connectors user. ::: ### Connect to the cluster To access the Orchestration Cluster and Connectors from your local machine using `kubectl port-forward`, refer to [Accessing components without Ingress](../ingress/accessing-components-without-ingress.md). Log in with the default credentials: - **Username:** `demo` - **Password:** `demo` ## Enable additional components The following components require Management Identity with an OIDC provider and, therefore, don't support basic authentication: - Console - Web Modeler - Optimize However, you can still enable these components alongside a Basic authentication Orchestration Cluster by using a hybrid authentication setup: - **Orchestration Cluster and Connectors** use basic authentication - **Console, Web Modeler, Optimize, and Management Identity** use OIDC In this section, you'll learn how to configure hybrid authentication with internal Keycloak. You can also apply this approach with other OIDC setups, such as [external Keycloak](./external-keycloak.md) or an [external OIDC provider](./external-oidc-provider.md) When deploying process models from Web Modeler to a Basic authentication Orchestration Cluster, you'll be prompted to enter credentials in the deployment dialog. ### Configuration steps Follow the [internal Keycloak guide](./internal-keycloak.md) with these modifications: 1. When you [create a secret](./internal-keycloak.md#create-a-secret), omit the following keys. They aren't needed for Basic authentication setups: - `identity-connectors-client-token` - `identity-orchestration-client-token` 2. Set `basic` as the authentication method for the Orchestration Cluster and Connectors: ```yaml orchestration: security: authentication: method: basic connectors: security: authentication: method: basic ``` 3. Skip the OIDC sections for the [Orchestration Cluster](./internal-keycloak.md#configure-orchestration-cluster) and [Connectors](./internal-keycloak.md#configure-connectors). ### Full configuration example This example shows a complete configuration of hybrid authentication with internal Keycloak: ```yaml global: identity: auth: enabled: true optimize: secret: existingSecret: "camunda-credentials" existingSecretKey: "identity-optimize-client-token" identity: enabled: true firstUser: secret: existingSecret: "camunda-credentials" existingSecretKey: "identity-firstuser-password" identityKeycloak: enabled: true auth: existingSecret: "camunda-credentials" passwordSecretKey: "identity-keycloak-admin-password" postgresql: auth: existingSecret: "camunda-credentials" secretKeys: adminPasswordKey: "identity-keycloak-postgresql-admin-password" userPasswordKey: "identity-keycloak-postgresql-user-password" optimize: enabled: true connectors: security: authentication: method: basic webModeler: enabled: true restapi: mail: fromAddress: noreply@example.com webModelerPostgresql: enabled: true auth: existingSecret: "camunda-credentials" secretKeys: adminPasswordKey: "webmodeler-postgresql-admin-password" userPasswordKey: "webmodeler-postgresql-user-password" orchestration: security: authentication: method: basic console: enabled: true ``` ### Connect to the cluster To access the additional components, refer to [Connect to the cluster](./internal-keycloak.md#connect-to-the-cluster) in the internal Keycloak guide. ## Next steps - [Enable centralized OIDC authentication for all components](./internal-keycloak.md). - [Integrate with an external identity provider](./external-oidc-provider.md). --- ## Helm chart user and client setup for Management Identity When connected to Keycloak, the Camunda Helm chart allows you to configure Management Identity users and OAuth2 clients through the `identity` section in your Helm values file. This page explains how to define users and clients with YAML examples and descriptions of common fields. ## Add OAuth2 clients Define custom OAuth2 clients under the `identity.clients` list. Example: ```yaml identity: clients: - id: test name: Test secret: existingSecret: test-secret # Kubernetes secret containing the client secret existingSecretKey: test-secret-key # Key inside the secret redirectUris: /dummy # Redirect URIs for the OAuth2 client rootUrl: http://dummy # Root URL of the client application type: confidential # Client type (confidential, public, m2m) permissions: - resourceServerId: camunda-identity-resource-server definition: read - resourceServerId: camunda-identity-resource-server definition: write - resourceServerId: orchestration-api definition: read:* - resourceServerId: orchestration-api definition: write:* - resourceServerId: optimize-api definition: write:* - resourceServerId: web-modeler-api definition: write:* - resourceServerId: console-api definition: write:* ``` ## Add Identity users Create Management Identity users under the `identity.users` list. Example: ```yaml identity: users: - username: foo secret: existingSecret: foo-secret # Secret containing the user's password existingSecretKey: foo-secret-key firstName: Foo lastName: Bar email: foo.bar@camunda.com roles: - ManagementIdentity # Assign roles to the user - Optimize - Console ``` ## Key points - OAuth2 clients must include at least one redirect URI. - Public clients do not require a client secret. - User roles define permissions across Management Identity components such as Web Modeler, Console, and Optimize. - All referenced Kubernetes secrets must exist before deploying the Helm chart. ## Troubleshooting - If clients or users do not appear after installation, verify that all referenced secrets exist and match the values in your Helm configuration. Missing secrets will prevent creation. - If users or clients encounter permission issues, confirm that their assigned roles align with the expected access rights. ### Note on environment variables If you previously configured users or clients using environment variables, migrate those definitions into `values.yaml` instead. Incorrect array indexing may cause errors in Management Identity such as: ``` Binding to target [Bindable@3e595da3 type = java.util.List, value = 'none', annotations = array[[empty]], bindMethod = [null]] failed: ``` --- ## Set up an external IdP with internal Keycloak This guide explains how to configure the internal Keycloak instance as an identity broker that delegates authentication to an external identity provider (IdP), such as an OIDC provider, SAML, LDAP, or Active Directory. This setup allows you to: - Use your organization's existing identity provider for user authentication - Retain the internal Keycloak for Camunda OIDC integration - Manage user authorization in Camunda identity components ## Prerequisites - A Camunda 8 deployment with internal Keycloak enabled. For setup instructions, see [Configure internal Keycloak for Helm deployments](/self-managed/deployment/helm/configure/authentication-and-authorization/internal-keycloak.md). - Access to your external IdP's configuration (client credentials, endpoints, etc.) ## Configure the external identity provider Complete the following steps: 1. [Add your external IdP to Keycloak](#add-your-external-idp-to-keycloak) 2. [Configure identity provider mappers](#configure-identity-provider-mappers) 3. [Configure Orchestration Cluster Admin](#configure-orchestration-cluster-admin) 4. [Configure Management Identity access](#configure-management-identity-access-optional) (optional) ### Add your external IdP to Keycloak Configure Keycloak to use your external identity provider by following the [Configure an external IdP using Keycloak](/self-managed/components/management-identity/configuration/configure-external-identity-provider.md) guide. ### Configure identity provider mappers After adding the identity provider, configure mappers in the **Camunda realm** (default: `camunda-platform`) to import user attributes and assign users to a group for authorization. :::tip For details on Keycloak identity provider mappers, see the [Keycloak documentation on identity broker mappers](https://www.keycloak.org/docs/latest/server_admin/index.html#_mappers). ::: #### Create attribute mappers Attribute mappers import user profile information from the external IdP into Keycloak user accounts. In Keycloak Admin Console, navigate to **Identity Providers** > select your IdP > **Mappers** tab. Create attribute mappers to import user profile information: | Name | Mapper type | Claim | User attribute | | ----------- | ------------------ | ------------- | -------------- | | `email` | Attribute Importer | `email` | `email` | | `firstName` | Attribute Importer | `given_name` | `firstName` | | `lastName` | Attribute Importer | `family_name` | `lastName` | #### Create username mapper The username mapper determines how Keycloak assigns usernames to federated users based on claims from the external IdP. Create a username mapper: - **Name**: `username` - **Mapper Type**: Username Template Importer - **Template**: `${CLAIM.preferred_username}` #### Create group for external IdP users Navigate to **Groups** > **Create group** and create a group: - **Name**: `external-idp-users` #### Assign users to the group The hardcoded group mapper automatically assigns all users authenticating through this IdP to a specified group. This group membership is then included in the user's access token. Create a mapper to assign federated users to this group: - **Name**: `assign-external-idp-group` - **Mapper Type**: Hardcoded Group - **Group**: `external-idp-users` ### Configure Orchestration Cluster Admin External IdP users can authenticate, but still require authorization to access Camunda components. Log in to **Orchestration Cluster Admin** as an administrator. #### Grant component access Grant access to Orchestration Cluster components for the external IdP users group: 1. Navigate to **Authorizations** > select **Component** > **Create authorization**. 2. Configure the authorization: - **Owner type**: `Group` - **Owner ID**: `external-idp-users` - **Resource ID**: `*` - **Permissions**: `ACCESS` #### Grant additional permissions (optional) Grant additional permissions as needed. For example, to allow users to view processes and complete tasks: | Resource type | Resource ID | Permissions | | ------------------ | ----------- | -------------------------------------------------------------------- | | Process definition | `*` | `READ_PROCESS_DEFINITION`, `READ_PROCESS_INSTANCE`, `READ_USER_TASK` | | User task | `*` | `UPDATE_USER_TASK` | :::info For more details, see [Configure Orchestration Cluster authorizations](/components/concepts/access-control/authorizations.md). ::: ### Configure Management Identity access (optional) For access to Console, Web Modeler, and Optimize, external IdP users need the corresponding realm roles assigned in Keycloak. The recommended approach is to assign users to groups that have these roles. :::note The hardcoded group mappers in this section grant access to all users authenticating through the external IdP. For more granular access control based on groups or attributes from your external IdP, see the [Keycloak documentation on identity provider mappers](https://www.keycloak.org/docs/latest/server_admin/index.html#_mappers). ::: #### Verify or create groups 1. In Keycloak Admin Console, navigate to **Groups**. 2. Verify that groups exist for each component (e.g., `Console`, `Optimize`, `Web Modeler`). If not, create them. #### Assign roles to groups Ensure each group has the corresponding realm role assigned: 1. Select the group > **Role Mappings** tab. 2. Click **Assign role** and add the role with the same name (e.g., `Console`). #### Create group mappers Create mappers to assign federated users to these groups: 1. Navigate to **Identity Providers** > select your IdP > **Mappers** tab. 2. Click **Add mapper** for each component: | Mapper name | Mapper type | Group | | ------------------------- | --------------- | ------------- | | `assign-console-group` | Hardcoded Group | `Console` | | `assign-optimize-group` | Hardcoded Group | `Optimize` | | `assign-webmodeler-group` | Hardcoded Group | `Web Modeler` | :::tip You can also assign roles directly to users in Keycloak, or use [mapping rules in Management Identity](/self-managed/components/management-identity/mapping-rules.md) to map token claims to roles. ::: ## Next steps - To understand the differences between Orchestration Cluster Admin and Management Identity, see [Identity types in Camunda 8](/components/concepts/access-control/access-control-overview.md#identity-types-in-camunda-8). - To learn more about mapping rules, see [Mapping rules](/components/concepts/access-control/mapping-rules.md). - To configure additional authorizations, see [Orchestration Cluster authorization](/components/concepts/access-control/authorizations.md). - To use an external IdP without the internal Keycloak, see [Set up the Helm chart with an external OIDC provider](/self-managed/deployment/helm/configure/authentication-and-authorization/external-oidc-provider.md). --- ## Set up the Helm chart with an external Keycloak instance :::caution Admin access required The external Keycloak setup requires administrative access to the Keycloak server. ::: The Camunda Helm chart can connect to an external Keycloak instance that acts as the identity management service for authentication and authorization. With minimal configuration for administrative access, the Management Identity component can automatically configure the Keycloak realm and required entities on startup—simplifying setup and reducing the learning curve. Use this guide if you already have an existing Keycloak instance and want Camunda to automatically configure the required Keycloak entities. If you prefer Camunda to also create and manage a Keycloak pod, see the [internal Keycloak guide](/self-managed/deployment/helm/configure/authentication-and-authorization/internal-keycloak.md). :::info Before you begin, ensure you’re running a Keycloak version that’s supported by your Camunda release. See [Supported environments](/reference/supported-environments.md#component-requirements). ::: ## Configure Keycloak Before setting up the Camunda Helm chart, prepare your Keycloak instance. For the Keycloak realm, you have two options: - [Use an existing realm](#option-1-prepare-an-existing-realm) - [Let Management Identity create a realm](#option-2-let-management-identity-create-a-realm) ### Option 1: Prepare an existing realm If you choose this option, configure your Keycloak realm following the [Management Identity configuration guide](/self-managed/components/management-identity/configuration/connect-to-an-existing-keycloak.md). Take note of the following values: - Realm name (``) - Client ID for Management Identity (``) - Administrative Keycloak username and password (``, ``) ### Option 2: Let Management Identity create a realm If you choose this option, Management Identity will create a realm named `camunda-platform` on startup. Ensure this realm doesn’t already exist before starting for the first time. Take note of the following values: - Realm name: `camunda-platform` (``) - Client ID generated for setup: `camunda-identity` (``) - Administrative Keycloak username and password (``, ``) ## Configure the Helm chart Next, prepare your Kubernetes cluster and install the Camunda Helm chart. Perform the following steps: 1. [Create a secret](#create-a-secret) 2. [Prepare global configuration](#prepare-global-configuration) 3. [Configure Management Identity](#configure-management-identity) 4. [Configure components using OIDC](#configure-components-using-oidc) You can also [view the full configuration example](#full-configuration-example). ### Create a secret Create a secret containing all required credentials. For example, the following command creates a `camunda-credentials` secret: ```bash kubectl create secret generic camunda-credentials \ --from-literal=identity-keycloak-admin-password= \ --from-literal=identity-firstuser-password=CHANGE_ME \ --from-literal=identity-connectors-client-token=CHANGE_ME \ --from-literal=identity-optimize-client-token=CHANGE_ME \ --from-literal=identity-orchestration-client-token=CHANGE_ME \ --from-literal=webmodeler-postgresql-admin-password=CHANGE_ME \ --from-literal=webmodeler-postgresql-user-password=CHANGE_ME ``` This secret includes the following keys: - `identity-keycloak-admin-password`: Password for an administrative Keycloak account (``). - `identity-firstuser-password`: Password for the initial Camunda user (default username: `demo`). - `identity-connectors-client-token`: Client secret of the Keycloak OIDC client `connectors `used by Connectors. - `identity-optimize-client-token`: Client secret of the Keycloak OIDC client `optimize` used by Optimize. - `identity-orchestration-client-token`: Client secret of the Keycloak OIDC client `orchestration` used by the Orchestration Cluster. - `literal=webmodeler-postgresql-admin-password`: Password for the administrative account of the PostgreSQL instance used by Web Modeler (username `postgres`). - `webmodeler-postgresql-user-password` Password non-privileged user account of the PostgreSQL instance used by Web Modeler (username `web-modeler`). For additional options on how to create and reference Kubernetes secrets (for example using YAML manifests or consolidated secrets), see [External Kubernetes secrets](/self-managed/deployment/helm/configure/secret-management.md#method-2-external-kubernetes-secrets-recommended-for-all-versions). ### Prepare global configuration Start with the following global configuration, which provides shared defaults across the deployment: ```yaml global: identity: auth: enabled: true publicIssuerUrl: /realms/ issuerBackendUrl: /realms/ authUrl: /realms//protocol/openid-connect/auth tokenUrl: /realms//protocol/openid-connect/token jwksUrl: /realms//protocol/openid-connect/certs security: authentication: method: oidc ``` Replace `KEYCLOAK_URL` with your Keycloak base URL (in the format `://:/`). :::info In some setups, Keycloak is accessible through different URLs from within the cluster and from the user’s browser. This can happen, for example, if you deployed Keycloak inside your Kubernetes cluster but didn’t expose it under a domain name that’s accessible both internally and externally. In this case: - Set `global.identity.auth.publicIssuerUrl` and `global.identity.auth.authUrl` to the URL reachable from users' browsers. - Set the remaining values to the URL reachable from within the cluster. ::: ### Configure Management Identity Configure Management Identity to access the realm and use the initial OIDC client. On startup, Management Identity creates the realm (if needed), sets up the OIDC clients, and creates the initial user account. If no clients appear in the Keycloak realm after startup, the configuration was not successful. Management Identity configuration: ```yaml global: identity: keycloak: url: # this URL must be reachable from within the cluster protocol: host: port: contextPath: # set to "/" for the root context path realm: /realms/ auth: adminUser: existingSecret: "camunda-credentials" existingSecretKey: "identity-keycloak-admin-password" auth: identity: clientId: identity: enabled: true firstUser: secret: existingSecret: "camunda-credentials" existingSecretKey: "identity-firstuser-password" env: - name: KEYCLOAK_REALM value: - name: IDENTITY_CLIENTID value: ``` Add the section under `global.identity` to the `global` configuration you created in the previous step. The `identity.firstUser` field defines the initial user that Management Identity creates in Keycloak with full access to all Camunda components. By default, this user is named `demo`. To use a different name, set `identity.firstUser.username`. For additional Keycloak-specific variables you can define under `identity.env`, see [Management Identity environment variables](/self-managed/components/management-identity/miscellaneous/configuration-variables.md). ### Configure components using OIDC To configure Orchestration Cluster and management components with OIDC, follow the steps in the [Configure components using OIDC section of the internal Keycloak setup guide](/self-managed/deployment/helm/configure/authentication-and-authorization/internal-keycloak.md#configure-components-using-oidc). ### Full configuration example The following example shows a complete configuration for connecting to an external Keycloak instance: ```yaml global: identity: auth: enabled: true publicIssuerUrl: /realms/ issuerBackendUrl: /realms/ authUrl: /realms//protocol/openid-connect/auth tokenUrl: /realms//protocol/openid-connect/token jwksUrl: /realms//protocol/openid-connect/certs identity: clientId: optimize: secret: existingSecret: "camunda-credentials" existingSecretKey: "identity-optimize-client-token" keycloak: url: # this URL must be reachable from within the cluster protocol: host: port: contextPath: # set to "/" for the root context path realm: /realms/ auth: adminUser: existingSecret: "camunda-credentials" existingSecretKey: "identity-keycloak-admin-password" security: authentication: method: oidc identity: enabled: true firstUser: secret: existingSecret: "camunda-credentials" existingSecretKey: "identity-firstuser-password" env: - name: KEYCLOAK_REALM value: - name: IDENTITY_CLIENTID value: optimize: enabled: true connectors: security: authentication: oidc: secret: existingSecret: "camunda-credentials" existingSecretKey: "identity-connectors-client-token" webModeler: enabled: true restapi: mail: fromAddress: noreply@example.com webModelerPostgresql: enabled: true auth: existingSecret: "camunda-credentials" secretKeys: adminPasswordKey: "webmodeler-postgresql-admin-password" userPasswordKey: "webmodeler-postgresql-user-password" orchestration: security: authentication: oidc: secret: existingSecret: "camunda-credentials" existingSecretKey: "identity-orchestration-client-token" console: enabled: true ``` To review how each component is configured and which OIDC clients are used: - Run `kubectl get pods` and `kubectl get configmap`, then use `kubectl describe` to inspect component configurations. - Log into Keycloak (using your administrative user) to review the OIDC client setup ## Connect to the cluster After applying this configuration, use the following `kubectl port-forward` commands to access the APIs and UIs from your localhost: ```bash # Management Identity kubectl port-forward svc/camunda-identity 8084:80 # Orchestration Cluster kubectl port-forward svc/camunda-zeebe-gateway 8080:8080 kubectl port-forward svc/camunda-zeebe-gateway 26500:26500 # Connectors kubectl port-forward svc/camunda-connectors 8086:8080 # Optimize kubectl port-forward svc/camunda-optimize 8083:80 # Web Modeler kubectl port-forward svc/camunda-web-modeler-restapi 8070:80 kubectl port-forward svc/camunda-web-modeler-websockets 8085:80 # Console kubectl port-forward svc/camunda-console 8087:80 ``` After port forwarding, access the UIs through `http://localhost:`. For example: - Orchestration Cluster UI: `http://localhost:8080` - Management Identity: `http://localhost:8084` Log in with the username `demo` and the password stored in the secret key `identity-firstuser-password`. --- ## Set up the Helm chart with an external OIDC provider Instead of using internal Keycloak, you can configure Camunda to connect to an external OpenID Connect (OIDC) identity provider, such as an external Keycloak, Microsoft Entra ID, or Okta. The Helm chart offers these options: --- ## Connect Camunda to any OIDC provider This guide shows you how to configure Camunda 8 Self-Managed to authenticate with any OpenID connect (OIDC)-compliant identity provider. :::info Before proceeding, since this is a general guide, refer to [External OIDC provider](./external-oidc-provider.md) to see the available provider-specific guides, as they include detailed setup instructions tailored to provider's interface. ::: ## Prerequisites Before you begin, ensure you have: - An OIDC-compliant provider already deployed and accessible. - Administrative access to create and configure OIDC clients in your provider. - Access to your provider's discovery document to obtain endpoint URLs. - A Kubernetes cluster with the Helm CLI v4 installed. - kubectl configured to access your cluster. :::note This guide assumes your OIDC provider is already operational. It does not cover provider installation or basic OIDC configuration. ::: ## Create OIDC clients Create the following OIDC clients in your provider. The exact process varies by provider; consult your provider's documentation for client creation procedures. | Client name | Type | Purpose | | --------------------- | ------------ | ------------------------------------------------ | | Management Identity | Confidential | User login and API authentication | | Orchestration Cluster | Confidential | User login and machine-to-machine authentication | | Optimize | Confidential | User login | | Web Modeler API | Confidential | Programmatic API access | | Web Modeler UI | Public | User login | | Console | Public | User login | :::tip For each client, record: - Client ID - Client secret (for confidential clients only) ::: ## Configure redirect URIs For each OIDC client you have created, configure the redirect URIs that correspond to where Camunda components will be accessible from users' browsers. ### Redirect URI table | Component | Redirect URI pattern | Example (localhost) | Example (Ingress) | | --------------------- | -------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------------------ | | Management Identity | `/auth/login-callback` | `http://localhost:8084/auth/login-callback` | `https://camunda.example.com/identity/auth/login-callback` | | Orchestration Cluster | `/sso-callback` | `http://localhost:8080/sso-callback` | `https://camunda.example.com/orchestration/sso-callback` | | Optimize | `/api/authentication/callback` | `http://localhost:8083/api/authentication/callback` | `https://camunda.example.com/optimize/api/authentication/callback` | | Web Modeler UI | `/login-callback` | `http://localhost:8070/login-callback` | `https://camunda.example.com/modeler/login-callback` | | Console | `/` | `http://localhost:8087/` | `https://camunda.example.com/` | Replace `<*_URL>` with the actual base URL where each component will be accessible. Use the localhost examples if testing locally with port forwarding, or the Ingress examples if exposing components via Ingress. :::important Security note Redirect URIs are security-critical. Only the URIs you configure in your OIDC provider are permitted as redirection targets after authentication. Ensure these values match the `redirectUrl` parameters you'll set in the Helm configuration. ::: :::tip Wildcard support Some OIDC providers support wildcard redirect URIs (e.g., `https://camunda.example.com/*`). Check your provider's documentation to see if this can simplify your configuration. ::: ## Discover provider configuration Since OIDC providers vary in their implementation details, you need to obtain the specific values from your provider. ### Find OIDC endpoints Most OIDC providers expose a discovery document at: ``` https://your-provider.example.com/.well-known/openid-configuration ``` Access this URL (replacing `your-provider.example.com` with your provider's domain) to retrieve a JSON document containing endpoint URLs. #### Example discovery document ```json { "issuer": "https://your-provider.example.com", "authorization_endpoint": "https://your-provider.example.com/oauth/authorize", "token_endpoint": "https://your-provider.example.com/oauth/token", "jwks_uri": "https://your-provider.example.com/.well-known/jwks.json", ... } ``` #### Record these values for Helm configuration - `issuer` → Used for: `publicIssuerUrl` - `authorization_endpoint` → Used for: `authUrl` - `token_endpoint` → Used for: `tokenUrl` - `jwks_uri` → Used for: `jwksUrl` ### Identify token claims Camunda needs to know which claims in access tokens identify users and clients. Claim names vary by provider. You need to identify: - **User identification claim** (`usernameClaim`): Identifies users during web login (for example, `email`, `preferred_username`). - **Client identification claim** (`clientIdClaim`): Identifies calling applications for M2M authentication (for example, `client_id`, `azp`). - **Audience claim** (`audience`): The expected `aud` value in tokens. For detailed instructions on obtaining and decoding tokens to identify these claims, see [JWT token claims reference](./jwt-token-claims.md). ### Scopes requested by Camunda Camunda components request OIDC scopes when authenticating users. The default scopes vary by component: | Scope | Description | Management Identity, Optimize, Web Modeler, Console | Orchestration Cluster applications (Identity, Operate, Tasklist) | | ---------------- | ----------------------------------- | --------------------------------------------------- | ---------------------------------------------------------------- | | `openid` | Required for OIDC authentication. | ✔ | ✔ | | `profile` | Access to user profile information. | ✔ | ✔ | | `email` | Access to user email address. | ✔ | | | `offline_access` | Enables refresh token issuance. | ✔ | | :::info If your provider supports the `offline_access` scope, components will receive refresh tokens. This allows sessions to remain active longer without requiring users to re-authenticate. If `offline_access` is not available or not granted, users will be redirected to your OIDC provider for re-authentication when their access token expires. For more information, see [OpenID Connect Core specification](https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess). ::: ## Create secrets Create two secrets in your Kubernetes namespace. First, create a secret that contains all OIDC client secrets: ```bash kubectl create secret generic oidc-credentials \ --from-literal=identity-client-secret="" \ --from-literal=orchestration-client-secret="" \ --from-literal=optimize-client-secret="" \ --from-literal=webmodeler-api-client-secret="" ``` :::info The secret key `webmodeler-api-client-secret` is not used elsewhere in this guide. This client is intended for your own use if you want to access the [Web Modeler API](/apis-tools/web-modeler-api/authentication.md) programmatically. ::: Next, create a secret with the remaining credentials for the Camunda Helm chart: ```bash kubectl create secret generic camunda-credentials \ --from-literal=identity-postgresql-admin-password=CHANGE_ME \ --from-literal=identity-postgresql-user-password=CHANGE_ME \ --from-literal=webmodeler-postgresql-admin-password=CHANGE_ME \ --from-literal=webmodeler-postgresql-user-password=CHANGE_ME ``` Unlike the OIDC client secrets, these passwords initialize the component databases. You can choose any values. :::tip Alternative secret management For production deployments, consider using external secret management solutions. See [External Kubernetes secrets](/self-managed/deployment/helm/configure/secret-management.md#method-2-external-kubernetes-secrets-recommended-for-all-versions) for more options. ::: ## Configure Camunda components Configure Camunda components to use your OIDC provider through Helm values. ### Global OIDC configuration Start with the global configuration that applies to all components: ```yaml global: security: authentication: method: oidc identity: auth: enabled: true type: "GENERIC" publicIssuerUrl: issuerBackendUrl: authUrl: tokenUrl: jwksUrl: ``` #### Parameter descriptions | Parameter | Description | Example | | ------------------ | --------------------------------------------------------- | ----------------------------------------------------------------------- | | `publicIssuerUrl` | Issuer URL accessible from users' browsers | `https://login.example.com` | | `issuerBackendUrl` | Issuer URL accessible from Kubernetes pods | `https://login.example.com` or `http://oidc-internal.svc.cluster.local` | | `authUrl` | Authorization endpoint (must be accessible from browsers) | `https://login.example.com/oauth/authorize` | | `tokenUrl` | Token endpoint (must be accessible from pods) | `https://login.example.com/oauth/token` | | `jwksUrl` | JWKS endpoint for token signature verification | `https://login.example.com/.well-known/jwks.json` | :::warning Network accessibility For generic OIDC providers, the **Issuer URL** must be accessible from both: 1. **Users' browsers**: To redirect users to the login page. 2. **Camunda components (backend)**: To fetch the provider's configuration and validate tokens. Split-horizon DNS setups (where the provider has different URLs for internal and external access) are **not supported** for generic OIDC providers. Ensure your OIDC provider is exposed via a URL that is resolvable and reachable from both locations. ::: ### Configure Management Identity Add configuration for Management Identity: ```yaml global: identity: auth: identity: clientId: audience: secret: existingSecret: oidc-credentials existingSecretKey: identity-client-secret initialClaimName: initialClaimValue: identity: fullURL: enabled: true identityPostgresql: enabled: true auth: existingSecret: camunda-credentials secretKeys: adminPasswordKey: identity-postgresql-admin-password userPasswordKey: identity-postgresql-user-password ``` #### Identity-specific parameters | Parameter | Description | How to Determine | | ------------------- | -------------------------------------------- | ----------------------------------------------------------------------------------------------- | | `clientId` | Client ID from your OIDC provider | From your Identity client configuration | | `audience` | Expected audience in access tokens | From token inspection (see [Discover provider configuration](#discover-provider-configuration)) | | `initialClaimName` | Claim that identifies the initial admin user | `email`, `sub`, or another user claim from token inspection | | `initialClaimValue` | Value granting initial admin access | Your admin user's value for the specified claim (e.g., `admin@example.com`) | :::warning Initial claim cannot be changed The `initialClaimName` and `initialClaimValue` parameters are used only during the first startup to grant initial admin access. Once Management Identity has started, these values are stored in the database and cannot be changed via Helm values. ::: ### Configure Orchestration Cluster Add configuration for the Orchestration Cluster (Zeebe, Operate, Tasklist, Identity): ```yaml orchestration: enabled: true security: authentication: method: oidc oidc: clientId: audience: redirectUrl: secret: existingSecret: oidc-credentials existingSecretKey: orchestration-client-secret # Claim mapping - uncomment and adjust if your provider doesn't use defaults # usernameClaim: # Default: preferred_username # clientIdClaim: # Default: client_id authorizations: enabled: true initialization: defaultRoles: admin: users: - connectors: clients: - ``` #### Orchestration-specific parameters | Parameter | Description | Value | | --------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------- | | `clientId` | Orchestration client ID | From your provider | | `audience` | Expected audience in tokens | From token inspection | | `redirectUrl` | Full URL for Orchestration Cluster | `http://localhost:8080` (local) or `https://your-domain.com/orchestration` (Ingress) | | `usernameClaim` | Claim identifying users | Default: `preferred_username`. Override if your provider uses `email`, `sub`, or another claim | | `clientIdClaim` | Claim identifying clients | Default: `client_id`. Override if your provider uses `azp` or another claim | :::note Username display in Web Modeler (Helm) In Helm deployments, the default OIDC username claim is `preferred_username`, which often maps to an email address. If you want Web Modeler to display usernames based on a different claim (for example `name`), set `CAMUNDA_MODELER_OAUTH2_TOKEN_USERNAMECLAIM=name` for the Web Modeler `restapi` environment. For available Web Modeler environment variables, see [Identity/Keycloak configuration](/self-managed/components/hub/configuration/modeler-configuration.md#identity--keycloak-1). ::: #### Default roles - `admin.users`: List of user claim values that should have admin access. - `connectors.clients`: List of client IDs that should have Connectors role (typically the orchestration client ID itself). :::note The admin user specified in `defaultRoles.admin.users` should match the value used for `initialClaimValue` in Management Identity configuration, so that the same user has admin access to both Management Identity and the Orchestration Cluster. ::: ### Configure Connectors Add configuration for Connectors: ```yaml connectors: enabled: true security: authentication: method: oidc oidc: clientId: audience: secret: existingSecret: oidc-credentials existingSecretKey: orchestration-client-secret ``` :::info Connectors shares credentials Connectors typically uses the same OIDC client as the Orchestration Cluster. This allows the Orchestration Cluster to accept the Connectors client's audience by default, since they share the same client configuration. If you prefer to use a separate OIDC client for Connectors, you'll need to configure the Orchestration Cluster to accept that client's audience. ::: ### Configure Optimize Add configuration for Optimize: ```yaml global: identity: auth: optimize: clientId: audience: redirectUrl: secret: existingSecret: oidc-credentials existingSecretKey: optimize-client-secret optimize: enabled: true ``` #### Optimize parameters | Parameter | Value | | ------------- | ------------------------------------------------------------------------------- | | `clientId` | Optimize client ID from your provider | | `audience` | Expected audience (from token inspection) | | `redirectUrl` | `http://localhost:8083` (local) or `https://your-domain.com/optimize` (Ingress) | ### Configure Web Modeler Web Modeler requires two OIDC clients: one for the UI (public) and one for the API (confidential). :::note If your IdP provides user-friendly names in the `name` claim, and you want Web Modeler to use that claim, configure the Web Modeler `restapi` environment variable `CAMUNDA_MODELER_OAUTH2_TOKEN_USERNAMECLAIM=name`. Without this override, Helm defaults typically resolve usernames from `preferred_username`. ::: ```yaml global: identity: auth: webModeler: clientId: redirectUrl: clientApiAudience: publicApiAudience: webModeler: enabled: true restapi: mail: fromAddress: noreply@example.com # Update with your email address # Additional SMTP configuration may be required - see Web Modeler docs webModelerPostgresql: enabled: true auth: existingSecret: camunda-credentials secretKeys: adminPasswordKey: webmodeler-postgresql-admin-password userPasswordKey: webmodeler-postgresql-user-password ``` #### Web Modeler parameters | Parameter | Description | Value | | ------------------- | ---------------------------------------- | ------------------------------------------------------------------------------ | | `clientId` | Web Modeler UI client ID (public client) | From your provider | | `redirectUrl` | Full URL for Web Modeler | `http://localhost:8070` (local) or `https://your-domain.com/modeler` (Ingress) | | `clientApiAudience` | Audience for UI-to-API communication | Usually the UI client ID | | `publicApiAudience` | Audience for external API access | The API client ID or custom audience | #### Email configuration Web Modeler requires email configuration for notifications. Update `restapi.mail.fromAddress` with an appropriate sender address. For full SMTP configuration, see [Web Modeler configuration](/self-managed/components/hub/configuration/modeler-configuration.md). ### Configure Console Add configuration for Console: ```yaml global: identity: auth: console: clientId: audience: redirectUrl: console: enabled: true ``` Replace `` with the base URL where Console will be accessible. For local deployment, use `http://localhost:8087`. ## Complete configuration example Below is a complete Helm values file with all components configured: ```yaml global: security: authentication: method: oidc identity: auth: enabled: true type: "GENERIC" # OIDC Provider Endpoints publicIssuerUrl: issuerBackendUrl: authUrl: tokenUrl: jwksUrl: # Management Identity identity: clientId: audience: secret: existingSecret: oidc-credentials existingSecretKey: identity-client-secret initialClaimName: initialClaimValue: # Optimize optimize: clientId: audience: redirectUrl: secret: existingSecret: oidc-credentials existingSecretKey: optimize-client-secret # Web Modeler webModeler: clientId: redirectUrl: clientApiAudience: publicApiAudience: # Console console: clientId: audience: redirectUrl: # Orchestration Cluster orchestration: enabled: true security: authentication: method: oidc oidc: clientId: audience: redirectUrl: secret: existingSecret: oidc-credentials existingSecretKey: orchestration-client-secret # The following claim mappings use Camunda defaults and usually don't need to be changed. # Only uncomment if your decoded access token uses different claim names: # usernameClaim: email # Use if tokens identify users with 'email' instead of 'preferred_username' # clientIdClaim: azp # Use if tokens identify clients with 'azp' instead of 'client_id' authorizations: enabled: true initialization: defaultRoles: admin: users: - connectors: clients: - # Connectors connectors: enabled: true security: authentication: method: oidc oidc: clientId: audience: secret: existingSecret: oidc-credentials existingSecretKey: orchestration-client-secret # Management Identity identity: fullURL: enabled: true identityPostgresql: enabled: true auth: existingSecret: camunda-credentials secretKeys: adminPasswordKey: identity-postgresql-admin-password userPasswordKey: identity-postgresql-user-password # Disable internal Keycloak identityKeycloak: enabled: false # Optimize optimize: enabled: true # Web Modeler webModeler: enabled: true restapi: mail: fromAddress: webModelerPostgresql: enabled: true auth: existingSecret: camunda-credentials secretKeys: adminPasswordKey: webmodeler-postgresql-admin-password userPasswordKey: webmodeler-postgresql-user-password # Console console: enabled: true ``` **Placeholders to replace:** | Placeholder | Replace with | | --------------------------------------------------------- | -------------------------------------------- | | `https://your-provider.example.com` | Your OIDC provider's issuer URL | | `identity`, `orchestration`, `optimize`, etc. | Your actual client IDs | | `identity`, `orchestration`, `optimize` (audience values) | Actual audience values from token inspection | | `admin@example.com` | Your admin user's claim value | ### Verify before deploying - All `` replaced with actual values. - All client secrets stored in the `oidc-credentials` secret. - Database passwords stored in the `camunda-credentials` secret. - Redirect URIs in OIDC provider match `redirectUrl` values. - Token inspection confirms audience values. - Verify tokens contain `preferred_username` and `client_id` claims, or uncomment and configure alternative claim names. ## Connect to the cluster After deploying Camunda with this configuration, use the following `kubectl port-forward` commands to access the APIs and UIs: ```bash # Management Identity kubectl port-forward svc/camunda-identity 8084:80 # Orchestration Cluster (Operate/Tasklist) kubectl port-forward svc/camunda-zeebe-gateway 8080:8080 # Zeebe Gateway (gRPC for clients) kubectl port-forward svc/camunda-zeebe-gateway 26500:26500 # Optimize kubectl port-forward svc/camunda-optimize 8083:80 # Web Modeler kubectl port-forward svc/camunda-web-modeler-restapi 8070:80 kubectl port-forward svc/camunda-web-modeler-websockets 8085:80 # Console kubectl port-forward svc/camunda-console 8087:80 ``` Once port forwarding is active, access each component through `http://localhost:`. For example, Management Identity at `http://localhost:8084` or the Orchestration Cluster at `http://localhost:8080` (which redirects to your OIDC provider for login). :::important Redirect URI configuration Ensure your redirect URIs in your OIDC provider match how you're accessing Camunda. If you configured redirect URIs for localhost testing (e.g., `http://localhost:8080/sso-callback`), the port-forward commands above will work. If you configured redirect URIs for Ingress (e.g., `https://camunda.example.com/orchestration/sso-callback`), you'll need to access via Ingress instead. For production deployments, configure Ingress to expose components. See [Ingress configuration](/self-managed/deployment/helm/configure/ingress/index.md) for more details. ::: ## Grant access to components After deployment, you must configure access for the following components. To grant a user access to the Web Modeler UI: - [Create a mapping rule in Management Identity](/self-managed/components/management-identity/mapping-rules.md#add-a-mapping-rule) for the `Web Modeler` role that matches the user's access token. To grant a client access to the Web Modeler API: - [Create a role in Management Identity](/self-managed/components/management-identity/application-user-group-role-management/manage-roles.md#add-a-role) for the Web Modeler API. - [Assign Web Modeler API permissions](/self-managed/components/management-identity/access-management/manage-permissions.md#manage-role-permissions) to that role in Management Identity. - [Create a mapping rule in Management Identity](/self-managed/components/management-identity/mapping-rules.md#add-a-mapping-rule) for that role that matches the client's access token. To grant a user access to Optimize: - [Create a mapping rule in Management Identity](/self-managed/components/management-identity/mapping-rules.md#add-a-mapping-rule) for the `Optimize` role that matches the user's access token. :::info When using an OIDC provider, the following Optimize features are not currently available: - The **User permissions** tab in collections. - The **Alerts** tab in collections. - Digests. - Accessible user names for resource owners (the value of the `sub` claim is displayed instead). ::: --- ## Helm chart authentication and authorization configuration Camunda 8 Self-Managed supports multiple authentication methods for securing access to components deployed with the Helm chart. This section provides an overview of available authentication options and links to configuration guides for each method. ## Overview By default, Camunda uses Basic authentication with predefined demo users. Alternatively, you can configure OpenID Connect (OIDC) authentication, either through an internal Keycloak instance deployed with Camunda or an external OIDC provider. ### Authentication options | Method | Description | Recommended for | | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | | [Basic authentication](./basic-authentication.md) | Authentication using preconfigured demo users. No external identity provider (IdP) required. | Local development and testing, as well as smaller-scale production setups. | | [Internal Keycloak](./internal-keycloak.md) | Deploys an internal Keycloak instance with the Helm release, preconfigured by Management Identity. | Small teams or self-contained environments. | | [External IdP via Internal Keycloak](./external-idp-via-internal-keycloak.md) | Uses the internal Keycloak as an identity broker, delegating authentication to an external identity provider (IdP). | Organizations with existing identity infrastructure that want to retain Keycloak features. | | [External OIDC provider](./external-oidc-provider.md) | Integrates Camunda with an external identity provider, such as Microsoft Entra ID or Okta, via OpenID Connect. | Organizations with an existing enterprise identity infrastructure. | :::note When running Camunda in **no secondary storage** mode, authentication requires special configuration. See [Authentication with no secondary storage](/self-managed/concepts/secondary-storage/no-secondary-storage.md#authentication) for details. ::: ### Limitations of OIDC setups Due to technical limitations regarding [third party content](https://openid.net/specs/openid-connect-frontchannel-1_0.html#ThirdPartyContent), front channel single sign out is not supported. This means that when a user logs out of one component, they will not be logged out of the OIDC provider or the other components. ## References - [Basic authentication guide](./basic-authentication.md) - [Internal Keycloak guide](./internal-keycloak.md) - [External IdP via Internal Keycloak guide](./external-idp-via-internal-keycloak.md) - [External Keycloak guide](./external-keycloak.md) - [Microsoft Entra guide](./microsoft-entra.md) - [Generic OIDC provider](./generic-oidc-provider.md) - Management Identity: [Configure an external IdP using Keycloak](/self-managed/components/management-identity/configuration/configure-external-identity-provider.md) --- ## Set up the Helm chart with the internal Keycloak instance The Camunda Helm chart can deploy an internal Keycloak instance that acts as the identity management service for authentication and authorization. The Management Identity component configures the internal Keycloak automatically on startup with a realm and several entities to simplify setup and reduce the learning curve. Enable internal Keycloak if you don’t have an external identity provider (IdP) and want to use additional Camunda components (Console, Web Modeler, Optimize, Management Identity) that are disabled by default in the Helm chart. If you prefer to run Keycloak externally and disable the internal one, see [Set up the Helm chart with an external Keycloak instance](/self-managed/deployment/helm/configure/authentication-and-authorization/external-keycloak.md). ## Configuration This guide shows you how to: - Configure the Helm chart to deploy an internal Keycloak instance. - Configure the Helm chart with a custom secret for accounts used across all components. - Enable the application components you want to include in the release. - Access all components from your local environment. To use an internal Keycloak instance, complete the following steps: 1. [Create a secret](#create-a-secret) 1. [Enable internal Keycloak](#enable-internal-keycloak) 1. [Configure Management Identity](#configure-management-identity) 1. [Configure components using OIDC](#configure-components-using-oidc) See the [full configuration example](#full-configuration-example) for the complete setup. ### Create a secret Create a secret that contains all required credentials. For example, the following command creates a `camunda-credentials` secret: ```bash kubectl create secret generic camunda-credentials \ --from-literal=identity-keycloak-postgresql-admin-password=CHANGE_ME \ --from-literal=identity-keycloak-postgresql-user-password=CHANGE_ME \ --from-literal=identity-keycloak-admin-password=CHANGE_ME \ --from-literal=identity-firstuser-password=CHANGE_ME \ --from-literal=identity-connectors-client-token=CHANGE_ME \ --from-literal=identity-optimize-client-token=CHANGE_ME \ --from-literal=identity-orchestration-client-token=CHANGE_ME \ --from-literal=webmodeler-postgresql-admin-password=CHANGE_ME \ --from-literal=webmodeler-postgresql-user-password=CHANGE_ME ``` This secret includes the following keys: - `identity-keycloak-postgresql-admin-password`: Password for the administrative account of the PostgreSQL instance used by Management Identity (`postgres`). - `identity-keycloak-postgresql-user-password`: Password for the non-privileged PostgreSQL account used by Management Identity (`bn_keycloak`). - `identity-keycloak-admin-password`: Password for the adminstrative account for the internal Keycloak instance (`admin`). - `identity-firstuser-password`: Password for the initial user account in Keycloak (default username `demo`), used to log in to the Camunda web apps. - `identity-connectors-client-token`: Client secret for the Keycloak OIDC client `connectors `used by Connectors. - `identity-optimize-client-token`: Client secret for the Keycloak OIDC client `optimize` used by Optimize. - `identity-orchestration-client-token`: Client secret for the Keycloak OIDC client `orchestration` used by the Orchestration Cluster. - `webmodeler-postgresql-admin-password`: Password for the administrative account of the PostgreSQL instance used by Web Modeler (`postgres`). - `webmodeler-postgresql-user-password` Password for the non-privileged PostgreSQL account used by Web Modeler (`web-modeler`). ### Enable internal Keycloak Enable the Keycloak subchart and configure it to use the secret: ```yaml identityKeycloak: enabled: true auth: existingSecret: "camunda-credentials" passwordSecretKey: "identity-keycloak-admin-password" postgresql: auth: existingSecret: "camunda-credentials" secretKeys: adminPasswordKey: "identity-keycloak-postgresql-admin-password" userPasswordKey: "identity-keycloak-postgresql-user-password" ``` ### Configure Management Identity and global defaults Management Identity configures the internal Keycloak instance during startup. For example, it creates the Camunda realm and other required Keycloak entities. If the Camunda realm doesn’t appear in Keycloak after deployment, the setup process did not complete successfully. Management Identity configuration: ```yaml global: identity: auth: enabled: true security: authentication: method: oidc identity: enabled: true firstUser: secret: existingSecret: "camunda-credentials" existingSecretKey: "identity-firstuser-password" ``` `identity.firstUser` defines the first user created in Keycloak. By default, this user is named `demo`. You can override this by setting `identity.firstUser.username`. ### Configure components using OIDC Once Management Identity is configured, you can set up OAuth and OIDC for the remaining components. You can skip components you don’t plan to run. By default, the Orchestration Cluster and Connectors are enabled and must be explicitly disabled if not required. #### Configure Orchestration Cluster The Orchestration cluster treats internal Keycloak as any other external IdP and connects through OIDC. Orchestration Cluster configuration: ```yaml orchestration: security: authentication: oidc: secret: existingSecret: "camunda-credentials" existingSecretKey: "identity-orchestration-client-token" ``` #### Configure Connectors Connectors must be configured in a similar fashion with OIDC client secret to access the Orchestration Cluster APIs. Connectors component configuration: ```yaml connectors: security: authentication: oidc: secret: existingSecret: "camunda-credentials" existingSecretKey: "identity-connectors-client-token" ``` #### Configure Optimize Optimize component configuration: ```yaml global: identity: auth: optimize: secret: existingSecret: "camunda-credentials" existingSecretKey: "identity-optimize-client-token" optimize: enabled: true ``` Add the section under `global.identity.auth` to the existing section you created when configuring Management Identity. #### Configure Web Modeler Web Modeler configures a second PostgreSQL instance and requires a redirect URL for OIDC authentication. Web Modeler component configuration: ```yaml global: identity: auth: webModeler: redirectUrl: "http://localhost:8070" # Change this when using a domain webModeler: enabled: true restapi: mail: fromAddress: noreply@example.com webModelerPostgresql: enabled: true auth: existingSecret: "camunda-credentials" secretKeys: adminPasswordKey: "webmodeler-postgresql-admin-password" userPasswordKey: "webmodeler-postgresql-user-password" ``` :::important Redirect URL configuration The `redirectUrl` parameter is **required** for Web Modeler authentication. The default value is `http://localhost:8070`, which works for local port-forwarding setups. If you're using a domain or Ingress to expose Web Modeler, you **must** update this value to match your Web Modeler URL: - With Ingress: `https://your-domain.com/modeler` (if using a context path) - Without context path: `https://modeler.your-domain.com` Mismatched redirect URLs will cause authentication failures that are difficult to debug. ::: You can update `webModeler.restapi.mail.fromAddress` with an address suitable for your environment. This address appears as the sender in emails sent by Web Modeler. For more details on configuring email delivery, see the [Web Modeler section in Enable additional Camunda components](../enable-additional-components.md#web-modeler). #### Configure Console Console component configuration: ```yaml console: enabled: true ``` Since Console is a public client, it does not need to be defined under `global.identity.auth`. ### Full configuration example The following example shows a complete configuration for connecting to internal Keycloak: ```yaml global: identity: auth: enabled: true optimize: secret: existingSecret: "camunda-credentials" existingSecretKey: "identity-optimize-client-token" webModeler: # Default: http://localhost:8070 # Update this when using a domain/Ingress, e.g., https://your-domain.com/modeler redirectUrl: "http://localhost:8070" security: authentication: method: oidc identity: enabled: true firstUser: secret: existingSecret: "camunda-credentials" existingSecretKey: "identity-firstuser-password" identityKeycloak: enabled: true auth: existingSecret: "camunda-credentials" passwordSecretKey: "identity-keycloak-admin-password" postgresql: auth: existingSecret: "camunda-credentials" secretKeys: adminPasswordKey: "identity-keycloak-postgresql-admin-password" userPasswordKey: "identity-keycloak-postgresql-user-password" optimize: enabled: true connectors: security: authentication: oidc: secret: existingSecret: "camunda-credentials" existingSecretKey: "identity-connectors-client-token" webModeler: enabled: true restapi: mail: fromAddress: noreply@example.com webModelerPostgresql: enabled: true auth: existingSecret: "camunda-credentials" secretKeys: adminPasswordKey: "webmodeler-postgresql-admin-password" userPasswordKey: "webmodeler-postgresql-user-password" orchestration: security: authentication: oidc: secret: existingSecret: "camunda-credentials" existingSecretKey: "identity-orchestration-client-token" console: enabled: true ``` In this setup, the Camunda Helm chart handles most of the Keycloak configuration automatically, including creating OIDC or OAuth clients and linking components. Your values file primarily enables components and defines client secrets. To review how each component is configured and which OIDC clients are used: - Run `kubectl get pods` and `kubectl get configmap`, then use `kubectl describe` to inspect component configurations. - Log into Keycloak (using your administrative user) to review the OIDC client setup ## Connect to the cluster ### Local access with port forwarding After applying this configuration, use the following `kubectl port-forward` commands to access the APIs and UIs from your localhost. If you use [Keycloak deployed via the Keycloak Operator](/self-managed/deployment/helm/configure/operator-based-infrastructure.md), also port-forward the Keycloak service: ```bash # Keycloak Operator service (only if using Keycloak) kubectl port-forward svc/keycloak-service 18080:18080 # Management Identity kubectl port-forward svc/camunda-identity 8084:80 # Orchestration Cluster kubectl port-forward svc/camunda-zeebe-gateway 8080:8080 kubectl port-forward svc/camunda-zeebe-gateway 26500:26500 # Connectors kubectl port-forward svc/camunda-connectors 8086:8080 # Optimize kubectl port-forward svc/camunda-optimize 8083:80 # Web Modeler kubectl port-forward svc/camunda-web-modeler-restapi 8070:80 kubectl port-forward svc/camunda-web-modeler-websockets 8085:80 # Console kubectl port-forward svc/camunda-console 8087:80 ``` Once port forwarding is active, access each component through `http://localhost:`. For example: - Keycloak: `http://localhost:18080` - Web Modeler: `http://localhost:8070` - Orchestration Cluster: `http://localhost:8080` Log in with username `demo` and the password you defined under `identity-firstuser-password`. :::note Default URLs and port forwarding The configuration shown above uses default `redirectUrl` values that match the port-forwarding setup (`http://localhost:8070` for Web Modeler, `http://localhost:18080` for Keycloak). These defaults work automatically when using `kubectl port-forward`. If you don't use port forwarding and instead expose components via Ingress or a domain, you **must** update the `redirectUrl` parameters under `global.identity.auth` to match your actual URLs. See [Ingress setup](/self-managed/deployment/helm/configure/ingress/ingress-setup.md) for domain-based configuration examples. ::: ### Domain-based access with Ingress If you're using Ingress to expose components via a domain (instead of port forwarding), update the redirect URLs in your Helm values: ```yaml global: identity: auth: webModeler: redirectUrl: "https://your-domain.com/modeler" # Or https://modeler.your-domain.com # Update other component URLs as needed ``` For complete Ingress configuration, see [Ingress setup](/self-managed/deployment/helm/configure/ingress/ingress-setup.md). ## External identity provider Instead of using an internal Keycloak instance, you can configure Camunda to connect to an external IdP, such as an external Keycloak, Microsoft Entra ID, or Okta. See [Set up the Helm chart with an external OIDC provider](/self-managed/deployment/helm/configure/authentication-and-authorization/external-oidc-provider.md) for details. --- ## JWT token claims reference Use this reference to understand the structure of JWT access tokens and identify the claims your OIDC provider uses. This information is required when configuring Camunda 8 to authenticate with an external identity provider. ## Obtain a test token Request an access token using your OIDC client credentials: ```bash curl -X POST 'https://your-provider.example.com/oauth/token' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'client_id=' \ -d 'client_secret=' \ -d 'grant_type=client_credentials' ``` The response includes an `access_token` field containing the JWT. ## Decode the token Decode the JWT to inspect its claims. ### Command line (Linux/macOS) ```bash echo "" | cut -d'.' -f2 | base64 -d | jq ``` ### Online tools You can also use online tools such as [jwt.io](https://jwt.io) by pasting the token value. :::caution Security warning Only decode tokens from **test or development environments** using online tools. Never paste production tokens or tokens containing sensitive information into third-party sites, as they may be logged or leaked. ::: ## Required claims When configuring OIDC authentication, Camunda requires you to identify the following claims in the token. ### User identification claim Used to uniquely identify users during interactive login. - **Helm configuration:** `usernameClaim` - **Common claim names:** `email`, `preferred_username`, `sub`, `upn`, `unique_name` ### Client identification claim Used for machine-to-machine authentication to identify the calling client. - **Helm configuration:** `clientIdClaim` - **Common claim names:** `client_id`, `azp`, `appid`, `clientId` ### Audience claim Specifies the intended audience of the token. - **Helm configuration:** `audience` - **Claim name:** `aud` - **Typical value:** Client ID or a custom value configured in the provider ## Common claim patterns by provider | Provider | User claim | Client claim | Audience default | | --------------- | ------------------------------- | -------------------- | ------------------------- | | Microsoft Entra | `preferred_username` | `azp` | Client ID | | Keycloak | `email` or `preferred_username` | `azp` or `client_id` | May require configuration | | Auth0 | `email` | `client_id` | Client ID | | Okta | `email` | `client_id` | Client ID | ## Verify audience configuration 1. Decode a test token. 2. Inspect the value of the `aud` claim. 3. Use that value for the `audience` setting in your Camunda Helm configuration. :::warning The audience claim is required for token validation. Camunda rejects tokens that do not include the expected audience value. ::: ### If the token does not include the expected audience - Review your OIDC provider’s documentation for configuring token audiences. - Some providers require explicit audience configuration on the client. - Keycloak may default to `aud: "account"` and require additional setup. See [External Keycloak](./external-keycloak.md) for details. --- ## Set up the Helm chart with an external Microsoft Entra tenant This guide shows you how to configure the Helm chart to use a Microsoft Entra tenant, with each Camunda component using a dedicated OIDC or OAuth client. ## Prerequisites Before you begin, ensure you have: - Access to a Microsoft Entra tenant with permission to create applications and app registrations - The ID of your tenant - An understanding of the structure and claims of access tokens in Entra ## Configuration To use Microsoft Entra, complete the following steps: 1. [Ensure Entra prerequisites](#ensure-entra-prerequisites) 1. [Create applications in Entra](#create-applications-in-entra) 1. [Create secrets](#create-secrets) 1. [Configure components using OIDC](#configure-components-using-oidc) See the [full configuration example](#full-configuration-example) for the complete setup. ### Ensure Entra prerequisites For authentication, the Camunda components use the following scopes: `email`, `openid`, `offline_access`, `profile`, and `/.default`. :::tip Optional scopes The `offline_access` scope is optional. If this scope is included, your OIDC provider issues a refresh token to Camunda components on user login. The components use the refresh token to renew the user's access token when it expires, so that sessions remain active without requiring the user to log in again. If `offline_access` is not included, users will be redirected to the OIDC provider for re-authentication whenever their access token expires. For more information, see the [OpenID Connect Core specification](https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess). ::: To allow users to successfully authenticate with Entra ID, you must either configure an [admin consent workflow](https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/configure-admin-consent-workflow) or grant consent on behalf of your users using [admin consent](https://learn.microsoft.com/en-gb/entra/identity/enterprise-apps/user-admin-consent-overview#admin-consent). The applications you configure in this guide must support the following `grant_type` values: - To create an M2M token: `client_credentials` (response contains an access token) - To renew a token using a refresh token: `refresh_token` - To create a token via authorization code flow: `authorization_code` (response contains access and refresh tokens) These grant types are enabled by default, but they may be restricted by custom policies in your organization. ### Create applications in Entra Before configuring Camunda, create the following app registrations that map to Camunda components. Application type **Web**: - Management Identity (``) - Orchestration Cluster (``) - Optimize (``) - Web Modeler API (``) Application type **Single-page application**: - Console (``) - Web Modeler UI (``) For **each** of the components above: 1. In the Entra ID admin center, [register the application](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app). 1. On the application's **Overview** page, note the **Client ID**. 1. In the app registration, [configure a platform](https://learn.microsoft.com/en-gb/entra/identity-platform/quickstart-register-app#configure-platform-settings) that matches the component: - **Web**: Management Identity, Orchestration Cluster, Optimize, Web Modeler API - **Single-page application**: Console, Web Modeler UI 1. Add the component's redirect URI from [the table below](#redirect-uris-per-camunda-component). :::note Redirect URIs are an allowlist. Only the URIs you define are permitted as redirection targets after authentication. This ensures that tokens and authorization codes are only sent to approved destinations. ::: 1. For app registrations of type **Web**, [create a new client secret](https://learn.microsoft.com/en-gb/entra/identity-platform/quickstart-register-app?tabs=client-secret#add-credentials), and record the secret **value**. You do not need the secret ID. 1. Enable the Entra `v2.0` API by opening the application's [manifest](https://learn.microsoft.com/en-us/entra/identity-platform/reference-microsoft-graph-app-manifest#configure-the-app-manifest-in-the-microsoft-entra-admin-center) and setting the [`requestedAccessTokenVersion`](https://learn.microsoft.com/en-us/entra/identity-platform/reference-microsoft-graph-app-manifest#api-attribute) property under `api` to `2`: ```json api: { ... "requestedAccessTokenVersion": 2, ... } ``` 1. (Optional) In **Token configuration**, [add the optional claim](https://learn.microsoft.com/en-us/entra/identity-platform/optional-claims?tabs=appui) `preferred_username`. This lets you map a Camunda user ID to your users’ email addresses. If you do not configure `preferred_username`, update later steps in this guide to use a different claim that uniquely identifies your users. #### Redirect URIs per Camunda component | Component | Redirect URI for the Entra app registration | Redirect URI for local deployment | | --------------------- | -------------------------------------------- | --------------------------------------------------- | | Management Identity | `/auth/login-callback` | `http://localhost:8084/auth/login-callback` | | Orchestration Cluster | `/sso-callback` | `http://localhost:8080/sso-callback` | | Optimize | `/api/authentication/callback` | `http://localhost:8083/api/authentication/callback` | | Web Modeler UI | `/login-callback` | `http://localhost:8070/login-callback` | | Console | `/` | `http://localhost:8087/` | Replace each `*_URL` placeholder with the base URL (in the format `://:/`) that will be accessible from your users’ browsers. If you plan to expose the services only on `localhost` (as described later in this guide), you can use the URIs in the local deployment column directly. ### Create secrets Create two secrets in your Kubernetes namespace. First, create a secret that contains all OIDC client secrets: ``` kubectl create secret generic entra-credentials \ --from-literal=identity-client-secret="" \ --from-literal=orchestration-cluster-client-secret="" \ --from-literal=optimize-client-secret="" \ --from-literal=webmodeler-api-client-secret="" ``` :::info In Microsoft Entra, the term _application secret_ is used. In Camunda configuration, this value is referred to as a _client secret_ to align with OIDC/OAuth standards. ::: :::info The secret key `webmodeler-api-client-secret` is not used elsewhere in this guide. This client is intended for your own use if you want to access the [Web Modeler API](/apis-tools/web-modeler-api/authentication.md) programmatically. ::: Next, create a secret with the remaining credentials for the Camunda Helm chart: ``` kubectl create secret generic camunda-credentials \ --from-literal=identity-postgresql-admin-password=CHANGE_ME \ --from-literal=identity-postgresql-user-password=CHANGE_ME \ --from-literal=webmodeler-postgresql-admin-password=CHANGE_ME \ --from-literal=webmodeler-postgresql-user-password=CHANGE_ME ``` Unlike the OIDC client secrets, these passwords initialize the component databases. You can choose any values. This secret includes the following keys: - `identity-postgresql-admin-password`: Password for the administrative PostgreSQL account used by Management Identity (`postgres`). - `identity-postgresql-user-password`: Password for the non-privileged PostgreSQL account used by Management Identity (`bn_keycloak`). - `literal=webmodeler-postgresql-admin-password`: Password for the administrative PostgreSQL account used by Web Modeler (`postgres`). - `webmodeler-postgresql-user-password` Password for the non-privileged PostgreSQL account used by Web Modeler (`web-modeler`). For additional options on how to create and reference Kubernetes secrets (for example using YAML manifests or consolidated secrets), see [External Kubernetes secrets](/self-managed/deployment/helm/configure/secret-management.md#method-2-external-kubernetes-secrets-recommended-for-all-versions). ### Configure components using OIDC With the OIDC clients and cluster secrets in place, configure OAuth and OIDC for the components. You can skip components you don’t plan to run. Keep in mind that the Orchestration Cluster and Connectors are enabled by default, so you must explicitly disable them if not needed. #### Global configuration Start with the following global configuration, which provides defaults for all components: ```yaml global: identity: auth: enabled: true issuer: https://login.microsoftonline.com//v2.0 issuerBackendUrl: https://login.microsoftonline.com//v2.0 authUrl: https://login.microsoftonline.com//oauth2/v2.0/authorize tokenUrl: https://login.microsoftonline.com//oauth2/v2.0/token jwksUrl: https://login.microsoftonline.com//discovery/v2.0/keys type: "MICROSOFT" security: authentication: method: oidc ``` Replace `` with your Microsoft Entra tenant ID. You’ll use this convention throughout the rest of this guide. #### Configure Orchestration Cluster Add the following configuration for the Orchestration Cluster: ```yaml orchestration: security: authentication: oidc: clientId: "" audience: "" usernameClaim: preferred_username clientIdClaim: azp preferUsernameClaim: true redirectUrl: "" scope: - openid - profile - offline_access - "/.default" secret: existingSecret: "entra-credentials" existingSecretKey: "orchestration-cluster-client-secret" initialization: defaultRoles: admin: users: - "" connectors: clients: - "" ``` Replace `` with the base URL of the Orchestration Cluster as it will be reachable from your users’ browsers. For local deployment, this is `http://localhost:8080`. `usernameClaim` defines which claim in the access token identifies the user. `clientIdClaim` defines which claim identifies the calling client. By default: - `preferred_username` carries the user’s email address. - `azp` carries the client ID in Entra. You can adjust these values if your organization uses different claim mappings. For more information, see the [Orchestration Cluster OIDC configuration guide](/self-managed/components/orchestration-cluster/admin/connect-external-identity-provider.md#step-1-configure-the-oidc-client-id-claim). :::note Username display in Web Modeler (Helm) With Helm defaults, usernames are typically resolved from `preferred_username`. If you want Web Modeler to use the `name` claim instead (for example, to show display names), set `CAMUNDA_MODELER_OAUTH2_TOKEN_USERNAMECLAIM=name` for the Web Modeler `restapi` environment. See [Identity/Keycloak configuration](/self-managed/components/hub/configuration/modeler-configuration.md#identity--keycloak-1). ::: #### Configure Connectors Add the following configuration for Connectors: ```yaml connectors: security: authentication: oidc: clientId: "" audience: "" tokenScope: "/.default" secret: existingSecret: "entra-credentials" existingSecretKey: "orchestration-cluster-client-secret" ``` #### Configure Management Identity Add the following configuration for Management Identity: ```yaml global: identity: auth: identity: clientId: "" audience: "" initialClaimName: preferred_username initialClaimValue: "" secret: existingSecret: "entra-credentials" existingSecretKey: "identity-client-secret" identity: enabled: true identityPostgresql: enabled: true auth: existingSecret: "camunda-credentials" secretKeys: adminPasswordKey: "identity-postgresql-admin-password" userPasswordKey: "identity-postgresql-admin-password" ``` Replace `` with the base URL of Management Identity as it will be reachable from your users' browser. For local deployment, use `http://localhost:8084`. - `initialClaimName` defines which claim in the access token identifies the initial administrative user. - `initialClaimValue` defines the value of that claim that grants administrative access to Management Identity. :::danger Once configured, the initial claim name and value cannot be changed using environment variables or Helm values. To update them, modify the Identity PostgreSQL database directly. ::: :::tip If Optimize is not enabled, add the following environment variable to ensure Management Identity starts successfully: ``` identity: env: - name: CAMUNDA_IDENTITY_AUDIENCE value: "" ``` ::: #### Configure Optimize Add the following configuration for Optimize: ```yaml global: identity: auth: optimize: clientId: "" audience: "" redirectUrl: "" secret: existingSecret: "entra-credentials" existingSecretKey: "optimize-client-secret" optimize: enabled: true ``` Replace `` with the base URL of Optimize as it will be reachable from your users' browser. For local deployment, use `http://localhost:8083`. #### Configure Web Modeler Add the following configuration for Web Modeler: :::note If you want Web Modeler to resolve usernames from the `name` claim instead of `preferred_username`, add `CAMUNDA_MODELER_OAUTH2_TOKEN_USERNAMECLAIM=name` to the Web Modeler `restapi` environment configuration. ::: ```yaml global: identity: auth: webModeler: clientId: "" clientApiAudience: "" publicApiAudience: "" redirectUrl: "" webModeler: enabled: true restapi: mail: fromAddress: noreply@example.com webModelerPostgresql: enabled: true auth: existingSecret: "camunda-credentials" secretKeys: adminPasswordKey: "webmodeler-postgresql-admin-password" userPasswordKey: "webmodeler-postgresql-user-password" ``` Replace `` with the base URL of Web Modeler as it will be reachable from your users' browser. For local deployment, use `http://localhost:8070`. You can update `webModeler.restapi.mail.fromAddress` with an address suitable for your environment. This address appears as the sender in emails sent by Web Modeler. For more details on configuring email delivery, see the [Web Modeler section in Enable additional Camunda components](../enable-additional-components.md#web-modeler). #### Configure Console Add the following configuration for Console: ```yaml global: identity: auth: console: clientId: "" audience: "" redirectUrl: "http://localhost:8087" console: enabled: true ``` ### Full configuration example The following example shows a full configuration to enable Microsoft Entra: ```yaml global: elasticsearch: enabled: true identity: auth: enabled: true issuer: https://login.microsoftonline.com//v2.0 issuerBackendUrl: https://login.microsoftonline.com//v2.0 authUrl: https://login.microsoftonline.com//oauth2/v2.0/authorize tokenUrl: https://login.microsoftonline.com//oauth2/v2.0/token jwksUrl: https://login.microsoftonline.com//discovery/v2.0/keys type: "MICROSOFT" identity: clientId: "" audience: "" initialClaimName: preferred_username initialClaimValue: "" secret: existingSecret: "entra-credentials" existingSecretKey: "identity-client-secret" optimize: clientId: "" audience: "" redirectUrl: "" secret: existingSecret: "entra-credentials" existingSecretKey: "optimize-client-secret" webModeler: clientId: "" clientApiAudience: "" publicApiAudience: "" redirectUrl: "" console: clientId: "" audience: "" redirectUrl: "http://localhost:8087" security: authentication: method: oidc orchestration: security: authentication: oidc: clientId: "" audience: "" usernameClaim: preferred_username clientIdClaim: azp preferUsernameClaim: true redirectUrl: "" scope: - openid - profile - offline_access - "/.default" secret: existingSecret: "entra-credentials" existingSecretKey: "orchestration-cluster-client-secret" initialization: defaultRoles: admin: users: - "" connectors: clients: - "" connectors: security: authentication: oidc: clientId: "" audience: "" tokenScope: "/.default" secret: existingSecret: "entra-credentials" existingSecretKey: "orchestration-cluster-client-secret" identity: enabled: true identityPostgresql: enabled: true auth: existingSecret: "camunda-credentials" secretKeys: adminPasswordKey: "identity-postgresql-admin-password" userPasswordKey: "identity-postgresql-admin-password" optimize: enabled: true webModeler: enabled: true restapi: mail: fromAddress: noreply@example.com webModelerPostgresql: enabled: true auth: existingSecret: "camunda-credentials" secretKeys: adminPasswordKey: "webmodeler-postgresql-admin-password" userPasswordKey: "webmodeler-postgresql-user-password" console: enabled: true elasticsearch: enabled: true ``` ### Connect to the cluster After applying this configuration, use the following `kubectl port-forward` commands to access the APIs and UIs from your localhost: ```bash # Management Identity kubectl port-forward svc/camunda-identity 8084:80 # Orchestration Cluster kubectl port-forward svc/camunda-zeebe-gateway 8080:8080 kubectl port-forward svc/camunda-zeebe-gateway 26500:26500 # Connectors kubectl port-forward svc/camunda-connectors 8086:8080 # Optimize kubectl port-forward svc/camunda-optimize 8083:80 # Web Modeler kubectl port-forward svc/camunda-web-modeler-restapi 8070:80 kubectl port-forward svc/camunda-web-modeler-websockets 8085:80 # Console kubectl port-forward svc/camunda-console 8087:80 ``` Once port forwarding is active, access each component through `http://localhost:`. For example: - Orchestration Cluster: `http://localhost:8080` (redirects you to Entra for login) - Management Identity: `http://localhost:8084` - Console: `http://localhost:8087` ## Grant access to components After deployment, you must configure access for the following components. To grant a user access to the Web Modeler UI: - [Create a mapping rule in Management Identity](/self-managed/components/management-identity/mapping-rules.md#add-a-mapping-rule) for the `Web Modeler` role that matches the user’s access token. To grant a client access to the Web Modeler API: - [Create a role in Management Identity](/self-managed/components/management-identity/application-user-group-role-management/manage-roles.md#add-a-role) for the Web Modeler API. - [Assign Web Modeler API permissions](/self-managed/components/management-identity/access-management/manage-permissions.md#manage-role-permissions) to that role in Management Identity. - [Create a mapping rule in Management Identity](/self-managed/components/management-identity/mapping-rules.md#add-a-mapping-rule) for that role that matches the client’s access token. To grant a user access to Optimize: - [Create a mapping rule in Management Identity](/self-managed/components/management-identity/mapping-rules.md#add-a-mapping-rule) for the `Optimize` role that matches the user’s access token. :::info When using an OIDC provider, the following Optimize features are not currently available: - The **User permissions** tab in collections - The **Alerts** tab in collections - Digests - Accessible user names for resource owners (the value of the `sub` claim is displayed instead). ::: --- ## Troubleshoot OIDC authentication This page provides solutions to common issues encountered when configuring OIDC authentication for Camunda 8 Self-Managed. ## Invalid redirect_uri **Observed behavior:** During login, your OIDC provider shows "Invalid redirect_uri". **Why this happens:** The `redirectUrl` in Helm values doesn't match an allowed redirect URI configured in your OIDC provider. **How to fix:** 1. Open the browser's developer tools (F12) and check the `redirect_uri` parameter sent to your OIDC provider. 2. Ensure this exact URI is configured in your OIDC provider's allowed redirect URIs. 3. Update `redirectUrl` in Helm values to match how users actually access the component. :::info Common misconfiguration Use `http://localhost:8080` in Helm values when users access via `https://camunda.example.com/orchestration`. ::: ## Invalid audience **Observed behavior:** Logs show "Invalid token" or "Audience mismatch" errors. **Why this happens:** The `audience` parameter doesn't match the `aud` claim in tokens. **How to fix:** 1. Obtain and decode a token: ```bash curl -X POST '' \ -d 'client_id=' \ -d 'client_secret=' \ -d 'grant_type=client_credentials' | jq -r '.access_token' | \ cut -d'.' -f2 | base64 -d | jq '.aud' ``` 2. Update the `audience` parameter in Helm values to match this value. 3. Redeploy Camunda. :::note Some providers, such as Keycloak, may not include the appropriate audience by default. Consult your provider's documentation on configuring token audiences. For Keycloak, see [External Keycloak](./external-keycloak.md). ::: ## Insufficient permissions **Observed behavior:** You authenticate, but Camunda shows "Insufficient permissions". **Why this happens:** Your account hasn't been granted access via mapping rules. **How to fix:** In Management Identity, create a mapping rule that matches your claim values and assign the appropriate role. See [Managing mapping rules](/self-managed/components/management-identity/mapping-rules.md) for more details. ## Claim not found **Observed behavior:** Logs show "Claim not found" or "Required claim missing" errors. **Why this happens:** The configured claim name doesn't exist in tokens issued by your provider. **How to fix:** 1. Decode a token to see available claims. See [JWT token claims reference](./jwt-token-claims.md) for instructions. 2. Update `usernameClaim` or `clientIdClaim` in Helm values to match the actual claim names. 3. Redeploy Camunda. **Common alternatives:** - User claims: `email`, `preferred_username`, `sub` - Client claims: `client_id`, `azp`, `appid`. For a complete list of common claim patterns by provider, see [JWT token claims reference](./jwt-token-claims.md#common-claim-patterns-by-provider). ## Pods not starting **Observed behavior:** Pods remain in `Pending`, `CrashLoopBackOff`, or `Error` states. **Why this happens:** Required secrets are missing, PostgreSQL is still initializing, or there are configuration typos in OIDC URLs. **How to fix:** 1. Inspect pod events and status with `kubectl describe pod -n camunda` and `kubectl logs -n camunda`. 2. Check component logs with `kubectl logs -n camunda deployment/ -f` and search for keywords: `auth`, `token`, `oidc`, `401`, `403`. ## Request header is too large **Observed behavior:** Logging in to Management Identity fails and the browser shows a Tomcat error page, for example `HTTP Status 400 – Bad Request`. Management Identity logs contain messages similar to: ```text o.a.coyote.http11.Http11Processor : Error parsing HTTP request header Note: further occurrences of HTTP request parsing errors will be logged at DEBUG level. java.lang.IllegalArgumentException: Request header is too large at org.apache.coyote.http11.Http11InputBuffer.fill(Http11InputBuffer.java:765) ... ``` **Why this happens:** When using an external OIDC provider (for example, Microsoft Entra ID), the access token and related cookies (such as `IDENTITY_JWT`, `IDENTITY_REFRESH_JWT`, and Optimize cookies) can make the HTTP request header larger than the default limit of the embedded application server (Tomcat). By default, Tomcat rejects requests whose headers exceed this limit (typically 8 KB). As a result, the request never reaches Camunda, and the login fails with request header is too large. **How to fix:** Increase the maximum allowed HTTP request header size for the Identity service. 1. Configure the Spring Boot property `server.max-http-request-header-size` (via the `SERVER_MAXHTTPREQUESTHEADERSIZE` environment variable) to a value higher than the default, for example 40KB. 2. If you are using the Helm chart, set this environment variable on the Identity deployment in your `values.yaml`, similar to other Identity environment variables: ```yaml identity: env: - name: SERVER_MAXHTTPREQUESTHEADERSIZE value: "40KB" ``` 3. Upgrade or redeploy the release so the new environment variable takes effect. --- ## Configure multi-tenancy in Helm chart Multi-tenancy lets you isolate users, data, and workloads across tenants (for example, business units, departments, or customers) within the same Camunda 8 cluster. This ensures separation while reducing infrastructure overhead by running multiple tenants on a shared installation. This page explains how to configure multi-tenancy in both Management Identity and [Orchestration Cluster Admin](/self-managed/components/orchestration-cluster/admin/overview.md). It also shows the defaults, how to enable or enforce tenant checks, and how to resolve common issues. ## Prerequisites - A running Camunda 8 Self-Managed deployment with authentication enabled. :::note Multi-tenancy requires authentication in the Orchestration Cluster Admin. If authentication is disabled, multi-tenancy does not work. ::: The memory limit for Management Identity is generally suitable for most multi-tenant deployments. If the tenancy model becomes more complex with many tenants, you might encounter memory pressure on the heap. If you anticipate higher memory usage, consider increasing the memory limit for Management Identity: ```yaml identity: resources: limits: memory: 4Gi ``` As an additional safeguard, you can increase the JVM heap allocation by setting: ```yaml identity: env: - name: JAVA_TOOL_OPTIONS value: -XX:MaxRAMPercentage=50.0 ``` ## Configuration Multi-tenancy behavior differs depending on the identity component: - **Management Identity:** Disabled by default. You must enable it. Once enabled, tenant checks are automatically enforced (all requests are validated against the active tenant configuration). - **Orchestration Cluster Admin:** Enabled by default, with a default tenant created. Tenant checks are not enforced unless explicitly enabled. ### Parameters | values.yaml option | type | default | description | | ------------------------------------------- | ------- | ------- | --------------------------------------------------------------------------------- | | `global.multitenancy.enabled` | boolean | `false` | (Management Identity) Enable multi-tenancy globally. | | `orchestration.multitenancy.checks.enabled` | boolean | `false` | (Orchestration Cluster Admin) Enforce tenant validation across requests. | | `orchestration.multitenancy.api.enabled` | boolean | `true` | (Orchestration Cluster Admin) Enable the multi-tenancy API for tenant management. | ### Example usage **Management Identity** Enable multi-tenancy in Management Identity: ```yaml global: multitenancy: enabled: true ``` **Orchestration Cluster Admin** Enable tenant checks and the multi-tenancy API: ```yaml orchestration: multitenancy: checks: enabled: true # Enforces tenant checks in all components api: enabled: true # Enables multi-tenancy API for tenant management ``` :::warning Disabling multi-tenancy after it has been enabled can cause unexpected behavior if active tenants exist. ::: --- ## Configure data retention Data retention policies automatically delete old data from secondary storage after a specified time period. This prevents unlimited data growth, reduces storage costs, and maintains system performance. If you use Elasticsearch or OpenSearch as your secondary storage backend, retention is implemented using Index Lifecycle Management (ILM) for Elasticsearch or Index State Management (ISM) for OpenSearch. See [Elasticsearch/OpenSearch](/reference/glossary.md#elasticsearchopensearch) for details. :::note If you use an RDBMS as your secondary storage backend, implement retention and cleanup using the RDBMS exporter and database-specific mechanisms. See [RDBMS configuration](/self-managed/concepts/databases/relational-db/configuration.md) for details. ::: :::tip Best practice **Configure data retention during initial installation.** Adding retention configuration after deployment may require manual policy creation in Elasticsearch/OpenSearch. See [Differences from previous versions](#differences-from-previous-versions) for version-specific behavior. ::: ## Prerequisites - Camunda 8.8+ Helm chart deployment - Elasticsearch 7+ or OpenSearch 2.5+ - Access to modify your `values.yaml` file ## Configuration Configure retention policies in your `values.yaml` file under the `orchestration` section. Retention policy types: 1. **Elasticsearch/OpenSearch Exporter indices (Zeebe records)** (`orchestration.retention`) – Retention for Zeebe record indices written by the legacy Elasticsearch/OpenSearch Exporter (for example, `zeebe-record-*`). These are _not_ Orchestration Cluster indices. 1. **Orchestration Cluster indices (historical data)** (`orchestration.history.retention`) – Retention for archived Operate, Tasklist, and Camunda indices stored in secondary storage. For index prefix requirements and examples when both index families share the same Elasticsearch/OpenSearch cluster, see [Configure Elasticsearch and OpenSearch index prefixes](/self-managed/deployment/helm/configure/database/elasticsearch/configure-elasticsearch-prefix-indices.md). :::warning Zeebe records retention requirements `orchestration.retention.*` apply only to Elasticsearch/OpenSearch Exporter indices (Zeebe records), not to Orchestration Cluster indices managed by the Camunda Exporter. The `orchestration.retention` configuration requires the legacy Zeebe Elasticsearch/OpenSearch Exporter to be enabled. The legacy exporter is automatically enabled when: - `orchestration.exporters.zeebe.enabled: true` is set, OR - Optimize is enabled (`optimize.enabled: true`), OR - Data migration is enabled (`orchestration.migration.data.enabled: true`) Starting in Camunda 8.8, `orchestration.exporters.zeebe.enabled` defaults to `false`. If you need Zeebe record retention without Optimize, you must explicitly enable it. For full exporter configuration and retention options, see [Zeebe Elasticsearch Exporter retention](../../../../components/orchestration-cluster/zeebe/exporters/elasticsearch-exporter). ::: ### Parameters **Zeebe records retention parameters:** | Key | Type | Default | Description | | ------------------------------------ | ------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `orchestration.retention.enabled` | boolean | `false` | If `true`, creates and applies the ILM/ISM policy to Zeebe record indices. **Requires the legacy Zeebe exporter to be enabled** (see prerequisites above). | | `orchestration.retention.minimumAge` | string | `30d` | How old the data must be before deletion. Uses [Elasticsearch TimeUnit format](https://www.elastic.co/guide/en/elasticsearch/reference/current/api-conventions.html#time-units) (for example, `30d`, `7d`, `1h`) | | `orchestration.retention.policyName` | string | `zeebe-record-retention-policy` | Name of the ILM/ISM policy to create and apply | :::note Exported data vs. retained data The `orchestration.retention` policy always applies to the Zeebe record indices that are actually written (for example, `zeebe-record-*`). If you configure exporter-side filters in the legacy Elasticsearch or OpenSearch exporter (such as exporting only a subset of variables or processes for data analysis tools like Optimize), retention policies apply only to data that was exported. They do not recreate or restore records that were filtered out by exporter-side filters. For Optimize and other data-analysis use cases, coordinate exporter-side filters and retention settings, and refer to: - [Elasticsearch exporter](../../../../components/orchestration-cluster/zeebe/exporters/elasticsearch-exporter/) - [OpenSearch exporter](../../../../components/orchestration-cluster/zeebe/exporters/opensearch-exporter/) - [Camunda 8 system configuration (Optimize)](../../../../components/optimize/configuration/system-configuration-platform-8/) ::: **History archiving and retention parameters:** | Key | Type | Default | Description | | -------------------------------------------------------- | ------- | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------- | | `orchestration.history.waitPeriodBeforeArchiving` | string | `1h` | Grace period before archiving completed processes. Processes finished within this window are not yet archived. | | `orchestration.history.rolloverInterval` | string | `1d` | Time range for creating dated indices (for example, `1d` creates daily indices). | | `orchestration.history.rolloverBatchSize` | integer | `100` | Maximum number of process instances per archiving batch | | `orchestration.history.elsRolloverDateFormat` | string | `date` | Date format for historical indices in Java DateTimeFormatter syntax | | `orchestration.history.delayBetweenRuns` | integer | `2000` | Millisecond interval between archiver runs | | `orchestration.history.maxDelayBetweenRuns` | integer | `60000` | Maximum millisecond interval between archiver runs due to failure backoffs | | `orchestration.history.retention.enabled` | boolean | `false` | If `true`, applies ILM/ISM policy to archived orchestration indices (Operate, Tasklist, Camunda) | | `orchestration.history.retention.minimumAge` | string | `30d` | How old archived data must be before deletion | | `orchestration.history.retention.policyName` | string | `camunda-history-retention-policy` | Name of the ILM/ISM policy for historical data | | `orchestration.history.retention.usageMetricsMinimumAge` | string | `730d` | Retention period for usage metrics indices (2 years by default) | | `orchestration.history.retention.usageMetricsPolicyName` | string | `camunda-usage-metrics-retention-policy` | Name of the ILM/ISM policy for usage metrics | ### Example usage #### Orchestration Cluster history retention (recommended) In most deployments starting with Camunda 8.8, you only need retention for Orchestration Cluster indices (archived Operate, Tasklist, and Camunda data). The example below sets most properties to their current default values and explicitly enables history retention (`orchestration.history.retention.enabled: true`). If defaults change in future versions, clusters that keep these explicit values will retain the behavior shown here. ```yaml orchestration: history: waitPeriodBeforeArchiving: 1h rolloverInterval: 1d rolloverBatchSize: 100 elsRolloverDateFormat: date delayBetweenRuns: 2000 maxDelayBetweenRuns: 60000 retention: enabled: true minimumAge: 30d policyName: camunda-history-retention-policy usageMetricsMinimumAge: 730d usageMetricsPolicyName: camunda-usage-metrics-retention-policy ``` #### Zeebe records retention Enable Zeebe records retention only if you still use the legacy Elasticsearch/OpenSearch Exporter (for example, when Optimize reads from `zeebe-record-*` indices). Exporter configuration and full retention examples are documented in [Zeebe Elasticsearch Exporter retention](../../../../components/orchestration-cluster/zeebe/exporters/elasticsearch-exporter). The snippet below shows only the Helm values related to enabling retention: ```yaml # Either enable Optimize, which in turn enables the legacy Zeebe exporter: optimize: enabled: true # Or enable the legacy Zeebe exporter directly: orchestration: exporters: zeebe: enabled: true # Zeebe records retention (Elasticsearch/OpenSearch Exporter indices) orchestration: retention: enabled: true minimumAge: 30d policyName: zeebe-record-retention-policy ``` #### Retention configuration Both scenarios use the same retention configuration: ```yaml orchestration: # Zeebe records retention retention: enabled: true minimumAge: 30d policyName: zeebe-record-retention-policy # Historical data archiving and retention history: waitPeriodBeforeArchiving: 1h rolloverInterval: 1d rolloverBatchSize: 100 elsRolloverDateFormat: date delayBetweenRuns: 2000 maxDelayBetweenRuns: 60000 retention: enabled: true minimumAge: 30d policyName: camunda-history-retention-policy usageMetricsMinimumAge: 730d usageMetricsPolicyName: camunda-usage-metrics-retention-policy ``` ## Troubleshooting ### Verifying retention policies After deploying with retention enabled, verify that the ILM/ISM policies were created successfully. - Zeebe records retention (`zeebe-record-retention-policy`) applies to Elasticsearch/OpenSearch Exporter indices (Zeebe records) created by the legacy exporter (for example, indices matching the exporter prefix such as `zeebe-record-*`). The Zeebe Elasticsearch/OpenSearch Exporter creates this policy automatically during initialization, typically within a few minutes of deployment. - History retention (`camunda-history-retention-policy`) applies to archived Orchestration Cluster indices used by Operate, Tasklist, and Camunda (for example, `operate-process-*`, `tasklist-task-*` with date suffixes). The schema manager creates this ILM/ISM policy on application startup when `orchestration.history.retention.enabled: true`. The archiver then: 1. Creates archived indices and attaches the existing policy. 2. Waits for `waitPeriodBeforeArchiving` (default: 1 hour) after the **root process instance** completes. 3. Archives the completed hierarchy into dated indices (for example, `operate-process-8.3.0_2024-01-15`, `tasklist-task-8.8.0_2024-01-15`). Set your database URL: ```bash export DATABASE_URL="https://your-database-host:9200" ``` Replace `your-database-host` with your Elasticsearch or OpenSearch hostname. Check if policies exist: For **Elasticsearch**: ```bash curl -X GET "${DATABASE_URL}/_ilm/policy/zeebe-record-retention-policy?pretty" curl -X GET "${DATABASE_URL}/_ilm/policy/camunda-history-retention-policy?pretty" ``` For **OpenSearch**: ```bash curl -X GET "${DATABASE_URL}/_plugins/_ism/policies/zeebe-record-retention-policy?pretty" curl -X GET "${DATABASE_URL}/_plugins/_ism/policies/camunda-history-retention-policy?pretty" ``` Expected response for a policy with 30-day retention: ```json { "zeebe-record-retention-policy": { "version": 1, "modified_date": "2025-01-15T10:30:00.000Z", "policy": { "phases": { "delete": { "min_age": "30d", "actions": { "delete": {} } } } } } } ``` Check if archived indices exist: ```bash curl -X GET "${DATABASE_URL}/_cat/indices/operate-*_20*?v" curl -X GET "${DATABASE_URL}/_cat/indices/tasklist-*_20*?v" ``` Archived indices follow the pattern: `{component}-{type}-{schema-version}_{date}` Examples: - `operate-process-8.3.0_2024-01-15` - `operate-variable-8.3.0_2024-01-15` - `tasklist-task-8.8.0_2024-01-15` :::note Index versioning The version number in index names (for example, `8.3.0`) represents the schema version, not necessarily the Camunda platform version. Schema versions evolve independently as index structures change. For details, see [schema and migration documentation](/self-managed/components/orchestration-cluster/core-settings/concepts/schema-and-migration.md). ::: If no archived indices exist: - No processes have completed and been archived yet - The `waitPeriodBeforeArchiving` period hasn't elapsed - Deploy and complete a test process, then wait for archiving to occur Check if retention policy is applied to an archived index: For **Elasticsearch**: ```bash curl -X GET "${DATABASE_URL}/operate-process-8.3.0_2024-01-15/_settings?pretty" | grep -A 3 lifecycle ``` For **OpenSearch**: ```bash curl -X GET "${DATABASE_URL}/_plugins/_ism/explain/operate-process-8.3.0_2024-01-15?pretty" ``` Expected output showing the policy is attached: ```json { "index.lifecycle.name": "camunda-history-retention-policy" } ``` ### Manually creating or updating policies (8.7 and earlier) For Camunda 8.8+, policies are created automatically by the retention tooling. If you need to manually create or update policies, use the policy names configured in your `values.yaml` with the commands in the [Camunda 8.7 manual policy management guide](/versioned_docs/version-8.7/self-managed/setup/guides/data-retention.md#manual-policy-management). **Camunda 8.8 default policy names** (customizable via Helm values): - `zeebe-record-retention-policy` - For Zeebe record indices (configured via `orchestration.retention.policyName`) - `camunda-history-retention-policy` - For historical Operate, Tasklist, and Camunda indices (configured via `orchestration.history.retention.policyName`) - `camunda-usage-metrics-retention-policy` - For usage metrics indices (configured via `orchestration.history.retention.usageMetricsPolicyName`) **Camunda 8.7 default policy names** (only Zeebe is customizable): - `zeebe-record-retention-policy` - For Zeebe records (customizable via `zeebe.retention.policyName`) - `operate_delete_archived_indices` - For Operate indices (hardcoded) - `tasklist_delete_archived_indices` - For Tasklist indices (hardcoded) The curl commands for creating and applying policies are the same across versions—only the policy names differ. See the [8.7 guide's manual policy section](/versioned_docs/version-8.7/self-managed/setup/guides/data-retention.md#manual-policy-management) for complete ILM and ISM policy creation commands. ### Known limitations **OpenSearch policy updates:** When using OpenSearch, updating an existing ISM policy's `minimumAge` may not take effect automatically. OpenSearch requires the `seq_no` and `primary_term` parameters during policy updates to ensure proper version control. Without these parameters, policy updates may fail or be skipped. **To update an OpenSearch policy:** 1. Get the current policy with its version information 1. Update the policy using the `seq_no` and `primary_term` from the current version 1. Verify the updated policy reflects the new `minimumAge` See the [OpenSearch ISM API documentation](https://opensearch.org/docs/latest/im-plugin/ism/api/) for details on policy version parameters. **Elasticsearch bulk operations:** When applying retention policies to a large number of existing indices, the operation may fail due to HTTP line length limits in Elasticsearch. This typically occurs when using wildcard patterns to apply settings to many indices at once (for example, `operate-*`, `tasklist-*`). **Workarounds:** - Apply settings to smaller batches of indices using more specific patterns - Apply settings to individual indices when the number of indices is very large - Use index templates for future indices instead of retroactively applying to all indices **Policy timing considerations:** The `camunda-history-retention-policy` is created by Camunda's retention tooling as part of the archiving workflow. If you query for this policy immediately after deployment and before any archiving occurs, you may receive a 404 response. The policy is created when the first archived index is created by the archiver. :::note Index naming Operate and Tasklist indices use schema-specific versioning in their names (for example, `operate-process-8.3.0_`, `tasklist-task-8.8.0_`). The version numbers represent schema versions, which may differ from the Camunda platform version. When archived, these indices receive a date suffix (for example, `operate-process-8.3.0_2024-01-15`). ::: ## References **Related Camunda documentation:** - [Configure Helm chart components](./application-configs.md) – How to use `orchestration.configuration` for advanced settings - [Upgrade from 8.8 to 8.9](../../../upgrade/helm/880-to-890.md) – Version upgrade guidance - [Zeebe Elasticsearch exporter retention](../../../components/orchestration-cluster/zeebe/exporters/elasticsearch-exporter.md?configuration=retention#retention) – Legacy Zeebe exporter retention settings - [Zeebe Camunda exporter](../../../components/orchestration-cluster/zeebe/exporters/camunda-exporter.md) – Camunda exporter configuration (controls `orchestration.history.*` settings) - [History archiving settings](../../../components/orchestration-cluster/zeebe/exporters/camunda-exporter.md#history) – Archiving and rollover configuration - [Retention settings](../../../components/orchestration-cluster/zeebe/exporters/camunda-exporter.md#retention) – Historical data retention policies - [Camunda 8 system configuration (Optimize)](../../../components/optimize/configuration/system-configuration-platform-8.md) – Optimize import and retention behavior and version support - [Operate data retention](../../../components/orchestration-cluster/core-settings/concepts/data-retention.md) – Operate-specific retention behavior - [Tasklist data retention](../../../components/orchestration-cluster/core-settings/concepts/data-retention.md) – Tasklist-specific retention behavior **External documentation:** - [Elasticsearch ILM documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/index-lifecycle-management.html) - Official Elasticsearch ILM guide - [Elasticsearch TimeUnit format](https://www.elastic.co/guide/en/elasticsearch/reference/current/api-conventions.html#time-units) - Valid time unit values - [OpenSearch ISM documentation](https://opensearch.org/docs/latest/im-plugin/ism/index/) - Official OpenSearch ISM guide - [Helm values documentation](https://helm.sh/docs/chart_template_guide/values_files/) - Working with Helm values files ## Differences from previous versions **Camunda 8.8:** - **Policy creation and attachment**: Policies are created automatically by retention tooling and attached to archived indices as they are created - **Unified history retention**: Historical data retention (Operate, Tasklist, Camunda indices) uses unified `orchestration.history.retention` configuration - **Zeebe record retention**: Legacy zeebe-record indices remain under exporter-specific configuration (`orchestration.retention`) - **OpenSearch policy updates**: Updating existing ISM policies requires `seq_no` and `primary_term` parameters; without them, updates may fail and require manual intervention - **Bulk operations**: Applying settings to many indices may fail due to HTTP line length limits, requiring batching or manual workarounds **Camunda 8.7 and earlier:** - **Operate**: Creates ILM policy when enabled, but configuration updates (for example, changing `minimumAge`) are not applied automatically - manual policy updates required - **Zeebe**: Creates policies on initial install and applies ILM configuration updates automatically - **Tasklist**: Does not apply ILM configuration updates after deployment - manual policy updates required - **Versions 8.5-8.6**: ILM policies sometimes missing after configuration; may require manual creation or new record export to trigger policy creation :::info For Camunda 8.7 and earlier, if you change retention configuration after initial deployment, you must manually update the policies in Elasticsearch/OpenSearch for Operate and Tasklist. Zeebe automatically applies configuration updates. See [Manually creating or updating policies](#manually-creating-or-updating-policies-87-and-earlier). ::: --- ## Access SQL and Liquibase scripts Access and safely use the SQL and Liquibase scripts provided with Camunda 8 for supported databases. These scripts can be used for provisioning, upgrading, or managing database schemas in your environment. :::note Related pages - **[Configure RDBMS](rdbms.md)** - Configuration reference and Helm values. - **[Schema management](rdbms-schema-management.md)** - Schema creation, permissions, and auto-DDL configuration. - **[JDBC driver management](rdbms-jdbc-drivers.md)** - Managing custom database drivers. ::: ## Where the scripts are published The scripts are included in the **Camunda 8 Run distribution** and in each **Camunda GitHub release** as a versioned ZIP file: - **GitHub release example:** [Camunda 8.9.0-alpha1](https://github.com/camunda/camunda/releases/tag/8.9.0-alpha1) - **C8Run distribution:** top-level folder `rdbms-schema/` ## Distribution & ZIP contents The ZIP contains SQL scripts and Liquibase change sets for all supported databases: ``` / - | liquibase - changelog-master.xml | changesets - 8.9.0.xml - 8.10.0.xml | sql | create | h2 - h2_create_8.9.0.sql | mariadb - mariadb_create_8.9.0.sql | mssql - mssql_create_8.9.0.sql | mysql - mysql_create_8.9.0.sql | oracle - oracle_create_8.9.0.sql | postgresql - postgres_create_8.9.0.sql | upgrade | h2 - h2_upgrade_8.9.0_to_8.10.0.sql ... ``` :::note Drop scripts are not provided. ::: ## How to download - **From a GitHub release (ZIP):** Download the schema scripts from `https://github.com/camunda/camunda/releases/tag//camunda-db-rdbms-schema-.zip`. - **From a C8Run distribution:** Retrieve the schema scripts from the `rdbms-schema/` folder included in the distribution. ## Usage guidance - **Version matching:** Always use scripts corresponding to your Camunda 8 version. - **Database selection:** Use the folder for your target database flavor (PostgreSQL, Oracle, MariaDB, etc.). - **Automatic schema management:** Camunda will manage the schema by default. Manual management requires disabling auto-DDL: ```yaml camunda: data: secondary-storage: rdbms: auto-ddl: false ``` - **SQL vs. Liquibase** - Do not mix SQL upgrade scripts with Liquibase-managed schema. - Liquibase changelogs are **forward-only**. Rollbacks are not supported. - **Liquibase lock recovery:** If a pod is interrupted during Liquibase execution, Camunda waits for stale DDL locks using `camunda.data.secondary-storage.rdbms.ddl-lock-wait-timeout` (default: `PT15M`). Increase this timeout for long-running migrations and only release `databasechangeloglock` manually after confirming no migration is running. See [RDBMS troubleshooting](rdbms-troubleshooting.md#liquibase-lock-after-pod-crash-or-restart). - **Backup first:** Always [back up](/self-managed/operational-guides/backup-restore/backup-and-restore.md) your database before applying scripts manually. ## Optional - **Checksums:** SHA1 or SHA256 checksums are provided in GitHub release assets. - **Liquibase CLI example:** See [Liquibase getting started](https://www.liquibase.org/get-started/running-your-first-update). - **Upgrade workflow:** Recommended approach is to allow Camunda to manage the schema automatically. Manual upgrades are supported, but users must apply scripts sequentially from the initial version to the target version. - **Performance:** Indexes are included in scripts as needed. Adding custom indexes may affect future upgrades. --- ## All shards failed errors When deploying Camunda 8.8+ with OpenSearch (or Elasticsearch) in a multi-node setup, you may encounter errors like the following in your Camunda logs: ``` [2025-07-18 22:00:59.235] [http-nio-0.0.0.0-8080-exec-13] WARN io.camunda.search.os.clients.OpensearchSearchClient - Failed to execute findAll query org.opensearch.client.opensearch._types.OpenSearchException: Request failed: [search_phase_execution_exception] all shards failed at org.opensearch.client.transport.aws.AwsSdk2Transport.parseResponse(AwsSdk2Transport.java:582) at org.opensearch.client.transport.aws.AwsSdk2Transport.executeSync(AwsSdk2Transport.java:440) at org.opensearch.client.transport.aws.AwsSdk2Transport.performRequest(AwsSdk2Transport.java:217) at org.opensearch.client.opensearch.OpenSearchClient.search(OpenSearchClient.java:1386) [...] ``` ### What this error means? The error `Request failed: [search_phase_execution_exception] all shards failed` indicates that OpenSearch could not retrieve data from any shard of the requested index. This does not necessarily mean data is corrupted or lost. The cause might be connectivity issues between OpenSearch nodes, especially in multi-node setups. ### Step 1: Check index and shard health Run the following command to verify shard allocation and index health: ```bach curl http://:9200/_cat/indices?v ``` - If the index health status is `green`, all primary and replica shards are healthy and available. - If Camunda still logs `all shards failed`,` it may be due to temporary connectivity issues between OpenSearch nodes. ### Multi-node deployments and default configuration pitfalls Camunda's Helm chart is per default configured for single-node deployments with a shard replication of 0. In a multi-node OpenSearch setup, this can cause problems if shard allocation is not balanced. Consider the following scenario: ``` Orchestration Cluster | v OpenSearch Node 1 (no shard) <-x-> OpenSearch Node 2 (not reachable; holds the shard) ``` :::info Starting with version 8.8, Camunda stores authorization data in secondary storage. In deployments using Elasticsearch or OpenSearch as the secondary storage backend, this data is stored in Elasticsearch/OpenSearch — see [Elasticsearch/OpenSearch](/reference/glossary.md#elasticsearchopensearch) for details. Previously, this data was stored in PostgreSQL/Keycloak. This means: - Every authorization check now triggers one or more search engine queries. - These queries are critical — if they fail, Camunda components may be unable to process requests. ::: ### Step 2: Improve resilience with replica shards To improve fault tolerance, increase the number of replica shards. This ensures that if one database node becomes unavailable, another can still serve the data. As a general rule for multi-node clusters: ``` numberOfReplicas = numberOfNodes - 1 ``` For example, in a 3-node OpenSearch cluster, set the following environment variables: ```yaml orchestration: env: - name: CAMUNDA_TASKLIST_OPENSEARCH_NUMBEROFREPLICAS value: "2" - name: CAMUNDA_OPERATE_OPENSEARCH_NUMBEROFREPLICAS value: "2" - name: CAMUNDA_DATABASE_INDEX_NUMBEROFREPLICAS value: "2" ``` --- ## Configure custom HTTP headers for database clients You can add custom HTTP headers to the Elasticsearch or OpenSearch clients used by Camunda components by creating a Java plugin and adding it to your Camunda 8 Self-Managed installation. When Elasticsearch/OpenSearch is configured as your secondary storage backend, custom headers can help with authentication, tracking, or debugging for those requests. See [Elasticsearch/OpenSearch](/reference/glossary.md#elasticsearchopensearch). This page applies to both the Orchestration Cluster and Optimize when they connect to Elasticsearch or OpenSearch. ## Prerequisites - A deployed Camunda 8 Self-Managed Helm chart installation - Access to modify container configurations - Basic knowledge of Java development - Maven or Gradle build environment ## Configuration ### Create the Java plugin #### Add the dependency Add the following dependency to a new Java project: ```xml io.camunda camunda-search-client-plugin ${version.camunda-search-client-plugin} provided ``` ```yml implementation "io.camunda:camunda-search-client-plugin:${version.camunda-search-client-plugin}" ``` #### Write your custom header After adding the dependency, create your plugin by implementing the `DatabaseCustomHeaderSupplier` interface provided by the `camunda-search-client-plugin` package. The following example implements the `DatabaseCustomHeaderSupplier` interface, and returns a custom authentication token and UUID: ```java package com.myplugin; public class MyCustomHeaderPlugin implements DatabaseCustomHeaderSupplier { public static final String CUSTOM_TOKEN_PLUGIN = "X-Custom-Auth-Token"; @Override public CustomHeader getSearchDatabaseCustomHeader() { return new CustomHeader(CUSTOM_TOKEN_PLUGIN, UUID.randomUUID().toString()); } } ``` #### Build your project Build your project with all dependencies included, and copy the resulting JAR file to a location accessible by your Camunda installation. This JAR file will be required later during configuration. :::note When building the project, the `camunda-search-client-plugin` dependency must have a scope of `provided`, otherwise there will be a class loader conflict between `camunda-search-client-plugin` classes loaded from different class paths. The JVM treats `ClassA` loaded by `ClassLoaderA` as completely different from `ClassA` loaded by `ClassLoaderB`. Without a `provided` scope, this causes `does not implement` or `ClassCastException` errors. ::: ### Add the plugin to your self-managed installation To use your new plugin, add it to your Camunda 8 Self-Managed installation. - **Mount the plugin**: For each container, mount your plugin JAR file inside the container's file system. For more information, see the [Docker](https://docs.docker.com/engine/storage/volumes/) or [Kubernetes](https://kubernetes.io/docs/concepts/storage/volumes/) documentation. - **Configure components**: Include the plugin parameters in each component's `application.yaml`, or pass them to the component as environment variables. For more information, see how to [configure components using Helm charts](../application-configs.md). ### Example usage The following examples add the new `my-plugin` JAR to the `application.yaml` for the Orchestration Cluster and Optimize: #### Zeebe Exporter ```yaml - ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_ARGS_INTERCEPTORPLUGINS_0_ID=my-plugin - ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_ARGS_INTERCEPTORPLUGINS_0_CLASSNAME=com.myplugin.MyCustomHeaderPlugin - ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_ARGS_INTERCEPTORPLUGINS_0_JARPATH=/usr/local/plugin/plg.jar ``` #### Optimize Importer :::note Due to technical limitations, Optimize currently allows registering up to five plugins. ::: ```yaml - CAMUNDA_OPTIMIZE_ELASTICSEARCH_INTERCEPTORPLUGINS_0_ID=my-plugin - CAMUNDA_OPTIMIZE_ELASTICSEARCH_INTERCEPTORPLUGINS_0_CLASSNAME=com.myplugin.MyCustomHeaderPlugin - CAMUNDA_OPTIMIZE_ELASTICSEARCH_INTERCEPTORPLUGINS_0_JARPATH=/usr/local/plugin/plg.jar ``` #### Zeebe Exporter ```yaml - ZEEBE_BROKER_EXPORTERS_OPENSEARCH_ARGS_INTERCEPTORPLUGINS_0_ID=my-plugin - ZEEBE_BROKER_EXPORTERS_OPENSEARCH_ARGS_INTERCEPTORPLUGINS_0_CLASSNAME=com.myplugin.MyCustomHeaderPlugin - ZEEBE_BROKER_EXPORTERS_OPENSEARCH_ARGS_INTERCEPTORPLUGINS_0_JARPATH=/usr/local/plugin/plg.jar ``` #### Optimize Importer :::note Due to technical limitations, Optimize currently allows registering up to five plugins. ::: ```yaml - CAMUNDA_OPTIMIZE_OPENSEARCH_INTERCEPTORPLUGINS_0_ID=my-plugin - CAMUNDA_OPTIMIZE_OPENSEARCH_INTERCEPTORPLUGINS_0_CLASSNAME=com.myplugin.MyCustomHeaderPlugin - CAMUNDA_OPTIMIZE_OPENSEARCH_INTERCEPTORPLUGINS_0_JARPATH=/usr/local/plugin/plg.jar ``` #### Zeebe Exporter :::note The following configuration uses the default name `camundaExporter`. To use a custom name, update `CAMUNDAEXPORTER` in the provided environment variables to match the name defined in your exporter [configuration](../../../../components/orchestration-cluster/zeebe/exporters/camunda-exporter.md). ::: ```yaml - ZEEBE_BROKER_EXPORTERS_CAMUNDAEXPORTER_ARGS_CONNECT_INTERCEPTORPLUGINS_0_ID=my-plugin - ZEEBE_BROKER_EXPORTERS_CAMUNDAEXPORTER_ARGS_CONNECT_INTERCEPTORPLUGINS_0_CLASSNAME=com.myplugin.MyCustomHeaderPlugin - ZEEBE_BROKER_EXPORTERS_CAMUNDAEXPORTER_ARGS_CONNECT_INTERCEPTORPLUGINS_0_JARPATH=/usr/local/plugin/plg.jar ``` ## Troubleshooting ### Exception: Unknown type of interceptor plugin or wrong class specified This exception means that the incorrect class was specified in the `CLASSNAME` property. Possible causes include: - The class name or package does not exist. - The class does not implement the required SDK interface. - The class is defined as `inner`, `static`, or `final`. To fix this: - Use the latest Search Plugins SDK. - Ensure your class implements the correct SDK interface. - Verify that the plugin class is `public` and not `final`. ### Exception: Failed to load interceptor plugin due to exception This error usually indicates an issue with JAR loading. - Make sure that the path to your plugin JAR file is correct and that the application has permission to read it. - Also confirm that the JAR is valid and contains all required dependencies. To check the contents of your JAR file, run the following command: ```bash jar xf .jar ``` ## References - [Configure components using Helm charts](../application-configs.md) - [Plugin examples](https://github.com/camunda/camunda-search-client-plugins-example) --- ## Configure Elasticsearch and OpenSearch index prefixes Camunda components store operational data in Elasticsearch or OpenSearch indices. By default, Camunda uses the standard index names created by each exporter. This page applies to both the Orchestration Cluster and Optimize when they use Elasticsearch or OpenSearch in Helm deployments. Configure an index prefix when you need to: - Organize indices by grouping related indices under a consistent naming pattern. - Isolate data when multiple Camunda instances share the same Elasticsearch or OpenSearch cluster, so they don’t write to or read from each other’s indices. - Avoid index name collisions in multi-instance environments (for example, separate dev/test/prod installations using one shared cluster). ## Index prefix configuration When Elasticsearch/OpenSearch Exporter indices and Orchestration Cluster indices (secondary storage) share the same cluster, their prefixes must follow all of the rules below. ### Requirements 1. Use unique prefixes – Do not reuse the same prefix for both index types. 2. Avoid prefix relationships between prefixes – Neither prefix may match the other prefix’s wildcard pattern. In other words, the literal string of prefix A must not match `prefixB*`, and the literal string of prefix B must not match `prefixA*`. For example, `custom` (Elasticsearch/OpenSearch Exporter) and `custom-zeebe` (Orchestration Cluster) are unsafe because `custom*` matches both groups, while `custom-index1` and `custom-index2` are safe because `custom-index1*` matches only `custom-index1…` indices and `custom-index2*` matches only `custom-index2…` indices. 3. Avoid reserved names as bare prefixes – Do not use `operate`, `tasklist`, or `camunda` as the full exporter prefix. Using these names as part of a longer custom prefix (for example, `custom-camunda`) is allowed, as long as rules 1 and 2 are still satisfied. ### Configuration properties | Index type | Configuration property | | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Elasticsearch/OpenSearch Exporter indices | `zeebe.broker.exporters.{elasticsearch\|opensearch}.args.index.prefix` (and `ZEEBE_BROKER_EXPORTERS_{ELASTICSEARCH\|OPENSEARCH}_ARGS_INDEX_PREFIX`) | | Orchestration Cluster indices | `camunda.data.secondary-storage.{elasticsearch\|opensearch}.index-prefix` (and `CAMUNDA_DATA_SECONDARYSTORAGE_{ELASTICSEARCH\|OPENSEARCH}_INDEXPREFIX`) | ### Common mistakes to avoid - **Do not** set the Orchestration Cluster index prefix to `zeebe-record` (the default exporter prefix `zeebe.broker.exporters.{elasticsearch|opensearch}.args.index.prefix`). - **Do not** set the exporter prefix to `operate`, `tasklist`, or `camunda`. ### Why this matters Prefixes that violate these rules can cause ILM/ISM policies and wildcard patterns to match unintended indices, potentially leading to unexpected data loss. :::warning Changing an index prefix after a Camunda instance has been running creates new, empty indices with the new prefix. Camunda does not provide built‑in migration support between old and new prefixes. ::: ## Exporters and index prefixes Starting with Camunda 8.8, index prefixes are configured per exporter. Camunda uses two exporters, and each exporter has its own index prefix configuration. ### Camunda Exporter (default) The Camunda Exporter is enabled by default. It creates Orchestration Cluster indices used by Orchestration Cluster applications and APIs, including Operate and Tasklist. - **Helm configuration**: `orchestration.index.prefix` - **Default value**: `""` (empty string, meaning no prefix) - **Controlled by**: `orchestration.exporters.camunda.enabled: true` (default) ### Legacy Zeebe Exporter The Legacy Zeebe Exporter creates `zeebe-record` indices. This exporter is disabled by default. Optimize reads from the `zeebe-record` indices. When Optimize is enabled, the Legacy Zeebe Exporter is automatically enabled to provide these indices. - **Helm configuration**: `global.elasticsearch.prefix` or `global.opensearch.prefix` - **Default value**: `zeebe-record` - **Controlled by**: `orchestration.exporters.zeebe.enabled: false` (default) :::info When the Legacy Zeebe Exporter is used The legacy Zeebe Exporter is automatically enabled when: - Optimize is enabled (`optimize.enabled: true`) - You explicitly enable it (`orchestration.exporters.zeebe.enabled: true`) - Data migration from pre-8.8 versions is required ::: ## Configuration reference | Configuration | Default | Used By | Purpose | | ----------------------------- | -------------- | --------------------------------------- | -------------------------------------------------------- | | `orchestration.index.prefix` | `""` | Camunda Exporter, Orchestration Cluster | Prefix for Orchestration Cluster indices | | `global.elasticsearch.prefix` | `zeebe-record` | Legacy Zeebe Exporter | Prefix for `zeebe-record` indices (consumed by Optimize) | | `global.opensearch.prefix` | `zeebe-record` | Legacy Zeebe Exporter | Prefix for `zeebe-record` indices when using OpenSearch | ### Optimize-specific configuration When you use a custom prefix for `zeebe-record` indices and Optimize is enabled, you must also configure Optimize to use the same prefixes. If these values do not match the exporter prefix exactly, Optimize can start but does not display process data. | Environment Variable | Purpose | | ------------------------------------------------------ | ---------------------------------------------------------------------- | | `CAMUNDA_OPTIMIZE_ELASTICSEARCH_SETTINGS_INDEX_PREFIX` | Prefix for Optimize's own indices (Elasticsearch) | | `CAMUNDA_OPTIMIZE_OPENSEARCH_SETTINGS_INDEX_PREFIX` | Prefix for Optimize's own indices (OpenSearch) | | `CAMUNDA_OPTIMIZE_ZEEBE_NAME` | Must match `global.elasticsearch.prefix` or `global.opensearch.prefix` | ## Configure index prefixes ### Basic configuration (without Optimize) If Optimize is not enabled, configure only the Camunda Exporter prefix. ```yaml orchestration: index: prefix: custom-camunda # Orchestration Cluster indices prefix ``` ```yaml global: elasticsearch: enabled: false opensearch: enabled: true orchestration: index: prefix: custom-camunda # Orchestration Cluster indices prefix ``` ### Full configuration (with Optimize) When Optimize is enabled, configure: - The Legacy Zeebe Exporter prefix (`global.elasticsearch.prefix` or `global.opensearch.prefix`) - The Camunda Exporter prefix (`orchestration.index.prefix`) - Optimize environment variables so Optimize can find the correct indices ```yaml global: elasticsearch: enabled: true prefix: custom-zeebe # Legacy Zeebe Exporter prefix (read by Optimize) orchestration: index: prefix: custom-camunda # Camunda Exporter prefix optimize: enabled: true env: - name: CAMUNDA_OPTIMIZE_ELASTICSEARCH_SETTINGS_INDEX_PREFIX value: custom-optimize # Optimize's own indices - name: CAMUNDA_OPTIMIZE_ZEEBE_NAME value: custom-zeebe # Must match global.elasticsearch.prefix ``` ```yaml global: elasticsearch: enabled: false opensearch: enabled: true prefix: custom-zeebe # Legacy Zeebe Exporter prefix (read by Optimize) orchestration: index: prefix: custom-camunda # Camunda Exporter prefix optimize: enabled: true env: - name: CAMUNDA_OPTIMIZE_OPENSEARCH_SETTINGS_INDEX_PREFIX value: custom-optimize # Optimize's own indices - name: CAMUNDA_OPTIMIZE_ZEEBE_NAME value: custom-zeebe # Must match global.opensearch.prefix migration: env: - name: CAMUNDA_OPTIMIZE_OPENSEARCH_SETTINGS_INDEX_PREFIX value: custom-optimize - name: CAMUNDA_OPTIMIZE_ZEEBE_NAME value: custom-zeebe ``` ### Elasticsearch ```sh # Camunda Exporter - Orchestration Cluster indices prefix CAMUNDA_DATA_SECONDARYSTORAGE_ELASTICSEARCH_INDEXPREFIX=custom-camunda # Legacy Zeebe Exporter - zeebe-record indices prefix (for Optimize) ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_ARGS_INDEX_PREFIX=custom-zeebe # Optimize indices prefix (when Optimize is enabled) CAMUNDA_OPTIMIZE_ELASTICSEARCH_SETTINGS_INDEX_PREFIX=custom-optimize CAMUNDA_OPTIMIZE_ZEEBE_NAME=custom-zeebe ``` For example, recommended: ```bash ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_ARGS_INDEX_PREFIX=custom-zeebe CAMUNDA_DATA_SECONDARYSTORAGE_ELASTICSEARCH_INDEXPREFIX=custom-camunda ``` Not allowed (will cause conflicts): ```bash ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_ARGS_INDEX_PREFIX=shared-prefix CAMUNDA_DATA_SECONDARYSTORAGE_ELASTICSEARCH_INDEXPREFIX=shared-prefix ``` ### OpenSearch ```sh # Camunda Exporter - Orchestration Cluster indices prefix CAMUNDA_DATA_SECONDARYSTORAGE_OPENSEARCH_INDEXPREFIX=custom-camunda # Legacy Zeebe Exporter - zeebe-record indices prefix (for Optimize) ZEEBE_BROKER_EXPORTERS_OPENSEARCH_ARGS_INDEX_PREFIX=custom-zeebe # Optimize indices prefix (when Optimize is enabled) CAMUNDA_OPTIMIZE_OPENSEARCH_SETTINGS_INDEX_PREFIX=custom-optimize CAMUNDA_OPTIMIZE_ZEEBE_NAME=custom-zeebe ``` For example, recommended: ```bash ZEEBE_BROKER_EXPORTERS_OPENSEARCH_ARGS_INDEX_PREFIX=custom-zeebe CAMUNDA_DATA_SECONDARYSTORAGE_OPENSEARCH_INDEXPREFIX=custom-camunda ``` Not allowed (will cause conflicts): ```bash ZEEBE_BROKER_EXPORTERS_OPENSEARCH_ARGS_INDEX_PREFIX=shared-prefix CAMUNDA_DATA_SECONDARYSTORAGE_OPENSEARCH_INDEXPREFIX=shared-prefix ``` ### Elasticsearch ```yaml camunda: data: secondary-storage: elasticsearch: index-prefix: custom-camunda # Camunda Exporter prefix zeebe: broker: exporters: elasticsearch: args: index: prefix: custom-zeebe # Legacy Zeebe Exporter prefix ``` ### OpenSearch ```yaml camunda: data: secondary-storage: opensearch: index-prefix: custom-camunda # Camunda Exporter prefix zeebe: broker: exporters: opensearch: args: index: prefix: custom-zeebe # Legacy Zeebe Exporter prefix ``` ## References - [Use external Elasticsearch](/self-managed/deployment/helm/configure/database/elasticsearch/using-external-elasticsearch.md) - [Use external OpenSearch](/self-managed/deployment/helm/configure/database/using-external-opensearch.md) --- ## Use external Elasticsearch for Orchestration Cluster with Helm Configure the Orchestration Cluster in Camunda 8 Self-Managed to connect to an external Elasticsearch instance as a secondary storage backend. Elasticsearch is used for indexing and querying operational data consumed by Orchestration Cluster applications and APIs. For a canonical definition, see [Elasticsearch/OpenSearch](/reference/glossary.md#elasticsearchopensearch). Starting with Camunda 8.9, the Helm chart no longer provisions Elasticsearch by default. To use Elasticsearch as secondary storage for the Orchestration Cluster, explicitly configure it in your Helm values under `orchestration.data.secondaryStorage.elasticsearch`. You can either deploy Elasticsearch using the [ECK operator](/self-managed/deployment/helm/configure/operator-based-infrastructure.md#elasticsearch-deployment) (recommended) or connect Camunda to an existing external Elasticsearch instance, either running inside the same Kubernetes cluster or outside it. This page applies to the Orchestration Cluster only. If you also deploy Optimize, configure Optimize separately using [use external Elasticsearch for Optimize with Helm](/self-managed/deployment/helm/configure/database/optimize/using-external-elasticsearch.md). :::note The bundled Elasticsearch Bitnami subchart (`elasticsearch.enabled: true`) is deprecated and will be removed in a future release. For production deployments, use the [ECK (Elastic Cloud on Kubernetes) operator](/self-managed/deployment/helm/configure/operator-based-infrastructure.md#elasticsearch-deployment) or a managed Elasticsearch service instead. See [deploy required dependencies with Kubernetes operators](/self-managed/deployment/helm/configure/operator-based-infrastructure.md) for details. ::: ## Prerequisites Before configuring, collect the following information about your external Elasticsearch instance: - URL to access the cluster (protocol, host, and port) - Authentication requirements and credentials (if needed) - TLS requirements: - Whether the certificate is publicly trusted - Whether you need to provide a custom or self-signed certificate ## Configuration ### Parameters #### Orchestration Cluster secondary storage | values.yaml option | type | default | description | | --------------------------------------------------------------------------------- | ------ | ------- | ----------------------------------------------------------------------------------------------------- | | `orchestration.data.secondaryStorage.type` | string | `""` | Type of secondary storage. Set to `elasticsearch` to use Elasticsearch. | | `orchestration.data.secondaryStorage.elasticsearch.url` | string | `""` | URL for the Elasticsearch cluster as `scheme://host:port` (for example, `http://elasticsearch:9200`). | | `orchestration.data.secondaryStorage.elasticsearch.auth.username` | string | `""` | Username for Elasticsearch authentication. | | `orchestration.data.secondaryStorage.elasticsearch.auth.secret.inlineSecret` | string | `""` | Elasticsearch password as a plain-text value (non-production only). | | `orchestration.data.secondaryStorage.elasticsearch.auth.secret.existingSecret` | string | `""` | Reference to an existing Kubernetes Secret containing the password. | | `orchestration.data.secondaryStorage.elasticsearch.auth.secret.existingSecretKey` | string | `""` | Key within the existing Kubernetes Secret containing the password. | | `orchestration.data.secondaryStorage.elasticsearch.tls.secret.existingSecret` | string | `""` | Reference to an existing Kubernetes Secret containing the TLS trust store. | | `orchestration.data.secondaryStorage.elasticsearch.tls.secret.existingSecretKey` | string | `""` | Key within the existing Kubernetes Secret for the TLS trust store. | | `orchestration.index.prefix` | string | `""` | Index prefix in Elasticsearch for the new Camunda exporter and the Orchestration Cluster. | #### Bundled Elasticsearch subchart (deprecated) | values.yaml option | type | default | description | | ----------------------- | ------- | ------- | ------------------------------------------------------- | | `elasticsearch.enabled` | boolean | `false` | Enables or disables the bundled Elasticsearch subchart. | ### Example usage #### Connect to external Elasticsearch without a certificate Configure the Orchestration Cluster as follows: ```yaml orchestration: data: secondaryStorage: type: elasticsearch elasticsearch: url: http://elastic.example.com:443 auth: username: elastic secret: inlineSecret: pass elasticsearch: enabled: false ``` #### Connect to external Elasticsearch with a self-signed certificate If the Elasticsearch cluster accepts only `https` requests with a self-signed certificate: 1. Create an `externaldb.jks` file from the Elasticsearch certificate file. For example, using the `keytool` CLI: ```yaml keytool -import -alias elasticsearch -keystore externaldb.jks -storetype jks -file elastic.crt -storepass changeit -noprompt ``` 1. Create a Kubernetes secret from the `externaldb.jks` file before installing Camunda: ```yaml kubectl create secret -n camunda generic elastic-jks --from-file=externaldb.jks ``` 1. Configure the Camunda 8 Self-Managed Helm chart: ```yaml orchestration: data: secondaryStorage: type: elasticsearch elasticsearch: url: https://elastic.example.com:443 auth: username: elastic secret: inlineSecret: pass tls: secret: existingSecret: elastic-jks existingSecretKey: externaldb.jks elasticsearch: enabled: false ``` ### Connect to external Elasticsearch with a publicly trusted certificate This configuration works with managed Elasticsearch services. It has been tested with Elastic Cloud on Google Cloud. ```yaml orchestration: data: secondaryStorage: type: elasticsearch elasticsearch: url: https://elastic.example.com:443 auth: username: elastic secret: inlineSecret: pass elasticsearch: enabled: false ``` ### Connect to external Elasticsearch with custom index prefixes When running multiple Camunda instances on a shared Elasticsearch cluster, use custom index prefixes to isolate data: ```yaml orchestration: data: secondaryStorage: type: elasticsearch elasticsearch: url: https://elastic.example.com:443 auth: username: elastic secret: inlineSecret: pass index: prefix: my-env-camunda # Prefix for Orchestration Cluster indices elasticsearch: enabled: false ``` For more details on index prefix configuration, including Optimize-specific settings when Optimize is enabled, see [prefix Elasticsearch/OpenSearch indices](/self-managed/deployment/helm/configure/database/elasticsearch/configure-elasticsearch-prefix-indices.md). ## Troubleshooting If Zeebe pods fail, check for the following error: - The host is unreachable or DNS is not properly resolving to an IP address listening on the specified port. ```text Caused by: java.net.UnknownHostException: elastic.example.com ``` ## References - [Camunda production installation guide with Kubernetes and Helm](versioned_docs/version-8.7/self-managed/operational-guides/production-guide/helm-chart-production-guide.md) (8.8 version not yet available) - [Use external Elasticsearch for Optimize with Helm](/self-managed/deployment/helm/configure/database/optimize/using-external-elasticsearch.md) - [Configure Elasticsearch and OpenSearch index prefixes](/self-managed/deployment/helm/configure/database/elasticsearch/configure-elasticsearch-prefix-indices.md) ## Next steps Use the custom values file to [deploy Camunda 8](/self-managed/setup/overview.md): ```sh helm install camunda camunda/camunda-platform --version $HELM_CHART_VERSION -f existing-elasticsearch-values.yaml ``` --- ## Helm chart database configuration :::tip Need end-to-end guidance for RDBMS? For a unified setup guide covering provisioning, topology decisions, driver management, and backup strategies across Orchestration Cluster and Web Modeler, see the [end-to-end RDBMS setup guide](/self-managed/concepts/databases/relational-db/rdbms-setup-guide.md). This guide is useful both when starting a new setup and when harmonizing existing component configurations. ::: :::tip Choosing a secondary storage backend? Use the [secondary storage overview](/self-managed/concepts/secondary-storage/index.md) as the navigation hub for Elasticsearch/OpenSearch and RDBMS paths. ::: Use this section to configure database layers for Helm deployments. This section is organized by component: - [Orchestration Cluster](/self-managed/deployment/helm/configure/database/non-sql.md): Configure Elasticsearch or OpenSearch as secondary storage, or use [RDBMS](/self-managed/deployment/helm/configure/database/rdbms.md). - [Management Identity and Web Modeler](/self-managed/deployment/helm/configure/database/using-existing-postgres.md): Configure PostgreSQL for management components. - [Optimize](/self-managed/deployment/helm/configure/database/optimize/index.md): Configure Elasticsearch or OpenSearch for Optimize. Some Elasticsearch/OpenSearch tasks, such as custom headers and index prefixes, apply to both the Orchestration Cluster and Optimize. Those shared pages call that out explicitly. --- ## Configure Elasticsearch and OpenSearch for Orchestration Cluster in Helm Use this page as the navigation hub for Elasticsearch and OpenSearch configuration for the Orchestration Cluster in Helm deployments. This page applies to the Orchestration Cluster only. If you also deploy Optimize, use the dedicated [Optimize database configuration](/self-managed/deployment/helm/configure/database/optimize/index.md) pages for Optimize-specific settings. ## Configure secondary storage backends Use the following pages based on your backend: - [Use external Elasticsearch with Helm](/self-managed/deployment/helm/configure/database/elasticsearch/using-external-elasticsearch.md) - [Use Amazon OpenSearch Service with the Helm chart](/self-managed/deployment/helm/configure/database/using-external-opensearch.md) ## Shared Elasticsearch and OpenSearch tasks Use the following pages when you need settings that can apply to both the Orchestration Cluster and Optimize: - [Configure custom HTTP headers for database clients](/self-managed/deployment/helm/configure/database/configure-db-custom-headers.md) - [Configure Elasticsearch and OpenSearch index prefixes](/self-managed/deployment/helm/configure/database/elasticsearch/configure-elasticsearch-prefix-indices.md) ## Troubleshooting - [All shards failed errors](/self-managed/deployment/helm/configure/database/all-shards-failed.md) ## Related concepts - [Secondary storage overview](/self-managed/concepts/secondary-storage/index.md) - [Elasticsearch/OpenSearch](/reference/glossary.md#elasticsearchopensearch) --- ## Configure Optimize databases in Helm chart Use this section to configure Optimize's database connection in Helm deployments. Optimize supports Elasticsearch or OpenSearch only. It does not support RDBMS. This section applies to Optimize only. If you also need to configure Elasticsearch or OpenSearch for the Orchestration Cluster, use the [Orchestration Cluster Elasticsearch/OpenSearch pages](/self-managed/deployment/helm/configure/database/non-sql.md). Use the following pages based on your backend: - [Use external Elasticsearch for Optimize with Helm](/self-managed/deployment/helm/configure/database/optimize/using-external-elasticsearch.md) - [Use external OpenSearch for Optimize with Helm](/self-managed/deployment/helm/configure/database/optimize/using-external-opensearch.md) Shared Elasticsearch and OpenSearch tasks: - [Configure custom HTTP headers for database clients](/self-managed/deployment/helm/configure/database/configure-db-custom-headers.md) - [Configure Elasticsearch and OpenSearch index prefixes](/self-managed/deployment/helm/configure/database/elasticsearch/configure-elasticsearch-prefix-indices.md) For background on secondary storage choices and exporter behavior, see [secondary storage overview](/self-managed/concepts/secondary-storage/index.md). --- ## Use external Elasticsearch for Optimize with Helm Configure Optimize in Camunda 8 Self-Managed to connect to an external Elasticsearch instance when deploying with Helm. This page applies to Optimize only. If the Orchestration Cluster also uses Elasticsearch as secondary storage, configure that separately using [use external Elasticsearch for Orchestration Cluster with Helm](/self-managed/deployment/helm/configure/database/elasticsearch/using-external-elasticsearch.md). Optimize supports Elasticsearch only through Elasticsearch or OpenSearch backends. It does not support RDBMS. ## Prerequisites Before configuring, collect the following information about your external Elasticsearch instance: - URL to access the cluster (protocol, host, and port) - Authentication requirements and credentials (if needed) - TLS requirements: - Whether the certificate is publicly trusted - Whether you need to provide a custom or self-signed certificate ## Configuration ### Parameters Use the following Helm values for Optimize's Elasticsearch connection: | values.yaml option | type | default | description | | --------------------------------------------------------------- | ------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `optimize.database.elasticsearch.enabled` | boolean | `false` | Enables Elasticsearch for Optimize. | | `optimize.database.elasticsearch.external` | boolean | `false` | Set to `true` to connect to an external Elasticsearch instance. | | `optimize.database.elasticsearch.auth.username` | string | `""` | Username for external Elasticsearch authentication. | | `optimize.database.elasticsearch.auth.secret.inlineSecret` | string | `""` | Elasticsearch password as a plain-text value for non-production environments only. | | `optimize.database.elasticsearch.auth.secret.existingSecret` | string | `""` | Reference to an existing Kubernetes Secret containing the password. | | `optimize.database.elasticsearch.auth.secret.existingSecretKey` | string | `""` | Key within the existing Kubernetes Secret containing the password. | | `optimize.database.elasticsearch.prefix` | string | `zeebe-record` | Index prefix for `zeebe-record` indices. See [configure Elasticsearch and OpenSearch index prefixes](/self-managed/deployment/helm/configure/database/elasticsearch/configure-elasticsearch-prefix-indices.md). | | `optimize.database.elasticsearch.tls.enabled` | boolean | `false` | Enables TLS when connecting to Elasticsearch. | | `optimize.database.elasticsearch.tls.secret.existingSecret` | string | `""` | Name of the Kubernetes Secret containing a TLS certificate. | | `optimize.database.elasticsearch.tls.secret.existingSecretKey` | string | `externaldb.jks` | Key within the secret containing the TLS certificate. | | `optimize.database.elasticsearch.url.protocol` | string | `""` | Protocol to use when connecting to Elasticsearch. Possible values are `http` and `https`. | | `optimize.database.elasticsearch.url.host` | string | `""` | Hostname or IP address of the Elasticsearch instance. | | `optimize.database.elasticsearch.url.port` | integer | `0` | Port number of the Elasticsearch instance. | ### Example usage #### Connect Optimize to external Elasticsearch without a certificate ```yaml optimize: enabled: true database: elasticsearch: enabled: true external: true auth: username: elastic secret: inlineSecret: pass url: protocol: http host: elastic.example.com port: 443 elasticsearch: enabled: false ``` #### Connect Optimize to external Elasticsearch with a self-signed certificate If the Elasticsearch cluster accepts only `https` requests with a self-signed certificate: 1. Create an `externaldb.jks` file from the Elasticsearch certificate file. For example, using the `keytool` CLI: ```yaml keytool -import -alias elasticsearch -keystore externaldb.jks -storetype jks -file elastic.crt -storepass changeit -noprompt ``` 1. Create a Kubernetes secret from the `externaldb.jks` file before installing Camunda: ```yaml kubectl create secret -n camunda generic elastic-jks --from-file=externaldb.jks ``` 1. Configure Optimize: ```yaml optimize: enabled: true database: elasticsearch: enabled: true external: true tls: enabled: true secret: existingSecret: elastic-jks auth: username: elastic secret: inlineSecret: pass url: protocol: https host: elastic.example.com port: 443 elasticsearch: enabled: false ``` #### Connect Optimize to external Elasticsearch with a publicly trusted certificate ```yaml optimize: enabled: true database: elasticsearch: enabled: true external: true auth: username: elastic secret: inlineSecret: pass url: protocol: https host: elastic.example.com port: 443 elasticsearch: enabled: false ``` ## Related tasks - [Configure Elasticsearch and OpenSearch index prefixes](/self-managed/deployment/helm/configure/database/elasticsearch/configure-elasticsearch-prefix-indices.md) - [Configure custom HTTP headers for database clients](/self-managed/deployment/helm/configure/database/configure-db-custom-headers.md) - [Use external Elasticsearch for Orchestration Cluster with Helm](/self-managed/deployment/helm/configure/database/elasticsearch/using-external-elasticsearch.md) --- ## Use external OpenSearch for Optimize with Helm Configure Optimize in Camunda 8 Self-Managed to use OpenSearch when deploying with the Helm chart. This page applies to Optimize only. If the Orchestration Cluster also uses OpenSearch as secondary storage, configure that separately using [use Amazon OpenSearch Service for Orchestration Cluster with Helm](/self-managed/deployment/helm/configure/database/using-external-opensearch.md). Optimize supports Elasticsearch or OpenSearch only. It does not support RDBMS. :::info OpenSearch support Camunda 8 supports both the open-source [OpenSearch](https://opensearch.org/) distribution and [Amazon OpenSearch Service](https://aws.amazon.com/opensearch-service). ::: ## Prerequisites Amazon OpenSearch requires two layers of permissions: - AWS IAM permissions - OpenSearch internal authentication To connect to OpenSearch using AWS IAM roles for service accounts (IRSA), see the [IAM roles for service accounts documentation](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/terraform-setup.md#opensearch-module-setup). To connect to OpenSearch using Basic authentication, follow the configuration below. ## Configuration ### Parameters | Parameter | Type | Default | Description | | ------------------------------------------------------------ | ------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `optimize.database.opensearch.enabled` | boolean | `false` | Enable external OpenSearch. | | `optimize.database.opensearch.aws.enabled` | boolean | `false` | Enable AWS IRSA integration. | | `optimize.database.opensearch.auth.username` | string | `""` | Username for external OpenSearch authentication. | | `optimize.database.opensearch.auth.secret.inlineSecret` | string | `""` | OpenSearch password as a plain-text value (non-production only). | | `optimize.database.opensearch.auth.secret.existingSecret` | string | `""` | Reference to an existing Kubernetes Secret containing the password. | | `optimize.database.opensearch.auth.secret.existingSecretKey` | string | `""` | Key within the existing Kubernetes Secret containing the password. | | `optimize.database.opensearch.prefix` | string | `zeebe-record` | Index prefix for `zeebe-record` indices. See [Configure Elasticsearch and OpenSearch index prefixes](/self-managed/deployment/helm/configure/database/elasticsearch/configure-elasticsearch-prefix-indices.md). | | `optimize.database.opensearch.tls.enabled` | boolean | `false` | Enable TLS for external OpenSearch. | | `optimize.database.opensearch.tls.secret.existingSecret` | string | `""` | Name of the Kubernetes Secret containing a TLS certificate. | | `optimize.database.opensearch.tls.secret.existingSecretKey` | string | `externaldb.jks` | Key within the secret containing the TLS certificate. | | `optimize.database.opensearch.url.protocol` | string | `""` | Access protocol for OpenSearch. Possible values are `http` and `https`. | | `optimize.database.opensearch.url.host` | string | `""` | OpenSearch host, ideally the service name within the namespace. | | `optimize.database.opensearch.url.port` | integer | `0` | Port used to access OpenSearch. | ### Example usage ```yaml optimize: enabled: true database: opensearch: enabled: true auth: username: user secret: # For non-production environments only: inlineSecret: "your-password-here" # For production (recommended): # existingSecret: "opensearch-secret" # existingSecretKey: "password" url: protocol: https host: opensearch.example.com port: 443 ``` To avoid storing the username and password in plaintext in your `values.yaml`, reference a Kubernetes secret. For details and examples, see [Helm charts secret management](/self-managed/deployment/helm/configure/secret-management.md). ### Connect Optimize to external OpenSearch with custom index prefixes When running multiple Camunda instances on a shared OpenSearch cluster, use custom index prefixes to isolate data: ```yaml optimize: enabled: true database: opensearch: enabled: true prefix: my-env-zeebe auth: username: admin secret: inlineSecret: pass url: protocol: https host: opensearch.example.com port: 443 ``` For more details about index prefix configuration and matching exporter settings, see [configure Elasticsearch and OpenSearch index prefixes](/self-managed/deployment/helm/configure/database/elasticsearch/configure-elasticsearch-prefix-indices.md). ## References - [Helm charts secret management](/self-managed/deployment/helm/configure/secret-management.md) - [IAM roles for service accounts](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/terraform-setup.md#opensearch-module-setup) - [Configure Elasticsearch and OpenSearch index prefixes](/self-managed/deployment/helm/configure/database/elasticsearch/configure-elasticsearch-prefix-indices.md) - [Use Amazon OpenSearch Service for Orchestration Cluster with Helm](/self-managed/deployment/helm/configure/database/using-external-opensearch.md) --- ## JDBC driver management for RDBMS This page covers JDBC driver management for RDBMS deployments in Kubernetes. For background on secondary storage, see [secondary storage overview](/self-managed/concepts/secondary-storage/index.md). For configuration and troubleshooting, see [configure RDBMS in Helm charts](/self-managed/deployment/helm/configure/database/rdbms.md). For an end-to-end example, see [RDBMS example deployment](/self-managed/deployment/helm/install/helm-with-rdbms.md). ## Bundled vs. custom JDBC drivers ### Which drivers are included? Camunda bundles JDBC drivers for databases where licensing permits: | Database | Bundled | When to supply custom drivers | | ---------- | ------- | -------------------------------------------------------------------------------------------- | | PostgreSQL | Yes | Patches, extensions, or compatibility with older server versions. | | MariaDB | Yes | Custom JDBC features or compliance requirements. | | SQL Server | Yes | Custom features or version-specific requirements. | | H2 | Yes | Development and testing only; not recommended for production due to scalability limitations. | | Oracle | No | Always; licensing prevents bundling. | | MySQL | No | Always; licensing prevents bundling. | ### When to supply a custom driver Consider supplying a custom JDBC driver in these scenarios: 1. **Oracle or MySQL databases**: No bundled drivers available; custom drivers required. 2. **Version compatibility**: Your database version is not compatible with the bundled driver. 3. **Security patches**: A critical patch is available for the bundled driver before the next Camunda release. 4. **Custom extensions**: You use database-specific features not covered by bundled drivers. 5. **Compliance or licensing**: Your organization policy requires specific driver versions or sources. ### Driver provisioning strategies Choose one of the three approaches below. **Init container is recommended for production.** | Strategy | Pros | Cons | Best for | | -------------------- | --------------------------------------- | ---------------------------------- | ----------------------- | | **Init container** | Automatic at pod startup; reproducible. | Requires external download source. | Production (standard) | | **Custom image** | Simple for teams with image registries. | Not yet validated in production. | Dev/test only | | **ConfigMap/Volume** | GitOps-friendly; no external downloads. | Requires manual driver management. | Teams with restrictions | ## Loading JDBC drivers into pods :::note The following applies to each of the components that require JDBC drivers: Orchestration Cluster, Identity, and Web Modeler, though examples may only reference Orchestration Cluster. ::: Some databases—such as Oracle and MySQL—require JDBC drivers that cannot be included in the Camunda image due to licensing restrictions. You must provide these drivers at runtime using one of the following approaches. ### Option 1: Using an init container :::note This example uses `/driver-lib`, which the Orchestration Cluster automatically adds to the classpath. If you use a different directory, additional override configuration may be required. ::: ```yaml orchestration: exporters: camunda: enabled: false rdbms: enabled: true data: secondaryStorage: type: rdbms rdbms: url: jdbc:oracle:thin:@//hostname:1521/FREEPDB1 username: myuser secret: inlineSecret: mypassword extraVolumeMounts: - name: jdbcdrivers mountPath: /driver-lib extraVolumes: - name: jdbcdrivers emptyDir: {} initContainers: - name: fetch-jdbc-drivers image: alpine:3.19 imagePullPolicy: Always command: - sh - -c - > wget https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc11/23.9.0.25.07/ojdbc11-23.9.0.25.07.jar -O /driver-lib/ojdbc.jar volumeMounts: - name: jdbcdrivers mountPath: /driver-lib securityContext: runAsUser: 1001 ``` After loading JDBC drivers into pods, run the validation checklist in [validate RDBMS connectivity](/self-managed/deployment/helm/configure/database/validate-rdbms.md) to confirm the application can load the driver, reach the database, and initialize schema. ### Option 2: Using a custom Docker image :::note This is a custom image approach. For production, prefer the init-container method to stay aligned with supported Helm patterns. ::: ```dockerfile FROM camunda/camunda-platform:8.9.0 ADD ojdbc8.jar /driver-lib/ojdbc8.jar ``` Build and push: ```sh docker build -t internal-registry/orchestration:8.9.0 . docker push internal-registry/orchestration:8.9.0 ``` Configure in Helm: ```yaml orchestration: exporters: camunda: enabled: false rdbms: enabled: true image: repository: internal-registry/orchestration tag: 8.9.0 data: secondaryStorage: type: rdbms rdbms: url: jdbc:oracle:thin:@//hostname:1521/FREEPDB1 username: myuser secret: inlineSecret: mypassword ``` ### Option 3: Mounting a JDBC driver from a volume :::warning Important Mounting an `emptyDir volume` does not persist across pod restarts. Use a ConfigMap, PersistentVolume, or custom image for production. ::: ```yaml orchestration: exporters: camunda: enabled: false rdbms: enabled: true data: secondaryStorage: type: rdbms rdbms: url: jdbc:oracle:thin:@//hostname:1521/FREEPDB1 username: myuser secret: inlineSecret: mypassword extraVolumeMounts: - name: jdbcdrivers mountPath: /driver-lib extraVolumes: - name: jdbcdrivers emptyDir: {} ``` Copy the driver manually to the pod: ```sh kubectl cp /path/to/ojdbc8.jar :/driver-lib/ojdbc8.jar ``` ## Verifying driver loading After deployment, verify the JDBC driver was loaded: ```bash # Check that the driver JAR exists kubectl exec -- ls -la /driver-lib/ # Check logs for successful driver initialization kubectl logs | grep -i "driver\|jdbc" ``` Common success indicators in logs: ``` INFO org.springframework.boot.StartupInfoLogger - Started Application in X seconds INFO io.camunda.exporter.rdbms.RdbmsExporter - RdbmsExporter created with Configuration ``` Common failure indicators: ``` java.lang.ClassNotFoundException: oracle.jdbc.OracleDriver java.sql.SQLException: No suitable driver found ``` If you see failures, verify: 1. The driver JAR file is present in `/driver-lib/`. 2. The init container or custom image executed successfully. 3. The JDBC URL in your configuration matches the driver (for example, Oracle URL with Oracle driver). ## JDBC driver updates ### Updating bundled drivers Bundled drivers are updated with new Camunda releases. To update: 1. Identify the new Camunda version with the updated driver. 2. Upgrade Camunda: `helm upgrade camunda camunda/camunda-platform --version X.Y.Z -f values.yaml -n camunda`. ### Updating custom drivers If you're supplying a custom driver, update it by: 1. **Init container approach**: Update the driver download URL or image in your Helm values. 2. **Custom image approach**: Rebuild and push a new image with the updated driver. 3. **ConfigMap/Volume approach**: Update the driver JAR in your ConfigMap or PersistentVolume. Then redeploy: ```bash helm upgrade camunda camunda/camunda-platform -f values.yaml -n camunda ``` Kubernetes will recreate the pods with the new driver. --- ## Schema creation and management This page covers schema creation, upgrades, and management for RDBMS deployments. For configuration reference and troubleshooting, see [configure RDBMS in Helm charts](/self-managed/deployment/helm/configure/database/rdbms.md). :::note Related pages - **[Access SQL and Liquibase scripts](access-sql-liquibase-scripts.md)** - Where to find and download schema scripts. - **[Validate RDBMS connectivity](validate-rdbms.md)** - Verify schema and exporter after deployment. - **[JDBC drivers](rdbms-jdbc-drivers.md)** - Managing database drivers. ::: ## Automatic schema creation (autoDDL) By default, `autoDDL: true` enables automatic schema creation via Liquibase. This happens at pod startup: 1. Liquibase detects that the schema does not exist or is outdated. 2. Liquibase executes all SQL migrations to initialize the schema. 3. The exporter begins writing data. **Prerequisites for autoDDL:** For all databases, the database user must have `CREATE TABLE`, `ALTER TABLE`, and `DROP TABLE` permissions. Additional database-specific requirements: - **PostgreSQL**: `CREATE` permission on the database. - **Oracle**: `CREATE TABLE` and `TABLESPACE` (if using non-default tablespaces). - **SQL Server**: `CREATE TABLE`, `ALTER TABLE`, and `CONTROL` on the schema. - **MariaDB/MySQL**: `ALL PRIVILEGES` on the target database. ## Database user permissions ### PostgreSQL ```sql CREATE ROLE camunda WITH LOGIN PASSWORD 'password'; GRANT CONNECT ON DATABASE camunda TO camunda; GRANT USAGE ON SCHEMA public TO camunda; GRANT CREATE ON DATABASE camunda TO camunda; ``` ### Oracle ```sql CREATE USER camunda IDENTIFIED BY password; GRANT CREATE TABLE TO camunda; GRANT UNLIMITED TABLESPACE TO camunda; ``` ### MariaDB/MySQL ```sql CREATE USER camunda@'%' IDENTIFIED BY 'password'; GRANT ALL PRIVILEGES ON camunda.* TO camunda@'%'; FLUSH PRIVILEGES; ``` ### SQL Server ```sql CREATE LOGIN camunda WITH PASSWORD = 'password'; CREATE USER camunda FOR LOGIN camunda; GRANT CREATE TABLE TO camunda; GRANT ALTER ON SCHEMA::dbo TO camunda; ``` ## Manual schema management If your database is managed by a dedicated DBA, disable autoDDL and manage schema updates manually: ```yaml orchestration: extraConfiguration: - file: "manual-schema-management.yaml" content: | camunda: data: secondary-storage: rdbms: auto-ddl: false ``` With `autoDDL: false`, you must: 1. Apply SQL scripts to the database before deploying Camunda. 2. SQL scripts are available in the Camunda release bundle or from the [Liquibase scripts page](/self-managed/deployment/helm/configure/database/access-sql-liquibase-scripts.md). ### When to use manual schema management - Your organization requires a separate schema deployment phase. - A dedicated DBA manages the database and DDL changes. - You need to validate schema changes before applying them to production. ## Schema verification After initial deployment or upgrade, verify the schema: ```sql -- PostgreSQL: Check tables exist SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'; -- MySQL/MariaDB: Check tables exist SELECT table_name FROM information_schema.tables WHERE table_schema = DATABASE(); -- Oracle: Check tables SELECT table_name FROM user_tables; -- SQL Server: Check tables SELECT table_name FROM information_schema.tables WHERE table_schema = 'dbo'; ``` Expected tables include workflow and history tables (for example, `process_instance`, `variable`, and `job`) and Liquibase metadata tables like `databasechangelog` and `databasechangeloglock`. You can also verify by checking logs: ```bash kubectl logs | grep -i liquibase ``` Success indicators: ``` INFO io.camunda.application.commons.rdbms.MyBatisConfiguration - Initializing Liquibase for RDBMS INFO org.springframework.web.servlet.DispatcherServlet - Completed initialization in X ms ``` ## Upgrading the schema When upgrading Camunda versions: ### Step 1: Backup your database Backup your database before upgrading. Use your database vendor's native tools: - **PostgreSQL**: [pg_dump documentation](https://www.postgresql.org/docs/current/app-pgdump.html) - **Oracle**: [EXPDP documentation](https://docs.oracle.com/en/database/oracle/oracle-database/sutil/oracle-data-pump-export-utility.html) - **MySQL**: [mysqldump documentation](https://dev.mysql.com/doc/refman/en/mysqldump.html) - **MariaDB**: [mariadb-dump documentation](https://mariadb.com/kb/en/mariadb-dump/) - **SQL Server**: [SQL Server backup documentation](https://learn.microsoft.com/en-us/sql/relational-databases/backup-restore/back-up-and-restore-of-sql-server-databases) ### Step 2: Test the upgrade in staging Deploy the new Camunda version in a staging environment first to validate schema migrations. ### Step 3: Scale down deployment (optional but recommended) For large production clusters, scale down to avoid connection pool exhaustion during migration: ```bash kubectl scale deployment camunda-orchestration --replicas=0 -n camunda ``` ### Step 4: Deploy the new Camunda version ```bash helm upgrade camunda camunda/camunda-platform --version X.Y.Z -f values.yaml -n camunda ``` Liquibase will automatically apply new migrations if `autoDDL: true`. ### Step 5: Scale back up ```bash kubectl scale deployment camunda-orchestration --replicas=3 -n camunda ``` Monitor the rollout: ```bash kubectl rollout status deployment/camunda-orchestration -n camunda ``` ### Step 6: Verify schema initialization Verify that Liquibase completed the schema migration successfully by checking logs: ```bash kubectl logs | grep -i liquibase ``` Look for "Liquibase: Update successful" or similar completion messages. If the migration fails, Liquibase will log the specific error. You can also verify schema initialization by checking the `databasechangelog` table: ```sql -- Verify Liquibase changelog table exists and contains entries SELECT COUNT(*) FROM databasechangelog; ``` This table should exist and contain entries for a fresh Camunda 8.9 installation. On upgrades, this number increases as new changesets are applied. For troubleshooting, see [schema troubleshooting](#schema-troubleshooting). ## Schema troubleshooting ### Liquibase lock issues If a previous schema migration failed, Liquibase may hold a lock. Camunda waits for stale Liquibase DDL locks using `camunda.data.secondary-storage.rdbms.ddl-lock-wait-timeout` (default: `PT15M`). For large schema changes, you can increase this timeout so a long-running migration is not treated as stale. See [RDBMS troubleshooting](/self-managed/deployment/helm/configure/database/rdbms-troubleshooting.md#liquibase-lock-after-pod-crash-or-restart). Only release the lock manually after confirming no migration is currently running: ```sql -- PostgreSQL/MariaDB: Release the lock DELETE FROM databasechangeloglock WHERE locked = true; -- Oracle: Connect as schema owner and release DELETE FROM databasechangeloglock WHERE locked = 1; ``` Then redeploy. ### Permission errors during autoDDL **Symptom:** Logs show "permission denied" or "cannot create table." **Fix:** Verify database user has DDL permissions (see [Database user permissions](#database-user-permissions) above). ### Out-of-sync schema If your schema doesn't match the expected version: 1. Check Liquibase logs for failed migrations. 2. Restore from backup if necessary. 3. Manually apply missing SQL scripts from the [Liquibase scripts page](/self-managed/deployment/helm/configure/database/access-sql-liquibase-scripts.md). ## Liquibase resource access SQL migration scripts and Liquibase change logs are available in the Camunda release bundle. For details on accessing these resources, see [access SQL and Liquibase scripts](/self-managed/deployment/helm/configure/database/access-sql-liquibase-scripts.md). --- ## RDBMS search APIs and result count behavior When using RDBMS as secondary storage, search APIs behave similarly to Elasticsearch/OpenSearch, but with important differences in how result counts are computed and limited. ## Result count behavior ### Total results capping In RDBMS-backed deployments, `totalResults` is capped at **10,000** to improve performance and match Elasticsearch/OpenSearch behavior. See [query performance guidance](#query-performance-guidance) for optimization strategies. Example response: ```json { "items": [ /* 100 items */ ], "totalResults": 10000, "hasMoreTotalItems": true } ``` ### hasMoreTotalItems field When the actual result set exceeds the cap, the `hasMoreTotalItems` boolean field is set to `true`. This indicates there are more results available and that pagination is still possible—you can continue querying with `searchAfter` or `page` parameters. Use this field in UI components to display "more results available" without computing exact counts. ## Configuration ### Maximum result limit The result count cap is configurable per deployment: | Parameter | Type | Default | Description | | ----------------------------------------------------------- | ------- | ------- | -------------------------------------------------------------------------------------------------------- | | `camunda.data.secondary-storage.rdbms.query.max-total-hits` | integer | `10000` | Maximum number of results to count. Set higher to count larger result sets (performance cost increases). | Current chart style (embedded `application.yml`): ```yaml orchestration: extraConfiguration: - file: application.yml content: | camunda.data.secondary-storage.rdbms.query.max-total-hits: 10000 ``` :::warning Increasing `maxTotalHits` gives accurate counts for larger result sets but increases database load, especially for queries with large result sets. Test in your environment before setting values above 10,000. ::: ## Query performance guidance To optimize query performance, use selective filters and pagination: ### 1. Use selective filters Always filter by indexed columns to reduce the result set size. Indexed columns typically include: - **Key fields**: Process definition key, instance ID, case instance ID - **Date properties**: Creation date, completion date, update timestamp **Good example** (efficient): ``` Filter: processDefinitionKey = "myProcess" AND createdDate >= 2024-01-01 Result size: < 100 items, fast COUNT(*) ``` **Poor example** (expensive): ``` Filter: category = "audit" Result size: Potentially millions of items, expensive COUNT(*) ``` ### 2. Avoid sorting large result sets When the result set is much larger than the page size, sorting requires the database to read and order all matching rows, even if only 100 are returned. **Good pattern**: ``` Filter by date range + key field → sort by creation date → paginate ``` **Avoid**: ``` No filter → sort by arbitrary field → paginate (requires reading entire table) ``` ### 3. Prefer pagination over exact counts Rather than requesting exact counts for large result sets: - Use page size limits (e.g., 100 items per page) - Check `hasMoreTotalItems` to determine if more results exist - Continue paginating as needed This avoids unnecessary COUNT(\*) operations on queries that may scan large portions of the table. ## Database-specific tuning (PostgreSQL) If you are using PostgreSQL as your RDBMS and experience slow search queries, tune compute resources, storage performance, and PostgreSQL server parameters based on your workload and hardware. Typical PostgreSQL parameters to evaluate for load-testing scenarios include: ```conf # WAL performance wal_buffers = 64MB max_wal_size = 4GB min_wal_size = 1GB checkpoint_timeout = 20min checkpoint_completion_target = 0.9 wal_writer_delay = 200ms wal_writer_flush_after = 1MB # Memory shared_buffers = 2GB effective_cache_size = 4500MB work_mem = 32MB maintenance_work_mem = 512MB # Autovacuum autovacuum_max_workers = 6 autovacuum_naptime = 15s autovacuum_vacuum_scale_factor = 0.03 autovacuum_analyze_scale_factor = 0.02 autovacuum_vacuum_cost_limit = 5000 # Monitoring pg_stat_statements.track = all pg_stat_statements.max = 10000 track_io_timing = on track_functions = all ``` :::note These are PostgreSQL-native settings. How you apply them depends on your deployment model (for example, managed database parameter groups, `postgresql.conf`, or Helm chart values). If you are using the Bitnami PostgreSQL chart, equivalent settings can be provided via chart-specific keys such as `primary.extendedConfiguration` (for PostgreSQL parameters) and `primary.resources` / `primary.persistence` (for sizing and storage). For production, start conservative and adjust based on your data volume, workload, and hardware. Camunda is a write-heavy application, so prioritize cache and vacuum settings for your environment. ::: Database-specific tuning for MariaDB and Oracle will be documented in future updates. ## Related guides - [Configure RDBMS in Helm charts](/self-managed/deployment/helm/configure/database/rdbms.md) - [JDBC driver management](/self-managed/deployment/helm/configure/database/rdbms-jdbc-drivers.md) - [RDBMS troubleshooting and operations](/self-managed/deployment/helm/configure/database/rdbms-troubleshooting.md) --- ## RDBMS troubleshooting and operations This page covers troubleshooting common issues, TLS configuration, and post-deployment operations for RDBMS deployments. For configuration reference, see [configure RDBMS in Helm charts](/self-managed/deployment/helm/configure/database/rdbms.md). :::note Related pages - **[Validate RDBMS connectivity](validate-rdbms.md)** - Quick validation checklist with database client examples. - **[Schema management](rdbms-schema-management.md)** - Schema creation and lifecycle. - **[JDBC drivers](rdbms-jdbc-drivers.md)** - Managing database drivers. ::: ## Connection failures **Symptom:** Pod fails to connect to the database (connection timeout, connection refused). **Diagnosis:** 1. Verify network connectivity from the pod to the database: ```bash kubectl exec -- nc -zv database-hostname port ``` 2. Check the JDBC URL in your configuration: ```bash kubectl get secret camunda-db-secret -o jsonpath='{.data.}' -n camunda | base64 -d ``` 3. Verify the database is running and accepting connections. **Fix:** Confirm the JDBC URL, hostname, port, and network policies allow traffic between pods and database. ## Authentication errors **Symptom:** "Authentication failed" or "Invalid password" in logs. **Diagnosis:** 1. Verify the secret exists and contains the correct password: ```bash kubectl get secret camunda-db-secret -o jsonpath='{.data.}' -n camunda | base64 -d ``` 2. Check the username in your Helm values matches the database user. 3. Test connection credentials manually (if possible from a pod or bastion host). **Fix:** Ensure the username, password, and secret key reference are correct in your Helm values. ## JDBC driver not found **Symptom:** ClassNotFoundException or "No suitable JDBC driver" in logs. **Diagnosis:** 1. Verify the driver JAR file was loaded: ```bash kubectl exec -- ls -la /driver-lib/ ``` 2. Check init container logs: ```bash kubectl logs -c fetch-jdbc-drivers ``` 3. Verify the JDBC URL matches the driver type (e.g., Oracle URL with Oracle driver). **Fix:** Re-apply the init container configuration or verify the custom image includes the driver. See [JDBC driver management](/self-managed/deployment/helm/configure/database/rdbms-jdbc-drivers.md). ## Schema creation failure **Symptom:** Liquibase errors; tables not created. **Diagnosis:** 1. Check Liquibase logs: ```bash kubectl logs | grep -i liquibase ``` 2. Verify `autoDDL` is enabled (default: `true`): ```yaml orchestration: extraConfiguration: - file: "manual-schema-management.yaml" content: | camunda: data: secondary-storage: rdbms: auto-ddl: false # Confirm this is set ``` 3. Test database user permissions (see [Schema management](/self-managed/deployment/helm/configure/database/rdbms-schema-management.md#database-user-permissions)). **Fix:** Ensure database user has DDL permissions or disable autoDDL and apply schema manually. See [Schema management](/self-managed/deployment/helm/configure/database/rdbms-schema-management.md). ## Liquibase lock after pod crash or restart **Symptom:** Pod startup appears stuck on Liquibase, or repeated restarts fail while waiting for `databasechangeloglock`. **Cause:** A previous pod may have been terminated while Liquibase was still running, leaving a lock row behind. **Behavior:** Camunda waits for a stale Liquibase DDL lock using `camunda.data.secondary-storage.rdbms.ddl-lock-wait-timeout` (default: `PT15M`). **Fix:** 1. Increase the timeout for slow migrations (for example, large index creation): ```yaml orchestration: extraConfiguration: - file: "rdbms-liquibase-lock-timeout.yaml" content: | camunda: data: secondary-storage: rdbms: ddl-lock-wait-timeout: PT30M ``` 2. Avoid terminating Orchestration Cluster pods while Liquibase is actively applying migrations. 3. Only release `databasechangeloglock` manually when you have verified no migration is still running. ## Slow data export **Symptom:** Data takes a long time to appear in the database after process events. **Cause:** Flush interval or queue size not tuned for your workload. **Diagnosis:** 1. Check current flush interval in logs: ```bash kubectl logs | grep -i flushinterval ``` 2. Verify queue size settings in your Helm values. **Fix:** Adjust these settings: ```yaml orchestration: extraConfiguration: - file: "flush-interval.yaml" content: | camunda: data: secondary-storage: rdbms: flush-interval: PT1S # More frequent flushes queue-size: 5000 # Larger queue for buffering queue-memory-limit: 50 # Increase if needed ``` - Smaller `flushInterval` → more frequent writes (increases DB load). - Larger `queueSize` → more events buffered before flush (increases memory). ## TLS/SSL configuration ### PostgreSQL with TLS Add SSL parameters to the JDBC URL: ```yaml orchestration: data: secondaryStorage: rdbms: url: jdbc:postgresql://hostname:5432/camunda?ssl=true&sslmode=require ``` ### Oracle with TLS Oracle uses TCPS (TLS over Oracle protocol): ```yaml orchestration: data: secondaryStorage: rdbms: url: jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCPS)(HOST=hostname)(PORT=2484))(CONNECT_DATA=(SERVICE_NAME=FREEPDB1))) ``` ### Self-signed certificates If your database uses self-signed certificates: 1. Extract the certificate from your database server. 2. Create a Kubernetes secret: ```bash kubectl create secret generic db-certs \ --from-file=ca.crt=/path/to/ca.crt \ -n camunda ``` 3. Mount the certificate and configure trust (consult your database vendor's JDBC documentation). ## Post-deployment operations These operations are officially supported on running Camunda clusters: ### Database password rotation Password rotations are safe: 1. Update the password in your RDBMS. 2. Update the Kubernetes secret: ```bash kubectl patch secret camunda-db-secret \ -p '{"data":{"db-password":"'$(echo -n 'new-password' | base64)'"}}' \ -n camunda ``` 3. Restart the Orchestration Cluster pods: ```bash kubectl rollout restart deployment/camunda-orchestration -n camunda ``` ### JDBC driver updates Updating bundled drivers or replacing custom drivers: 1. For custom drivers via init container: Update the JAR source in your Helm values. 2. For bundled drivers: Update the Camunda version. 3. Redeploy: ```bash helm upgrade camunda camunda/camunda-platform -f values.yaml -n camunda ``` See [JDBC driver management](/self-managed/deployment/helm/configure/database/rdbms-jdbc-drivers.md#jdbc-driver-updates). ### Schema validation Verify schema integrity after upgrades or restores: ```sql -- PostgreSQL: Count expected tables SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND (table_name LIKE 'zeebe_%' OR table_name LIKE 'process_%'); -- Oracle: Count expected tables SELECT COUNT(*) FROM user_tables WHERE table_name LIKE 'ZEEBE_%' OR table_name LIKE 'PROCESS_%'; ``` Expect roughly 20-30 tables depending on your Camunda version. ## Connectivity health checks After deployment, verify the cluster is healthy: 1. **Check pod readiness:** ```bash kubectl get pods -n camunda | grep orchestration ``` 2. **Check exporter logs:** ```bash kubectl logs | grep RdbmsExporter ``` 3. **Verify table creation:** ```sql SELECT COUNT(*) FROM zeebe_process; ``` 4. **Deploy a test process** and verify it appears in the database. For a complete post-deployment checklist, see [validate RDBMS connectivity](/self-managed/deployment/helm/configure/database/validate-rdbms.md). --- ## Configure RDBMS in Helm chart Camunda 8 Self-Managed supports using an external relational database (RDBMS) as the Orchestration Cluster's secondary storage instead of Elasticsearch or OpenSearch. This page provides: - **[Configuration reference](#configuration)**: All Helm values organized by function. - **[Quick example](#example-usage)**: Minimal YAML to get started. - Links to detailed guides for specific tasks. Related guides: - [Secondary storage architecture](/self-managed/reference-architecture/reference-architecture.md#secondary-storage-architecture) - [Secondary storage overview](/self-managed/concepts/secondary-storage/index.md) - [RDBMS example deployment](/self-managed/deployment/helm/install/helm-with-rdbms.md) - [JDBC driver management](/self-managed/deployment/helm/configure/database/rdbms-jdbc-drivers.md) ## Prerequisites Provide a supported relational database that is reachable by the Camunda components. See the [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) for the complete list of supported databases and versions. Ensure that: - Your network allows traffic from Camunda pods to the database. - Required JDBC parameters (SSL/TLS, authentication, failover) are configured as needed. - The database user has permissions to create and modify schema objects if `autoDDL` is enabled. For a short checklist and troubleshooting steps you can run after configuring the database, see [validate RDBMS connectivity (Helm)](/self-managed/deployment/helm/configure/database/validate-rdbms.md). ## Configuration ### Connection parameters (required) | Parameter | Type | Default | Description | | ---------------------------------------------------- | ------ | ------- | --------------------------------------------- | | `orchestration.data.secondaryStorage.type` | string | `""` | Must be `rdbms` to use a relational database. | | `orchestration.data.secondaryStorage.rdbms.url` | string | `""` | JDBC connection URL for the database. | | `orchestration.data.secondaryStorage.rdbms.username` | string | `""` | Username for database authentication. | ### Database credentials Store the database password in a Kubernetes secret and reference it. For testing only, you can use `inlineSecret`. | Parameter | Type | Default | Description | | -------------------------------------------------------------------- | ------ | ------- | --------------------------------------------------- | | `orchestration.data.secondaryStorage.rdbms.secret.existingSecret` | string | `""` | Name of Kubernetes secret containing the password. | | `orchestration.data.secondaryStorage.rdbms.secret.existingSecretKey` | string | `""` | Key within the secret storing the password. | | `orchestration.data.secondaryStorage.rdbms.secret.inlineSecret` | string | `""` | Password value (testing only, not production-safe). | ### Connection pool and performance tuning Most tuning options are configured as application properties via [extraConfiguration](/self-managed/deployment/helm/configure/application-configs.md) (embedded `application.yml`). | Parameter | Type | Default | Description | | ------------------------------------------------------------------------- | ----------------- | ------- | --------------------------------------------- | | `orchestration.data.secondaryStorage.rdbms.flushInterval` | ISO-8601 duration | `""` | How frequently the exporter flushes events. | | `orchestration.data.secondaryStorage.rdbms.queueSize` | integer | `1000` | Exporter queue size. Larger = more buffering. | | `camunda.data.secondary-storage.rdbms.queue-memory-limit` | integer | `20` | Memory limit (MB) for the exporter queue. | | `camunda.data.secondary-storage.rdbms.connection-pool.maximum-pool-size` | integer | `10` | Maximum JDBC connections. | | `camunda.data.secondary-storage.rdbms.connection-pool.minimum-idle` | integer | `10` | Minimum idle connections. | | `camunda.data.secondary-storage.rdbms.connection-pool.connection-timeout` | integer | `30000` | Timeout (ms) for acquiring a connection. | ### Search APIs and result limits | Parameter | Type | Default | Description | | -------------------------------------------------------------- | ------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `orchestration.data.secondaryStorage.rdbms.query.maxTotalHits` | integer | `10000` | Maximum result count cap for search APIs. Limits COUNT(\*) queries to improve performance. See [Search APIs and result limits](/self-managed/deployment/helm/configure/database/rdbms-search-and-result-limits.md) for details and performance implications. | ### Schema and table management | Parameter | Type | Default | Description | | --------------------------------------------------- | ------- | ------- | ------------------------------------------ | | `orchestration.data.secondaryStorage.rdbms.autoDDL` | boolean | `true` | Enable Liquibase auto-schema creation. | | `orchestration.data.secondaryStorage.rdbms.prefix` | string | `""` | Optional table name prefix for all tables. | ### History and data retention | Parameter | Type | Default | Description | | -------------------------------------------------------------------------------------------------- | ----------------- | ------- | ----------------------------------------- | | `orchestration.data.secondaryStorage.rdbms.history.defaultHistoryTTL` | ISO-8601 duration | `""` | Default TTL for historic process data. | | `orchestration.data.secondaryStorage.rdbms.history.minHistoryCleanupInterval` | ISO-8601 duration | `""` | Minimum interval for history cleanup. | | `orchestration.data.secondaryStorage.rdbms.history.maxHistoryCleanupInterval` | ISO-8601 duration | `""` | Maximum interval for history cleanup. | | `orchestration.data.secondaryStorage.rdbms.history.historyCleanupBatchSize` | integer | `1000` | Batch size when deleting historic data. | | `orchestration.data.secondaryStorage.rdbms.history.defaultBatchOperationHistoryTTL` | ISO-8601 duration | `""` | TTL for batch operation history. | | `orchestration.data.secondaryStorage.rdbms.history.batchOperationCancelProcessInstanceHistoryTTL` | ISO-8601 duration | `""` | TTL for cancel-process-instance history. | | `orchestration.data.secondaryStorage.rdbms.history.batchOperationMigrateProcessInstanceHistoryTTL` | ISO-8601 duration | `""` | TTL for migrate-process-instance history. | | `orchestration.data.secondaryStorage.rdbms.history.batchOperationModifyProcessInstanceHistoryTTL` | ISO-8601 duration | `""` | TTL for modify-process-instance history. | | `orchestration.data.secondaryStorage.rdbms.history.batchOperationResolveIncidentHistoryTTL` | ISO-8601 duration | `""` | TTL for resolve-incident history. | ### Connection pool lifecycle | Parameter | Type | Default | Description | | ------------------------------------------------------------------------------ | ----------------- | ------- | ------------------------------------------ | | `orchestration.data.secondaryStorage.rdbms.history.connectionPool.idleTimeout` | ISO-8601 duration | `""` | Maximum time a connection can remain idle. | | `orchestration.data.secondaryStorage.rdbms.history.connectionPool.maxLifetime` | ISO-8601 duration | `""` | Maximum lifetime of a JDBC connection. | ### Other parameters RDBMS supports other configuration options that can be configured in the helm chart `values.yaml` via [extraConfiguration](/self-managed/deployment/helm/configure/application-configs.md). See [RDBMS options](/self-managed/concepts/databases/relational-db/configuration.md). For Liquibase lock recovery behavior, configure `camunda.data.secondary-storage.rdbms.ddl-lock-wait-timeout` (default: `PT15M`) via `extraConfiguration` if you need a longer wait time for heavy schema migrations. ### Example usage :::note Operate has limited functionality when using RDBMS as secondary storage in Camunda 8.9-alpha3. See [Operate limitations](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md#operate-with-rdbms) for details. ::: ```yaml orchestration: exporters: camunda: enabled: false rdbms: enabled: true data: secondaryStorage: type: rdbms rdbms: url: jdbc:postgresql://hostname:5432/camunda username: camunda secret: existingSecret: camunda-db-secret existingSecretKey: password ``` ## Bundled vs. custom JDBC drivers Camunda bundles JDBC drivers for some databases. For others, you must supply a custom driver. See [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) for the complete list of supported databases. **See:** [JDBC driver management](/self-managed/deployment/helm/configure/database/rdbms-jdbc-drivers.md) for: - Which drivers are bundled - When to supply custom drivers - How to load drivers (init containers, custom images, volumes) ## Search APIs and result limits RDBMS search APIs return a `totalResults` field capped at **10,000** to improve performance. However, actual query performance depends on filter selectivity and database optimization. Key concepts: - **Total count cap**: `totalResults` is capped at 10,000 by default (configurable via `maxTotalHits`). - **hasMoreTotalItems flag**: Indicates if results exist beyond the cap. - **Query optimization**: Apply selective filtering and pagination for better performance. - **Database tuning**: Configure PostgreSQL for write-heavy workloads. **See:** [Search APIs and result limits](/self-managed/deployment/helm/configure/database/rdbms-search-and-result-limits.md) for configuration options, performance trade-offs, optimization best practices, and database-specific tuning. ## Schema creation and management Camunda automatically creates your database schema using Liquibase (when `autoDDL: true`). You can also manage the schema manually if required. **See:** [Schema management](/self-managed/deployment/helm/configure/database/rdbms-schema-management.md) for: - Automatic schema creation with autoDDL - Database user permissions for each RDBMS type - Manual schema management and DBA workflows - Schema upgrades and verification ## Troubleshooting and operations For detailed troubleshooting of common issues and post-deployment operations, see [RDBMS troubleshooting and operations](/self-managed/deployment/helm/configure/database/rdbms-troubleshooting.md), which covers: - Connection failures and authentication errors - JDBC driver loading issues - Schema creation failures - Slow data export and performance tuning - TLS/SSL configuration - Post-deployment operations (password rotation, driver updates, schema validation) ## Verifying connectivity After deployment, verify the Orchestration Cluster is writing to the database: 1. Confirm tables were created: ```sql -- PostgreSQL example SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'; ``` 2. Deploy a process and start an instance using Web Modeler. 3. Query the database to confirm the instance was recorded: ```sql SELECT * FROM process_instances; ``` 4. Review logs for successful initialization: ``` INFO io.camunda.exporter.rdbms.RdbmsExporter - RdbmsExporter created with Configuration: flushInterval=PT0.5S INFO io.camunda.exporter.rdbms.RdbmsExporter - Exporter opened with last exported position ``` For a complete post-deployment checklist, see [validate RDBMS connectivity (Helm)](/self-managed/deployment/helm/configure/database/validate-rdbms.md). ## Using AWS Aurora PostgreSQL (optional) If you are using AWS Aurora PostgreSQL as your relational database, you can configure it the same way as a standard PostgreSQL instance. Optionally, Camunda also supports the AWS JDBC wrapper driver, which provides additional features such as improved failover handling and IAM-based authentication. For details and examples, see [using AWS Aurora PostgreSQL with Camunda](../../../../concepts/databases/relational-db/configuration.md#usage-with-aws-aurora-postgresql). ## Limitations and unsupported scenarios ### Component-specific RDBMS support - **Orchestration Cluster**: ✅ Full RDBMS support for secondary storage (includes Zeebe, Operate, Tasklist, Orchestration Identity). - **Connectors**: ✅ Supports RDBMS for process definitions and state. - **Web Modeler**: ✅ RDBMS support available in 8.9. - **Optimize**: ❌ **Requires Elasticsearch or OpenSearch only.** Optimize cannot use RDBMS. If you deploy Optimize, you must still provision Elasticsearch or OpenSearch. ### Multi-region deployments Cross-region RDBMS deployments are **not yet tested or supported** in Camunda 8.9. Deploy RDBMS in the same region as your Kubernetes cluster. ### Self-managed database HA Camunda assumes your RDBMS handles its own HA (replication, failover). Use cloud-managed databases or vendor-specific HA solutions for production. ### Custom JDBC driver libraries Only JDBC drivers from official vendor sources are supported. Custom or modified drivers may cause unexpected behavior. --- ## Use external PostgreSQL The [Helm chart deployment](/self-managed/deployment/helm/install/quick-install.md) can optionally install an internal PostgreSQL using [Bitnami subcharts](../../configure/registry-and-images/install-bitnami-enterprise-images.md). For production environments, we advise deploying PostgreSQL separately from the Camunda Helm charts. This guide steps through using an external PostgreSQL instance. This page applies to Management Identity, Keycloak, and Web Modeler. It does not apply to the Orchestration Cluster or Optimize. ## Prerequisites - **Running external PostgreSQL service** - **Connection details:** following sample values are used in this guide (replace them with your own): ```yaml host: `db.example.com` port: `5432` username: `postgres` password: `examplePassword` ``` - **Supported versions:**: Check the [supported environments](/reference/supported-environments.md) and [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) pages to confirm which PostgreSQL versions are supported. - **Database setup:** Ensure the required databases exist in your PostgreSQL instance. For this guide, create the following databases: ```SQL CREATE DATABASE "web-modeler"; CREATE DATABASE "keycloak"; CREATE DATABASE "management-identity"; ``` - **Kubernetes secrests:** Store the database password in a Kubernetes secret so it is not referenced in plain text within your values.yaml (This secret exists outside the Helm chart and will not be overwritten by subsequent helm upgrade commands). For example: ```bash kubectl create secret generic camunda-psql-db --from-literal=password=examplePassword -n camunda ``` ## Configuration Three Camunda 8 Self-Managed components require PostgreSQL: Management Identity, Keycloak, and Web Modeler. Each of these components must be configured to connect to the external PostgreSQL instance. ### Parameters | values.yaml option | type | default | description | | -------------------------------------------------------------- | ------- | ------- | ------------------------------------------------------------------------ | | `webModeler.restapi.externalDatabase.url` | string | `""` | JDBC url of the database | | `webModeler.restapi.externalDatabase.user` | string | `""` | Username of the database | | `webModeler.restapi.externalDatabase.secret.existingSecret` | string | `""` | Kubernetes Secret name containing a database password | | `webModeler.restapi.externalDatabase.secret.existingSecretKey` | string | `""` | Key within the Kubernetes Secret that has the database password | | `webModeler.restapi.externalDatabase.secret.inlineSecret` | string | `""` | string literal of the database password if not using a Kubernetes Secret | | `identity.externalDatabase.enabled` | boolean | `false` | Enable the externalDatabase options | | `identity.externalDatabase.host` | string | `""` | Hostname of the database | | `identity.externalDatabase.port` | integer | `5432` | Port of the database | | `identity.externalDatabase.username` | string | `""` | Username of the database | | `identity.externalDatabase.secret.existingSecret` | string | `""` | Kubernetes Secret name containing database password | | `identity.externalDatabase.secret.existingSecretKey` | string | `""` | Key within the Kubernetes Secret that contains the database password | | `identity.externalDatabase.database` | string | `""` | Database name | | `identityKeycloak.externalDatabase.host` | string | `""` | Database host name | | `identityKeycloak.externalDatabase.port` | integer | `5432` | Database port number | | `identityKeycloak.externalDatabase.user` | string | `""` | Database user name | | `identityKeycloak.externalDatabase.existingSecret` | string | `""` | Kubernetes Secret containing the database password | | `identityKeycloak.externalDatabase.existingSecretKey` | string | `""` | Key within the Kubernetes Secret containing the database password | | `identityKeycloak.externalDatabase.database` | string | `""` | Database name | ### Example usage ```yaml webModeler: enabled: true restapi: mail: fromAddress: noreply@camunda.mycompany.com fromName: Camunda 8 WebModeler externalDatabase: url: "jdbc:postgresql://db.example.com:5432/web-modeler" user: "postgres" secret: existingSecret: "camunda-psql-db" existingSecretKey: "password" identity: externalDatabase: enabled: true host: "db.example.com" port: 5432 username: "postgres" secret: existingSecret: "camunda-psql-db" existingSecretKey: "password" database: "management-identity" identityKeycloak: externalDatabase: url: "jdbc:postgresql://db.example.com:5432/modeler" user: "postgres" existingSecret: "camunda-psql-db" existingSecretKey: "password" database: "keycloak" auth: adminUser: postgres existingSecret: "camunda-psql-db" existingSecretPasswordKey: "password" # disable internal psql for keycloak postgresql: enabled: false ``` ## Troubleshooting - If the database for Keycloak is misconfigured, other applications will output a `401` error code in the logs as they are not able to correctly authenticate against Keycloak. - If you have not created the databases in your external PostgreSQL instance, a `database missing` error will output in the logs of the respective component. ## References --- ## Use Amazon OpenSearch Service for Orchestration Cluster with Helm Configure the Orchestration Cluster in Camunda 8 Self-Managed to use Amazon OpenSearch Service as a secondary storage backend when deploying with the Helm chart. OpenSearch is used for indexing and querying operational data consumed by Orchestration Cluster applications and APIs. For a canonical definition, see [Elasticsearch/OpenSearch](/reference/glossary.md#elasticsearchopensearch). Starting with Camunda 8.9, the Helm chart no longer provisions Elasticsearch by default. You can configure the Helm chart to connect to an external Amazon OpenSearch Service instance as an alternative secondary storage backend. This page applies to the Orchestration Cluster only. If you also deploy Optimize, configure Optimize separately using [use external OpenSearch for Optimize with Helm](/self-managed/deployment/helm/configure/database/optimize/using-external-opensearch.md). Secondary storage is configurable. For supported components, you can use an RDBMS-based secondary store instead. See [RDBMS configuration](/self-managed/concepts/databases/relational-db/configuration.md) or the glossary entry [RDBMS](/reference/glossary.md#rdbms). For the [quick-install](/self-managed/deployment/helm/install/quick-install.md) scenario, RDBMS with embedded H2 is used instead. :::info OpenSearch support Camunda 8 supports both the open-source [OpenSearch](https://opensearch.org/) distribution and [Amazon OpenSearch Service](https://aws.amazon.com/opensearch-service). ::: ## Prerequisites Amazon OpenSearch requires two layers of permissions: - AWS IAM permissions - OpenSearch internal authentication To connect to OpenSearch using AWS IAM roles for service accounts (IRSA), see the [IAM roles for service accounts documentation](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/terraform-setup.md#opensearch-module-setup). To connect to OpenSearch using Basic authentication, follow the configuration below. ## Configuration ### Parameters #### Orchestration Cluster secondary storage | Parameter | Type | Default | Description | | ------------------------------------------------------------------------------ | ------ | ------- | ----------------------------------------------------------------------------------------------- | | `orchestration.data.secondaryStorage.type` | string | `""` | Type of secondary storage. Set to `opensearch` to use OpenSearch. | | `orchestration.data.secondaryStorage.opensearch.url` | string | `""` | URL for the OpenSearch cluster as `scheme://host:port` (for example, `https://opensearch:443`). | | `orchestration.data.secondaryStorage.opensearch.auth.username` | string | `""` | Username for OpenSearch authentication. | | `orchestration.data.secondaryStorage.opensearch.auth.secret.inlineSecret` | string | `""` | OpenSearch password as a plain-text value (non-production only). | | `orchestration.data.secondaryStorage.opensearch.auth.secret.existingSecret` | string | `""` | Reference to an existing Kubernetes Secret containing the password. | | `orchestration.data.secondaryStorage.opensearch.auth.secret.existingSecretKey` | string | `""` | Key within the existing Kubernetes Secret containing the password. | | `orchestration.data.secondaryStorage.opensearch.tls.secret.existingSecret` | string | `""` | Reference to an existing Kubernetes Secret containing the TLS trust store. | | `orchestration.data.secondaryStorage.opensearch.tls.secret.existingSecretKey` | string | `""` | Key within the existing Kubernetes Secret for the TLS trust store. | | `orchestration.index.prefix` | string | `""` | Index prefix in OpenSearch for the new Camunda exporter and the Orchestration Cluster. | ### Example usage ```yaml orchestration: data: secondaryStorage: type: opensearch opensearch: url: https://opensearch.example.com:443 auth: username: user secret: # For non-production environments only: inlineSecret: "your-password-here" # For production (recommended): # existingSecret: "opensearch-secret" # existingSecretKey: "password" ``` This configuration connects the Orchestration Cluster to an external Amazon OpenSearch Service instance as its secondary storage backend. To avoid storing the username and password in plaintext in your `values.yaml`, reference a Kubernetes secret. For details and examples, see [Helm charts secret management](/self-managed/deployment/helm/configure/secret-management.md). ### Connect to external OpenSearch with custom index prefixes When running multiple Camunda instances on a shared OpenSearch cluster, use custom index prefixes to isolate data: ```yaml orchestration: data: secondaryStorage: type: opensearch opensearch: url: https://opensearch.example.com:443 auth: username: admin secret: inlineSecret: pass index: prefix: my-env-camunda # Prefix for Orchestration Cluster indices ``` For more details about index prefix configuration and Optimize-specific settings, see [Configure Elasticsearch and OpenSearch index prefixes](/self-managed/deployment/helm/configure/database/elasticsearch/configure-elasticsearch-prefix-indices.md). ### Component configuration Orchestration Cluster components use the same configuration keys for both Elasticsearch and OpenSearch. To switch, replace the `elasticsearch` prefix with `opensearch` and provide the corresponding values. For example: - **Operate**: `CAMUNDA_OPERATE_ELASTICSEARCH_URL` → `CAMUNDA_OPERATE_OPENSEARCH_URL` - **Tasklist**: `CAMUNDA_TASKLIST_ELASTICSEARCH_URL` → `CAMUNDA_TASKLIST_OPENSEARCH_URL` For **Zeebe**, configure the [OpenSearch exporter](/self-managed/components/orchestration-cluster/zeebe/exporters/opensearch-exporter.md). For full parameter details, see: - [Operate configuration](/self-managed/components/orchestration-cluster/operate/operate-configuration.md#settings-for-opensearch) - [Tasklist configuration](/self-managed/components/orchestration-cluster/tasklist/tasklist-configuration.md#elasticsearch-or-opensearch) ## References - [Helm charts secret management](/self-managed/deployment/helm/configure/secret-management.md) - [IAM roles for service accounts](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/terraform-setup.md#opensearch-module-setup) - [OpenSearch exporter](/self-managed/components/orchestration-cluster/zeebe/exporters/opensearch-exporter.md) - [Use external OpenSearch for Optimize with Helm](/self-managed/deployment/helm/configure/database/optimize/using-external-opensearch.md) - [Configure Elasticsearch and OpenSearch index prefixes](/self-managed/deployment/helm/configure/database/elasticsearch/configure-elasticsearch-prefix-indices.md) - [Deploy Camunda 8](/self-managed/setup/overview.md) --- ## Validate RDBMS connectivity Verify RDBMS connectivity, schema initialization, and exporter activity for Helm deployments. :::note Related pages - **[Configure RDBMS](rdbms.md)** - Configuration reference and Helm values. - **[JDBC driver management](rdbms-jdbc-drivers.md)** - Managing custom database drivers. - **[Schema management](rdbms-schema-management.md)** - Schema creation and lifecycle. - **[Troubleshooting](rdbms-troubleshooting.md)** - Common issues and diagnostic steps. ::: :::note 8.9-alpha3 stabilizes application-side RDBMS behavior. Log lines and behavior may change in later alphas. This page documents current alpha3 patterns. ::: Supported databases include PostgreSQL, MySQL/MariaDB, and Oracle (examples below). Use your database vendor’s CLI tools for direct verification. ## Prerequisites - The database endpoint is reachable from the Kubernetes cluster (DNS, routing, firewall/security groups, NetworkPolicies). - Your Helm values are configured for RDBMS secondary storage and the RDBMS exporter is enabled. - For databases that require external JDBC drivers (for example Oracle), the driver JAR is available to the Orchestration Cluster at runtime. :::note In alpha3, the pod should only become `Ready` when the application can connect to the database. Success of Liquibase and a running exporter are best confirmed via logs and direct database inspection. ::: ## How to verify connectivity Run a database client from within the cluster (either in an existing pod or in a temporary debug pod). ### PostgreSQL example (psql) ```bash # Option A: run an ephemeral pod with psql (recommended) kubectl run -i --rm --tty pg-client \ --image=postgres:15 \ --restart=Never \ --namespace camunda \ --command -- bash # Inside the pod: psql "host=POSTGRES_HOST port=5432 user=camunda dbname=camunda password=REDACTED" -c '\dt' ``` If you prefer `kubectl exec`, ensure the target container image includes `psql`. ### MySQL/MariaDB example (mysql) ```bash kubectl run -i --rm --tty mysql-client \ --image=mysql:8 \ --restart=Never \ --namespace camunda \ --command -- bash # Inside the pod: mysql -h MYSQL_HOST -P 3306 -u camunda -pREDACTED camunda -e 'SHOW TABLES;' ``` ### Oracle example (sqlplus) Use an image that includes Oracle client tools (for example sqlplus) and list tables: ```sql SELECT table_name FROM user_tables; ``` What to expect: - Success: The client connects and can list tables (or list schemas). - Failure: DNS/connection/authentication errors (the application typically fails fast and does not become `Ready`). ## What successful schema creation looks like ### Liquibase log indicator Check Orchestration Cluster logs for Liquibase initialization: ```bash kubectl -n camunda logs deploy/orchestration | grep -E "Liquibase|MyBatisConfiguration" ``` A successful run includes a line similar to: ```pgsql [TIMESTAMP] [main] INFO io.camunda.application.commons.rdbms.MyBatisConfiguration - Initializing Liquibase for RDBMS with global table trimmedPrefix ''. ``` When Liquibase runs without errors, schema creation is considered successful. If `autoDDL` is disabled on an empty database, the exporter will fail because required tables do not exist. ### Confirm tables exist (SQL) PostgreSQL: ```sql \dt ``` MySQL/MariaDB: ```sql SHOW TABLES; ``` Oracle: ```sql SELECT table_name FROM user_tables; ``` If tables exist (for example `EXPORTER_POSITION`, `AUTHORIZATIONS`, `BATCH_OPERATION`), schema initialization succeeded. ## Verify the RDBMS exporter is running and flushing ### Exporter log indicators Search Orchestration Cluster logs for exporter startup: ```bash kubectl -n camunda logs deploy/orchestration | grep -E "RdbmsExporter|RDBMS Exporter" ``` Successful startup includes lines similar to: ```pgsql [TIMESTAMP] INFO io.camunda.exporter.rdbms.RdbmsExporter - [RDBMS Exporter] RdbmsExporter created with Configuration: flushInterval=PT0.5S, queueSize=1000 [TIMESTAMP] INFO io.camunda.exporter.rdbms.RdbmsExporter - [RDBMS Exporter] Exporter opened with last exported position 126 ``` ### Confirm exporter progress in the database Query the exporter position table and confirm values update over time. PostgreSQL: ```sql SELECT partition_id, exporter, last_exported_position FROM exporter_position ORDER BY partition_id; ``` MySQL/MariaDB: ```sql SELECT PARTITION_ID, EXPORTER, LAST_EXPORTED_POSITION FROM EXPORTER_POSITION ORDER BY PARTITION_ID; ``` Oracle: ```sql SELECT partition_id, exporter, last_exported_position FROM exporter_position ORDER BY partition_id; ``` Exporter progress is the most reliable “is it working” signal. If `last_exported_position` never advances after you generate workload, inspect exporter logs and database permissions. ## Logs to inspect ### Success patterns - Liquibase initialization line in Orchestration Cluster logs (see above). - RDBMS exporter created/opened lines in Orchestration Cluster logs (see above). ### Failure patterns - Missing driver - Example: `Failed to load driver class oracle.jdbc.OracleDriver` - Result: Camunda fails to start. - Invalid JDBC URL, host, or DNS resolution - Example messages: - `Factory method 'databaseProperties' threw exception... Error occurred when getting DB product name.` - Root cause stack traces including `UnknownHostException` or socket connection failures. - Result: Camunda fails to start. - Invalid credentials - Example: `SQLInvalidAuthorizationSpecException: Access denied for user ... (using password: YES)` - Result: Camunda fails to start. - `autoDDL=false` on an empty database - Example: - `SQLSyntaxErrorException: Table '...EXPORTER_POSITION' doesn't exist` - Result: Exporter fails because required tables are missing. This is expected behavior unless schema is pre-created. Fetch logs: ```bash kubectl -n camunda logs deploy/orchestration kubectl -n camunda logs statefulset/zeebe-broker-0 ``` If logs contain connection, driver, or authentication stack traces, the application typically fails fast and does not reach full readiness. ## Health and readiness verification In 8.9-alpha3, pod readiness reflects database connectivity through a database health indicator. However, readiness alone does not confirm schema creation or exporter progress. Use the following checks together: Kubernetes readiness: ```bash kubectl get pods -n camunda ``` Application health endpoint (if exposed): ```bash kubectl -n camunda port-forward svc/orchestration 8080:8080 curl -sS http://localhost:8080/actuator/health ``` Look for an overall `UP` status and a healthy database component. Always confirm with logs and database queries. ## Common error patterns and troubleshooting - DNS or network issues - Symptoms: `UnknownHostException`, socket connection errors. - Fix: Verify the JDBC host, service name, NetworkPolicies, firewall/security group rules, and routing from the cluster. - Missing JDBC driver - Symptoms: `Failed to load driver class ...`. - Fix: Provide the driver using `extraVolumes`/`extraVolumeMounts`, an init container, or a custom image. - Invalid credentials - Symptoms: `Access denied...`, `SQLInvalidAuthorizationSpecException`. - Fix: Confirm username, password, and required privileges. Test connectivity using a database client from inside the cluster. - `autoDDL=false` on an empty database - Symptoms: missing table errors such as `EXPORTER_POSITION` not found. - Fix: Enable auto-DDL for initial schema creation or provision the schema manually before startup. - Exporter position not advancing - Symptoms: `last_exported_position` remains unchanged after generating workload. - Fix: Inspect exporter logs for errors, confirm database write permissions, and review exporter settings (`flushInterval`, `queueSize`). Enable `DEBUG` logging if needed. ## Vendor-specific query examples ### PostgreSQL ```sql \dt SELECT partition_id, exporter, last_exported_position FROM exporter_position; ``` ### MySQL/MariaDB ```sql SHOW TABLES; SELECT PARTITION_ID, EXPORTER, LAST_EXPORTED_POSITION FROM EXPORTER_POSITION; ``` ### Oracle ```sql SELECT table_name FROM user_tables; SELECT partition_id, exporter, last_exported_position FROM exporter_position; ``` ## What success looks like - Orchestration Cluster pods reach `Ready` state. - Logs show Liquibase initialization without errors. - Logs show the RDBMS exporter created and opened with a last exported position. - The database contains expected Camunda tables (for example `EXPORTER_POSITION`, `AUTHORIZATIONS`, `BATCH_OPERATION`). - `last_exported_position` advances after generating workload. ## Notes and caveats - **Timing:** In 8.9-alpha3, schema creation and exporter startup occur during application startup. Exported data visibility depends on the exporter `flushInterval` and workload. - If observed behavior or log lines differ from this documentation, open an issue and include: - Relevant log excerpts - Helm values used for RDBMS configuration - Database type and version --- ## Enable additional Camunda components Starting with Camunda 8.8, the Helm chart reflects a new architecture where Zeebe, Zeebe Gateway, Operate, Tasklist, and Admin (formerly Orchestration Cluster Identity) are consolidated into a single [Orchestration Cluster](/reference/glossary.md#orchestration-cluster). As a result, the default deployment includes only the Orchestration Cluster and Connectors. This page explains how to enable additional components you may need. ## Default vs. additional components ### Enabled by default (8.8+) - Orchestration Cluster (Zeebe, Zeebe Gateway, Operate, Tasklist, Orchestration Cluster Admin) - Connectors ### Additional components (must be explicitly enabled) - Console - Management Identity - Web Modeler - Optimize - Elasticsearch - OpenSearch - RDBMS - PostgreSQL (Bitnami subchart) - only if needed for Web Modeler or Identity - Keycloak (Bitnami subchart) - only if using internal authentication :::note Upgrading from 8.7? In Camunda 8.7, more components were enabled by default. If you're upgrading from 8.7 and used any of the components listed above, you must explicitly enable them in your 8.8 `values.yaml`. See the [8.7 to 8.8 upgrade guide](/versioned_docs/version-8.8/self-managed/upgrade/helm/870-to-880.md#ensure-required-components) for upgrade-specific instructions. ::: ## Management Identity In Camunda 8.8, identity management is split into two distinct scopes: - **Orchestration Cluster Admin** - Manages authentication and authorization for core orchestration components (Zeebe, Operate, Tasklist) and their APIs. This is built into the Orchestration Cluster and does not require Management Identity. - **Management Identity** - Controls access for management and modeling components (Web Modeler, Console, Optimize). This is a separate component that must be explicitly enabled. Management Identity must be enabled if you want to use any of the following components: - Web Modeler - Console - Optimize Check the [authentication and authorization](./authentication-and-authorization/index.md) guide for detailed steps on enabling and configuring Management Identity. :::info If you enable Web Modeler, Console, or Optimize without enabling Management Identity, these components will not function properly as they require authentication. The Orchestration Cluster (Zeebe, Operate, Tasklist, and Orchestration Cluster Admin) does not depend on Management Identity. ::: ## Web Modeler To enable Web Modeler, configure the required values in the Helm chart. For the full list of options, see the [Web Modeler Helm values](https://artifacthub.io/packages/helm/camunda/camunda-platform#webmodeler-parameters). - Set `webModeler.enabled: true` (disabled by default). - **Enable Management Identity** (required for authentication) - see [authentication and authorization](./authentication-and-authorization/index.md). - Configure your SMTP server under `webModeler.restapi.mail`. Web Modeler requires an SMTP server to send notification emails to users. - Configure the database connection. Web Modeler requires a PostgreSQL database for persistent storage. Other databases are not supported. - **Option 1:** Set `webModelerPostgresql.enabled: true` to install a new PostgreSQL instance using the [Bitnami PostgreSQL Helm chart](https://github.com/bitnami/charts/tree/main/bitnami/postgresql). - **Option 2:** Set `webModelerPostgresql.enabled: false` and connect to an external PostgreSQL instance. We recommend specifying values in a YAML file and passing it to the `helm install` command. Minimal configuration file: ```yaml webModeler: enabled: true restapi: mail: # Email address to be displayed as sender of emails from Web Modeler. fromAddress: no-reply@example.com smtpHost: smtp.example.com smtpPort: 587 smtpUser: user smtpPassword: secret # Also, the key "webModeler.restapi.mail.smtpPassword" could be used, # but it's not secure to save sensitive data in the values file. secret: existingSecret: "camunda-credentials-webmodeler" existingSecretKey: "webmodeler-smtp-user-password" webModelerPostgresql: enabled: true ``` To connect Web Modeler to an external database, set `webModelerPostgresql.enabled: false` and provide values under `webModeler.restapi.externalDatabase`: ```yaml webModeler: restapi: externalDatabase: url: jdbc:postgresql://postgres.example.com:5432/modeler-db user: modeler-user # Also, the key "webModeler.restapi.externalDatabase.password" could be used, # but it's not secure to save sensitive data in the values file. secret: existingSecret: "camunda-credentials-webmodeler" existingSecretKey: "webmodeler-postgresql-user-password" webModelerPostgresql: # Disables the PostgreSQL chart dependency. enabled: false ``` For more details, see the [Web Modeler Helm values](https://artifacthub.io/packages/helm/camunda/camunda-platform#webmodeler-parameters). ## Console Console Self-Managed is disabled by default in the Camunda 8 Helm chart. To enable it: - Set `console.enabled: true` in the values file. - **Enable Management Identity** (required for authentication) - see [authentication and authorization](./authentication-and-authorization/index.md). ```yaml console: enabled: true ``` For a full list of options, see the [Console Helm values](https://artifacthub.io/packages/helm/camunda/camunda-platform#console-parameters). :::note Console requires the Identity component for authentication. The Camunda Helm chart installs Identity by default. If you log in to Console using port-forward and use [Keycloak deployed via the Keycloak Operator](/self-managed/deployment/helm/configure/operator-based-infrastructure.md), you must also port-forward the Keycloak service: ``` kubectl port-forward svc/keycloak-service 18080:18080 ``` Alternatively, configure Identity with Ingress. See the [Ingress setup guide](/self-managed/deployment/helm/configure/ingress/ingress-setup.md). ::: ## Optimize Optimize is disabled by default in the Camunda 8 Helm chart. To enable it: - Set `optimize.enabled: true` in a values file. - **Enable Management Identity** (required for authentication) - see [authentication and authorization](./authentication-and-authorization/index.md). ```yaml optimize: enabled: true ``` For a full list of options, see the [Optimize Helm values](https://artifacthub.io/packages/helm/camunda/camunda-platform#optimize-parameters). --- ## Helm chart user guides Camunda 8 Self-Managed is highly customizable and can be deployed in different setups. This section highlights various use cases and scenarios of configuring Camunda 8 beyond the default values. Each guide is considered complete and a standalone use case. However, view the [deploying Camunda 8 using Helm charts](/self-managed/deployment/helm/install/quick-install.md) page, as this is the base for all guides. --- ## Helm chart configuration In this section, find details on configuration associated with Kubernetes with Helm. --- ## Helm chart without Ingress setup By default, the [Camunda Helm chart](/self-managed/deployment/helm/install/quick-install.md) does not expose the Camunda services externally. So to interact with the Camunda services inside a Kubernetes cluster without Ingress setup, you can use `kubectl port-forward` to route traffic from your local machine to the cluster. This is useful for quick tests or for development purposes. :::note You need to keep `port-forward` running all the time to communicate with the remote cluster. ::: ## Accessing workflow engine To interact with Camunda workflow engine via [Zeebe Gateway](/self-managed/components/orchestration-cluster/zeebe/configuration/gateway.md) using a local client/worker from outside the Kubernetes cluster, run `kubectl port-forward` to the Zeebe cluster as follows: ``` # gRPC kubectl port-forward svc/camunda-zeebe-gateway 26500:26500 # REST API kubectl port-forward svc/camunda-zeebe-gateway 8080:8080 ``` Now, you can connect and execute operations against your new Zeebe cluster. Port `26500` provides gRPC access, and port `8080` provides [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) access. :::note Accessing the Zeebe cluster directly using `kubectl port-forward` is recommended for development purposes. ::: ## Accessing web applications To interact with Camunda web applications like Operate, Tasklist, and Optimize, also `kubectl port-forward` will be used. :::note To use the web applications without Camunda Identity, you can set `global.identity.auth.enabled: false` in the values file to disable the authentication mechanism. Do _not_ disable it if you want to use Web Modeler, as it requires Camunda Identity and Keycloak. ::: First, port-forward for each application service: ```shell kubectl port-forward svc/camunda-optimize 8083:80 kubectl port-forward svc/camunda-connectors 8086:8080 ``` :::note The Zeebe Gateway port-forward on port `8080` (shown in the [workflow engine section](#accessing-workflow-engine) above) also serves the Orchestration web interface. ::: To be able to use Web Modeler, create additional port-forward commands for Web Modeler itself, and if you use [Keycloak deployed via the Keycloak Operator](/self-managed/deployment/helm/configure/operator-based-infrastructure.md), also port-forward the Keycloak service: ``` kubectl port-forward svc/camunda-web-modeler-restapi 8070:80 kubectl port-forward svc/camunda-web-modeler-websockets 8085:80 # Only if using Keycloak Operator kubectl port-forward svc/keycloak-service 18080:18080 ``` To use Console, create additional port-forward commands for Console. If you use [Keycloak deployed via the Keycloak Operator](/self-managed/deployment/helm/configure/operator-based-infrastructure.md), also port-forward the Keycloak service: ``` kubectl port-forward svc/camunda-console 8087:80 # Only if using Keycloak Operator kubectl port-forward svc/keycloak-service 18080:18080 ``` Finally, you can access each app pointing your browser at: - Orchestration: [http://localhost:8080](http://localhost:8080) - Optimize: [http://localhost:8083](http://localhost:8083) - Web Modeler: [http://localhost:8070](http://localhost:8070) - Console: [http://localhost:8087](http://localhost:8087) Log in to these services using the default first user credentials `demo`/`demo`. These defaults come from the Helm chart value `orchestration.security.initialization.users` (which seeds the `demo` user with password `demo` for the orchestration cluster). If you have overridden these values or use a custom identity provider, use the credentials you configured instead.
Operate and Tasklist Login
Operate and Tasklist Dashboard
If you deploy process definitions, they will appear in the dashboard. Then, you can drill down to see your active instances. You can deploy and create new instances using the Zeebe clients. You can also trigger **Connectors** inbound webhook, given you deployed one. You can do so with the following example: `curl -X POST -H "Content-Type: application/json" -d '{"myId": 123456, "myMessage": "Hello, world!"}' http://localhost:8086/inbound/`. --- ## Configure the Helm chart with Gateway API Use this guide to configure the Camunda 8 Helm chart with the Kubernetes Gateway API instead of a traditional Ingress controller. The Gateway API provides a modern way to manage Ingress traffic in Kubernetes clusters. It improves on the Ingress API in the following ways: - Separates cluster operators, who manage Gateway resources, from application developers, who manage HTTPRoute resources. - Enables configuration of NGINX without relying on labels and annotations, which also helps limit permissions. :::note The Ingress-NGINX controller is planned to reach end of life in March 2026 (see [the Kubernetes announcement on Ingress-NGINX retirement](https://www.kubernetes.dev/blog/2025/11/12/ingress-nginx-retirement/)). Plan a migration to the Gateway API where it fits your use case. If you decide not to adopt the Gateway API, you can migrate to a different Ingress controller and continue using the Ingress API. This remains a supported approach. ::: ## Prerequisites Ensure both are installed in your cluster. - Gateway API CRDs - A Gateway API controller ### Gateway controllers Just like Ingress Controllers, Gateway controllers need to be installed before a cluster can use the Gateway API. [See the list of Gateway API implementations](https://gateway-api.sigs.k8s.io/implementations/) for details. In testing, we use the [NGINX Gateway Fabric](https://github.com/nginx/nginx-gateway-fabric). ## Configure the Helm chart | Parameter | Type | Default | Description | | -------------------------------------- | ------- | ------- | ------------------------------------------------------------------------------------------------------------------------- | | `global.host` | string | `""` | The external-facing URL hostname where Camunda will be installed. | | `global.gateway.enabled` | boolean | `false` | Enable creating resources for the Kubernetes Gateway API. | | `global.gateway.createGatewayResource` | boolean | `true` | Create the Gateway CustomResource. Do not enable if you already have a Gateway resource. | | `global.gateway.external` | boolean | `true` | Set this to true if you are using the Gateway API but want to create the resources yourself. | | `global.gateway.className` | string | `""` | Name of the GatewayClass resource that defines which Gateway controller operates on your Gateway and HTTPRoute resources. | | `global.gateway.labels` | map | `{}` | Labels to add to the Gateway and HTTPRoute resources. | | `global.gateway.annotations` | map | `{}` | Annotations to add to the Gateway and HTTPRoute resources. | | `global.gateway.tls.enabled` | boolean | `false` | Enable TLS. | | `global.gateway.tls.secretName` | string | `""` | Name of the Kubernetes Secret resource containing a TLS cert | | `global.gateway.controllerNamespace` | string | `""` | The namespace where the Gateway controller is installed. | ## Example configuration ```yaml global: host: "camunda.example.com" gateway: createGatewayResource: true enabled: true className: nginx tls: enabled: true secretName: camunda-platform annotations: external-dns.alpha.kubernetes.io/hostname: "{{ .Values.global.gateway.hostname }}" external-dns.alpha.kubernetes.io/ttl: "60" ``` ### NGINX Gateway Fabric: ProxySettingsPolicy If you are using the Gateway API with the NGINX Gateway Fabric, the default proxy buffer size is likely too small. [ProxySettingsPolicy documentation](https://docs.nginx.com/nginx-gateway-fabric/traffic-management/proxy-settings/). You may need to install a CRD to be able to create ProxySettingsPolicy resources. This can be found here: [CRD location](https://github.com/nginx/nginx-gateway-fabric/tree/main/config/crd/bases) An error that might indicate you need to change something is: > 502: upstream sent too big header while reading response header from upstream ```yaml apiVersion: gateway.nginx.org/v1alpha1 kind: ProxySettingsPolicy metadata: name: camunda-platform namespace: camunda spec: buffering: bufferSize: 128k buffers: number: 8 size: 128k busyBuffersSize: 256k targetRefs: - group: gateway.networking.k8s.io kind: Gateway name: camunda-platform ``` --- ## Helm chart Ingress configuration In this section, find details on Ingress configuration associated with Kubernetes with Helm. --- ## Configure the Helm chart with Ingress :::caution Starting with Camunda 8.8, the separated Ingress configuration is no longer supported. Instead, follow the combined Ingress setup described in this guide. If you want to replicate the behavior of the previous separated Ingress approach, check separated Ingress migration. ::: Camunda 8 Self-Managed has multiple web applications and gRPC services. You can expose them externally with a combined Ingress setup. ## Prerequisites - An Ingress controller deployed in advance. The examples below use the [ingress-nginx controller](https://github.com/kubernetes/ingress-nginx), but you can use any Ingress controller by setting `ingress.className`. - TLS configuration is not included in the examples because it varies between different workflows. Configure TLS in one of these ways: - Use `ingress.tls` options directly. - Use an external tool such as [Cert-Manager](https://github.com/cert-manager/cert-manager) with `ingress.annotations`. For more information, see the available [configuration options](https://artifacthub.io/packages/helm/camunda/camunda-platform#configuration). :::note Camunda 8 Helm chart only deploys Ingress resources. It does not manage or deploy Ingress controllers. You must have an Ingress controller running in your cluster. ::: ## Configuration In this configuration, two Ingress objects are created: - **Web applications**: One Ingress object for all Camunda 8 web applications using one domain. Each application has a sub-path. For example, `camunda.example.com/operate`, `camunda.example.com/optimize`. - **Zeebe Gateway**: Another Ingress object using the gRPC protocol for Zeebe Gateway. For example, `zeebe.camunda.example.com`. By default, all web applications use `/` as a base, so we just need to set the context path, Ingress configuration, and authentication redirect URLs. :::note For Operate, Tasklist, Optimize, Modeler, Connectors, and Console, the Ingress path (`global.identity.auth..redirectUrl`) must match the `contextPath` for that component. ::: ### Example configuration ```yaml # Chart values for the Camunda 8 Helm chart in combined Ingress setup. # This file deliberately contains only the values that differ from the defaults. # For changes and documentation, use your favorite diff tool to compare it with: # https://artifacthub.io/packages/helm/camunda/camunda-platform # IMPORTANT: Make sure to change "camunda.example.com" to your domain. global: ingress: enabled: true className: nginx host: "camunda.example.com" identity: auth: publicIssuerUrl: "https://camunda.example.com/auth/realms/camunda-platform" optimize: redirectUrl: "https://camunda.example.com/optimize" webModeler: redirectUrl: "https://camunda.example.com/modeler" console: redirectUrl: "https://camunda.example.com/console" identity: contextPath: "/identity" fullURL: "https://camunda.example.com/identity" optimize: contextPath: "/optimize" orchestration: contextPath: "/orchestration" ingress: grpc: enabled: true className: nginx host: "zeebe.camunda.example.com" webModeler: # The context path is used for the web application that will be accessed by users in the browser. # In addition, a WebSocket endpoint will be exposed on "[contextPath]-ws", e.g. "/modeler-ws". contextPath: "/modeler" console: contextPath: "/console" connectors: contextPath: "/connectors" ``` Incorporate these custom values into the values file you use to deploy Camunda (see [Install Camunda with Helm](/self-managed/deployment/helm/install/quick-install.md)): ```shell helm install camunda camunda/camunda-platform --version $HELM_CHART_VERSION -f values-combined-ingress.yaml ``` After deployment, access the Camunda 8 components at: - **Management Applications:** `https://camunda.example.com/[identity|modeler|console]` - **Core Orchestration Applications and REST API:** `https://camunda.example.com/orchestration/[identity|operate|optimize|tasklist|v2]` - **Web Modeler WebSocket:** Web Modeler exposes a WebSocket endpoint on `https://camunda.example.com/modeler-ws`. This is only used internally by the application and not for direct user access. - **Keycloak authentication:** `https://camunda.example.com/auth` - **Zeebe gRPC Gateway:** `grpc://zeebe.camunda.example.com` :::note This configuration shows only the Ingress-related values for `webModeler`and `Console`. For full setup, see [Enable additional components](/self-managed/deployment/helm/configure/enable-additional-components.md). ::: ## Separated Ingress migration As the separated Ingress was removed in Camunda 8.8, there are two options for migration: 1. **Recommended:** Use the [combined Ingress configuration](#configuration). 2. **Alternative:** You can use global.extraManifests to define and deploy your own Ingress objects, providing functionality similar to the former separated Ingress setup. The example below demonstrates how to add an Ingress object for Optimize, replicating the separated Ingress behavior. You can apply the same approach to create additional Ingress objects for other components, as needed. ```yaml # values-separated-ingress.yaml global: extraManifests: - | --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: camunda-platform-optimize namespace: camunda-dev annotations: nginx.ingress.kubernetes.io/backend-protocol: HTTP nginx.ingress.kubernetes.io/proxy-body-size: 10m nginx.ingress.kubernetes.io/proxy-buffer-size: 128k nginx.ingress.kubernetes.io/proxy-buffering: "on" nginx.ingress.kubernetes.io/rewrite-target: / nginx.ingress.kubernetes.io/ssl-redirect: "false" spec: ingressClassName: nginx rules: - host: optimize.example.com http: paths: - backend: service: name: camunda-platform-optimize port: number: 80 path: / pathType: Prefix tls: - hosts: - optimize.example.com secretName: camunda-platform-optimize-tls - | [Add more Ingresses as needed] ``` Please note that, you need to create all resources referenced by the objects in `global.extraManifests`, such as the TLS secret `camunda-platform-optimize-tls`. ## Ingress controllers Ingress resources require the cluster to have a running [Ingress Controller](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/). There are many options for configuring your Ingress Controller. If you use a cloud provider such as AWS or GCP, follow their Ingress setup guides if an Ingress Controller is not already pre-installed. For AWS EKS Ingress configuration, see [Install Camunda 8 on an EKS cluster](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/eks-helm.md). ### Local setup example An Ingress controller is also required for local Camunda 8 installation. The following example shows an Ingress controller configuration using the [ingress-nginx controller](https://kubernetes.github.io/ingress-nginx/deploy/#bare-metal-clusters/): ```yaml # ingress_nginx_values.yml controller: updateStrategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1 service: type: NodePort publishService: enabled: false ``` Install the [ingress-nginx controller](https://github.com/kubernetes/ingress-nginx) to your local cluster: ```shell helm install -f ingress_nginx_values.yml \ ingress-nginx ingress-nginx \ --repo https://kubernetes.github.io/ingress-nginx \ --version "4.9.0" \ --namespace ingress-nginx \ --create-namespace ``` ## Troubleshooting If Ingress is not working as expected, see [Camunda components troubleshooting](self-managed/operational-guides/troubleshooting.md). --- ## Configure license key Use this page to configure an enterprise license key for Camunda 8 components. You can either enter the key directly in your `values.yaml` file or reference an existing Kubernetes secret. ## Configuration ### Helm values Camunda 8 components consume enterprise license information through the following Helm configuration: ```yaml global: ## License configuration. ## @extra global.license license: ## @extra global.license.secret configuration to provide the license secret. secret: ## @param global.license.secret.inlineSecret can be used to provide the license as a plain-text value for non-production usage. inlineSecret: "" ## @param global.license.secret.existingSecret can be used to reference an existing Kubernetes Secret containing the license. existingSecret: "" ## @param global.license.secret.existingSecretKey defines the key within the existing secret object. existingSecretKey: "" ``` ### Provide the key directly Enter your license key directly in `global.license.key`. ```yaml global: license: secret: inlineSecret: >- --------------- BEGIN CAMUNDA LICENSE KEY --------------- [...] --------------- END CAMUNDA LICENSE KEY --------------- ``` ### Provide the key with a secret You can also store the license key in a Kubernetes secret and reference it from `values.yaml`. For more details on working with secrets, see [Secret management](/self-managed/deployment/helm/configure/secret-management.md). 1. Create the secret: ```yaml apiVersion: v1 kind: Secret metadata: name: camunda-license stringData: key: >- --------------- BEGIN CAMUNDA LICENSE KEY --------------- [...] --------------- END CAMUNDA LICENSE KEY --------------- ``` 2. Reference the secret in `values.yaml`: ```yaml global: license: secret: existingSecret: "camunda-license" existingSecretKey: "key" ``` :::note Camunda 8 components without a valid license may display **Non-Production License** in the navigation bar and log warnings. These warnings do not impact startup or functionality, except that Web Modeler is limited to five users. ::: --- ## Deploy required dependencies with Kubernetes operators This guide explains how to deploy Camunda 8 infrastructure components using **official Kubernetes operators** as an alternative to the Bitnami subcharts. This approach provides production-grade, officially maintained deployment solutions for PostgreSQL, Elasticsearch, and Keycloak. ## Overview :::info New in Camunda 8.8 Starting with Camunda 8.8, we continue to strengthen our commitment to robust, production-ready deployments based on solid foundations. ::: As outlined in [our strategy](https://camunda.com/blog/2025/08/changes-to-camunda-helm-sub-charts-what-you-need-to-know/), Camunda reinforces building deployments on solid foundations—primarily managed PostgreSQL and Elasticsearch services, along with external OIDC providers. However, we understand that managed infrastructure components aren't always available in your organization's service catalog. This guide demonstrates how to integrate these infrastructure components using official Kubernetes operators that don't depend on Bitnami subcharts. These operators are the recommended way to deploy and manage these services in production environments. :::warning Support scope PostgreSQL, Elasticsearch, and Keycloak are **external dependencies** — they are not Camunda products, regardless of the deployment method used. - **Camunda support scope**: Camunda supports the **integration and configuration** of these components with the Camunda Helm chart. Camunda does not provide operational support for the infrastructure components themselves. - **Operator support**: For operational support on infrastructure components, engage the respective project teams or community support channels directly (CloudNativePG, Elastic, Keycloak), or use managed services. ::: :::note Alternative: Bitnami Enterprise Images If you prefer to continue using Bitnami subcharts, you can enable them by using Bitnami Enterprise images. See [Install Bitnami enterprise images](/self-managed/deployment/helm/configure/registry-and-images/install-bitnami-enterprise-images.md) for detailed instructions. ::: ## Why use Kubernetes operators? Using official Kubernetes operators provides several advantages over traditional subcharts: - **Vendor maintenance**: Each deployment method is maintained by the respective project team (Elastic, CloudNativePG community, Keycloak team) with dedicated engineering resources - **Production-grade features**: Built-in management, monitoring, and scaling capabilities designed for enterprise environments - **Vendor support channels**: Official support channels, dedicated vendor support teams, and comprehensive documentation available directly from each project - **Security-focused**: Regular updates and CVE patches from upstream maintainers with specialized security teams - **Advanced lifecycle management**: Automated upgrades, failover, and disaster recovery capabilities - **Best practices implementation**: Following upstream recommended deployment patterns established by vendor experts - **Vendor expertise**: Access to specialized knowledge and troubleshooting from the teams that build these technologies (through vendor support channels) - **Future-proof architecture**: Doesn't depend on deprecated Bitnami subcharts, ensuring long-term maintainability ## Prerequisites Before proceeding with this guide, ensure you have: - **Kubernetes cluster**: A functioning cluster with `kubectl` access and block-storage persistent volumes - **Cluster admin privileges**: Required to install Custom Resource Definitions (CRDs) and operators - **Command-line tools**: - `kubectl` configured to access your cluster - `helm` CLI for deploying Camunda using the Helm chart - `openssl` for generating random passwords - `envsubst` command (part of `gettext` package) for environment variable substitution ## Architecture overview This deployment approach separates infrastructure management from application deployment: ![Operator-based infrastructure architecture](assets/vendor-components-arch.jpg) ## Infrastructure components This approach uses three operator-managed infrastructure components, each maintained by their respective project teams: | Component | Purpose | Official Documentation | | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | | **[PostgreSQL with CloudNativePG](#postgresql-deployment)** | Production-grade PostgreSQL clusters for Keycloak, Management Identity, and Web Modeler databases | [CloudNativePG Documentation](https://cloudnative-pg.io/docs/1.28/) | | **[Elasticsearch with ECK](#elasticsearch-deployment)** | Official Elasticsearch deployment for Zeebe records, Operate, Tasklist, and Optimize data storage | [ECK Guide](https://www.elastic.co/guide/en/cloud-on-k8s/current/index.html) | | **[Keycloak with Keycloak Operator](#keycloak-deployment)** | Automated OIDC authentication provider for Management Identity | [Keycloak Operator Documentation](https://www.keycloak.org/operator/installation) | ## Quick start ### Step 1: Get deployment resources All configuration files, deployment scripts, and automation tools referenced in this guide are available in the Camunda deployment references repository: **Repository**: [camunda-deployment-references](https://github.com/camunda/camunda-deployment-references/tree/main/generic/kubernetes/operator-based)
Quick deployment commands ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/get-your-copy.sh ``` Then execute: ```bash # Set up environment (required for all deployments) source ./0-set-environment.sh # Review and deploy infrastructure components in order # PostgreSQL deployment cd postgresql/ cat deploy.sh # Review the deployment script ./deploy.sh # Elasticsearch deployment cd ../elasticsearch/ cat deploy.sh # Review the deployment script ./deploy.sh # Keycloak deployment cd ../keycloak/ cat deploy.sh # Review the deployment script ./deploy.sh ```
The deployment scripts (`deploy.sh`) contain all the necessary steps to install each component. You can either execute them directly or use them as reference for manual deployment or GitOps integration. ### Step 2: Environment setup All deployment scripts require environment variables to be set. This is a prerequisite for all subsequent steps: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/0-set-environment.sh ``` :::note Ensure you source this environment setup before running any deployment scripts in the following sections. ::: ### Step 3: Deployment overview Each infrastructure component should be deployed individually in the following order: | Order | Component | Dependencies | Purpose | | ----- | ---------------------------------------------- | ------------ | -------------------------------------------------------------------- | | 1 | **[PostgreSQL](#postgresql-deployment)** | None | Database clusters for Keycloak, Management Identity, and Web Modeler | | 2 | **[Elasticsearch](#elasticsearch-deployment)** | None | Secondary storage for orchestration cluster components | | 3 | **[Keycloak](#keycloak-deployment)** | PostgreSQL | Authentication and identity management | | 4 | **[Camunda](#camunda-deployment)** | All above | Deploy using Helm with operator-managed infrastructure | :::tip Automation with GitOps While this guide demonstrates manual deployment using command-line tools, these same configurations can be automated using GitOps solutions like ArgoCD, Flux, or other Kubernetes deployment pipelines. All configuration files referenced in this guide are designed to work seamlessly with declarative deployment approaches. ::: ## PostgreSQL deployment ### Overview [CloudNativePG](https://cloudnative-pg.io/) is a CNCF project that provides the official Kubernetes deployment method for PostgreSQL. It's designed specifically for cloud-native environments with enterprise-grade features including automated backups, point-in-time recovery, and rolling updates. **Official documentation**: [CloudNativePG Documentation](https://cloudnative-pg.io/docs/1.28/) ### Architecture Our setup provisions three separate PostgreSQL clusters for different Camunda components. Use the latest PostgreSQL version listed in our [supported environments matrix](/reference/supported-environments.md) that is compatible across the required components: - **pg-identity**: Database for Camunda Identity component - **pg-keycloak**: Database for Keycloak identity service - **pg-webmodeler**: Database for Web Modeler component :::note Component flexibility If you don't plan to use certain components (for example, Web Modeler), you can simply remove the corresponding cluster definition from the configuration before deployment. This allows you to deploy only the PostgreSQL clusters you actually need, reducing resource consumption. ::: ### Installation **Prerequisites**: Ensure environment variables are sourced (see [Environment setup](#step-2-environment-setup)) The PostgreSQL deployment follows these steps, automated via the `postgresql/deploy.sh` script: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/postgresql/deploy.sh ``` **Deployment steps performed by the script:** - Auto-detect OpenShift and apply Security Context Constraints (SCC) patches if needed - Install CloudNativePG operator to `cnpg-system` namespace - Generate PostgreSQL authentication secrets using `./set-secrets.sh` - Deploy PostgreSQL clusters from `postgresql-clusters.yml` (optionally filtered via `CLUSTER_FILTER` environment variable) - Wait for readiness validation of all deployed clusters #### Operator Custom Resources This configuration creates three dedicated PostgreSQL clusters, each optimized for its specific use case. **Save as** `postgresql-clusters.yml`: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/postgresql/postgresql-clusters.yml ``` **Use cases:** - `pg-keycloak`: Database for Keycloak authentication - `pg-identity`: Database for Management Identity component - `pg-webmodeler`: Database for Web Modeler component #### Execution 1. **Navigate to PostgreSQL directory**: `cd postgresql/` 2. **Review deployment script**: `cat deploy.sh` to understand the deployment steps 3. **Review cluster configuration**: `cat postgresql-clusters.yml` to verify PostgreSQL cluster settings 4. **Adapt configuration if needed**: Modify `postgresql-clusters.yml` for your specific requirements (resource limits, storage, etc.) 5. **Execute deployment**: `./deploy.sh` :::note OpenShift compatibility The `deploy.sh` script automatically detects OpenShift environments and applies the necessary Security Context Constraints (SCC) patches for CloudNativePG compatibility. No separate script is required. ::: ### Camunda Helm configuration The following configuration files integrate PostgreSQL clusters with Camunda components. **Save these files locally** and include them in your Helm installation command. Configure Camunda Identity to use the PostgreSQL cluster. **Save as** `camunda-identity-values.yml`: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/postgresql/camunda-identity-values.yml ``` **Installation**: Add `-f camunda-identity-values.yml` to your Helm install command. Configure Web Modeler to use the PostgreSQL cluster. **Save as** `camunda-webmodeler-values.yml`: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/postgresql/camunda-webmodeler-values.yml ``` **Installation**: Add `-f camunda-webmodeler-values.yml` to your Helm install command. ## Elasticsearch deployment ### Overview [Elastic Cloud on Kubernetes (ECK)](https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s) is the official Kubernetes deployment method for Elasticsearch, maintained by Elastic. ECK provides the vendor-recommended approach for deploying Elasticsearch in Kubernetes environments, automatically handling cluster deployment, scaling, upgrades, and security configuration. Use the latest Elasticsearch version listed in our [supported environments matrix](../../../../reference/supported-environments.md) and verify compatibility there before deploying. **Official documentation**: [ECK Guide](https://www.elastic.co/guide/en/cloud-on-k8s/current/index.html) ### Architecture The ECK deployment creates an Elasticsearch cluster with: - **Three multi-role nodes**: Each node is master-eligible and also serves data, ingest, and coordinating roles (no separate master-only tier) - **Security configuration**: TLS disabled for internal communication (can be enabled for production) - **Anti-affinity rules**: Ensures nodes are distributed across different Kubernetes nodes - **Resource optimization**: Configured for Camunda's specific requirements :::note Baseline This topology is an opinionated minimal baseline. Adjust node count/roles (e.g., add dedicated ingest, coordinating, or hot/warm tiers), JVM heap, storage class/size, security (TLS & auth), and other settings to match your workload characteristics, retention, and compliance requirements. ::: **Elasticsearch as the secondary storage for Camunda 8:** Elasticsearch serves as the secondary storage for Camunda 8 orchestration cluster components, providing persistent storage and search capabilities. [Learn more about the secondary storage](/self-managed/concepts/secondary-storage/index.md) and how it supports advanced features like web applications, search APIs, process monitoring, task management, and analytics. ### Installation **Prerequisites**: Ensure environment variables are sourced (see [Environment setup](#step-2-environment-setup)) The Elasticsearch deployment follows these steps, automated via the `elasticsearch/deploy.sh` script: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/elasticsearch/deploy.sh ``` **Deployment steps performed by the script:** - Install ECK Custom Resource Definition - Deploy ECK operator to `elastic-system` namespace - Create Elasticsearch cluster from `elasticsearch-cluster.yml` - Wait for cluster health validation #### Operator Custom Resources This configuration creates a production-ready Elasticsearch cluster with security enabled. **Save as** `elasticsearch-cluster.yml`: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/elasticsearch/elasticsearch-cluster.yml ``` #### Execution 1. **Navigate to Elasticsearch directory**: `cd ../elasticsearch/` 2. **Review deployment script**: `cat deploy.sh` to understand the deployment steps 3. **Review cluster configuration**: `cat elasticsearch-cluster.yml` to verify Elasticsearch cluster settings 4. **Adapt configuration if needed**: Modify `elasticsearch-cluster.yml` for your specific requirements (node count, resources, security settings, etc.) 5. **Execute deployment**: `./deploy.sh` ### Camunda Helm configuration The following configuration integrates ECK-managed Elasticsearch with Camunda components. **Save this file locally** and include it in your Helm installation command. Configure Camunda components to use the ECK-managed Elasticsearch. **Save as** `camunda-elastic-values.yml`: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/elasticsearch/camunda-elastic-values.yml ``` **Use case**: External Elasticsearch connection for all orchestration cluster components (Zeebe, Operate, Tasklist, Optimize). **Installation**: Add `-f camunda-elastic-values.yml` to your Helm install command. ## Keycloak deployment ### Overview The [Keycloak Operator](https://www.keycloak.org/operator/installation) provides the official operator-based way to deploy and manage Keycloak instances on Kubernetes. Maintained by the Keycloak team, it provides the recommended approach for automated deployment, configuration, and lifecycle management. Use the latest Keycloak version listed in our [supported environments matrix](/reference/supported-environments.md). We use the Camunda-maintained quay-optimized Keycloak image [camunda/keycloak:quay-optimized-version](https://github.com/camunda/keycloak) as it bundles the Camunda Identity login theme, the `/auth` base path, the AWS JDBC wrapper, and pre-baked configuration. **Official documentation**: [Keycloak Operator Documentation](https://www.keycloak.org/operator/installation) ### Architecture The Keycloak deployment provides: - **Database integration**: Connects to CloudNativePG-managed PostgreSQL cluster - **Authentication path**: Configured to serve under `/auth` path prefix - **Flexible domain support**: Options for local development, [ingress-nginx](https://kubernetes.github.io/ingress-nginx/), or [OpenShift routes](https://docs.redhat.com/en/documentation/openshift_container_platform/4.11/html/networking/configuring-routes) - **Resource optimization**: Sized appropriately for typical Camunda authentication loads - **Custom Ingress management**: Uses dedicated Ingress manifests integrated within the operator configuration for subpath management constraints ### Ingress management Due to subpath management constraints, the Keycloak operator's built-in Ingress configuration is disabled in favor of dedicated Ingress manifests. This approach provides better control over path routing and TLS certificate management when serving Keycloak under the `/auth` path prefix. The dedicated Ingress configuration is integrated directly within the operator manifest to ensure proper deployment coordination and resource management. ### Installation **Prerequisites**: - Ensure environment variables are sourced (see [Environment setup](#step-2-environment-setup)) - PostgreSQL must be deployed first (Keycloak requires database) The Keycloak deployment follows these steps, automated via the `keycloak/deploy.sh` script: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/keycloak/deploy.sh ``` **Deployment steps performed by the script:** - Install Keycloak Custom Resource Definitions - Deploy Keycloak operator to the target namespace - Create Keycloak instance from the selected configuration file - Wait for Keycloak readiness validation #### Operator Custom Resources Basic Keycloak instance for local development. **Save as** `keycloak-instance-no-domain.yml`: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/keycloak/keycloak-instance-no-domain.yml ``` **Use case**: Local development and testing without external domain. :::note Local hostname configuration In certain setups, Keycloak is configured to use its service name as the hostname, which may result in redirections. For local deployments, you need to add the Keycloak service name to your local hosts file (`/etc/hosts` on Linux and macOS) by adding the entry `127.0.0.1 keycloak-service` and use this hostname to access Keycloak. ::: Production Keycloak instance with nginx-ingress. **Save as** `keycloak-instance-domain-nginx.yml`: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/keycloak/keycloak-instance-domain-nginx.yml ``` **Use case**: Production deployment with external domain using [nginx-ingress controller](https://kubernetes.github.io/ingress-nginx/). Keycloak instance configured for OpenShift Routes. **Save as** `keycloak-instance-domain-openshift.yml`: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/keycloak/keycloak-instance-domain-openshift.yml ``` **Use case**: [OpenShift](https://docs.redhat.com/en/documentation/openshift_container_platform/4.11/html/networking/configuring-routes) deployment using native Route resources. #### Execution 1. **Navigate to Keycloak directory**: `cd ../keycloak/` 2. **Review deployment script**: `cat deploy.sh` to understand the deployment steps 3. **Review instance configuration**: `cat keycloak-instance-no-domain.yml` to verify Keycloak instance settings 4. **Adapt configuration if needed**: Choose appropriate instance configuration for your setup: - `keycloak-instance-no-domain.yml` for local development - `keycloak-instance-domain-nginx.yml` for nginx-ingress - `keycloak-instance-domain-openshift.yml` for OpenShift Routes 5. **Execute deployment**: `./deploy.sh` ### Camunda Helm configuration The following configurations integrate Keycloak with Camunda Identity. **Save the appropriate file locally** based on your deployment setup and include it in your Helm installation command. Configure Camunda to use Keycloak for local development. **Save as** `camunda-keycloak-no-domain-values.yml`: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/keycloak/camunda-keycloak-no-domain-values.yml ``` **Use case**: Local development setup with port-forwarding access. **Installation**: Add `-f camunda-keycloak-no-domain-values.yml` to your Helm install command. Configure Camunda to use Keycloak with external domain. **Save as** `camunda-keycloak-domain-values.yml`: ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/keycloak/camunda-keycloak-domain-values.yml ``` :::note Domain configuration step This configuration file contains `${CAMUNDA_DOMAIN}` placeholder variables that must be replaced with your actual domain before deployment. **Options for domain injection:** - **Automatic substitution**: Use `envsubst < camunda-keycloak-domain-values.yml > camunda-keycloak-domain-values-final.yml` (requires `CAMUNDA_DOMAIN` environment variable) - **Manual replacement**: Replace all instances of `${CAMUNDA_DOMAIN}` with your actual domain name in `camunda-keycloak-domain-values.yml` ::: **Use case**: Production setup with external domain and proper OIDC configuration. **Installation**: Add `-f camunda-keycloak-domain-values.yml` to your Helm install command. ## Camunda deployment With all infrastructure components deployed and configured, you can now deploy Camunda using the helm chart. ### Prerequisites **Prerequisites**: - Ensure environment variables are sourced (see [Environment setup](#step-2-environment-setup)) - All infrastructure components (PostgreSQL, Elasticsearch, Keycloak) must be deployed first - Save all configuration files from previous sections locally ### Configuration files summary Before deploying Camunda, ensure you have saved all required configuration files locally. The files are organized by deployment phase: #### Infrastructure deployment files (Custom Resources) | Component | File Name | Purpose | Required for | | -------------------- | ---------------------------------------- | -------------------------------- | ------------------------- | | PostgreSQL | `postgresql-clusters.yml` | PostgreSQL cluster definitions | Infrastructure deployment | | Elasticsearch | `elasticsearch-cluster.yml` | Elasticsearch cluster definition | Infrastructure deployment | | Keycloak (Local) | `keycloak-instance-no-domain.yml` | Local Keycloak instance | Infrastructure deployment | | Keycloak (NGINX) | `keycloak-instance-domain-nginx.yml` | Production Keycloak with NGINX | Infrastructure deployment | | Keycloak (OpenShift) | `keycloak-instance-domain-openshift.yml` | OpenShift Keycloak instance | Infrastructure deployment | #### Camunda integration files (Helm values) | Component | File Name | Purpose | Required for | | ------------------------ | --------------------------------------- | ------------------------------------------ | ------------------ | | Elasticsearch | `camunda-elastic-values.yml` | Connects to ECK-managed Elasticsearch | Camunda deployment | | PostgreSQL (Identity) | `camunda-identity-values.yml` | Connects Identity to PostgreSQL cluster | Camunda deployment | | PostgreSQL (Web Modeler) | `camunda-webmodeler-values.yml` | Connects Web Modeler to PostgreSQL cluster | Camunda deployment | | Keycloak (Local) | `camunda-keycloak-no-domain-values.yml` | Local development OIDC configuration | Camunda deployment | | Keycloak (Production) | `camunda-keycloak-domain-values.yml` | Production OIDC configuration | Camunda deployment | ### Pre-deployment checklist Before deploying Camunda, ensure you have completed the following: - [ ] All infrastructure components deployed (PostgreSQL, Elasticsearch, Keycloak) - [ ] Configuration files saved locally from previous sections - [ ] Authentication secrets generated (previous section) ### Helm deployment First, source the environment setup script to set `HELM_CHART_VERSION` and other required variables. See the [Helm chart version matrix](https://helm.camunda.io/camunda-platform/version-matrix/) to choose the appropriate chart version for your deployment: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/0-set-environment.sh ``` Then, deploy Camunda using the infrastructure configuration files you saved from previous sections. For end-to-end configuration patterns (OIDC-enabled "Full Cluster" including Optimize, Web Modeler, Console, and Identity), see the Full Cluster section of our [Helm installation guide](/self-managed/deployment/helm/install/quick-install.md#full-cluster). Deploy Camunda with external domain configuration: ```bash helm install "$CAMUNDA_RELEASE_NAME" camunda/camunda-platform \ --version $HELM_CHART_VERSION \ -f camunda-elastic-values.yml \ -f camunda-identity-values.yml \ -f camunda-webmodeler-values.yml \ -f camunda-keycloak-domain-values.yml \ -n "$CAMUNDA_NAMESPACE" ``` Deploy Camunda for local development: ```bash helm install "$CAMUNDA_RELEASE_NAME" camunda/camunda-platform \ --version $HELM_CHART_VERSION \ -f camunda-elastic-values.yml \ -f camunda-identity-values.yml \ -f camunda-webmodeler-values.yml \ -f camunda-keycloak-no-domain-values.yml \ -n "$CAMUNDA_NAMESPACE" ``` :::tip Helm value files Order & precedence: The order of `-f` flags matters—later files override earlier ones, so place the most specific/override files (e.g. secrets, domain-specific settings) last. File origin: Every `-f` file corresponds to a configuration you saved in previous sections (Elasticsearch integration, PostgreSQL clusters, Keycloak, Identity secrets). Make sure they're present locally and reflect any custom adjustments before running the command. Component flexibility: Drop files for components you don't deploy (for example, remove `camunda-webmodeler-values.yml` if you're not using Web Modeler) to reduce footprint. ::: ## Verification and troubleshooting ### Verify infrastructure deployment Check that all infrastructure components are running correctly: ```bash # Check PostgreSQL clusters kubectl get clusters -n $CAMUNDA_NAMESPACE # Verify services kubectl get svc -n $CAMUNDA_NAMESPACE | grep "pg-" # Check cluster status kubectl describe cluster pg-identity -n $CAMUNDA_NAMESPACE ``` ```bash # Check Elasticsearch cluster kubectl get elasticsearch -n $CAMUNDA_NAMESPACE # Verify services kubectl get svc -n $CAMUNDA_NAMESPACE | grep "elasticsearch" # Check cluster health kubectl get elasticsearch elasticsearch -n $CAMUNDA_NAMESPACE -o jsonpath='{.status.health}' ``` ```bash # Check Keycloak instance kubectl get keycloak -n $CAMUNDA_NAMESPACE # Verify services kubectl get svc -n $CAMUNDA_NAMESPACE | grep keycloak # Check readiness kubectl get keycloak keycloak -n $CAMUNDA_NAMESPACE -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' ``` ### Common issues and solutions #### PostgreSQL cluster not starting **Symptoms:** PostgreSQL pods stuck in pending or crash loop **Solutions:** - Verify persistent volume claims are bound: `kubectl get pvc -n $CAMUNDA_NAMESPACE` - Check node resources and storage availability - Review CloudNativePG operator logs: `kubectl logs -n cnpg-system deployment/cnpg-controller-manager` **Reference:** [CloudNativePG Troubleshooting](https://cloudnative-pg.io/docs/1.28/troubleshooting) #### Elasticsearch cluster yellow/red status **Symptoms:** Elasticsearch cluster health is not green **Solutions:** - Check disk space and memory allocation - Verify all nodes are running: `kubectl get pods -n $CAMUNDA_NAMESPACE -l elasticsearch.k8s.elastic.co/cluster-name=elasticsearch` - Review ECK operator logs: `kubectl logs -n elastic-system statefulset/elastic-operator` **Reference:** [ECK Troubleshooting Guide](https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-troubleshooting.html) #### Keycloak authentication errors **Symptoms:** Camunda components cannot authenticate with Keycloak **Solutions:** - Verify Keycloak is accessible: `kubectl port-forward svc/keycloak-service 18080:18080 -n $CAMUNDA_NAMESPACE` :::note This uses `keycloak-service` (the service name created by the Keycloak Operator) and port `18080` (configured via `httpPort` in the Keycloak CR for local deployments). This differs from Helm chart deployments which use `camunda-keycloak` service name and port `80`. ::: - Check client configurations in Keycloak admin console - Verify redirect URLs match your deployment setup **Reference:** [Keycloak Operator Documentation](https://www.keycloak.org/operator/basic-deployment) ## Production considerations ### Security - **Network policies**: Implement network policies to restrict traffic between components - **TLS encryption**: Enable TLS for all inter-component communication - **Secret management**: Use external secret management systems in production - **RBAC**: Configure proper role-based access control for infrastructure and applications ### Backup and disaster recovery - **Elasticsearch**: Perform backups using Camunda for Elastic (see [Camunda backup guide](/self-managed/operational-guides/backup-restore/elasticsearch/backup.md)). - **PostgreSQL**: Configure automated backups using [CloudNativePG's backup capabilities](https://cloudnative-pg.io/docs/1.28/recovery) - **Keycloak**: Configure regular [exports of realm and user data](https://www.keycloak.org/server/importExport) - **Configuration**: Store all configuration files in version control ### Monitoring and observability - **Metrics**: Enable Prometheus monitoring for all infrastructure components - **Logging**: Aggregate logs from infrastructure and application components - **Alerting**: Set up alerts for critical infrastructure events ### Resource planning - **CPU and memory**: Size clusters based on expected workload - **Storage**: Plan for data growth and I/O requirements - **Network**: Consider bandwidth requirements between components ## Migration from subcharts If you're migrating from existing Bitnami sub-chart deployments: 1. **Export data**: Create backups of existing databases and Elasticsearch indices 2. **Deploy operator-based infrastructure**: Install the operator-managed infrastructure alongside existing deployment 3. **Migrate data**: Transfer data to operator-managed services 4. **Update configuration**: Switch Camunda configuration to use new services 5. **Cleanup**: Remove old sub-chart deployments once migration is complete ## Additional resources - [CloudNativePG documentation](https://cloudnative-pg.io/docs/1.28/) - [Elastic Cloud on Kubernetes guide](https://www.elastic.co/guide/en/cloud-on-k8s/current/index.html) - [Keycloak Operator documentation](https://www.keycloak.org/operator/installation) - [Camunda 8 Helm chart parameters](https://artifacthub.io/packages/helm/camunda/camunda-platform#parameters) - [Kubernetes Operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/) ## Next steps - [Configure Ingress and TLS](/self-managed/deployment/helm/configure/ingress/index.md) - [Set up monitoring and observability](/self-managed/deployment/helm/configure/application-configs.md) - [Configure multi-tenancy](/self-managed/deployment/helm/configure/configure-multi-tenancy.md) - [Production deployment guide](/self-managed/deployment/helm/install/production/index.md) --- ## Configure pod networking The Camunda Helm chart exposes values that control how component pods connect to the network. Use these settings when your infrastructure requires custom DNS resolution behavior, or when orchestration cluster pods need to share the host node's network namespace. ## DNS policy Every Camunda component supports a `dnsPolicy` value that controls how DNS resolution works for its pods. It maps directly to the Kubernetes [`dnsPolicy` pod spec field](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy). The following components support `dnsPolicy`: | Component | Value key | | ---------------------- | --------------------------------- | | Orchestration cluster | `orchestration.dnsPolicy` | | Identity | `identity.dnsPolicy` | | Connectors | `connectors.dnsPolicy` | | Optimize | `optimize.dnsPolicy` | | Console | `console.dnsPolicy` | | Web Modeler REST API | `webModeler.restapi.dnsPolicy` | | Web Modeler WebSockets | `webModeler.websockets.dnsPolicy` | Example — setting a custom DNS policy for the orchestration cluster: ```yaml orchestration: dnsPolicy: ClusterFirst ``` If you don't set `dnsPolicy` for a component, Kubernetes applies its default (`ClusterFirst`). Common values: | Value | Behavior | | ------------------------- | ------------------------------------------------------------------------------------------------------------ | | `ClusterFirst` | In-cluster DNS takes priority; unresolved names fall back to the upstream nameserver. Default for most pods. | | `ClusterFirstWithHostNet` | Same as `ClusterFirst`, but required when `hostNetwork: true` to preserve in-cluster DNS resolution. | | `Default` | Pods inherit the DNS configuration of the node they run on. | | `None` | DNS is configured entirely via `dnsConfig`. | ## Custom DNS configuration Every Camunda component also supports a `dnsConfig` value that lets you supply custom DNS nameservers, search domains, and resolver options. It maps directly to the Kubernetes [`dnsConfig` pod spec field](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-dns-config). The following components support `dnsConfig`: | Component | Value key | | ---------------------- | --------------------------------- | | Orchestration cluster | `orchestration.dnsConfig` | | Identity | `identity.dnsConfig` | | Connectors | `connectors.dnsConfig` | | Optimize | `optimize.dnsConfig` | | Console | `console.dnsConfig` | | Web Modeler REST API | `webModeler.restapi.dnsConfig` | | Web Modeler WebSockets | `webModeler.websockets.dnsConfig` | Use `dnsConfig` when you need to override or extend the default DNS resolver — for example, to add a private nameserver or a custom search domain: ```yaml connectors: dnsPolicy: None dnsConfig: nameservers: - 192.168.1.100 searches: - my-namespace.svc.cluster.local - svc.cluster.local options: - name: ndots value: "5" ``` ## Host network (orchestration cluster only) Setting `orchestration.hostNetwork` to `true` makes orchestration cluster pods use the host node's network namespace instead of the default pod network. In this mode, pods share the node's IP address and port space rather than receiving their own cluster IP. This option is available only for the orchestration cluster (a StatefulSet). It's useful in environments where: - Pods must be reachable directly via the node IP (for example, bare-metal deployments without a CNI overlay network). - A network plugin or firewall requires pods to appear as node-level processes. - You're integrating with infrastructure that doesn't support pod-level IP routing. ```yaml orchestration: hostNetwork: true ``` When `hostNetwork` is enabled and you haven't set `orchestration.dnsPolicy`, the chart automatically sets `dnsPolicy` to `ClusterFirstWithHostNet`. This ensures pods on the host network can still resolve in-cluster DNS names (for example, Kubernetes `Service` names). If you set `orchestration.dnsPolicy` explicitly, that value always takes precedence. :::note Using `hostNetwork: true` means all ports opened by the orchestration cluster pods are bound directly on the node. Make sure the required ports are not already in use on the node, and review your network policies accordingly. ::: ### Combining host network with custom DNS To fully control DNS on a host-network pod: ```yaml orchestration: hostNetwork: true dnsPolicy: None dnsConfig: nameservers: - 10.96.0.10 searches: - cluster.local ``` --- ## Install Helm chart in air-gapped environments The [Camunda Helm chart](/self-managed/deployment/helm/install/quick-install.md) supports installation in air-gapped environments. By default, Docker images are pulled from Docker Hub. Because the chart depends on third-party images and charts, additional steps are required to make all charts available in your environment. ## Prerequisites - A private Docker registry accessible from your air-gapped environment - A private or local Helm chart repository - Access to a connected environment to pull required Camunda and Bitnami images - [Helm CLI](https://helm.sh/docs/intro/install/) installed - `kubectl` access to your Kubernetes cluster ## Configuration ### List required images The Docker images required for your Helm release depend on your `values.yaml`. To list the required images, run the following command: ```shell helm repo add camunda https://helm.camunda.io helm repo update helm template camunda/camunda-platform -f values.yaml | grep 'image:' ``` ### Required Docker images The following images must be available in your air-gapped environment: **Camunda images:** - [camunda/zeebe](https://hub.docker.com/r/camunda/zeebe) - [camunda/optimize](https://hub.docker.com/r/camunda/optimize) - [camunda/connectors-bundle](https://hub.docker.com/r/camunda/connectors-bundle) - [camunda/identity](https://hub.docker.com/r/camunda/identity) **Optional components:** - [Web Modeler images](/self-managed/deployment/docker/docker.md#component-images): - [camunda/web-modeler-restapi](https://hub.docker.com/r/camunda/web-modeler-restapi) - [camunda/web-modeler-websockets](https://hub.docker.com/r/camunda/web-modeler-websockets) - [Console images](/self-managed/deployment/docker/docker.md#component-images): - `console/console-sm` **Infrastructure images:** :::info When are infrastructure images needed? For air-gapped deployments, you must mirror Bitnami infrastructure images only if you use the embedded subcharts. These include PostgreSQL (for Identity and Web Modeler), Elasticsearch (for data storage), and Keycloak (for authentication). Skip this section if you're using external managed services or separately deployed infrastructure. ::: Choose one of the following image options: #### Option A: Open-source Bitnami images (community default) - [bitnamilegacy/postgresql](https://hub.docker.com/r/bitnamilegacy/postgresql) - [camunda/keycloak](https://hub.docker.com/r/camunda/keycloak) (tag: `bitnami-*`) - [bitnamilegacy/os-shell](https://hub.docker.com/r/bitnamilegacy/os-shell/) - [bitnamilegacy/elasticsearch](https://hub.docker.com/r/bitnamilegacy/elasticsearch/) :::warning Not recommended for production These open-source images are the community default but are not recommended for production environments due to security and support limitations. Customers should transition to Option B or use managed infrastructure services. ::: #### Option B: Enterprise Bitnami Premium images (recommended) - `registry.camunda.cloud/vendor-ee/postgresql` (requires enterprise credentials) - `registry.camunda.cloud/keycloak-ee/keycloak` (tag: `bitnami-ee-*`, requires enterprise credentials) - `registry.camunda.cloud/vendor-ee/os-shell` (requires enterprise credentials) - `registry.camunda.cloud/vendor-ee/elasticsearch` (requires enterprise credentials) :::tip Enterprise benefits The `vendor-ee` registry provides proxied access to Bitnami Premium images from Broadcom, offering enhanced security patches, enterprise support, and compliance features. For detailed configuration and installation instructions, see [Install Bitnami enterprise images](/self-managed/deployment/helm/configure/registry-and-images/install-bitnami-enterprise-images.md). ::: #### Camunda Keycloak images Camunda provides custom [Keycloak images](https://github.com/camunda/keycloak) that include the AWS JDBC wrapper and Camunda Identity theme. These images follow Bitnami's environment variable conventions. | Variant | Registry | Tag prefix | Availability | | ----------- | ----------------------------------------------------------------------- | -------------- | ---------------------------- | | Open-source | [docker.io/camunda/keycloak](https://hub.docker.com/r/camunda/keycloak) | `bitnami-*` | Public (Docker Hub) | | Enterprise | `registry.camunda.cloud/keycloak-ee/keycloak` | `bitnami-ee-*` | Camunda Enterprise customers | For backward compatibility, both variants are also available without the prefix in their respective registries. :::note The open-source variant is based on the `bitnamilegacy` repository and receives no further updates from Bitnami. For production environments, use the enterprise variant or a managed Keycloak service. ::: :::tip About the original Bitnami Keycloak images If you prefer to use the original Bitnami Keycloak images directly (`bitnamilegacy/keycloak` or `registry.camunda.cloud/vendor-ee/keycloak`), you can override the image in your Helm values. This is not required, as Camunda Keycloak images are fully compatible and recommended. ::: A helper script is available in the [camunda-helm-respository](https://github.com/camunda/camunda-platform-helm/blob/c6a6e0c327f2acb8746802fbe03b3774b8284de3/scripts/download-chart-docker-images.sh) to pull and save Docker images. ### Access Camunda images from the Camunda registry All required images published on Docker Hub (Camunda and Bitnami organizations) are also available in the Camunda registry: - `registry.camunda.cloud/camunda/` - `registry.camunda.cloud/bitnami/` For example, you can pull the Zeebe and PostgreSQL images from Docker Hub or the Camunda registry: ```shell docker pull camunda/zeebe:latest docker pull registry.camunda.cloud/camunda/zeebe:latest docker pull bitnamilegacy/postgresql:latest docker pull registry.camunda.cloud/bitnami/postgresql:latest ``` ### Access enterprise images with Skopeo :::info Registry migration notice As of November 30, 2025, our image vendor has migrated its repositories. All images downloaded before this date remain available but are no longer listable by the `skopeo` command. The `skopeo` command will return only images added by Bitnami after November 30, 2025. ::: If you use Bitnami Premium images from the `vendor-ee` registry, you can use [Skopeo](https://github.com/containers/skopeo) to copy images directly to your private registry without requiring Docker locally: ```shell # Copy Bitnami Premium PostgreSQL image skopeo copy --src-creds=: \ docker://registry.camunda.cloud/vendor-ee/postgresql:16.6.0-debian-12-r0 \ docker://your-private-registry.com/bitnami/postgresql:16.6.0-debian-12-r0 # Copy Bitnami Premium Elasticsearch image skopeo copy --src-creds=: \ docker://registry.camunda.cloud/vendor-ee/elasticsearch:8.11.4-debian-12-r0 \ docker://your-private-registry.com/bitnami/elasticsearch:8.11.4-debian-12-r0 # Copy Bitnami Premium Keycloak image skopeo copy --src-creds=: \ docker://registry.camunda.cloud/vendor-ee/keycloak:26.0.7-debian-12-r0 \ docker://your-private-registry.com/bitnami/keycloak:26.0.7-debian-12-r0 ``` **Configuration notes:** - Replace `` and `` with your Camunda Enterprise LDAP credentials. - Replace `your-private-registry.com` with your actual private registry URL. - Use the image tags that match your Helm chart version requirements. - For a complete list of available enterprise images and their tags, see [Install Bitnami enterprise images](/self-managed/deployment/helm/configure/registry-and-images/install-bitnami-enterprise-images.md). ### Required Helm charts The [Camunda Helm chart](https://artifacthub.io/packages/helm/camunda/camunda-platform) must be available in your air-gapped environment. Download it from [GitHub](https://github.com/camunda/camunda-platform-helm/releases) or run: ```shell helm repo add camunda https://helm.camunda.io helm repo update helm pull camunda/camunda-platform ``` The package is self-contained and includes these dependencies: - [Elasticsearch Helm chart](https://artifacthub.io/packages/helm/bitnami/elasticsearch) - [Keycloak Helm chart](https://artifacthub.io/packages/helm/bitnami/keycloak) - [Postgres Helm chart](https://artifacthub.io/packages/helm/bitnami/postgresql) - [Bitnami Common Helm chart](https://artifacthub.io/packages/helm/bitnami/common) Install the Helm chart by either making it available in a [private repository](https://helm.sh/docs/topics/chart_repository/) that can be accessed from the air-gapped environment or providing the downloaded chart archive locally, for example: ```shell helm install camunda --version $HELM_CHART_VERSION ./camunda-platform-11.1.0.tgz ``` For supported versions, see [supported environments](/reference/supported-environments.md#camunda-8-self-managed) and the [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md). ### Dependency overview Identity uses Keycloak and lets you manage users, roles, and permissions for Camunda 8 components. This third-party dependency is reflected in the Helm chart as follows: ``` camunda-platform |_ elasticsearch |_ identity |_ identityKeycloak |_ postgresql |_ orchestration |_ optimize |_ connectors |_ webModeler |_ postgresql ``` - Keycloak depends on Camunda Identity, and PostgreSQL depends on Keycloak. - PostgreSQL is also a dependency for Web Modeler. - This dependency is optional: you can install PostgreSQL with Helm or use an existing [external database](/self-managed/deployment/helm/install/quick-install.md#optional-configure-external-database). - Elasticsearch is a dependency for the Orchestration Cluster and Optimize. - Connectors can run stand-alone, but if you use inbound capabilities, Operate becomes a dependency. You can configure Keycloak and PostgreSQL values at the same hierarchy level: ```yaml identity: [identity values] identityKeycloak: [keycloak values] postgresql: [postgresql values] ``` ### Push Docker images to a repository Push all [required Docker images](#required-docker-images) to your repository: 1. Tag the image: ```shell docker tag example.jfrog.io/camunda/: ``` 1. Push the image: ```shell docker push example.jfrog.io/camunda/: ``` ### Deploy Helm charts to a repository You must deploy the [required Helm charts](#required-helm-charts) to your repository. For hosting options, see the [chart repository guide](https://helm.sh/docs/topics/chart_repository). #### Add a Helm repository To use the chart, add your Helm chart repository: ```shell helm repo add camunda https://example.jfrog.io/artifactory/api/helm/camunda-platform helm repo update ``` #### Override Helm chart values You can override the image registry and tag in a custom `values.yaml` file: ```yaml global: image: registry: example.jfrog.io orchestration: image: repository: camunda/zeebe # e.g. work with the latest versions in development tag: latest elasticsearch: image: registry: example.jfrog.io repository: bitnamilegacy/elasticsearch sysctlImage: registry: example.jfrog.io repository: bitnamilegacy/os-shell identity: image: repository: camunda/identity ... identityKeycloak: image: registry: example.jfrog.io repository: bitnamilegacy/keycloak ... postgresql: image: registry: example.jfrog.io repository: bitnamilegacy/postgresql ... optimize: image: repository: camunda/optimize ... connectors: image: repository: camunda/connectors-bundle ... webModeler: image: # registry and tag will be used for both Web Modeler images tag: latest restapi: image: repository: camunda/web-modeler-restapi websockets: image: repository: camunda/web-modeler-websockets ... postgresql: image: registry: example.jfrog.io repository: bitnamilegacy/postgresql ... ``` #### Deploy Camunda with custom values Finally, deploy Camunda with Helm using the custom values file: ```shell helm install camunda camunda/camunda-platform --version $HELM_CHART_VERSION -f values.yaml ``` ## Best practices - **Mirror images and charts regularly**: Sync images and charts on a schedule to avoid version drift. - **Pin versions**: Use explicit tags instead of `latest` to ensure reproducibility. - **Validate charts**: Test Helm charts in a staging air-gapped environment before production. - **Monitor dependencies**: Track Bitnami and Camunda dependency updates, since they affect required images. - **Secure access**: Restrict permissions to your private registry and Helm repository. --- ## Configure registry and images This section explains how to adjust registry and image sources for production setups, including using enterprise images and working in air-gapped environments. --- ## Install Bitnami enterprise images This guide explains how to configure the Camunda Helm chart to use Bitnami Premium images designed for production environments. These images are referred to as Enterprise images, indicating that they are intended for Camunda Enterprise customers. The guide also covers configuration steps, security considerations, CVE reporting, and best practices. ## Overview By default, the Camunda Helm chart deploys Bitnami open-source images. For production environments that require enhanced security and timely patches, Camunda enables access to Bitnami Premium images through a vendor-proxied registry for Camunda Enterprise customers. :::warning Support boundaries Bitnami Premium images do not change Camunda's support policy for infrastructure components. PostgreSQL, Elasticsearch, and Keycloak deployed via Bitnami subcharts remain external dependencies from a support standpoint. - Customer support: Contact Camunda support through the standard support channels. - No direct vendor support: Customers do not receive direct support from Bitnami (Broadcom). - Recommendation: For production environments, Camunda recommends using managed services or [deploying required dependencies with Kubernetes operators](/self-managed/deployment/helm/configure/operator-based-infrastructure.md) rather than Bitnami subcharts. ::: ### Changes since Camunda 8.8 Previously, some users deployed Bitnami subcharts in production. Starting with Camunda 8.8, Bitnami subcharts are primarily intended for development and testing unless your teams have specific expertise running Bitnami charts in production. For existing users: You can continue using Bitnami subcharts in your environments. If you have production deployments using these subcharts prior to 8.8, review the implications for your setup. See [Changes to Camunda Helm Sub-Charts](https://camunda.com/blog/2025/08/changes-to-camunda-helm-sub-charts-what-you-need-to-know/) for details. ### Alternative for production deployments For production environments, consider using the official Kubernetes operators for PostgreSQL, Elasticsearch/OpenSearch, and Keycloak instead of Bitnami subcharts. For detailed instructions, see [Deploy required dependencies with Kubernetes operators](/self-managed/deployment/helm/configure/operator-based-infrastructure.md). If you use Bitnami-based subcharts in production, Camunda strongly recommends using Bitnami Premium images licensed by Camunda and maintained by Bitnami (Broadcom). This guide explains how to configure and install Camunda with these images. ## Understanding Bitnami image offerings Following [Bitnami chart security policy changes](https://github.com/bitnami/charts/issues/30850), Camunda transitioned from open-source Bitnami images to Bitnami Premium images licensed by Broadcom. These images require an additional values file for configuration, detailed below. While Bitnami also provides a repository of Secure images, Camunda continues to mirror the Premium versions in its subcharts. **You don't need to use any charts other than those specified in the `Chart.yaml` dependencies.** The Camunda Helm chart automatically manages all required chart dependencies. :::info Camunda provides Premium images only Camunda provides access to **Bitnami Premium images** exclusively for Camunda Enterprise customers. The Helm charts, however, remain based on the open-source Bitnami charts. Each Camunda Helm chart version lists its chart dependencies in the `Chart.yaml` file. For example, see the [Camunda 8.9 Chart.yaml](https://github.com/camunda/camunda-platform-helm/blob/main/charts/camunda-platform-8.9/Chart.yaml) file for a complete list of dependent charts. **Keycloak Helm chart fork:** Camunda uses a fork of the Bitnami Keycloak Helm chart. The Keycloak image has been upgraded to the latest public release, and environment variable names have been adjusted for compatibility with both enterprise and open-source deployments. This fork ensures ease of distribution and backward compatibility. ::: ### Available image types | Image Type | Registry Path | Base OS | Maintenance Level | Intended Use | | --------------- | ------------------------------------------------------------------------ | ------- | -------------------- | ----------------------- | | **Open-source** | `bitnamilegacy/*` | Debian | Community-maintained | Development and testing | | **Premium** | `bitnamipremium/*` (Camunda proxied through `vendor-ee` repository) | Debian | Vendor-maintained | Production | ### Why Camunda uses Bitnami Premium images - **Availability:** Debian-based Premium images accessible via a vendor-proxied registry (Camunda facilitates access for Camunda Enterprise customers) - **Timely patches:** Bitnami maintains these images with regular security patches and updates - **Exclusions:** PhotonOS-based Premium images are not distributed by Camunda For more information, see [Bitnami](https://bitnami.com/) and [Bitnami Documentation](https://docs.bitnami.com/). ## Benefits of Bitnami Premium images Bitnami Premium images offer key advantages over open-source variants: | Benefit Category | Key Features | | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Security** | Timely CVE patchesSecurity hardeningRegular vulnerability assessments | | **Maintenance** | Regular image updates by Bitnami (Broadcom)Patched builds for critical vulnerabilities | | **Enterprise** | Access via private registry (`registry.camunda.cloud`)Customer-exclusive availabilityRedistributed by Camunda for Camunda Enterprise customers | ## Environment-specific recommendations Select your deployment approach based on security requirements and operational needs: | Environment Type | Infrastructure Approach | CVE Management Strategy | | ---------------------------------- | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | | **Development/Testing** | Bitnami subcharts with open-source images | Prioritize functionality; security hardening less critical | | **Production (Moderate Security)** | Bitnami Premium images with timely patches | Accept OS-layer CVE reports; prioritize critical/high severity vulnerabilities with available fixes | | **Production (Strict Compliance)** | Managed services (AWS RDS, Azure Database, Google Cloud SQL) or separately deployed hardened images | Engage managed service providers for enterprise support and SLA-backed security patching | | **High-Security/Near-Zero CVEs** | Minimal base images (Alpine, Distroless) with custom infrastructure or alternative secure image distributions | Use alternative secure image distributions or custom-built containers to meet strict CVE requirements | ## Installation process ### Step 1: Create a Kubernetes registry secret To access the private registry, create a Kubernetes `docker-registry` secret with your Camunda Enterprise credentials: ```shell kubectl create secret docker-registry registry-camunda-cloud \ --docker-server=registry.camunda.cloud \ --docker-username= \ --docker-password= \ --docker-email=unused@example.com ``` **Notes:** - Replace `` and `` with your LDAP credentials - The email field is required by Kubernetes but not used - See [Specifying `imagePullSecrets` on a Pod](https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod) for details ### Step 2: Install the Helm chart with enterprise images Camunda provides a `values-enterprise.yaml` file to configure the chart to use Premium images. :::note About vendor pull secrets The `values-enterprise.yaml` references `commonVendorPullSecrets` to specify the secret for accessing the private registry. This is necessary because `global.image.pullSecrets` does not apply to vendor charts. ::: **Default secret name:** `registry-camunda-cloud`. You can override this via: - The `--set` flag - A custom `values-enterprise.yaml` - Other Helm value override methods ([Helm values files](https://helm.sh/docs/chart_template_guide/values_files/#using-helm-install--f)) **Installation command:** ```shell helm install camunda camunda/camunda-platform --version $HELM_CHART_VERSION \ --values https://raw.githubusercontent.com/camunda/camunda-platform-helm/main/charts/camunda-platform-8.9/values-enterprise.yaml ``` This deploys Camunda with Bitnami Premium images, recommended for secure, stable environments when using Bitnami subcharts. :::info Image versions: `main` branch vs chart release This command references `values-enterprise.yaml` from the **`main` branch**, which always contains the **latest tested image versions**. These versions may be newer than the ones bundled with a specific Helm chart release (for example, Elasticsearch `8.19.11` on `main` vs `8.19.9` in chart release `12.7.6`). This is **intentional and recommended**: using the latest images ensures you benefit from the most recent security patches and bug fixes. All dependency versions listed in [supported environments](/reference/supported-environments.md#component-requirements) are supported. If you need to use the exact image versions from a specific chart release, reference the release tag instead of `main`: ```shell helm install camunda camunda/camunda-platform --version $HELM_CHART_VERSION \ --values https://raw.githubusercontent.com/camunda/camunda-platform-helm/camunda-platform-8.9-$HELM_CHART_VERSION/charts/camunda-platform-8.9/values-enterprise.yaml ``` ::: ## Understanding CVEs in Bitnami images Working with Bitnami images requires understanding CVE (Common Vulnerabilities and Exposures) reporting and how to interpret scan results. ### CVE responsibility matrix Security responsibilities differ by component: | Component Type | Examples | Security Responsibility | CVE Handling | | ---------------------- | -------------------------------------------------------- | ----------------------- | ---------------------------------------------------------------------------------------------- | | **Camunda components** | Zeebe, Operate, Tasklist, Optimize, Connectors, Identity | Camunda | Regular updates and patches in Camunda releases. See [Security notices](/reference/notices.md) | | **Vendor components** | Bitnami PostgreSQL, Elasticsearch, Keycloak | Bitnami (Broadcom) | Camunda facilitates upgrades to latest vendor versions | If a vendor determines a vulnerability has no practical impact, Camunda may accept that assessment or migrate to alternatives. ### Expected behavior: high CVE counts Bitnami images often report many CVEs in vulnerability scans. This occurs because these images include multiple layers: - **Application layer:** PostgreSQL, Elasticsearch, Keycloak - **Operating system:** Debian with system libraries - **Runtime dependencies:** JVM, Python, system utilities - **Supporting libraries:** SSL, compression, networking components Scanners report CVEs across all layers, inflating counts even when images are secure and up-to-date. :::tip To reduce CVE exposure, Camunda recommends using managed services (AWS RDS, Azure Database, Google Cloud SQL) rather than Bitnami subcharts in production. ::: ### Bitnami’s CVE management approach [Bitnami’s Open CVE Policy](https://docs.bitnami.com/kubernetes/open-cve-policy/) outlines their security process: - Fixable CVEs are patched promptly when upstream fixes are available - Open or unfixable CVEs remain until resolved by OS or application maintainers - Critical vulnerabilities receive priority with expedited security updates Even enterprise Bitnami Premium images will show CVE counts due to OS-level vulnerabilities despite patches for critical issues. ### Limitations When using Bitnami images, consider these constraints: - **CVE persistence:** OS-level CVEs may remain visible in scans - **Compliance requirements:** Near-zero CVE policies may not be achievable with these images - **Alternatives:** For strict security, consider managed services or custom minimal images ## Compatibility note :::info Registry migration notice As of November 30, 2025, our image vendor has migrated its repositories. All images downloaded before this date remain available but are no longer listable by the `skopeo` command. The `skopeo` command will return only images added by Bitnami after November 30, 2025. ::: This configuration follows Bitnami's official image and chart usage guidelines. For licensing, support levels, and CVE management, refer to [Bitnami Documentation](https://docs.bitnami.com/) and [Bitnami Enterprise](https://bitnami.com/enterprise). --- ## Run custom connectors in Helm charts You can deploy a custom connector in your Helm Kubernetes cluster along with the connectors bundle. The default runtime loads connectors from the classpath using the Java Service Provider Interface (SPI). For the custom connectors, there is a dedicated folder in the Connectors Docker image `/opt/custom`. Any JAR placed in this folder is included in the runtime classpath. This page explains how to place your custom connector JAR in `/opt/custom`. ## Prerequisites - A custom connector built as a **fat JAR** (JAR with dependencies). For details on creating and building custom connectors, see [Connector SDK](/components/connectors/custom-built-connectors/connector-sdk.md). Example JAR name used in this guide: `custom-connector-0.0.1-with-dependencies.jar` - A hosting location accessible by Helm during installation. Example path used in this guide: `https://my.host:80/dist/custom-connector-0.0.1-with-dependencies.jar` ## Configure the Helm chart Update the values of the [Camunda Helm charts](https://artifacthub.io/packages/helm/camunda/camunda-platform#parameters) to download the JAR into `/opt/custom` before the connectors runtime starts: ```yaml connectors: initContainers: - name: init-script-downloader image: appropriate/curl securityContext: runAsUser: 1000 runAsNonRoot: true args: - "-o" - "/opt/custom/custom-connector-0.0.1-with-dependencies.jar" - "https://my.host:80/dist/custom-connector-0.0.1-with-dependencies.jar" volumeMounts: - name: init-script mountPath: /opt/custom extraVolumes: - name: init-script emptyDir: {} extraVolumeMounts: - mountPath: /opt/custom/custom-connector-0.0.1-with-dependencies.jar name: init-script subPath: custom-connector-0.0.1-with-dependencies.jar ``` After updating the values, run [Helm install](/self-managed/deployment/helm/install/quick-install.md#install-camunda-helm-chart) as usual. :::note The `appropriate/curl` image is not the only image option for the `initContainers`. You can use other `curl`-based images, such as `curlimages/curl`. Adjust the `args` to match the image you choose. On clusters that enforce non-root containers (for example, restricted Pod Security admission), keep the `securityContext` in the init container to avoid startup failures. ::: ## Troubleshooting If your custom connector does not start: - Verify that your connector JAR is present in the `/opt/custom` folder in the pod. - Confirm that the original connector JAR matches the one in `/opt/custom`. A file size check is often sufficient, but you can also compare checksums if needed. --- ## Helm charts secret management This guide provides an overview for configuring and managing secrets when using the official Helm chart. ## Secret configuration patterns The Helm chart supports different patterns for secret management. ### Structured secret pattern The structured `secret:` configuration under components provides three options: - `inlineSecret`: Plain-text value for non-production usage - `existingSecret`: Reference to an existing Kubernetes Secret name - `existingSecretKey`: Key within the existing secret object Example: ```yaml component: auth: secret: inlineSecret: "my-plain-text-secret" # Non-production only existingSecret: "my-secret-name" # Recommended existingSecretKey: "secret-key" ``` ### Bitnami subchart pattern Some components use Bitnami subcharts for database services (PostgreSQL), which follow their own authentication patterns that differ from the main Camunda secret structure. These use the standard Bitnami PostgreSQL Helm chart pattern with `existingSecret` and `secretKeys` containing `adminPasswordKey` and `userPasswordKey`. The following Bitnami subchart configurations are available: - **`identityPostgresql.auth`** - PostgreSQL database for Identity service - **`identityKeycloak.auth`** - Keycloak admin credentials - **`identityKeycloak.postgresql.auth`** - PostgreSQL database for Keycloak (when using Identity with Keycloak) - **`webModelerPostgresql.auth`** - PostgreSQL database for Web Modeler ## Application secrets These secrets are used by Camunda applications and external integrations. Configure them manually when using external secrets. ### Secrets using the structured pattern | **Secret** | **Chart values key** | **Type** | **Purpose** | | ----------------------------------------------- | --------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------- | | **Enterprise License Key** | `global.license.secret` | Internal | Camunda Enterprise license key | | **Identity First User Password** | `identity.firstUser.secret` | Internal | Default user password (`demo/demo`) | | **OAuth Client Secret (Admin)** | `global.identity.auth.admin.secret` | Internal | OAuth admin client secret for administrative operations | | **OAuth Client Secret (Connectors)** | `connectors.security.authentication.oidc.secret` | Internal | OAuth client secret for connectors | | **OAuth Client Secret (Orchestration)** | `orchestration.security.authentication.oidc.secret` | Internal | OAuth client secret for Orchestration Cluster | | **OAuth Client Secret (Optimize)** | `global.identity.auth.optimize.secret` | Internal | OAuth client secret for Optimize | | **Identity External Database Password** | `identity.externalDatabase.secret` | External | Password for external PostgreSQL when using an external database for Identity | | **Web Modeler External Database Password** | `webModeler.restapi.externalDatabase.secret` | External | Password for external PostgreSQL when using an external database for Web Modeler | | **SMTP Password** | `webModeler.restapi.mail.secret` | External | SMTP credentials for sending email notifications | | **RDBMS Auth** | `orchestration.data.secondaryStorage.rdbms.secret` | External | Password for external RDBMS authentication (Basic authentication) | | **External Elasticsearch Auth (Orchestration)** | `orchestration.data.secondaryStorage.elasticsearch.auth.secret` | External | Password for external Elasticsearch authentication (Basic authentication) | | **External OpenSearch Auth (Orchestration)** | `orchestration.data.secondaryStorage.opensearch.auth.secret` | External | Password for external OpenSearch authentication (Basic authentication) | | **External Elasticsearch Auth (Optimize)** | `optimize.database.elasticsearch.auth.secret` | External | Password for external Elasticsearch authentication (Basic authentication) | | **External OpenSearch Auth (Optimize)** | `optimize.database.opensearch.auth.secret` | External | Password for external OpenSearch authentication (Basic authentication) | ### Secrets using Bitnami subchart patterns | **Secret** | **Chart values key** | **Purpose** | | ----------------------------------- | ------------------------------------------------- | ------------------------------------------------- | | **Identity PostgreSQL Password** | `identityPostgresql.auth.existingSecret` | Password for embedded PostgreSQL used by Identity | | **Keycloak Admin Password** | `identityKeycloak.auth.existingSecret` | Admin password for Keycloak (Camunda Identity) | | **Keycloak PostgreSQL Password** | `identityKeycloak.postgresql.auth.existingSecret` | Password for embedded PostgreSQL used by Keycloak | | **Web Modeler PostgreSQL Password** | `webModelerPostgresql.auth.existingSecret` | Passwords for Web Modeler's embedded PostgreSQL | **PostgreSQL Secret Keys**: For PostgreSQL subcharts, both `adminPasswordKey` and `userPasswordKey` are required: - `adminPasswordKey`: Password for the PostgreSQL administrator (typically used for administrative operations) - `userPasswordKey`: Password for the application-specific database user (used by the Camunda component) ## How to configure secrets Secrets can be configured in different ways: - Use the structured `secret:` pattern with `inlineSecret` for non-production or external Kubernetes Secrets for production - For Bitnami subchart components (PostgreSQL, Keycloak), use their native `existingSecret` pattern ### Method 1: Inline secrets (non-production only) For development or testing environments, provide secrets directly in your `values.yaml` using the `inlineSecret` field: ```yaml global: license: secret: inlineSecret: "my-license-key-here" identity: firstUser: secret: inlineSecret: "demo-password" ``` ### Method 2: External Kubernetes secrets (recommended) For production environments, create a Kubernetes Secret and reference it from your `values.yaml`. #### Step 1: Create the secret Create a secret using `kubectl` or a YAML manifest: ```sh kubectl create secret generic optimize-secret \ --from-literal=client-secret=camundapassword \ --namespace camunda ``` Or using YAML: ```yaml apiVersion: v1 kind: Secret metadata: name: optimize-secret namespace: camunda type: Opaque stringData: client-secret: "camundapassword" ``` #### Step 2: Reference in `values.yaml` **For components using the structured pattern:** ```yaml global: identity: auth: optimize: secret: existingSecret: "optimize-secret" existingSecretKey: "client-secret" ``` **For Bitnami subchart components:** ```yaml # PostgreSQL database for Identity service identityPostgresql: auth: existingSecret: camunda-credentials secretKeys: adminPasswordKey: identity-postgresql-admin-password userPasswordKey: identity-postgresql-user-password # Keycloak admin credentials identityKeycloak: auth: existingSecret: camunda-credentials passwordSecretKey: identity-keycloak-admin-password # PostgreSQL database for Keycloak (when using Identity with Keycloak) identityKeycloak: postgresql: auth: existingSecret: camunda-credentials secretKeys: adminPasswordKey: identity-keycloak-postgresql-admin-password userPasswordKey: identity-keycloak-postgresql-user-password # PostgreSQL database for Web Modeler webModelerPostgresql: auth: existingSecret: camunda-credentials secretKeys: adminPasswordKey: web-modeler-postgresql-admin-password userPasswordKey: web-modeler-postgresql-user-password ``` :::note Auto-generated secrets (`global.secrets.autoGenerated`) are no longer supported starting in Camunda 8.9. Create your secrets explicitly using [inline secrets](#method-1-inline-secrets-non-production-only) for non-production environments or [external Kubernetes secrets](#method-2-external-kubernetes-secrets-recommended) for production environments. ::: ## Document Store secrets Document Store secrets use the structured `secret:` pattern with separate secret configurations for each credential component. | **Secret** | **Chart values key** | **Purpose** | **Required when** | | ------------------------------------------ | --------------------------------------------------------- | ------------------------------------------------------------------------ | ---------------------------------------------------- | | **AWS Document Store Access Key ID** | `global.documentStore.type.aws.accessKeyId.secret` | AWS access key ID for S3 document storage authentication | Using AWS S3 with IAM authentication | | **AWS Document Store Secret Access Key** | `global.documentStore.type.aws.secretAccessKey.secret` | AWS secret access key for S3 document storage authentication | Using AWS S3 with IAM authentication | | **GCP Document Store Service Account** | `global.documentStore.type.gcp.secret` | GCP service account JSON for GCS document storage authentication | Using GCP Cloud Storage | | **Azure Document Store Connection String** | `global.documentStore.type.azure.connectionString.secret` | Azure Storage connection string for Blob document storage authentication | Using Azure Blob Storage with connection string auth | :::note For Azure Blob Storage with DefaultAzureCredential (managed identities and Workload Identity), the connection string secret is not required. ::: ## TLS certificates TLS certificate secrets for Camunda components and external services. :::note Migrating from legacy TLS secret configuration The structured `secret:` pattern for TLS certificates was introduced in Camunda 8.9. If you are upgrading from an earlier version and using legacy TLS secret fields (such as `global.elasticsearch.tls.existingSecret`), see the [8.8 secret management guide](/versioned_docs/version-8.8/self-managed/deployment/helm/configure/secret-management.md) for migration instructions. ::: ### TLS certificate secrets | **Secret** | **Chart values key** | **Purpose** | | ----------------------------------- | --------------------------------- | --------------------------------------------------- | | **Console TLS Certificate** | `console.tls.secret` | TLS certificate for Console web application | | **External Elasticsearch TLS Cert** | `global.elasticsearch.tls.secret` | TLS certificate for external Elasticsearch over SSL | | **External OpenSearch TLS Cert** | `global.opensearch.tls.secret` | TLS certificate for external OpenSearch over SSL | **TLS Certificate Configuration**: Unlike password-based secrets, TLS certificates do not support `inlineSecret` (certificates are binary files unsuitable for inline configuration). For Elasticsearch and OpenSearch, both `existingSecret` and `existingSecretKey` are required to specify which key in the secret contains the certificate file. For Console, only `existingSecret` is required as the entire secret is mounted as a directory. Create the secrets with your certificate files using `kubectl create secret generic`: ```sh kubectl create secret generic \ --from-file== \ --namespace camunda ``` Reference them in your values: ```yaml # Elasticsearch/OpenSearch global: elasticsearch: tls: enabled: true secret: existingSecret: elasticsearch-tls-secret existingSecretKey: externaldb.jks # Console console: tls: enabled: true secret: existingSecret: console-tls-secret certKeyFilename: ca.crt ``` ### Ingress TLS Configure TLS for Camunda services exposed via Ingress: ```yaml global: ingress: tls: enabled: true secretName: camunda-platform ``` ## Extract plaintext values and reference them as Kubernetes Secrets Use this guide if any sensitive values (client secrets, DB passwords, etc.) are written directly as plaintext in your values.yaml. The production best practice is to store these in Kubernetes Secrets and reference them from your values. _Note: If your chart already references Kubernetes Secrets (and not plaintext), no change is needed—just ensure each existingSecret/existingSecretKey points to the correct secret and key._ **What you need to do** 1. Find any plaintext secrets in your `values.yaml`. 1. Create a Kubernetes secret that stores those values. 1. Reference that secret in your Helm values file (replace inline plaintext literals). :::tip You can use a single consolidated secret (e.g., app-credentials) or one secret per component. Consolidated keeps things tidy; per-component can be clearer for ownership/rotation. Choose what fits your operations model. ::: ### Find any plaintext secrets in your `values.yaml` #### A - If the secrets already exist in Kubernetes You can read the current (base64-encoded) data from existing secrets and reuse it in your new consolidated secret. ```bash # Adjust to your release name / namespace RELEASE_NAME=camunda RELEASE_NAMESPACE=camunda # Examples of extracting values from existing secrets (jsonpath reads base64 data) export SOME_CLIENT_SECRET=$( kubectl -n "$RELEASE_NAMESPACE" get secret "${RELEASE_NAME}-some-app-secret" \ -o jsonpath="{.data.client-secret}" | base64 --decode ) export DB_ADMIN_PASSWORD=$( kubectl -n "$RELEASE_NAMESPACE" get secret "${RELEASE_NAME}-postgresql" \ -o jsonpath="{.data.postgres-password}" | base64 --decode ) ``` Repeat for each value you want to consolidate. #### B - If they only exist in values.yaml `values.yaml` Copy those literal strings into environment variables (locally), then proceed to create a Kubernetes secret from them: ```bash # Example: pulling from your own notes or from a secure password manager export SOME_CLIENT_SECRET="paste-the-current-client-secret" export DB_ADMIN_PASSWORD="paste-the-current-db-admin-password" ``` ### Create a secret Create one secret with your values (example: `app-credentials`): ```bash kubectl -n "$RELEASE_NAMESPACE" create secret generic app-credentials --from-literal=some-app-client-secret="$SOME_CLIENT_SECRET" --from-literal=db-admin-password="$DB_ADMIN_PASSWORD" ``` ### Reference it in your values file Replace plaintext values with secret references. ```yaml someApp: auth: secret: existingSecret: "app-credentials" existingSecretKey: "some-app-client-secret" database: auth: existingSecret: "app-credentials" secretKeys: adminPasswordKey: "db-admin-password" ``` _Note: Remove any old plaintext values so the chart doesn’t override the secret._ ### Hands-on example (Camunda setup) Below is a ready-to-use example for Camunda deployments. Keep only those secrets that match your setup. #### 1. Extract (only for enabled components) ```shell # Change this according to your Helm chart release/deployment name and namespace. RELEASE_NAME=camunda-dev RELEASE_NAMESPACE=camunda-dev # "global.identity.auth.enabled: true" is assumed for all "IDENTITY_*_CLIENT_SECRET" values. # Only if "connectors.enabled: true". export IDENTITY_CONNECTORS_CLIENT_SECRET=$(kubectl get secret "${RELEASE_NAME}-connectors-identity-secret" -o jsonpath="{.data.connectors-secret}" | base64 --decode) # Only if "optimize.enabled: true". export IDENTITY_OPTIMIZE_CLIENT_SECRET=$(kubectl get secret "${RELEASE_NAME}-optimize-identity-secret" -o jsonpath="{.data.optimize-secret}" | base64 --decode) # Only if "zeebe.enabled: true". export IDENTITY_ZEEBE_CLIENT_SECRET=$(kubectl get secret "${RELEASE_NAME}-zeebe-identity-secret" -o jsonpath="{.data.zeebe-secret}" | base64 --decode) # Only if "postgresql.enabled: true". export WEB_MODELER_POSTGRESQL_ADMIN_SECRET=$(kubectl get secret "${RELEASE_NAME}-postgresql-web-modeler" -o jsonpath="{.data.postgres-password}" | base64 --decode) export WEB_MODELER_POSTGRESQL_USER_SECRET=$(kubectl get secret "${RELEASE_NAME}-postgresql-web-modeler" -o jsonpath="{.data.password}" | base64 --decode) # Only if "identityPostgresql.enabled: true". export IDENTITY_POSTGRESQL_ADMIN_SECRET=$(kubectl get secret "${RELEASE_NAME}-identity-postgresql" -o jsonpath="{.data.postgres-password}" | base64 --decode) export IDENTITY_POSTGRESQL_USER_SECRET=$(kubectl get secret "${RELEASE_NAME}-identity-postgresql" -o jsonpath="{.data.password}" | base64 --decode) # Only if "identityKeycloak.enabled: true". export KEYCLOAK_ADMIN_SECRET=$(kubectl get secret "${RELEASE_NAME}-keycloak" -o jsonpath="{.data.admin-password}" | base64 --decode) # Only if "identityKeycloak.postgresql.enabled: true". export KEYCLOAK_POSTGRESQL_ADMIN_SECRET=$(kubectl get secret "${RELEASE_NAME}-postgresql" -o jsonpath="{.data.postgres-password}" | base64 --decode) export KEYCLOAK_POSTGRESQL_USER_SECRET=$(kubectl get secret "${RELEASE_NAME}-postgresql" -o jsonpath="{.data.password}" | base64 --decode) ``` #### 2. Create the consolidated secret ```shell cat << EOF >> existing-secrets-manifest.yaml --- apiVersion: v1 kind: Secret metadata: name: camunda-credentials namespace: "${RELEASE_NAMESPACE}" type: Opaque stringData: # Only if "connectors.enabled: true". identity-connectors-client-token: "${IDENTITY_CONNECTORS_CLIENT_SECRET}" # Only if "optimize.enabled: true". identity-optimize-client-token: "${IDENTITY_OPTIMIZE_CLIENT_SECRET}" # Only if "orchestration.enabled: true". identity-orchestration-client-token: "${IDENTITY_ZEEBE_CLIENT_SECRET}" # Only if "identityPostgresql.enabled: true". identity-postgresql-admin-password: "${IDENTITY_POSTGRESQL_ADMIN_SECRET}" identity-postgresql-user-password: "${IDENTITY_POSTGRESQL_USER_SECRET}" # Only if "keycloak.enabled: true". identity-keycloak-admin-password: "${KEYCLOAK_ADMIN_SECRET}" # Only if "keycloak.postgresql.enabled: true". identity-keycloak-postgresql-admin-password: "${KEYCLOAK_POSTGRESQL_ADMIN_SECRET}" identity-keycloak-postgresql-user-password: "${KEYCLOAK_POSTGRESQL_USER_SECRET}" # Only if "postgresql.enabled: true". webmodeler-postgresql-admin-password: "${WEB_MODELER_POSTGRESQL_ADMIN_SECRET}" webmodeler-postgresql-user-password: "${WEB_MODELER_POSTGRESQL_USER_SECRET}" # Only if connecting to Elasticsearch orchestration-elasticsearch-password: "${ORCHESTRATION_ELASTICSEARCH_SECRET}" optimize-elasticsearch-password: "${OPTIMIZE_ELASTICSEARCH_SECRET}" # Only if connecting to OpenSearch orchestration-opensearch-password: "${ORCHESTRATION_OPENSEARCH_SECRET}" optimize-opensearch-password: "${OPTIMIZE_OPENSEARCH_SECRET}" # Only if connecting to RDBMS orchestration-rdbms-password: "${ORCHESTRATION_RDBMS_SECRET}" EOF ``` Review `existing-secrets-manifest.yaml` and ensure it contains exactly the secrets for the components enabled in your deployment. ```shell kubectl apply -n "${RELEASE_NAMESPACE}" -f existing-secrets-manifest.yaml ``` #### 3. Reference the secret in your values file If a component already uses its own existing secret, make sure to remove that section from the configuration to avoid overriding it: ```yaml # existing-secrets-values.yaml global: identity: auth: optimize: secret: existingSecret: "camunda-credentials" existingSecretKey: "identity-optimize-client-token" identityPostgresql: auth: existingSecret: "camunda-credentials" secretKeys: adminPasswordKey: "identity-postgresql-admin-password" userPasswordKey: "identity-postgresql-user-password" identityKeycloak: auth: existingSecret: "camunda-credentials" passwordSecretKey: "identity-keycloak-admin-password" postgresql: auth: existingSecret: "camunda-credentials" secretKeys: adminPasswordKey: "identity-keycloak-postgresql-admin-password" userPasswordKey: "identity-keycloak-postgresql-user-password" webModelerPostgresql: auth: existingSecret: "camunda-credentials" secretKeys: adminPasswordKey: "webmodeler-postgresql-admin-password" userPasswordKey: "webmodeler-postgresql-user-password" connectors: security: authentication: oidc: secret: existingSecret: "camunda-credentials" existingSecretKey: "identity-connectors-client-token" orchestration: data: secondaryStorage: rdbms: secret: existingSecret: "camunda-credentials" existingSecretKey: "orchestration-rdbms-password" elasticsearch: auth: secret: existingSecret: "camunda-credentials" existingSecretKey: "orchestration-elasticsearch-password" opensearch: auth: secret: existingSecret: "camunda-credentials" existingSecretKey: "orchestration-opensearch-password" security: authentication: oidc: secret: existingSecret: "camunda-credentials" existingSecretKey: "identity-orchestration-client-token" optimize: database: elasticsearch: auth: secret: existingSecret: "camunda-credentials" existingSecretKey: "optimize-elasticsearch-password" opensearch: auth: secret: existingSecret: "camunda-credentials" existingSecretKey: "optimize-opensearch-password" ``` Then upgrade your deployment via: ```bash helm upgrade --install "$RELEASE_NAME" camunda/camunda-platform \ -n "$RELEASE_NAMESPACE" \ -f existing-secrets-values.yaml ``` --- ## Camunda Helm chart Camunda recommends using Kubernetes and Helm to deploy and run Camunda 8 Self-Managed in production environments. There are many ways to provision and configure a Kubernetes cluster, and several architectural decisions to consider. For example, will your workers run inside the Kubernetes cluster or externally? You'll need to configure the cluster accordingly and tailor the setup to your architecture. Camunda provides continuously improved Helm charts that are not tied to any specific cloud provider allowing you to choose your preferred Kubernetes platform. These charts are available in the [Camunda Helm repository](https://artifacthub.io/packages/helm/camunda/camunda-platform). To provide feedback or report issues, use the [Helm GitHub repository](https://github.com/camunda/camunda-platform-helm/issues). ## What is Helm? [Helm](https://helm.sh/) is a package manager for Kubernetes resources. It lets you install a set of components by referencing a chart name and overriding configurations to suit various deployment scenarios. Helm also manages dependencies between charts, so that multiple components can be installed and configured with a single command. For details, see the full list of [Helm values](https://artifacthub.io/packages/helm/camunda/camunda-platform#parameters). ## Reference architecture For guidance on sizing and deployment patterns, see the [Kubernetes reference architecture](/self-managed/reference-architecture/kubernetes.md). When you install the [camunda-platform](https://artifacthub.io/packages/helm/camunda/camunda-platform) Helm chart, the default installation includes the Orchestration Cluster components (Zeebe, Operate, Tasklist, and Admin). Other components from the reference architecture, such as Web Modeler and Console, require additional configuration and an external Identity Provider (IdP). ## Versioning Starting with Camunda 8.4 (January 2024), the Helm chart version is independent of the application version. For example, the chart version may be `9.0.0` while the application version is `8.4.x`. To see which application versions are included in a specific Helm chart, see the [Camunda 8 Helm Chart Version Matrix](https://helm.camunda.io/camunda-platform/version-matrix/). ## Get started To install Camunda with the default orchestration cluster, see [Install Camunda with Helm](/self-managed/deployment/helm/install/quick-install.md). If managed databases or an external OIDC provider are not available in your organization, see [Deploy required dependencies](/self-managed/deployment/helm/configure/operator-based-infrastructure.md) to set up PostgreSQL, Elasticsearch, and Keycloak on Kubernetes using official operators. --- ## RDBMS example deployment for Camunda with Helm This guide is a focused walkthrough for teams using an external relational database (RDBMS) as secondary storage in the Helm production installation flow, instead of a document-store secondary backend (Elasticsearch or OpenSearch). Use [production install](/self-managed/deployment/helm/install/production/index.md) as the primary installation guide. Use this page when you want additional RDBMS-specific examples for that flow. If you deploy on AWS EKS, use [Install Camunda 8 on an EKS cluster](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/eks-helm.md) for the cluster, Ingress, and AWS-managed service setup, then return to this page for the RDBMS-specific Helm configuration and installation steps. Related guides: - [Production install](/self-managed/deployment/helm/install/production/index.md) - [Secondary storage architecture](/self-managed/reference-architecture/reference-architecture.md#secondary-storage-architecture) - [Secondary storage overview](/self-managed/concepts/secondary-storage/index.md) - [Configure RDBMS in Helm charts](/self-managed/deployment/helm/configure/database/rdbms.md) - [JDBC driver management](/self-managed/deployment/helm/configure/database/rdbms-jdbc-drivers.md) ## What changes when using RDBMS? In Camunda 8, secondary storage stores historical data and process state. You can use either a document-store backend (Elasticsearch/OpenSearch) or an RDBMS, depending on your requirements. For the canonical production trade-off guidance, see [secondary storage architecture](/self-managed/reference-architecture/reference-architecture.md#secondary-storage-architecture). This guide focuses on the Helm-specific RDBMS path. In practice, that means you provide and operate an external supported relational database, and the Orchestration Cluster reads operational data through that backend. When using RDBMS, **Optimize still requires Elasticsearch or OpenSearch**. Only the Orchestration Cluster uses RDBMS. In this topology: - The RDBMS exporter writes Orchestration Cluster data to your external relational database. - Operate, Tasklist, and Admin use the Orchestration Cluster API, and that API queries the configured RDBMS secondary storage. - If you also deploy Optimize, keep Elasticsearch or OpenSearch available and enable an additional Elasticsearch/OpenSearch exporter for Optimize. ## Prerequisites Before you begin: 1. **Kubernetes cluster**: 1.24+ with sufficient resources for Camunda pods. 2. **Helm CLI v4**: Install or upgrade [Helm](https://helm.sh/docs/intro/install/). Helm v3 is not supported since Camunda 8.10. 3. **External RDBMS**: A supported database reachable from your cluster. See the [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) for the complete list of supported databases and versions. 4. **Database credentials**: Username and password for a database user with DDL permissions (if using auto-schema creation). 5. **Document-store backend (Elasticsearch/OpenSearch)** (for Optimize): Required if you deploy Optimize alongside Camunda. ## Installation workflow ### Step 1: Choose your RDBMS **PostgreSQL:** - Bundled driver included; no additional setup required. - Excellent Kubernetes operator support (optional). - Managed services available on AWS (Aurora PostgreSQL), Azure, GCP, etc. **Oracle:** - Custom JDBC driver required; use init container to load. - Advanced security and HA features. - Managed services available on AWS (RDS), Azure, OCI. **MariaDB/MySQL:** - Bundled driver available for MariaDB; custom driver for MySQL. - Community-friendly; good for development. - Managed services widely available on AWS (RDS), Azure, GCP. ### Step 2: Prepare your database :::note On Amazon Aurora PostgreSQL Skip the local `createdb` and `createuser` commands. Connect to your Aurora writer endpoint with `psql`, and prefer IAM database authentication over a static password. See [Install Camunda 8 on an EKS cluster](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/eks-helm.md) and the [Aurora Terraform module](https://github.com/camunda/camunda-deployment-references/tree/stable/8.9/aws/modules/aurora). ::: Create a database and user. For example, in PostgreSQL: ```bash createdb camunda createuser camunda ``` Then set permissions: ```sql ALTER USER camunda WITH ENCRYPTED PASSWORD 'your-secure-password'; GRANT CONNECT ON DATABASE camunda TO camunda; GRANT USAGE ON SCHEMA public TO camunda; GRANT CREATE ON DATABASE camunda TO camunda; ``` :::note The user needs DDL permissions only if you enable auto-schema creation (`autoDDL: true`). For manually managed schemas, only SELECT/INSERT/UPDATE/DELETE permissions are needed. ::: ### Step 3: Add the Camunda Helm repository ```bash helm repo add camunda https://camunda.github.io/camunda-platform-helm helm repo update ``` ### Step 4: Create a values file Create a `values-rdbms.yaml` file with your RDBMS configuration: ```yaml # Configure the Orchestration Cluster to use RDBMS orchestration: enabled: true exporters: camunda: enabled: false rdbms: enabled: true data: secondaryStorage: type: rdbms rdbms: url: jdbc:postgresql://postgres.example.com:5432/camunda username: camunda secret: existingSecret: camunda-db-secret existingSecretKey: db-password extraConfiguration: - file: "flush-interval.yaml" content: | camunda: data: secondary-storage: rdbms: # Optional: Tune for your workload flush-interval: PT1S # More frequent flushes queue-size: 5000 # Larger queue for buffering queue-memory-limit: 50 # Increase if needed # Optional: Configure history retention history: default-history-ttl: P30D # Disable default Elasticsearch subchart elasticsearch: enabled: false # If deploying Optimize, you still need Elasticsearch/OpenSearch # Uncomment below and configure as needed: # opensearch: # enabled: true ``` ### Step 5: Create the Kubernetes secret for database credentials ```bash kubectl create namespace camunda kubectl create secret generic camunda-db-secret \ --from-literal=db-password='your-secure-password' \ -n camunda ``` ### Step 6: Handle custom JDBC drivers (if required) If you're using Oracle, MySQL, or a database version not covered by bundled drivers, you must provide the JDBC driver. :::note For detailed information about JDBC driver strategies, security configurations, and validation, see [JDBC driver management](/self-managed/deployment/helm/configure/database/rdbms-jdbc-drivers.md). ::: #### Option A: Init container (recommended for production) Update your `values-rdbms.yaml`: ```yaml orchestration: extraVolumeMounts: - name: jdbcdrivers mountPath: /driver-lib extraVolumes: - name: jdbcdrivers emptyDir: {} initContainers: - name: fetch-jdbc-drivers image: alpine:3.19 imagePullPolicy: Always command: - sh - -c - > wget https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc11/23.9.0.25.07/ojdbc11-23.9.0.25.07.jar -O /driver-lib/ojdbc.jar volumeMounts: - name: jdbcdrivers mountPath: /driver-lib securityContext: runAsUser: 1001 ``` For other driver sources (e.g., private repositories), adjust the `wget` command or use a private container registry for pre-built images. #### Option B: ConfigMap (GitOps-friendly) Store the driver JAR in a ConfigMap and mount it: ```yaml apiVersion: v1 kind: ConfigMap metadata: name: jdbc-drivers namespace: camunda data: ojdbc.jar: --- orchestration: extraVolumeMounts: - name: jdbcdrivers mountPath: /driver-lib extraVolumes: - name: jdbcdrivers configMap: name: jdbc-drivers ``` See [JDBC driver loading](/self-managed/deployment/helm/configure/database/rdbms.md#bundled-vs-custom-jdbc-drivers) for more strategies. ### Step 7: Install Camunda ```bash helm install camunda camunda/camunda-platform \ --namespace camunda \ -f values-rdbms.yaml ``` Monitor the installation: ```bash kubectl get pods -n camunda kubectl logs -n camunda -l app.kubernetes.io/name=orchestration --tail=50 ``` ### Step 8: Verify the installation Check that tables were created and data is being written: ```bash # Port-forward to the database (if not directly accessible) kubectl port-forward -n camunda svc/postgres 5432:5432 # Connect and verify psql -h localhost -U camunda -d camunda -c "SELECT * FROM zeebe_process;" ``` Deploy a test process using Web Modeler and verify it appears in the database: ```sql SELECT COUNT(*) FROM process_instances; ``` For a full post-deployment checklist, see [validate RDBMS connectivity](/self-managed/deployment/helm/configure/database/validate-rdbms.md). ## Common deployment scenarios ### PostgreSQL with AWS Aurora For AWS Aurora PostgreSQL, configure the JDBC URL with the Aurora endpoint: ```yaml orchestration: data: secondaryStorage: rdbms: url: jdbc:postgresql://my-aurora-cluster.xxxxxxx.us-east-1.rds.amazonaws.com:5432/camunda ``` Aurora supports automatic failover. For advanced failover features, consider the [AWS JDBC wrapper driver](/self-managed/concepts/databases/relational-db/configuration.md#usage-with-aws-aurora-postgresql). ### Oracle with Kubernetes init container To load the Oracle JDBC driver: ```yaml orchestration: extraVolumeMounts: - name: jdbcdrivers mountPath: /driver-lib extraVolumes: - name: jdbcdrivers emptyDir: {} initContainers: - name: fetch-jdbc-drivers image: alpine:3.19 command: - sh - -c - > wget https://your-private-repo.com/ojdbc11.jar -O /driver-lib/ojdbc.jar volumeMounts: - name: jdbcdrivers mountPath: /driver-lib ``` Configure the JDBC URL: ```yaml orchestration: data: secondaryStorage: rdbms: url: jdbc:oracle:thin:@//my-oracle-host:1521/FREEPDB1 ``` ### Multi-namespace deployment (Orchestration + Management) In production, separate the Orchestration Cluster from management components (WebModeler, Console, Identity, Optimize): #### Namespace 1: Orchestration + Connectors ```yaml orchestration: enabled: true # RDBMS configuration data: secondaryStorage: type: rdbms rdbms: url: jdbc:postgresql://postgres:5432/camunda connectors: enabled: true # Disable management components console: enabled: false optimize: enabled: false webModeler: enabled: false identity: enabled: false ``` #### Namespace 2: Management components (with document-store secondary storage) ```yaml orchestration: enabled: false console: enabled: true optimize: enabled: true webModeler: enabled: true identity: enabled: true # Optimize requires Elasticsearch/OpenSearch opensearch: enabled: true # or # elasticsearch: # enabled: true ``` ## Configuration reference For detailed configuration options, see: - [Configure RDBMS in Helm charts](/self-managed/deployment/helm/configure/database/rdbms.md): All Helm values, bundled vs. custom drivers, schema management, and troubleshooting. - [Production installation best practices](/self-managed/deployment/helm/install/production/index.md): Network policies, TLS, OIDC, and multi-namespace setup. - [Helm chart parameters](/self-managed/deployment/helm/chart-parameters.md): Full Helm chart reference. ## Important: Component storage requirements **Optimize requires Elasticsearch or OpenSearch, not RDBMS.** If you deploy Optimize, configure it with Elasticsearch or OpenSearch and enable the corresponding exporter for Zeebe, even if your Orchestration Cluster uses RDBMS: ```yaml orchestration: data: secondaryStorage: type: rdbms # Orchestration uses RDBMS optimize: enabled: true # Choose one secondary storage for Optimize: # opensearch: # enabled: true # elasticsearch: # enabled: true ``` Mixing storage types (RDBMS for Orchestration, Elasticsearch/OpenSearch for Optimize) is supported and tested. ## Troubleshooting ### Pod fails to start **Check logs:** ```bash kubectl logs -n camunda ``` **Common issues:** - Database unreachable: Verify network policies, firewall, and JDBC URL. - Authentication failed: Confirm secret and credentials. - Driver not found (Oracle/MySQL): Verify init container or custom image has loaded the driver. See [troubleshooting RDBMS connectivity](/self-managed/deployment/helm/configure/database/rdbms.md#troubleshooting-and-operations) for detailed diagnostics. ### Data not appearing in database **Cause:** Flush interval delay or JDBC configuration issue. **Check:** Monitor logs for exporter messages: ```bash kubectl logs -n camunda | grep -i exporter ``` **Fix:** Adjust `flushInterval` and `queueSize` in your values file (see [Configuration reference](#configuration-reference)). ### JDBC driver version mismatch **Symptom:** "ClassNotFoundException" or driver-related errors. **Fix:** Ensure the driver version matches your database. See [Bundled vs. custom drivers](/self-managed/deployment/helm/configure/database/rdbms.md#bundled-vs-custom-jdbc-drivers). ## Known limitations and unsupported scenarios ### Multi-region deployments Cross-region RDBMS deployments are **not tested or supported in Camunda 8.9**. Latency and consistency requirements are not yet validated. Deploy your RDBMS in the same region as your Kubernetes cluster. ### Optimize storage Optimize **cannot use RDBMS** and requires Elasticsearch or OpenSearch. If deploying Optimize alongside an RDBMS-based Orchestration Cluster, you must provision Elasticsearch/OpenSearch for Optimize only. ### Self-Managed database HA Camunda does not manage database HA. Use cloud-managed databases (AWS Aurora, Azure Database, GCP Cloud SQL) or vendor-supplied HA solutions. Camunda assumes the database handles its own replication and failover. ## Next steps - **[Production guide](/self-managed/deployment/helm/install/production/index.md)**: Network security, TLS, OIDC, and high-availability configurations. - **[Helm quick install](/self-managed/deployment/helm/install/quick-install.md)**: Get started with default settings for evaluation. - **[Operational tasks](/self-managed/deployment/helm/operational-tasks/index.md)**: Scaling, upgrades, and maintenance. - **[Backup and restore](/self-managed/operational-guides/backup-restore/backup-and-restore.md)**: Data protection strategies. --- ## Install Camunda with Helm Install Camunda 8 Self-Managed on Kubernetes using Helm charts. Choose the path that matches your environment and requirements. ## Choose your installation path | Use case | Installation guide | Secondary storage | Complexity | Best for | | ------------------------- | ------------------------------------------ | ---------------------------------------------- | ---------- | ------------------------------------------------- | | **Testing & evaluation** | [Quick install](./quick-install) | Embedded H2 (single-broker only) | Low | Local development, POCs, learning | | **Production with RDBMS** | [Install with RDBMS](./helm-with-rdbms) | PostgreSQL, Oracle, MariaDB, MySQL, SQL Server | Medium | RDBMS-first organizations, no ES/OS license | | **High-scale production** | [Install for production](./production) | Elasticsearch/OpenSearch or RDBMS | High | High-throughput, multi-team, OIDC, HA, monitoring | :::tip Decision criteria - Use **quick install** if you want to evaluate quickly without external dependencies. - Use **install with RDBMS** if your organization standardizes on relational databases. - Use **production install** if you need security, scalability, and operational features. - If you don’t have required infrastructure, see [deploy required dependencies](/self-managed/deployment/helm/configure/operator-based-infrastructure.md). ::: ## Installation guides - **[Quick install](./quick-install)**: Deploy a single-broker Orchestration Cluster with embedded H2 secondary storage. Best for testing and early development. - **[Install with RDBMS](./helm-with-rdbms)**: Configure Camunda to use a relational database for secondary storage, including schema setup and JDBC driver configuration. - **[Production install](./production)**: Deploy a production-ready environment with TLS, OIDC authentication, multi-namespace support, and monitoring. Supports both Elasticsearch/OpenSearch and RDBMS backends. - **[Deploy required dependencies](/self-managed/deployment/helm/configure/operator-based-infrastructure.md)**: Deploy PostgreSQL (management components), Elasticsearch/OpenSearch (secondary storage), and Keycloak on Kubernetes using official operators when managed services are not available. --- ## Install Camunda for production with Helm This is a **scenario-based, production-focused, step-by-step guide** for setting up the [Camunda Helm chart](https://artifacthub.io/packages/helm/camunda/camunda-platform). It provides a resilient baseline for most production use cases. This is a single production install guide with database options in one flow: - **Non-SQL secondary storage** (Elasticsearch/OpenSearch) - **RDBMS secondary storage** (for supported components) AWS examples are used where helpful, but the flow applies to other [supported Kubernetes distributions](/reference/supported-environments.md#deployment-options) with equivalent services. ## Prerequisites Before proceeding with the setup, ensure the following requirements are met: - **Kubernetes Cluster**: A functioning Kubernetes cluster with kubectl access and block storage persistent volumes for stateful components. This guide will use an AWS EKS cluster for reference. Step-by-step documentation is available to deploy an EKS cluster with [Terraform](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/terraform-setup.md), and [install Camunda 8](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/eks-helm.md). - **Helm**: Make sure the [Helm CLI v4](/reference/supported-environments.md#clients) is installed. Helm v3 is not supported for Camunda 8.10 and later. - **DNS Configuration**: You must have access to configure DNS for your domain in order to point to the Kubernetes cluster Ingress. - **TLS Certificates**: Obtain valid X.509 certificates for your domain from a trusted Certificate Authority. - **External Dependencies**: Provision the following external dependencies: - **PostgreSQL-compatible database**: Required for Web Modeler persistence. This guide uses Amazon Aurora PostgreSQL as an example. For AWS-specific steps, see [Aurora PostgreSQL module setup](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/terraform-setup.md#postgresql-module-setup). - **Secondary storage backend for the Orchestration Cluster**: Choose one option: - **Non-SQL**: Elasticsearch/OpenSearch (this guide uses Amazon OpenSearch as the example). For AWS-specific steps, see [OpenSearch](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/eksctl.md#4-opensearch-domain). - **RDBMS**: See [configure RDBMS in Helm](/self-managed/deployment/helm/configure/database/rdbms.md) and the [RDBMS example deployment](/self-managed/deployment/helm/install/helm-with-rdbms.md). - **Identity Provider (IdP)**: An OIDC-compatible identity provider for authentication. See [Authentication and authorization](/self-managed/deployment/helm/configure/authentication-and-authorization/index.md) for supported options. :::tip No managed services available? If managed PostgreSQL, Elasticsearch, or an external OIDC provider are not available in your organization, you can deploy these infrastructure components on Kubernetes using official operators. See [Required infrastructure](/self-managed/deployment/helm/configure/operator-based-infrastructure.md) for instructions. ::: - **Ingress NGINX**: Ensure the [Ingress-nginx](https://github.com/kubernetes/ingress-nginx) controller is set up in the cluster. - **AWS OpenSearch Snapshot Repository** - To store the backups of the Camunda web applications. This repository must be configured with OpenSearch to take backups which are stored in Amazon S3. See the [official AWS guide](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/managedomains-snapshot-registerdirectory.html) for detailed steps. - **Amazon S3** - An additional bucket to store backup files of the Orchestration Cluster brokers. - **Resource Planning**: Make sure you have understood the considerations for [sizing Camunda Clusters](/components/best-practices/architecture/sizing-your-environment.md#camunda-8-self-managed), and have evaluated sufficient CPU, memory, and storage necessary for the deployment. Ensure all prerequisites are in place to avoid issues during installation or when upgrading in a production environment. ## Architecture overview This is the high-level architecture diagram for our production setup, as illustrated below: ![Architecture Diagram](./img/architecture.jpg) For more information refer to the Camunda 8 [Kubernetes reference architectures](/docs/self-managed/reference-architecture/kubernetes/#kubernetes). ## Installation and configuration After following the [prerequisites](#prerequisites), you should have a Kubernetes cluster ready with `kubectl` and the `helm` CLI installed. ### Namespace setup To get started, create two namespaces: ```bash kubectl create namespace management-and-modeling kubectl create namespace orchestration ``` - **Namespace `management-and-modeling`:** We will install [Management Identity](/self-managed/components/management-identity/overview.md), Console, and the Web Modeler components. - **Namespace `orchestration`**: We will install [Orchestration Cluster](/self-managed/components/orchestration-cluster/zeebe/overview.md), [Connectors](/self-managed/components/connectors/overview.md) and [Optimize](/self-managed/components/optimize/overview.md). Each component is installed by the Helm chart automatically, and does not need to be installed separately. :::note For more information on the difference between the Orchestration Cluster and the Web Modeler and Console cluster, see the Camunda 8 [reference architecture](/self-managed/reference-architecture/reference-architecture.md#orchestration-cluster-vs-web-modeler-and-console). ::: ### Install the Helm chart As there will be a Helm deployment in each namespace, create your own `management-and-modeling-values.yaml` and `orchestration-values.yaml`, or modify an existing setup by applying the production recommendations in the next section. Example values files can be found at the [end of this guide](#create-a-production-valuesyaml). The Camunda Helm chart can be installed in each namespace using the following command: ```bash # This will add our chart repository so you can pull from it helm repo add camunda https://helm.camunda.io # This will update the chart repository. Please make sure to run this command before every install or upgrade helm repo update # This will install the latest Camunda Helm chart in the management-and-modeling namespace with the latest applications/dependencies. helm install camunda camunda/camunda-platform --version $HELM_CHART_VERSION -n management-and-modeling \ --values management-and-modeling-values.yaml # This will install the latest Camunda Helm chart in the Orchestration namespace with the latest applications/dependencies. helm install camunda camunda/camunda-platform --version $HELM_CHART_VERSION -n orchestration \ --values orchestration-values.yaml ``` ### Ingress TLS setup In order to access Camunda through HTTPS with Ingress, TLS must be enabled. Enabling TLS requires the following: 1. **Domain name**: A public registered domain that has configurable DNS records. This guide will use `camunda.example.com` as the domain. 2. **TLS certificate**: A TLS certificate created for your domain. The certificate must be an X.509 certificate, issued by a trusted Certificate Authority. The certificate must include the correct domain names (Common Name or Subject Alternative Names) to secure Ingress resources. Reach out to your DNS provider if you are unsure on how to create a TLS certificate. It is not recommended to use self-signed certificates. 3. **TLS secret**: A TLS secret created from your TLS certificate. This guide will use a secret called `camunda-platform`. For more information, see the Kubernetes documentation on how to create a [TLS secret](https://kubernetes.io/docs/concepts/configuration/secret/#tls-secrets). :::note Multiple ingress controller support Multiple ingress controllers are supported. Specify `ingress.className` or `ingress.grpc.className` to assign the ingress to the desired ingress controller. ::: The following is an example `values.yaml` configuration using the example Ingress domain and TLS secret: ```yaml global: ingress: enabled: true className: nginx host: camunda.example.com tls: enabled: true secretName: camunda-platform ``` Optionally, you can configure Ingress for the Orchestration Cluster [gRPC API](/apis-tools/zeebe-api/grpc.md): ```yaml orchestration: ingress: grpc: enabled: true className: nginx host: zeebe-grpc.camunda.example.com tls: enabled: true secretName: camunda-platform-zeebe-grpc ``` More information can be found in the [Ingress setup](/self-managed/deployment/helm/configure/ingress/ingress-setup.md) guide. ### Identity provider integration Once secure HTTPS connections are enabled and correctly configured via Ingress, the next step is configuring authentication with an OIDC-compatible identity provider. Camunda supports several authentication methods. Choose the guide that matches your identity provider: - **[Microsoft Entra ID](/self-managed/deployment/helm/configure/authentication-and-authorization/microsoft-entra.md)**: For organizations using Microsoft Entra ID (formerly Azure Active Directory). - **[External Keycloak](/self-managed/deployment/helm/configure/authentication-and-authorization/external-keycloak.md)**: For organizations with an existing Keycloak instance. - **[Generic OIDC provider](/self-managed/deployment/helm/configure/authentication-and-authorization/generic-oidc-provider.md)**: For other OIDC-compatible providers such as Okta, Auth0, or Amazon Cognito. For a complete overview of authentication options and their trade-offs, see [Authentication and authorization](/self-managed/deployment/helm/configure/authentication-and-authorization/index.md). :::note You must create Kubernetes secrets for all client secrets required by your identity provider configuration before installing the Helm chart. ::: ### Connect external databases :::note To allow for easier testing, the Camunda Helm chart provides databases as an external dependency, such as [Bitnami Elasticsearch Helm chart](https://artifacthub.io/packages/helm/bitnami/elasticsearch) and the [Bitnami PostgreSQL Helm chart](https://artifacthub.io/packages/helm/bitnami/postgresql). These dependency charts should be disabled in a production setting, and production databases should be used instead. ::: This guide keeps database configuration in one flow and provides two options: - **Option A (Non-SQL secondary storage):** Use Elasticsearch or OpenSearch for the Orchestration Cluster secondary storage backend. This path is also required when deploying Optimize. - **Option B (RDBMS secondary storage):** Use a relational database as secondary storage for supported components. For this path, follow [Configure RDBMS in Helm chart](/self-managed/deployment/helm/configure/database/rdbms.md) and use the [RDBMS example deployment](/self-managed/deployment/helm/install/helm-with-rdbms.md) as a focused walkthrough. The examples below use Option A with Amazon OpenSearch for secondary storage, and Amazon Aurora PostgreSQL for Management Identity and Web Modeler. You should have one Amazon OpenSearch instance and one Amazon Aurora PostgreSQL instance (with two databases) ready to use, complete with a username, password, and URL for each datastore. If these have not been configured, see the [prerequisites](#prerequisites) for requirements. #### Connecting to Amazon OpenSearch The following example `values.yaml` enables OpenSearch with the required configuration. This example also globally disables all internal component configuration for Elasticsearch through `global.elasticsearch.enabled: false`, and disables internal Elasticsearch through `elasticsearch.enabled: false`: ```yaml global: elasticsearch: enabled: false opensearch: enabled: true auth: username: user secret: existingSecret: opensearch-credentials existingSecretKey: password url: protocol: https host: opensearch.example.com port: 443 elasticsearch: enabled: false ``` #### Connect to an external database for Management Identity The following example `values.yaml` configures Management Identity with an external Amazon Aurora PostgreSQL database: ```yaml identity: externalDatabase: enabled: true host: external-postgres-host port: 5432 username: identity_user database: identity_db secret: existingSecret: identity-db-secret existingSecretKey: database-password ``` :::note Make sure the host and port are correctly defined. ::: #### Connect to an external database for Web Modeler The following example `values.yaml` configures Web Modeler with an external Amazon Aurora PostgreSQL database: ```yaml webModeler: externalDatabase: url: jdbc:postgresql://external-postgres-host:5432/camunda_db user: web_modeler_user secret: existingSecret: web-modeler-db-secret existingSecretKey: database-password ``` Use the `existingSecret` parameter to specify a pre-existing Kubernetes secret containing the password. This approach allows the Camunda Helm chart to reference credentials stored securely in your cluster, rather than hardcoding sensitive data in values files or templates. For more information on connecting to external databases, the following guides are available for the Camunda Helm chart: - [Helm chart database configuration](/self-managed/deployment/helm/configure/database/index.md) - [Non-SQL database configuration](/self-managed/deployment/helm/configure/database/non-sql.md) - Using an [existing Elasticsearch instance](/self-managed/deployment/helm/configure/database/elasticsearch/using-external-elasticsearch.md) - Using [Amazon OpenSearch service](/self-managed/deployment/helm/configure/database/using-external-opensearch.md) - [RDBMS configuration](/self-managed/deployment/helm/configure/database/rdbms.md) - Using Amazon OpenSearch service [through IRSA](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/terraform-setup.md#opensearch-module-setup) (only applicable if you are using EKS) - Running Web Modeler on [Amazon Aurora PostgreSQL](/self-managed/components/hub/configuration/database.md#running-web-modeler-on-amazon-aurora-postgresql) ## Orchestration Cluster configuration :::note At this point, you should be able to connect to your platform through HTTPS, correctly authenticate users using your configured identity provider, and have connected to external databases such as Amazon OpenSearch and Amazon Aurora PostgreSQL. ::: The next steps focus on the Camunda application-specific configurations suitable for a production environment. The following sections continue to add to the `management-and-modeling-values.yaml` and `orchestration-values.yaml` at the Camunda component-level. ### Elasticsearch/OpenSearch index retention An index lifecycle management (ILM) policy in OpenSearch is crucial for efficient management and operation of large-scale search and analytics workloads. ILM policies provide a framework for automating the management of index lifecycles, which directly impacts performance, cost efficiency, and data retention compliance. The following example configures an ILM policy for the Orchestration Cluster, and can be added to your `orchestration-values.yaml`: ```yaml orchestration: retention: enabled: true minimumAge: 30d policyName: zeebe-record-retention-policy ``` For more information on configuring ILM policy, refer to the configuration guide on the [OpenSearch exporter](/self-managed/components/orchestration-cluster/zeebe/exporters/opensearch-exporter.md#configuration). ### Configure backups In order to configure backups, refer to the [backup guide](/self-managed/operational-guides/backup-restore/backup-and-restore.md) for Self-Managed installations. ## Operational configuration options After following the above steps, you should already have a solid base for running your platform in a production setting. The rest of this guide provides general Kubernetes-based guidance on configuration of the Camunda Helm chart for long-term maintenance. ### Scaling and performance The following resources and configuration options are important to keep in mind regarding scaling: - To scale the Orchestration Cluster, the following values can be modified: ```yaml orchestration: clusterSize: "3" partitionCount: "3" replicationFactor: "3" ``` - `orchestration.clusterSize`: to the amount of brokers to configure - `orchestration.partitionCount`: how many [Orchestration Cluster partitions](/components/zeebe/technical-concepts/partitions.md) are configured for each cluster - `orchestration.replicationFactor`: the [number of replicas](/components/zeebe/technical-concepts/partitions.md#replication) that each partition replicates to :::note `orchestration.partitionCount` does not yet support dynamic scaling. You will not be able to modify this property. It is better to over-provision the partitions to allow potential growth as dynamic partitioning isn't possible yet. ::: - Ensure the resources (CPU and memory) are appropriate for your workload size. For example, the resource limits can be changed for the Orchestration Cluster by modifying the following values: ```yaml orchestration: resources: requests: cpu: 800m memory: 1200Mi limits: cpu: 2000m memory: 1920Mi ``` - It is possible to set a LimitRange on the namespace. Please refer to the [Kubernetes documentation](https://kubernetes.io/docs/tasks/administer-cluster/manage-resources/memory-default-namespace/) on setting a LimitRange. ### Reliability The following resources and configuration options are important to keep in mind regarding reliability: #### Persistent volume reclaim policy :::warning Risk of data loss If your StorageClass uses a `Delete` reclaim policy (the default in many Kubernetes distributions and OpenShift), Orchestration Cluster broker data will be **permanently lost** if a PVC is deleted. This can lead to complete data loss and is unrecoverable. ::: Ensure your StorageClass uses a `Retain` reclaim policy for production deployments. Verify your configuration on Kubernetes: ```bash kubectl get storageclass # RECLAIMPOLICY should show "Retain", not "Delete" ``` On OpenShift, verify your configuration using the `oc` CLI: ```bash oc get storageclass # RECLAIMPOLICY should show "Retain", not "Delete" ``` :::note Equivalent mechanisms Alternative configurations that preserve the underlying volume and allow it to be reattached to a recreated PVC are also acceptable — for example, patching the `persistentVolumeReclaimPolicy` of individual PVs to `Retain`, or admission policies (Kyverno, Gatekeeper) enforcing `Retain` on dynamically provisioned PVs. Reattaching a retained PV to a new PVC may require manual steps, such as clearing the `claimRef` on the released PV, depending on your provisioner. Snapshot- or backup-based strategies are not equivalent, because they do not preserve the original volume binding required for a Zeebe broker to resume from its existing partition data without manual recovery. If you rely on an alternative mechanism, you are responsible for validating it against your upgrade and disaster recovery scenarios in a non-production environment. ::: For more details, see [troubleshooting](/self-managed/operational-guides/troubleshooting.md#zeebe-data-loss-after-pvc-deletion) and the [Kubernetes documentation on reclaim policies](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#reclaiming). #### Node affinity and tolerations - Check node affinity and tolerations. Refer to the [Kubernetes documentation](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) to modify the node affinity and tolerations. For example, this is the default affinity configuration for the zeebe Pod in the Camunda Helm chart: ```yaml affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app.kubernetes.io/component operator: In values: - zeebe topologyKey: kubernetes.io/hostname ``` This configuration ensures that Zeebe Pods with the default label `app.kubernetes.io/component=zeebe` are not scheduled on the same node. The primary benefits include: - High availability: If one node fails, other nodes running the same component remain unaffected. - Load distribution: Balances the workload across nodes. - Fault tolerance: Reduces the impact of a node-level failure. - It is possible to set a `podDisruptionBudget`, as in the following example for the Orchestration Cluster: ```yaml orchestration: podDisruptionBudget: enabled: false minAvailable: 0 maxUnavailable: 1 ``` - Version management: Stay on a stable Camunda and Kubernetes version. Follow Camunda’s [release notes](/reference/announcements-release-notes/870/870-release-notes.md) for security patches or critical updates. - Secrets should be created prior to installing the Helm chart so they can be referenced as existing secrets. Create your secrets explicitly using `kubectl` or an external secret manager, then reference them in your Helm values files. For details, see the [secret management guide](/self-managed/deployment/helm/configure/secret-management.md). :::note It is best to store secrets in an external secret manager such as [Vault by HashiCorp](https://www.vaultproject.io/) in case of a total outage. ::: - When upgrading the Camunda Helm chart, make sure to read the [upgrade guide](/self-managed/upgrade/components/index.md) and corresponding new version release notes before upgrading. Perform the upgrade on a test environment first before attempting in production. The following is an example configuration for the Orchestration Cluster to create persistent storage: ```yaml orchestration: extraVolumes: - name: persistent-state emptyDir: {} extraVolumeMounts: - name: persistent-state mountPath: /mount ``` - It is recommended to set a memory and resource quota for your namespace. Please refer to the [Kubernetes documentation](https://kubernetes.io/docs/tasks/administer-cluster/manage-resources/quota-memory-cpu-namespace/) to do so. Namespace-Level Quotas apply limits to all workloads within a namespace. It ensures aggregate resource consumption by all pods in the namespace do not exceed your desired resource limits. ### Security The following resources and configuration options are important to keep in mind regarding security: - To limit unnecessary permissions, and reduce risk of token exploitations, it is recommended to disable auto-mounting of the default Service Account. It is possible to do so by adding the following to both of your values files: ```yaml identity: serviceAccount: enabled: false console: serviceAccount: enabled: false webModeler: serviceAccount: enabled: false connectors: serviceAccount: enabled: false orchestration: serviceAccount: enabled: false optimize: serviceAccount: enabled: false ``` :::note You should only enable the auto-mounting of a service account token when the application explicitly needs access to the Kubernetes API server, or you have created a service account with the exact permissions required for the application and bound it to the pod. ::: - [Network Policies](https://kubernetes.io/docs/concepts/services-networking/network-policies/) can be enabled with Camunda Helm charts if needed by your infrastructure requirements. - It is possible to have a pod security standard that is suited to your security constraints. This is enabled by modifying the Pod Security Admission. See the [Pod Security Admission](https://kubernetes.io/docs/concepts/security/pod-security-admission/) guide in the official Kubernetes documentation for more information. - By default, the Camunda Helm chart is configured to use a read-only root file system for the pod. It is advisable to retain this default setting, and no modifications are required in your Helm values files. - Disable privileged containers. This can be achieved by implementing a pod security policy. For more information, see the official [Kubernetes documentation](https://kubernetes.io/docs/concepts/security/pod-security-admission/). - It is possible to modify either the `containerSecurityContext` or the `podSecurityContext`. The following example show the default configuration for the Orchestration Cluster: ```yaml podSecurityContext: runAsNonRoot: true fsGroup: 1001 seccompProfile: type: RuntimeDefault containerSecurityContext: allowPrivilegeEscalation: false privileged: false readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 1001 seccompProfile: type: RuntimeDefault ``` To add any other security constraints to your Helm values files, refer to the official [Kubernetes documentation](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). - It is recommended to pull images exclusively from a private registry, such as [Amazon ECR](https://aws.amazon.com/ecr/), rather than directly from Docker Hub. Doing so enhances control over the images, avoids rate limits, and improves performance and reliability. Additionally, you can configure your cluster to pull images only from trusted registries. Tools like [Open Policy Agent](https://blog.openpolicyagent.org/securing-the-kubernetes-api-with-open-policy-agent-ce93af0552c3#3c6e) can be used to enforce this restriction. - Please refer to our [installing in an air-gapped environment guide](/self-managed/deployment/helm/configure/registry-and-images/air-gapped-installation.md) when deploying Camunda in Air-gapped environments - Open Policy Agent can also be used to [allowlist Ingress hostnames](https://www.openpolicyagent.org/docs/latest/kubernetes-tutorial/#4-define-a-policy-and-load-it-into-opa-via-kubernetes). ### Observability and monitoring The following resources and configuration options are important to keep in mind regarding observability and monitoring: - It is possible to enable integration with Prometheus, a popular monitoring solution, in the Camunda Helm chart. This can be configured by adding the following configuration below to your preferred Helm values file: ```yaml prometheusServiceMonitor: enabled: true ``` - A tool such as [Loki](https://grafana.com/oss/loki/) can be used for the retention and archival of logs. It can also be used to aggregate logs. ## Create a production `values.yaml` The following is a complete configuration example, taking all the above sections into consideration. ### Example management-and-modeling configuration The following example `management-and-modeling-values.yaml` is provided to the `management-and-modeling` [namespace](#namespace-setup): ```yaml global: ingress: enabled: true className: nginx host: management-and-modeling-host.com tls: enabled: true secretName: camunda-platform identity: auth: # Configure authentication based on your identity provider. # Set type to: "KEYCLOAK" (default), "MICROSOFT", or "GENERIC" # See: https://docs.camunda.io/docs/self-managed/deployment/helm/configure/authentication-and-authorization/ type: "" # ... additional provider-specific configuration # Refer to the authentication guide for your chosen provider. identity: enabled: true contextPath: /identity firstUser: secret: existingSecret: camunda-credentials existingSecretKey: identity-user-password externalDatabase: enabled: true host: external-postgres-host port: 5432 username: identity_user database: identity_db secret: existingSecret: identity-db-secret existingSecretKey: database-password webModeler: enabled: true contextPath: /modeler restapi: mail: # This value is required, otherwise the restapi pod wouldn't start. fromAddress: noreply@example.com externalDatabase: url: jdbc:postgresql://external-postgres-host:5432/camunda_db user: camunda_user secret: existingSecret: camunda-db-secret existingSecretKey: database-password prometheusServiceMonitor: enabled: true labels: release: kube-prometheus-stack orchestration: enabled: false optimize: enabled: false connectors: enabled: false elasticsearch: enabled: false console: # Multi-namespace deployments require manual console.configuration to define # components across namespaces. The oAuth section must match your global.identity.auth settings. # See: https://docs.camunda.io/docs/self-managed/deployment/helm/configure/authentication-and-authorization/ configuration: | camunda: console: oAuth: audience: "" clientId: "" issuer: "" jwksUri: "" type: "" wellKnown: "" managed: method: plain releases: - name: camunda namespace: management-and-modeling version: 14.x.x components: - name: Console id: console version: 8.9.x url: https://management-and-modeling-host.com/ readiness: http://camunda-console.oidc:9100/health/readiness metrics: http://camunda-console.oidc:9100/prometheus - name: Identity id: identity version: 8.9.x url: https://management-and-modeling-host.com/identity readiness: http://camunda-identity.oidc:82/actuator/health metrics: http://camunda-identity.oidc:82/actuator/prometheus - name: WebModeler id: webModelerWebApp version: 8.9.x url: https://management-and-modeling-host.com/modeler readiness: http://camunda-web-modeler-restapi.oidc:8091/health/readiness metrics: http://camunda-web-modeler-restapi.oidc:8091/metrics - name: camunda namespace: orchestration version: 14.x components: - name: Operate id: operate version: 8.9.x url: https://orchestration-host.com/orchestration/operate readiness: http://camunda-zeebe.orchestration:9600/operate/actuator/health/readiness metrics: http://camunda-zeebe.orchestration:9600/operate/actuator/prometheus - name: Optimize id: optimize version: 8.9.x url: https://orchestration-host.com/optimize readiness: http://camunda-optimize.orchestration:80/optimize/api/readyz metrics: http://camunda-optimize.orchestration:8092/actuator/prometheus - name: Tasklist id: tasklist version: 8.9.x url: https://orchestration-host.com/orchestration/tasklist readiness: http://camunda-zeebe.orchestration:9600/tasklist/actuator/health/readiness metrics: http://camunda-zeebe.orchestration:9600/tasklist/actuator/prometheus - name: Orchestration Cluster id: orchestration version: 8.9.x urls: grpc: https://zeebe-orchestration-host.com http: https://orchestration-host.com/orchestration readiness: http://camunda-zeebe.orchestration:9600/actuator/health/readiness metrics: http://camunda-zeebe.orchestration:9600/actuator/prometheus ``` ### Example orchestration configuration The following example `orchestration-values.yaml` is provided to the `orchestration` [namespace](#namespace-setup): ```yaml global: ingress: enabled: true className: nginx host: orchestration-host.com tls: enabled: true secretName: camunda-platform elasticsearch: enabled: false opensearch: enabled: true auth: username: user secret: existingSecret: opensearch-credentials existingSecretKey: password url: protocol: https host: opensearch.example.com port: 443 identity: service: url: "http://management-identity.management-and-modeling.svc.cluster.local:80/identity" auth: # Configure authentication based on your identity provider. # Set type to: "KEYCLOAK" (default), "MICROSOFT", or "GENERIC" # See: https://docs.camunda.io/docs/self-managed/deployment/helm/configure/authentication-and-authorization/ type: "" # ... additional provider-specific configuration # Refer to the authentication guide for your chosen provider. orchestration: contextPath: /orchestration ingress: grpc: enabled: true className: nginx host: grpc-{{ .Values.global.ingress.host }} tls: enabled: true secretName: camunda-platform-zeebe-grpc retention: enabled: true minimumAge: 30d policyName: zeebe-record-retention-policy orchestration: security: authentication: method: oidc connectors: contextPath: /connectors optimize: enabled: true contextPath: /optimize identity: enabled: false identityKeycloak: enabled: false webModeler: enabled: false webModelerPostgresql: enabled: false elasticsearch: enabled: false ``` ## Next steps ### Upgrade and maintenance - Make sure to follow our [upgrade guide](/self-managed/upgrade/helm/index.md) when performing the upgrade on your Helm chart. - Ensure your Kubernetes secrets are created before installing or upgrading the Helm chart. For details, see the [secret management guide](/self-managed/deployment/helm/configure/secret-management.md). ### Adding more orchestration clusters Camunda 8 supports running multiple orchestration clusters in separate namespaces. This setup allows you to isolate environments such as development, staging, and production, while sharing infrastructure resources. To add another orchestration cluster, install the Helm chart again in a different namespace with unique release names and configuration values. ### Running benchmarks If you would like to run benchmarks on the platform, refer to the Camunda 8 benchmark [community project](https://github.com/camunda-community-hub/camunda-8-benchmark). ### Reference architectures You can lean more about Camunda production deployment and available deployment architectures in [Camunda Deployment Reference Architecture](/self-managed/reference-architecture/reference-architecture.md) section of our documentation. --- ## Install Camunda with Helm for development Use this guide to quickly install the Camunda 8 orchestration cluster for testing and development. :::tip Need a Kubernetes cluster? If you don't have a Kubernetes cluster yet, check out our setup guides: - **Local development**: Follow our [kind tutorial](/self-managed/deployment/helm/cloud-providers/kind.md) to set up a local Kubernetes cluster. - **Cloud providers**: See our [cloud provider guides](/self-managed/deployment/helm/cloud-providers/index.md) for Amazon EKS, Google GKE, Azure AKS, and Red Hat OpenShift. ::: :::note In this guide, you deploy the Orchestration Cluster with Basic authentication and RDBMS (embedded H2) as secondary storage. For a full deployment with all components (Optimize, Web Modeler, Console, Management Identity, and Keycloak), follow our [kind tutorial](/self-managed/deployment/helm/cloud-providers/kind.md). For production environments, see the [production installation guide](/self-managed/deployment/helm/install/production/index.md). ::: ## Prerequisites - **Kubernetes cluster**: A functioning Kubernetes cluster with [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) access and block-storage persistent volumes for stateful components. - **Helm**: The Helm CLI v4 installed. See [Installing Helm](https://helm.sh/docs/intro/install/). ## Orchestration Cluster only The Helm chart deploys the Camunda Orchestration Cluster with **Basic authentication** and **RDBMS** as secondary storage (using embedded H2 when no external database URL is provided), intended only for testing and development. No external database or identity provider is required. :::warning H2 topology limitations Embedded H2 is non-production only and valid only with a single broker and a single partition. Do not run embedded H2 with a multi-broker Helm topology. H2 is local to each broker and does not provide shared secondary storage, which can cause incomplete or inconsistent query results. ::: In production, Camunda 8 is typically deployed together with additional components such as Optimize, Web Modeler, and Console, which require OIDC-based authentication and external databases. For a full local deployment with all components, follow our [kind tutorial](/self-managed/deployment/helm/cloud-providers/kind.md). 1. **Create a namespace to install the platform on Kubernetes:** ```bash kubectl create namespace orchestration ``` Output: ```bash namespace/orchestration created ``` 2. **Add the Helm repository:** To install the Camunda 8 Self-Managed [Helm chart](https://helm.sh/docs/topics/charts/), add the [Helm repository](https://helm.sh/docs/topics/chart_repository/) with the following command: ```bash helm repo add camunda https://helm.camunda.io helm repo update ``` 3. **Install the Helm chart:** ```bash helm install camunda camunda/camunda-platform \ --set orchestration.exporters.rdbms.enabled=true \ --set orchestration.clusterSize=1 \ --set orchestration.partitionCount=1 \ --set orchestration.replicationFactor=1 \ --set-string 'orchestration.env[0].name=CAMUNDA_PERSISTENT_SESSIONS_ENABLED' \ --set-string 'orchestration.env[0].value=false' \ -n orchestration ``` This enables the RDBMS exporter with embedded H2 as secondary storage. The cluster is configured with a single broker, a single partition, and a replication factor of 1. The replication factor must be less than or equal to the cluster size. For a single-broker cluster, the only valid replication factor is 1. The embedded H2 database is local to each broker. Running multiple brokers would result in separate, independent databases and incomplete query results. If you need multiple brokers, switch to a shared external backend (for example, PostgreSQL) instead of H2. If you already have an invalid H2 setup: 1. For local/dev use, reduce to `clusterSize=1`, `partitionCount=1`, `replicationFactor=1` and use file-based H2. 2. For clustered use, migrate secondary storage to an external persistent backend. Starting with Camunda 8.9, the Helm chart no longer provisions Elasticsearch by default. You must explicitly enable a secondary storage backend (RDBMS, Elasticsearch, or OpenSearch) in your Helm values. 4. **Access the components:** Use the default credentials: ``` username: demo password: demo ``` Set up port-forwarding to access the services: ```bash # Zeebe Gateway (for gRPC and REST API) kubectl port-forward svc/camunda-zeebe-gateway 26500:26500 -n orchestration kubectl port-forward svc/camunda-zeebe-gateway 8080:8080 -n orchestration # Connectors kubectl port-forward svc/camunda-connectors 8088:8080 -n orchestration ``` **Verify the installation:** Test the [Zeebe Gateway](/reference/glossary.md#zeebe-gateway) HTTP endpoint (Orchestration Cluster REST API): ```bash curl -u demo:demo http://localhost:8080/v2/topology ``` You should see a JSON response with the cluster topology information. Available services: - **Operate:** [http://localhost:8080/operate](http://localhost:8080/operate) - Monitor process instances - **Tasklist:** [http://localhost:8080/tasklist](http://localhost:8080/tasklist) - Complete user tasks - **Admin:** [http://localhost:8080/admin](http://localhost:8080/admin) - User and permission management - **Connectors:** [http://localhost:8088](http://localhost:8088) - External system integrations - **Zeebe Gateway (gRPC):** localhost:26500 - Process deployment and execution - **Zeebe Gateway (HTTP):** [http://localhost:8080](http://localhost:8080) - Orchestration Cluster REST API :::note In Camunda 8.8+, Operate, Tasklist, and Admin are integrated into the Orchestration Cluster and share the same endpoint (port 8080). ::: ## Full Cluster To deploy the full Camunda 8 platform with all components (Optimize, Web Modeler, Console, Management Identity, and Keycloak), follow our [kind tutorial](/self-managed/deployment/helm/cloud-providers/kind.md). The full deployment requires OIDC-based authentication and [deploying required dependencies with Kubernetes operators](/self-managed/deployment/helm/configure/operator-based-infrastructure.md) for PostgreSQL, Elasticsearch, and Keycloak. ## Troubleshoot installation issues Verify that each pod is running and ready. If a pod is pending, it cannot be scheduled onto a node. This usually happens when the cluster does not have enough resources. To check messages from the scheduler, run: ```shell kubectl describe pods ``` If `describe` does not help, check the pod logs by running: ```shell kubectl logs -f ``` ## Install a specific version By default, the Camunda Helm chart installs the latest version of the [Camunda 8 applications](/reference/supported-environments.md). Because Helm chart and application versions are released independently, their version numbers differ. For details, see the [Camunda 8 Helm Chart Version Matrix](https://helm.camunda.io/camunda-platform/version-matrix/). To install the latest version of the chart and its application dependencies, run: ```shell helm install camunda camunda/camunda-platform \ --values https://helm.camunda.io/camunda-platform/values/values-latest.yaml ``` To install a specific chart version, use the `--version` flag with the chart version number. For example, the chart version for Camunda 8.8 is `13`: ```shell helm install camunda camunda/camunda-platform --version 13 \ --values https://helm.camunda.io/camunda-platform/values/values-v8.8.yaml ``` Specifying only the major chart version (for example, `13`) installs the latest available `13.x.y` release. You can also specify a minor version (for example, `12.6`) to install the latest `12.6.y` release. If you are unsure which chart version corresponds to your Camunda application version, run: ```shell helm search repo -l camunda/camunda-platform ``` This command lists all available chart versions and their corresponding application versions. ## Notes and requirements - **Zeebe** supports Kubernetes startup and liveness probes. See [Gateway health probes](/self-managed/components/orchestration-cluster/zeebe/configuration/gateway-health-probes.md). - **Zeebe** must be deployed as a [StatefulSet](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/) to preserve cluster node identities. StatefulSets require persistent storage, which you must provision in advance. The type of storage depends on your cloud provider. - **Docker pull limits** apply when downloading Camunda 8 images from Docker Hub. To avoid disruptions, authenticate with Docker Hub or use a mirror registry. - **Air-gapped environments** require additional configuration. See [Helm chart air-gapped environment installation](/self-managed/deployment/helm/configure/registry-and-images/air-gapped-installation.md). - **Full deployment**: To deploy all Camunda 8 components with OIDC authentication and Kubernetes operators, follow our [kind tutorial](/self-managed/deployment/helm/cloud-providers/kind.md) or the [cloud provider guides](/self-managed/deployment/helm/cloud-providers/index.md). ## Next steps - Explore the [Camunda Reference Architectures](/self-managed/reference-architecture/reference-architecture.md) to learn how to run Camunda 8 in production. ## Additional resources - [Deploy infrastructure with Kubernetes operators](/self-managed/deployment/helm/configure/operator-based-infrastructure.md) — deploy PostgreSQL, Elasticsearch, and Keycloak using official Kubernetes operators for the full platform. - [Helm chart Amazon OpenSearch service usage](/self-managed/deployment/helm/configure/database/using-external-opensearch.md) — configure Camunda to use Amazon OpenSearch Service instead of the default Elasticsearch. - [Getting started with document handling](/self-managed/concepts/document-handling/overview.md) — configure document storage and management in Camunda 8. - [Production installation](/self-managed/deployment/helm/install/production/index.md) — configure and install the helm chart for production environments. - [Helm Configuration](/self-managed/deployment/helm/configure/index.md) - customize your installation by modifying the Helm chart configuration. --- ## Helm chart diagnostics ## Diagnostics collection script This script automates the process of gathering logs and diagnostics from a Camunda Helm chart deployment running in a Kubernetes cluster. The script collects all relevant information (including pod logs, events, and resource details) into a single directory, and outputs it in a .zip file to make it easier to share this information with the Camunda Support team. :::caution Data privacy notice Before sharing the generated diagnostics file with Camunda Support, review and remove any sensitive information such as passwords, API keys, personal data, or business-sensitive data from the collected logs and configuration data. ::: ### What the script collects The script outputs the following data from your namespace and creates a zip file containing the following: - **Pod Information**: Current and previous logs and full pod descriptions. - **Cluster Events**: Sorted by time to help identify recent issues. - **Storage Details**: PV and PVC descriptions. - **Cluster Nodes**: Node descriptions. - **Network Resources**: Services, endpoints, and ingresses. - **Configuration**: Config map information. ### Usage 1. Save the following script as `camunda-collect-diagnostics.sh` for example. 2. Make the script executable: ```bash chmod +x camunda-collect-diagnostics.sh ``` 3. Execute the script, replacing `` with the namespace of your Camunda deployment: ```bash ./camunda-collect-diagnostics.sh --namespace ``` 4. Review the generated `.zip` archive and remove any sensitive or personally identifiable information (PII) before sharing. 5. Share the reviewed `.zip` archive with Camunda Support. ### Diagnostic collection script ```bash #!/bin/bash # Parse arguments while [[ "$#" -gt 0 ]]; do case $1 in --namespace) namespace="$2"; shift ;; *) echo "Unknown parameter passed: $1"; exit 1 ;; esac shift done # Check if namespace is provided if [[ -z "$namespace" ]]; then echo "ERROR: Namespace not provided. Please run the script with the --namespace parameter." echo "Example:" echo " ./camunda-collect-diagnostics.sh --namespace camunda-platform" exit 1 fi # Check if namespace exists if ! kubectl get namespace "$namespace" > /dev/null 2>&1; then echo "ERROR: Namespace '$namespace' does not exist in the cluster." exit 1 fi # Set output directory with timestamp timestamp=$(date +"%Y%m%d-%H%M%S") output_dir="camunda-diagnostics-logs-$timestamp" # Start diagnostics collection echo "========================================" echo "Camunda Diagnostics Collection Script" echo "========================================" echo "Namespace: $namespace" echo "Output Directory: $output_dir" echo "Current kubectl context: $(kubectl config current-context)" echo "========================================" # Create output directory mkdir -p "$output_dir" && cd "$output_dir" # Collect general Kubernetes resources echo "Collecting resource information..." echo " - Collecting Kubernetes version." kubectl version -o yaml > kubernetes-version.txt echo " - Collecting pod information (current state of all pods in the namespace)." kubectl get pod -n "$namespace" -o wide > pods.txt echo " - Collecting cluster events (recent events in the namespace)." kubectl get events -n "$namespace" --sort-by='.lastTimestamp' > events.txt echo " - Collecting Persistent Volume Claims (PVCs) descriptions (storage claims in the namespace)." kubectl describe pvc -n "$namespace" > pvc-describe.txt echo " - Collecting Persistent Volumes (PVs) descriptions (underlying storage volumes)." for pvc in $(kubectl get pvc -n "$namespace" --no-headers -o custom-columns=":spec.volumeName"); do echo " - Collecting information for PV: $pvc" kubectl describe pv "$pvc" >> pv-describe.txt done echo " - Collecting service information (list of services in the namespace)." kubectl get svc -n "$namespace" > services.txt echo " - Collecting detailed service descriptions (configuration of services)." kubectl describe svc -n "$namespace" > services-describe.txt echo " - Collecting endpoint information (list of endpoints in the namespace)." kubectl get ep -n "$namespace" > endpoints.txt echo " - Collecting detailed endpoint descriptions (configuration of endpoints)." kubectl describe ep -n "$namespace" > endpoints-describe.txt echo " - Collecting ingress descriptions (configuration of ingress resources)." kubectl describe ing -n "$namespace" > ingresses-describe.txt echo " - Collecting config map information (configuration data stored in the namespace)." kubectl get cm -n "$namespace" -o yaml > configmaps.yaml echo " - Collecting node information:" for node in $(kubectl get pods -n "$namespace" -o custom-columns=":spec.nodeName" --no-headers | sort | uniq); do echo " - Collecting information for node: $node" kubectl describe node "$node" >> node-describe.txt echo "" >> node-describe.txt done echo " - Collecting logs and descriptions for each pod..." for pod in $(kubectl get pod -n "$namespace" --no-headers -o custom-columns=":metadata.name"); do echo " - Collecting logs for pod: $pod" kubectl logs -n "$namespace" "$pod" > "$pod.log" 2>/dev/null kubectl logs -n "$namespace" "$pod" -p > "${pod}-previous.log" 2>/dev/null kubectl describe pod -n "$namespace" "$pod" > "describe-$pod.log" 2>/dev/null done # Collect Helm resources echo " - Collecting information via Helm..." release_name=`helm list -n "$namespace" --no-headers -q` 2>/dev/null if [[ -z "$release_name" ]]; then echo " INFO: unable to detect Camunda release name, so Helm values.yaml will not be collected. Install \"helm\" command, make it available in the PATH and re-run the script. (Alternatively, upload \"values.yaml\" separately)" else helm version > helm-version.txt helm history -n "$namespace" "$release_name" > helm-history.txt helm get values -n "$namespace" "$release_name" > helm-values.yaml fi echo "All logs and descriptions collected." # Compress the output directory echo "Compressing collected diagnostics into ${output_dir}.zip..." cd .. zip -r "${output_dir}.zip" "$output_dir" > /dev/null echo "Diagnostics collected and compressed into ${output_dir}.zip." # Final message echo "========================================" echo "Diagnostics collection completed." echo "Please share the file '${output_dir}.zip' with the Camunda Support team." echo "" echo "To clean up the generated files and folder, run the following command:" echo " rm -rf $output_dir ${output_dir}.zip" echo "========================================" ``` --- ## Helm chart dual-region operational procedure ## Introduction This operational blueprint procedure is a step-by-step guide on how to restore operations in the case of a total region failure. It explains how to temporarily restore functionality in the surviving region and how to ultimately do a full recovery to restore the dual-region setup. The operational procedure builds on top of the [dual-region AWS setup guidance](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/dual-region.md), but is generally applicable for any dual-region setup. It has been also validated for the [OpenShift dual-region setup guidance](/self-managed/deployment/helm/cloud-providers/openshift/dual-region.md). Before proceeding with the operational procedure, thoroughly review and understand the contents of the [dual-region concept page](/self-managed/concepts/multi-region/dual-region.md). This page outlines various limitations and requirements pertinent to the procedure, which are crucial for successful execution. ## Disclaimer :::caution Running a dual-region configuration requires users to detect and manage any regional failures, and implement the operational procedure for failover and failback that matches their environment. ::: ## Prerequisites - A dual-region Camunda 8 setup installed in two different regions, preferably derived from our [AWS dual-region concept](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/dual-region.md) or [OpenShift dual-region concept](/self-managed/deployment/helm/cloud-providers/openshift/dual-region.md). - In that guide, we're showcasing Kubernetes dual-region installation, based on the following tools: - [Helm](https://helm.sh/docs/intro/install/) for installing and upgrading the [Camunda Helm chart](https://artifacthub.io/packages/helm/camunda/camunda-platform). - [Kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) to interact with the Kubernetes cluster. - `cURL` or similar to interact with the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). ## Terminology - **Surviving region** - A surviving region refers to a region within a dual-region setup that remains operational and unaffected by a failure or disaster that affects other regions. - **Lost region** - A lost region is a region within a dual-region setup that becomes unavailable or unusable due to a failure or disaster. - **Recreated region** - A recreated region is a region within a dual-region setup that was previously lost but has been restored or recreated to resume its operational state. - We assume this region does not contain Camunda 8 deployments or related persistent volumes. Ensure this is the case before executing the failover procedure. ## Procedure We use the same procedure to handle the loss of both active and passive regions. For clarity, this section focuses on the scenario where the passive region is lost while the active region remains operational. The same procedure will be valid in case of active region loss. **Temporary Loss Scenario:** If a region loss is temporary — such as from transient network issues — Zeebe can handle this situation without initiating recovery procedures, provided there is sufficient free space on the persistent disk. However, processing may halt due to a loss of quorum during this time. #### Key steps to handle passive region loss 1. **Traffic rerouting:** Use DNS to reroute traffic to the surviving active region. (Details on managing DNS rerouting depend on your specific DNS setup and are not covered in this guide.) 2. **Failover phase:** Temporarily restores Camunda 8 functionality by removing the lost brokers and handling the export to the unreachable Elasticsearch instance. 3. **Failback phase:** Fully restores the failed region to its original functionality. This phase requires the region to be ready for the redeployment of Camunda 8. :::caution For the failback procedure, the recreated region must not include any active Camunda 8 deployments or residual persistent volumes associated with Camunda 8 or its Elasticsearch instance. It is essential to initiate a clean deployment to prevent data replication and state conflicts. ::: :::info In the following examples, direct API calls are used because authentication methods may vary depending on your Admin configuration. The **Management API** (default port `9600`) is not secured by default. The **v2 REST API** (default port `8080`) **requires authentication**, described in the [API authentication guide](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md). ::: ### Prerequisites The following procedures assume the following dual-region deployment for: - **AWS:** the deployment has been created using [AWS setup guide](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/dual-region.md) and you have your own copy of the [camunda-deployment-references](https://github.com/camunda/camunda-deployment-references/tree/main/aws/kubernetes/eks-dual-region) repository and previously completed changes in the `camunda-values.yml` to adjust them in your setup. Follow the [dual-region cluster deployment](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/dual-region.md#3-deploy-camunda-8-via-helm-charts) guide to install Camunda 8, configure a dual-region setup, and have the general environment variables (see [environment prerequisites](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/dual-region.md#export-environment-variables) already set up). - **OpenShift:** the deployment has been created using [OpenShift setup guide](/self-managed/deployment/helm/cloud-providers/openshift/dual-region.md#deploying-camunda-8-via-helm-charts-in-a-dual-region-setup) and previously completed changes in your `generated-values-region-0.yml` and `generated-values-region-1.yml` to adjust them in your setup. :::note OpenShift cluster reference The OpenShift guide now uses the same 0-indexed naming convention as the AWS guide: `CLUSTER_0`/`CLUSTER_1` for cluster contexts and `CAMUNDA_NAMESPACE_0`/`CAMUNDA_NAMESPACE_1` for namespaces. No conversion is needed. In version 8.8 and earlier, the OpenShift guide used 1-indexed naming (`CLUSTER_1_NAME`/`CLUSTER_2_NAME` and `CAMUNDA_NAMESPACE_1`/`CAMUNDA_NAMESPACE_2`), which required converting variable names before following this procedure. ::: We will avoid referencing both scenarios of losing either region. Instead, we have generalized the commands and require a one-time setup to configure environment variables, enabling you to execute the procedure based on the surviving region and the one that needs to be recreated. Depending on which region you lost, select the correct tab below and export those environment variables to your terminal for a smoother procedure execution: ```bash export CLUSTER_SURVIVING=$CLUSTER_1 export CLUSTER_RECREATED=$CLUSTER_0 export CAMUNDA_NAMESPACE_SURVIVING=$CAMUNDA_NAMESPACE_1 export CAMUNDA_NAMESPACE_RECREATED=$CAMUNDA_NAMESPACE_0 export REGION_SURVIVING=region1 export REGION_RECREATED=region0 echo "You have lost $CLUSTER_RECREATED, $CLUSTER_SURVIVING is still alive" ``` ```bash export CLUSTER_SURVIVING=$CLUSTER_0 export CLUSTER_RECREATED=$CLUSTER_1 export CAMUNDA_NAMESPACE_SURVIVING=$CAMUNDA_NAMESPACE_0 export CAMUNDA_NAMESPACE_RECREATED=$CAMUNDA_NAMESPACE_1 export REGION_SURVIVING=region0 export REGION_RECREATED=region1 echo "You have lost $CLUSTER_RECREATED, $CLUSTER_SURVIVING is still alive" ``` The `camunda-zeebe-x` pod represents the new architecture that contains the Orchestration Cluster and its components. It includes the former Zeebe Gateway, Operate, Tasklist, the new embedded Admin, and the new Camunda Exporter. ### Failover phase The Failover phase outlines steps for removing lost brokers, redistributing load, disabling Elasticsearch export to a failed region, and restoring user interaction with Camunda 8 to ensure smooth recovery and continued functionality. #### Remove lost brokers from Zeebe cluster in the surviving region | **Current state** | **Desired state** | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | You have ensured that you fully lost a region and want to start the temporary recovery. One of the regions is lost, meaning Zeebe: - No data has been lost thanks to Zeebe data replication. - Is unable to process new requests due to losing the quorum - Stops exporting new data to Elasticsearch in the lost region - Stops exporting new data to Elasticsearch in the survived region | The lost brokers have been removed from the Zeebe cluster. Continued processing is enabled, and new brokers in the failback procedure will only join the cluster with our intervention. | #### Procedure Start with creating a port-forward to the `Zeebe Gateway` in the surviving region to the local host so you can interact with the Zeebe Gateway. The following alternatives to port-forwarding are possible: - If the Zeebe Gateway is exposed to the outside of the Kubernetes cluster, you can skip port-forwarding and use the URL directly - [`exec`](https://kubernetes.io/docs/reference/kubectl/generated/kubectl_exec/) into an existing pod (such as Elasticsearch), and execute `curl` commands from inside of the pod - [`run`](https://kubernetes.io/docs/reference/kubectl/generated/kubectl_run/) an Ubuntu pod in the cluster to execute `curl` commands from inside the Kubernetes cluster In our example, we went with port-forwarding to a localhost, but other alternatives can also be used. 1. Use the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) to retrieve the list of the remaining brokers ```bash kubectl --context $CLUSTER_SURVIVING port-forward services/$CAMUNDA_RELEASE_NAME-zeebe-gateway 8080:8080 -n $CAMUNDA_NAMESPACE_SURVIVING curl -L -X GET 'http://localhost:8080/v2/topology' \ -H 'Accept: application/json' ```
Example output ```bash { "brokers": [ { "nodeId": 0, "host": "camunda-zeebe-0.camunda-zeebe.camunda-london", "port": 26501, "partitions": [ { "partitionId": 1, "role": "leader", "health": "healthy" }, { "partitionId": 6, "role": "follower", "health": "healthy" }, { "partitionId": 7, "role": "follower", "health": "healthy" }, { "partitionId": 8, "role": "follower", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 2, "host": "camunda-zeebe-1.camunda-zeebe.camunda-london", "port": 26501, "partitions": [ { "partitionId": 1, "role": "follower", "health": "healthy" }, { "partitionId": 2, "role": "follower", "health": "healthy" }, { "partitionId": 3, "role": "follower", "health": "healthy" }, { "partitionId": 8, "role": "leader", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 4, "host": "camunda-zeebe-2.camunda-zeebe.camunda-london", "port": 26501, "partitions": [ { "partitionId": 2, "role": "follower", "health": "healthy" }, { "partitionId": 3, "role": "leader", "health": "healthy" }, { "partitionId": 4, "role": "follower", "health": "healthy" }, { "partitionId": 5, "role": "follower", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 6, "host": "camunda-zeebe-3.camunda-zeebe.camunda-london", "port": 26501, "partitions": [ { "partitionId": 4, "role": "follower", "health": "healthy" }, { "partitionId": 5, "role": "follower", "health": "healthy" }, { "partitionId": 6, "role": "follower", "health": "healthy" }, { "partitionId": 7, "role": "leader", "health": "healthy" } ], "version": "8.8.0" } ], "clusterSize": 8, "partitionsCount": 8, "replicationFactor": 4, "gatewayVersion": "8.8.0" } ```
2. Port-forward the Zeebe Gateway service to access the [Management REST API](/self-managed/components/orchestration-cluster/zeebe/configuration/gateway.md#managementserver): ```bash kubectl --context $CLUSTER_SURVIVING port-forward services/$CAMUNDA_RELEASE_NAME-zeebe-gateway 9600:9600 -n $CAMUNDA_NAMESPACE_SURVIVING ``` 3. Based on the [Cluster Scaling APIs](/self-managed/components/orchestration-cluster/zeebe/operations/cluster-scaling.md), send a request to the Zeebe Gateway to redistribute load to the remaining brokers and remove the lost ones. Depending on which region was lost, you must redistribute to either the even- or odd-numbered brokers. In this example, `region 1` was lost, along with the odd-numbered brokers. Therefore, the load is redistributed to the even-numbered brokers. Run the appropriate command for the surviving region to remove the lost brokers and trigger redistribution. Removing the lost (odd-numbered) brokers will automatically redistribute partitions to the remaining (even-numbered) brokers. ```bash curl -XPATCH 'http://localhost:9600/actuator/cluster?force=true' \ -H 'Content-Type: application/json' \ -d '{ "brokers": { "remove": [1,3,5,7] } }' ``` ```bash curl -XPATCH 'http://localhost:9600/actuator/cluster?force=true' \ -H 'Content-Type: application/json' \ -d '{ "brokers": { "remove": [0,2,4,6] } }' ``` Using the `force=true` parameter reduces the replication factor accordingly. #### Verification Port-forwarding the Zeebe Gateway via `kubectl` and printing the topology should reveal that the cluster size has decreased to 4, partitions have been redistributed over the remaining brokers, and new leaders have been elected. ```bash kubectl --context $CLUSTER_SURVIVING port-forward services/$CAMUNDA_RELEASE_NAME-zeebe-gateway 8080:8080 -n $CAMUNDA_NAMESPACE_SURVIVING curl -L -X GET 'http://localhost:8080/v2/topology' \ -H 'Accept: application/json' ```
Example output ```bash { "brokers": [ { "nodeId": 0, "host": "camunda-zeebe-0.camunda-zeebe.camunda-london", "port": 26501, "partitions": [ { "partitionId": 1, "role": "leader", "health": "healthy" }, { "partitionId": 6, "role": "leader", "health": "healthy" }, { "partitionId": 7, "role": "follower", "health": "healthy" }, { "partitionId": 8, "role": "follower", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 2, "host": "camunda-zeebe-1.camunda-zeebe.camunda-london", "port": 26501, "partitions": [ { "partitionId": 1, "role": "follower", "health": "healthy" }, { "partitionId": 2, "role": "leader", "health": "healthy" }, { "partitionId": 3, "role": "follower", "health": "healthy" }, { "partitionId": 8, "role": "leader", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 4, "host": "camunda-zeebe-2.camunda-zeebe.camunda-london", "port": 26501, "partitions": [ { "partitionId": 2, "role": "follower", "health": "healthy" }, { "partitionId": 3, "role": "leader", "health": "healthy" }, { "partitionId": 4, "role": "follower", "health": "healthy" }, { "partitionId": 5, "role": "follower", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 6, "host": "camunda-zeebe-3.camunda-zeebe.camunda-london", "port": 26501, "partitions": [ { "partitionId": 4, "role": "leader", "health": "healthy" }, { "partitionId": 5, "role": "leader", "health": "healthy" }, { "partitionId": 6, "role": "follower", "health": "healthy" }, { "partitionId": 7, "role": "leader", "health": "healthy" } ], "version": "8.8.0" } ], "clusterSize": 4, "partitionsCount": 8, "replicationFactor": 2, "gatewayVersion": "8.8.0" } ```
You can also use the Zeebe Gateway's REST API to ensure the scaling progress has been completed. For better output readability, we use [jq](https://jqlang.github.io/jq/). ```bash kubectl --context $CLUSTER_SURVIVING port-forward services/$CAMUNDA_RELEASE_NAME-zeebe-gateway 9600:9600 -n $CAMUNDA_NAMESPACE_SURVIVING curl -XGET 'http://localhost:9600/actuator/cluster' | jq .lastChange ```
Example output ```bash { "id": 2, "status": "COMPLETED", "startedAt": "2024-08-23T11:33:08.355681311Z", "completedAt": "2024-08-23T11:33:09.170531963Z" } ```
#### Configure Zeebe to disable the Elastic exporter to the lost region | **Details** | **Current state** | **Desired state** | | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | | **Zeebe configuration** | Zeebe brokers in the surviving region are still configured to point to the Elasticsearch instance of the lost region. Zeebe cannot continue exporting data. | Elasticsearch exporter to the failed region has been disabled in the Zeebe cluster. Zeebe can export data to Elasticsearch again. | | **User interaction** | Regular interaction with Camunda 8 is not restored. | Regular interaction with Camunda 8 is restored, marking the conclusion of the temporary recovery. | :::info If you have upgraded from a previously migrated 8.7 system, you may still have the legacy `elasticsearchregion0` and `elasticsearchregion1` exporters configured. - If **both exporters are disabled**, you can safely ignore them. - If they are **enabled** (for example, because you’re using **Optimize**), apply the same logic to these exporters as described for the new ones in this guide. ::: #### Procedure 1. Port-forward the service of the Zeebe Gateway for the [management REST API](/self-managed/components/orchestration-cluster/zeebe/configuration/gateway.md#managementserver) ```bash kubectl --context $CLUSTER_SURVIVING port-forward services/$CAMUNDA_RELEASE_NAME-zeebe-gateway 9600:9600 -n $CAMUNDA_NAMESPACE_SURVIVING ``` 2. List all exporters to find the corresponding ID. Alternatively, you can check your Helm chart `camunda-values.yml` file, which lists the exporters as those that had to be configured explicitly. ```bash curl -XGET 'http://localhost:9600/actuator/exporters' ```
Example output ```bash [{"exporterId":"camundaregion0","status":"ENABLED"},{"exporterId":"camundaregion1","status":"ENABLED"}] ```
3. Based on the Exporter APIs you will send a request to the Zeebe Gateway to disable the Elasticsearch exporter connected with the lost region. ```bash curl -XPOST 'http://localhost:9600/actuator/exporters/camundaregion1/disable' ``` #### Verification Port-forwarding the Zeebe Gateway via `kubectl` for the REST API and listing all exporters will reveal their current status. ```bash kubectl --context $CLUSTER_SURVIVING port-forward services/$CAMUNDA_RELEASE_NAME-zeebe-gateway 9600:9600 -n $CAMUNDA_NAMESPACE_SURVIVING curl -XGET 'http://localhost:9600/actuator/exporters' ```
Example output ```bash [{"exporterId":"camundaregion0","status":"ENABLED"},{"exporterId":"camundaregion1","status":"DISABLED"}] ```
Via the already port-forwarded Zeebe Gateway, you can also check the status of the change by using the Cluster API. ```bash curl -XGET 'http://localhost:9600/actuator/cluster' | jq .lastChange ```
Example output ```bash { "id": 4, "status": "COMPLETED", "startedAt": "2024-08-23T11:36:14.127510679Z", "completedAt": "2024-08-23T11:36:14.379980715Z" } ```
### Failback phase #### Deploy Camunda 8 in the newly created region | **Details** | **Current state** | **Desired state** | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Camunda 8** | A standalone region with a fully functional Camunda 8 setup, including the Orchestration Cluster (Zeebe, Operate, Tasklist, Zeebe Gateway) and Elasticsearch. | Restore dual-region functionality by deploying Camunda 8 isolated to the Orchestration Cluster (Zeebe and Zeebe Gateway) and Elasticsearch in the newly restored region. Disable the standalone Schema Manager to prevent seeding Elasticsearch. | | **Operate and Tasklist** | Operate and Tasklist are operational in the standalone region. | Keep Operate and Tasklist disabled in the restored region to avoid interference during the database backup and restore process. They will also be disabled in the following steps for the surviving region. | #### Procedure This step involves redeploying the recreated region using the same values files from the initial deployment. The Helm command also disables Operate and Tasklist. These components will be re-enabled only after region recovery is complete. Keeping them disabled in the newly created region helps prevent data loss, as Operate and Tasklist may still rely on v1 APIs and functionality that are isolated to a single region. Disabling them also prevents user confusion, since no visible updates will appear for their actions while the exporters remain disabled in the following steps. This procedure requires your Helm values file, `camunda-values.yml`, in `aws/kubernetes/eks-dual-region/helm-values`, used to deploy EKS Dual-region Camunda clusters. Ensure the values for `CAMUNDA_DATA_EXPORTERS_CAMUNDAREGION0_ARGS_CONNECT_URL` and `CAMUNDA_DATA_EXPORTERS_CAMUNDAREGION1_ARGS_CONNECT_URL` correctly point to their respective regions. The placeholder in `CAMUNDA_CLUSTER_INITIALCONTACTPOINTS` should contain the Zeebe endpoints for both regions, the result of the `aws/kubernetes/eks-dual-region/procedure/generate_zeebe_helm_values.sh`. This step is equivalent to applying for the region to be recreated: - [Setting up the Camunda 8 Dual-Region Helm chart](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/dual-region.md#camunda-8-helm-chart-prerequisites) - [Deploying the Camunda 8 Dual-Region Helm chart](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/dual-region.md#install-camunda-8-using-helm) :::important The standalone Schema Manager must be disabled; otherwise, it will prevent a successful restore of the Elasticsearch backup later on. If you forget to disable it, you must manually remove all created indices in Elasticsearch in the restored region before restoring the backup. ::: There is no Helm chart option for this setting. Because `orchestration.env` is an array, it cannot be overwritten through an overlay and must be added manually on a temporary basis. Edit `camunda-values.yml` in `aws/kubernetes/eks-dual-region/helm-values` to include the following under `orchestration.env`: ```yaml orchestration: env: - name: CAMUNDA_DATABASE_SCHEMAMANAGER_CREATESCHEMA value: "false" # ... ``` ##### Deploy prerequisite services Before installing Camunda via Helm, you must redeploy the ECK-managed Elasticsearch cluster and synchronize cross-region passwords. Without this, the Helm install will fail because there is no Elasticsearch to connect to. 1. From `generic/kubernetes/operator-based/elasticsearch`, deploy the ECK operator and Elasticsearch cluster in the recreated region: ```shell cd generic/kubernetes/operator-based/elasticsearch export ELASTICSEARCH_CLUSTER_FILE="elasticsearch-cluster-dual-region.yml" KUBE_CONTEXT=$CLUSTER_RECREATED ./deploy.sh cd - ``` 2. Wait for Elasticsearch to become ready. Verify the cluster health is `green` before proceeding: ```shell kubectl get elasticsearch --context $CLUSTER_RECREATED --namespace $CAMUNDA_NAMESPACE_RECREATED ``` 3. From `aws/kubernetes/eks-dual-region/procedure`, resynchronize the Elasticsearch passwords across regions: ```shell cd aws/kubernetes/eks-dual-region/procedure ./sync_elasticsearch_passwords.sh cd - ``` This recreates the cross-region password secrets (`elasticsearch-es-password-region-0` and `elasticsearch-es-password-region-1`) required by the Zeebe exporter configuration. For more details on these steps, see [Deploy the ECK operator and Elasticsearch clusters](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/dual-region.md#deploy-the-eck-operator-and-elasticsearch-clusters) and [Synchronize Elasticsearch passwords across regions](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/dual-region.md#synchronize-elasticsearch-passwords-across-regions). ##### Install Camunda using Helm From the terminal context of `aws/kubernetes/eks-dual-region/helm-values` execute: ```bash helm install $CAMUNDA_RELEASE_NAME camunda/camunda-platform \ --version $HELM_CHART_VERSION \ --kube-context $CLUSTER_RECREATED \ --namespace $CAMUNDA_NAMESPACE_RECREATED \ -f camunda-values.yml \ -f ../../../../generic/kubernetes/operator-based/elasticsearch/camunda-elastic-values.yml \ -f $REGION_RECREATED/camunda-values.yml \ --set orchestration.profiles.operate=false \ --set orchestration.profiles.tasklist=false ``` After successfully applying the recreated region, remove the temporary `CAMUNDA_DATABASE_SCHEMAMANAGER_CREATESCHEMA` environment variable. ##### Deploy prerequisite services Before installing Camunda via Helm, you must redeploy the ECK-managed Elasticsearch cluster and synchronize cross-region passwords. Without this, the Helm install will fail because there is no Elasticsearch to connect to. 1. From the `generic/kubernetes/operator-based/elasticsearch` folder, deploy the ECK operator and Elasticsearch cluster in the recreated region: ```bash cd generic/kubernetes/operator-based/elasticsearch export ELASTICSEARCH_CLUSTER_FILE="elasticsearch-cluster-dual-region.yml" CAMUNDA_NAMESPACE=$CAMUNDA_NAMESPACE_RECREATED KUBE_CONTEXT=$CLUSTER_RECREATED ./deploy.sh cd - ``` 2. Wait for Elasticsearch to become ready. Verify the cluster health is `green` before proceeding: ```bash kubectl get elasticsearch --context $CLUSTER_RECREATED --namespace $CAMUNDA_NAMESPACE_RECREATED ``` 3. From the `generic/openshift/dual-region/procedure` folder, re-synchronize the Elasticsearch passwords across regions: ```bash cd generic/openshift/dual-region/procedure ./sync-elasticsearch-passwords.sh cd - ``` This recreates the cross-region password secrets (`elasticsearch-es-password-region-0` and `elasticsearch-es-password-region-1`) required by the Zeebe exporter configuration. For more details on these steps, see [Deploy the ECK operator and Elasticsearch clusters](/self-managed/deployment/helm/cloud-providers/openshift/dual-region.md#deploy-the-eck-operator-and-elasticsearch-clusters) and [Synchronize Elasticsearch passwords across regions](/self-managed/deployment/helm/cloud-providers/openshift/dual-region.md#synchronize-elasticsearch-passwords-across-regions). ##### Install Camunda using Helm Follow the installation steps for the **recreated region**: - [Setting up the Camunda 8 Dual-Region Helm chart](/self-managed/deployment/helm/cloud-providers/openshift/dual-region.md#configure-your-deployment-for-each-region) _Optional if you already have your pre-configured `generated-values-file.yml`_ - Once your values file is generated from the installation step, install **Camunda 8 only in the recreated region**. Adjust the installation command to disable Operate and Tasklist: ```bash --set orchestration.profiles.operate=false \ --set orchestration.profiles.tasklist=false ``` :::important The standalone Schema Manager must be disabled; otherwise, it will prevent a successful restore of the Elasticsearch backup later on. If you forget to disable it, you must manually remove all created indices in Elasticsearch in the restored region before restoring the backup. ::: There is no Helm chart option for this setting. Because `orchestration.env` is an array, it cannot be overwritten through an overlay and must be added manually on a temporary basis. Edit the `generated-values-region-0|1.yaml` to include the following under `orchestration.env`: ```yaml orchestration: env: - name: CAMUNDA_DATABASE_SCHEMAMANAGER_CREATESCHEMA value: "false" # ... ``` Example command adapted from the installation step: ```bash helm upgrade --install \ "$CAMUNDA_RELEASE_NAME" camunda/camunda-platform \ --version "$HELM_CHART_VERSION" \ --kube-context "$CLUSTER_RECREATED" \ --namespace "$CAMUNDA_NAMESPACE_RECREATED" \ -f "" \ --set orchestration.profiles.operate=false \ --set orchestration.profiles.tasklist=false ``` After successfully applying the recreated region, remove the temporary `CAMUNDA_DATABASE_SCHEMAMANAGER_CREATESCHEMA` environment variable again. - [Follow the installation step for the **recreated region only**](/self-managed/deployment/helm/cloud-providers/openshift/dual-region.md#install-camunda-8-using-helm). #### Verification The following command will show the pods deployed in the newly created region. ```bash kubectl --context $CLUSTER_RECREATED get pods -n $CAMUNDA_NAMESPACE_RECREATED ``` Half of the amount of your set `clusterSize` is used to spawn Zeebe brokers. For example, in the case of `clusterSize: 8`, four Zeebe brokers are provisioned in the newly created region. :::danger It is expected that the Zeebe Broker pods will not reach the "Ready" state since they are not yet part of a Zeebe cluster and, therefore, not considered healthy by the readiness probe. ::: Port-forwarding the Zeebe Gateway via `kubectl` and printing the topology should reveal that the new Zeebe brokers are recognized but yet a full member of the Zeebe cluster. ```bash kubectl --context $CLUSTER_SURVIVING port-forward services/$CAMUNDA_RELEASE_NAME-zeebe-gateway 8080:8080 -n $CAMUNDA_NAMESPACE_SURVIVING curl -L -X GET 'http://localhost:8080/v2/topology' \ -H 'Accept: application/json' ```
Example output ```bash { "brokers": [ { "nodeId": 0, "host": "camunda-zeebe-0.camunda-zeebe.camunda-london", "port": 26501, "partitions": [ { "partitionId": 1, "role": "leader", "health": "healthy" }, { "partitionId": 6, "role": "leader", "health": "healthy" }, { "partitionId": 7, "role": "follower", "health": "healthy" }, { "partitionId": 8, "role": "follower", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 1, "host": "camunda-zeebe-0.camunda-zeebe.camunda-paris", "port": 26501, "partitions": [], "version": "8.8.0" }, { "nodeId": 2, "host": "camunda-zeebe-1.camunda-zeebe.camunda-london", "port": 26501, "partitions": [ { "partitionId": 1, "role": "follower", "health": "healthy" }, { "partitionId": 2, "role": "leader", "health": "healthy" }, { "partitionId": 3, "role": "follower", "health": "healthy" }, { "partitionId": 8, "role": "leader", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 3, "host": "camunda-zeebe-1.camunda-zeebe.camunda-paris", "port": 26501, "partitions": [], "version": "8.8.0" }, { "nodeId": 4, "host": "camunda-zeebe-2.camunda-zeebe.camunda-london", "port": 26501, "partitions": [ { "partitionId": 2, "role": "follower", "health": "healthy" }, { "partitionId": 3, "role": "leader", "health": "healthy" }, { "partitionId": 4, "role": "follower", "health": "healthy" }, { "partitionId": 5, "role": "follower", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 5, "host": "camunda-zeebe-2.camunda-zeebe.camunda-paris", "port": 26501, "partitions": [], "version": "8.8.0" }, { "nodeId": 6, "host": "camunda-zeebe-3.camunda-zeebe.camunda-london", "port": 26501, "partitions": [ { "partitionId": 4, "role": "leader", "health": "healthy" }, { "partitionId": 5, "role": "leader", "health": "healthy" }, { "partitionId": 6, "role": "follower", "health": "healthy" }, { "partitionId": 7, "role": "leader", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 7, "host": "camunda-zeebe-3.camunda-zeebe.camunda-paris", "port": 26501, "partitions": [], "version": "8.8.0" }, ], "clusterSize": 4, "partitionsCount": 8, "replicationFactor": 2, "gatewayVersion": "8.8.0" } ```
#### Deactivate Operate and Tasklist in the active region | **Details** | **Current State** | **Desired State** | | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | | **Camunda 8** | The recovered region has been deployed with Operate and Tasklist disabled. Users can still access Operate and Tasklist through the surviving region. | Operate and Tasklist are turned off in the surviving region to avoid data loss during the backup procedure. | #### How to get there With the Orchestration Cluster, Operate and Tasklist are consolidated with the Zeebe Broker and Gateway into a single application. Similar to `Step 1`, this step redeploys the active region using the same value files from the initial deployment. Additionally, the Helm command disables Operate and Tasklist. These components will only be enabled at the end of the full region recovery again. This step reduces the deployed application to the Zeebe Cluster and Elasticsearch only. This procedure requires your Helm values file, `camunda-values.yml`, in `aws/kubernetes/eks-dual-region/helm-values`, used to deploy EKS dual-region Camunda clusters. Ensure that the values for `CAMUNDA_DATA_EXPORTERS_CAMUNDAREGION0_ARGS_CONNECT_URL` and `CAMUNDA_DATA_EXPORTERS_CAMUNDAREGION1_ARGS_CONNECT_URL` correctly point to their respective regions. The placeholder in `CAMUNDA_CLUSTER_INITIALCONTACTPOINTS` should contain the Zeebe endpoints for both regions, generated by the `aws/kubernetes/eks-dual-region/procedure/generate_zeebe_helm_values.sh` script. This step is equivalent to applying the configuration for the recreated region: - [Setting up the Camunda 8 Dual-Region Helm chart](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/dual-region.md#camunda-8-helm-chart-prerequisites) - [Deploying the Camunda 8 Dual-Region Helm chart](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/dual-region.md#install-camunda-8-using-helm) If not already done, remove the `CAMUNDA_DATABASE_SCHEMAMANAGER_CREATESCHEMA` variable from the `camunda-values.yml`. Edit the `camunda-values.yml` in `aws/kubernetes/eks-dual-region/helm-values` and remove the following from `orchestration.env`: ```yaml orchestration: env: - name: CAMUNDA_DATABASE_SCHEMAMANAGER_CREATESCHEMA value: "false" # ... ``` From the `aws/kubernetes/eks-dual-region/helm-values` directory, run: ```bash helm upgrade --install $CAMUNDA_RELEASE_NAME camunda/camunda-platform \ --version $HELM_CHART_VERSION \ --kube-context $CLUSTER_SURVIVING \ --namespace $CAMUNDA_NAMESPACE_SURVIVING \ -f camunda-values.yml \ -f ../../../../generic/kubernetes/operator-based/elasticsearch/camunda-elastic-values.yml \ -f $REGION_SURVIVING/camunda-values.yml \ --set orchestration.profiles.operate=false \ --set orchestration.profiles.tasklist=false ``` Follow the installation steps for the **surviving region**: - [Set up the Camunda 8 Dual-Region Helm chart](/self-managed/deployment/helm/cloud-providers/openshift/dual-region.md#configure-your-deployment-for-each-region) (optional if you already have your pre-configured `generated-values-file.yml`) - Once your values file is generated from the installation step, upgrade **Camunda 8 only in the surviving region**. Adjust the installation command to disable Operate and Tasklist: ```bash --set orchestration.profiles.operate=false \ --set orchestration.profiles.tasklist=false ``` Example command adapted from the installation step: ```bash helm upgrade --install \ "$CAMUNDA_RELEASE_NAME" camunda/camunda-platform \ --version "$HELM_CHART_VERSION" \ --kube-context "$CLUSTER_SURVIVING" \ --namespace "$CAMUNDA_NAMESPACE_SURVIVING" \ -f "" \ --set orchestration.profiles.operate=false \ --set orchestration.profiles.tasklist=false ``` - [Follow the installation step for the **surviving region only**](/self-managed/deployment/helm/cloud-providers/openshift/dual-region.md#install-camunda-8-using-helm). #### Verification 1. If the environment is exposed through an Ingress, verify that Operate and Tasklist are no longer accessible via the Ingress. 2. Check the logs of any `camunda-zeebe-X` pod to confirm that only a subset of profiles are active: ```bash kubectl --context $CLUSTER_SURVIVING logs camunda-zeebe-0 | grep "profiles are active" ``` ```bash # The default are 5 profiles, so this confirms that Operate and Tasklist are not enabled io.camunda.application.StandaloneCamunda - The following 3 profiles are active: "broker", "admin", "consolidated-auth" ``` 3. Alternatively, verify that the configuration does not list Operate and Tasklist as active profiles: ```bash kubectl --context $CLUSTER_SURVIVING get cm camunda-zeebe-configuration-unified -oyaml | grep spring -A2 ``` ```bash spring: profiles: active: "broker,admin,consolidated-auth" ``` #### Pause Camunda exporters to Elasticsearch | **Details** | **Current state** | **Desired state** | | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Camunda 8** | The Orchestration Cluster is operating in a single region: Isolated to a [Zeebe cluster](/reference/glossary.md#zeebe-cluster) in the surviving region.Non-participating [Zeebe Brokers](/reference/glossary.md#zeebe-broker) in the recreated region.Data is currently being exported to Elasticsearch from the surviving region. | Preparing the newly created region to take over and restore the dual-region setup. Stop Camunda exporters to prevent new data from being exported to Elasticsearch, allowing an Elasticsearch backup to be created. | :::note This step **does not** affect process instances. Process information may not be visible in Operate and Tasklist running in the affected instance. ::: #### Procedure 1. Disable the Camunda Exporter exporters in Zeebe using kubectl and the [exporting API](/self-managed/components/orchestration-cluster/zeebe/operations/management-api.md#exporting-api): ```bash kubectl --context $CLUSTER_SURVIVING port-forward services/$CAMUNDA_RELEASE_NAME-zeebe-gateway 9600:9600 -n $CAMUNDA_NAMESPACE_SURVIVING curl -i localhost:9600/actuator/exporting/pause -XPOST # The successful response should be: # HTTP/1.1 204 No Content ``` #### Verification There is no API available to confirm the status of the Camunda exporters. A response code of `204` indicates that the disabling was successful. This is a synchronous operation. #### Create and restore Elasticsearch backup | **Details** | **Current State** | **Desired State** | | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Camunda 8**_(Zeebe Cluster)_ | Reachable by end-users but not processing any new process instances nor reflecting User changes. This state allows for data backup without loss. | Remain not processing any new instances nor processing user inputs. | | **Elasticsearch Backup** | No backup is in progress. | Backup of Elasticsearch in the surviving region is initiated and being restored in the recreated region, containing all necessary data. The backup process may take time to complete. | #### How to get there This builds on top of the [AWS setup](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/dual-region.md) and assumes the S3 bucket was automatically created as part of the Terraform execution. :::info The procedure works for other Cloud providers and bare metal. You have to adjust the AWS S3-specific part depending on your chosen backup source for Elasticsearch. Consult the [Elasticsearch documentation on snapshot and restore](https://www.elastic.co/guide/en/elasticsearch/reference/current/snapshot-restore.html) to learn more about this, and specifically the [different supported types](https://www.elastic.co/guide/en/elasticsearch/reference/current/snapshots-register-repository.html#ess-repo-types) by Elasticsearch. ::: 1. Determine the S3 bucket Retrieve the name of the bucket via Terraform. Go to `aws/kubernetes/eks-dual-region/terraform` within the repository and retrieve the bucket name from the Terraform state: ```bash export S3_BUCKET_NAME=$(terraform output -raw s3_bucket_name) ``` Retrieve the name of the bucket from the [verify the pre-requisites step of OpenShift Dual-region](/self-managed/deployment/helm/cloud-providers/openshift/dual-region.md#verify-the-pre-requisites) step, it should be referenced as the `AWS_ES_BUCKET_NAME` variable. Export it: ```bash export S3_BUCKET_NAME="$AWS_ES_BUCKET_NAME" ``` 2. Retrieve the Elasticsearch password, and configure the backup endpoint in the surviving namespace `CAMUNDA_NAMESPACE_SURVIVING`: ```bash ELASTIC_POD=$(kubectl --context $CLUSTER_SURVIVING get pod --selector=elasticsearch.k8s.elastic.co/cluster-name=elasticsearch -o jsonpath='{.items[0].metadata.name}' -n $CAMUNDA_NAMESPACE_SURVIVING) ES_PASSWORD=$(kubectl --context $CLUSTER_SURVIVING get secret elasticsearch-es-elastic-user -n $CAMUNDA_NAMESPACE_SURVIVING -o jsonpath='{.data.elastic}' | base64 -d) kubectl --context $CLUSTER_SURVIVING exec -n $CAMUNDA_NAMESPACE_SURVIVING -it $ELASTIC_POD -c elasticsearch -- \ curl -u "elastic:$ES_PASSWORD" -XPUT 'http://localhost:9200/_snapshot/camunda_backup' \ -H 'Content-Type: application/json' \ -d' { "type": "s3", "settings": { "bucket": "'$S3_BUCKET_NAME'", "client": "camunda", "base_path": "backups" } } ' ``` 3. Create an Elasticsearch backup in the surviving namespace `CAMUNDA_NAMESPACE_SURVIVING`. Depending on the amount of data, this operation will take a while to complete. It also explicitly includes the global state, which is required during restore because it contains the Camunda index templates. ```bash # The backup will be called failback kubectl --context $CLUSTER_SURVIVING exec -n $CAMUNDA_NAMESPACE_SURVIVING -it $ELASTIC_POD -c elasticsearch -- \ curl -u "elastic:$ES_PASSWORD" -XPUT 'http://localhost:9200/_snapshot/camunda_backup/failback?wait_for_completion=true' \ -H 'Content-Type: application/json' \ -d '{"include_global_state": true}' ``` 4. Verify the backup has been completed successfully by checking all backups and ensuring the `state` is `SUCCESS`: ```bash kubectl --context $CLUSTER_SURVIVING exec -n $CAMUNDA_NAMESPACE_SURVIVING -it $ELASTIC_POD -c elasticsearch -- curl -u "elastic:$ES_PASSWORD" -XGET 'http://localhost:9200/_snapshot/camunda_backup/_all' ```
Example output ```json { "snapshots": [ { "snapshot": "failback", "uuid": "1S_C05K0RjqFyWMfjSKI_A", "repository": "camunda_backup", "version_id": 8525000, "version": "8.18.0", "indices": [ "camunda-usage-metric-tu-8.8.0_", "tasklist-metric-8.3.0_", "camunda-mapping-rule-8.8.0_", "operate-job-8.6.0_", "camunda-user-8.8.0_", "camunda-usage-metric-8.8.0_", "operate-import-position-8.3.0_", "camunda-correlated-message-subscription-8.8.0_", "operate-variable-8.3.0_", "operate-message-8.5.0_", "operate-decision-requirements-8.3.0_", "operate-process-8.3.0_", "camunda-authorization-8.8.0_", "operate-decision-instance-8.3.0_", "operate-list-view-8.3.0_", "camunda-group-8.8.0_", "operate-batch-operation-1.0.0_", "tasklist-form-8.4.0_", "tasklist-task-variable-8.3.0_", "operate-metric-8.3.0_", "camunda-web-session-8.8.0_", "operate-sequence-flow-8.3.0_", "tasklist-task-8.8.0_", "tasklist-draft-task-variable-8.3.0_", "operate-flownode-instance-8.3.1_", "operate-event-8.3.0_", "tasklist-import-position-8.2.0_", "operate-decision-8.3.0_", "operate-incident-8.3.1_", "operate-operation-8.4.1_", "camunda-role-8.8.0_", "operate-post-importer-queue-8.3.0_", "camunda-tenant-8.8.0_" ], "data_streams": [], "include_global_state": true, "state": "SUCCESS", "start_time": "2025-09-29T11:38:18.285Z", "start_time_in_millis": 1759145898285, "end_time": "2025-09-29T11:38:19.292Z", "end_time_in_millis": 1759145899292, "duration_in_millis": 1007, "failures": [], "shards": { "total": 33, "failed": 0, "successful": 33 }, "feature_states": [] } ], "total": 1, "remaining": 0 } ```
5. Configure Elasticsearch backup endpoint in the new region namespace `CAMUNDA_NAMESPACE_RECREATED`. It's essential to only do this step now as otherwise it won't see the backup: ```bash ELASTIC_POD=$(kubectl --context $CLUSTER_RECREATED get pod --selector=elasticsearch.k8s.elastic.co/cluster-name=elasticsearch -o jsonpath='{.items[0].metadata.name}' -n $CAMUNDA_NAMESPACE_RECREATED) ES_PASSWORD=$(kubectl --context $CLUSTER_RECREATED get secret elasticsearch-es-elastic-user -n $CAMUNDA_NAMESPACE_RECREATED -o jsonpath='{.data.elastic}' | base64 -d) kubectl --context $CLUSTER_RECREATED exec -n $CAMUNDA_NAMESPACE_RECREATED -it $ELASTIC_POD -c elasticsearch -- curl -u "elastic:$ES_PASSWORD" -XPUT 'http://localhost:9200/_snapshot/camunda_backup' -H 'Content-Type: application/json' -d' { "type": "s3", "settings": { "bucket": "'$S3_BUCKET_NAME'", "client": "camunda", "base_path": "backups" } } ' ``` 6. Verify that the backup can be found in the shared S3 bucket: ```bash kubectl --context $CLUSTER_RECREATED exec -n $CAMUNDA_NAMESPACE_RECREATED -it $ELASTIC_POD -c elasticsearch -- curl -u "elastic:$ES_PASSWORD" -XGET 'http://localhost:9200/_snapshot/camunda_backup/_all' ``` The example output above should be the same since it's the same backup. 7. Restore Elasticsearch backup in the new region namespace `CAMUNDA_NAMESPACE_RECREATED`. Depending on the amount of data, this operation may take a while to complete. ```bash kubectl --context $CLUSTER_RECREATED exec -n $CAMUNDA_NAMESPACE_RECREATED -it $ELASTIC_POD -c elasticsearch -- \ curl -u "elastic:$ES_PASSWORD" -XPOST 'http://localhost:9200/_snapshot/camunda_backup/failback/_restore?wait_for_completion=true' \ -H 'Content-Type: application/json' \ -d '{"include_global_state": true}' ``` 8. Verify that the restore has been completed successfully in the new region: ```bash kubectl --context $CLUSTER_RECREATED exec -n $CAMUNDA_NAMESPACE_RECREATED -it $ELASTIC_POD -c elasticsearch -- curl -u "elastic:$ES_PASSWORD" -XGET 'http://localhost:9200/_snapshot/camunda_backup/failback/_status' ```
Example output **This is only an example, and the values will differ for you.** Ensure you see `state: "SUCCESS"`, and that the properties `done` and `total` have equal values. ```json { "snapshots": [ { "snapshot": "failback", "repository": "camunda_backup", "uuid": "1S_C05K0RjqFyWMfjSKI_A", "state": "SUCCESS", "include_global_state": true, "shards_stats": { "initializing": 0, "started": 0, "finalizing": 0, "done": 33, "failed": 0, "total": 33 }, "stats": { "incremental": { "file_count": 145, "size_in_bytes": 353953 }, "total": { "file_count": 145, "size_in_bytes": 353953 }, "start_time_in_millis": 1712058365525, "time_in_millis": 1005 }, "indices": { ... } } ] } ```
#### Initialize new Camunda exporter to the recreated region | **Details** | **Current state** | **Desired state** | | ------------------------ | --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Camunda 8** | Remains unreachable by end-users while restoring functionality. | Start a new exporter to the recreated region. Ensure that both Elasticsearch instances are populated for data redundancy. Separate the initialization step (asynchronous) and confirm completion before resuming the exporters. | | **Elasticsearch Backup** | Backup has been created and restored to the recreated region. | N/A | :::info If you have upgraded from a previously migrated 8.7 system, you may still have the legacy `elasticsearchregion0` and `elasticsearchregion1` exporters configured. - If **both exporters are disabled**, you can safely ignore them. - If the old exporter is **enabled** in the survived region (for example, because you’re using **Optimize**), apply the same logic to these exporters as described for the new ones in this guide. ::: #### How to get there 1. Initialize the new exporter for the recreated region by sending an API request via the Zeebe Gateway: ```bash kubectl --context $CLUSTER_SURVIVING port-forward services/$CAMUNDA_RELEASE_NAME-zeebe-gateway 9600:9600 -n $CAMUNDA_NAMESPACE_SURVIVING curl -XPOST 'http://localhost:9600/actuator/exporters/camundaregion1/enable' -H 'Content-Type: application/json' -d '{"initializeFrom" : "camundaregion0"}' ``` #### Verification Port-forwarding the Zeebe Gateway via `kubectl` for the REST API and listing all exporters will reveal their current status. ```bash kubectl --context $CLUSTER_SURVIVING port-forward services/$CAMUNDA_RELEASE_NAME-zeebe-gateway 9600:9600 -n $CAMUNDA_NAMESPACE_SURVIVING curl -XGET 'http://localhost:9600/actuator/exporters' ```
Example output ```bash [{"exporterId":"camundaregion0","status":"ENABLED"},{"exporterId":"camundaregion1","status":"ENABLED"}] ```
You can also check the status of the change using the Cluster API via the already port-forwarded Zeebe Gateway. **Ensure the status is "COMPLETED" before proceeding with the next step.** ```bash curl -XGET 'http://localhost:9600/actuator/cluster' | jq .lastChange ```
Example output ```bash { "id": 6, "status": "COMPLETED", "startedAt": "2024-08-23T12:54:07.968549269Z", "completedAt": "2024-08-23T12:54:09.282558853Z" } ```
#### Reactivate Camunda exporter | **Details** | **Current state** | **Desired state** | | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | | **Camunda 8** | Not reachable yet by end-users and currently not exporting any data. Exporters are enabled for both regions, with the operation confirmed to be completed. | Reactivate existing exporters that will allow Zeebe to export data to Elasticsearch again. | #### How to get there 1. Reactivate the exporters by sending the [exporting API](/self-managed/components/orchestration-cluster/zeebe/operations/management-api.md#exporting-api) activation request via the Zeebe Gateway: ```bash kubectl --context $CLUSTER_SURVIVING port-forward services/$CAMUNDA_RELEASE_NAME-zeebe-gateway 9600:9600 -n $CAMUNDA_NAMESPACE_SURVIVING curl -i localhost:9600/actuator/exporting/resume -XPOST # The successful response should be: # HTTP/1.1 204 No Content ``` #### Verification There is currently no API available to confirm the reactivation of the exporters. Only the response code `204` indicates a successful resumption. This is a synchronous operation. #### Add new brokers to the Zeebe cluster | **Details** | **Current state** | **Desired state** | | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------ | | **Camunda 8** | Running in two regions, but not yet utilizing all Zeebe brokers. Operate and Tasklist remain unavailable, Elasticsearch exporters enabled. | Fully functional Zeebe cluster setup utilizing both regions, recovering the main dual-region benefits. | #### How to get there 1. From the base Helm values file (`camunda-values.yml`) in `aws/kubernetes/eks-dual-region/helm-values`, extract the `clusterSize` and `replicationFactor` values. You’ll need these when re-adding the brokers to the Zeebe cluster. 2. Port-forward the Zeebe Gateway to access the [management REST API](/self-managed/components/orchestration-cluster/zeebe/configuration/gateway.md#managementserver). This allows you to send a Cluster API call to add the new brokers to the Zeebe cluster using the previously extracted `clusterSize` and `replicationFactor`. In this example, the `clusterSize` is `8` and the `replicationFactor` is `4`. Because the uneven brokers were lost, re-add them (brokers with IDs `1, 3, 5, 7`) and set the appropriate `replicationFactor` in the request. ```bash kubectl --context $CLUSTER_SURVIVING port-forward services/$CAMUNDA_RELEASE_NAME-zeebe-gateway 9600:9600 -n $CAMUNDA_NAMESPACE_SURVIVING ``` ```bash curl -XPATCH 'http://localhost:9600/actuator/cluster?force=true' \ -H 'Content-Type: application/json' \ -d '{ "brokers": { "add": [1,3,5,7] }, "partitions": { "replicationFactor": 4 } }' ``` ```bash curl -XPATCH 'http://localhost:9600/actuator/cluster?force=true' \ -H 'Content-Type: application/json' \ -d '{ "brokers": { "add": [0,2,4,6] }, "partitions": { "replicationFactor": 4 } }' ``` :::note This step can take longer depending on the cluster size, data volume, and current load. ::: #### Verification Port-forwarding the Zeebe Gateway via `kubectl` for the REST API and checking the Cluster API endpoint will show the status of the last change. ```bash kubectl --context $CLUSTER_SURVIVING port-forward services/$CAMUNDA_RELEASE_NAME-zeebe-gateway 9600:9600 -n $CAMUNDA_NAMESPACE_SURVIVING curl -XGET 'http://localhost:9600/actuator/cluster' | jq .lastChange ```
Example output ```bash { "id": 6, "status": "COMPLETED", "startedAt": "2024-08-23T12:54:07.968549269Z", "completedAt": "2024-08-23T12:54:09.282558853Z" } ```
Another way to confirm this is to use the v2 topology REST endpoint to see that partitions are actively re-distributed to the uneven members. ```bash kubectl --context $CLUSTER_SURVIVING port-forward services/$CAMUNDA_RELEASE_NAME-zeebe-gateway 8080:8080 -n $CAMUNDA_NAMESPACE_SURVIVING curl -L -X GET 'http://localhost:8080/v2/topology' \ -H 'Accept: application/json' ```
Example output ```bash { "brokers": [ { "nodeId": 0, "host": "camunda-zeebe-0.camunda-zeebe.camunda-london", "port": 26501, "partitions": [ { "partitionId": 1, "role": "leader", "health": "healthy" }, { "partitionId": 6, "role": "leader", "health": "healthy" }, { "partitionId": 7, "role": "follower", "health": "healthy" }, { "partitionId": 8, "role": "follower", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 1, "host": "camunda-zeebe-0.camunda-zeebe.camunda-paris", "port": 26501, "partitions": [ { "partitionId": 1, "role": "follower", "health": "healthy" }, { "partitionId": 6, "role": "follower", "health": "healthy" }, { "partitionId": 7, "role": "follower", "health": "healthy" }, { "partitionId": 8, "role": "follower", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 2, "host": "camunda-zeebe-1.camunda-zeebe.camunda-london", "port": 26501, "partitions": [ { "partitionId": 1, "role": "follower", "health": "healthy" }, { "partitionId": 2, "role": "leader", "health": "healthy" }, { "partitionId": 3, "role": "follower", "health": "healthy" }, { "partitionId": 8, "role": "leader", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 3, "host": "camunda-zeebe-1.camunda-zeebe.camunda-paris", "port": 26501, "partitions": [ { "partitionId": 1, "role": "follower", "health": "healthy" }, { "partitionId": 2, "role": "follower", "health": "healthy" }, { "partitionId": 3, "role": "follower", "health": "healthy" }, { "partitionId": 8, "role": "follower", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 4, "host": "camunda-zeebe-2.camunda-zeebe.camunda-london", "port": 26501, "partitions": [ { "partitionId": 2, "role": "follower", "health": "healthy" }, { "partitionId": 3, "role": "leader", "health": "healthy" }, { "partitionId": 4, "role": "follower", "health": "healthy" }, { "partitionId": 5, "role": "follower", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 5, "host": "camunda-zeebe-2.camunda-zeebe.camunda-paris", "port": 26501, "partitions": [ { "partitionId": 2, "role": "follower", "health": "healthy" }, { "partitionId": 3, "role": "follower", "health": "healthy" }, { "partitionId": 4, "role": "follower", "health": "healthy" }, { "partitionId": 5, "role": "follower", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 6, "host": "camunda-zeebe-3.camunda-zeebe.camunda-london", "port": 26501, "partitions": [ { "partitionId": 4, "role": "leader", "health": "healthy" }, { "partitionId": 5, "role": "leader", "health": "healthy" }, { "partitionId": 6, "role": "follower", "health": "healthy" }, { "partitionId": 7, "role": "leader", "health": "healthy" } ], "version": "8.8.0" }, { "nodeId": 7, "host": "camunda-zeebe-3.camunda-zeebe.camunda-paris", "port": 26501, "partitions": [ { "partitionId": 4, "role": "follower", "health": "healthy" }, { "partitionId": 5, "role": "follower", "health": "healthy" }, { "partitionId": 6, "role": "follower", "health": "healthy" }, { "partitionId": 7, "role": "follower", "health": "healthy" } ], "version": "8.8.0" }, ], "clusterSize": 4, "partitionsCount": 8, "replicationFactor": 2, "gatewayVersion": "8.8.0" } ```
#### Start Operate and Tasklist | **Details** | **Current state** | **Desired state** | | -------------------- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | | **Camunda 8** | Remains unreachable by end-users while dual-region functionality is being restored. | Enable Operate and Tasklist in both the surviving and recreated regions to restore user interaction with Camunda 8. | | **User interaction** | Users can interact with Zeebe cluster again. Dual-region functionality is restored, improving reliability and performance. | Users can fully utilize the Camunda 8 environment again. | #### Procedure :::info This step is executed at this stage because the application must be redeployed — all components run within the same Kubernetes pod. Performing it earlier would block the automatic rollout, as readiness only complete once Zeebe brokers have joined the cluster. Executing it prematurely would therefore require manual intervention. ::: Reapply or upgrade the Helm release to enable and deploy Operate and Tasklist. Assuming based on **Step 1** and **Step 2**, the base Helm values file `camunda-values.yml` in `aws/kubernetes/eks-dual-region/helm-values` includes the adjustments for Elasticsearch and the Zeebe initial brokers. Make sure to remove the `CAMUNDA_DATABASE_SCHEMAMANAGER_CREATESCHEMA` variable from `camunda-values.yml`. Edit the `camunda-values.yml` in `aws/kubernetes/eks-dual-region/helm-values` and remove the following from the `orchestration.env`: ```yaml orchestration: env: - name: CAMUNDA_DATABASE_SCHEMAMANAGER_CREATESCHEMA value: "false" # ... ``` 1. Upgrade the Camunda environment in the surviving region (`CAMUNDA_NAMESPACE_SURVIVING` and `REGION_SURVIVING`) to deploy Operate and Tasklist: ```bash helm upgrade $CAMUNDA_RELEASE_NAME camunda/camunda-platform \ --version $HELM_CHART_VERSION \ --kube-context $CLUSTER_SURVIVING \ --namespace $CAMUNDA_NAMESPACE_SURVIVING \ -f camunda-values.yml \ -f ../../../../generic/kubernetes/operator-based/elasticsearch/camunda-elastic-values.yml \ -f $REGION_SURVIVING/camunda-values.yml ``` 2. Upgrade the environment in new region (`CAMUNDA_NAMESPACE_RECREATED` and `REGION_RECREATED`) to deploy Operate and Tasklist: ```bash helm upgrade $CAMUNDA_RELEASE_NAME camunda/camunda-platform \ --version $HELM_CHART_VERSION \ --kube-context $CLUSTER_RECREATED \ --namespace $CAMUNDA_NAMESPACE_RECREATED \ -f camunda-values.yml \ -f ../../../../generic/kubernetes/operator-based/elasticsearch/camunda-elastic-values.yml \ -f $REGION_RECREATED/camunda-values.yml ``` Follow the installation instructions for both regions. You’ll need to apply `helm upgrade` on both `CLUSTER_RECREATED` and `CLUSTER_SURVIVING`. Make sure to remove the `CAMUNDA_DATABASE_SCHEMAMANAGER_CREATESCHEMA` variable from `camunda-values.yml`. Edit the `generated-values-region-0|1.yml` file and remove the following from the `orchestration.env`: ```yaml orchestration: env: - name: CAMUNDA_DATABASE_SCHEMAMANAGER_CREATESCHEMA value: "false" # ... ``` - [Apply the initial installation on the two regions](/self-managed/deployment/helm/cloud-providers/openshift/dual-region.md#install-camunda-8-using-helm). - Ensure that the services are exported correctly using `subctl`. - This step re-enables Operate and Tasklist in both regions. #### Verification 1. If the environment is exposed through an Ingress, verify that Operate and Tasklist are reachable again. 2. Check the logs of any `camunda-zeebe-X` pod to confirm the active profiles. Run this command for both the surviving and recreated clusters (`CLUSTER_RECREATED` and `CAMUNDA_NAMESPACE_RECREATED`): ```bash kubectl --context $CLUSTER_SURVIVING logs camunda-zeebe-0 | grep "profiles are active" ``` ```bash # The default are 5 profiles, so this confirms that Operate and Tasklist are enabled io.camunda.application.StandaloneCamunda - The following 5 profiles are active: "broker", "operate", "tasklist", "admin", "consolidated-auth" ``` 3. Alternatively, verify that the configuration lists Operate and Tasklist as active profiles: ```bash kubectl --context $CLUSTER_SURVIVING get cm camunda-zeebe-configuration-unified -oyaml | grep spring -A2 ``` ```bash spring: profiles: active: "broker,operate,tasklist,admin,consolidated-auth" ```
--- ### Conclusion Following this procedure ensures a structured and efficient recovery process that maintains operational continuity in dual-region deployments. Always manage dual-region environments carefully, and be prepared to follow these steps to perform a successful failover and failback. --- ## Helm 4 Since Camunda 8.10 (chart 15.x), Helm CLI v4 is required. Use this page to understand Helm 4 behavior changes that affect the Camunda Helm chart and to apply the relevant workarounds. :::note Switching from Helm v3 Switching CLIs does not require a release-state migration; Helm is client-side only. See [Move from the Helm v3 CLI to v4](/self-managed/deployment/helm/operational-tasks/moving-helm-v3-to-v4.md). ::: :::info Learn more about Helm 4 changes in the [Helm documentation](https://helm.sh/docs/overview/#whats-new). ::: ## Camunda Helm chart compatibility Helm CLI compatibility depends on the Camunda Helm chart version. | Chart version | Helm CLI v3 | Helm CLI v4 | | ------------------------- | ----------- | ----------- | | Camunda 8.6 – Chart 11.x | ✅ | ❌ | | Camunda 8.7 – Chart 12.x | ✅ | ❌ | | Camunda 8.8 – Chart 13.x | ✅ | ❌ | | Camunda 8.9 – Chart 14.x | ✅ | ✅ \* | | Camunda 8.10 – Chart 15.x | ❌ | ✅ \* | \* Helm CLI v4 may require workarounds when overriding environment variables. See [Workarounds](#workarounds). ## Helm 4 breaking changes ### Server-side apply is enabled by default Server-side apply is a Kubernetes feature that improves how changes to Kubernetes resources are merged. When multiple clients update the same resource, server-side apply reduces the risk of unintentional overwrites. In Helm 4, server-side apply is enabled by default. This changes the behavior of `helm install` and `helm upgrade` compared to Helm 3. #### Problem Server-side apply has a known limitation: duplicate environment variables on the same Kubernetes resource are treated as an error, rather than later entries overwriting earlier ones. In the Camunda Helm chart, environment variables are sometimes used as an override mechanism for values defined by the chart. For example, if the following is set in `values.yaml`: ```yaml identity: env: - name: CAMUNDA_LICENSE_KEY value: "--- YOUR LICENSE KEY HERE ---" ``` The rendered Identity deployment may contain duplicate entries: ```yaml spec: containers: - name: identity env: - name: CAMUNDA_LICENSE_KEY value: "--- CAMUNDA_DEFAULT_VALUE ---" - name: CAMUNDA_LICENSE_KEY value: "--- YOUR LICENSE KEY HERE ---" ``` In Helm 4, this causes the install or upgrade to fail with an error similar to the following: > Error: INSTALLATION FAILED: failed to create typed patch object (default/RELEASE-identity; apps/v1, Kind=Deployment): > .spec.template.spec.containers[name="identity"].env: duplicate entries for key [name="CAMUNDA_LICENSE_KEY"] This behavior can affect any Camunda Helm chart component where environment variables are overridden using the `env` list. #### Workarounds If you encounter a duplicate environment variable error, apply one of the following workarounds: 1. Prefer dedicated `values.yaml` options over environment variable overrides whenever available. For example, use `global.license` instead of setting `CAMUNDA_LICENSE_KEY` via the `env` section. 2. Override application configuration using the `configuration` or `extraConfiguration` options in `values.yaml` instead of environment variables. See [Configure Helm chart components](/self-managed/deployment/helm/configure/application-configs.md) for details. 3. Disable server-side apply when running Helm commands: ```bash helm install ... --server-side=false helm upgrade ... --server-side=false ``` 4. On older chart versions where Helm CLI v3 is still supported, run installs and upgrades with Helm v3. :::note Helm CLI v3 support timeline Helm CLI v3 receives bug fixes until July 8, 2026, and security fixes until November 11, 2026. See the [Helm support policy](https://helm.sh/blog/helm-4-released#helm-v3-support). Chart 15.x and later require Helm v4 regardless of this timeline. ::: If you are on a supported chart version and your package manager no longer provides Helm CLI v3, you can run it using Docker: ```bash docker run \ -v ~/.kube:/root/.kube \ alpine/helm:3.19.4 \ install RELEASE camunda/camunda-platform \ ``` ### Post-renderers are now plugins In Helm 4, post-renderers must be implemented as Helm plugins. If you previously passed an executable path (for example, a shell script) using the `--post-renderer` option, you must migrate that logic into a Helm plugin. :::info Refer to the Helm documentation for a [tutorial on building a post-renderer plugin](https://helm.sh/docs/plugins/developer/tutorial-postrenderer-plugin/). ::: --- ## Helm chart operational tasks In this section, find details on operational tasks associated with Kubernetes with Helm. --- ## Advanced migration alternatives This guide covers advanced migration alternatives for organizations that **cannot use Kubernetes operators or managed services** for their infrastructure components. These approaches require more manual effort but provide full control over the deployment. :::warning Advanced topic The approaches described here are not automated via the migration scripts and require significant manual configuration and operational expertise. For most deployments, we recommend using either [Kubernetes operators](./bitnami-to-operators.md) or [managed services](./bitnami-to-managed-services.md). ::: ## When to use this guide Consider these alternatives if: - Your organization doesn't allow operator installations in the cluster—for example, due to security or compliance constraints. - You're running on bare-metal infrastructure without managed service access. - You need to migrate to an existing database infrastructure, like a shared PostgreSQL cluster managed by a DBA team. - You're running Camunda outside of Kubernetes—for example, using Docker Compose or VM-based deployments. Read the [topic overview](./index.md#why-migrate) to learn why you should migrate. :::info Use official installation documentation for untested targets This page intentionally avoids prescribing full installation commands for PostgreSQL, Elasticsearch, or Keycloak on custom targets, such as standalone StatefulSets, VMs, or bare metal. Use the official documentation for the distribution you operate, and use this page only for the Camunda-specific migration flow and Helm wiring. ::: ## Prerequisites Before starting the migration, ensure you have the following [general prerequisites](./index.md#prerequisites-all-paths): ## Precautions Review the [general precautions](./index.md#precautions) that apply to all migration paths. :::tip Before running in production Review the [operational readiness](#operational-readiness) checklist, including the staging rehearsal and pre-migration checklist, before starting a production migration. ::: ## Option 1: Manually-deployed PostgreSQL and Elasticsearch on Kubernetes If you can't install CloudNativePG (CNPG) or Amazon Elastic Cloud on Kubernetes (ECK) operators, but still run on Kubernetes, provision PostgreSQL and Elasticsearch using your platform standard manifests or the official product documentation for the distributions you operate. Before cutover, ensure the target platform provides the following: - A stable PostgreSQL endpoint reachable from the Camunda namespace. - A stable Elasticsearch endpoint reachable from the Camunda namespace. - Persistent storage sized for the current data set and expected growth. - Databases and users for `identity`, `keycloak`, and `webmodeler`. - Credentials stored in Kubernetes Secrets for the migration jobs and the Helm upgrade. Once the targets exist, the migration flow stays the same: 1. Freeze Camunda during the final cutover window. 2. Migrate PostgreSQL with `pg_dump` and `pg_restore` (see [PostgreSQL migration flags](#postgresql-migration-flags)). 3. Migrate Elasticsearch with the method that fits your target (see [Elasticsearch migration decision matrix](#elasticsearch-migration-decision-matrix)). ### Reconfigure Helm After you migrate the data, update your Helm values to point to the external endpoints: ```yaml # Disable Bitnami subcharts identityPostgresql: enabled: false webModelerPostgresql: enabled: false elasticsearch: enabled: false identityKeycloak: enabled: false identity: externalDatabase: host: "" port: 5432 database: "identity" username: "identity" existingSecret: "external-pg-identity" existingSecretPasswordKey: "password" webModeler: restapi: externalDatabase: host: "" port: 5432 database: "webmodeler" user: "webmodeler" existingSecret: "external-pg-webmodeler" existingSecretPasswordKey: "password" orchestration: data: secondaryStorage: type: elasticsearch elasticsearch: url: https://:9200 optimize: database: elasticsearch: enabled: true external: true url: protocol: https host: port: 9200 ``` Finally, run `helm upgrade` to switch Camunda to the new endpoints: ```bash helm upgrade ${CAMUNDA_RELEASE_NAME} camunda/camunda-platform \ -n ${NAMESPACE} \ --version ${CAMUNDA_HELM_CHART_VERSION} \ -f your-custom-values.yaml ``` ## Option 2: VM-based PostgreSQL and Elasticsearch If your infrastructure runs on virtual machines (VMs) or bare-metal servers, treat PostgreSQL and Elasticsearch provisioning as a separate platform task, and follow the official product documentation: - [PostgreSQL documentation](https://www.postgresql.org/docs/current/) for installation, remote access, backup/restore tooling, and hardening. - [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html) for installation, cluster topology, TLS, and operations. Before migration, make sure you have: - VM endpoints or DNS names reachable from Kubernetes. - Firewall and TLS settings validated from the cluster to the target hosts. - Databases, users, and credentials created for the Camunda components. - A staging rehearsal showing that `pg_restore` and your chosen Elasticsearch migration method work against those endpoints. Once the services are ready, [reconfigure Helm](#reconfigure-helm), and replace the hosts with your VM or bare-metal addresses. For the data migration, use the approaches described in the [data migration approaches summary](#data-migration-approaches-summary). ## Option 3: Docker Compose deployment If you're targeting Docker Compose, use this guide for the migration workflow, and use the dedicated Docker Compose assets as the source of truth: - Follow the local [Docker Compose quickstart](/self-managed/quickstart/developer-quickstart/docker-compose.md) for the supported setup and runtime behavior. - Use the maintained Compose assets in [camunda-distributions/docker-compose](https://github.com/camunda/camunda-distributions/tree/main/docker-compose) instead of copying an embedded example from this page. You still need to migrate PostgreSQL and Elasticsearch data separately using the same approaches described in [data migration approaches summary](#data-migration-approaches-summary). :::warning Not suitable for production Docker Compose deployments are suitable for development and testing only. For production environments, use [Kubernetes operators](./bitnami-to-operators.md) or [managed services](./bitnami-to-managed-services.md). ::: ## Data migration approaches summary Regardless of the target infrastructure, the data migration approach remains the same: | Component | Method | Tools | | ------------- | ----------------------------------------- | ------------------------------------------------------ | | PostgreSQL | Dump and restore | `pg_dump` / `pg_restore` (custom format) | | Elasticsearch | Snapshot/restore, reindex, or elasticdump | Elasticsearch Snapshot API, `elasticdump`, Reindex API | | Keycloak | Via PostgreSQL data migration | No separate migration needed | ### PostgreSQL migration flags When you migrate your PostgreSQL data, use these flags: ```bash pg_restore \ --clean # Drop objects before recreating --if-exists # Don't error if objects don't exist --no-owner # Don't set ownership (avoids permission issues) --no-privileges # Don't restore privilege assignments -d # Target database ``` ### Elasticsearch migration decision matrix | Scenario | Recommended method | | ----------------------------------------------------------- | --------------------------------------- | | Target accessible from Kubernetes + shared storage possible | Filesystem snapshot/restore | | Target accessible from Kubernetes + no shared storage | `elasticdump` or S3 snapshot repository | | Target not accessible from Kubernetes | S3 snapshot repository | | Large datasets (> 50 GB) | Snapshot/restore (fastest method) | ## Keycloak considerations Regardless of the infrastructure target, Keycloak migration always involves migrating its PostgreSQL database. After the data migration: - If using the **Keycloak Operator** (recommended): Deploy a Keycloak Custom Resource pointing to the migrated PostgreSQL database. - If using an **external OIDC provider**: Configure Camunda to use the external provider via [external OIDC provider](/self-managed/deployment/helm/configure/authentication-and-authorization/external-oidc-provider.md). You can then decommission Keycloak entirely. - If using a **standalone Keycloak instance** (VM or Docker): Point it to the migrated PostgreSQL database and update the Camunda Helm values to reference the external Keycloak URL. ## Operational readiness Before running any of the alternative migration approaches in production, follow these steps to minimize risk. ### Staging rehearsal 1. **Replicate your production environment** in a staging/test cluster, including the target infrastructure (standalone StatefulSets, VMs, Docker Compose, etc.). 2. **Run the full migration end to end** using the chosen approach (manual StatefulSets, VMs, or Docker). 3. **Measure actual timings**: since alternative deployments vary widely, timing data from staging is critical for setting maintenance windows. 4. **Test the failback path**: verify you can roll back by restoring the original Helm values and reconnecting to the Bitnami subcharts. :::tip For VM-based or Docker Compose targets, include network connectivity testing (firewall rules, DNS resolution from Kubernetes to external hosts) as part of the rehearsal. ::: ### Production dry-run Create a step-by-step runbook and walk through it without executing destructive commands. Document each command and expected output. For inspiration, review the [backup](https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/migration/2-backup.sh) and [cutover](https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/migration/3-cutover.sh) migration scripts used by the automated paths. They illustrate the sequence of operations and safety checks you should replicate in your runbook. ### Pre-migration checklist - **Verify target connectivity**: confirm the Kubernetes cluster can reach the target infrastructure (VMs, external databases). Test with `curl`, `psql`, or `kubectl exec` from within the cluster. - **Notify stakeholders**: announce the maintenance window. - **Verify backups**: ensure you have a recent backup from your existing backup strategy, independent of the migration scripts. - **Document the runbook**: for manual migrations, have a written, step-by-step runbook reviewed by a second team member. - **Prepare rollback commands**: pre-write the `helm upgrade` command needed to revert to Bitnami subcharts. ### Failback procedure 1. **Helm rollback**: revert the Helm values to use Bitnami subcharts again. Since the Bitnami PVCs still exist (they are not deleted during migration), data is intact. 2. **If Bitnami PVCs are deleted**: restore from your independent backup or from the `pg_dump` files created during migration. ### Data safety measures - Always create `pg_dump` backups before any data migration, regardless of the target infrastructure. - Store backup files outside the cluster (cloud storage bucket, NFS share) for redundancy. - The same `pg_restore` flags (`--clean --if-exists --no-owner --no-privileges`) apply to all targets and are idempotent. - Keep the old Bitnami infrastructure running in read-only mode, if possible, for several days as a safety net. ### Post-migration monitoring After completing the migration, monitor for at least 48 hours: - **Pod restarts**: `kubectl get pods -n ${NAMESPACE} --watch` - **Target database health**: monitor connection counts, replication status (if using replicas), and storage usage. - **Camunda component logs**: look for connection errors, authentication failures, or data inconsistencies. - **Process instance completion**: verify that in-flight process instances continue to execute correctly. - **External connectivity stability**: for VM or Docker targets, monitor network latency and connection drops between Kubernetes and the external infrastructure. --- ## Migrate from Bitnami subcharts to managed services Migrate a Camunda 8 Helm installation from Bitnami-managed infrastructure to **cloud-managed services**, such as: - **PostgreSQL**: AWS RDS, Azure Database for PostgreSQL, Google Cloud SQL, or any managed PostgreSQL service - **Elasticsearch**: Elastic Cloud or any managed Elasticsearch service - **Keycloak**: This guide does not assume a managed Keycloak service. Keep Keycloak on the [Keycloak Operator](https://www.keycloak.org/operator/installation), or replace it with an [external OIDC provider](/self-managed/deployment/helm/configure/authentication-and-authorization/external-oidc-provider.md) if that better fits your environment. ## When to use this guide Managed services are ideal when your organization: - Prefers minimal operational overhead for database and search infrastructure - Already uses a cloud provider's managed services catalog - Requires SLA-backed availability and automated patching from the cloud vendor - Does not want to manage Kubernetes operators for infrastructure components Read the [topic overview](./index.md#why-migrate) to learn why you should migrate. ## Choose your migration strategy ## Prerequisites Before starting the migration, ensure you have the following [general prerequisites](./index.md#prerequisites-all-paths): In addition to the general prerequisites: - **Managed services already provisioned**: PostgreSQL and Elasticsearch instances must be running and accessible before starting the migration. If you haven't provisioned them yet, see the [provisioning reference](#provisioning-reference) in Step 1. - Ensure network connectivity between your Kubernetes cluster and the managed services. - Have credentials ready for each managed service. ## Precautions Review the [general precautions](./index.md#precautions) that apply to all migration paths. :::tip Before running in production Review the [operational readiness](#operational-readiness) checklist, including the staging rehearsal and pre-migration checklist, before starting a production migration. ::: ### IRSA / IAM-based authentication not supported
AWS only The migration jobs use password-based PostgreSQL authentication (`PGPASSWORD`) and standard Elasticsearch HTTP API. Setups using AWS IAM Roles for Service Accounts (IRSA) with `jdbc:aws-wrapper` or Elasticsearch endpoints protected by cloud-specific IAM auth require a custom migration approach.
### Identity authentication You need to decide how Identity will authenticate before the cutover. For managed services, the infrastructure decision is separate from the authentication decision: - If you **keep Keycloak**, deploy it with the Keycloak Operator and set the hostname to the full public URL, for example `https://your-domain.example.com/auth`. - If you **replace Keycloak with external OIDC**, prepare the provider configuration and the corresponding Identity Helm values before running the migration. ### Elasticsearch to OpenSearch not supported This migration path does not support moving data from the Bitnami Elasticsearch subchart to Amazon OpenSearch Service or another OpenSearch target. If your target architecture requires OpenSearch, treat that as a separate replatforming effort instead of a supported in-place migration from the Bitnami Elasticsearch subchart. ## Clone the deployment references repository ## Step 1: Create Kubernetes Secrets for managed services This step assumes your managed PostgreSQL and Elasticsearch services are already provisioned and accessible from the Kubernetes cluster. You need the endpoint, port, database names, usernames, and credentials for each service.
Provisioning reference If you haven't provisioned your managed services yet, use your cloud provider's official documentation: **PostgreSQL:** - [AWS RDS for PostgreSQL](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_PostgreSQL.html) - [Azure Database for PostgreSQL - Flexible Server](https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/overview) - [Google Cloud SQL for PostgreSQL](https://cloud.google.com/sql/docs/postgres) Minimum requirements: - Create the `identity`, `keycloak`, and `webmodeler` databases. - Create or assign credentials for each database according to your provider's model. - Ensure private connectivity from the Kubernetes cluster to PostgreSQL. - Record the endpoint, port, database names, usernames, and secret material. **Elasticsearch:** - [Elastic Cloud](https://www.elastic.co/guide/en/cloud/current/ec-getting-started.html) — note the deployment endpoint, Cloud ID, and credentials. - Self-managed Elasticsearch — ensure it is accessible from the cluster, and note the endpoint and credentials.
### Create Kubernetes Secrets Store the managed service credentials as Kubernetes Secrets so both the migration scripts and Camunda Helm chart can use them: ```bash # PostgreSQL secrets — one per component kubectl create secret generic external-pg-identity \ -n ${NAMESPACE} \ --from-literal=password='' kubectl create secret generic external-pg-keycloak \ -n ${NAMESPACE} \ --from-literal=password='' kubectl create secret generic external-pg-webmodeler \ -n ${NAMESPACE} \ --from-literal=password='' # Elasticsearch secret kubectl create secret generic external-es \ -n ${NAMESPACE} \ --from-literal=elastic='' ``` ## Step 2: Configure the migration for external targets :::info Terminology — "managed services" vs. "external targets" The migration scripts use the term **external targets** (`PG_TARGET_MODE=external`, `ES_TARGET_MODE=external`) for any non-operator target. This includes cloud-managed services (AWS RDS, Elastic Cloud, etc.) but also self-hosted databases outside the Kubernetes cluster. This guide uses "managed services" as a shorthand, but the scripts themselves are not restricted to cloud-managed offerings. ::: Edit `env.sh`, and set the target mode to `external`. The base configuration variables (`NAMESPACE`, `CAMUNDA_RELEASE_NAME`, `MIGRATE_*`, etc.) are the same as in the [operator-based guide](./bitnami-to-operators.md#key-configuration-variables), only the target mode and external endpoint variables differ:
Show details: external target configuration example ```bash # Set target modes export PG_TARGET_MODE="external" export ES_TARGET_MODE="external" # PostgreSQL external targets export EXTERNAL_PG_IDENTITY_HOST="your-rds-endpoint.region.rds.amazonaws.com" export EXTERNAL_PG_IDENTITY_PORT="5432" export EXTERNAL_PG_IDENTITY_SECRET="external-pg-identity" export EXTERNAL_PG_KEYCLOAK_HOST="your-rds-endpoint.region.rds.amazonaws.com" export EXTERNAL_PG_KEYCLOAK_PORT="5432" export EXTERNAL_PG_KEYCLOAK_SECRET="external-pg-keycloak" export EXTERNAL_PG_WEBMODELER_HOST="your-rds-endpoint.region.rds.amazonaws.com" export EXTERNAL_PG_WEBMODELER_PORT="5432" export EXTERNAL_PG_WEBMODELER_SECRET="external-pg-webmodeler" # Elasticsearch external target export EXTERNAL_ES_HOST="your-elastic-endpoint.example.com" export EXTERNAL_ES_PORT="443" export EXTERNAL_ES_SECRET="external-es" ```
You can use the same managed PostgreSQL host for all components—each database is separate. This is common when using a single RDS instance with multiple databases. ### Create custom Helm values When using external targets, you need a custom Helm values file that configures Camunda to connect to the managed services. Set `CUSTOM_HELM_VALUES_FILE` to point to this file: ```bash export CUSTOM_HELM_VALUES_FILE="./my-external-values.yaml" ``` Example custom values file for AWS RDS + external Elasticsearch:
Show details: external Helm values example ```yaml # Disable Bitnami subcharts identityPostgresql: enabled: false webModelerPostgresql: enabled: false elasticsearch: enabled: false identityKeycloak: enabled: false # Configure Identity to use external PostgreSQL identity: externalDatabase: host: "your-rds-endpoint.region.rds.amazonaws.com" port: 5432 database: "identity" username: "identity" existingSecret: "external-pg-identity" existingSecretPasswordKey: "password" # Configure Web Modeler to use external PostgreSQL webModeler: restapi: externalDatabase: host: "your-rds-endpoint.region.rds.amazonaws.com" port: 5432 database: "webmodeler" user: "webmodeler" existingSecret: "external-pg-webmodeler" existingSecretPasswordKey: "password" # Configure external Elasticsearch using the per-component values schema orchestration: data: secondaryStorage: type: elasticsearch elasticsearch: url: "https://your-elastic-endpoint.example.com:443" auth: username: "elastic" secret: existingSecret: "external-es" existingSecretKey: "elastic" optimize: database: elasticsearch: enabled: true external: true url: protocol: "https" host: "your-elastic-endpoint.example.com" port: 443 auth: username: "elastic" secret: existingSecret: "external-es" existingSecretKey: "elastic" elasticsearch: enabled: false ```
:::warning Helm values customization The example above is a starting point. Adjust the values to match your specific managed service configuration, authentication method (IAM, username/password, etc.), and TLS requirements. Refer to the [Camunda Helm chart parameters](/self-managed/deployment/helm/chart-parameters.md) for all available options. ::: ### Source `env.sh` Source the configuration: ```bash source env.sh ``` ## Step 3: Run the migration The migration follows the same five-phase approach described in the [migration overview](/self-managed/deployment/helm/operational-tasks/migration-from-bitnami/index.md#migration-phases). Each phase is idempotent and can be safely rerun. The key difference with external targets is that operator installation is skipped for components using managed services. ### Phase 1: Deploy targets (no downtime) ```bash bash 1-deploy-targets.sh ``` What happens: - When `PG_TARGET_MODE=external`, the CloudNativePG (CNPG) operator is not installed; your managed PostgreSQL is used directly. - When `ES_TARGET_MODE=external`, the Elastic Cloud on Kubernetes (ECK) operator is not installed; your managed Elasticsearch target is used directly. - The Keycloak Operator is still deployed with a Custom Resource pointing to your managed PostgreSQL. - The script validates connectivity to each external endpoint before proceeding. ### Phase 2: Initial backup (no downtime) ```bash bash 2-backup.sh ``` What happens: 1. **PostgreSQL**: A `pg_dump` Kubernetes Job is created for each component (Identity, Keycloak, and Web Modeler). 2. **Elasticsearch**: A verification job checks source Elasticsearch health and lists all Camunda indices to be migrated. 3. All backup data is stored on a shared Persistent Volume Claim (PVC). What happens: 1. **PostgreSQL**: A `pg_dump` Kubernetes Job is created for each component (Identity, Keycloak, and Web Modeler). 2. **Elasticsearch**: A verification job checks source Elasticsearch health and lists all Camunda indices to be migrated. 3. **Elasticsearch warm reindex**: A full reindex from the source Bitnami ES to the managed target is performed while the application is still running. This pre-populates the target with all existing data so Phase 3 only needs a fast delta reindex. The warm reindex may take a significant amount of time depending on your data volume, but it runs **without any downtime**. 4. All backup data is stored on a shared Persistent Volume Claim (PVC). The target type does not affect backup operations — backups always run against the source Bitnami instances. ### Phase 3: Cutover (downtime required) :::warning Maintenance window required This is the only phase that causes downtime. Schedule a maintenance window before proceeding. Downtime typically lasts **5–60 minutes**, depending on Elasticsearch data volume. See [downtime estimation](./bitnami-to-operators.md#downtime-estimation) for benchmarked timings. With `ES_WARM_REINDEX=true`, downtime is reduced to **~5 minutes** regardless of Elasticsearch data volume. Phase 3 only syncs the delta written since the warm reindex in Phase 2. ::: :::tip Measure downtime before the real cutover You can run `bash 3-cutover.sh --estimate` to measure the actual cutover duration on your environment **without causing any downtime**. This runs the real data operations (PG backup/restore and ES reindex) against the target infrastructure but skips freezing the application and the Helm upgrade. See [Measure with `--estimate`](#measure-with---estimate) for details. ::: ```bash bash 3-cutover.sh ``` What happens: 1. **Save** current Helm values for rollback. 2. **Freeze** all Camunda deployments and StatefulSets (scale to zero replicas). 3. **Final backup** — consistent backup with no active connections. 4. **Restore** — `pg_restore` runs against the managed PostgreSQL endpoints instead of CNPG clusters. Elasticsearch **full reindex from remote** — all indices are copied from the source Bitnami ES to the managed target using the `_reindex` API. This is the dominant factor in downtime duration. 5. **Helm upgrade** — reconfigures Camunda to use the new backends and restarts all components. What happens: 1. **Save** current Helm values for rollback. 2. **Freeze** all Camunda deployments and StatefulSets (scale to zero replicas). 3. **Final backup** — consistent backup with no active connections. 4. **Restore** — `pg_restore` runs against the managed PostgreSQL endpoints. Elasticsearch **delta reindex** — only documents written between Phase 2 (warm reindex) and the freeze are synced. This uses `version_type=external` with `conflicts=proceed` to skip documents already present on the target, making it dramatically faster than a full reindex. 5. **Helm upgrade** — reconfigures Camunda to use the new backends and restarts all components. #### Elasticsearch data migration for managed services When `ES_WARM_REINDEX=true` is set in `env.sh`, the migration scripts **automatically** handle Elasticsearch data transfer for external targets using the `_reindex` API. Phase 2 performs a full reindex from the source Bitnami ES to the managed target (no downtime), and Phase 3 runs a fast delta reindex to sync changes. :::important Prerequisite for automated ES migration Your managed Elasticsearch target must have `reindex.remote.whitelist` configured to allow pulling data from the source Bitnami ES service. This is required for the `_reindex` API to work across clusters. Consult your managed Elasticsearch provider's documentation for how to configure this setting. ::: If you cannot configure `reindex.remote.whitelist` on the managed target, or prefer a manual approach, you can leave `ES_WARM_REINDEX=false` (default) and transfer data manually. See [alternative: manual Elasticsearch migration](#alternative-manual-elasticsearch-migration) below. ### Phase 4: Validate (no downtime) ```bash bash 4-validate.sh ``` The validation script checks that all Camunda deployments and StatefulSets are ready, and that the Keycloak Custom Resource is healthy. For external PostgreSQL and Elasticsearch targets, it verifies connectivity to the managed service endpoints rather than checking CNPG/ECK cluster status. A migration report is generated at `.state/migration-report.md`. :::warning Wait before cleanup Do not move on to the next phase immediately after validation. Operate with the new infrastructure through at least one full business cycle (for example, a complete weekday with peak traffic) before cleanup. Once Bitnami resources are deleted, rollback is no longer possible without restoring from backup. If you need to fail back, run `bash rollback.sh` **before** this phase (see [rollback](#rollback)). ::: ### Phase 5: Cleanup Bitnami resources (no downtime) :::warning Destructive and irreversible This phase **permanently deletes** old Bitnami StatefulSets, PVCs, and the migration backup PVC. After cleanup, rollback to Bitnami subcharts is **no longer possible**. Before running this phase, strongly consider: 1. Taking a full backup of all databases (`pg_dumpall` or equivalent) 2. Taking PVC or storage volume snapshots (cloud provider snapshots) 3. Storing backups in cold storage—for example, S3 Glacier or GCS Archive 4. Keeping rollback artifacts in `.state/` as a safety net ::: After confirming the migration is successful, remove old Bitnami StatefulSets, PVCs, services, and the migration backup PVC: ```bash bash 5-cleanup-bitnami.sh ``` What happens: 1. The script requires Phase 4 to be completed and displays a **destructive operation warning** with a confirmation prompt. 2. **Deletes old Bitnami PostgreSQL** StatefulSets, their PVCs, and headless services (for each migrated component: Identity, Keycloak, and Web Modeler). 3. **Deletes old Bitnami Elasticsearch** StatefulSet, PVCs, and services. 4. **Deletes old Bitnami Keycloak** StatefulSet. 5. **Deletes the migration backup PVC**. 6. **Reverifies** that all Camunda components and operator-managed targets remain healthy after cleanup. 7. Suggests removing the `reindex.remote.whitelist` setting from the ECK Elasticsearch configuration as a post-cleanup step. The script checks whether each resource exists before attempting deletion, so it can be safely rerun if interrupted.
Show details: Phase 5 script reference ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/migration/5-cleanup-bitnami.sh ```
### Measure with `--estimate` ### Rollback If the migration fails or produces unexpected results, you can roll back to the pre-cutover state: ```bash bash rollback.sh ``` This restores the previous Helm values (re-enabling Bitnami subcharts) and restarts Camunda on the original infrastructure. Rollback is available after Phase 3 (cutover) and before Phase 5 (cleanup). Before Phase 3, simply stop the migration; your Bitnami infrastructure is still active and untouched. ## Operational readiness Before running this migration in production, use the checklist below to reduce risk, especially where network policy and external service access add complexity. If you are using the migration scripts, also consult the [downtime estimation](./bitnami-to-operators.md#downtime-estimation), [migration hooks](./index.md#migration-hooks), and [troubleshooting](./bitnami-to-operators.md#troubleshooting) sections in the operator-based guide — they apply equally to external targets. ### Staging rehearsal 1. **Provision staging managed services** that mirror your production setup—same cloud provider, same region, and same tier/SKU. 2. **Run the full migration end to end** in staging, including all five phases: deploy, backup, cutover, validate, and cleanup. 3. **Measure actual timings**: record how long each phase takes. Network latency to external services, such as RDS, Cloud SQL, and Elasticsearch, may increase backup and restore times compared to in-cluster operators. 4. **Test rollback**: after a successful staging migration, run `bash rollback.sh` to verify the Helm values revert correctly and Camunda reconnects to the Bitnami subcharts. :::tip When staging with managed services, use the same authentication method (IAM, managed identity, workload identity) that you plan to use in production. Password-based staging does not catch permission issues. ::: ### Production dry-run Pay special attention to: - External endpoints and port configurations. - Kubernetes Secret names and keys referenced in the Helm values. - Network connectivity from the cluster to the managed services (security groups, private endpoints, firewall rules). ### Pre-migration checklist Before starting the migration in production: - **Verify managed service connectivity**: from within the cluster, confirm you can connect to each managed service endpoint using `kubectl run` with a temporary client pod. - **Notify stakeholders**: announce the maintenance window at least 48 hours in advance. Include expected start time, duration (measured in staging), and impact on end users. - **Verify independent backups**: confirm a recent backup exists via both your cluster backup tool (Velero, snapshots) and the cloud provider's managed service backup (RDS snapshots, automated backups). - **Check IAM permissions**: ensure the Kubernetes service account has the correct role bindings for the managed services (IRSA for AWS, Workload Identity for GCP, Managed Identity for Azure). - **Monitor readiness**: have dashboards open for cluster health, managed service metrics (CPU, connections, storage), and pod status. ### Failback procedure 1. **Immediate failback** (Bitnami PVCs still exist): run `bash rollback.sh` to revert the Helm values. 2. **Late failback** (Bitnami PVCs deleted): restore from the backup taken during Phase 2. If your managed service has point-in-time recovery (PITR), you can also restore from a managed service snapshot, but note that Camunda would need to be reconfigured to point back to the Bitnami infrastructure. ### Data safety measures - All `pg_dump` backups are stored on a dedicated PVC that persists independently. - Managed services typically offer their own automated backups (RDS snapshots, Cloud SQL backups). Verify these are enabled and have adequate retention. - The migration scripts are idempotent and can be rerun safely. - No Bitnami resources are deleted during migration. They must be explicitly removed afterward. ### Post-migration monitoring After completing the migration, monitor the following for at least 48 hours: - **Pod restarts**: `kubectl get pods -n ${NAMESPACE} --watch` - **Managed service metrics**: check connection counts, latency, CPU, and storage usage in your cloud provider console. - **Camunda component logs**: look for connection timeouts, SSL/TLS handshake errors, or authentication failures. - **Process instance completion**: verify that in-flight process instances continue to execute correctly. - **Zeebe export lag**: confirm that Zeebe exporters are writing to the external Elasticsearch target without delays. ## Alternative: manual Elasticsearch migration {#alternative-manual-elasticsearch-migration} If you cannot use the automated migration scripts (for example, because `reindex.remote.whitelist` cannot be configured on the managed target), you can transfer Elasticsearch data manually using one of the methods below. :::warning Verify that aliases and templates are migrated Many manual Elasticsearch migration methods copy index data by default, but do **not automatically preserve or recreate** everything Camunda components rely on, especially index aliases and index templates. Whether aliases or templates are migrated depends on the method and options used. For example, snapshot/restore typically restores index aliases, while templates generally require restoring global state, which may have additional side effects. Missing aliases or templates can prevent components from applying their schema, causing pod startup failures. The automated migration scripts in the [deployment references repository](https://github.com/camunda/camunda-deployment-references/tree/main/generic/kubernetes/migration) handle aliases, templates, and indices together. Only fall back to a manual method if the automated path is not feasible for your environment, and verify that aliases and templates are handled explicitly. ::: The examples below search for the default Camunda index prefixes (`zeebe-*`, `operate-*`, `tasklist-*`, `optimize-*`, `connectors-*`, `camunda-*`). If your installation uses [custom index prefixes](/self-managed/deployment/helm/configure/database/elasticsearch/configure-elasticsearch-prefix-indices.md#index-prefix-configuration), replace these patterns with your actual prefixes. Use the [`elasticdump`](https://github.com/elasticsearch-dump/elasticsearch-dump) npm tool to transfer indices from source to target. For example: ```bash # Install elasticdump npm install -g elasticdump # Get source ES password SOURCE_ES_PWD=$(kubectl get secret ${CAMUNDA_RELEASE_NAME}-elasticsearch \ -n ${NAMESPACE} -o jsonpath='{.data.elasticsearch-password}' | base64 -d) # Port-forward source ES kubectl port-forward svc/${CAMUNDA_RELEASE_NAME}-elasticsearch -n ${NAMESPACE} 9200:9200 & # Dump and restore each index pattern for pattern in zeebe operate tasklist optimize connectors camunda; do elasticdump \ --input="http://elastic:${SOURCE_ES_PWD}@localhost:9200/${pattern}-*" \ --output="https://elastic:@your-elastic-endpoint.example.com:443/${pattern}-*" \ --type=data \ --limit=1000 done ``` If both source and target elasticsearch support Amazon S3 snapshot repositories, you can use a shared S3 bucket. For example: ```bash # Register an S3 snapshot repository on the source Bitnami Elasticsearch, and create a snapshot curl -X PUT "localhost:9200/_snapshot/s3_backup" \ -H 'Content-Type: application/json' \ -d '{"type":"s3","settings":{"bucket":"my-migration-bucket","region":"us-east-1"}}' curl -X PUT "localhost:9200/_snapshot/s3_backup/migration?wait_for_completion=true" \ -H 'Content-Type: application/json' \ -d '{"indices":"*","ignore_unavailable":true}' # Register the same S3 repository on the target managed Elasticsearch, and restore the snapshot curl -X PUT "https://target-endpoint/_snapshot/s3_backup" \ -H 'Content-Type: application/json' \ -d '{"type":"s3","settings":{"bucket":"my-migration-bucket","region":"us-east-1"}}' curl -X POST "https://target-endpoint/_snapshot/s3_backup/migration/_restore" \ -H 'Content-Type: application/json' \ -d '{"indices":"*","ignore_unavailable":true}' ``` Use the Elasticsearch [reindex API](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-reindex.html) to copy data from the source to the target. This requires the target to allowlist the source as a remote. Reindex each concrete Camunda index individually rather than using a single wildcard destination. To stay aligned with the migration scripts, include `zeebe-*`, `operate-*`, `tasklist-*`, `optimize-*`, `connectors-*`, and `camunda-*` indices. For example: ```bash # On the target, add source to reindex.remote.allowlist. # Then, iterate over each Camunda index you want to copy. for idx in $(curl -s -u "elastic:" \ "http://source-es:9200/_cat/indices/zeebe-*,operate-*,tasklist-*,optimize-*,connectors-*,camunda-*?h=index"); do curl -X POST "https://target-endpoint/_reindex?wait_for_completion=true" \ -H 'Content-Type: application/json' \ -d '{ "source": { "remote": { "host": "http://source-es:9200", "username": "elastic", "password": "" }, "index": "'${idx}'" }, "dest": { "index": "'${idx}'" } }' done ``` Review the source index list before running the loop. If your deployment uses custom index prefixes, include those prefixes in the `_cat/indices` query. --- ## Migrate from Bitnami subcharts to Kubernetes operators Migrate a Camunda 8 Helm installation from Bitnami-managed infrastructure (PostgreSQL, Elasticsearch, and Keycloak) to **Kubernetes operator-managed equivalents**: - **[CloudNativePG](https://cloudnative-pg.io/)** for PostgreSQL - **[Elastic Cloud on Kubernetes (ECK)](https://www.elastic.co/guide/en/cloud-on-k8s/current/index.html)** for Elasticsearch - **[Keycloak Operator](https://www.keycloak.org/operator/installation)** for Keycloak After migration, your setup will be aligned with the [operator-based reference architecture](/self-managed/deployment/helm/configure/operator-based-infrastructure.md). ![Initial cluster state before migration from Bitnami subcharts to Kubernetes operators](./img/bitnami-migration-initial-state.jpg) ## When to use this guide This guide is intended for customers running Camunda 8 with Bitnami subcharts enabled. If your installation already uses external databases, managed services, or operator-managed infrastructure, you do not need to migrate from Bitnami subcharts. Read the [topic overview](./index.md#why-migrate) to learn why you should migrate. ## Choose your migration strategy ## Prerequisites Before starting the migration, ensure you have the following [general prerequisites](./index.md#prerequisites-all-paths): Additionally, the migration scripts require: - [`envsubst`](https://www.man7.org/linux/man-pages/man1/envsubst.1.html) available (usually included in `gettext`) - [`jq`](https://jqlang.github.io/jq/download/) installed - [`yq`](https://github.com/mikefarah/yq) installed (for selective CloudNativePG cluster deployment) - `base64` and `openssl` available (used for credential management) For the tool versions used and tested, check the [.tool-versions](https://github.com/camunda/camunda-deployment-references/blob/main/.tool-versions) file. ## Precautions Review the [general precautions](./index.md#precautions) that apply to all migration paths. :::tip Before running in production Review the [operational readiness](#operational-readiness) checklist, including the staging rehearsal and pre-migration checklist, before starting a production migration. ::: ### Operator-specific precautions These precautions are specific to the operator-based migration: - **Monitor resource quotas:** CNPG and ECK clusters consume additional resources. Ensure your namespace quotas and node capacity allow for the temporary duplication. - **Elasticsearch `reindex.remote.whitelist`:** The target ECK cluster must have `reindex.remote.whitelist` configured to allow pulling data from the source Bitnami Elasticsearch via the `_reindex` API. The migration scripts patch this automatically. - **Keycloak hooks:** If you use a DNS CNAME for Keycloak, use the `hooks/post-phase-3.sh` hook to update the DNS target to the new Keycloak Operator service after cutover. ## Clone the deployment references repository ## Step 1: Configure the migration Edit `env.sh` to match your current Camunda installation:
Show details: `env.sh` reference ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/migration/env.sh ```
### Key configuration variables | Variable | Default | Description | | ---------------------------- | ---------------------- | --------------------------------------------------------------------------------------------------------- | | `NAMESPACE` | `camunda` | Kubernetes namespace of your Camunda installation | | `CAMUNDA_RELEASE_NAME` | `camunda` | Helm release name | | `CAMUNDA_HELM_CHART_VERSION` | (chart version) | Target Helm chart version for the upgrade | | `CAMUNDA_DOMAIN` | (empty) | Domain for Keycloak Ingress. Leave empty for port-forward setups | | `IDENTITY_DB_NAME` | `identity` | Identity database name (must match the source installation) | | `IDENTITY_DB_USER` | `identity` | Identity database user (must match the source installation) | | `KEYCLOAK_DB_NAME` | `keycloak` | Keycloak database name (must match the source installation) | | `KEYCLOAK_DB_USER` | `keycloak` | Keycloak database user (must match the source installation) | | `WEBMODELER_DB_NAME` | `webmodeler` | Web Modeler database name (must match the source installation) | | `WEBMODELER_DB_USER` | `webmodeler` | Web Modeler database user (must match the source installation) | | `BACKUP_PVC` | `migration-backup-pvc` | PVC name for storing backup data | | `BACKUP_STORAGE_SIZE` | `50Gi` | Backup PVC size (must fit all database dumps) | | `MIGRATE_IDENTITY` | `true` | Enables the Identity PostgreSQL database migration | | `MIGRATE_KEYCLOAK` | `true` | Enables the Keycloak and its PostgreSQL database migration | | `MIGRATE_WEBMODELER` | `true` | Enables the Web Modeler PostgreSQL database migration | | `MIGRATE_ELASTICSEARCH` | `true` | Enables the Elasticsearch data migration | | `ES_WARM_REINDEX` | `false` | When `true`, pre-copies ES data during Phase 2 (no downtime), reducing Phase 3 to a ~5 minute delta sync. | Set any `MIGRATE_*` variable to `false` to skip a component. This is useful, for example, if the component isn't deployed or already uses an external service. ### Operator-specific variables These variables control the operator deployments. Defaults work for most setups: | Variable | Default | Description | | ------------------------- | ---------------- | ---------------------------------------- | | `CNPG_OPERATOR_NAMESPACE` | `cnpg-system` | Namespace for the CloudNativePG operator | | `ECK_OPERATOR_NAMESPACE` | `elastic-system` | Namespace for the ECK operator | | `CNPG_IDENTITY_CLUSTER` | `pg-identity` | CNPG cluster name for Identity | | `CNPG_KEYCLOAK_CLUSTER` | `pg-keycloak` | CNPG cluster name for Keycloak | | `CNPG_WEBMODELER_CLUSTER` | `pg-webmodeler` | CNPG cluster name for Web Modeler | | `ECK_CLUSTER_NAME` | `elasticsearch` | ECK Elasticsearch cluster name | ### Source `env.sh` Once you've configured the environment variables, source the file: ```bash source env.sh ``` ## Step 2: Customize operator manifests Before running the migration, **you must review and customize** the operator-based manifests to match your production requirements. The migration deploys operators and instances using these manifests. The default settings may not be appropriate for your workload. Follow this guidance while reviewing the manifests: | Component | Must review before production | Defaults may be acceptable for | | ------------- | -------------------------------------------------------------------------- | ------------------------------------------------------- | | PostgreSQL | Storage size, replica count, CPU and memory, connection-related parameters | Short-lived staging rehearsals with representative data | | Elasticsearch | Node count, storage size, JVM and resource limits | Dry runs where you only validate the workflow | | Keycloak | Hostname, Ingress or route mode, replica count, resource limits | Non-production validation only | If you are rehearsing the migration for the first time, keep the manifests simple but ensure storage is at least as large as the existing Bitnami volumes. Before production, revisit the sizing based on the timings and load observed during rehearsal. ### PostgreSQL (CloudNativePG) Review the CloudNativePG (CNPG) cluster specifications in `operator-based/postgresql/postgresql-clusters.yml`. Key settings to verify: - Storage size (must be >= your current Bitnami PVC sizes) - Number of replicas - PostgreSQL version - Resource requests and limits - PostgreSQL parameters (for example, `shared_buffers` and `max_connections`)
Show details: CloudNativePG manifest reference ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/postgresql/postgresql-clusters.yml ```
### Elasticsearch (ECK) The migration patches the reference ECK cluster manifest from `operator-based/elasticsearch/elasticsearch-cluster.yml` at runtime to add `reindex.remote.whitelist` support for data transfer via the `_reindex` API. Review the base manifest: - Node count - Storage size (must be >= your current Bitnami ES PVC size) - Resource requests and limits
Show details: Elasticsearch manifest reference ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/elasticsearch/elasticsearch-cluster.yml ```
### Keycloak Review the Keycloak Custom Resource in `operator-based/keycloak/`. For the broader deployment context and Helm values layering, see [operator-based infrastructure](/self-managed/deployment/helm/configure/operator-based-infrastructure.md#keycloak-deployment). Choose the appropriate variant: - [`keycloak-instance-domain-nginx.yml`](https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/keycloak/keycloak-instance-domain-nginx.yml) — if you have a domain with nginx Ingress - [`keycloak-instance-domain-openshift.yml`](https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/keycloak/keycloak-instance-domain-openshift.yml) — for OpenShift deployments with Routes - [`keycloak-instance-no-domain.yml`](https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/operator-based/keycloak/keycloak-instance-no-domain.yml) — for port-forward setups Key settings to verify: - Replicas - Resource limits - Hostname configuration :::info Keycloak 26 hostname configuration The Keycloak Custom Resource uses the v2 hostname provider (Keycloak 25+). The `hostname` field must include the full URL with scheme and path — for example, `https://your-domain.example.com/auth`. This ensures that the OIDC issuer URL is consistent and includes the `/auth` path prefix used by `http-relative-path`. The v1 hostname provider (Keycloak 24 and earlier) is not compatible with these manifests. ::: ## Step 3: Run the migration The migration follows five sequential phases. Each phase is idempotent and can, therefore, be rerun safely. ### Phase 1: Deploy target infrastructure (no downtime) ![Illustration of Phase 1: deploy the operator-managed target infrastructure alongside the Bitnami components](./img/bitnami-migration-phase-1-deploy-targets.jpg) This phase installs the Kubernetes operators and creates the target clusters alongside your existing Bitnami components. Your application continues to run normally: ```bash bash 1-deploy-targets.sh ``` What happens: 1. The script displays a customization warning and asks for confirmation. 2. It validates target resource allocations (CPU, memory, and PVC sizes) against your current Bitnami StatefulSets. 3. It installs the **CloudNativePG operator** and creates PostgreSQL clusters for each component. 4. It installs the **ECK operator** and creates an Elasticsearch cluster with `reindex.remote.whitelist` configured for data migration via the `_reindex` API. 5. It installs the **Keycloak Operator** and creates the Keycloak Custom Resource. The script only deploys operators for components that are being migrated. For example, if `MIGRATE_ELASTICSEARCH=false`, the ECK operator is not installed. All targets are created empty; no traffic is routed to them yet.
Show details: Phase 1 script reference ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/migration/1-deploy-targets.sh ```
### Phase 2: Initial backup (no downtime) ![Illustration of Phase 2: take the initial backup while the source platform is still running](./img/bitnami-migration-phase-2-initial-backup.jpg) ![Illustration of Phase 2 with warm reindex: take the initial backup and pre-copy Elasticsearch data to the target](./img/bitnami-migration-phase-2-warm-reindex.jpg) This phase takes a backup of all data sources while the application is still running. This reduces the cutover window in Phase 3. ```bash bash 2-backup.sh ``` What happens: 1. **PostgreSQL**: A `pg_dump` Kubernetes Job is created for each component (Identity, Keycloak, and Web Modeler). 2. **Elasticsearch (ES)**: A verification job checks source ES health and lists all Camunda indices to be migrated. 3. All backup data is stored on a shared Persistent Volume Claim (PVC). What happens: 1. **PostgreSQL**: A `pg_dump` Kubernetes Job is created for each component (Identity, Keycloak, and Web Modeler). 2. **Elasticsearch (ES)**: A verification job checks source ES health and lists all Camunda indices to be migrated. 3. **Elasticsearch warm reindex**: A full reindex from the source Bitnami ES to the target is performed while the application is still running. This pre-populates the target with all existing data so Phase 3 only needs a fast delta reindex. The warm reindex may take a significant amount of time depending on your data volume, but it runs **without any downtime**. 4. All backup data is stored on a shared Persistent Volume Claim (PVC). Reference templates used in this phase:
Show details: PostgreSQL backup job template ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/migration/jobs/pg-backup.job.yml ```
Show details: Elasticsearch verification job template ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/migration/jobs/es-backup.job.yml ```
Show details: Phase 2 script reference ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/migration/2-backup.sh ```
### Phase 3: Cutover (downtime required) ![Illustration of Phase 3: stop traffic, restore the data, and switch Camunda to the new backends](./img/bitnami-migration-phase-3-cutover.jpg) ![Illustration of Phase 3 with warm reindex: stop traffic, delta reindex, and switch Camunda to the new backends](./img/bitnami-migration-phase-3-cutover-warm-reindex.jpg) :::warning Maintenance window required This is the only phase that causes downtime. Schedule a maintenance window before proceeding. Downtime typically lasts **5–60 minutes**, depending on Elasticsearch data volume. See [downtime estimation](#downtime-estimation) for benchmarked timings. With `ES_WARM_REINDEX=true`, downtime is reduced to **~5 minutes** regardless of Elasticsearch data volume. Phase 3 only syncs the delta written since the warm reindex in Phase 2. ::: :::tip Measure downtime before the real cutover You can run `bash 3-cutover.sh --estimate` to measure the actual cutover duration on your environment **without causing any downtime**. This runs the real data operations (PG backup/restore and ES reindex) against the target infrastructure but skips freezing the application and the Helm upgrade. See [Measure with `--estimate`](#measure-with---estimate) for details. ::: ```bash bash 3-cutover.sh ``` What happens: 1. **Save** current Helm values for rollback. 2. **Freeze** all Camunda deployments and StatefulSets (scale to zero replicas). 3. **Final backup** — consistent backup with no active connections to ensure data integrity. 4. **Restore** data to the new operator-managed targets: - `pg_restore` to CNPG clusters for each PostgreSQL database. - Elasticsearch **full reindex from remote** — all indices are copied from the source Bitnami ES to the ECK cluster using the `_reindex` API. This is the dominant factor in downtime duration. 5. **Sync Keycloak admin credentials** — copies the restored admin password to the Keycloak Operator secret so Keycloak and Identity stay in sync. 6. **Helm upgrade** — reconfigures Camunda to use the new backends and restarts all components. What happens: 1. **Save** current Helm values for rollback. 2. **Freeze** all Camunda deployments and StatefulSets (scale to zero replicas). 3. **Final backup** — consistent backup with no active connections to ensure data integrity. 4. **Restore** data to the new operator-managed targets: - `pg_restore` to CNPG clusters for each PostgreSQL database. - Elasticsearch **delta reindex** — only documents written between Phase 2 (warm reindex) and the freeze are synced. This uses `version_type=external` with `conflicts=proceed` to skip documents already present on the target, making it dramatically faster than a full reindex. 5. **Sync Keycloak admin credentials** — copies the restored admin password to the Keycloak Operator secret so Keycloak and Identity stay in sync. 6. **Helm upgrade** — reconfigures Camunda to use the new backends and restarts all components. Reference templates used in this phase:
Show details: PostgreSQL restore job template ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/migration/jobs/pg-restore.job.yml ```
Show details: Elasticsearch restore job template ```yaml reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/migration/jobs/es-restore.job.yml ```
Show details: Phase 3 script reference ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/migration/3-cutover.sh ```
### Phase 4: Validate (no downtime) ![Illustration of Phase 4: validate the operator-managed platform after cutover](./img/bitnami-migration-phase-4-validate.jpg) ```bash bash 4-validate.sh ``` This phase verifies all components are healthy: - All Camunda deployments and StatefulSets are ready. - CNPG PostgreSQL clusters report a healthy state. - ECK Elasticsearch cluster is in `Ready` phase with restored indices. - Keycloak Custom Resource is ready. - A migration report is generated at `.state/migration-report.md`.
Show details: Phase 4 script reference ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/migration/4-validate.sh ```
:::warning Wait before cleanup Do not move on to the next phase immediately after validation. Operate with the new infrastructure through at least one full business cycle (for example, a complete weekday with peak traffic) to confirm stability. Once Bitnami resources are deleted, rollback is no longer possible without restoring from backup. If you need to fail back, run `bash rollback.sh` **before** this phase (see [rollback](#rollback)). ::: ### Phase 5: Cleanup Bitnami resources (no downtime) ![Illustration of Phase 5: remove the old Bitnami resources after the new platform is stable](./img/bitnami-migration-phase-5-cleanup.jpg) :::warning Destructive and irreversible This phase **permanently deletes** old Bitnami StatefulSets, PVCs, and the migration backup PVC. After cleanup, rollback to Bitnami subcharts is **no longer possible**. Before running this phase, strongly consider: 1. Taking a full backup of all databases (`pg_dumpall` or equivalent) 2. Taking PVC or storage volume snapshots (cloud provider snapshots) 3. Storing backups in cold storage—for example, S3 Glacier or GCS Archive 4. Keeping rollback artifacts in `.state/` as a safety net ::: After confirming the migration is successful, remove old Bitnami StatefulSets, PVCs, services, and the migration backup PVC: ```bash bash 5-cleanup-bitnami.sh ``` What happens: 1. The script requires Phase 4 to be completed and displays a **destructive operation warning** with a confirmation prompt. 2. **Deletes old Bitnami PostgreSQL** StatefulSets, their PVCs, and headless services (for each migrated component: Identity, Keycloak, and Web Modeler). 3. **Deletes old Bitnami Elasticsearch** StatefulSet, PVCs, and services. 4. **Deletes old Bitnami Keycloak** StatefulSet. 5. **Deletes the migration backup PVC**. 6. **Reverifies** that all Camunda components and operator-managed targets remain healthy after cleanup. 7. Suggests removing the `reindex.remote.whitelist` setting from the ECK Elasticsearch configuration as a post-cleanup step. The script checks whether each resource exists before attempting deletion, so it can be safely rerun if interrupted.
Show details: Phase 5 script reference ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/migration/5-cleanup-bitnami.sh ```
## Migration hooks The migration scripts support custom hooks that run before or after each phase. See [migration hooks](./index.md#migration-hooks) for the full reference and examples. ## Rollback If the migration fails or produces unexpected results, you can roll back to the pre-cutover state: ```bash bash rollback.sh ``` This restores the previous Helm values (re-enabling Bitnami subcharts) and restarts Camunda on the original infrastructure. The operator-managed resources (CNPG clusters, ECK, and Keycloak Custom Resource) are **not deleted**, allowing you to retry or debug.
Show details: Rollback script reference ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/migration/rollback.sh ```
Rollback is available after Phase 3 (cutover). Before that, simply stop the migration; your Bitnami infrastructure is still active and untouched. ## Downtime estimation Only Phase 3 (cutover) causes downtime. The estimates below were measured on minimal Kubernetes clusters with standard storage. **Production clusters with faster storage and networking will perform significantly better**. Always run a [staging rehearsal](#staging-rehearsal) with representative data volumes to measure your actual downtime. ### Reference timings The following timings were observed migrating a Camunda 8 installation with all components (Identity, Keycloak, Web Modeler, and Elasticsearch): | Data profile | ES data | PG data (3 databases) | Observed downtime | | ---------------------------- | -------- | --------------------- | ----------------- | | Minimal (fresh install) | < 100 MB | ~30 MB | **~4 min** | | Large (~6.5 million ES docs) | ~9 GB | ~30 MB | **~40 min** | ### Phase 3 breakdown | Step | Duration | Notes | | ----------------------------- | ----------- | --------------------------------------------------------------------------------- | | Freeze components (scale → 0) | ~10 s | Scale down all deployments and StatefulSets | | PostgreSQL backup + restore | ~40 s | `pg_dump` / `pg_restore` for all databases (usually negligible at moderate sizes) | | **Elasticsearch reindex** | **~38 min** | **(Dominant factor)** Copies all indices via the `_reindex` API | | Helm upgrade + restart | ~2 min | Reconfigure backends and restart all components | ### Estimates by Elasticsearch data volume | ES Data Volume | Estimated Downtime | Bottleneck | | -------------- | ------------------- | -------------------------- | | < 1 GB | ~5 minutes | Helm upgrade + pod startup | | 1–10 GB | ~10–40 minutes | ES reindex | | 10–50 GB | ~40 minutes–2 hours | ES reindex | | > 50 GB | 2+ hours | ES reindex | ### Key observations - **Elasticsearch reindex dominates downtime.** With ~9 GB of ES data, the reindex step accounts for ~95% of the total cutover time. PostgreSQL backup and restore completes in under a minute regardless of reasonable data sizes. - **Downtime scales linearly with ES data volume.** The largest indices — such as Optimize process instance history — drive the overall duration. - **Your cluster will likely be faster.** These timings were measured on constrained test infrastructure. Production clusters with NVMe storage, dedicated nodes, and higher network bandwidth typically achieve much higher reindex throughput. - **Always measure in staging.** Run the full migration on a staging environment with representative data volumes to get an accurate downtime estimate for your specific setup. ### Reference timings With `ES_WARM_REINDEX=true`, the bulk of the Elasticsearch data transfer happens during Phase 2 (no downtime). Phase 3 only needs a fast delta reindex to sync documents written between Phase 2 and the freeze: | Data profile | ES data | PG data (3 databases) | Observed downtime | | ---------------------------- | -------- | --------------------- | ----------------------- | | Minimal (fresh install) | < 100 MB | ~30 MB | **~4 min** | | Large (~6.5 million ES docs) | ~9 GB | ~30 MB | **~5 min** (delta only) | ### Phase 3 breakdown (warm reindex) | Step | Duration | Notes | | ----------------------------- | ------------ | ------------------------------------------------------------------- | | Freeze components (scale → 0) | ~10 s | Scale down all deployments and StatefulSets | | PostgreSQL backup + restore | ~40 s | `pg_dump` / `pg_restore` for all databases | | **ES delta reindex** | **~1–2 min** | Only documents written after Phase 2 warm reindex need to be synced | | Helm upgrade + restart | ~2 min | Reconfigure backends and restart all components | ### Estimates by Elasticsearch data volume | ES Data Volume | Standard downtime | Warm reindex downtime | Notes | | -------------- | ------------------- | --------------------- | --------------------------------------- | | < 1 GB | ~5 minutes | ~5 minutes | No significant benefit at small volumes | | 1–10 GB | ~10–40 minutes | **~5 minutes** | Warm reindex eliminates the bottleneck | | 10–50 GB | ~40 minutes–2 hours | **~5 minutes** | Most impactful reduction | | > 50 GB | 2+ hours | **~5 minutes** | Critical for large data volumes | :::info Key observations - **ES delta reindex is nearly instant.** After the warm reindex pre-copies all existing data in Phase 2, only new documents created between Phase 2 and the freeze need to be synced. The delta reindex uses `version_type=external` and `conflicts=proceed` to efficiently skip up-to-date documents. - **Downtime becomes data-volume independent.** The dominant factor (ES reindex) is removed from the critical path. Downtime is determined by the freeze, PG restore, and Helm upgrade steps (~5 minutes total). - **Phase 2 takes longer.** The warm reindex adds runtime to Phase 2 proportional to your ES data volume, but this runs without any downtime. ::: ### Measure with `--estimate` ## Operational readiness Before running this migration in production, use the checklist below to reduce risk and confirm the cutover plan is ready. ### Staging rehearsal 1. **Clone your production environment** to a staging cluster with the same Helm chart version, same component configuration, and comparable data volumes. 2. **Run the full migration end to end** in staging, including all five phases: deploy, backup, cutover, validate, and cleanup. 3. **Measure actual timings**: record how long each phase takes, especially the `3-cutover.sh` phase, as it determines your downtime window. The [benchmarked timings](#downtime-estimation) show that Elasticsearch reindex dominates. Expect downtime to scale linearly with your ES data volume. 4. **Test rollback**: after a successful staging migration, intentionally run `bash rollback.sh` to verify you can revert cleanly. :::tip Use a representative data set; empty databases migrate in seconds but do not reveal the Elasticsearch reindex bottleneck that large datasets will. As a reference, ~9 GB of ES data takes ~40 min on minimal test infrastructure, whereas production clusters with faster storage and networking will perform significantly better. ::: ### Production dry-run Review the output carefully. Ensure that all Kubernetes resources, secrets, and Helm values match your expectations before removing `--dry-run`. ### Pre-migration checklist Before starting the migration in production: - **Notify stakeholders**: announce the maintenance window at least 48 hours in advance. Include expected start time, duration (measured in staging), and impact on end users. - **Verify backups**: confirm your existing backup strategy (Velero, volume snapshots, or cloud provider backups) has a recent successful backup. The migration creates its own backup, but an independent one provides an additional safety net. - **Scale down non-essential consumers**: if you have external systems consuming Camunda APIs, consider pausing them during the freeze window to prevent data inconsistencies. - **Check cluster resources**: ensure the cluster has enough CPU, memory, and storage to run both old and new infrastructure simultaneously during the migration—both exist briefly. - **Review `env.sh`**: double-check all variables, especially `NAMESPACE`, `CAMUNDA_RELEASE_NAME`, `PG_TARGET_MODE`, and `ES_TARGET_MODE`. - **Monitor readiness**: have dashboards open for cluster health, pod status, and storage capacity. ### Failback procedure If the migration succeeds but you discover issues in the hours or days following: 1. **Immediate failback** (before Phase 5 when Bitnami PVCs still exist): run `bash rollback.sh` to revert the Helm values and re-attach to the original Bitnami StatefulSets. 2. **Late failback** (after Phase 5 when Bitnami PVCs have been deleted): restore from the backup taken during Phase 2 or from your independent backup. ### Data safety measures - All `pg_dump` backups are stored on a dedicated PVC (`migration-backup-pvc`) that persists independently of the migration. - Elasticsearch snapshots are stored in a registered repository and retained according to the configured retention policy. - The migration scripts are **idempotent**: rerunning a phase that was interrupted picks up where it left off. - No Bitnami resources are deleted during Phases 1–4; they're only disconnected from the Helm release. Phase 5 explicitly removes them after validation. ### Post-migration monitoring After completing the migration, monitor the following for at least 48 hours: - **Pod restarts**: `kubectl get pods -n ${NAMESPACE} --watch` - **CNPG cluster health**: `kubectl get clusters -n ${NAMESPACE}` (should show `Cluster in healthy state`) - **ECK cluster health**: `kubectl get elasticsearch -n ${NAMESPACE}` (should show `green`) - **Camunda component logs**: check for connection errors, authentication failures, or data inconsistencies. - **Process instance completion**: verify that in-flight process instances continue to execute correctly. - **Zeebe export lag**: confirm that Zeebe exporters are writing to the new Elasticsearch without delays. ## Troubleshooting ### A migration job fails Check the job logs for details: ```bash # List migration jobs kubectl get jobs -n ${NAMESPACE} -l migration.camunda.io/type # View logs for a specific job kubectl logs -n ${NAMESPACE} job/ # Describe the job for events kubectl describe job -n ${NAMESPACE} ``` Each phase is idempotent; you can rerun it after fixing the issue. ### PostgreSQL restore fails with permission errors When restoring to CNPG, the `pg_restore` command uses `--no-owner --no-privileges` flags to avoid permission mismatches. If you see errors related to ownership, verify that the target database user has the correct permissions: ```bash kubectl exec -it -n ${NAMESPACE} -- psql -U postgres -c "\\du" ``` ### Elasticsearch reindex fails The ES restore uses the `_reindex` API to pull data from the source Bitnami Elasticsearch to the target ECK cluster. Both clusters must be reachable within the same namespace. Check that the source ES is still running and accessible: ```bash # Check if source ES is reachable from the target kubectl exec -it -n ${NAMESPACE} -- \ curl -s http://${CAMUNDA_RELEASE_NAME}-elasticsearch:9200/_cluster/health ``` If the reindex fails for specific indices, check the job logs for mapping conflicts or timeout errors. You can delete the problematic indices on the target and rerun Phase 3. ### Migration status check View the current migration progress: ```bash bash 1-deploy-targets.sh --status ``` This shows which phases have been completed and their timestamps. ### State tracking The scripts maintain migration state in `.state/migration.env`, a plain key-value file that records phase completion timestamps and deployment decisions. Each run appends to `.state/migration-YYYY-MM-DD.log`. The `.state/` directory is local and gitignored. To reset state and start over, run: ```bash rm -rf .state/ ``` --- ## Migrate from Bitnami subcharts This section provides guidance for migrating your Camunda 8 Self-Managed infrastructure components from [Bitnami subcharts](/self-managed/deployment/helm/chart-parameters.md#bitnami-subcharts) to production-grade alternatives. :::note Target audience This guide is for customers running Camunda 8 with Bitnami subcharts enabled. If your installation already uses external databases, managed services, or operator-managed infrastructure, you do not need to follow this migration. ::: ## Why migrate? Bitnami subcharts (PostgreSQL, Elasticsearch, Keycloak) provided with the Camunda Helm chart are convenient for development and testing. However, for production environments, Camunda [recommends](https://camunda.com/blog/2026/03/camunda-8-helm-chart-and-bitnami-sub-charts/) using managed services or [operator-based deployments](/self-managed/deployment/helm/configure/operator-based-infrastructure.md): - **End of open-source Bitnami images**: Bitnami has [archived open-source container images](https://github.com/bitnami/containers/issues/83267), requiring a transition to alternatives. - **Production readiness**: Operators and managed services offer automated failover, backup, monitoring, and security patching. - **Vendor support**: Operators and managed services offer dedicated support channels from infrastructure vendors (Elastic, CloudNativePG, Keycloak, AWS, Azure, GCP). - **Long-term maintainability**: Decoupling infrastructure lifecycle from the Camunda Helm chart ensures independent upgrade paths. ## What gets migrated? The migration covers all Bitnami-managed infrastructure components deployed as part of the Camunda Helm chart: | Source (Bitnami subchart) | Data | Migration method | | -------------------------------- | ---------------------------------------------- | ----------------------------------------- | | Bitnami PostgreSQL (Identity) | User data and authorizations | `pg_dump` / `pg_restore` | | Bitnami PostgreSQL (Keycloak) | Realms, users, and clients | `pg_dump` / `pg_restore` | | Bitnami PostgreSQL (Web Modeler) | Projects and diagrams | `pg_dump` / `pg_restore` | | Bitnami Elasticsearch | Zeebe, Operate, Tasklist, and Optimize indices | Reindex from remote (`_reindex` API) | | Bitnami Keycloak (StatefulSet) | Realms, users, and clients (via PostgreSQL) | Keycloak Operator CR replaces StatefulSet | :::info Camunda core components are not affected The Camunda application components themselves (Zeebe, Operate, Tasklist, Optimize, Connectors, Identity, and Web Modeler) are not migrated; they're reconfigured via a Helm upgrade to use the new infrastructure backends. Your process instances, decisions, and forms remain intact. ::: ## Migration phases The migration follows a five-phase approach designed to minimize downtime: | Phase | Downtime | Outcome | | ------------------ | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | | 1. Deploy targets | No planned downtime | Install operators and create the target infrastructure alongside Bitnami. | | 2. Initial backup | No planned downtime | Back up data while the application is still running. | | 3. Cutover | **Maintenance window**Typically 5–60 minutes (or ~5 minutes with warm reindex) | Freeze traffic, take a final backup, restore data, run the Helm upgrade, and resume. | | 4. Validate | No planned downtime | Verify that all components are healthy on the new infrastructure. | | 5. Cleanup Bitnami | No planned downtime | Remove old Bitnami StatefulSets, PVCs, and migration artifacts. | ### Downtime estimation | Elasticsearch data volume | Standard downtime | With warm reindex (`ES_WARM_REINDEX=true`) | | ------------------------- | ------------------- | ------------------------------------------ | | < 1 GB | ~5 minutes | ~5 minutes | | 1–10 GB | ~10–40 minutes | ~5 minutes | | 10–50 GB | ~40 minutes–2 hours | ~5 minutes | | > 50 GB | 2+ hours | ~5 minutes | The main downtime driver is the Elasticsearch reindex duration. With the **warm reindex** strategy, Elasticsearch data is pre-copied during Phase 2 (no downtime), reducing Phase 3 to a fast delta sync. See [downtime estimation](./bitnami-to-operators.md#downtime-estimation) for benchmarked timings. ### Migration scripts The migration phases above are automated by shell scripts maintained in the [Camunda deployment references](https://github.com/camunda/camunda-deployment-references) repository. Each migration guide includes instructions to clone the repository and configure the scripts for your environment. The scripts also support [migration hooks](#migration-hooks) for custom logic at each phase boundary. ## Precautions (all paths) {#precautions} Regardless of the migration path you choose, review the following precautions **before starting** the migration.
General precautions - **Test in staging first:** Run the full migration in a non-production environment before migrating production. - **Schedule a maintenance window:** All migration paths (except zero-downtime) require a downtime window during cutover. - **Check cluster capacity:** During the migration, both old and new infrastructure run simultaneously, requiring additional CPU, memory, and storage. - **Backup your Helm values:** Consider a manual backup before starting: `helm get values camunda -n camunda > backup-values.yaml`. - **DNS TTL:** If using a domain for Keycloak, ensure DNS TTL is low before cutover to minimize propagation delay. - **Keycloak OIDC impact:** Keycloak is the OIDC provider for all Camunda components (and possibly external applications). Migrating Keycloak changes the underlying service. If you use a DNS CNAME for Keycloak, plan to update the DNS target to the new Keycloak service after cutover. If external applications share the same Keycloak realm, coordinate the DNS switch with their teams. - **Session impact:** The database migration preserves all persistent data (realms, users, clients, signing keys, and refresh tokens). Since Keycloak 25+, user sessions are persisted in the database and survive the switch. In-flight authentication flows (login pages in progress) and pending action tokens (password reset links) are lost; users simply need to retry. This is inherent to the downtime window and has no lasting effect. - **Dual-region Elasticsearch:** There is currently no dedicated migration procedure for dual-region setups. This applies only to installations upgrading from Camunda 8.8, which was the last version to include Bitnami Elasticsearch as a default subchart. If you need to perform this migration in a dual-region environment, follow the single-region migration procedure and apply it individually to each region.
## Choose your migration target Depending on your infrastructure capabilities and organizational requirements, choose one of the following migration paths: | Scenario | Recommended path | Guide | | -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------- | | You want production-grade, self-managed infrastructure in Kubernetes with operator lifecycle management. | **Kubernetes operators** (CloudNativePG, ECK, Keycloak Operator) | [Migrate to Kubernetes operators](./bitnami-to-operators.md) | | You prefer fully managed infrastructure from your cloud provider with minimal operational overhead. | **Managed services** (AWS RDS, Elastic Cloud, Azure Database for PostgreSQL, etc.) | [Migrate to managed services](./bitnami-to-managed-services.md) | | You cannot use operators or managed services, or require full control (VMs, bare-metal, Docker Compose). | **Manual deployment** | [Advanced alternatives](./alternatives.md) | | Your SLA does not allow any maintenance window. | **Zero-downtime migration** (logical replication, CCR) | [Zero-downtime migration](./zero-downtime.md) | ## Prerequisites (all paths) Regardless of your chosen migration target, ensure the following: :::important Plan authentication and service access up front All migration paths require an explicit decision for authentication and connectivity: - If you keep **Keycloak**, plan for a Keycloak Operator deployment, and configure the hostname with the full external URL, for example `https://your-domain.example.com/auth`. - If you replace Keycloak with an [external OIDC provider](/self-managed/deployment/helm/configure/authentication-and-authorization/external-oidc-provider.md), complete that design before cutting over because Identity configuration changes are part of the migration. - If your PostgreSQL or Elasticsearch access depends on cloud-specific IAM authentication such as AWS IRSA, the provided migration jobs are not sufficient, and you need a custom migration workflow. ::: ## Migration guides ## Advanced usage ### Migration hooks {#migration-hooks} The migration scripts support **hooks** — custom shell scripts that run before or after each migration phase. Place executable scripts in the [`hooks/`](https://github.com/camunda/camunda-deployment-references/tree/main/generic/kubernetes/migration/hooks) directory after cloning the deployment references repository: | Hook | Trigger | | ------------------ | --------------------------------------- | | `pre-phase-1.sh` | Before deploying target infrastructure | | `post-phase-1.sh` | After target infrastructure is deployed | | `pre-phase-2.sh` | Before initial backup | | `post-phase-2.sh` | After initial backup | | `pre-phase-3.sh` | Before cutover (before freeze) | | `post-phase-3.sh` | After cutover is complete | | `pre-phase-4.sh` | Before validation | | `post-phase-4.sh` | After validation | | `pre-phase-5.sh` | Before Bitnami cleanup | | `post-phase-5.sh` | After Bitnami cleanup | | `pre-rollback.sh` | Before rollback | | `post-rollback.sh` | After rollback | For example, send a Slack notification before cutover: ```bash #!/bin/bash # hooks/pre-phase-3.sh curl -X POST "$SLACK_WEBHOOK" \ -H 'Content-Type: application/json' \ -d '{"text":"⚠️ Camunda migration cutover starting — downtime expected"}' ``` :::note Hook scripts are sourced (not forked), so they have access to all library functions and variables. A failing hook aborts the migration (due to `set -e`). Add `|| true` to make a hook best-effort. ::: Typical hook use cases: - Pause external consumers before Phase 3 and resume them after validation. - Send change-management or on-call notifications at the start and end of cutover. - Run smoke tests after Phase 3 or Phase 4, and fail the migration if a critical endpoint is unavailable. - Update DNS or Ingress records for Keycloak after the new service becomes active. --- ## Zero-downtime migration from Bitnami subcharts Migrate a Camunda 8 Helm installation from Bitnami-managed infrastructure to operator-managed or managed service equivalents **without planned application downtime**. Instead of the freeze-backup-restore-switch pattern used in the standard migration, this approach keeps source and target synchronized with **real-time data replication** before cutover. :::warning Advanced topic — commands provided for reference only This guide describes an **advanced migration strategy** that eliminates the downtime window present in the [standard migration](./bitnami-to-operators.md). The commands and examples in this guide are provided **for informational purposes only**. You must test them on a staging environment that mirrors your production setup before executing them in production. You are expected to familiarize yourself with the underlying concepts and write your own cutover runbook that accounts for your specific constraints, network topology, and data volumes. For most deployments, Camunda recommends the simpler [standard migration](./bitnami-to-operators.md) with a 5–60 minute maintenance window. ::: ## When to use this guide Use this guide only if all of the following are true: - You have already ruled out the [standard migration](./bitnami-to-operators.md) because even a short maintenance window is unacceptable. - Your team is comfortable operating PostgreSQL logical replication and one of the supported Elasticsearch synchronization strategies. - You can monitor replication lag and validate consistency before cutover. - You are prepared to adapt the examples to your topology, especially if the targets are managed services instead of in-cluster operators. Read the [topic overview](./index.md#why-migrate) to learn why you should migrate. ## How it works The zero-downtime migration replaces the [backup/restore phases](./index.md#migration-phases) with continuous replication: | Phase | Name | Downtime | Description | | ----- | --------------------- | -------- | --------------------------------------------------------------- | | 1 | Deploy targets | None | Install operators and create target clusters alongside Bitnami | | 2 | Enable replication | None | Set up PG logical replication and ES CCR / continuous snapshots | | 3 | Sync and verify | None | Wait for replication lag to reach 0, verify data consistency | | 4 | Instantaneous cutover | None | Helm upgrade to switch backends (rolling restart, no freeze) | | 5 | Validate and clean up | None | Verify health, tear down replication, remove old resources | ### Key differences from the standard migration | Aspect | Standard migration | Zero-downtime migration | | ------------------------------ | -------------------------------------- | ---------------------------------------------- | | Downtime | 5–60 minutes (Phase 3 freeze) | None | | Data transfer | `pg_dump`/`pg_restore` + ES `_reindex` | Logical replication + CCR/continuous snapshot | | Complexity | Low — scripted and automated | High — manual setup, monitoring required | | Risk | Low — rollback via Helm values | Medium — replication lag must be monitored | | PostgreSQL version requirement | Any | PostgreSQL 10+ (logical replication) | | Elasticsearch requirement | `_reindex` API (reindex from remote) | CCR (Platinum license) or continuous snapshots | ## Prerequisites Before starting the migration, ensure you have the following [general prerequisites](./index.md#prerequisites-all-paths): In addition to the general prerequisites: - PostgreSQL source must support **logical replication** (`wal_level = logical`). This may require a restart of the Bitnami PostgreSQL StatefulSet. - Deep understanding of your data volumes, replication lag tolerances, and network throughput between source and target. - A monitoring solution to track replication lag (for example, Prometheus, Grafana, or manual queries). Elasticsearch requires an explicit tradeoff: - Cross-cluster replication - Use if you need the closest possible parity at cutover. - This option requires an **Elastic Platinum license** - Continuous snapshots - Use if a small lag window is acceptable. - This option requires **the ability to run continuous snapshots** with very short intervals. ## Precautions Review the [general precautions](./index.md#precautions) that apply to all migration paths. Additionally, note that while this migration path removes the planned downtime window, it does not remove the need for rehearsal, monitoring, and rollback planning. Treat it as a custom migration pattern rather than a push-button alternative to the standard workflow. :::tip Before running in production Review the [operational readiness](#operational-readiness) checklist, including the staging rehearsal and pre-migration checklist, before starting a production migration. ::: ### PostgreSQL logical replication limitations - **DDL not replicated**: Schema changes (CREATE TABLE, ALTER TABLE, etc.) are not replicated. If the source schema changes during migration, you must apply the same changes to the target manually. - **Large objects**: `pg_largeobject` data is not replicated via logical replication. - **Sequences**: Sequence values are not replicated. After cutover, sequences on the target may need to be reset: ```sql -- Run on each target database after cutover SELECT setval(pg_get_serial_sequence(table_name, column_name), max(column_name)) FROM table_name; ``` - **TRUNCATE**: `TRUNCATE` is replicated only in PostgreSQL 11+. ### Elasticsearch limitations - **CCR requires Platinum license**: The open-source and Basic tiers do not include cross-cluster replication. - **Continuous snapshots have lag**: The snapshot approach introduces a replication delay equal to the snapshot interval. - **Index mapping conflicts**: If the source creates new indices during replication, they must be manually added to the CCR follow configuration. ### Keycloak considerations Keycloak data is stored in PostgreSQL, so it is covered by the PostgreSQL logical replication. The Keycloak Operator CR will start using the replicated data in the CNPG cluster after the Helm upgrade. However, be aware of Keycloak session data: - Active user sessions stored in PostgreSQL will be replicated. - In-memory Infinispan caches will be rebuilt on the new Keycloak pods. - Users may need to re-authenticate after the cutover (session cookies point to the old Keycloak pods). ### Assumed target infrastructure The commands and snippets in this guide assume **CloudNativePG (CNPG)** as the PostgreSQL target and **ECK** as the Elasticsearch target. If you are migrating to managed services (for example, AWS RDS or Elastic Cloud), replace the target hostnames, credentials, and connection methods accordingly. ## Clone the deployment references repository This guide uses scripts from the [Camunda deployment references](https://github.com/camunda/camunda-deployment-references) repository. Clone the repository and navigate to the migration directory: ```bash git clone https://github.com/camunda/camunda-deployment-references.git cd camunda-deployment-references/generic/kubernetes/migration ``` Configure the migration by editing `env.sh` to match your current Camunda installation, then source it: ```bash source env.sh ``` For a full description of configuration variables, see [configure the migration](./bitnami-to-operators.md#step-1-configure-the-migration). ## Phase 1: Deploy target infrastructure This phase is identical to Phase 1 of the [standard migration](./bitnami-to-operators.md#phase-1-deploy-target-infrastructure-no-downtime). Deploy the target operators and clusters alongside the existing Bitnami components: ```bash bash 1-deploy-targets.sh ``` After this phase, both the old Bitnami infrastructure and the new operator-managed infrastructure run side by side. No traffic is routed to the new targets yet. ## Phase 2: Enable real-time replication ### PostgreSQL: Logical replication [PostgreSQL logical replication](https://www.postgresql.org/docs/current/logical-replication.html) allows streaming changes in real time from the Bitnami PostgreSQL instances to the CNPG (or managed service) targets without stopping the source. #### Step 1: Enable logical replication on the source The source Bitnami PostgreSQL must have `wal_level = logical`. Check the current setting: ```bash kubectl exec -it ${CAMUNDA_RELEASE_NAME}-postgresql-0 -n ${NAMESPACE} -- \ psql -U postgres -c "SHOW wal_level;" ``` If it returns `replica` (the default), you need to change it: ```bash # Patch the Bitnami PostgreSQL ConfigMap or StatefulSet kubectl exec -it ${CAMUNDA_RELEASE_NAME}-postgresql-0 -n ${NAMESPACE} -- \ psql -U postgres -c "ALTER SYSTEM SET wal_level = 'logical';" ``` :::warning Restart required Changing `wal_level` requires a PostgreSQL restart. This is the **only brief interruption** in the zero-downtime approach — a PostgreSQL restart typically completes in a few seconds, and Camunda components reconnect automatically. ```bash kubectl rollout restart statefulset ${CAMUNDA_RELEASE_NAME}-postgresql -n ${NAMESPACE} kubectl rollout status statefulset ${CAMUNDA_RELEASE_NAME}-postgresql -n ${NAMESPACE} --timeout=120s ``` ::: Also ensure `max_replication_slots` and `max_wal_senders` are sufficient (at least 4 each — one per database plus overhead): ```bash kubectl exec -it ${CAMUNDA_RELEASE_NAME}-postgresql-0 -n ${NAMESPACE} -- \ psql -U postgres -c "SHOW max_replication_slots; SHOW max_wal_senders;" ``` #### Step 2: Create publications on the source For each database, create a publication that includes all tables: ```bash # Identity database kubectl exec -it ${CAMUNDA_RELEASE_NAME}-postgresql-0 -n ${NAMESPACE} -- \ psql -U postgres -d identity -c "CREATE PUBLICATION identity_migration FOR ALL TABLES;" # Keycloak database KEYCLOAK_STS="${CAMUNDA_RELEASE_NAME}-keycloak-postgresql" kubectl exec -it ${KEYCLOAK_STS}-0 -n ${NAMESPACE} -- \ psql -U postgres -d keycloak -c "CREATE PUBLICATION keycloak_migration FOR ALL TABLES;" # Web Modeler database WEBMODELER_STS="${CAMUNDA_RELEASE_NAME}-postgresql-web-modeler" kubectl exec -it ${WEBMODELER_STS}-0 -n ${NAMESPACE} -- \ psql -U postgres -d webmodeler -c "CREATE PUBLICATION webmodeler_migration FOR ALL TABLES;" ``` Depending on your Helm chart version, each component may use a separate Bitnami PostgreSQL StatefulSet or share one. Adjust the StatefulSet names accordingly. #### Step 3: Perform initial data sync Before enabling subscriptions, perform a one-time schema and data sync. Logical replication only replicates DML (INSERT/UPDATE/DELETE), not DDL (schema changes):
Show details: initial PostgreSQL sync example ```bash # For each component, dump the schema + data and restore to the target for COMPONENT in identity keycloak webmodeler; do SOURCE_STS=$(kubectl get statefulset -n ${NAMESPACE} -o name | grep -i "${COMPONENT}.*postgresql" | head -1 | sed 's|statefulset.apps/||') SOURCE_HOST="${SOURCE_STS}.${NAMESPACE}.svc.cluster.local" # Determine the target based on operator or external if [[ "$COMPONENT" == "identity" ]]; then TARGET_HOST="${CNPG_IDENTITY_CLUSTER}-rw.${NAMESPACE}.svc.cluster.local" TARGET_SECRET="${CNPG_IDENTITY_CLUSTER}-secret" elif [[ "$COMPONENT" == "keycloak" ]]; then TARGET_HOST="${CNPG_KEYCLOAK_CLUSTER}-rw.${NAMESPACE}.svc.cluster.local" TARGET_SECRET="${CNPG_KEYCLOAK_CLUSTER}-secret" elif [[ "$COMPONENT" == "webmodeler" ]]; then TARGET_HOST="${CNPG_WEBMODELER_CLUSTER}-rw.${NAMESPACE}.svc.cluster.local" TARGET_SECRET="${CNPG_WEBMODELER_CLUSTER}-secret" fi echo "Syncing ${COMPONENT}: ${SOURCE_HOST} → ${TARGET_HOST}" # Dump and restore (this is a one-time operation, not a freeze) kubectl exec -it ${SOURCE_STS}-0 -n ${NAMESPACE} -- \ pg_dump -U ${COMPONENT} -d ${COMPONENT} -F custom -f /tmp/${COMPONENT}.dump kubectl cp ${NAMESPACE}/${SOURCE_STS}-0:/tmp/${COMPONENT}.dump ./${COMPONENT}.dump # Get target password TARGET_PWD=$(kubectl get secret ${TARGET_SECRET} -n ${NAMESPACE} -o jsonpath='{.data.password}' | base64 -d) # Restore to target via a temporary pod kubectl run pg-restore-${COMPONENT} --rm -i --restart=Never \ --image=postgres:16 -n ${NAMESPACE} \ --env="PGPASSWORD=${TARGET_PWD}" -- \ pg_restore -h ${TARGET_HOST} -U ${COMPONENT} -d ${COMPONENT} \ --clean --if-exists --no-owner --no-privileges /dev/stdin < ./${COMPONENT}.dump done ```
#### Step 4: Create subscriptions on the target On each CNPG target cluster, create a subscription pointing to the source:
Show details: subscription creation example ```bash # Get source password SOURCE_PWD=$(kubectl get secret ${CAMUNDA_RELEASE_NAME}-postgresql -n ${NAMESPACE} \ -o jsonpath='{.data.postgres-password}' | base64 -d) # Identity — target the -rw service to ensure writes land on the current primary kubectl exec -it $(kubectl get pod -n ${NAMESPACE} -l cnpg.io/cluster=${CNPG_IDENTITY_CLUSTER},cnpg.io/instanceRole=primary -o jsonpath='{.items[0].metadata.name}') -n ${NAMESPACE} -- \ psql -U postgres -d identity -c " CREATE SUBSCRIPTION identity_sub CONNECTION 'host=${CAMUNDA_RELEASE_NAME}-postgresql.${NAMESPACE}.svc.cluster.local port=5432 dbname=identity user=postgres password=${SOURCE_PWD}' PUBLICATION identity_migration WITH (copy_data = false); " # Keycloak KEYCLOAK_PWD=$(kubectl get secret ${CAMUNDA_RELEASE_NAME}-keycloak-postgresql -n ${NAMESPACE} \ -o jsonpath='{.data.postgres-password}' | base64 -d) kubectl exec -it $(kubectl get pod -n ${NAMESPACE} -l cnpg.io/cluster=${CNPG_KEYCLOAK_CLUSTER},cnpg.io/instanceRole=primary -o jsonpath='{.items[0].metadata.name}') -n ${NAMESPACE} -- \ psql -U postgres -d keycloak -c " CREATE SUBSCRIPTION keycloak_sub CONNECTION 'host=${CAMUNDA_RELEASE_NAME}-keycloak-postgresql.${NAMESPACE}.svc.cluster.local port=5432 dbname=keycloak user=postgres password=${KEYCLOAK_PWD}' PUBLICATION keycloak_migration WITH (copy_data = false); " # Web Modeler WEBMODELER_PWD=$(kubectl get secret ${CAMUNDA_RELEASE_NAME}-postgresql-web-modeler -n ${NAMESPACE} \ -o jsonpath='{.data.postgres-password}' | base64 -d) kubectl exec -it $(kubectl get pod -n ${NAMESPACE} -l cnpg.io/cluster=${CNPG_WEBMODELER_CLUSTER},cnpg.io/instanceRole=primary -o jsonpath='{.items[0].metadata.name}') -n ${NAMESPACE} -- \ psql -U postgres -d webmodeler -c " CREATE SUBSCRIPTION webmodeler_sub CONNECTION 'host=${CAMUNDA_RELEASE_NAME}-postgresql-web-modeler.${NAMESPACE}.svc.cluster.local port=5432 dbname=webmodeler user=postgres password=${WEBMODELER_PWD}' PUBLICATION webmodeler_migration WITH (copy_data = false); " ```
The `copy_data = false` flag is important because we already performed the initial sync in Step 3. The subscription will now stream only new changes in real-time. ### Elasticsearch: Continuous synchronization Unlike PostgreSQL, Elasticsearch does not have a built-in logical replication feature available in the open-source version. Choose one of the following approaches: | Strategy | Best when | Tradeoff | | -------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------- | | CCR | You need the closest possible real-time replica and have an Elastic Platinum license | Highest operational complexity | | Continuous snapshots | You can tolerate a small lag window and want an open-source-compatible approach | Recent writes may be missing until re-export catches up | If you have an **Elastic Platinum license**, you can use [cross-cluster replication (CCR)](https://www.elastic.co/guide/en/elasticsearch/reference/current/ccr-overview.html) to replicate indices in real-time:
Show details: CCR setup example ```bash # Get ECK ES password ECK_PWD=$(kubectl get secret ${ECK_CLUSTER_NAME}-es-elastic-user -n ${NAMESPACE} \ -o jsonpath='{.data.elastic}' | base64 -d) # Get source ES password SOURCE_ES_PWD=$(kubectl get secret ${CAMUNDA_RELEASE_NAME}-elasticsearch -n ${NAMESPACE} \ -o jsonpath='{.data.elasticsearch-password}' | base64 -d) # Configure the target ECK cluster to recognize the source as a remote kubectl exec -it ${ECK_CLUSTER_NAME}-es-masters-0 -n ${NAMESPACE} -- \ curl -sf -u "elastic:${ECK_PWD}" -X PUT \ "http://localhost:9200/_cluster/settings" \ -H 'Content-Type: application/json' \ -d '{ "persistent": { "cluster": { "remote": { "bitnami_source": { "seeds": ["'${CAMUNDA_RELEASE_NAME}'-elasticsearch-master-0.'${CAMUNDA_RELEASE_NAME}'-elasticsearch-master-headless.'${NAMESPACE}'.svc.cluster.local:9300"] } } } } }' # Create follower indices for each Camunda index pattern for PATTERN in zeebe operate tasklist optimize connectors camunda; do # List source indices matching the pattern INDICES=$(kubectl exec -it ${CAMUNDA_RELEASE_NAME}-elasticsearch-master-0 -n ${NAMESPACE} -- \ curl -sf -u "elastic:${SOURCE_ES_PWD}" \ "http://localhost:9200/_cat/indices/${PATTERN}-*?h=index" | tr -d '[:space:]' | tr '\n' ' ') for IDX in $INDICES; do kubectl exec -it ${ECK_CLUSTER_NAME}-es-masters-0 -n ${NAMESPACE} -- \ curl -sf -u "elastic:${ECK_PWD}" -X PUT \ "http://localhost:9200/${IDX}/_ccr/follow" \ -H 'Content-Type: application/json' \ -d '{ "remote_cluster": "bitnami_source", "leader_index": "'${IDX}'" }' done done ```
If you don't have a Platinum license, use **continuous snapshot/restore with SLM (Snapshot Lifecycle Management)** to keep the target close to the source. This approach has a small replication lag (typically 5–15 minutes):
Show details: continuous snapshot setup example ```bash # Get source ES password SOURCE_ES_PWD=$(kubectl get secret ${CAMUNDA_RELEASE_NAME}-elasticsearch -n ${NAMESPACE} \ -o jsonpath='{.data.elasticsearch-password}' | base64 -d) # Register a shared snapshot repository on the source (using the backup PVC) kubectl exec -it ${CAMUNDA_RELEASE_NAME}-elasticsearch-master-0 -n ${NAMESPACE} -- \ curl -sf -u "elastic:${SOURCE_ES_PWD}" -X PUT \ "http://localhost:9200/_snapshot/migration_continuous" \ -H 'Content-Type: application/json' \ -d '{"type":"fs","settings":{"location":"/backup/elasticsearch/continuous"}}' # Create an SLM policy for frequent snapshots (every 5 minutes) kubectl exec -it ${CAMUNDA_RELEASE_NAME}-elasticsearch-master-0 -n ${NAMESPACE} -- \ curl -sf -u "elastic:${SOURCE_ES_PWD}" -X PUT \ "http://localhost:9200/_slm/policy/migration_continuous" \ -H 'Content-Type: application/json' \ -d '{ "schedule": "0 */5 * * * ?", "name": "", "repository": "migration_continuous", "config": { "indices": ["*"], "ignore_unavailable": true, "include_global_state": false }, "retention": { "expire_after": "1h", "min_count": 1, "max_count": 5 } }' ```
Before cutover, you will restore the latest snapshot to the target ECK cluster.
## Phase 3: Verify synchronization Before performing the cutover, verify that replication is caught up and data is consistent. ### Monitor PostgreSQL replication lag Check the replication lag on each subscription:
Show details: PostgreSQL lag check example ```bash # On each CNPG target, check subscription status for CLUSTER in ${CNPG_IDENTITY_CLUSTER} ${CNPG_KEYCLOAK_CLUSTER} ${CNPG_WEBMODELER_CLUSTER}; do echo "=== ${CLUSTER} ===" kubectl exec -it $(kubectl get pod -n ${NAMESPACE} -l cnpg.io/cluster=${CLUSTER},cnpg.io/instanceRole=primary -o jsonpath='{.items[0].metadata.name}') -n ${NAMESPACE} -- \ psql -U postgres -c " SELECT subname, received_lsn, latest_end_lsn, latest_end_lsn - received_lsn AS lag_bytes FROM pg_stat_subscription; " done ```
Wait until `lag_bytes` is consistently `0` or near-zero before proceeding. ### Monitor Elasticsearch sync
Show details: CCR status check example ```bash # Check CCR follower status ECK_PWD=$(kubectl get secret ${ECK_CLUSTER_NAME}-es-elastic-user -n ${NAMESPACE} \ -o jsonpath='{.data.elastic}' | base64 -d) kubectl exec -it ${ECK_CLUSTER_NAME}-es-masters-0 -n ${NAMESPACE} -- \ curl -sf -u "elastic:${ECK_PWD}" \ "http://localhost:9200/_ccr/stats" | jq '.follow_stats.indices[].shards[].leader_global_checkpoint' ```
Show details: snapshot status check example ```bash # Check the latest snapshot status SOURCE_ES_PWD=$(kubectl get secret ${CAMUNDA_RELEASE_NAME}-elasticsearch -n ${NAMESPACE} \ -o jsonpath='{.data.elasticsearch-password}' | base64 -d) kubectl exec -it ${CAMUNDA_RELEASE_NAME}-elasticsearch-master-0 -n ${NAMESPACE} -- \ curl -sf -u "elastic:${SOURCE_ES_PWD}" \ "http://localhost:9200/_slm/policy/migration_continuous" | jq '.last_success' ```
### Verify row counts Compare row counts between source and target for each database to confirm data consistency:
Show details: row count verification example ```bash for COMPONENT in identity keycloak webmodeler; do SOURCE_STS=$(kubectl get statefulset -n ${NAMESPACE} -o name | grep -i "${COMPONENT}.*postgresql" | head -1 | sed 's|statefulset.apps/||') echo "=== ${COMPONENT} ===" echo "Source:" kubectl exec -it ${SOURCE_STS}-0 -n ${NAMESPACE} -- \ psql -U ${COMPONENT} -d ${COMPONENT} -c " SELECT schemaname, relname, n_live_tup FROM pg_stat_user_tables ORDER BY n_live_tup DESC LIMIT 10; " CNPG_CLUSTER_VAR="CNPG_${COMPONENT^^}_CLUSTER" echo "Target (${!CNPG_CLUSTER_VAR}):" kubectl exec -it ${!CNPG_CLUSTER_VAR}-1 -n ${NAMESPACE} -- \ psql -U ${COMPONENT} -d ${COMPONENT} -c " SELECT schemaname, relname, n_live_tup FROM pg_stat_user_tables ORDER BY n_live_tup DESC LIMIT 10; " done ```
## Phase 4: Instantaneous cutover Once replication is confirmed in sync, perform the cutover. This phase uses a **rolling Helm upgrade** instead of the freeze-then-restore approach, resulting in zero downtime. Before starting the cutover, confirm this checklist: - PostgreSQL subscriptions show `lag_bytes` at `0` or close to `0` for a sustained interval. - Your chosen Elasticsearch sync method is healthy and up to date. - The target services are reachable from the Camunda namespace. - You have the rollback command, values, and on-call contacts ready before the Helm upgrade. ### Step 1: Stop replication (PostgreSQL) Drop the subscriptions on the target to stop replication and allow the targets to accept writes:
Show details: stop PostgreSQL replication example ```bash # Drop subscriptions — target the current primary by label selector kubectl exec -it $(kubectl get pod -n ${NAMESPACE} -l cnpg.io/cluster=${CNPG_IDENTITY_CLUSTER},cnpg.io/instanceRole=primary -o jsonpath='{.items[0].metadata.name}') -n ${NAMESPACE} -- \ psql -U postgres -d identity -c "ALTER SUBSCRIPTION identity_sub DISABLE; DROP SUBSCRIPTION identity_sub;" kubectl exec -it $(kubectl get pod -n ${NAMESPACE} -l cnpg.io/cluster=${CNPG_KEYCLOAK_CLUSTER},cnpg.io/instanceRole=primary -o jsonpath='{.items[0].metadata.name}') -n ${NAMESPACE} -- \ psql -U postgres -d keycloak -c "ALTER SUBSCRIPTION keycloak_sub DISABLE; DROP SUBSCRIPTION keycloak_sub;" kubectl exec -it $(kubectl get pod -n ${NAMESPACE} -l cnpg.io/cluster=${CNPG_WEBMODELER_CLUSTER},cnpg.io/instanceRole=primary -o jsonpath='{.items[0].metadata.name}') -n ${NAMESPACE} -- \ psql -U postgres -d webmodeler -c "ALTER SUBSCRIPTION webmodeler_sub DISABLE; DROP SUBSCRIPTION webmodeler_sub;" ```
### Step 2: Stop Elasticsearch replication Promote follower indices to regular indices:
Show details: CCR cutover example ```bash ECK_PWD=$(kubectl get secret ${ECK_CLUSTER_NAME}-es-elastic-user -n ${NAMESPACE} \ -o jsonpath='{.data.elastic}' | base64 -d) # Pause and unfollow each replicated index INDICES=$(kubectl exec -it ${ECK_CLUSTER_NAME}-es-masters-0 -n ${NAMESPACE} -- \ curl -sf -u "elastic:${ECK_PWD}" "http://localhost:9200/_cat/indices?h=index" | grep -E "^(zeebe|operate|tasklist|optimize)-") for IDX in $INDICES; do kubectl exec -it ${ECK_CLUSTER_NAME}-es-masters-0 -n ${NAMESPACE} -- \ curl -sf -u "elastic:${ECK_PWD}" -X POST "http://localhost:9200/${IDX}/_ccr/pause_follow" kubectl exec -it ${ECK_CLUSTER_NAME}-es-masters-0 -n ${NAMESPACE} -- \ curl -sf -u "elastic:${ECK_PWD}" -X POST "http://localhost:9200/${IDX}/_close" kubectl exec -it ${ECK_CLUSTER_NAME}-es-masters-0 -n ${NAMESPACE} -- \ curl -sf -u "elastic:${ECK_PWD}" -X POST "http://localhost:9200/${IDX}/_ccr/unfollow" kubectl exec -it ${ECK_CLUSTER_NAME}-es-masters-0 -n ${NAMESPACE} -- \ curl -sf -u "elastic:${ECK_PWD}" -X POST "http://localhost:9200/${IDX}/_open" done ```
Restore the latest snapshot to the target ECK cluster:
Show details: snapshot restore example ```bash # Delete the SLM policy SOURCE_ES_PWD=$(kubectl get secret ${CAMUNDA_RELEASE_NAME}-elasticsearch -n ${NAMESPACE} \ -o jsonpath='{.data.elasticsearch-password}' | base64 -d) kubectl exec -it ${CAMUNDA_RELEASE_NAME}-elasticsearch-master-0 -n ${NAMESPACE} -- \ curl -sf -u "elastic:${SOURCE_ES_PWD}" -X DELETE \ "http://localhost:9200/_slm/policy/migration_continuous" # Get the latest snapshot name LATEST_SNAP=$(kubectl exec -it ${CAMUNDA_RELEASE_NAME}-elasticsearch-master-0 -n ${NAMESPACE} -- \ curl -sf -u "elastic:${SOURCE_ES_PWD}" \ "http://localhost:9200/_snapshot/migration_continuous/_all" | jq -r '.snapshots[-1].snapshot') # Restore to target ECK ECK_PWD=$(kubectl get secret ${ECK_CLUSTER_NAME}-es-elastic-user -n ${NAMESPACE} \ -o jsonpath='{.data.elastic}' | base64 -d) # Register the repo on the target kubectl exec -it ${ECK_CLUSTER_NAME}-es-masters-0 -n ${NAMESPACE} -- \ curl -sf -u "elastic:${ECK_PWD}" -X PUT \ "http://localhost:9200/_snapshot/migration_continuous" \ -H 'Content-Type: application/json' \ -d '{"type":"fs","settings":{"location":"/backup/elasticsearch/continuous"}}' # Restore kubectl exec -it ${ECK_CLUSTER_NAME}-es-masters-0 -n ${NAMESPACE} -- \ curl -sf -u "elastic:${ECK_PWD}" -X POST \ "http://localhost:9200/_snapshot/migration_continuous/${LATEST_SNAP}/_restore?wait_for_completion=true" \ -H 'Content-Type: application/json' \ -d '{"indices":"*","ignore_unavailable":true,"include_global_state":false}' ```
With the continuous snapshot approach, there is a small window (up to the snapshot interval, for example 5 minutes) where recent Elasticsearch writes may not be captured. Zeebe will re-export these events after the cutover.
### Step 3: Helm upgrade (rolling restart) Perform the Helm upgrade to switch Camunda to the new backends. Because there is no freeze, pods are restarted in rolling fashion. The zero-downtime approach does not use `3-cutover.sh` — that script freezes the application, which defeats the purpose. Instead, run the Helm upgrade manually with the operator-based values:
Show details: Helm upgrade example ```bash helm upgrade ${CAMUNDA_RELEASE_NAME} camunda/camunda-platform \ -n ${NAMESPACE} \ --version ${CAMUNDA_HELM_CHART_VERSION} \ -f operator-based-values.yaml \ --wait --timeout 10m ```
Build the values file by combining the operator-based Helm values files from the reference architecture (for example, `camunda-identity-values.yml`, `camunda-elastic-values.yml`, `camunda-keycloak-domain-values.yml`) to point Camunda at the new backends. Ensure Bitnami subcharts are disabled. The Helm upgrade triggers a rolling restart of Camunda pods. During this process: - Zeebe StatefulSet pods restart one at a time, maintaining quorum. - Operate, Tasklist, Optimize, and other deployments restart with zero-downtime rollout strategy. - There is a brief period where some pods use old backends and others use new ones, but this is safe because the data has already been replicated. ### Step 4: Clean up source publications After the cutover is confirmed working, clean up the publications on the source:
Show details: source publication cleanup example ```bash kubectl exec -it ${CAMUNDA_RELEASE_NAME}-postgresql-0 -n ${NAMESPACE} -- \ psql -U postgres -d identity -c "DROP PUBLICATION IF EXISTS identity_migration;" kubectl exec -it ${CAMUNDA_RELEASE_NAME}-keycloak-postgresql-0 -n ${NAMESPACE} -- \ psql -U postgres -d keycloak -c "DROP PUBLICATION IF EXISTS keycloak_migration;" kubectl exec -it ${CAMUNDA_RELEASE_NAME}-postgresql-web-modeler-0 -n ${NAMESPACE} -- \ psql -U postgres -d webmodeler -c "DROP PUBLICATION IF EXISTS webmodeler_migration;" ```
## Phase 5: Validate and clean up ### Validate Run the standard validation to confirm all components are healthy on the new infrastructure: ```bash bash 4-validate.sh ``` :::warning Wait before cleanup Do not clean up immediately after validation. Operate with the new infrastructure through at least one full business cycle (for example, a complete weekday with peak traffic) to confirm stability. Once Bitnami resources are deleted, rollback is no longer possible without restoring from backup. If you need to fail back, run `bash rollback.sh` before cleanup (see below). ::: ### Rollback (if needed) If the zero-downtime migration reveals issues after cutover: 1. **Immediate rollback** (within minutes): If detected quickly, the source Bitnami databases still have all data (publications were only cleaned up in the last step). Run the standard rollback: ```bash bash rollback.sh ``` 2. **Late rollback** (after publications are dropped): You would need to perform a reverse data migration — dump from the CNPG targets back to the Bitnami sources. This is the same process in reverse. ### Clean up Bitnami resources :::warning Destructive and irreversible This phase **permanently deletes** old Bitnami StatefulSets, PVCs, and the migration backup PVC. After cleanup, rollback to Bitnami subcharts is **no longer possible**. Before running this phase, strongly consider: 1. Taking a full backup of all databases (`pg_dumpall` or equivalent) 2. Taking PVC or storage volume snapshots (cloud provider snapshots) 3. Storing backups in cold storage—for example, S3 Glacier or GCS Archive 4. Keeping rollback artifacts in `.state/` as a safety net ::: After confirming the migration is successful, remove old Bitnami StatefulSets, PVCs, services, and the migration backup PVC: ```bash bash 5-cleanup-bitnami.sh ``` What happens: 1. The script requires cutover and validation to be completed and displays a **destructive operation warning** with a confirmation prompt. 2. **Deletes old Bitnami PostgreSQL** StatefulSets, their PVCs, and headless services (for each migrated component: Identity, Keycloak, and Web Modeler). 3. **Deletes old Bitnami Elasticsearch** StatefulSet, PVCs, and services. 4. **Deletes old Bitnami Keycloak** StatefulSet. 5. **Deletes the migration backup PVC**. 6. **Reverifies** that all Camunda components and operator-managed targets remain healthy after cleanup. 7. Suggests removing the `reindex.remote.whitelist` setting from the ECK Elasticsearch configuration as a post-cleanup step. The script checks whether each resource exists before attempting deletion, so it can be safely rerun if interrupted.
Show details: Cleanup script reference ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/kubernetes/migration/5-cleanup-bitnami.sh ```
## Operational readiness Before running this migration in production, use the checklist below to reduce risk and confirm the cutover plan is ready. ### Staging rehearsal 1. **Clone your production environment** to a staging cluster with the same Helm chart version, same component configuration, and comparable data volumes. 2. **Run the full migration end to end** in staging, including all five phases: deploy targets, enable replication, verify synchronization, cutover, and validate/cleanup. 3. **Measure replication convergence**: record how long it takes for PostgreSQL subscriptions to reach `lag_bytes = 0` and for Elasticsearch to fully synchronize. These timings determine how long you must wait before cutover. 4. **Test failback**: after a successful staging migration, verify you can roll back cleanly with `bash rollback.sh`. :::tip Use a representative data set. Small databases converge almost instantly but hide the replication lag behavior you will face with production-sized workloads. Include realistic write load during the staging rehearsal to observe replication behavior under pressure. ::: ### Pre-migration checklist Before starting the migration in production: - **Verify replication prerequisites**: confirm `wal_level = logical`, sufficient `max_replication_slots` and `max_wal_senders`, and (if using CCR) a valid Elastic Platinum license. - **Notify stakeholders**: although there is no planned downtime, inform them of the migration. If the PostgreSQL restart for `wal_level` is required, coordinate it during a low-traffic period. - **Verify backups**: confirm your existing backup strategy (Velero, volume snapshots, or cloud provider backups) has a recent successful backup. - **Check cluster resources**: ensure the cluster has enough CPU, memory, and storage to run both old and new infrastructure simultaneously — they coexist for the entire replication phase. - **Review `env.sh`**: double-check all variables, especially `NAMESPACE`, `CAMUNDA_RELEASE_NAME`, and target cluster names. - **Prepare monitoring**: set up dashboards for PostgreSQL replication lag, Elasticsearch sync status, pod health, and storage capacity. ### Production dry-run Although the zero-downtime migration does not use `3-cutover.sh`, you can still validate Phase 1 (target deployment) with the `--dry-run` flag: ```bash bash 1-deploy-targets.sh --dry-run ``` Review the output to confirm that operator installations, target cluster manifests, and namespace settings match your expectations before deploying real resources. ### Post-migration monitoring After completing the cutover, monitor the following for at least 48 hours: - **Pod restarts**: `kubectl get pods -n ${NAMESPACE} --watch` - **CNPG cluster health**: `kubectl get clusters -n ${NAMESPACE}` (should show `Cluster in healthy state`) - **ECK cluster health**: `kubectl get elasticsearch -n ${NAMESPACE}` (should show `green`) - **Camunda component logs**: check for connection errors, authentication failures, or data inconsistencies. - **Process instance completion**: verify that in-flight process instances continue to execute correctly. - **Zeebe export lag**: confirm that Zeebe exporters are writing to the new Elasticsearch without delays. - **Sequence values**: verify PostgreSQL sequences are correct after cutover (see [PostgreSQL logical replication limitations](#postgresql-logical-replication-limitations)). --- ## Move from the Helm v3 CLI to v4 Since Camunda 8.10 (chart 15.x), Helm CLI v4 is required. If you are upgrading from Camunda 8.7, 8.8, or 8.9 using the Helm v3 CLI, switch to the Helm v4 CLI before you upgrade. **No release-state migration is required.** ## Why no migration is required Helm is a client-side tool. The CLI renders chart templates and applies the resulting manifests to the cluster. Helm release metadata is stored as Kubernetes Secrets in the release namespace, and both the v3 and v4 CLIs read and write the same release-storage format. This means: - The same release works under both CLIs against the same cluster. - There is no `helm 3to4` step for Camunda charts. - You do not need to reinstall, re-import, or back up and restore release state when you change CLI versions. ## Switch from the v3 CLI to the v4 CLI 1. Install the Helm CLI v4 on the workstation that runs your Helm commands. See [Installing Helm](https://helm.sh/docs/intro/install/). 2. Verify the CLI version: ```bash helm version ``` Confirm the output reports a `v4.x` client version. 3. Verify the existing release is visible to the new CLI: ```bash helm list -n ``` Your existing Camunda release appears with the same name, chart version, and revision history. 4. Continue with your normal `helm upgrade` workflow. See [Upgrade Helm chart](/self-managed/upgrade/helm/index.md). ## Helm 4 behavior changes to review Helm 4 enables server-side apply by default and removes some Helm 3 plugin behaviors. Review these changes before your first install or upgrade with the v4 CLI: - [Helm 4 server-side apply and post-renderer changes](/self-managed/deployment/helm/operational-tasks/helm-v4.md) ## Helm v3 support - Chart 15.x and later do not support Helm v3. Chart 14.x is the last minor that supports Helm v3. - Helm v3 itself reaches end of support upstream: bug fixes through July 8, 2026, and security fixes through November 11, 2026. See the [Helm support policy](https://helm.sh/blog/helm-4-released#helm-v3-support). --- ## Deploy and manage Camunda Self-Managed In this section, find details on the Self-Managed installation methods. --- ## Amazon EC2 This guide provides a detailed walkthrough for installing the Camunda 8 single JAR on AWS EC2 instances. It focuses on managed services provided by AWS and their cloud offering. Finally, you will verify that the connection to your Self-Managed Camunda 8 environment is functioning correctly. This guide focuses on setting up the [Orchestration Cluster](/self-managed/reference-architecture/reference-architecture.md#orchestration-cluster-vs-web-modeler-and-console) for Camunda 8. Web Modeler and Console are not covered in this manual deployment approach, as these components are not supported on virtual machines. To deploy Web Modeler and Console, explore the available options for [Kubernetes-based deployments](/self-managed/deployment/helm/install/quick-install.md#install-web-modeler). :::note Using other cloud providers This guide is based on tools and services provided by AWS but is not limited to them. The scripts and concepts included can be adapted for other cloud providers and use cases. When using a different cloud provider, you are responsible for configuring and maintaining the resulting infrastructure. Support is limited to questions related to this guide—not to the specific tools or services of your chosen cloud provider. ::: :::danger Cost management Following this guide will incur costs on your cloud provider account—primarily for EC2 instances and OpenSearch. Visit AWS and their [pricing calculator](https://calculator.aws/#/) for detailed cost estimates, as pricing varies by region. You may refer to this [example calculation](https://calculator.aws/#/estimate?id=ca54a43f3b3b7eb42fe8836854775e60d8c7e04d), which can be further optimized for your specific use case. ::: ## Architecture The architecture outlined below describes a standard three-node deployment, distributed across three [availability zones](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html) within a single AWS region. It includes a managed OpenSearch domain deployed under the same conditions. This approach ensures high availability and redundancy in case of a zone failure. _Infrastructure diagram for a 3-node EC2 architecture (click the image to view the PDF version)_ [![AWS EC2 Architecture](./assets/aws-ec2-arch.jpg)](./assets/aws-ec2-arch.pdf) The setup includes: - A [Virtual Private Cloud](https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html) (VPC), which is a logically isolated virtual network. - A [Private Subnet](https://docs.aws.amazon.com/vpc/latest/userguide/configure-subnets.html), which does not have direct internet access. - Three [EC2](https://aws.amazon.com/ec2/) instances running Ubuntu—one in each availability zone—host the Camunda 8 components. - A [managed OpenSearch](https://aws.amazon.com/what-is/opensearch/) cluster, also distributed across the three zones. - A [Public Subnet](https://docs.aws.amazon.com/vpc/latest/userguide/configure-subnets.html), which has internet access via an [Internet Gateway](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html). - (Optional) An [Application Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html) (ALB) to expose web interfaces such as Operate, Tasklist, Connectors, and the Orchestration Cluster REST API. This uses sticky sessions, as requests are otherwise distributed round-robin across EC2 instances. - (Optional) A [Network Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/introduction.html) (NLB) to expose the gRPC endpoint of the Zeebe Gateway, if external applications need to connect. - (Optional) A [Bastion Host](https://en.wikipedia.org/wiki/Bastion_host) to allow access to private EC2 instances that are not publicly exposed. - Alternatively, use [AWS Client VPN](https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/what-is.html) to access the private subnet. This setup requires additional effort and certificates but can be implemented using [AWS’s getting started guide](https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/cvpn-getting-started.html). - A [NAT Gateway](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html) that allows private EC2 instances to access the internet for downloading and updating software packages. Note that this does **not** provide inbound access to the instances. - [Security Groups](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-security-groups.html) to control network traffic to and from the EC2 instances. - An [Internet Gateway](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html) to route traffic between the VPC and the internet. Both subnet types are distributed across three availability zones in a single AWS region, supporting a high-availability architecture. :::note Single deployment You can also run this setup using a single AWS EC2 instance. However, in the event of a zone failure, the entire environment would become unreachable. ::: ## Requirements - An AWS account to provision resources. - At a high level, permissions are needed for **ec2**, **iam**, **elasticloadbalancing**, **kms**, **logs**, and **es** services. - For detailed permissions, refer to this [example policy](https://github.com/camunda/camunda-deployment-references/blob/main/aws/compute/ec2-single-region/example/policy.json). - Terraform (v1.7 or later) - A Unix-based operating system with `ssh` and `sftp` - Windows may be used with [Cygwin](https://www.cygwin.com/) or [Windows WSL](https://learn.microsoft.com/en-us/windows/wsl/install), though these configurations have not been tested. ### Outcome The result is a fully functioning Camunda Orchestration Cluster deployed in a high-availability setup using AWS EC2 and a managed OpenSearch domain. Each EC2 instance includes an additional disk, dedicated to Camunda, to separate application data from the operating system. ## 1. Configure AWS and initialize Terraform :::note Terraform infrastructure example We do not recommend using the following Terraform-based infrastructure as a module, since we cannot guarantee compatibility. Instead, we suggest reusing or extending components of the Terraform example to ensure alignment with your environment. ::: ### Obtain a copy of the reference architecture Start by downloading a copy of the reference architecture from the GitHub repository. This content will be used throughout the rest of the guide. The reference architectures are versioned according to Camunda releases (e.g., stable/8.x). The reference architecture repository allows you to reuse and extend the provided Terraform examples. This flexible implementation avoids the constraints of relying on third-party-maintained Terraform modules: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/compute/ec2-single-region/procedure/get-your-copy.sh ``` With the reference architecture in place, you can proceed with the remaining steps in this documentation. Make sure you're in the correct directory before continuing with the instructions. ### Terraform prerequisites To manage Camunda 8 infrastructure on AWS using Terraform, you need to configure Terraform's backend to store the state file remotely in an S3 bucket. This provides secure, persistent primary storage for your infrastructure. :::note Advanced users may choose to configure a different backend. The setup described here is a recommended starting point for new users. ::: #### Set up AWS authentication The [AWS Terraform provider](https://registry.terraform.io/providers/hashicorp/aws/latest/docs) is required to provision resources in AWS. Before using the provider, you must authenticate it with your AWS credentials. :::caution Ownership of the created resources Any user who creates AWS resources retains administrative access to them. For better control and security, it is recommended to create a dedicated [AWS IAM user](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users.html) specifically for Terraform. This ensures the resources are properly managed and owned by a single identity. ::: You can customize the region and authentication settings as needed. Terraform supports multiple [authentication methods](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#authentication-and-configuration), including: - For development or testing, you can use the [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html). If your AWS CLI is already configured, Terraform will automatically detect and use those credentials. To configure the AWS CLI: ```bash aws configure ``` Enter your `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, region, and output format. These can be retrieved from the [AWS Console](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html). - For production environments, it is recommended to use a dedicated IAM user. Create [access keys](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html) for that user via the AWS console, and export them as environment variables: `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. #### Create an S3 bucket for Terraform state management Before initializing Terraform, you must create an S3 bucket to store the state file. This is essential for collaborative work and helps prevent issues such as state file corruption. Begin by setting your preferred AWS region as an environment variable to avoid repeating it in every command: ```bash export AWS_REGION= ``` Replace `` with your chosen AWS region (e.g., `eu-central-1`). Next, follow these steps to create an S3 bucket with versioning enabled: 1. Open your terminal and ensure that the AWS CLI is installed and properly configured. 2. Run the following command to create an S3 bucket for storing your Terraform state. Be sure to choose a unique bucket name, and ensure that the `AWS_REGION` environment variable is already set: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/common/procedure/s3-bucket/s3-bucket-creation.sh ``` 3. Enable versioning on the S3 bucket to track changes and protect the state file from accidental deletions or overwrites: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/common/procedure/s3-bucket/s3-bucket-versioning.sh ``` 4. Secure the bucket by blocking public access: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/common/procedure/s3-bucket/s3-bucket-private.sh ``` 5. Verify versioning is enabled on the bucket: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/common/procedure/s3-bucket/s3-bucket-verify.sh ``` The S3 bucket is now ready to securely store your Terraform state files, with versioning enabled for added protection. #### Initialize Terraform Once authentication is configured, you can initialize your Terraform project. Earlier, you created a dedicated S3 bucket (`S3_TF_BUCKET_NAME`) for storing the state file. In this step, Terraform will use that bucket along with a specific key to manage your infrastructure state. Initialize the backend and download the required provider plugins: :::note Make sure you are in the `terraform` subfolder: `camunda-deployment-references/aws/compute/ec2-single-region/terraform`. ::: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/common/procedure/s3-bucket/s3-bucket-tf-init.sh ``` Terraform will now use the S3 bucket to manage the state file, ensuring remote and persistent storage. ### EC2 setup The `ec2.tf` file handles the creation of compute instances and, optionally, a bastion host. Within this file, you can configure disk size, instance type, enable or disable the bastion host, and define the [Amazon Machine Image (AMI)](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html) to override the default behavior of using the latest available image. The file defines all resources related to the EC2 setup and can be customized as needed in your copied reference architecture. Note that the embedded code snippet below is limited to 30 lines. For the complete file, see the link provided at the bottom of the snippet: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/compute/ec2-single-region/terraform/cluster/ec2.tf#L1-L30 ``` ### Security setup The `security.tf` file defines several security groups to manage access and traffic flow for different use cases, including: - Allowing internal VPC traffic on Camunda ports - Permitting EC2 instances to access external traffic on ports 80 and 443 to download dependencies (e.g., Java, Camunda) - Allowing inbound traffic to the Load Balancer on specific ports - Enabling SSH access for the bastion host In addition to traffic management, this file also includes: - A KMS key for encrypting EC2 disks and the OpenSearch domain - An SSH key pair used to authorize remote SSH connections The embedded snippet below shows which resources are created and how they can be customized in your copied reference. The preview is limited to 30 lines. For the complete file, refer to the link at the bottom of the snippet: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/compute/ec2-single-region/terraform/cluster/security.tf#L1-L30 ``` ### Load balancer setup The `lb.tf` file defines the load balancer configuration used to expose Camunda 8 either publicly or within your internal network. You can further restrict access based on your security requirements. The configuration includes two types of load balancers: - A **Network Load Balancer** to expose the gRPC endpoint - An **Application Load Balancer** to expose the Camunda WebApps and REST API The embedded snippet below shows the resources defined in this file and how they can be customized in your copied reference. The preview is limited to 30 lines. For the complete file, refer to the link at the bottom of the snippet: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/compute/ec2-single-region/terraform/cluster/lb.tf#L1-L30 ``` ### OpenSearch module setup :::info Optional module If you do not want to use this module, you can skip this section. However, you will need to adjust the remaining steps to remove any references to it. If you choose not to use this module, you must provide your own Elasticsearch or OpenSearch service. Additionally, be sure to delete the `opensearch.tf` file in your reference copy—otherwise, the resources defined in it will still be created. ::: The OpenSearch module provisions an OpenSearch domain for use with Camunda. OpenSearch is a powerful alternative to Elasticsearch. :::note Migration to OpenSearch is not supported Using Amazon OpenSearch Service requires [setting up a new Camunda installation](/self-managed/setup/overview.md). Migration from earlier Camunda versions using Elasticsearch is not currently supported. Switching between Elasticsearch and OpenSearch in either direction is also unsupported. ::: #### Set up the OpenSearch domain module 1. The `opensearch.tf` file in your reference contains a basic OpenSearch setup using a local Terraform module. The snippet below shows the structure of this file, which you can modify within your cloned setup to suit your needs. :::caution Network-based security The default OpenSearch deployment relies primarily on network-level security. While this simplifies access, it can expose sensitive data within your VPC. To enhance security, consider enabling [fine-grained access control](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/fgac.html). ::: ```hcl reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/compute/ec2-single-region/terraform/cluster/opensearch.tf#L1-L30 ``` 2. Customize the cluster setup using various input options. For a complete list of available parameters, refer to the [OpenSearch module documentation](https://github.com/camunda/camunda-deployment-references/blob/main/aws/modules/opensearch/README.md). :::tip The instance type `m7i.large.search` used in the example is only a suggestion. You can change it based on your workload and requirements. ::: ### Define outputs **Terraform** allows you to define outputs—useful for retrieving important values generated during execution, such as database endpoints and other configuration details. Each Terraform definition in the reference includes output blocks at the end of the file. You can modify these as needed. The provided defaults are ready to use in automation scripts. Defining outputs helps you easily reference resources in later steps or scripts, simplifying your deployment workflow. ### Execution :::note Secret management We strongly recommend managing sensitive information using a secure secrets management tool such as HashiCorp Vault. For guidance on injecting secrets into Terraform via Vault, refer to the [Terraform Vault Secrets Injection Guide](https://developer.hashicorp.com/terraform/tutorials/secrets/secrets-vault). ::: 1. Open a terminal in the reference directory containing `config.tf` and the other `.tf` files. 2. Perform a final initialization to apply any changes made throughout this guide: ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/aws/common/procedure/s3-bucket/s3-bucket-tf-init.sh#L7 ``` 3. Plan the configuration files: ```bash terraform plan -out cluster.plan # describe what will be created ``` 4. After reviewing the plan, you can confirm and apply the changes: ```bash terraform apply cluster.plan # apply the creation ``` Terraform will now provision the Amazon EC2 resources and the OpenSearch domain with all necessary configurations. This process may take approximately 20–30 minutes to complete. ### Connect to remote machines via Bastion host (optional) Since the EC2 instances are not publicly accessible, you must connect to them through a Bastion host. Alternatively, you can use the [AWS VPN Client](https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/what-is.html) to securely access the private VPC. This guide does not cover the VPN client setup, as it requires specific manual configuration and user interaction: ```sh export BASTION_HOST=$(terraform output -raw bastion_ip) # retrieves the first IP from the camunda_ips array export CAMUNDA_IP=$(tf output -json camunda_ips | jq -r '.[0]') ssh -J admin@${BASTION_HOST} admin@${CAMUNDA_IP} ``` ## 2. Deploy Camunda 8 The following example uses scripts from the Terraform installation to deploy and configure Camunda 8. You can adapt these scripts to your own environment. Alternatively, use the [manual installation instructions](/self-managed/deployment/manual/install.md) to perform the base installation and configuration. ### Configure and run the installation procedure 1. Navigate to the procedure directory: ```sh cd procedure ``` The `procedure` directory contains Bash scripts for installing and configuring Camunda 8. 2. Configure script behavior using the following environment variables: - `CLOUDWATCH_ENABLED`: Defaults to `false`. Set to `true` to install the CloudWatch agent on each EC2 instance and export Camunda logs and Prometheus metrics to AWS CloudWatch. - `CAMUNDA_DISTRO_USER`: Camunda Enterprise LDAP username for authenticating against `artifacts.camunda.com` (Artifactory). Required to download artifacts from `artifacts.camunda.com`. - `CAMUNDA_DISTRO_PASSWORD`: Camunda Enterprise LDAP password for authenticating against `artifacts.camunda.com` (Artifactory). Required to download artifacts from `artifacts.camunda.com`. 3. Override default versions in the `camunda-install.sh` script by modifying these variables: - `OPENJDK_VERSION`: The Temurin Java version to install. - `CAMUNDA_VERSION`: The Camunda 8 version to install. - `CAMUNDA_CONNECTORS_VERSION`: The Camunda 8 Connectors version to install. :::note These variables must be set inside the `camunda-install.sh` script itself; they cannot be set as environment variables. ::: 4. Run the `all-in-one-install.sh` script. This script installs all required dependencies and configures Camunda 8 to run in a highly available setup using a managed OpenSearch instance. It automatically retrieves all required IP addresses and other details from the Terraform state via Terraform outputs. During the initial run, you will be prompted to confirm SSH connections to each EC2 instance by typing `yes`. ### Connect and use Camunda 8 The Application Load Balancer (ALB) and Network Load Balancer (NLB) endpoints are available via Terraform outputs: - `terraform output alb_endpoint`: Use this to access Operate and other Web UIs (such as Tasklist, Optimize, and Connectors). The ALB handles HTTP traffic for these interfaces, as well as the Orchestration Cluster REST API (e.g., connectors on port `9090`). - `terraform output nlb_endpoint`: Use this to access the Zeebe Gateway’s gRPC endpoint. The NLB is designed for TCP-based gRPC traffic, while the ALB handles HTTP. These endpoints use AWS-assigned public hostnames. To use your own domain, create CNAME records pointing to these hostnames or use [Route 53](https://aws.amazon.com/route53/) for DNS management and to enable SSL certificates. Note that enabling SSL and custom domains will require additional configuration in the Terraform blueprint, since it listens on HTTP by default. If you prefer not to expose your environment publicly, you can use the Bastion host (jump host) to access services locally via port forwarding. For a more secure, enterprise-grade solution, use the [AWS Client VPN](https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/what-is.html) to access the private subnet within your VPC. This setup requires additional certificates and configuration, detailed in the [AWS getting started tutorial](https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/cvpn-getting-started.html). The following commands can be run from within the Terraform folder to bind remote ports to your local machine via SSH port forwarding: ```sh export BASTION_HOST=$(terraform output -raw bastion_ip) # retrieves the first IP from the camunda_ips array export CAMUNDA_IP=$(tf output -json camunda_ips | jq -r '.[0]') # 26500 - gRPC; 8080 - WebUI; 9090 - Connectors ssh -L 26500:${CAMUNDA_IP}:26500 -L 8080:${CAMUNDA_IP}:8080 -L 9090:${CAMUNDA_IP}:9090 admin@${BASTION_HOST} ``` ### Turn off bastion host (optional) If you used the [bastion host](#turn-off-bastion-host-optional) for access, it can be turned off when longer needed for direct access to the EC2 instances. To turn off the bastion host, set the `enable_jump_host` variable to `false` in the `variables.tf` file, and reapply Terraform. ## 3. Verify connectivity to Camunda 8 Using Terraform, you can obtain the HTTP endpoint of the Application Load Balancer and interact with Camunda through the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). 1. Navigate to the Terraform folder: ```sh cd terraform ``` 2. Retrieve the Application Load Balancer output: ```sh terraform output -raw alb_endpoint ``` 3. Use the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) to communicate with Camunda: Follow the example in the [Orchestration Cluster REST API documentation](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md) to authenticate and retrieve the cluster topology. ## Manage Camunda 8 ### Upgrade Camunda 8 :::info Direct upgrade not supported Upgrading directly from Camunda 8.7 to 8.8 is not supported and cannot be performed. ::: For manual installations, see the [upgrade guide](/self-managed/upgrade/manual/index.md) for detailed instructions. ### Monitoring Camunda exposes metrics in Prometheus format by default. For details on scraping Camunda 8 metrics, see [metrics](/self-managed/operational-guides/monitoring/metrics.md). In AWS environments, you can leverage CloudWatch for log collection and for gathering [Prometheus metrics](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/ContainerInsights-Prometheus-metrics.html). While Camunda natively integrates with Prometheus and Grafana, using CloudWatch for metrics visualization requires additional configuration. ### Backups Refer to the general backup and restore documentation in [backup and restore](/self-managed/operational-guides/backup-restore/backup-and-restore.md). When using AWS, you can utilize [S3](https://aws.amazon.com/s3/) for backing up both Zeebe and Elasticsearch / OpenSearch data. ## Troubleshooting For troubleshooting assistance, consult the [operational guides troubleshooting documentation](/self-managed/operational-guides/troubleshooting.md). ## Next steps After setting up your cluster, many users typically do the following: - [Connect to an identity provider](/self-managed/components/orchestration-cluster/admin/connect-external-identity-provider.md) – integrate with an external identity system for authentication. - [Secure cluster communication](/self-managed/components/orchestration-cluster/zeebe/security/secure-cluster-communication.md) – protect traffic between cluster nodes. - [Secure client communication](/self-managed/components/orchestration-cluster/zeebe/security/secure-client-communication.md) – ensure secure communication between clients and the cluster. --- ## Camunda manual installation This page guides you through the manual installation of Camunda 8 on a local machine, bare metal server, or virtual machine. ## Prerequisites - Bare metal or virtual machine - Operating system: - Linux - Windows, macOS, and other operating systems are supported for development only and not for production. - Java Virtual Machine. See [supported environments](/reference/supported-environments.md) for version details. - Configure the web applications to use an available port. By default, the Orchestration Cluster listens on port 8080. - Secondary storage - Choose a supported secondary storage backend for your installation path. - **Document-store backend (Elasticsearch or OpenSearch)**: See [supported environments](/reference/supported-environments.md). - For deployment options, see the [Elasticsearch documentation](https://www.elastic.co/docs/deploy-manage/deploy). - **RDBMS**: See [secondary storage architecture](/self-managed/reference-architecture/reference-architecture.md#secondary-storage-architecture) for backend trade-offs, and [manual installation with RDBMS](/self-managed/deployment/manual/rdbms/index.md) for supported databases and setup details. For suggested minimum hardware requirements and networking, see the [manual reference architecture requirements](/self-managed/reference-architecture/manual.md#requirements). :::tip Performance on musl-based distributions There are known performance limitations on systems that use `musl` instead of `glibc`, because Java relies on `glibc` for running native libraries. For example, Alpine Linux, which uses `musl`, has shown performance degradation compared to Debian or Ubuntu in benchmark tests. ::: :::warning Unsupported components The following components are not supported for manual installation: - Management Identity - Optimize - Web Modeler To install these components, use one of the supported methods: - [Install with Docker](/self-managed/deployment/docker/docker.md) - [Install on Kubernetes with Helm](/self-managed/deployment/helm/index.md) ::: ## Download artifacts Download the required Camunda 8 artifacts from the following sources. Make sure that all artifacts use the same minor version to ensure compatibility. :::note Artifactory authentication Downloading artifacts from [artifactory](https://artifacts.camunda.com) requires authentication. Use your Camunda Enterprise LDAP credentials. When using `curl`, pass your username with the `-u` flag and let `curl` prompt for the password: ```sh curl -u "$CAMUNDA_DISTRO_USER" -fL ``` ::: Orchestration Cluster: - File names follow the pattern `camunda-zeebe-x.y.z.(zip|tar.gz)`. - [Maven Central](https://central.sonatype.com/artifact/io.camunda/camunda-zeebe/versions) - Select a version, then click **Browse** to view downloadable files such as `.zip` or `.tar.gz`. - [Artifactory](https://artifacts.camunda.com/ui/native/zeebe/io/camunda/camunda-zeebe/) - Select a version, then browse the files to download. - [GitHub](https://github.com/camunda/camunda/releases) - Select a release to download the files. Connectors: - Bundle (includes pre-bundled connectors from Camunda) - File names follow the pattern `connector-runtime-bundle-x.y.z-with-dependencies.jar`. - [Maven Central](https://central.sonatype.com/artifact/io.camunda.connector/connector-runtime-bundle/versions) - Select a version, then click **Browse** to view the `.jar`. - [Artifactory](https://artifacts.camunda.com/ui/native/zeebe/io/camunda/connector/connector-runtime-bundle/) - Select a version, then browse the files to download. - Runtime-only - File names follow the pattern `connector-runtime-application-x.y.z.jar`. - [Maven Central](https://central.sonatype.com/artifact/io.camunda.connector/connector-runtime-application/versions) - Select a version, then click **Browse** to view the `.jar`. - [Artifactory](https://artifacts.camunda.com/ui/native/zeebe/io/camunda/connector/connector-runtime-application/) - Select a version, then browse the files to download. :::note Some out-of-the-box connectors are licensed under the [Camunda Self-Managed Free Edition license](https://camunda.com/legal/terms/cloud-terms-and-conditions/camunda-cloud-self-managed-free-edition-terms/). See [Camunda Connectors Bundle project](https://github.com/camunda/connectors) for an overview. ::: ## Reference architecture Review the following reference architectures for deployment guidance: - [Manual reference architecture](/self-managed/reference-architecture/manual.md) - Provides an overview of the environment and requirements. - [Amazon EC2](/self-managed/deployment/manual/cloud-providers/amazon/aws-ec2.md) - A reference architecture built on Amazon Web Services (AWS) using Elastic Compute Cloud (EC2) with Ubuntu, and Amazon OpenSearch as the document-store secondary storage example. ## Orchestration Cluster For background, see the [Orchestration Cluster glossary entry](/reference/glossary.md#orchestration-cluster). For architecture details, review the [architecture](/self-managed/reference-architecture/reference-architecture.md#architecture). For configuration details, see the [Orchestration Cluster components](/self-managed/components/orchestration-cluster/overview.md). ### Configure the Orchestration Cluster This guide uses a single-node orchestration cluster with a local Elasticsearch instance as the document-store secondary storage example. If this setup matches your environment, no additional configuration is required. If you want to use RDBMS as secondary storage instead, follow [manual installation with RDBMS](/self-managed/deployment/manual/rdbms/index.md). If you plan to: - Add more nodes to the cluster - Use a different external secondary storage - Enable Connectors - Apply a license key You need to make targeted configuration changes. The following sections outline the minimum required adjustments for each use case. Combine these changes into a single `application.yaml` under the appropriate configuration keys, or export them as environment variables. For detailed configuration options and advanced setup guidance, refer to each component’s documentation under the [Orchestration cluster section](/self-managed/components/orchestration-cluster/overview.md). :::note Configuration is being unified across components. Some changes will only take effect in future versions, so you may see a mix of old and new configuration options. ::: #### Configure the secondary storage Set the secondary storage type value to `elasticsearch` or `opensearch` for this configuration path. Remove fields that do not apply to your selection. If your security settings require authentication for the secondary storage, configure both `username` and `password`. Omit these fields if authentication is not required. The following configuration defines how the Orchestration Cluster connects to document-store secondary storage (Elasticsearch or OpenSearch). This applies to the included Operate, Tasklist, Admin, and Camunda Exporter. For detailed configuration options, see the [Orchestration Cluster configuration](/self-managed/components/orchestration-cluster/core-settings/overview.md) ```bash CAMUNDA_DATA_SECONDARYSTORAGE_TYPE=elasticsearch|opensearch # defaults to elasticsearch # Elasticsearch CAMUNDA_DATA_SECONDARYSTORAGE_ELASTICSEARCH_URL=http://localhost:9200 CAMUNDA_DATA_SECONDARYSTORAGE_ELASTICSEARCH_USERNAME= CAMUNDA_DATA_SECONDARYSTORAGE_ELASTICSEARCH_PASSWORD= # OpenSearch CAMUNDA_DATA_SECONDARYSTORAGE_OPENSEARCH_URL=http://localhost:9200 CAMUNDA_DATA_SECONDARYSTORAGE_OPENSEARCH_USERNAME= CAMUNDA_DATA_SECONDARYSTORAGE_OPENSEARCH_PASSWORD= ``` ```yaml camunda: data: type: elasticsearch|opensearch # defaults to elasticsearch secondary-storage: # Elasticsearch elasticsearch: url: http://localhost:9200 username: password: # OpenSearch opensearch: url: http://localhost:9200 username: password: ``` #### Configure a multi-broker cluster This example shows a 3-broker cluster. - Set `size` to `3`. - Assign a unique `node-id` to each broker, starting from `0` and incrementing up to the total number of brokers (`0`, `1`, `2`). - Use the same `initial-contact-points` on all brokers. For more details, see the [Zeebe Broker cluster configuration](/self-managed/components/orchestration-cluster/zeebe/configuration/broker.md#zeebebrokercluster). ```bash CAMUNDA_CLUSTER_SIZE=3 CAMUNDA_CLUSTER_NODEID=0 # unique ID of this broker node in a cluster. The ID should be between 0 and number of nodes in the cluster (exclusive). CAMUNDA_CLUSTER_INITIALCONTACTPOINTS=HOST_0:26502,HOST_1:26502,HOST_2:26502 ``` ```yaml camunda: cluster: initial-contact-points: [HOST_0:26502, HOST_1:26502, HOST_2:26502] size: 3 node-id: 0 # unique ID of this broker node in a cluster. The ID should be between 0 and number of nodes in the cluster (exclusive). ``` #### Configure Connectors authentication Connectors require authentication to use their full capabilities. By default, the Orchestration Cluster uses Basic authentication. You can configure the cluster to automatically create a user with the necessary permissions at startup. If you don’t configure a user at startup, create one manually in the Admin UI after deployment. For more details, see [Admin configuration overview](/self-managed/components/orchestration-cluster/admin/overview.md). ```bash CAMUNDA_SECURITY_INITIALIZATION_USERS_1_USERNAME=connectors CAMUNDA_SECURITY_INITIALIZATION_USERS_1_PASSWORD=connectors CAMUNDA_SECURITY_INITIALIZATION_USERS_1_NAME="Connectors User" CAMUNDA_SECURITY_INITIALIZATION_USERS_1_EMAIL=connectors@company.com CAMUNDA_SECURITY_INITIALIZATION_DEFAULTROLES_CONNECTORS_USERS_0=connectors ``` ```yaml camunda: security: initialization: users: - username: connectors password: connectors name: Connectors User email: connectors@company.com default-roles: connectors: users: - connectors ``` #### Configure the license key If your Camunda 8 Self-Managed installation requires a license, provide the license key in one of the following ways: ```bash CAMUNDA_LICENSE_KEY="" ``` ```yaml camunda: license: key: >- --------------- BEGIN CAMUNDA LICENSE KEY --------------- [...] --------------- END CAMUNDA LICENSE KEY --------------- ``` ### Run the Orchestration Cluster Once you've downloaded the Orchestration Cluster distribution, extract it into a folder. 1. Extract the files using your GUI or CLI: ```bash mkdir -p camunda && unzip camunda-zeebe-x.y.z.zip -d camunda mkdir -p camunda && tar -xzf camunda-zeebe-x.y.z.tar.gz -C camunda ``` 2. Open the extracted folder. 3. Update the configuration in `config/application.yaml`, or export the environment variables. 4. Navigate to `bin` folder. 5. Run `camunda.sh` (Linux/macOS) or `camunda.bat` (Windows). 6. Open [http://localhost:8080](http://localhost:8080). On first access, you'll be asked to create an admin user unless [Admin](/self-managed/components/orchestration-cluster/core-settings/configuration/properties.md) is configured with OIDC or a similar option. :::note Camunda 8 components without a valid license may display **Non-Production License** in the navigation bar and issue warnings in the logs. These warnings don’t affect startup or functionality, except that Web Modeler is limited to five users. To obtain a license, visit the [Camunda Enterprise page](https://camunda.com/platform/camunda-platform-enterprise-contact/). ::: ### Run the Orchestration Cluster as a service This example shows how to run the Orchestration Cluster as a [`systemd`](https://systemd.io/) service on Ubuntu. Adjust the paths, user, and group as needed for your environment. The example uses a file with environment variables, but you can adapt it to use an `application.yaml` instead. 1. Create a `systemd` service file named `camunda.service` and adjust it fit your own paths, user and group in `/etc/systemd/system/camunda.service`. ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/compute/debian/configs/camunda.service ``` 2. Change the permissions on `/etc/systemd/system/camunda.service` to `644`: ```bash sudo chmod 644 /etc/systemd/system/camunda.service ``` 3. Reload `systemd` and start the new service: ```bash sudo systemctl daemon-reload sudo systemctl start camunda.service ``` 4. Verify that the service is running: ```bash systemctl status camunda.service ``` View logs with: ```bash journalctl -e -u camunda ``` ### Verify the Orchestration Cluster Check the logs for a successful startup message, such as: ```bash [2025-08-05 13:34:51.964] [main] INFO org.springframework.boot.web.embedded.tomcat.TomcatWebServer - Tomcat started on port 8080 (http) with context path '/' ... [2025-08-05 13:34:52.006] [main] INFO org.springframework.boot.web.embedded.tomcat.TomcatWebServer - Tomcat initialized with port 9600 (http) [2025-08-05 13:34:52.048] [main] INFO org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 79 ms [2025-08-05 13:34:52.054] [main] INFO org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver - Exposing 17 endpoints beneath base path '/actuator' [2025-08-05 13:34:52.078] [main] INFO org.springframework.boot.web.embedded.tomcat.TomcatWebServer - Tomcat started on port 9600 (http) with context path '/' [2025-08-05 13:34:52.088] [main] INFO io.camunda.application.StandaloneCamunda - Started StandaloneCamunda in 9.376 seconds (process running for 9.817) ``` Check the cluster topology with the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md): ```bash # replace username and password with the details of the admin user you created on first startup curl -u username:password -L 'http://localhost:8080/v2/topology' \ -H 'Accept: application/json' ```
Example output ```json // amount of brokers, size, partitions etc. depends on your configuration // Example: 1 broker, 3 partitions { "brokers": [ { "nodeId": 0, "host": "HOST_0", "port": 26501, "partitions": [ { "partitionId": 1, "role": "leader", "health": "healthy" }, { "partitionId": 2, "role": "leader", "health": "healthy" }, { "partitionId": 3, "role": "leader", "health": "healthy" } ], "version": "8.8.0" } ], "clusterSize": 1, "partitionsCount": 3, "replicationFactor": 1, "gatewayVersion": "8.8.0", "lastCompletedChangeId": "-1" } ```
Check the health status of the Orchestration Cluster with the actuator endpoint: ```bash curl localhost:9600/actuator/health ```
Example output ```json { "status": "UP", "groups": ["liveness", "readiness", "startup", "status"], "components": { "brokerReady": { "status": "UP" }, "brokerStartup": { "status": "UP" }, "brokerStatus": { "status": "UP" }, "indicesCheck": { "status": "UP" }, "livenessState": { "status": "UP" }, "readinessState": { "status": "UP" }, "searchEngineCheck": { "status": "UP" } } } ```
## Connectors For background, see the [Connectors glossary entry](/reference/glossary.md#connector). For architecture details, review the [architecture](/self-managed/reference-architecture/reference-architecture.md#architecture). For configuration options, see the [Connectors components documentation](/self-managed/components/connectors/overview.md). ### Configure Connectors If you run Connectors on the same machine as the Orchestration Cluster, change the default port (`8080`) to avoid conflicts. Connectors require authentication to communicate with the Orchestration Cluster REST API and Zeebe. By default, Connectors connect to: - `localhost:8080` (Orchestration Cluster REST API) - `localhost:26500` (Zeebe) ```bash SERVER_PORT=9090 CAMUNDA_CLIENT_RESTADDRESS=http://localhost:8080 CAMUNDA_CLIENT_GRPCADDRESS=http://localhost:26500 CAMUNDA_CLIENT_MODE=selfManaged CAMUNDA_CLIENT_AUTH_METHOD=basic CAMUNDA_CLIENT_AUTH_USERNAME=connectors CAMUNDA_CLIENT_AUTH_PASSWORD=connectors ``` Save the following as `application.yaml` in the same folder as your `connector-runtime-(application|bundle)-x-y-z(-with-dependencies).jar`. ```yaml server: port: 9090 camunda: client: rest-address: http://localhost:8080 grpc-address: http://localhost:26500 mode: selfManaged auth: method: basic username: connectors password: connectors ``` For more information about the configuration of the Connectors, see [Connectors configuration](/self-managed/components/connectors/connectors-configuration.md) ### Run Connectors Both the pre-bundled and runtime-only versions of the Connectors behave the same at runtime. They automatically detect and register all connectors available on the classpath during execution. Each connector uses its default configuration as defined by the `@OutboundConnector` or `@InboundConnector` annotations. Consider the following file structure: ```shell /home/user/connectors $ └── connector-runtime-(application|bundle)-x.y.z-with-dependencies.jar ``` To start the connector runtime locally, run: ```shell java -jar /home/user/connectors/connector-runtime-bundle-x.y.z-with-dependencies.jar ``` The runtime bundle is packaged as a Spring Boot uber-jar (with its dependencies under `BOOT-INF/lib/`), so it must be launched with `java -jar`. The older flat-classpath invocation (`java -cp "/home/user/connectors/*" "io.camunda.connector.runtime.app.ConnectorRuntimeApplication"`) no longer works as of connector runtime 8.9.4. To load additional custom connectors, place their JARs in a separate directory and point Spring Boot's loader at it: ```shell java -Dloader.path=/home/user/custom-connectors \ -jar /home/user/connectors/connector-runtime-bundle-x.y.z-with-dependencies.jar ``` This starts a Zeebe client, registering the defined connector as a job worker. By default, it connects to a local Zeebe instance at port `26500`. ### Run Connectors as a service This example shows how to run the Connectors as a [`systemd`](https://systemd.io/) service on Ubuntu. Adjust the paths, user, and group as needed for your environment. The example uses a file with environment variables, but you can adapt it to use an `application.yaml` instead. 1. Create a `systemd` service file named `camunda-connectors.service` and adjust it fit your own paths, user and group in `/etc/systemd/system/camunda-connectors.service`. ```bash reference https://github.com/camunda/camunda-deployment-references/blob/main/generic/compute/debian/configs/camunda-connectors.service ``` 2. Change the permissions on `/etc/systemd/system/camunda-connectors.service` to `644`: ```bash sudo chmod 644 /etc/systemd/system/camunda-connectors.service ``` 3. Reload `systemd` and start the service: ```bash sudo systemctl daemon-reload sudo systemctl start camunda-connectors.service ``` 4. Verify that the service is running: ```bash systemctl status camunda-connectors.service ``` View logs with: ```bash journalctl -e -u camunda-connectors ``` ### Verify Connectors Check the logs for a successful startup message, such as: ```bash 2025-08-05T14:49:58.641+02:00 INFO 99856 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 3 endpoints beneath base path '/actuator' 2025-08-05T14:49:58.666+02:00 INFO 99856 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 9090 (http) with context path '/' 2025-08-05T14:49:58.702+02:00 INFO 99856 --- [ main] i.c.c.r.app.ConnectorRuntimeApplication : Started ConnectorRuntimeApplication in 1.286 seconds (process running for 1.386) ``` Check the health status of Connectors with the actuator endpoint: ```bash curl localhost:9090/actuator/health ```
Example output ```json { "status": "UP", "groups": ["readiness"], "components": { "camundaClient": { "status": "UP" }, "diskSpace": { "status": "UP", "details": { "total": -1, "free": -1, "threshold": -1, "path": "/home/user/connectors/.", "exists": true } }, "ping": { "status": "UP" }, "processDefinitionImport": { "status": "UP", "details": { "operateEnabled": true } }, "ssl": { "status": "UP", "details": { "validChains": [], "invalidChains": [] } }, "zeebeClient": { "status": "UP", "details": { "numBrokers": 1, "anyPartitionHealthy": true } } } } ```
## Next steps After setting up your cluster, many users typically do the following: - [Connect to an identity provider](/self-managed/components/orchestration-cluster/admin/connect-external-identity-provider.md) – integrate with an external identity system for authentication. - [Secure cluster communication](/self-managed/components/orchestration-cluster/zeebe/security/secure-cluster-communication.md) – protect traffic between cluster nodes. - [Secure client communication](/self-managed/components/orchestration-cluster/zeebe/security/secure-client-communication.md) – ensure secure communication between clients and the cluster. --- ## Configure RDBMS for manual installations Configure RDBMS secondary storage drivers, connections, and initial schema for **manual** Camunda 8 installations (VM, bare metal, or standalone Java). ## Prerequisites - **Supported RDBMS**: See the [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md). - **JDBC drivers**: PostgreSQL, MariaDB, SQL Server, and H2 are bundled. Oracle and MySQL must be user-supplied. - **Java 21+**: Required for Orchestration Cluster components (Zeebe, Operate, Tasklist, Identity). Management Identity remains Java 17+. See [supported environments](/reference/supported-environments.md). - **Database user**: Needs DDL permissions (CREATE TABLE, ALTER TABLE, DROP TABLE) for schema initialization. ## JDBC driver management ### Bundled drivers Camunda bundles these JDBC drivers for redistribution: | Database | Driver artifact | | -------------------- | -------------------------------------- | | PostgreSQL | `org.postgresql:postgresql` | | MariaDB | `org.mariadb.jdbc:mariadb-java-client` | | Microsoft SQL Server | `com.microsoft.sqlserver:mssql-jdbc` | | H2 | `com.h2database:h2` | | AWS Aurora JDBC | Bundled | You can optionally supply your own version of any bundled driver for flexibility or compliance requirements. ### User-supplied drivers (Oracle, MySQL) **Recommended approach**: Place drivers in `/opt/camunda-drivers` and set CLASSPATH: ```bash export CLASSPATH="/opt/camunda-drivers/*:$CLASSPATH" ./camunda.sh ``` For Docker, mount external drivers using a volume. The driver JAR must be placed directly in the mounted directory (no subdirectories): ```yaml services: camunda: image: camunda/camunda-platform:8.9.0 ports: - "8080:8080" - "26500:26500" - "9600:9600" environment: CAMUNDA_DATA_SECONDARY_STORAGE_TYPE: rdbms CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_URL: jdbc:mysql://mysql:3306/camunda CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_USERNAME: camunda CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_PASSWORD: demo volumes: - /path/to/driver-lib:/driver-lib ``` ## Database setup ### Step 1: Create database and user PostgreSQL example: ```sql CREATE DATABASE camunda ENCODING 'UTF8'; CREATE USER camunda WITH PASSWORD 'your-secure-password'; GRANT CONNECT ON DATABASE camunda TO camunda; GRANT USAGE ON SCHEMA public TO camunda; GRANT CREATE ON DATABASE camunda TO camunda; ``` For other databases, see [RDBMS Helm configuration](/self-managed/deployment/helm/configure/database/rdbms.md). ### Step 2: Configure connection Use the unified Camunda configuration properties: ```bash export CAMUNDA_DATA_SECONDARY_STORAGE_TYPE=rdbms export CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_URL="jdbc:postgresql://localhost:5432/camunda" export CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_USERNAME="camunda" export CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_PASSWORD="your-secure-password" ``` ### Step 3: Schema initialization By default, Liquibase automatically creates the schema on first startup. To manually manage schema: ```bash export CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_AUTO_DDL=false ``` Then apply SQL/Liquibase scripts manually using your DBA tools. See [access SQL and Liquibase scripts](/self-managed/deployment/helm/configure/database/access-sql-liquibase-scripts.md). ## Orchestration Cluster connection parameters The Orchestration Cluster uses a single RDBMS configuration shared across orchestration services and UIs that read from secondary storage. Set the connection properties as shown in [Step 2: Configure connection](#step-2-configure-connection) above. ### Zeebe exporter configuration Configure Zeebe's exporter flush behavior: ```bash export CAMUNDA_DATA_SNAPSHOTPERIOD=5m export CAMUNDA_DATA_EXPORTERS_RDBMS_CLASSNAME=io.camunda.exporter.rdbms.RdbmsExporter export CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_FLUSHINTERVAL=PT0.5S export CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_QUEUESIZE=1000 ``` ## Schema management ### Liquibase (recommended) Liquibase is the recommended approach for schema management. It is supported for the Orchestration Cluster and automatically applies migrations on startup (when `auto-ddl=true`, the default). When Liquibase runs successfully, you'll see this log entry at INFO level: ```text [INFO] io.camunda.application.commons.rdbms.MyBatisConfiguration - Initializing Liquibase for RDBMS with global table trimmedPrefix ''. ``` ### Manual SQL execution Manual SQL execution is supported as an alternative. When using manual SQL: - You must strictly adhere to the bundled scripts in the order provided. - Camunda cannot guarantee future updates will work if you modify scripts. - This approach is useful when DBAs require strict control over database changes. Download scripts: [Access SQL and Liquibase scripts](/self-managed/deployment/helm/configure/database/access-sql-liquibase-scripts.md). ## Next steps - [Operations and maintenance](/self-managed/deployment/manual/rdbms/operations.md) - [Secondary storage architecture](/self-managed/reference-architecture/reference-architecture.md#secondary-storage-architecture) - [RDBMS production architecture](/self-managed/deployment/manual/rdbms/rdbms-production-architecture.md) - [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) - [Access SQL and Liquibase scripts](/self-managed/deployment/helm/configure/database/access-sql-liquibase-scripts.md) --- ## Manual installation with RDBMS Install Camunda 8 Self-Managed manually on a VM, bare-metal server, or standalone Java runtime while using a relational database (RDBMS) as **secondary storage**. :::caution Manual installation is **not** supported for Kubernetes. If you run on Kubernetes, use the [Helm charts](/self-managed/deployment/helm/index.md). ::: ## Manual deployment data flow For backend trade-offs and production architecture decisions, see [secondary storage architecture](/self-managed/reference-architecture/reference-architecture.md#secondary-storage-architecture). In this manual deployment path, the Orchestration Cluster reads from a single configured secondary storage type: RDBMS. However, the Zeebe Broker can export to multiple targets simultaneously. If you deploy Optimize, configure both the RDBMS exporter (for Orchestration Cluster operations) and an Elasticsearch/OpenSearch exporter for Optimize. ```mermaid graph LR subgraph oc[Orchestration Cluster] broker[Zeebe Broker] rdbms_exporter[RDBMS Exporter] doc_exporter["Elasticsearch/OpenSearch Exporter\n(for Optimize)"] api[Orchestration Cluster API] apps[Operate · Tasklist · Admin] end subgraph storage[Data Stores] rdbms[RDBMS secondary storage] es["Elasticsearch/OpenSearch for Optimize"] end opt[Optimize] broker -->|Export orchestration data| rdbms_exporter rdbms_exporter -->|Write| rdbms apps -->|Use| api broker -.->|Optional export for Optimize| doc_exporter doc_exporter -.->|Optional write| es api -->|Query| rdbms opt -->|Read/Write| es style rdbms_exporter fill:#e4eef8,stroke:#2272c9,color:#14082c style doc_exporter fill:#e4eef8,stroke:#2272c9,color:#14082c style api fill:#e4eef8,stroke:#2272c9,color:#14082c style apps fill:#e4eef8,stroke:#2272c9,color:#14082c style rdbms fill:#fde8da,stroke:#fc5d0d,color:#14082c style es fill:#fde8da,stroke:#fc5d0d,color:#14082c style opt fill:#e8fdf1,stroke:#10c95d,color:#14082c style oc fill:#f0f5ff,stroke:#2272c9 style storage fill:#fff8f4,stroke:#fc5d0d ``` **Key points:** - Operate, Tasklist, and Admin use the Orchestration Cluster API, and that API reads from the configured secondary storage (RDBMS). - Optimize requires Elasticsearch or OpenSearch and reads and writes directly to it. - The Zeebe Broker can export to multiple targets simultaneously to support this architecture. ## Supported installation targets - VM-based deployments - Bare-metal installations - Standalone Java application deployments ## Prerequisites - **Supported RDBMS**: See [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md). - **JDBC drivers**: See [RDBMS configuration](/self-managed/deployment/manual/rdbms/configuration.md). - **Schemas and scripts**: Use the bundled SQL or Liquibase scripts for schema creation and upgrades. See [Access SQL and Liquibase scripts](/self-managed/deployment/helm/configure/database/access-sql-liquibase-scripts.md). ## When to choose manual installation Choose manual installation when you run Camunda 8 on **VMs, bare metal, or a standalone Java runtime** and need full control over the operating system, networking, and lifecycle management. If you run on Kubernetes, use [Helm charts](/self-managed/deployment/helm/index.md). For local development or evaluation, consider [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md). ## Supported features The Orchestration Cluster is fully supported with RDBMS secondary storage for workflow execution, instance monitoring, and user task management. ## Get started 1. [Configure drivers and connections](/self-managed/deployment/manual/rdbms/configuration.md). 2. Review [operations and maintenance](/self-managed/deployment/manual/rdbms/operations.md) for backup, upgrades, troubleshooting, and tuning. 3. Review [secondary storage architecture](/self-managed/reference-architecture/reference-architecture.md#secondary-storage-architecture) for backend trade-offs and [manual production architecture](/self-managed/deployment/manual/rdbms/rdbms-production-architecture.md) for manual deployment topology guidance. ## Related documentation - [Secondary storage architecture](/self-managed/concepts/secondary-storage/index.md) - [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) - [Manual installation overview](/self-managed/deployment/manual/install.md) --- ## Operations and maintenance for RDBMS manual installations Operate and maintain RDBMS secondary storage for manual Camunda 8 installations (VM, bare metal, or standalone Java). ## Operations ### Backup and restore **DBA owns backups** using native RDBMS tools (pg_dump, mysqldump, RMAN, etc.). Camunda handles consistency. - Use vendor-recommended backup procedures. - Backup frequency depends on your RPO (Recovery Point Objective). - Database backups capture consistent state (Camunda flushes synchronously). - Zeebe exporter position is stored in RDBMS. **Restore procedure**: 1. Restore database from backup. 2. Start Camunda pointing to restored database. 3. Check logs for Liquibase completion and RdbmsExporter success. ### Schema upgrades 1. Backup your database. 2. Download scripts for target version: [Access SQL and Liquibase scripts](/self-managed/deployment/helm/configure/database/access-sql-liquibase-scripts.md). 3. **If using autoDDL (default)**: Liquibase runs on Zeebe startup. 4. **If using manual schema management**: Apply scripts manually, then start new Camunda version. ### Disabling automatic schema updates For strict change control environments: ```bash export CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_AUTO_DDL=false ``` DBA must apply schema changes manually using scripts. Zeebe fails to start if schema is out of date. ## Verification and troubleshooting ### Success indicators **Liquibase migration complete:** ``` [INFO] io.camunda.application.commons.rdbms.MyBatisConfiguration - Initializing Liquibase for RDBMS with global table trimmedPrefix ''. ``` No errors in Liquibase logs means migration was successful. **RdbmsExporter running:** ``` [INFO] io.camunda.exporter.rdbms.RdbmsExporter - [RDBMS Exporter] RdbmsExporter created with Configuration: flushInterval=PT0.5S, queueSize=1000 [INFO] io.camunda.exporter.rdbms.RdbmsExporter - [RDBMS Exporter] Exporter opened with last exported position 126 ``` No errors in RdbmsExporter logs means the exporter is healthy. For more details about exported records, enable DEBUG log level. ### Common failure modes | Symptom | Root cause | Fix | | ------------------------------------------------------------ | ------------------------------- | ----------------------------------------------------- | | `SQLNonTransientConnectionException: Socket fail to connect` | Invalid URL or unreachable host | Verify JDBC URL format and hostname/port resolution | | `SQLInvalidAuthorizationSpecException: Access denied` | Wrong credentials | Check username/password and database user permissions | | `Failed to load driver class oracle.jdbc.OracleDriver` | Missing JDBC driver | Verify driver JAR in `/driver-lib` or classpath | | `Table 'camunda.EXPORTER_POSITION' doesn't exist` | autoDDL=false on empty DB | Run schema initialization scripts manually | **All failure modes above prevent Camunda startup.** ### Auto-DDL behavior When started with `auto-ddl=false` on an empty database, the RdbmsExporter throws errors like: ``` ### Error querying database. Cause: java.sql.SQLSyntaxErrorException: (conn=3) Table 'camunda.EXPORTER_POSITION' doesn't exist ### The error may exist in URL [jar:file:.../camunda-db-rdbms-8.9.0-SNAPSHOT.jar!/mapper/ExporterPositionMapper.xml] ### The error occurred while setting parameters ### SQL: SELECT PARTITION_ID, EXPORTER, LAST_EXPORTED_POSITION, CREATED, LAST_UPDATED FROM EXPORTER_POSITION WHERE PARTITION_ID = ? ### Cause: java.sql.SQLSyntaxErrorException: (conn=3) Table 'camunda.EXPORTER_POSITION' doesn't exist ``` **Solution**: Run schema initialization scripts manually using the bundled SQL or Liquibase scripts before starting Camunda. ## Performance tuning ### Exporter batching ```bash # Lower flush interval = lower latency, more frequent writes export CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_FLUSHINTERVAL=PT0.1S # Higher queue size = higher throughput, more memory export CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_QUEUESIZE=5000 ``` ### Connection pooling Tune based on your workload. Camunda uses the Hikari connection pool following Spring Boot best practices: ```bash export CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_CONNECTION_POOL_MAXIMUM_POOL_SIZE=20 export CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_CONNECTION_POOL_MINIMUM_IDLE=10 export CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_CONNECTION_POOL_IDLE_TIMEOUT=600000 export CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_CONNECTION_POOL_MAX_LIFETIME=1800000 export CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_CONNECTION_POOL_CONNECTION_TIMEOUT=30000 ``` ### Database tuning Consult your database vendor's guides: - **PostgreSQL**: Index tuning, autovacuum settings - **Oracle**: SGA size, parallel execution - **MySQL/MariaDB**: Buffer pool sizing - **SQL Server**: Memory configuration - **AWS Aurora**: Consult AWS RDS documentation for Aurora-specific tuning ## Security ### TLS/SSL All RDBMS connections must use TLS in production: ```bash # PostgreSQL export CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_URL="jdbc:postgresql://localhost:5432/camunda?sslmode=require" # See [RDBMS Helm configuration](/self-managed/deployment/helm/configure/database/rdbms.md) for other databases ``` ### Secrets Never hardcode passwords. Use environment variable injection: ```bash export CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_PASSWORD=$(cat /run/secrets/db_password) ``` ### Database permissions Create user with minimum required privileges: ```sql -- PostgreSQL example CREATE ROLE camunda LOGIN PASSWORD 'secure-password'; GRANT CONNECT ON DATABASE camunda TO camunda; GRANT USAGE ON SCHEMA public TO camunda; GRANT CREATE ON DATABASE camunda TO camunda; ``` Avoid DROP, TRUNCATE, SUPERUSER, or other unnecessary privileges. ## Production deployment Before deploying to production, review [secondary storage architecture](/self-managed/reference-architecture/reference-architecture.md#secondary-storage-architecture) for backend trade-offs and the [manual production architecture guide](/self-managed/deployment/manual/rdbms/rdbms-production-architecture.md) for recommended topology. Then use the verification steps in the [troubleshooting section](#verification-and-troubleshooting) above to validate your RDBMS setup is healthy. ## Next steps - [RDBMS configuration](/self-managed/deployment/manual/rdbms/configuration.md) - [Secondary storage architecture](/self-managed/reference-architecture/reference-architecture.md#secondary-storage-architecture) - [RDBMS production architecture](/self-managed/deployment/manual/rdbms/rdbms-production-architecture.md) - [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) - [Access SQL and Liquibase scripts](/self-managed/deployment/helm/configure/database/access-sql-liquibase-scripts.md) --- ## Production architecture for Camunda 8 with RDBMS Apply RDBMS secondary storage to a **manual deployment**. For the canonical production architecture and trade-off guidance across manual, containerized, and Kubernetes deployments, see [secondary storage architecture](/self-managed/reference-architecture/reference-architecture.md#secondary-storage-architecture). ## Manual deployment baseline For manual production deployments with RDBMS, use these baseline decisions: - Run at least three brokers across three availability zones for high availability. - Use an external supported RDBMS for the Orchestration Cluster's secondary storage. - Keep the database in the same region as the Orchestration Cluster. - Use managed database services or vendor-supported high-availability mechanisms when possible. For backend selection trade-offs, Optimize requirements, and Elasticsearch/OpenSearch versus RDBMS comparison guidance, see [secondary storage architecture](/self-managed/reference-architecture/reference-architecture.md#secondary-storage-architecture). ## Example topology For production deployments with RDBMS, Camunda recommends a **HA Zeebe cluster backed by an external managed RDBMS**: ```mermaid graph TB subgraph oc["Orchestration Cluster (HA)"] direction TB gateway["Gateway(gRPC / HTTP)"] subgraph brokers["Brokers"] b1["Broker 1(AZ-1)"] b2["Broker 2(AZ-2)"] b3["Broker 3(AZ-3)"] end api["Orchestration Cluster API (v2)"] gateway --> brokers gateway --> api b1 -.->|gRPC| b2 b2 -.->|gRPC| b3 b1 -.->|gRPC| b3 end subgraph storage["Secondary Storage"] rdbms["External RDBMSPostgreSQL • MariaDBOracle • MySQL • SQL ServerHA: 3-way replicationacross AZs"] opt["Elasticsearch/OpenSearch(for Optimize only)"] end clients["ClientsWeb UIs • Workers • ApplicationsOperate • Tasklist • Identity • Connectors"] clients --> gateway brokers -->|Export events| rdbms brokers -->|Export analyticsOptional| opt style oc fill:#e1f5ff style storage fill:#fff3e0 style gateway fill:#f3e5f5 ``` ### Key characteristics #### Clustering - Minimum three brokers for production HA - Each broker in separate availability zone - Default replication factor 3 (spans AZs) #### Secondary storage - Single external managed RDBMS instance - Database handles replication and failover - Camunda does not manage database HA #### Data flow - Processes are executed - State is flushed to RDBMS - Orchestration Cluster applications (Operate, Tasklist, and Identity) and API clients access data through the Orchestration Cluster interfaces; they do not directly access secondary storage ## When document-store secondary storage is required Elasticsearch or OpenSearch is required **only for Optimize**. When Optimize is enabled: - Deploy Elasticsearch/OpenSearch alongside your RDBMS - Enable the Elasticsearch or OpenSearch exporter to store analytics data - The Orchestration Cluster uses RDBMS as secondary storage Without Optimize: RDBMS-only stack is fully supported. ## Production constraints ❌ **ES/OS ↔ RDBMS migration not supported**: Choose your secondary storage backend before production. No automated migration tools are available. ❌ **Uniform broker configuration required**: All brokers in the Orchestration Cluster must export to the same secondary storage backend for its Orchestration Cluster indices. In addition, you may run Elasticsearch/OpenSearch to support Optimize, but the Orchestration Cluster still uses a single backend for its own secondary storage. ❌ **v1 API not supported**: Only the v2 Orchestration Cluster REST API works with RDBMS. See [migrate to the Orchestration Cluster API](/apis-tools/migration-manuals/migrate-to-camunda-api.md). ## Network and security - **Orchestration Cluster ↔ RDBMS**: Private network connectivity with TLS in production - **Network isolation**: Restrict RDBMS access to Orchestration Cluster pods only (use NetworkPolicies) ## Supported scenarios ✅ **Single-node orchestration + external RDBMS** (non-HA, acceptable for non-critical workloads) ✅ **HA Zeebe cluster + external managed RDBMS** (recommended for production) ✅ **Managed database services** (AWS Aurora, Azure Database, GCP Cloud SQL) ## Next steps - [Secondary storage architecture](/self-managed/reference-architecture/reference-architecture.md#secondary-storage-architecture) - [RDBMS configuration](/self-managed/deployment/manual/rdbms/configuration.md) - [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) - [RDBMS Helm configuration](/self-managed/deployment/helm/configure/database/rdbms.md) --- ## Camunda back up and restore Use the backup feature to back up and restore your Camunda 8 Self-Managed components and cluster. ## About this guide This guide covers how to back up and restore your Camunda 8 Self-Managed components and cluster. Automate backup and restore procedures with tools that meet your organization's requirements. :::info With Camunda 8.8, the architecture was updated. For clarity, the [Orchestration Cluster](/reference/glossary.md#orchestration-cluster) now consists of: - Zeebe - Web Applications (Operate and Tasklist) - Identity Depending on context, we may refer to a specific subcomponent of the Orchestration Cluster where appropriate. ::: This guide covers two backup paths depending on your secondary storage. Choose the path that matches your deployment: | | Elasticsearch / OpenSearch | Relational databases (RDBMS) | | ----------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- | | **Components covered** | Zeebe, Operate, Tasklist, Optimize | Zeebe, Operate, Tasklist only | | **Optimize backup** | Included | Not supported — use the ES/OpenSearch path | | **Backup coordination** | All components must be backed up together using the same backup ID | Decoupled — Zeebe and RDBMS are backed up independently; Camunda aligns them automatically during restore | | **Continuous backups** | Not supported | Supported, fine-grained restore options available | | **Backup ID** | User-supplied integer | Auto-generated by cluster | :::info The RDBMS backup path does **not** support Optimize. If you use Optimize, you must back up and restore Camunda components using the [Elasticsearch / OpenSearch path](#elasticsearch--opensearch). ::: ### Elasticsearch / OpenSearch Covers all Orchestration Cluster components and Optimize. Back up and restore with no downtime using coordinated Elasticsearch or OpenSearch snapshots. All components must use the same backup ID to ensure consistency. ### Relational databases (RDBMS) This is the **first phase of new backup capabilities** enabled by using an RDBMS as secondary storage. It covers Orchestration Cluster components only (Zeebe, Operate, and Tasklist). Optimize is not included. Using an RDBMS as secondary storage unlocks three new capabilities not available in the Elasticsearch / OpenSearch path: - **Decoupled backups**: Zeebe (primary storage) and the RDBMS (secondary storage) can be backed up independently, on their own schedules. During restore, Camunda automatically aligns the two backups — there is no need to coordinate a shared backup ID or take snapshots at the same time. - **Scheduled backups**: Because backups are decoupled now, Zeebe can take backups automatically on a fixed schedule, no need to call the backup API externally. - **Point in time restore**: Zeebe continuously takes snapshots of its log stream. This creates a range of available restore points that you can restore to by timestamp, rather than being limited to a specific backup ID. :::note - The examples in this guide are based on using the following tools: [curl](https://curl.se/), [jq](https://jqlang.org/), and [kubectl](https://kubernetes.io/de/docs/reference/kubectl/). ::: ## Considerations ### Backup IDs (Elasticsearch / OpenSearch path only) When using Elasticsearch or OpenSearch as secondary storage, each component backup is identified by a user-supplied integer backup ID. The backup ID must be greater than the ID of any previous backup. :::note We recommend using the Unix timestamp as the backup ID. ::: :::note When using the RDBMS path, backup IDs are auto-generated by the cluster and do not need to be managed manually. ::: The steps outlined on this page are generally applicable for any kind of deployment but might differ slightly depending on your setup. ### Management API The management API is an extension of the [Spring Boot Actuator](https://docs.spring.io/spring-boot/reference/actuator/index.html), typically used for monitoring and other operational purposes. This is not a public API and not exposed. You will need direct access to your Camunda cluster to be able to interact with these management APIs. This is why you'll often see the reference to `localhost`. Direct access will depend on your deployment environment. For example, direct Kubernetes cluster access with [port-forwarding](https://kubernetes.io/docs/reference/kubectl/generated/kubectl_port-forward/) or [exec](https://kubernetes.io/docs/reference/kubectl/generated/kubectl_exec/) to execute commands directly on Kubernetes pods. In a manual deployment you will need to be able to reach the machines that host Camunda. Typically, the management port is on port `9600` but might differ on your setup and on the components. You can find the default for each component in their configuration page. | Component | Port | | ----------------------------------------------------------------------------------------------------------------------- | ---- | | [Optimize](/self-managed/components/optimize/configuration/system-configuration.md#container) | 8092 | | [Orchestration Cluster](/self-managed/components/orchestration-cluster/zeebe/configuration/gateway.md#managementserver) | 9600 | #### Examples for Kubernetes approaches Port-forwarding allows you to temporarily bind a remote Kubernetes cluster port of a service or pod directly to your local machine, allowing you to interact with it via `localhost:PORT`. Since the services are bound to your local machine, you **cannot reuse the same port for all port-forwards** unless you start and stop each one based on usage. To avoid this limitation, the examples use different local ports for each service, allowing them to run simultaneously without conflict. ```bash export CAMUNDA_RELEASE_NAME="camunda" # kubectl port-forward services/$SERVICE_NAME $LOCAL_PORT:$REMOTE_PORT kubectl port-forward services/$CAMUNDA_RELEASE_NAME-zeebe-gateway 9600:9600 & \ kubectl port-forward services/$CAMUNDA_RELEASE_NAME-optimize 8092:8092 & \ kubectl port-forward services/$CAMUNDA_RELEASE_NAME-elasticsearch 9200:9200 & ``` Using the bash instruction `&` at the end of each line would run the command in a subshell allowing the use of a single terminal. An alternative to port-forwarding is to run commands directly on Kubernetes pods. In this example we're going to spawn a temporary pod to execute a curl request. Alternatives are to use existing pods within the namespace. Camunda's pod includes different base images, each with a different feature set. ```bash # following will create a temporary alias within your terminal to overwrite the normal curl export CAMUNDA_NAMESPACE="camunda" export CAMUNDA_RELEASE_NAME="camunda" # temporary overwrite of curl, can be removed with `unalias curl` again alias curl="kubectl run curl --rm -i -n $CAMUNDA_NAMESPACE --restart=Never --image=alpine/curl -- -sS" curl $CAMUNDA_RELEASE_NAME-zeebe-gateway:9600/actuator/health curl $CAMUNDA_RELEASE_NAME-optimize:8092/actuator/health curl $CAMUNDA_RELEASE_NAME-elasticsearch:9200/_cluster/health ``` This allows you to directly execute commands within the namespace and communicate with available services. The examples in this guide showcase the backup process in a manual fashion to help you fully understand the process. You might want to use [Kubernetes Cronjobs](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/) to automate the backup process for your own use case based on your own environment on a regular schedule. Kubernetes Cronjobs will spawn a [Job](https://kubernetes.io/docs/concepts/workloads/controllers/job/) on a regular basis. The job will run a defined image within a given namespace, allowing you to run commands and interact with the environment. :::note You can see further examples from Camunda consultants in the [Backup and Restore Workshop](https://github.com/camunda-consulting/c8-devops-workshop/tree/main/03%20-%20Lab%203%20-%20Backup%20and%20Restore). You can use these examples to achieve similar automation. ::: ### ContextPath If you are defining the `contextPath` in the Camunda Helm chart or the `management.server.servlet.context-path` in a standalone setup, your API requests must prepend the value specific to the `contextPath` for the individual component. If the `management.server.port` is defined this also applies to `management.endpoints.web.base-path`. You can learn more about this behavior in the [Spring Boot documentation](https://docs.spring.io/spring-boot/docs/2.1.7.RELEASE/reference/html/production-ready-monitoring.html#production-ready-customizing-management-server-context-path). :::warning Optimize Helm chart Exception Setting the `contextPath` in the Helm chart for Optimize will not overwrite the `contextPath` of the management API, it will remain as `/`. :::
Example If you are defining the `contextPath` for the Orchestration Cluster in the Camunda Helm chart: ```bash orchestration: contextPath: /example ``` A call to the management API of the Orchestration Cluster would look like the following example: ```bash ORCHESTRATION_CLUSTER_MANAGEMENT_API=http://localhost:9600 curl $ORCHESTRATION_CLUSTER_MANAGEMENT_API/example/actuator/health ``` Without the `contextPath` it would just be: ```bash ORCHESTRATION_CLUSTER_MANAGEMENT_API=http://localhost:9600 curl $ORCHESTRATION_CLUSTER_MANAGEMENT_API/actuator/health ```
--- ## Camunda backup creation (Elasticsearch/OpenSearch) Back up your Camunda 8 Self-Managed components and cluster. ### Prerequisites The following prerequisites are required before you can create a backup. | Prerequisite | Description | | :------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Set up a snapshot repository in the secondary datastore. | Configure a snapshot repository on the datastore itself:[Elasticsearch snapshot repository](https://www.elastic.co/docs/deploy-manage/tools/snapshot-and-restore/manage-snapshot-repositories)[OpenSearch snapshot repository](https://docs.opensearch.org/docs/latest/tuning-your-cluster/availability-and-recovery/snapshots/snapshot-restore/)Note: For Elasticsearch configuration with the Camunda Helm chart on AWS EKS using IRSA, see [configuration example](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/irsa.md#backup-related). | | Configure component backup storage. | Configure the backup storage for the following components. This is also important for restoring a backup.[Operate](/self-managed/components/orchestration-cluster/operate/operate-configuration.md#backups)[Optimize Elasticsearch](/self-managed/components/optimize/configuration/system-configuration.md#elasticsearch-backup-settings) / [Optimize OpenSearch](/self-managed/components/optimize/configuration/system-configuration.md#opensearch-backup-settings)[Tasklist](/self-managed/components/orchestration-cluster/tasklist/tasklist-configuration.md#backups) | | Configure Zeebe backup storage. | Configure the backup storage for Zeebe. This is required regardless of your secondary storage choice. See [Zeebe backup configuration](/self-managed/components/orchestration-cluster/zeebe/configuration/broker.md#zeebebrokerdatabackup). | :::note You should keep the backup storage of the components configured at all times to ease the backup and restore process and avoid unnecessary restarts. ::: :::tip You can use the same backup storage location for both Elasticsearch / OpenSearch snapshots and Zeebe partition backups, as long as different paths are configured: - Set the `basePath` for Zeebe. - Set the `base_path` for Elasticsearch / OpenSearch. To learn more about how to configure these settings, refer to the prerequisites linked documentation above. ::: ## Considerations ### Why you should use backup and restore :::note The Camunda 8.8 release introduces breaking changes for [Operate and Tasklist](../webapps-backup.md). ::: :::note If the Camunda applications cannot access Elasticsearch with cluster-level privileges, run the backup of Operate and Tasklist indices (steps 2 and 4 from the [backup](#back-up-process) procedure) as a standalone application separate from the main application. For details, see the **standalone backup application** for [Elasticsearch](/self-managed/concepts/databases/elasticsearch/elasticsearch-without-cluster-privileges.md#standalone-backup-application) or [OpenSearch](/self-managed/concepts/databases/elasticsearch/opensearch-without-cluster-privileges.md#standalone-backup-application). ::: The Camunda 8 components like the Orchestration Cluster and Optimize store data in various formats and across multiple indices in Elasticsearch or OpenSearch. Because of this distributed and interdependent architecture, creating a consistent and reliable backup requires coordination between the components. For example, using Elasticsearch or OpenSearch's native snapshot capabilities directly does not produce a coherent backup. This is because Operate, Tasklist, and Optimize each manage their data across multiple indices, which cannot be reliably captured together without involvement from the components that understand their structure. For this reason, backups must be initiated through each component individually, using their built-in backup functionality. The same principle applies to Zeebe. Backups must be scheduled through Zeebe to ensure a consistent snapshot of all partition data. Simply taking a disk-level snapshot of each Zeebe Broker is not enough, as the brokers operate independently and data may not be aligned across them at the time of the snapshot. Since disk-level backups are not synchronized, this can lead to inconsistencies and invalid recovery points. A complete backup of a Camunda 8 cluster using Elasticsearch or OpenSearch includes: - Backups of Web Applications (Operate, Tasklist), and Optimize (triggered through their APIs). - Backup of indices from Elasticsearch/OpenSearch containing exported Zeebe records. - A Zeebe Broker partition backup (triggered through its API). Because the data across these systems is interdependent, all components must be backed up as part of the same backup window. Backups taken independently at different times may not align and could result in an unreliable restore point. :::warning To ensure a consistent backup, you must follow the process outlined in this guide. Deviating from it can result in undetected data loss, as there is no reliable method to verify cross-component data integrity after backup. ::: Following the documented procedure results in a hot backup, meaning that: - Zeebe continues to process and export data. - Web Applications (Operate, Tasklist), and Optimize remain fully operational during the backup process. This ensures high availability while preserving the integrity of the data snapshot. ### Additional considerations In this guide, `$BACKUP_ID` is the placeholder for a single backup ID, such as `1748937221`. When you create a full Camunda 8 backup using Elasticsearch or OpenSearch, use the same backup ID for each component backup in that backup set. For example, backup `1748937221` of Camunda 8 consists of the following component backups: - A Zeebe backup with backup ID `1748937221`, which contains one snapshot per partition. - An Optimize backup with backup ID `1748937221`, which can contain multiple Optimize snapshots. - A Web Applications backup with backup ID `1748937221`, which can contain multiple Operate and Tasklist snapshots. This means one `backupId` identifies the full Camunda 8 backup set, while each component can still create multiple underlying snapshots. The backup ID must be an integer and greater than any previous backup ID. Optimize is not part of the Web Applications backup API and needs to be executed separately to successfully make a backup. Depending on your deployment configuration, you may not have Optimize deployed. It is safe to ignore the backup instructions for Optimize if it is not deployed. :::warning breaking change As of Camunda 8.8, the `indexPrefix` of Operate and Tasklist must match. By default, it is set to `""`. If overridden, it must be set consistently across Operate and Tasklist. ::: :::warning breaking change As of Camunda 8.8, configuring Operate and Tasklist with different repository names will potentially create multiple backups in different repositories. ::: :::warning breaking changes As of Camunda 8.8, the `/actuator` endpoints for backups have been moved to `/actuator/backupHistory` (Web Applications) and `/actuator/backupRuntime` (Zeebe). The previous `/actuator/backups` endpoint is still active only if the applications are deployed standalone (each application is running in its own process). ::: ## About the backup process To create a backup, complete the following [backup process](#back-up-process). You can also optionally [back up your Web Modeler data](#back-up-web-modeler-data). :::caution before you begin - To create a consistent backup, you **must** complete the steps in the order outlined below. - You must complete the [prerequisites](#prerequisites) before creating a backup. ::: ## Back up process ### Example API endpoint definition :::note This depends heavily on your setup. The following examples are based on those given in the [Management API](../backup-and-restore.md#management-api) section for Kubernetes using either active port-forwarding or an override of the local `curl` command. As noted in the [Management API](../backup-and-restore.md#management-api) section, this API is typically not publicly exposed. Therefore, you will need to access it directly using any means available within your environment. ::: ```bash # only export the BACKUP_ID once as it has to stay consistent throughout the backup procedure export BACKUP_ID=$(date +%s) # unix timestamp as unique always increasing ID export ELASTIC_SNAPSHOT_REPOSITORY="camunda" # the name of your snapshot repository export ELASTIC_ENDPOINT="http://localhost:9200" export OPENSEARCH_SNAPSHOT_REPOSITORY="camunda" # the name of your snapshot repository export OPENSEARCH_ENDPOINT="" # highly dependent on your environment export ORCHESTRATION_CLUSTER_MANAGEMENT_API="http://localhost:9600" export OPTIMIZE_MANAGEMENT_API="http://localhost:9620" ``` ```bash # only export the BACKUP_ID once as it has to stay consistent throughout the backup procedure export BACKUP_ID=$(date +%s) # unix timestamp as unique always increasing ID export CAMUNDA_RELEASE_NAME="camunda" export ELASTIC_SNAPSHOT_REPOSITORY="camunda" # the name of your snapshot repository export ELASTIC_ENDPOINT="$CAMUNDA_RELEASE_NAME-elasticsearch:9200" export OPENSEARCH_SNAPSHOT_REPOSITORY="camunda" # the name of your snapshot repository export OPENSEARCH_ENDPOINT="" # highly dependent on your environment export ORCHESTRATION_CLUSTER_MANAGEMENT_API="http://$CAMUNDA_RELEASE_NAME-zeebe-gateway:9600" export OPTIMIZE_MANAGEMENT_API="http://$CAMUNDA_RELEASE_NAME-optimize:8092" ``` ### 1. Soft pause exporting in Zeebe This step uses the [management API](/self-managed/components/orchestration-cluster/zeebe/operations/management-api.md?exporting=softPause#exporting-api). This will continue exporting records, but not delete those records (log compaction) from Zeebe. This makes the backup a hot backup, as covered in the [why you should use backup and restore](../backup-and-restore.md#why-you-should-use-backup-and-restore). ```bash curl -XPOST "$ORCHESTRATION_CLUSTER_MANAGEMENT_API/actuator/exporting/pause?soft=true" ``` :::warning This endpoint always returns HTTP `200`. Check the `status` field in the response body to determine whether the operation succeeded: `204` indicates success and `500` indicates failure. If the request fails, verify that all brokers are running and retry. :::
Example output :::note Yes, 204 is the expected result and indicates a successful soft pause. ::: ```json { "body":null, "status":204, "contentType":null } ```
#### Behavior during a Zeebe hot backup During a hot backup, the Zeebe cluster remains fully operational: - Zeebe continues to accept new client requests (for example, starting process instances) and to process existing workflow instances. - Job workers and other external workers continue to receive and complete jobs. - Exporters continue to export records. While soft pause is active, Zeebe temporarily does not advance the exporter position, which prevents log compaction and increases broker disk usage for the duration of the backup window. Ensure broker disks have enough free space. - If a broker restarts while soft pause is active, some already-exported records may be exported again after the restart. This is expected, because exporting always resumes from the last acknowledged exporter position. The `/actuator/backupRuntime` API then creates a consistent backup of each partition while processing continues. The “wait for backup to complete” steps in this guide only poll backup status and do not introduce any additional pause in processing beyond the initial soft export pause. ### 2. Start the web applications backup (Operate / Tasklist) This step uses the [web applications management backup API](/self-managed/operational-guides/backup-restore/webapps-backup.md). ```bash curl -XPOST "$ORCHESTRATION_CLUSTER_MANAGEMENT_API/actuator/backupHistory" \ -H "Content-Type: application/json" \ -d "{\"backupId\": $BACKUP_ID}" ```
Example output ```json { "scheduledSnapshots":[ "camunda_webapps_1748937221_8.8.0_part_1_of_5", "camunda_webapps_1748937221_8.8.0_part_2_of_5", "camunda_webapps_1748937221_8.8.0_part_3_of_5", "camunda_webapps_1748937221_8.8.0_part_4_of_5", "camunda_webapps_1748937221_8.8.0_part_5_of_5" ] } ```
### 3. Start the Optimize backup This step uses the [Optimize management backup API](/self-managed/operational-guides/backup-restore/optimize-backup.md). ```bash curl -XPOST "$OPTIMIZE_MANAGEMENT_API/actuator/backups" \ -H "Content-Type: application/json" \ -d "{\"backupId\": $BACKUP_ID}" ```
Example output ```json { "message":"Backup creation for ID 1748937221 has been scheduled. Use the GET API to monitor completion of backup process" } ```
### 4. Wait for the web applications backup to complete This step uses the [web applications management backup API](/self-managed/operational-guides/backup-restore/webapps-backup.md). ```bash curl -s "$ORCHESTRATION_CLUSTER_MANAGEMENT_API/actuator/backupHistory/$BACKUP_ID" ```
Example output ```json { "backupId":1748937221, "state":"COMPLETED", "failureReason":null, "details":[ { "snapshotName":"camunda_webapps_1748937221_8.8.0_part_1_of_5", "state":"SUCCESS", "startTime":"2025-06-03T07:55:15.685+0000", "failures":[ ] }, { "snapshotName":"camunda_webapps_1748937221_8.8.0_part_2_of_5", "state":"SUCCESS", "startTime":"2025-06-03T07:55:16.288+0000", "failures":[ ] }, { "snapshotName":"camunda_webapps_1748937221_8.8.0_part_3_of_5", "state":"SUCCESS", "startTime":"2025-06-03T07:55:17.092+0000", "failures":[ ] }, { "snapshotName":"camunda_webapps_1748937221_8.8.0_part_4_of_5", "state":"SUCCESS", "startTime":"2025-06-03T07:55:17.293+0000", "failures":[ ] }, { "snapshotName":"camunda_webapps_1748937221_8.8.0_part_5_of_5", "state":"SUCCESS", "startTime":"2025-06-03T07:55:18.298+0000", "failures":[ ] } ] } ```
Alternatively as a one-line to wait until the state is `COMPLETED` using a while loop and jq to parse the response JSON. ```bash while [[ "$(curl -s "$ORCHESTRATION_CLUSTER_MANAGEMENT_API/actuator/backupHistory/$BACKUP_ID" | jq -r .state)" != "COMPLETED" ]]; do echo "Waiting..."; sleep 5; done; echo "Finished backup with ID $BACKUP_ID" ``` ### 5. Wait for the Optimize backup to complete This step uses the [Optimize management backup API](/self-managed/operational-guides/backup-restore/optimize-backup.md). ```bash curl -s "$OPTIMIZE_MANAGEMENT_API/actuator/backups/$BACKUP_ID" ```
Example output ```json { "backupId":1748937221, "failureReason":null, "state":"COMPLETED", "details":[ { "snapshotName":"camunda_optimize_1748937221_8.8.0_part_1_of_2", "state":"SUCCESS", "startTime":"2025-06-03T07:53:54.389+0000", "failures":[ ] }, { "snapshotName":"camunda_optimize_1748937221_8.8.0_part_2_of_2", "state":"SUCCESS", "startTime":"2025-06-03T07:53:54.389+0000", "failures":[ ] } ] } ```
Alternatively as a one-line to wait until the state is `COMPLETED` using a while loop and jq to parse the response JSON. ```bash while [[ "$(curl -s "$OPTIMIZE_MANAGEMENT_API/actuator/backups/$BACKUP_ID" | jq -r .state)" != "COMPLETED" ]]; do echo "Waiting..."; sleep 5; done; echo "Finished backup with ID $BACKUP_ID" ``` ### 6. Create a backup of the exported Zeebe indices in Elasticsearch/OpenSearch You can create this backup using the respective Snapshots API. By default, the old Elasticsearch or OpenSearch exporter creates indices with the prefix `zeebe-record`. If you configured a different prefix in the exporter, use that prefix instead. This remains relevant if you run Optimize, which still relies on the former exporters. The following uses the [Elasticsearch snapshot API](https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-snapshot-create) to create a snapshot. ```bash curl -XPUT "$ELASTIC_ENDPOINT/_snapshot/$ELASTIC_SNAPSHOT_REPOSITORY/camunda_zeebe_records_backup_$BACKUP_ID?wait_for_completion=true" \ -H 'Content-Type: application/json' \ -d '{ "indices": "zeebe-record*", "feature_states": ["none"] }' ```
Example output ```json { "snapshot":{ "snapshot":"camunda_zeebe_records_backup_1748937221", "uuid":"1p_HdzKeTZ-zY-SN1LJ9VQ", "repository":"camunda", "version_id":8521000, "version":"8.18.0", "indices":[ "zeebe-record_process_8.8.0_2025-06-03", "zeebe-record_job_8.8.0_2025-06-03", "zeebe-record_process-instance_8.8.0_2025-06-03", "zeebe-record_deployment_8.8.0_2025-06-03" ], "data_streams":[ ], "include_global_state":true, "state":"SUCCESS", "start_time":"2025-06-03T08:05:10.633Z", "start_time_in_millis":1748937910633, "end_time":"2025-06-03T08:05:11.336Z", "end_time_in_millis":1748937911336, "duration_in_millis":603, "failures":[ ], "shards":{ "total":8, "failed":0, "successful":8 }, "feature_states":[ ] } } ```
The following uses the [OpenSearch snapshot API](https://docs.opensearch.org/docs/latest/api-reference/snapshots/create-snapshot/) to create a snapshot. ```bash curl -XPUT "$OPENSEARCH_ENDPOINT/_snapshot/$OPENSEARCH_SNAPSHOT_REPOSITORY/camunda_zeebe_records_backup_$BACKUP_ID?wait_for_completion=true" \ -H 'Content-Type: application/json' \ -d '{ "indices": "zeebe-record*" }' ```
Example output ```json { "snapshot":{ "snapshot":"camunda_zeebe_records_backup_1748937221", "uuid":"PUFbcSJZT1Cqc4jY8OE2uA", "version_id":136408027, "version":"2.19.2", "remote_store_index_shallow_copy":false, "indices":[ "zeebe-record_process_8.8.0_2025-06-03", "zeebe-record_job_8.8.0_2025-06-03", "zeebe-record_process-instance_8.8.0_2025-06-03", "zeebe-record_deployment_8.8.0_2025-06-03" ], "data_streams":[ ], "include_global_state":true, "state":"SUCCESS", "start_time":"2025-06-03T09:37:45.623Z", "start_time_in_millis":1748943465623, "end_time":"2025-06-03T09:37:46.342Z", "end_time_in_millis":1748943466342, "duration_in_millis":719, "failures":[ ], "shards":{ "total":8, "failed":0, "successful":8 } } } ```
### 7. Wait for the backup of the exported Zeebe indices to complete before proceeding Using `?wait_for_completion=true` in the previous call, as outlined, ensures that the request only returns once the backup has completed. However, to double-check that the backup completed successfully, you can perform the following verification: The following uses the [Elasticsearch snapshot API](https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-snapshot-status-2) to get the snapshot status. ```bash curl "$ELASTIC_ENDPOINT/_snapshot/$ELASTIC_SNAPSHOT_REPOSITORY/camunda_zeebe_records_backup_$BACKUP_ID/_status" ```
Example output ``` { "snapshots":[ { "snapshot":"camunda_zeebe_records_backup_1748937221", "repository":"camunda", "uuid":"1p_HdzKeTZ-zY-SN1LJ9VQ", "state":"SUCCESS", "include_global_state":true, "shards_stats":{ "initializing":0, "started":0, "finalizing":0, "done":8, "failed":0, "total":8 }, "stats":{ "incremental":{ "file_count":0, "size_in_bytes":0 }, "total":{ "file_count":8, "size_in_bytes":0 }, "start_time_in_millis":1748937910633, "time_in_millis":0 }, "indices":{ "zeebe-record_process_8.8.0_2025-06-03", "zeebe-record_job_8.8.0_2025-06-03", "zeebe-record_process-instance_8.8.0_2025-06-03", "zeebe-record_deployment_8.8.0_2025-06-03" } } ] } ```
Using `?wait_for_completion=true` in the previous call, as outlined, ensures that the request only returns once the backup has finished. However, to double-check that the backup completed successfully, you can perform the following verification: The following uses the [OpenSearch snapshot API](https://docs.opensearch.org/docs/latest/api-reference/snapshots/get-snapshot-status/) to get the snapshot status. ```bash curl "$OPENSEARCH_ENDPOINT/_snapshot/$OPENSEARCH_SNAPSHOT_REPOSITORY/camunda_zeebe_records_backup_$BACKUP_ID/_status" ```
Example output ```json { "snapshots":[ { "snapshot":"camunda_zeebe_records_backup_1748937221", "repository":"camunda", "uuid":"PUFbcSJZT1Cqc4jY8OE2uA", "state":"SUCCESS", "include_global_state":true, "shards_stats":{ "initializing":0, "started":0, "finalizing":0, "done":8, "failed":0, "total":8 }, "stats":{ "incremental":{ "file_count":0, "size_in_bytes":0 }, "total":{ "file_count":8, "size_in_bytes":0 }, "start_time_in_millis":1748943465623, "time_in_millis":0 }, "indices":{ "zeebe-record_process_8.8.0_2025-06-03", "zeebe-record_job_8.8.0_2025-06-03", "zeebe-record_process-instance_8.8.0_2025-06-03", "zeebe-record_deployment_8.8.0_2025-06-03" } } ] } ```
### 8. Create the Zeebe broker backup This step uses the [Zeebe management backup API](/self-managed/operational-guides/backup-restore/zeebe-backup-and-restore.md). ```bash curl -XPOST "$ORCHESTRATION_CLUSTER_MANAGEMENT_API/actuator/backupRuntime" \ -H "Content-Type: application/json" \ -d "{\"backupId\": $BACKUP_ID}" ```
Example output ```json { "message":"A backup with id 1748937221 has been scheduled. Use GET actuator/backups/1748937221 to monitor the status." } ```
### 9. Wait for the Zeebe backup to complete before proceeding This step uses the [Zeebe management backup API](/self-managed/operational-guides/backup-restore/zeebe-backup-and-restore.md). ```bash curl "$ORCHESTRATION_CLUSTER_MANAGEMENT_API/actuator/backupRuntime/$BACKUP_ID" ```
Example output ```json { "backupId":1748937221, "state":"COMPLETED", "details":[ { "partitionId":1, "state":"COMPLETED", "createdAt":"2025-06-03T08:06:06.246997293Z", "lastUpdatedAt":"2025-06-03T08:06:10.408893628Z", "checkpointPosition":1, "brokerVersion":"8.8.0" } ] } ```
Alternatively as a one-line to wait until the state is `COMPLETED` using a while loop and jq to parse the response JSON. ```bash while [[ "$(curl -s "$ORCHESTRATION_CLUSTER_MANAGEMENT_API/actuator/backupRuntime/$BACKUP_ID" | jq -r .state)" != "COMPLETED" ]]; do echo "Waiting..."; sleep 5; done; echo "Finished backup with ID $BACKUP_ID" ``` ### 10. Resume exporting in Zeebe using the [management API](/self-managed/components/orchestration-cluster/zeebe/operations/management-api.md) ```bash curl -XPOST "$ORCHESTRATION_CLUSTER_MANAGEMENT_API/actuator/exporting/resume" ``` :::warning This endpoint always returns HTTP `200`. Check the `status` field in the response body to determine whether the operation succeeded: `204` indicates success and `500` indicates failure. If the request fails, verify that all brokers are running and retry. :::
Example output :::note Yes, 204 is the expected result and indicates a successful resume. ::: ```json { "body":null, "status":204, "contentType":null } ```
:::warning If any of the steps above fail, you might have to restart with a new backup ID. Ensure Zeebe exporting is resumed if the backup process force quits in the middle of the process. ::: ## (Optional) Back up Web Modeler data {#back-up-web-modeler-data} To create a Web Modeler data backup, refer to the [official PostgreSQL documentation](https://www.postgresql.org/docs/current/backup-dump.html) to back up the database that Web Modeler uses. For example, to create a backup of the database using `pg_dumpall`, use the following command: ```bash pg_dumpall -U -h -p -f dump.psql --quote-all-identifiers Password: ``` `pg_dumpall` might ask multiple times for the same password. The database will be dumped into `dump.psql`. :::note Database dumps created with `pg_dumpall`/`pg_dump` can only be restored into a database with the same or later version of PostgreSQL, see [PostgreSQL documentation](https://www.postgresql.org/docs/current/app-pgdump.html#PG-DUMP-NOTES). ::: :::info You can [restore a Web Modeler data backup](restore.md#optional-restore-a-web-modeler-data-backup). ::: ## Cleaning up backups Depending on your company’s backup policies (for example, retention periods and number of backups to keep) you should consider regularly cleaning up your old backups to reduce storage costs and efficiently manage resources. You can use the **delete backup APIs** for each component to remove the associated resources from the configured backup storage. You will have to provide the same backup ID for all calls to remove it from all backup stores. - [Web Applications](/self-managed/operational-guides/backup-restore/webapps-backup.md#delete-backup-api) - [Optimize](/self-managed/operational-guides/backup-restore/optimize-backup.md#delete-backup-api) - [Zeebe](/self-managed/operational-guides/backup-restore/zeebe-backup-and-restore.md#delete-backup-api) For Zeebe, you would also have to remove the separately backed up `zeebe-record` index snapshot using the [Elasticsearch](https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-snapshot-delete) / [OpenSearch](https://docs.opensearch.org/docs/latest/api-reference/snapshots/delete-snapshot/) API directly. ### Primary storage retention :::note This only affects the primary storage and does not interfere with Elasticsearch/OpenSearch backups. ::: With Camunda 8.9 you now have the option to enable a retention mechanism over primary storage (Zeebe's) backups. This will periodically delete backups from the configured blob storage based on the preconfigured retention window. Learn more about configuring backup retention [here](../../../../components/orchestration-cluster/core-settings/configuration/properties/#camundadataprimary-storagebackupretention). --- ## Restore a backup Restore a previous backup of your Camunda 8 Self-Managed components and cluster. ## About restoring a backup To restore a backup you must complete the following main steps: 1. [Restore of Elasticsearch/OpenSearch](#restore-elasticsearch-opensearch) 2. [Restore Zeebe Cluster](#restore-zeebe-cluster) 3. [Start all Camunda 8 components](#start-all-camunda-8-components) :::note When restoring Camunda 8 from a backup, all components must be restored from their backup that corresponds to the same backup ID. ::: ## Prerequisites The following general prerequisites are required before you can restore a backup: | Prerequisite | Description | | :-------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Component clean state | The restore process assumes a **clean state** for all components, including Elasticsearch/OpenSearch. This means **no prior persistent volumes** or **component state** should exist - all data is restored from scratch. | | Camunda version | **Backups must be restored** using the **exact Camunda version** they were created with. As noted during the backup process, the version is embedded in the backup name.This is essential because starting a component with a mismatched version may result in startup failures due to schema incompatibilities with Elasticsearch/OpenSearch and the component itself. Although schema changes are generally avoided in patch releases, they can still occur.When using the Camunda Helm chart, this means figuring out the corresponding version. For this the [Camunda Helm chart Version Matrix](https://helm.camunda.io/camunda-platform/version-matrix/) can help. Click on the `major.minor` release and then search for the backed up patch release of your component. The other components would typically fit in there as well. |
Example: Work out your correct Camunda version Our Backups look as follows: ```bash camunda_optimize_1748937221_8.8.0_part_1_of_2 camunda_optimize_1748937221_8.8.0_part_2_of_2 camunda_webapps_1748937221_8.8.0_part_1_of_5 camunda_webapps_1748937221_8.8.0_part_2_of_5 camunda_webapps_1748937221_8.8.0_part_3_of_5 camunda_webapps_1748937221_8.8.0_part_4_of_5 camunda_webapps_1748937221_8.8.0_part_5_of_5 camunda_zeebe_records_backup_1748937221 ``` From this, we know: - Optimize: 8.8.0 - Web Applications (Operate / Tasklist): 8.8.0 Based on this, we can look in the [matrix versioning of 8.8](https://helm.camunda.io/camunda-platform/version-matrix/camunda-8.8) and see the corresponding Camunda Helm chart version is `13.0.0`.
## Step 1: Restore of Elasticsearch/OpenSearch {#restore-elasticsearch-opensearch} ### Prerequisites The following specific prerequisites are required when restoring Elasticsearch/OpenSearch: | Prerequisite | Description | | :------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Clean state/data | Elasticsearch or OpenSearch is set up and running with a clean state and no existing data. | | Snapshot repository | Elasticsearch or OpenSearch is configured to use the same snapshot repository as the backup. See [prerequisites](./backup.md#prerequisites). | | Sizing | Elasticsearch or OpenSearch should be sized the same or larger than the original cluster. Restoring to a smaller cluster (for example, with fewer data nodes) can prevent shards from being assigned and cause restore failures. | ### 1. Restore [Templates](https://www.elastic.co/docs/manage-data/data-store/templates) This step includes restoring index and component templates crucial for Camunda 8 to function properly on continuous use. These templates are automatically applied on newly created indices. These templates are only created on the initial start of the components and the first seeding of the secondary datastore, due to which you have to temporarily restore them before you can restore all Elasticsearch/OpenSearch snapshots. **Start Camunda 8 configured with your secondary datastore endpoint** - For example, deploy the Camunda Helm chart. - For manual context, start Camunda 8 components manually. - Depending on your setup this can mean Orchestration Cluster (Operate, Tasklist, Zeebe), Optimize and the required secondary datastore. The templates are created by the Web Applications (Operate, Tasklist), and Optimize on startup on the first seeding of the datastore. Zeebe creates this whenever it is required, and isn't limited to the initial start. We recommend starting your full required Camunda 8 stack for the applications to show up as healthy. You can confirm the successful creation of the index templates by using the Elasticsearch/OpenSearch API. The index templates rely on the component templates, so it also confirms these were successfully recreated. The following uses the [Elasticsearch Index API](https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-indices-get-index-template) to list all index templates. ```bash curl -s "$ELASTIC_ENDPOINT/_index_template" \ | jq -r '.index_templates[].name' \ | grep -E 'operate|tasklist|optimize|zeebe' \ | sort ```
Example Output ```bash operate-batch-operation-1.0.0_template operate-decision-instance-8.3.0_template operate-event-8.3.0_template operate-flownode-instance-8.3.1_template operate-incident-8.3.1_template operate-job-8.6.0_template operate-list-view-8.3.0_template operate-message-8.5.0_template operate-operation-8.4.1_template operate-post-importer-queue-8.3.0_template operate-sequence-flow-8.3.0_template operate-variable-8.3.0_template tasklist-draft-task-variable-8.3.0_template tasklist-task-8.5.0_template tasklist-task-variable-8.3.0_template ... ```
The following uses the [OpenSearch Index API](https://docs.opensearch.org/docs/latest/api-reference/index-apis/get-index-template/) to list all index templates. ```bash curl -s "$OPENSEARCH_ENDPOINT/_index_template" \ | jq -r '.index_templates[].name' \ | grep -E 'operate|tasklist|optimize|zeebe' \ | sort ```
Example Output ```bash operate-batch-operation-1.0.0_template operate-decision-instance-8.3.0_template operate-event-8.3.0_template operate-flownode-instance-8.3.1_template operate-incident-8.3.1_template operate-job-8.6.0_template operate-list-view-8.3.0_template operate-message-8.5.0_template operate-operation-8.4.1_template operate-post-importer-queue-8.3.0_template operate-sequence-flow-8.3.0_template operate-user-task-8.5.0_template operate-variable-8.3.0_template tasklist-draft-task-variable-8.3.0_template tasklist-task-8.5.0_template tasklist-task-variable-8.3.0_template ... ```
### 2. Find available backup IDs With the active environment that was required to restore the datastore templates you can quickly work out available backups, using the backup APIs for each component to list available backups. :::note You will need the output for your chosen backup ID in the following steps to be able to restore datastore snapshots as it contains the snapshot names. :::
Web Applications Example Using the [Web Applications management API](/self-managed/operational-guides/backup-restore/webapps-backup.md#get-backups-list-api) to list backups. You must have the Elasticsearch / OpenSearch backup repository configured to be able to retrieve backups. ```bash curl $ORCHESTRATION_CLUSTER_MANAGEMENT_API/actuator/backupHistory ``` ```json [ { "backupId": 1748937221, "state": "COMPLETED", "details": [ { "snapshotName":"camunda_webapps_1748937221_8.8.0_part_1_of_5", "state":"SUCCESS", "startTime":"2025-06-03T07:55:15.685+0000", "failures":[] }, { "snapshotName":"camunda_webapps_1748937221_8.8.0_part_2_of_5", "state":"SUCCESS", "startTime":"2025-06-03T07:55:16.288+0000", "failures":[] }, { "snapshotName":"camunda_webapps_1748937221_8.8.0_part_3_of_5", "state":"SUCCESS", "startTime":"2025-06-03T07:55:17.092+0000", "failures":[] }, { "snapshotName":"camunda_webapps_1748937221_8.8.0_part_4_of_5", "state":"SUCCESS", "startTime":"2025-06-03T07:55:17.293+0000", "failures":[] }, { "snapshotName":"camunda_webapps_1748937221_8.8.0_part_5_of_5", "state":"SUCCESS", "startTime":"2025-06-03T07:55:18.298+0000", "failures":[] } ] } ] ```
Optimize Example Using the [Optimize management API](/self-managed/operational-guides/backup-restore/optimize-backup.md#get-backup-info-api) to list backups. You must have the Elasticsearch / OpenSearch backup repository configured to be able to retrieve backups. ```bash curl $OPTIMIZE_MANAGEMENT_API/actuator/backups ``` ```json [ { "backupId": 1748937221, "state": "COMPLETED", "details": [ { "snapshotName":"camunda_optimize_1748937221_8.8.0_part_1_of_2", "state":"SUCCESS", "startTime":"2025-06-03T07:53:54.389+0000", "failures":[] }, { "snapshotName":"camunda_optimize_1748937221_8.8.0_part_2_of_2", "state":"SUCCESS", "startTime":"2025-06-03T07:53:54.389+0000", "failures":[] } ] } ] ```
Zeebe Example Using the [Zeebe management API](/self-managed/operational-guides/backup-restore/zeebe-backup-and-restore.md#list-backups-api) to list backups. ```bash curl $ORCHESTRATION_CLUSTER_MANAGEMENT_API/actuator/backupRuntime ``` ```json [ { "backupId": 1748937221, "state": "COMPLETED", "details": [ { "partitionId": 1, "state": "COMPLETED", "createdAt": "2025-06-03T08:06:10.408893628Z", "brokerVersion": "8.8.0" }, { "partitionId": 2, "state": "COMPLETED", "createdAt": "2025-06-03T08:06:10.408893628Z", "brokerVersion": "8.8.0" }, { "partitionId": 3, "state": "COMPLETED", "createdAt": "2025-06-03T08:06:10.408893628Z", "brokerVersion": "8.8.0" } ] } ] ```
As there may be cases where this is not possible, an alternative approach is covered in the following example. #### Available Backups on Elasticsearch/OpenSearch In this scenario, follow the steps above, but when you have your Elasticsearch/OpenSearch available, use the snapshot API to list available snapshots and correlate this to the available snapshots in your backup bucket (AWS S3, Azure Store, Google GCS). It is important to use the same ID for all backups. The following uses the [Elasticsearch snapshot API](https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-snapshot-get) to list all registered snapshots in a repository. ```bash ELASTIC_ENDPOINT=http://localhost:9200 # Your Elasticsearch endpoint ELASTIC_SNAPSHOT_REPOSITORY=camunda_backup # Your defined snapshot repository on Elasticsearch for Camunda backups # Get a list of all available snapshots curl $ELASTIC_ENDPOINT/_snapshot/$ELASTIC_SNAPSHOT_REPOSITORY/_all # Get a list of all available snapshots and use jq to parse just the names for easier readability curl $ELASTIC_ENDPOINT/_snapshot/$ELASTIC_SNAPSHOT_REPOSITORY/_all | jq -r '.snapshots[].snapshot' ``` Ensure that all backups and parts exist for each component for your chosen backup ID.
Example output ```bash camunda_optimize_1748937221_8.8.0_part_1_of_2 camunda_optimize_1748937221_8.8.0_part_2_of_2 camunda_webapps_1748937221_8.8.0_part_1_of_5 camunda_webapps_1748937221_8.8.0_part_2_of_5 camunda_webapps_1748937221_8.8.0_part_3_of_5 camunda_webapps_1748937221_8.8.0_part_4_of_5 camunda_webapps_1748937221_8.8.0_part_5_of_5 camunda_zeebe_records_backup_1748937221 ```
The following uses the [OpenSearch snapshot API](https://docs.opensearch.org/docs/latest/api-reference/snapshots/get-snapshot/) to list all registered snapshots in a repository. ```bash OPENSEARCH_ENDPOINT=http://localhost:9200 # Your OpenSearch endpoint OPENSEARCH_SNAPSHOT_REPOSITORY=camunda_backup # Your defined snapshot repository on OpenSearch for Camunda backups # Get a list of all available snapshots curl $OPENSEARCH_ENDPOINT/_snapshot/$OPENSEARCH_SNAPSHOT_REPOSITORY/_all # Get a list of all available snapshots and use jq to parse just the names for easier readability curl $OPENSEARCH_ENDPOINT/_snapshot/$OPENSEARCH_SNAPSHOT_REPOSITORY/_all | jq -r '.snapshots[].snapshot' ``` Ensure that all backups and parts exist for each component for your chosen backup ID.
Example output ```bash camunda_optimize_1748937221_8.8.0_part_1_of_2 camunda_optimize_1748937221_8.8.0_part_2_of_2 camunda_webapps_1748937221_8.8.0_part_1_of_5 camunda_webapps_1748937221_8.8.0_part_2_of_5 camunda_webapps_1748937221_8.8.0_part_3_of_5 camunda_webapps_1748937221_8.8.0_part_4_of_5 camunda_webapps_1748937221_8.8.0_part_5_of_5 camunda_zeebe_records_backup_1748937221 ```
#### Available Backups of Zeebe Partitions For the Zeebe partitions backup, you will need to check your configured backup store for available backup IDs, and correlate those to the available backups on Elasticsearch/OpenSearch. Zeebe creates a folder for each Partition ID and subfolder in this with each backup ID. :::warning Using the [Zeebe Management Backup API](/self-managed/operational-guides/backup-restore/zeebe-backup-and-restore.md#list-backups-api) is the recommended method for listing available backups, as it ensures the backups are complete and valid. Manually identifying backup IDs can result in restoring an incomplete backup, which will fail the restore process. If this occurs, you will need to choose a different backup ID and repeat the restore process for all components with the new backup ID, including the datastore, to avoid mismatched backup windows and potential data loss. :::
Example output Example in the case of 3 partitions with two available backups: ```bash #PartitionID folder # BackupID folder 1/ ├── 1748937221 └── 1749130104 2/ ├── 1748937221 └── 1749130104 3/ ├── 1748937221 └── 1749130104 ```
### 3. Stop all components apart from Elasticsearch/OpenSearch If you are using an external Elasticsearch/OpenSearch and Kubernetes, you could temporarily [uninstall](https://helm.sh/docs/helm/helm_uninstall/) the Camunda Helm chart or [scale](https://kubernetes.io/docs/reference/kubectl/generated/kubectl_scale/) all components to 0, so that nothing is running and potentially interacting with the datastore. In a manual setup, you can simply stop all components. If you are using the Camunda Helm chart with an embedded Elasticsearch, you can achieve this by (for example) disabling all other components in the `values.yml`. ```yaml elasticsearch: enabled: true connectors: enabled: false optimize: enabled: false orchestration: enabled: false ``` ### 4. Delete all indices Now that you have successfully restored the templates and stopped the components adding more indices, you must delete the existing indices to be able to successfully restore the snapshots (otherwise these will block a successful restore). The following uses the [Elasticsearch CAT API](https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-cat-indices) to list all indices. It also uses the [Elasticsearch Index API](https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-indices-delete) to delete an index. ```bash for index in $(curl -s "$ELASTIC_ENDPOINT/_cat/indices?h=index" \ | grep -E 'camunda|operate|tasklist|optimize|zeebe'); do echo "Deleting index: $index" curl -X DELETE "$ELASTIC_ENDPOINT/$index" done ```
Example Output ```bash Deleting index: operate-import-position-8.3.0_ {"acknowledged":true}Deleting index: operate-migration-steps-repository-1.1.0_ {"acknowledged":true}Deleting index: operate-flownode-instance-8.3.1_ {"acknowledged":true}Deleting index: operate-event-8.3.0_ {"acknowledged":true}Deleting index: operate-incident-8.3.1_ {"acknowledged":true}Deleting index: tasklist-web-session-1.1.0_ {"acknowledged":true}Deleting index: tasklist-variable-8.3.0_ {"acknowledged":true}Deleting index: operate-user-task-8.5.0_ {"acknowledged":true}Deleting index: tasklist-import-position-8.2.0_ {"acknowledged":true}Deleting index: tasklist-task-variable-8.3.0_ {"acknowledged":true}Deleting index: tasklist-flownode-instance-8.3.0_ {"acknowledged":true}Deleting index: operate-process-8.3.0_ {"acknowledged":true}Deleting index: tasklist-process-instance-8.3.0_ {"acknowledged":true}Deleting index: operate-operation-8.4.1_ {"acknowledged":true}Deleting index: operate-job-8.6.0_ {"acknowledged":true}Deleting index: operate-metric-8.3.0_ {"acknowledged":true}Deleting index: tasklist-migration-steps-repository-1.1.0_ {"acknowledged":true}Deleting index: operate-decision-8.3.0_ {"acknowledged":true}Deleting index: tasklist-process-8.4.0_ {"acknowledged":true}Deleting index: operate-variable-8.3.0_ {"acknowledged":true}Deleting index: operate-message-8.5.0_ {"acknowledged":true}Deleting index: operate-decision-requirements-8.3.0_ {"acknowledged":true}Deleting index: operate-batch-operation-1.0.0_ {"acknowledged":true}Deleting index: operate-web-session-1.1.0_ {"acknowledged":true}Deleting index: tasklist-user-1.4.0_ {"acknowledged":true}Deleting index: operate-list-view-8.3.0_ {"acknowledged":true}Deleting index: tasklist-metric-8.3.0_ {"acknowledged":true}Deleting index: operate-post-importer-queue-8.3.0_ {"acknowledged":true}Deleting index: tasklist-task-8.5.0_ {"acknowledged":true}Deleting index: tasklist-form-8.4.0_ {"acknowledged":true}Deleting index: operate-user-1.2.0_ {"acknowledged":true}Deleting index: tasklist-draft-task-variable-8.3.0_ {"acknowledged":true}Deleting index: operate-decision-instance-8.3.0_ {"acknowledged":true}Deleting index: operate-sequence-flow-8.3.0_ {"acknowledged":true} ```
The following uses the [OpenSearch CAT API](https://docs.opensearch.org/docs/latest/api-reference/cat/cat-indices/) to list all indices. It also uses the [OpenSearch Index API](https://docs.opensearch.org/docs/latest/api-reference/index-apis/delete-index/) to delete an index. ```bash for index in $(curl -s "$OPENSEARCH_ENDPOINT/_cat/indices?h=index" \ | grep -E 'operate|tasklist|optimize|zeebe'); do echo "Deleting index: $index" curl -X DELETE "$OPENSEARCH_ENDPOINT/$index" done ```
Example Output ```bash Deleting index: operate-import-position-8.3.0_ {"acknowledged":true}Deleting index: operate-migration-steps-repository-1.1.0_ {"acknowledged":true}Deleting index: operate-flownode-instance-8.3.1_ {"acknowledged":true}Deleting index: operate-event-8.3.0_ {"acknowledged":true}Deleting index: operate-incident-8.3.1_ {"acknowledged":true}Deleting index: tasklist-web-session-1.1.0_ {"acknowledged":true}Deleting index: tasklist-variable-8.3.0_ {"acknowledged":true}Deleting index: operate-user-task-8.5.0_ {"acknowledged":true}Deleting index: tasklist-import-position-8.2.0_ {"acknowledged":true}Deleting index: tasklist-task-variable-8.3.0_ {"acknowledged":true}Deleting index: tasklist-flownode-instance-8.3.0_ {"acknowledged":true}Deleting index: operate-process-8.3.0_ {"acknowledged":true}Deleting index: tasklist-process-instance-8.3.0_ {"acknowledged":true}Deleting index: operate-operation-8.4.1_ {"acknowledged":true}Deleting index: operate-job-8.6.0_ {"acknowledged":true}Deleting index: operate-metric-8.3.0_ {"acknowledged":true}Deleting index: tasklist-migration-steps-repository-1.1.0_ {"acknowledged":true}Deleting index: operate-decision-8.3.0_ {"acknowledged":true}Deleting index: tasklist-process-8.4.0_ {"acknowledged":true}Deleting index: operate-variable-8.3.0_ {"acknowledged":true}Deleting index: operate-message-8.5.0_ {"acknowledged":true}Deleting index: operate-decision-requirements-8.3.0_ {"acknowledged":true}Deleting index: operate-batch-operation-1.0.0_ {"acknowledged":true}Deleting index: operate-web-session-1.1.0_ {"acknowledged":true}Deleting index: tasklist-user-1.4.0_ {"acknowledged":true}Deleting index: operate-list-view-8.3.0_ {"acknowledged":true}Deleting index: tasklist-metric-8.3.0_ {"acknowledged":true}Deleting index: operate-post-importer-queue-8.3.0_ {"acknowledged":true}Deleting index: tasklist-task-8.5.0_ {"acknowledged":true}Deleting index: tasklist-form-8.4.0_ {"acknowledged":true}Deleting index: operate-user-1.2.0_ {"acknowledged":true}Deleting index: tasklist-draft-task-variable-8.3.0_ {"acknowledged":true}Deleting index: operate-decision-instance-8.3.0_ {"acknowledged":true}Deleting index: operate-sequence-flow-8.3.0_ {"acknowledged":true} ```
### 5. Restore Elasticsearch/OpenSearch snapshots Although the backup order was important so far to ensure consistent backups, you can restore the backed up indices in any order. As the components do not have an endpoint to restore the backup in Elasticsearch, you will need to restore it yourself directly in your selected datastore. Based on your chosen backup ID in [find available backup IDs](#2-find-available-backup-ids), you can now restore the snapshots in Elasticsearch/OpenSearch for each available backup under the same backup ID. The following uses the [Elasticsearch snapshot API](https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-snapshot-restore) to restore a snapshot. ```bash curl -XPOST "$ELASTIC_ENDPOINT/_snapshot/$ELASTIC_SNAPSHOT_REPOSITORY/$SNAPSHOT_NAME/_restore?wait_for_completion=true" ``` The following uses the [OpenSearch snapshot API](https://docs.opensearch.org/docs/latest/api-reference/snapshots/restore-snapshot/) to restore a snapshot. ```bash curl -XPOST "$OPENSEARCH_ENDPOINT/_snapshot/$OPENSEARCH_SNAPSHOT_REPOSITORY/$SNAPSHOT_NAME/_restore?wait_for_completion=true" ``` Where `$SNAPSHOT_NAME` would be any of the following based on our example in [find available backups IDs](#2-find-available-backup-ids). Ensure that all your backups correspond to the same backup ID and that each one is restored one-by-one. ```bash camunda_optimize_1748937221_8.8.0_part_1_of_2 camunda_optimize_1748937221_8.8.0_part_2_of_2 camunda_webapps_1748937221_8.8.0_part_1_of_5 camunda_webapps_1748937221_8.8.0_part_2_of_5 camunda_webapps_1748937221_8.8.0_part_3_of_5 camunda_webapps_1748937221_8.8.0_part_4_of_5 camunda_webapps_1748937221_8.8.0_part_5_of_5 camunda_zeebe_records_backup_1748937221 ``` ## Step 2: Restore Zeebe Cluster {#restore-zeebe-cluster} ### Prerequisites The following specific prerequisites are required when restoring the Zeebe Cluster: | Prerequisite | Description | | :----------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Pre-existing data | Persistent volumes or disks must not contain any pre-existing data. | | Backup storage | Zeebe is configured with the same backup storage as outlined in the [prerequisites](./backup.md#prerequisites). | | Components stopped | It’s critical that no Camunda components are running during a Zeebe restore. Restored components may propagate an incorrect cluster configuration, potentially disrupting cluster communication. | ### Restore Zeebe Cluster :::note During the restoration of the Elasticsearch / OpenSearch state, we had to temporarily deploy Zeebe. This will have resulted in persistent volumes on Kubernetes and a filled data directory on each Zeebe Broker in case of a manual deployment. In the case of Kubernetes to remove all related persistent volumes. ```bash kubectl get pvc -o custom-columns=NAME:.metadata.name --no-headers \ | grep zeebe \ | while read pvc; do kubectl delete pvc "$pvc" done ``` New persistent volumes will be created on a new Camunda Helm chart upgrade and install. In case of a manual deployment, this means to remove the data directory of each Zeebe Broker. ::: Camunda provides a standalone app which must be run on each node where a Zeebe Broker will be running. This is a Spring Boot application similar to the broker and can run using the binary provided as part of the distribution. The app can be configured the same way a broker is configured - via environment variables or using the configuration file located in `config/application.yaml`. :::warning When restoring, provide the same configuration (node id, data directory, cluster size, and replication count) as the broker that will be running in this node. The partition count **must be same** as in the backup. The amount of partitions backed up are also visible in the backup store of Zeebe, see [how to figure out available backups](#available-backups-of-zeebe-partitions). If brokers were dynamically scaled between backup and restore, this is not an issue - as long as the partition count remains unchanged. ::: Assuming you're using the official [Camunda Helm chart](/self-managed/deployment/helm/install/quick-install.md), you'll have to adjust your Helm `values.yml` to supply the following temporarily. It will overwrite the start command of the resulting Zeebe pod, executing a restore script. It's important that the backup is configured for Zeebe to be able to restore from the backup! ```yaml orchestration: enabled: true env: # Environment variables to overwrite the Zeebe startup behavior - name: SPRING_PROFILES_ACTIVE value: "restore" - name: ZEEBE_RESTORE value: "true" - name: ZEEBE_RESTORE_FROM_BACKUP_ID value: "$BACKUP_ID" # Change the $BACKUP_ID to your actual value # all the envs related to the backup store as outlined in the prerequisites - name: CAMUNDA_DATA_BACKUP_STORE value: "S3" # just as an example - name: CAMUNDA_DATA_BACKUP_REPOSITORYNAME value: camunda # Change to name of the repository in Elasticsearch/OpenSearch ... # If you use Elasticsearch from the embedded Helm chart, set this to true. Otherwise, set it to false. elasticsearch: enabled: true connectors: enabled: false optimize: enabled: false ``` :::note Alternative overwrite Use this alternative approach to restore Zeebe partitions: ```yaml orchestration: enabled: true command: - "/usr/local/camunda/bin/restore" - "--backupId=$BACKUP_ID" # Change the $BACKUP_ID to your actual value. env: - name: SPRING_PROFILES_ACTIVE value: "restore" # All the envs related to the backup store as outlined in the prerequisites ... ``` If you're not using the Camunda Helm chart, you can use a similar approach natively with Kubernetes to overwrite the command. ::: The application exits after restore and Kubernetes restarts the pod, which appears as `CrashLoopBackOff`. This is expected behavior. The restore application does not restore state again once partitions are already restored to persistent disk. After removing the temporary restore command or unsetting the `ZEEBE_RESTORE` and related backup ID environment variable to restore Zeebe’s default behavior, you may optionally restart the StatefulSet to ensure the changes take effect immediately. This can be done by [scaling](https://kubernetes.io/docs/reference/kubectl/generated/kubectl_scale/) the StatefulSet down and back up, or by [deleting](https://kubernetes.io/docs/reference/kubectl/generated/kubectl_delete/) the pods so they are recreated with the newly deployed revision. :::tip In Kubernetes, Zeebe runs as a [StatefulSet](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/), which is intended for long-running, persistent applications. Because StatefulSet pods are restarted automatically, restore-mode pods can appear in `CrashLoopBackOff` after a successful restore. Observe Zeebe Broker logs during restore, and use `--previous` if a pod has already restarted. The restore app will not import or overwrite data again, but you may miss the first successful run if you are not observing logs actively. ::: To restore a Zeebe Cluster, run the following in each node where the broker will be running: ```bash mkdir -p camunda tar -xzf camunda-zeebe-X.Y.Z.tar.gz --strip-components=1 -C camunda/ ./camunda/bin/restore --backupId= ``` ### Restore success or failure If restore was successful, the app exits with the log message `Successfully restored broker from backup`. However, the restore will fail if: - There is no valid backup with the given backupId. - The backup store is not configured correctly. - The configured data directory is not empty. - Due to any other unexpected errors. If the restore fails, you can re-run the application after fixing the root cause. #### Data directory is not empty If the data directory is not empty, the restore will fail with an error message: ``` Brokers's data directory /usr/local/zeebe/data is not empty. Aborting restore to avoid overwriting data. Please restart with a clean directory ``` On some filesystems, the data directory may contain special files and folders that can't or shouldn't be deleted. In such cases, the restore application can be configured to ignore the presence of these files and folders. The config `zeebe.restore.ignoreFilesInTarget` takes a list of file and folder names to ignore. By default, it ignores `lost+found` folder found on ext4 filesystems. To also ignore `.snapshot` folders, set `zeebe.restore.ignoreFilesInTarget: [".snapshot", "lost+found"]` or the equivalent environment variable `ZEEBE_RESTORE_IGNOREFILESINTARGET=".snapshot,lost+found"`. ## Step 3: Start all Camunda 8 components {#start-all-camunda-8-components} Now that you have actively restored Elasticsearch/OpenSearch and the Zeebe cluster partitions, you can start all components again and use Camunda 8 as normal. For example: - For Kubernetes, apply Helm values for normal startup and explicitly set `ZEEBE_RESTORE=false`. Also, keep your backup-store environment variables configured as outlined in the prerequisites. ```yaml orchestration: enabled: true env: - name: ZEEBE_RESTORE value: "false" # all the envs related to the backup store as outlined in the prerequisites - name: CAMUNDA_DATA_BACKUP_STORE value: "S3" # just as an example - name: CAMUNDA_DATA_BACKUP_REPOSITORYNAME value: camunda # Change to name of the repository in Elasticsearch/OpenSearch ``` Ensure restore-only settings are not present in this final configuration (for example, `SPRING_PROFILES_ACTIVE=restore`, `ZEEBE_RESTORE_FROM_BACKUP_ID`, or a temporary restore command override). - For a manual setup, execute the broker and all other components in their normal way. ## (Optional) Restore a Web Modeler data backup If you have previously backed up your Web Modeler data, you can restore this backup. Backups can only be restored with downtime. To restore the database dump, first ensure that Web Modeler is stopped. Then, to restore the database use the following command: ```bash psql -U -h -p -f dump.psql ``` After the database has been restored, you can start Web Modeler again. :::danger When restoring Web Modeler data from a backup, ensure that the ids of the users stored in your OIDC provider (e.g. Keycloak) do not change in between the backup and restore. Otherwise, users may not be able to access their projects after the restore (see [Web Modeler's troubleshooting guide](/self-managed/components/hub/troubleshooting/troubleshoot-missing-data.md)). ::: :::tip Some vendors provide tools that help with database backups and restores, such as [AWS Backup](https://aws.amazon.com/getting-started/hands-on/amazon-rds-backup-restore-using-aws-backup/) or [Cloud SQL backups](https://cloud.google.com/sql/docs/postgres/backup-recovery/backups). ::: --- ## Backup and restore Web Modeler data ## Create backup To create a backup of Web Modeler data, you must back up the database that Web Modeler uses by following the instructions of the official [PostgreSQL documentation](https://www.postgresql.org/docs/current/backup-dump.html). For example, to create a backup of the database using `pg_dumpall`, use the following command: ```bash pg_dumpall -U -h -p -f dump.psql --quote-all-identifiers Password: ``` `pg_dumpall` may ask multiple times for the same password. The database will be dumped into `dump.psql`. :::note Database dumps created with `pg_dumpall`/`pg_dump` can only be restored into a database with the same or later version of PostgreSQL, see [PostgreSQL documentation](https://www.postgresql.org/docs/current/app-pgdump.html#PG-DUMP-NOTES). ::: ## Restore Backups can only be restored with downtime. To restore the database dump, first ensure that Web Modeler is stopped. Then, to restore the database use the following command: ```bash psql -U -h -p -f dump.psql ``` After the database has been restored, you can start Web Modeler again. :::danger When restoring Web Modeler data from a backup, ensure that the ids of the users stored in your OIDC provider (e.g. Keycloak) do not change in between the backup and restore. Otherwise, users may not be able to access their projects after the restore (see [Web Modeler's troubleshooting guide](/self-managed/components/hub/troubleshooting/troubleshoot-missing-data.md)). ::: :::tip Some vendors provide tools that help with database backups and restores, such as [AWS Backup](https://aws.amazon.com/getting-started/hands-on/amazon-rds-backup-restore-using-aws-backup/) or [Cloud SQL backups](https://cloud.google.com/sql/docs/postgres/backup-recovery/backups). ::: --- ## Optimize backup management API Back up your Optimize data using the Backup Management API. ## About this API Optimize is a dedicated application that stores its data over multiple indices in the database. To ensure data integrity across indices, a backup of Optimize data consists of two Elasticsearch/OpenSearch snapshots, each containing a different set of Optimize indices. Each backup is identified by a positive integer backup ID. For example, a backup with ID `123456` consists of the following snapshots: ``` camunda_optimize_123456_8.8.0_part_1_of_2 camunda_optimize_123456_8.8.0_part_2_of_2 ``` Optimize provides an API to trigger a backup and retrieve information about a given backup's state. During backup creation Optimize can continue running. The backed up data can later be restored using the standard Elasticsearch/OpenSearch snapshot restore API. :::warning Usage of this API requires the backup store to be configured for the component. - Optimize configuration - [Elasticsearch](/self-managed/components/optimize/configuration/system-configuration.md#elasticsearch-backup-settings) - [OpenSearch](/self-managed/components/optimize/configuration/system-configuration.md#opensearch-backup-settings) 1. A snapshot repository of your choice must be registered with Elasticsearch/OpenSearch. 2. The repository name must be specified using the `CAMUNDA_OPTIMIZE_BACKUP_REPOSITORY_NAME` environment variable, or by adding it to your Optimize [`environment-config.yaml`](/self-managed/components/optimize/configuration/system-configuration.md): - [Elasticsearch snapshot repository](https://www.elastic.co/docs/deploy-manage/tools/snapshot-and-restore/manage-snapshot-repositories) - [OpenSearch snapshot repository](https://docs.opensearch.org/docs/latest/tuning-your-cluster/availability-and-recovery/snapshots/snapshot-restore/) ::: ## Create backup API Note that the backup API can be reached via the `/actuator` management port, which by default is `8092`. The configured context path does not apply to the management port. The following endpoint can be used to trigger the backup process: ``` POST actuator/backups { "backupId": } ``` ### Response | Code | Description | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | 202 Accepted | Backup process was successfully initiated. To determine whether backup process was completed refer to the GET API. | | 400 Bad Request | Indicates issues with the request, for example when the `backupId` contains invalid characters. | | 409 Conflict | Indicates that a backup with the same `backupId` already exists. | | 500 Server Error | All other errors, e.g. issues communicating with the database for snapshot creation. Refer to the returned error message for more details. | | 502 Bad Gateway | Optimize has encountered issues while trying to connect to the database. | ### Example request ```shell curl --request POST 'http://localhost:8092/actuator/backups' \ -H 'Content-Type: application/json' \ -d '{ "backupId": 123456 }' ``` ### Example response ```json { "message": "Backup creation for ID 123456 has been scheduled. Use the GET API to monitor completion of backup process" } ``` ## Get backup info API Note that the backup API can be reached via the `/actuator` management port, which by default is `8092`. The configured context path does not apply to the management port. Information about a specific backup can be retrieved using the following request: ``` GET actuator/backups/{backupId} ``` Information about all existing Optimize backups can be retrieved by omitting the optional `backupId` parameter: ``` GET actuator/backups ``` ### Response | Code | Description | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | 200 OK | Backup state could be determined and is returned in the response body (see example below). | | 400 Bad Request | There is an issue with the request, for example the repository name specified in the Optimize configuration does not exist. Refer to returned error message for details. | | 404 Not Found | If a backup ID was specified, no backup with that ID exists. | | 500 Server Error | All other errors, e.g. issues communicating with the database for snapshot state retrieval. Refer to the returned error message for more details. | | 502 Bad Gateway | Optimize has encountered issues while trying to connect to the database. | ### Example request ```shell curl --request GET 'http://localhost:8092/actuator/backups/123456' ``` ### Example response ```json { "backupId": 123456, "failureReason": null, "state": "COMPLETE", “details”: [ { "snapshotName": "camunda_optimize_123456_8.8.0_part_1_of_2", "state": "SUCCESS", "startTime": "2024-11-09T10:11:36.978+0100", "failures": [] }, { "snapshotName": "camunda_optimize_123456_8.8.0_part_2_of_2", "state": "SUCCESS", "startTime": "2024-11-09T10:11:37.178+0100", "failures": [] } ] } ``` Note that the endpoint will return a single item when called with a `backupId` and a list of items when called without specifying a `backupId`. Possible states of the backup: - `COMPLETE`: The backup can be used for restoring data. - `IN_PROGRESS`: The backup process for this backup ID is still in progress. - `FAILED`: Something went wrong when creating this backup. To find out the exact problem, use the [Elasticsearch](https://www.elastic.co/guide/en/elasticsearch/reference/current/get-snapshot-status-api.html)/[OpenSearch](https://opensearch.org/docs/latest/api-reference/snapshots/get-snapshot-status/) get snapshot status API for each of the snapshots included in the given backup. - `INCOMPATIBLE`: The backup is incompatible with the current Elasticsearch/OpenSearch version. - `INCOMPLETE`: The backup is incomplete (this could occur when the backup process was interrupted or individual snapshots were deleted). ## Delete backup API Note that the backup API can be reached via the `/actuator` management port, which by default is `8092`. The configured context path does not apply to the management port. An existing backup can be deleted using the below API which deletes all Optimize snapshots associated with the supplied backupID. ``` DELETE actuator/backups/{backupId} ``` ### Response | Code | Description | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | 204 No Content | The delete request for the associated snapshots was submitted to the database successfully. | | 400 Bad Request | There is an issue with the request, for example the repository name specified in the Optimize configuration does not exist. Refer to returned error message for details. | | 500 Server Error | An error occurred, for example the snapshot repository does not exist. Refer to the returned error message for details. | | 502 Bad Gateway | Optimize has encountered issues while trying to connect to Elasticsearch/OpenSearch. | ### Example request ```shell curl --request DELETE 'http://localhost:8092/actuator/backups/123456' ``` --- ## Camunda backup creation (RDBMS) Back up your Camunda 8 Self-Managed Orchestration cluster components (Zeebe, Operate, and Tasklist) when using a relational database management system (RDBMS) as secondary storage. ## Prerequisites The following prerequisites are required before you can create a backup. | Prerequisite | Description | | :---------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Configure Zeebe backup storage. | Configure the backup storage for Zeebe. This is required regardless of your secondary storage choice. See [Zeebe backup configuration](/self-managed/components/orchestration-cluster/zeebe/configuration/broker.md#zeebebrokerdatabackup). | | Enable continuous backups | Enable continuous backups. See [Zeebe scheduler configuration](../../../../components/orchestration-cluster/core-settings/configuration/properties/#camundadataprimary-storagebackup). | | (Recommended) Configure Zeebe scheduled backup. | Configure Zeebe's internal primary storage backup scheduler. See [Zeebe scheduler configuration](../../../../components/orchestration-cluster/core-settings/configuration/properties/#camundadataprimary-storagebackup). | | Set up RDBMS backups. | You are responsible for backing up the RDBMS using your database vendor's native tools (for example, `pg_dump`, `mysqldump`, `RMAN`). Back up the **entire Camunda database**, including all component tables. Schedule RDBMS backups at a similar frequency to your Zeebe backups. | ## Architecture overview When Camunda uses an RDBMS as **secondary storage**, backups involve **two independent systems**: - **Zeebe (primary storage)**: Backs up its internal state (log stream, snapshots) to an external blob store (S3, GCS, Azure, or filesystem). These are called **primary storage backups**. - **The external RDBMS**: Backed up using your database vendor's native tools (pg_dump, mysqldump, RMAN, etc.). This is the **secondary storage backup**. During restore, Zeebe uses the exporter position stored in the RDBMS to find the correct primary storage backup, or backups, that match the RDBMS state. This ensures consistency between the two systems without requiring synchronized backup timing. :::tip While Zeebe during restore will match the secondary storage's backup position, it is recommended to have the backups taken at similar intervals. This minimizes the time required after restore for Zeebe to re-export events to the secondary storage. ::: ## Continuous backups Continuous backups must be enabled for RDBMS backup and restore. ### How continuous backups work Continuous backups rely on a Zeebe feature that retains data records in the log stream until they are backed up. This ensures that after restore, the state of primary and secondary storage is in sync, without the need to orchestrate primary and secondary storage backups. When continuous backups are enabled: 1. Zeebe prevents log compaction from deleting any segment that hasn't been backed up yet, and tracks backup metadata so you can query available backup ranges. 2. The system tracks **backup ranges** — unbroken sequences of consecutive backups. These ranges define the time windows available for restore and can be queried via the [backup state actuator](../zeebe-backup-and-restore.md#request-runtime-state). :::warning Without [scheduled backups](#scheduled-backup) or regular manual backups, continuous mode causes disk usage to grow indefinitely because Zeebe cannot compact any log segments. Always pair continuous backups with a backup schedule. ::: ### Enable continuous backups Set the following configuration property: ```yaml camunda: data: primary-storage: backup: store: S3 # or GCS, AZURE, FILESYSTEM continuous: true ``` ```bash export CAMUNDA_DATA_PRIMARYSTORAGE_BACKUP_STORE=S3 export CAMUNDA_DATA_PRIMARYSTORAGE_BACKUP_CONTINUOUS=true ``` ### Checkpoint interval The checkpoint interval controls how frequently Zeebe injects marker checkpoints into the log stream. These markers serve as potential restore points — since a cluster can only be restored to a checkpoint that exists on all partitions, more frequent markers enable finer-grained [point-in-time restore](./restore.md#point-in-time-restore). ```yaml camunda: data: primary-storage: backup: checkpoint-interval: PT15M # inject a marker checkpoint every 15 minutes ``` Note that the checkpoint interval does **not** determine how frequently backups are taken — that is controlled by the [backup schedule](#scheduled-backup). A shorter checkpoint interval provides more precise restore points within the backed-up range, at the cost of slightly more metadata overhead. ## Backup ranges A **backup range** is a contiguous sequence of backups that together cover the complete log data between the first and last backup, with no gaps. Ranges are tracked per partition. Ranges are critical for restore because restoring from a time window or to match the RDBMS state requires complete log coverage. Each successful backup extends the current range as long as its log data connects to the previous backup. If a backup fails and the next successful backup no longer covers the missing log data, the current range ends and a new range begins. Use the [backup state actuator](../zeebe-backup-and-restore.md#request-runtime-state) to inspect the current ranges for each partition. ## Backup process ### Scheduled backup Zeebe's internal backup scheduler creates primary storage backups at a predefined interval. The **schedule** and **continuous mode** serve complementary purposes: the schedule triggers the backups, while continuous mode prevents log compaction and enables backup range trackin, this ensures you always have backup ranges available for disaster recoveryg. Both should be enabled together for a complete backup strategy. Learn more about configuring the backup scheduler [here](../../../../components/orchestration-cluster/core-settings/configuration/properties/#camundadataprimary-storagebackup). #### Recommended configuration ```yaml camunda: data: primary-storage: backup: store: S3 # or GCS, AZURE, FILESYSTEM continuous: true schedule: PT1H # take a backup every hour checkpoint-interval: PT15M # restore point granularity retention: window: P7D # retain backups for 7 days cleanup-schedule: PT1H # check for expired backups every hour ``` :::warning To properly configure your backup interval, monitor the `zeebe.backup.operations.latency` metric (with `operation=take`) to understand your average backup duration. As a general rule of thumb, the backup interval must be greater than your latency. Latency is affected by the size of Zeebe's runtime state. ::: :::tip You can still request on-demand primary storage backups through the regular [API](../../zeebe-backup-and-restore/#request), without the `backupId` parameter as it's being generated by the cluster. ::: ### Manual backup If you opt out of the scheduled backup approach, you can still request on-demand backups using the [backup management API](/self-managed/operational-guides/backup-restore/zeebe-backup-and-restore.md). The `backupId` is generated automatically by the cluster. ```bash # For Kubernetes port-forwarding, set the following endpoints: export ORCHESTRATION_CLUSTER_MANAGEMENT_API=http://localhost:9600 curl -X POST "$ORCHESTRATION_CLUSTER_MANAGEMENT_API/actuator/backupRuntime" \ -H "Content-Type: application/json" ``` :::tip It is recommended to have the primary storage backup taken after the secondary storage one, if you are coordinating the backup process. ::: ## Verify your backup setup After configuring backups, verify that they are working correctly by querying the [backup state actuator](../zeebe-backup-and-restore.md#request-runtime-state): ```bash curl 'http://localhost:9600/actuator/backupRuntime/state' ``` Confirm the following in the response: - **`backupStates`** contains an entry for each partition, indicating that at least one backup has been taken. - **`ranges`** contains at least one range per partition. Each range has a `start` and `end` timestamp defining the time period you can restore from. - If you see **multiple ranges** for a single partition, there is a gap between them where no data is available for restore. If the response is empty or missing partitions, check your backup store configuration and ensure continuous backups are enabled. ## Backing up RDBMS Back up the RDBMS as described in the [prerequisites](#prerequisites). While exact synchronization with Zeebe backups is not required, as the restore process handles alignment automatically, keeping backup intervals similar minimizes the time Zeebe needs to re-export events after a restore. During restore, Zeebe reads the **exporter position** from the `EXPORTER_POSITION` table — which records the last Zeebe log stream position exported to the RDBMS — to determine which primary storage backup to restore from. ## (Optional) Back up Web Modeler data {#back-up-web-modeler-data} If you are using Web Modeler, you can also back up its data. Web Modeler stores its data in a relational database, so you can use the same backup tools as for the RDBMS secondary storage. See [backup and restore Web Modeler data](../modeler-backup-and-restore.md) for more details. ## Primary storage retention Automatic retention for primary storage (Zeebe's) backups is available. This periodically deletes backups from the configured blob storage based on a preconfigured retention window. At least one backup is always retained to prevent potential data loss, even if it falls outside the configured retention window.. To configure backup retention settings, see [backup retention configuration](../../../../components/orchestration-cluster/core-settings/configuration/properties/#camundadataprimary-storagebackupretention). When retention deletes old backups, the affected [backup ranges](#backup-ranges) shrink accordingly, narrowing your available restore window. Ensure that your retention window is at least as long as the restore window you require. For example, if you need the ability to restore to any point in the last 7 days, set the retention window to at least `P7D`. :::note Backups created outside or before the scheduler was activated are also susceptible to being deleted by the retention mechanism. This only affects the primary storage. ::: --- ## Restore a backup (RDBMS) Restore a previous backup of your Camunda 8 Self-Managed Orchestration cluster components (Zeebe, Operate, and Tasklist) when using a relational database management system (RDBMS) as secondary storage. ## How RDBMS restore works As described in the [architecture overview](./backup.md#architecture-overview), backups involve two independent systems: **primary storage backups** (Zeebe's log stream and snapshots in a blob store) and the **secondary storage backup** (the RDBMS). During restore, Zeebe reads the **exporter position** from the restored RDBMS — the last log stream position that was successfully exported — and uses it to determine which primary storage backup, or backups, to restore from. This ensures that Zeebe's state is at least as advanced as what the RDBMS contains. After restart, Zeebe re-exports any events between the RDBMS position and its restored checkpoint position, bringing the secondary storage up to date. ## Restore process overview 1. **Ensure all [prerequisites](#prerequisites) are met** — all Camunda components are stopped. 2. RDBMS has been restored. 3. **Restore Zeebe** from its primary storage backup using one of the [restore options](#restore-options). 4. **Start all Camunda components.** ## Prerequisites The following prerequisites are required before you can restore a backup: | Prerequisite | Description | | :----------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Camunda version | Backups can be restored using the same Camunda version they were created with, or up to one minor version newer. For example, a backup taken with 8.9.x can be restored with 8.9.x or 8.10.x. | | RDBMS restored | You have already restored the RDBMS from its backup using your database vendor's native tools. The restored database must contain the entire Camunda schema. | | Backup available | At least one Zeebe primary storage backup is available in the configured blob store. See [Create a backup](./backup.md). | | Backup storage | Zeebe is configured with the same backup storage as outlined in the [prerequisites](./backup.md#prerequisites). | | Components stopped | All Camunda components (Zeebe, Operate, Tasklist, Optimize, Connectors) must be stopped before starting the restore process. | :::warning It is critical that no Camunda components are running during the restore. Running components may propagate an incorrect cluster configuration, potentially disrupting cluster communication and data consistency. ::: ## Step 1: Restore Zeebe from its primary storage backup Camunda provides a standalone restore application that must be run on each node where a Zeebe Broker will be running. This is a Spring Boot application similar to the broker and can run using the binary provided as part of the distribution. The app can be configured the same way a broker is configured — via environment variables or using the configuration file located in `config/application.yaml`. :::warning Persistent volumes or disks must not contain any pre-existing data before restoring Zeebe. If data exists from a previous deployment, it must be cleared first. ::: :::warning When restoring, provide the same configuration (node id, data directory, cluster size, and replication count) as the broker that will be running on this node. The partition count **must be the same** as in the backup. The number of partitions backed up is also visible via the [backup management API](../zeebe-backup-and-restore.md#list-backups-api). If brokers were dynamically scaled between backup and restore, this is not an issue — as long as the partition count remains unchanged. ::: ### Restore options There are four restore options. In all cases, the restore app reads the exporter position from the restored RDBMS to ensure consistency between primary and secondary storage. | Restore option | Parameters | Use case | | :------------------------------------------------ | :--------------------- | :--------------------------------------------------------------------------------------------- | | [Default restore](#default-restore) (recommended) | No parameters required | Restores as much data as possible from the latest available backup range. | | [Point-in-time restore](#point-in-time-restore) | `--to` | Restores to a specific point in time. | | [Time range restore](#time-range-restore) | `--from` and `--to` | Constrains the search to a specific backup range. Useful when automatic matching doesn't work. | | [Backup ID restore](#backup-id-restore) | `--backupId=` | Escape hatch to restore from a specific backup ID. Use at your own risk. | :::note `--backupId` is mutually exclusive with `--from`/`--to`. Specifying both will result in error. ::: --- ### Default restore **This is the recommended restore option.** No additional parameters are required — the restore application automatically determines the best backup to use. The restore app reads the exporter position from the restored RDBMS for each partition and identifies the most recent backup taken before that position. It then applies all subsequent backups in the range, restoring up to the latest available backup. ```yaml orchestration: enabled: true env: - name: SPRING_PROFILES_ACTIVE value: "restore" - name: ZEEBE_RESTORE value: "true" - name: CAMUNDA_DATA_PRIMARYSTORAGE_BACKUP_STORE value: "S3" # or GCS, AZURE, FILESYSTEM # Rest of the backup store configuration (bucket, region, etc.) - name: CAMUNDA_DATA_SECONDARY_STORAGE_TYPE value: "rdbms" # Rest of the RDBMS configuration (URL, username, password) connectors: enabled: false optimize: enabled: false ``` ```bash # Ensure RDBMS configuration is set (URL, credentials, etc.) export CAMUNDA_DATA_SECONDARY_STORAGE_TYPE=rdbms export CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_URL="jdbc:postgresql://localhost:5432/camunda" # ... other RDBMS config # Ensure backup store is configured export CAMUNDA_DATA_PRIMARYSTORAGE_BACKUP_STORE=S3 # ... other store config mkdir -p camunda tar -xzf camunda-zeebe-X.Y.Z.tar.gz --strip-components=1 -C camunda/ ./camunda/bin/restore ``` --- ### Point-in-time restore Restore Zeebe to a specific point in time using `--to`. The restore app finds the closest backup to the provided timestamp. The configured [checkpoint interval](./backup.md#checkpoint-interval) determines how fine-grained the restore points are. Use the [backup state actuator](../zeebe-backup-and-restore.md#request-runtime-state) to inspect available backup ranges. ```yaml orchestration: enabled: true env: - name: SPRING_PROFILES_ACTIVE value: "restore" - name: ZEEBE_RESTORE value: "true" - name: ZEEBE_RESTORE_TO_TIMESTAMP value: "2026-01-10T14:00:00Z" - name: CAMUNDA_DATA_PRIMARYSTORAGE_BACKUP_STORE value: "S3" # Rest of the backup store configuration - name: CAMUNDA_DATA_SECONDARY_STORAGE_TYPE value: "rdbms" # Rest of the secondary storage configuration connectors: enabled: false optimize: enabled: false ``` ```bash export ZEEBE_RESTORE_TO_TIMESTAMP=2026-01-10T14:00:00Z mkdir -p camunda tar -xzf camunda-zeebe-X.Y.Z.tar.gz --strip-components=1 -C camunda/ ./camunda/bin/restore --to="${ZEEBE_RESTORE_TO_TIMESTAMP}" ``` :::warning The `--to` timestamp must not be before the restored state of the RDBMS. If it is, the secondary storage would be ahead of the primary storage and the restore will fail. ::: --- ### Time range restore Constrain the restore to a specific backup range by specifying both `--from` and `--to`. This is useful if automatic matching did not work. The restore app finds a backup range whose start is at or before `--from` and whose end is at or after `--to`, then restores to the checkpoint closest to `--to`. ```yaml orchestration: enabled: true env: - name: SPRING_PROFILES_ACTIVE value: "restore" - name: ZEEBE_RESTORE value: "true" - name: ZEEBE_RESTORE_FROM_TIMESTAMP value: "2026-01-10T13:00:00Z" - name: ZEEBE_RESTORE_TO_TIMESTAMP value: "2026-01-10T14:00:00Z" - name: CAMUNDA_DATA_PRIMARYSTORAGE_BACKUP_STORE value: "S3" # Rest of the backup store configuration - name: CAMUNDA_DATA_SECONDARY_STORAGE_TYPE value: "rdbms" # Rest of the secondary storage configuration connectors: enabled: false optimize: enabled: false ``` ```bash export ZEEBE_RESTORE_FROM_TIMESTAMP=2026-01-10T13:00:00Z export ZEEBE_RESTORE_TO_TIMESTAMP=2026-01-10T14:00:00Z mkdir -p camunda tar -xzf camunda-zeebe-X.Y.Z.tar.gz --strip-components=1 -C camunda/ ./camunda/bin/restore --from="${ZEEBE_RESTORE_FROM_TIMESTAMP}" --to="${ZEEBE_RESTORE_TO_TIMESTAMP}" ``` :::warning The `--to` timestamp must not be before the restored state of the RDBMS. If it is, the secondary storage would be ahead of the primary storage and the restore will fail. ::: --- ### Backup ID restore Restore from one or more specific backup IDs directly. Multiple IDs can be provided as a comma-separated list. This is an escape hatch for situations where the automatic matching does not work. Use at your own risk — you are responsible for ensuring that the backups are compatible with the restored RDBMS state. ```yaml orchestration: enabled: true env: - name: SPRING_PROFILES_ACTIVE value: "restore" - name: ZEEBE_RESTORE value: "true" - name: CAMUNDA_DATA_PRIMARYSTORAGE_BACKUP_STORE value: "S3" # Rest of the backup store configuration - name: CAMUNDA_DATA_SECONDARY_STORAGE_TYPE value: "rdbms" # Rest of the secondary storage configuration connectors: enabled: false optimize: enabled: false ``` With the backup ID passed as a command-line argument: ```yaml orchestration: command: - "/usr/local/camunda/bin/restore" - "--backupId=1772001869309,1772001899400" ``` ```bash mkdir -p camunda tar -xzf camunda-zeebe-X.Y.Z.tar.gz --strip-components=1 -C camunda/ ./camunda/bin/restore --backupId=1772001869309,1772001899400 ``` :::caution When using backup ID restore, the RDBMS exporter position is not consulted. You must ensure that the provided backups are compatible with the restored RDBMS state. ::: --- ### Kubernetes-specific behavior When restoring in Kubernetes using the official [Camunda Helm chart](/self-managed/deployment/helm/install/quick-install.md), there are specific behaviors to be aware of. :::note Alternative startup override An alternative approach to overwriting the startup behavior to restore the partitions: ```yaml orchestration: enabled: true command: - "/usr/local/camunda/bin/restore" env: - name: SPRING_PROFILES_ACTIVE value: "restore" # all the envs related to the backup store as above ``` ::: The application exits after restore and Kubernetes restarts the pod, which appears as `CrashLoopBackOff`. This is expected behavior. The restore application does not restore state again once partitions are already restored to persistent disk. After removing the temporary restore command or unsetting `ZEEBE_RESTORE` to restore Zeebe's default behavior, you may optionally restart the StatefulSet to ensure the changes take effect immediately. This can be done by [scaling](https://kubernetes.io/docs/reference/kubectl/generated/kubectl_scale/) the StatefulSet down and back up, or by [deleting](https://kubernetes.io/docs/reference/kubectl/generated/kubectl_delete/) the pods so they are recreated with the newly deployed revision. :::tip In Kubernetes, Zeebe runs as a [StatefulSet](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/), which is intended for long-running, persistent applications. Because StatefulSet pods are restarted automatically, restore-mode pods can appear in `CrashLoopBackOff` after a successful restore. Observe Zeebe Broker logs during restore. If a pod has already restarted, use `--previous` to view logs from the completed restore run: ```bash kubectl logs --previous ``` The restore app will not import or overwrite data again, but you may miss the first successful run if you are not observing logs actively. ::: ## Restore success or failure If restore was successful, the app exits with the log message `Successfully restored broker from backup`. However, the restore will fail if: - There is no valid backup matching the secondary storage (the exporter position exceeds all available backups). - There is no valid backup within the specified time range. - The backup store is not configured correctly. - The configured data directory is not empty. - There is a gap in the backup range needed for restore (missing backups between the range start and the required checkpoint). - The exporter position in the RDBMS is missing for one or more partitions (when using RDBMS-aware restore). - Due to any other unexpected errors. If the restore fails, you can re-run the application after fixing the root cause. ### Data directory is not empty If the data directory is not empty, the restore will fail with an error message: ``` Broker's data directory /usr/local/zeebe/data is not empty. Aborting restore to avoid overwriting data. Please restart with a clean directory ``` On some filesystems, the data directory may contain special files and folders that can't or shouldn't be deleted. In such cases, the restore application can be configured to ignore the presence of these files and folders. The configuration option `zeebe.restore.ignoreFilesInTarget` takes a list of file and folder names to ignore. By default, it ignores the `lost+found` folder found on ext4 filesystems. To also ignore `.snapshot` folders, set `zeebe.restore.ignoreFilesInTarget: [".snapshot", "lost+found"]` or the equivalent environment variable `ZEEBE_RESTORE_IGNOREFILESINTARGET=".snapshot,lost+found"`. ## Step 2: Start all Camunda 8 components {#start-all-camunda-8-components} After both primary and secondary storage are restored, start all Camunda components. Ensure all components are configured to use the restored database instance and that the configuration matches the original deployment. :::note After starting the components, monitor the logs for any errors or warnings. Components will reconcile their state with the restored data, which may take some time depending on the size of the data. When using RDBMS-aware or time range restore, Zeebe re-exports events from the backup's checkpoint position up to its current state, bringing the RDBMS up to date. ::: ## (Optional) Restore Web Modeler data If you previously backed up Web Modeler data, restore it using the same RDBMS restore tools. See [backup and restore Web Modeler data](../modeler-backup-and-restore.md) for more details. --- ## Web applications backup management API :::warning breaking changes As of the Camunda 8.8 release, the `/actuator` endpoints for backups have been moved to `/actuator/backupHistory`. The previous `/actuator/backups` endpoint is still active only if the applications are deployed standalone (each application is running in its own process). ::: :::note This page refers to the components Operate and Tasklist as "web applications". Optimize is not backed up as part of this process. Optimize is a dedicated application with its own backup system. Please see the [documentation for Optimize](./optimize-backup.md) to perform a backup. ::: ## About this API The Camunda web applications store their data over multiple indices in Elasticsearch. A backup of web application data includes several Elasticsearch snapshots containing sets of different indices. Each backup is identified by a `backupId`. For example, a backup with an ID of `123` may contain the following Elasticsearch snapshots: ``` camunda_webapps_123_8.8.0_part_1_of_6 camunda_webapps_123_8.8.0_part_2_of_6 camunda_webapps_123_8.8.0_part_3_of_6 camunda_webapps_123_8.8.0_part_4_of_6 camunda_webapps_123_8.8.0_part_5_of_6 camunda_webapps_123_8.8.0_part_6_of_6 ``` All web applications provide the same API to perform a backup and manage backups (list, check state, delete). Restore a backup using the standard Elasticsearch API. :::note The backup API can be reached via the Actuator management port, which default defaults to port 9600. ::: :::warning Usage of this API requires the backup store to be configured with the **same** repository name. - [Operate configuration](/self-managed/components/orchestration-cluster/operate/operate-configuration.md#backups) - [Tasklist configuration](/self-managed/components/orchestration-cluster/tasklist/tasklist-configuration.md#backups) Additionally, it requires the same backup store to be configured on your chosen datastore. - [Elasticsearch snapshot repository](https://www.elastic.co/docs/deploy-manage/tools/snapshot-and-restore/manage-snapshot-repositories) - [OpenSearch snapshot repository](https://docs.opensearch.org/docs/latest/tuning-your-cluster/availability-and-recovery/snapshots/snapshot-restore/) Web applications must have the right to take snapshots. ::: ## Create backup API During backup creation, web applications can continue running. To create the backup, call the following endpoint: ``` POST actuator/backupHistory { "backupId": } ``` :::note For backward compatibility, the endpoint `actuator/backups` is available if the component is running standalone. ::: Response: | Code | Description | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | 200 OK | Backup was successfully started, snapshots will be created asynchronously. List of snapshots is returned in the response body (see example below). This list must be persisted together with the backup ID to be able to restore it later. | | 400 Bad Request | In case something is wrong with `backupId`, e.g. the same backup ID already exists. | | 500 Server Error | All other errors, e.g. ES returned error response when attempting to create a snapshot. | | 502 Bad Gateway | Elasticsearch is not accessible, the request can be retried when it is back. | Example request: ```shell curl --request POST 'http://localhost:9600/actuator/backupHistory' \ -H 'Content-Type: application/json' \ -d '{ "backupId": 123 }' ``` Example response: ```json { "scheduledSnapshots": [ "camunda_webapps_123_8.8.0_part_1_of_6", "camunda_webapps_123_8.8.0_part_2_of_6", "camunda_webapps_123_8.8.0_part_3_of_6", "camunda_webapps_123_8.8.0_part_4_of_6", "camunda_webapps_123_8.8.0_part_5_of_6", "camunda_webapps_123_8.8.0_part_6_of_6" ] } ``` ## Get backup state API As a backup is created asynchronously, call the following endpoint to check the state of the backup: ``` GET actuator/backupHistory/{backupId} ``` :::note For backward compatibility, the endpoint `actuator/backups` is available if the component is running standalone. ::: Response: | Code | Description | | ---------------- | --------------------------------------------------------------------------------------- | | 200 OK | Backup state could be determined and is returned in the response body. | | 404 Not Found | Backup with given ID does not exist. | | 500 Server Error | All other errors, e.g. ES returned error response when attempting to execute the query. | | 502 Bad Gateway | Elasticsearch is not accessible, the request can be retried when it is back. | For example, the request could look like this: ```shell curl 'http://localhost:9600/actuator/backupHistory/123' ``` Example response: ```json { "backupId": 123, "state": "COMPLETED", "failureReason": null, "details": [ //here goes the list of all Elasticsearch snapshots included in the backup { "snapshotName": "camunda_webapps_123_8.8.0_part_1_of_6", "state": "SUCCESS", "startTime": "2023-01-01T10:10:10.100+0000", "failures": [] }, <..> ] } ``` Possible **states** of the backup: - `COMPLETED`: Backup can be used for restoring the data. - `IN_PROGRESS`: Wait until the backup completes to use it for restore. - `FAILED`: Something went wrong when creating this backup. To find out the exact problem, use the [Elasticsearch get snapshot status API](https://www.elastic.co/guide/en/elasticsearch/reference/current/get-snapshot-status-api.html) for each of the snapshots included in the given backup. - `INCOMPATIBLE`: Backup is incompatible with the current Elasticsearch version. - `INCOMPLETE`: Backup is incomplete (e.g. when backup process was interrupted). **State** of the individual snapshot is a copy of [Elasticsearch state](https://www.elastic.co/guide/en/elasticsearch/reference/7.17/get-snapshot-api.html#get-snapshot-api-response-state). ## Get backups list API To get the list of existing backups, the following endpoint can be used: ``` GET actuator/backupHistory ``` :::note For backward compatibility, the endpoint `actuator/backups` is available if the component is running standalone. ::: ### Query parameters The following optional query parameters can be used to optimize the response: | Parameter | Description | | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | `verbose` | Controls the level of detail in the response. When set to `false`, returns a streamlined response with essential information. Default: `true`. | | `pattern` | Filters backups by ID pattern. For example, `pattern=20250401*` returns only backups with IDs starting with `20250401`. | Response: | Code | Description | | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | 200 OK | Backup list could be determined and is returned in the response body. Can be an empty response in case no backups were created yet. | | 404 Not Found | Backup repository is not configured. | | 500 Server Error | All other errors, e.g. ES returned error response when attempting to execute the query. | | 502 Bad Gateway | Elasticsearch is not accessible, the request can be retried when it is back. | Example requests: ```shell # Get all backups curl 'http://localhost:9600/actuator/backupHistory' # Get backups with streamlined response curl 'http://localhost:9600/actuator/backupHistory?verbose=false' # Get backups matching a specific pattern curl 'http://localhost:9600/actuator/backupHistory?pattern=20250401*' # Combine parameters for optimized filtered results curl 'http://localhost:9600/actuator/backupHistory?verbose=false&pattern=20250401*' ``` Response will contain JSON with array of objects representing state of each backup (see [get backup state API endpoint](#get-backup-state-api)). ## Delete backup API To delete all the Elasticsearch snapshots associated with the specific backup id, the following endpoint may be used: ``` DELETE actuator/backupHistory/123 ``` :::note For backward compatibility, the endpoint `actuator/backups` is available if the component is running standalone. ::: Response: | Code | Description | | ---------------- | ---------------------------------------------------------------------------------------------------------------------------- | | 204 No Content | All commands to delete corresponding ELS snapshots were successfully sent to ELS. ELS will continue deletion asynchronously. | | 404 Not Found | Not a single snapshot corresponding to given ID exist. | | 500 Server Error | All other errors, e.g. ES returned error response when attempting to execute the query. | | 502 Bad Gateway | Elasticsearch is not accessible, the request can be retried when it is back. | --- ## Zeebe backup management API :::warning breaking changes As of the Camunda 8.8 release, the `/actuator` endpoints for backups have been moved to `/actuator/backupRuntime`. The previous `/actuator/backups` endpoint is still active only if the applications are deployed standalone (each application is running in its own process). ::: Back up a running Zeebe cluster using the Backup Management API. ## About this API A backup of a Zeebe cluster comprises a consistent snapshot of all partitions. The backup is taken asynchronously in the background while Zeebe is processing. Thus, backups can be taken with minimal impact on typical processing. Backups can be used to restore a cluster in case of failures that lead to full data loss or data corruption. Zeebe provides a REST API to create, query, and manage backups. The backup management API is a custom endpoint `backups`, available via [Spring Boot Actuator](https://docs.spring.io/spring-boot/docs/2.7.x/reference/htmlsingle/#actuator.endpoints). It is accessible via the management port of the Zeebe Gateway. The API documentation is also available as an [OpenAPI specification](https://github.com/camunda/camunda/blob/main/dist/src/main/resources/api/backup-management-api.yaml). :::warning Usage of this API requires the backup store to be configured for the component. - [Zeebe configuration](/self-managed/components/orchestration-cluster/zeebe/configuration/broker.md#zeebebrokerdatabackup) ::: To use the backup feature in Zeebe, you must choose which external storage system you will use. Make sure to set the same configuration on all brokers in your cluster. Zeebe supports [S3](/self-managed/components/orchestration-cluster/zeebe/configuration/broker.md#zeebebrokerdatabackups3), [Google Cloud Storage (GCS)](/self-managed/components/orchestration-cluster/zeebe/configuration/broker.md#zeebebrokerdatabackupgcs), and [Azure](/self-managed/components/orchestration-cluster/zeebe/configuration/broker.md#zeebebrokerdatabackupazure), and [local filesystem](/self-managed/components/orchestration-cluster/zeebe/configuration/broker.md#zeebebrokerdatabackupfilesystem) for external storage. :::caution Backups created with one store are not available in or restorable from another store. This is especially relevant if you were using GCS through the S3 compatibility mode and want to switch to the new built-in support for GCS now. Even when the underlying storage bucket is the same, backups from one are not compatible with the other. ::: ## Create backup API The following request can be used to start a backup. ### Request ``` POST actuator/backupRuntime { "backupId": } ``` A `backupId` is an integer and must be greater than the ID of previous backups that are completed, failed, or deleted. Zeebe does not take two backups with the same IDs. If a backup fails, a new `backupId` must be provided to trigger a new backup. The `backupId` cannot be reused, even if the backup corresponding to the backup ID is deleted. :::note When continuous backups, the backup scheduler, or the checkpoint scheduler are enabled, the `backupId` parameter must be omitted. Instead, the ID is generated by the cluster. :::
Example request ```shell curl --request POST 'http://localhost:9600/actuator/backupRuntime' \ -H 'Content-Type: application/json' \ -d '{ "backupId": "100" }' ```
### Response | Code | Description | | ---------------- | ------------------------------------------------------------------------------------------------------------------------ | | 202 Accepted | A backup has been successfully scheduled. To determine if the backup process was completed, refer to the GET API. | | 400 Bad Request | Indicates issues with the request, for example when the `backupId` is not valid or backup is not enabled on the cluster. | | 409 Conflict | Indicates a backup with the same `backupId` or a higher ID already exists. | | 500 Server Error | All other errors. Refer to the returned error message for more details. | | 502 Bad Gateway | Zeebe has encountered issues while communicating with different brokers. | | 504 Timeout | Zeebe failed to process the request within a predetermined timeout. |
Example response body with 202 Accepted ```json { "backupId": 1772011199310, "message": "A backup with id 100 has been scheduled. Use GET actuator/backups/100 to monitor the status." } ```
## Get backup info API Information about a specific backup can be retrieved using the following request: ### Request ``` GET actuator/backupRuntime/{backupId} ```
Example request ```shell curl --request GET 'http://localhost:9600/actuator/backupRuntime/100' ```
### Response | Code | Description | | ---------------- | ------------------------------------------------------------------------------------------ | | 200 OK | Backup state could be determined and is returned in the response body (see example below). | | 400 Bad Request | There is an issue with the request. Refer to the returned error message for details. | | 404 Not Found | A backup with that ID does not exist. | | 500 Server Error | All other errors. Refer to the returned error message for more details. | | 502 Bad Gateway | Zeebe has encountered issues while communicating with different brokers. | | 504 Timeout | Zeebe failed to process the request within a pre-determined timeout. | When the response is 200 OK, the response body consists of a JSON object describing the state of the backup. - `backupId`: ID in the request. - `state`: Gives the overall status of the backup. The state can be one of the following: - `COMPLETED` if all partitions have completed the backup. - `FAILED` if at least one partition has failed. In this case, `failureReason` contains a string describing the reason for failure. - `INCOMPLETE` if at least one partition's backup does not exist. - `IN_PROGRESS` if at least one partition's backup is in progress. - `DELETED` if at least one partition's backup is deleted. - `details`: Gives the state of each partition's backup. - `failureReason`: The reason for failure if the state is `FAILED`.
Example response body with 200 OK ```json { "backupId": 100, "details": [ { "brokerVersion": "8.2.0-SNAPSHOT", "checkpointPosition": 5, "createdAt": "2022-12-08T13:00:55.344276672Z", "lastUpdatedAt": "2022-12-08T13:00:55.805351556Z", "partitionId": 1, "snapshotId": "2-1-3-2", "state": "COMPLETED" }, { "brokerVersion": "8.2.0-SNAPSHOT", "checkpointPosition": 7, "createdAt": "2022-12-08T13:00:55.370965069Z", "lastUpdatedAt": "2022-12-08T13:00:55.84756566Z", "partitionId": 2, "snapshotId": "3-1-5-3", "state": "COMPLETED" } ], "state": "COMPLETED" } ```
## List backups API Information about all backups can be retrieved using the following request: ### Request to list all backups ``` GET actuator/backupRuntime ```
Example request ```shell curl --request GET 'http://localhost:9600/actuator/backupRuntime' ```
### Request to list backups matching a prefix The list of backups can be filtered by specifying a backup ID prefix: ``` GET actuator/backupRuntime/{backupIdPrefix} ``` The backup ID prefix must end with `*`, for example `10*` will match all backups with IDs starting with `10`.
Example request ```shell curl --request GET 'http://localhost:9600/actuator/backupRuntime/10*' ```
### Response | Code | Description | | ---------------- | ------------------------------------------------------------------------------------------ | | 200 OK | Backup state could be determined and is returned in the response body (see example below). | | 400 Bad Request | There is an issue with the request. Refer to the returned error message for details. | | 500 Server Error | All other errors. Refer to the returned error message for more details. | | 502 Bad Gateway | Zeebe has encountered issues while communicating with different brokers. | | 504 Timeout | Zeebe failed to process the request within a predetermined timeout. | When the response is 200 OK, the response body consists of a JSON object with a list of backup info. See [get backup info API response](#response-1) for the description of each field.
Example response body with 200 OK ```json [ { "backupId": 100, "details": [ { "brokerVersion": "8.2.0-SNAPSHOT", "createdAt": "2022-12-08T13:00:55.344276672Z", "partitionId": 1, "state": "COMPLETED" }, { "brokerVersion": "8.2.0-SNAPSHOT", "createdAt": "2022-12-08T13:00:55.370965069Z", "partitionId": 2, "state": "COMPLETED" } ], "state": "COMPLETED" }, { "backupId": 200, "details": [ { "brokerVersion": "8.2.0-SNAPSHOT", "createdAt": "2022-12-08T13:01:15.27750375Z", "partitionId": 1, "state": "COMPLETED" }, { "brokerVersion": "8.2.0-SNAPSHOT", "createdAt": "2022-12-08T13:01:15.279995106Z", "partitionId": 2, "state": "COMPLETED" } ], "state": "COMPLETED" } ] ```
## Delete backup API A backup can be deleted using the following request: ### Request ``` DELETE actuator/backupRuntime/{backupId} ```
Example request ```shell curl --request DELETE 'http://localhost:9600/actuator/backupRuntime/100' ```
### Response | Code | Description | | ---------------- | ------------------------------------------------------------------------------------ | | 204 No Content | The backup has been deleted. | | 400 Bad Request | There is an issue with the request. Refer to the returned error message for details. | | 500 Server Error | All other errors. Refer to the returned error message for more details. | | 502 Bad Gateway | Zeebe has encountered issues while communicating with different brokers. | | 504 Timeout | Zeebe failed to process the request within a predetermined timeout. | ## Request runtime state Acquire information regarding backups from the runtime state. ### Request ``` GET actuator/backupRuntime/state ```
Example request ```shell curl --request GET 'http://localhost:9600/actuator/backupRuntime/state' ```
### Response This endpoint returns the current runtime state of backups and checkpoints across all partitions. #### HTTP status codes | Code | Description | | ------------------------- | ----------------------------------------------------------------------------------------------------------- | | 200 OK | The backup state was successfully determined and is returned in the response body. | | 400 Bad Request | The request is invalid. Refer to the returned error message for details. | | 500 Internal Server Error | An unexpected error occurred while processing the request. Refer to the returned error message for details. | | 502 Bad Gateway | Zeebe encountered issues while communicating with other brokers. | | 504 Gateway Timeout | Zeebe was unable to process the request within the configured timeout. | #### Response body When the response status is `200 OK`, the response body contains a JSON object describing the current backup and checkpoint state. ##### `checkpointStates` Latest checkpoint information per partition. - `checkpointId`: Identifier of the checkpoint. - `partitionId`: Identifier of the partition. - `checkpointType`: Type of the checkpoint. One of `MARKER`, `SCHEDULED_BACKUP`, or `MANUAL_BACKUP`. - `checkpointPosition`: Log stream position of the checkpoint record. - `checkpointTimestamp`: Timestamp when the checkpoint was created. ##### `backupStates` Latest backup information per partition. - `checkpointId`: Identifier of the associated checkpoint. - `partitionId`: Identifier of the partition. - `checkpointType`: Type of the checkpoint. One of `MARKER`, `SCHEDULED_BACKUP`, or `MANUAL_BACKUP`. - `checkpointPosition`: Log stream position of the checkpoint record. - `checkpointTimestamp`: Timestamp when the checkpoint was created. - `firstLogPosition`: First available log stream position included in this backup. ##### `ranges` List of active continuous backup ranges per partition. - `partitionId`: Identifier of the partition. - `start`: First backup in the continuous backup range. - `checkpointId`: Identifier of the checkpoint. - `checkpointType`: Type of the checkpoint. - `checkpointPosition`: Log stream position of the checkpoint record. - `checkpointTimestamp`: Timestamp when the checkpoint was created. - `firstLogPosition`: First available log stream position included in this backup. - `end`: Last backup in the continuous backup range. - `checkpointId`: Identifier of the checkpoint. - `checkpointType`: Type of the checkpoint. - `checkpointPosition`: Log stream position of the checkpoint record. - `checkpointTimestamp`: Timestamp when the checkpoint was created. - `firstLogPosition`: First available log stream position included in this backup.
Example response body with 200 OK ```json { "checkpointStates": [ { "checkpointId": 1772001869309, "checkpointType": "SCHEDULED_BACKUP", "partitionId": 1, "checkpointPosition": 580, "checkpointTimestamp": "2026-02-25T06:44:29.309Z" }, { "checkpointId": 1772001869309, "checkpointType": "SCHEDULED_BACKUP", "partitionId": 2, "checkpointPosition": 135, "checkpointTimestamp": "2026-02-25T06:44:29.308Z" } ], "backupStates": [ { "checkpointId": 1772001869309, "checkpointType": "SCHEDULED_BACKUP", "partitionId": 1, "checkpointPosition": 580, "firstLogPosition": 1, "checkpointTimestamp": "2026-02-25T06:44:29.391Z" }, { "checkpointId": 1772001869309, "checkpointType": "SCHEDULED_BACKUP", "partitionId": 2, "checkpointPosition": 135, "firstLogPosition": 1, "checkpointTimestamp": "2026-02-25T06:44:29.394Z" } ], "ranges": [ { "partitionId": 1, "start": { "checkpointId": 1772001838336, "checkpointType": "SCHEDULED_BACKUP", "checkpointPosition": 580, "firstLogPosition": 1, "checkpointTimestamp": "2026-02-25T06:44:29.391Z" }, "end": { "checkpointId": 1772001869309, "checkpointType": "SCHEDULED_BACKUP", "checkpointPosition": 580, "firstLogPosition": 1, "checkpointTimestamp": "2026-02-25T06:44:29.391Z" } }, { "partitionId": 2, "start": { "checkpointId": 1772001838336, "checkpointType": "SCHEDULED_BACKUP", "checkpointPosition": 135, "firstLogPosition": 1, "checkpointTimestamp": "2026-02-25T06:44:29.394Z" }, "end": { "checkpointId": 1772001869309, "checkpointType": "SCHEDULED_BACKUP", "checkpointPosition": 135, "firstLogPosition": 1, "checkpointTimestamp": "2026-02-25T06:44:29.394Z" } } ] } ```
## Sync runtime state Force a synchronization of the backup metadata stored in the blob store. This returns the updated state in the same format as the [GET state](#request-runtime-state) endpoint. ### Request ``` POST actuator/backupRuntime/state/sync ```
Example request ```shell curl --request POST 'http://localhost:9600/actuator/backupRuntime/state/sync' ```
### Response | Code | Description | | ------------------------- | ----------------------------------------------------------------------------------------------------------- | | 200 OK | The backup state was successfully synced and is returned in the response body. | | 500 Internal Server Error | An unexpected error occurred while processing the request. Refer to the returned error message for details. | | 502 Bad Gateway | Zeebe encountered issues while communicating with other brokers. | | 504 Gateway Timeout | Zeebe was unable to process the request within the configured timeout. | ## Delete runtime state Clear the backup runtime state (checkpoint state and range tracking). Ranges will be rebuilt from the next backup onward. :::caution This clears all tracked checkpoint and range state. Ranges will be rebuilt from the next backup onward, but previously tracked ranges are lost. This can be useful when switching backup stores (for example, from S3 to GCS) or after all backups have been lost from the store. ::: ### Request ``` DELETE actuator/backupRuntime/state ```
Example request ```shell curl --request DELETE 'http://localhost:9600/actuator/backupRuntime/state' ```
### Response | Code | Description | | ------------------------- | ----------------------------------------------------------------------------------------------------------- | | 204 No Content | The runtime state has been cleared. | | 500 Internal Server Error | An unexpected error occurred while processing the request. Refer to the returned error message for details. | | 502 Bad Gateway | Zeebe encountered issues while communicating with other brokers. | | 504 Gateway Timeout | Zeebe was unable to process the request within the configured timeout. | --- ## Camunda components flow control configuration When internal requests are processed faster than the rate at which they are exported, backlogs of unexported records can occur. Flow control slows the write rate of new records through both static write limits and optional dynamic throttling, and prevents the stream from building an excessive backlog of records not yet exported. For user commands, this will show up as increased [backpressure](/components/zeebe/technical-concepts/internal-processing.md#handling-backpressure), observed latency, and reduced throughput. [Internal processing](/components/zeebe/technical-concepts/internal-processing.md) slows down as the stream processor waits longer for processing results to be written. Write rate limiting applies to all new records, including processing results, user commands, inter-partition messages, and scheduled tasks. ## Enable flow control The write rate can be set as a static limit, which defines the upper rate at which records can be written. The dynamic limit, known as **throttling,** adjusts the write rate based on the exporting rate and backlog, and can be applied alongside the static limit. A static write rate limit can prevent throughput peaks, and write rate throttling can keep the backlog stable by temporarily decreasing the static limit to keep the exporting backlog small. When configuring dynamic throttling, configuring a high static limit can help maintain a high write rate if the exporting can keep up. :::note These write limits are enabled by default in SaaS and disabled in Self-Managed. For most use cases, write rate limits can be enabled as needed if an issue arises. ::: Flow control is configured in your Zeebe Broker's `application.yaml` file. The default values can be found in the `# flowControl` section of the Zeebe Broker [configuration](https://github.com/camunda/camunda/blob/main/dist/src/main/config/defaults.yaml) templates. ```yaml zeebe: broker: flowControl: write: enabled: false rampUp: 0 limit: 1000 throttling: enabled: false acceptableBacklog: 100000 minimumLimit: 100 resolution: 15s ``` :::note The limit and in-flight count are calculated per partition. ::: | Field | Description | Default Value | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------- | | `rampUp` | The time from startup during which the write limit is slowly increased until the configured limit. Useful for a warm-up period where a lower write rate is beneficial. This value is given in seconds and cannot be set null nor negative. | `0` | | `limit` | A static value to use as the write rate. This value cannot be null nor negative. | `1000` | | `throttling` | If enabled, `throttling` will additionally limit the write rate based on the exporting backlog. The exporting backlog is the quantity of records that were written, but not yet exported. An excessive exporting backlog is usually due to a mismatch of the rate of processed and exported records, or to degraded exporting capability. The `throttling` algorithm used takes into account the ratio between the acceptable backlog and the actual backlog. If the acceptable backlog is at least twice as long as the real backlog, the current rate is set as the limit. If the ratio between the acceptable backlog and current backlog is less than two, the rate is calculated by multiplying the ratio with the current exporting rate, with `minimumLimit` as the floor and `limit` as the ceiling. The intention is to proportionally increase or decrease the rate according to the ratio of the acceptable backlog and the actual backlog. If the acceptable backlog is larger than the current backlog, the rate is increased, and if the acceptable backlog is smaller than the current backlog, the rate is decreased. If the processing speed continues to outpace the exporting speed, the current backlog should stabilize around the acceptable backlog size. | `enabled: false` | | `resolution` | The frequency with which `throttling` is adjusted, given in seconds. Adjusting this value sets the speed at which the processing rate can respond to changes. | `15s` | The exporting rate is the number of exported records per second, averaged out over the last five minutes. ## Configure temporary write limits The flow control endpoint can be used to adjust the flow control configuration temporarily, without having to reset your clusters. :::caution The flow control endpoint is intended as a temporary solution, and changes should be reverted after the issue is addressed. Permanent configuration changes should be made through the environment variables. Configuring flow control through the available endpoint does not preserve the configuration in the broker state. If the broker restarts, any leader partition in this broker will revert to the defined configuration in the environment variables. ::: ### Fetch current configuration The backup API can be reached via the `/actuator` management port, which is 9600 by default. The configured context path does not apply to the management port. The following endpoint can be used to fetch the flow control configuration: ``` GET actuator/flowControl ``` #### Response | Code | Description | | ---------------- | ----------------------------------------------------------------------- | | 200 Accepted | The flow configuration was retrieved successfully. | | 400 Bad Request | Indicates issues with the request. | | 500 Server Error | All other errors. Refer to the returned error message for more details. | #### Example request ``` curl -X GET 'localhost:9600/actuator/flowControl' ``` #### Example response ```json { "1": { "requestLimiter": { "delegate": { "limit": 100, "minLimit": 1, "maxLimit": 1000, "backoffRatio": 0.9, "expectedRTT": 200000000 } }, "writeRateLimit": { "enabled": true, "limit": 4000, "rampUp": 0.0, "throttling": { "enabled": true, "acceptableBacklog": 100000, "minRate": 100, "resolution": 15.0 } } } } ``` :::note The `writeRateLimit` value can be null if it has not been defined yet. ::: ### Set a new configuration To set a new flow control configuration, make a `POST` request to the `actuator/flowControl` endpoint. This request will attempt to configure all partitions. Partitions might differ in configuration if, for example, a broker restarts and the leader partition reverts to the configuration defined in the environment variables. ``` POST actuator/flowControl ``` ```json { "write": { "rampUp": , "enabled": , "limit": , "throttling": { "enabled": , "acceptableBacklog": , "minimumLimit": , "resolution": } } } ``` #### Response | Code | Description | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | 200 Accepted | The flow configuration request was processed correctly. | | 400 Bad Request | Indicates issues with the request, for example, one of the fields contains an invalid type. | | 500 Server Error | All other errors. For example, when the values set do not conform to the imposed restriction (such as `minimumLimit` being higher than `limit`). Refer to the returned error message for more details. | #### Example request ```bash curl -X POST 'localhost:9600/actuator/flowControl' -H "Content-Type: application/json" --data '{ "write": { "rampUp": 0, "enabled": true, "limit": 2000, "throttling": { "enabled": true, "acceptableBacklog": 100000, "minimumLimit": 200, "resolution": 15 } } }' ``` ### Example response ```js { "1": { "requestLimiter": { "delegate": { "limit": 100, "minLimit": 1, "maxLimit": 1000, "backoffRatio": 0.9, "expectedRTT": 200000000 } }, "writeRateLimit": { "enabled": true, "limit": 4000, "rampUp": 0.0, "throttling": { "enabled": true, "acceptableBacklog": 100000, "minRate": 100, "resolution": 15.0 } } } } ``` :::note The first value in the response (`1` in the example) refers to the partition before the flow configuration is defined. ::: ## View write rate limits in Grafana ### Throttling Dynamic throttling, when actively acting on the current rate (and not only enabled), displays in Grafana with an underlying yellow bar for the period it was active. ![backpressure-throttling](img/backpressure-throttling.png) ### Exporting backlog The exporting backlog panel is found under the **Processing** row and displays the number of records not yet exported per partition. ![exporting-backlog](img/exporting-backlog.png) ### Exporting and write rate The **Measured exporting rate** and **Accepted writes by source** panels are found under the **Logstream** row. The first shows the number of records accepted by flow control per second, organized by partition and write source (for example, processing result, scheduled tasks, etc.). The second displays measured average exporting rate which may be used to throttle write rate. ![measured-exporting-rate](img/mesured-exporting-rate.png) ![accepted-by-writes-source](img/accepted-by-writes-source.png) #### Write rate limit The **Write rate limits** panel is under the **Logstream** row, and displays the current and maximum permissible write rate limit per partition. ![write-rate-limit](img/write-rate-limit.png) --- ## Camunda data purge The data purge feature allows you to delete all runtime and historical data from your cluster. This operation resets the cluster to an empty state while maintaining the original topology. The purge operation performs two main actions: 1. **Runtime Data Deletion**: Removes all live data from brokers, for example process definitions, instances, and jobs. 2. **Historical Data Purge**: Clears exported data from configured exporters The data purge feature can be used to: - Delete data between test runs and therefore enabling reuse of the same cluster for multiple tests. - Resetting development or staging environments to a clean state. ## Purge data You will need access to the Cluster API as described in the [Cluster scaling guide](self-managed/components/orchestration-cluster/zeebe/operations/cluster-scaling.md) to perform the purge. :::danger The purge operation is irreversible. It will delete the runtime data in the cluster and the historical data in the exporters! Make sure to back up your data before proceeding. ::: ### Usage The purge operation is a cluster-wide, asynchronous operation. Since it is asynchronous, you first launch it by sending a `POST` request to `/actuator/cluster/purge`, and then monitor by polling the topology via `/actuator/cluster` until it is finished. :::note This example relies on the [curl](https://curl.se/) and [jq](https://jqlang.org/) utilities. ::: ```sh changeId=$(curl -sL -X POST 'http://localhost:9600/actuator/cluster/purge' | jq '.changeId') lastChangeId=-1 while [ ! $changeId -eq $lastChangeId ]; do lastChangeId=$(curl -sL 'http://localhost:9600/actuator/cluster' | jq '.lastChange.id') [ $changeId -ge $lastChangeId ] && break echo "Awaiting last change ID ${lastChangeId} to be equal to purge change ID ${changeId}" sleep 1 done ``` :::note This example relies on code generated from [this OpenAPI spec](https://github.com/camunda/camunda/blob/main/dist/src/main/resources/api/cluster/cluster-api.yaml), bundled with the distribution. ::: ```java final String baseURL = "http://localhost:9600/actuator/cluster"; final URL monitorURI = URI.create(baseURL).toURL(); final URI purgeURI = URI.create(baseURL + "/purge"); final ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule()); try (final HttpClient client = HttpClient.newHttpClient()) { final HttpRequest purgeRequest = HttpRequest.newBuilder().uri(purgeURI).POST(HttpRequest.BodyPublishers.noBody()).build(); final HttpResponse purgeResponse = client.send(purgeRequest, BodyHandlers.ofInputStream()); final PlannedOperationsResponse purgePlan = objectMapper.readValue(purgeResponse.body(), PlannedOperationsResponse.class); final long purgeChangeId = purgePlan.getChangeId(); long lastChangeId = -1; while (purgeChangeId != lastChangeId) { final GetTopologyResponse topology = objectMapper.readValue(monitorURI, GetTopologyResponse.class); lastChangeId = topology.getLastChange().getId(); if (lastChangeId >= purgeChangeId) { break; } System.out.println( "Waiting until the last change ID " + lastChangeId + " is equal to the purge change ID " + purgeChangeId); Thread.sleep(1_000); } } ``` To know if your purge operation is finished, compare the change ID returned by launching it with the last change ID from the topology request. When the last change ID is greater than or equal to your purge operation's change ID, then purging is finished. ### 1. Send the purge request to the Zeebe Gateway To purge data from your cluster, send a `POST` request to the `/actuator/cluster/purge` endpoint: ```sh curl -X POST 'http://localhost:9600/actuator/cluster/purge' ``` The response is a [JSON object](https://github.com/camunda/camunda/blob/main/dist/src/main/resources/api/cluster/cluster-api.yaml): ```json { changeId: currentTopology: [...] plannedChanges: [...] expectedTopology: [...] } ``` - `changeId`: The ID of the changes initiated to scale the cluster. This can be used to monitor the progress of the scaling operation. The ID typically increases, so new requests have a higher ID than previous requests. - `currentTopology`: A list of current brokers and the partition distribution. - `plannedChanges`: A sequence of operations that has to be executed to achieve scaling. - `expectedToplogy`: The expected list of brokers and the partition distribution once the scaling is completed. For the purge feature, the expected topology will be the same as the current topology.
Example response ```json { "changeId": 2, "currentTopology": [ { "id": 0, "state": "ACTIVE", "version": 0, "lastUpdatedAt": "0000-01-01T00:00:00Z", "partitions": [ { "id": 1, "state": "ACTIVE", "priority": 1, "config": { "exporting": { "exporters": [] } } } ] } ], "plannedChanges": [ { "operation": "PARTITION_LEAVE", "brokerId": 0, "partitionId": 1, "brokers": [] }, { "operation": "DELETE_HISTORY", "brokers": [] }, { "operation": "PARTITION_BOOTSTRAP", "brokerId": 0, "partitionId": 1, "priority": 1, "brokers": [] } ], "expectedTopology": [ { "id": 0, "state": "ACTIVE", "version": 4, "lastUpdatedAt": "2025-03-04T09:50:14.979435Z", "partitions": [ { "id": 1, "state": "ACTIVE", "priority": 1, "config": { "exporting": { "exporters": [] } } } ] } ] } ```
The purging is done asynchronously. ### 2. Monitor the progress of the purge operation The purge operation can take some time to complete, depending on the amount of data and the type of exporter. You can monitor the progress of the operation by sending a `GET` request to the `/actuator/cluster` endpoint: ```sh curl --request GET 'http://localhost:9600/actuator/cluster' ``` When the scaling has completed, the `changeId` from the previous response will be marked as completed: ```json { "version": 3, "brokers": [ { "id": 0, "state": "ACTIVE", "version": 4, "lastUpdatedAt": "2025-03-04T09:50:15.534347Z", "partitions": [ { "id": 1, "state": "ACTIVE", "priority": 1, "config": { "exporting": { "exporters": [] } } } ] } ], "lastChange": { "id": 2, "status": "COMPLETED", "startedAt": "2025-03-04T09:50:14.980254Z", "completedAt": "2025-03-04T09:50:15.534398Z" } } ``` ## Considerations ### 1. DryRun You can set the `dryRun` parameter to `true` to simulate the purge operation without deleting any data. This can be useful to understand the impact of the operation before proceeding. ```sh curl -X POST 'http://localhost:9600/actuator/cluster/purge?dryRun=true' ``` ### 2. Don't perform the purge operation during other cluster operations You cannot perform the purge operation if another cluster operation is already in progress (for example, scaling). Similarly, you cannot perform other cluster operations while the purge operation is in progress. ## Troubleshooting The data purge operation is idempotent, meaning you can retry the operation if it fails. ### 409 - ConcurrentChangeError The `409 - ConcurrentChangeError` response means another cluster operation is already in progress. Wait for the current operation to complete before retrying the purge operation. --- ## Camunda components log levels When working with Camunda 8, you may see various messages in your logs. Not all messages require action. ## Understanding log levels Camunda 8 uses the following log levels: | Log level | Description | | :-------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | TRACE | Information which is helpful only if you want to trace the execution of a particular component. | | DEBUG | Information which can provide helpful context when debugging. You may see a DEBUG message right after an INFO message to provide more context. | | INFO | Information about the system that is useful for the user (in the case of the broker, the user here is the user deploying it). For example, leader changes, a new node added to or removed from the membership, and so on. | | WARN | Expected errors (for example, connection timeouts, the remote node is unavailable, and so on) which might indicate that parts of the system are not working and would require attention if they persist, but could resolve by themselves. These should be monitored, but may not require a support ticket. | | ERROR | Errors that require further investigation. For example, log corruption, inconsistent log, anything which could shut down a partition, and so on. | ## Enable logging Enable logging for each Camunda 8 component as follows: - [Orchestration Cluster](/self-managed/components/orchestration-cluster/core-settings/configuration/logging.md) - [Web Modeler](/self-managed/components/hub/configuration/logging.md) - [Identity](/self-managed/components/management-identity/miscellaneous/configure-logging.md) --- ## Camunda components metrics For distributed system monitoring, Camunda uses the [Micrometer](https://micrometer.io/) library as a facade to export metrics to [supported implementations](https://docs.micrometer.io/micrometer/reference/implementations.html) such as Prometheus, OpenTelemetry, Datadog, and Dynatrace. ## Access metrics You can access your metrics data using your chosen monitoring implementation. Metrics data is only stored in-memory in Camunda, so it needs to be consumed and aggregated by a monitoring system. Monitoring typically uses either a polling (default) or pushing system. ### Polling The system (for example, Prometheus) polls an endpoint exposed by Camunda at a regular interval. - Each request constitutes a data point for each metric. - When working with such systems, configure the polling interval to get information quickly but without overwhelming Camunda itself (which still has to serve this data) or having to store too much data in your monitoring system itself. - Additionally, this means exposing the Camunda endpoint to your external monitoring system. ### Pushing For a pushing system (for example, OpenTelemetry), Camunda is configured to asynchronously push metric updates to an external endpoint at a regular interval. - This implies that the system is accessible to Camunda via the network, so you should ensure communication is secure. - Similarly to the polling approach, balance how fast you are pushing (and getting updates/data points) without overwhelming your external system. ## Configuration Configure your metrics using the built-in [Spring Boot Micrometer configuration](https://docs.spring.io/spring-boot/reference/actuator/metrics.html). ### Defaults Camunda includes built-in support for [Prometheus](https://prometheus.io) and [OpenTelemetry](https://opentelemetry.io/). By default, the configuration only exports Prometheus metrics via [a scraping endpoint](https://docs.spring.io/spring-boot/reference/actuator/metrics.html#actuator.metrics.export.prometheus), with OpenTelemetry disabled. #### Prometheus The scraping endpoint for Prometheus is located under the management context (default `:9600/actuator/prometheus`). Configure this via the following properties: ```yaml management: endpoint.prometheus.access: unrestricted prometheus.metrics.export.enabled: true ``` To collect metrics, you must define the new scraping endpoint for Prometheus. Add the following scraping job: ``` - job_name: camunda scrape_interval: 30s metrics_path: /actuator/prometheus scheme: http static_configs: - targets: - localhost: 9600 ``` :::warning If you've configured your management context to use HTTPS, you must also update the `scheme` for the scraping job above. This also applies if you change the management port. ::: :::note The scraping interval is `30s` by default. This means you will get new data points in Prometheus every 30 seconds. - This is a good default to minimize the storage requirements for Prometheus. - To run alerts or auto-scaling based on the provided metrics, you can configure a shorter interval. As this results in more data being ingested, use at your own risk. ::: #### OpenTelemetry Protocol Zeebe also comes built-in with support to export metrics via OpenTelemetry (using the `micrometer-registry-otlp`). Configure this via the following properties: ```yaml management: # Disable Prometheus promethus.metrics.export.enabled: false # Configure OpenTelemetry Metrics otlp: metrics: export: # Enable OTLP enabled: true # Since metrics are pushed, you will need to configure at least one endpoint url: "https://otlp.example.com:4318/v1/metrics" ``` For a complete list of configuration options for OTLP, refer to the [Micrometer](https://docs.micrometer.io/micrometer/reference/implementations/otlp.html#_configuring) documentation. :::warning When using the OTLP exporter, check the requirements of your target endpoint, as it might require additional configuration. For example, you might need to pass a client secret and ID for authentication via the `otlp.metrics.export.headers` options, or your system might not support `cumulative` aggregation temporality and instead require `delta` (for example, Dynatrace). ::: :::tip A wide variety of existing monitoring systems also support ingesting OpenTelemetry data (for example, Dynatrace, Datadog, and so on). Camunda recommends using these instead of the specific Micrometer implementations. ::: ### Use a different monitoring system To use a different monitoring system, refer to the [Spring Boot](https://docs.spring.io/spring-boot/reference/actuator/metrics.html#actuator.metrics.export) documentation. Zeebe only ships with built-in support for the [Prometheus](https://docs.spring.io/spring-boot/reference/actuator/metrics.html#actuator.metrics.export.prometheus) and [OTLP](https://docs.spring.io/spring-boot/reference/actuator/metrics.html#actuator.metrics.export.otlp) systems. To use a different system, you must add the required dependencies to your Zeebe installation, specifically to the distribution's `lib/` folder. :::note When using the container image, you must add it to the following paths, based on your image: - `camunda/zeebe`: `/usr/local/zeebe/lib` - `camunda/camunda`: `/usr/local/camunda/lib` ::: For example, to export to Datadog, download the `io.micrometer:micrometer-registry-datadog` JAR and place it in the `./lib` folder of the distribution. Running from the root of the distribution, you can use Maven to do this for you: ```shell mvn dependency:copy -Dartifact=io.micrometer:micrometer-registry-datadog:1.14.4 -Dtransitive=false -DoutputDirectory=./lib ``` :::note The version must be the same as the Micrometer version used by Camunda. - Find this information by checking the distribution artifact on [Maven Central](https://central.sonatype.com/artifact/io.camunda/camunda-zeebe/dependencies). - Select the distribution version you are using, and filter for `micrometer` to get the expected Micrometer version. ::: ### Customize metrics You can modify and filter the metrics exposed in Camunda via configuration. #### Common tags [Tags provide a convenient way of aggregating metrics over common attributes](https://docs.spring.io/spring-boot/reference/actuator/metrics.html#actuator.metrics.customizing.common-tags). Via configuration, you can ensure that all metrics for a specific instance of Camunda share common tags. For example, if you deployed two different clusters and wanted to differentiate them: The first cluster could be configured as: ```yaml management: metrics: tags: cluster: "foo" ``` And the second cluster configured as: ```yaml management: metrics: tags: cluster: "bar" ``` #### Filtering [You can additionally disable certain metrics](https://docs.spring.io/spring-boot/reference/actuator/metrics.html#actuator.metrics.customizing.per-meter-properties). This can be useful for high cardinality metrics which you do not care for, but which may end up being expensive to store in your target system. To filter a metric called `zeebe.foo`, you would configure the following property: ```yaml management: metrics: enable: zeebe: foo: false ``` :::note Filtering applies not only to direct name matches (for example, `zeebe.foo`), but as a prefix. This means any metric starting with the prefix `zeebe.foo` in the example would also be filtered out, and would not be exported. ::: ## Available metrics [Spring already exposes various metrics](https://docs.spring.io/spring-boot/reference/actuator/metrics.html#actuator.metrics.supported), some of which will be made available through Camunda: - [JVM metrics](https://docs.spring.io/spring-boot/reference/actuator/metrics.html#actuator.metrics.supported.jvm) - [System metrics](https://docs.spring.io/spring-boot/reference/actuator/metrics.html#actuator.metrics.supported.system) - [Application startup metrics](https://docs.spring.io/spring-boot/reference/actuator/metrics.html#actuator.metrics.supported.application-startup) - [Logger metrics](https://docs.spring.io/spring-boot/reference/actuator/metrics.html#actuator.metrics.supported.logger) - [Spring MVC metrics](https://docs.spring.io/spring-boot/reference/actuator/metrics.html#actuator.metrics.supported.spring-mvc) Camunda also exposes several custom metrics, most of them under the `zeebe`, `atomix`, `operate`, `tasklist`, or `optimize` prefixes. :::note While all nodes in a Camunda cluster expose metrics, they will expose relevant metrics based on their role. For example, brokers will expose processing related metrics, while gateways will expose REST API relevant metrics. ::: :::note **Not all metrics are available at all times.** This can apply to various metrics, but is especially noticeable for **processing-related metrics**, which are recorded on events that can occur infrequently. For example, the `zeebe_incident_events_total` metric is only recorded when an incident is **created** or **resolved**. ::: ### Process processing metrics The following metrics are related to process processing: | Metric | Description | | :------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `zeebe_stream_processor_records_total` | The number of events processed by the stream processor. The `action` label separates processed, skipped, and written events. | | `zeebe_exporter_events_total` | The number of events processed by the exporter processor. The `action` label separates exported and skipped events. | | `zeebe_element_instance_events_total` | The number of occurred process element instance events. The `action` label separates the number of activated, completed, and terminated elements. The `type` label separates different BPMN element types. | | `zeebe_job_events_total` | The number of job events. The `action` label separates the number of created, activated, timed out, completed, failed, and canceled jobs. | | `zeebe_incident_events_total` | The number of incident events. The `action` label separates the number of created and resolved incident events. | | `zeebe_pending_incidents_total` | The number of currently pending incidents, that is, not resolved. | ### Performance metrics The following metrics are related to performance. For example, Zeebe has a backpressure mechanism to reject requests when it receives more requests than it can handle without incurring high processing latency. Monitor backpressure and processing latency of the commands using the following metrics: | Metric | Description | | :-------------------------------------- | :---------------------------------------------------------------------- | | `zeebe_dropped_request_count_total` | The number of user requests rejected by the broker due to backpressure. | | `zeebe_backpressure_requests_limit` | The limit for the number of inflight requests used for backpressure. | | `zeebe_stream_processor_latency_bucket` | The processing latency for commands and event. | ### Health metrics The health of partitions in a broker can be monitored using the metric `zeebe_health`. ## Execution latency metrics Brokers can export optional execution latency metrics. To enable export of execution metrics, set the `CAMUNDA_MONITORING_METRICS_ENABLEEXPORTEREXECUTIONMETRICS` environment variable to `true` in your Zeebe [configuration file](/self-managed/components/orchestration-cluster/zeebe/configuration/configuration.md). ## Optimize error metrics Optimize exposes a counter metric for tracking errors by type. | Metric name | Type | Description | Labels | | ---------------------- | ------- | -------------------------------------------------------- | ------------------------ | | `optimize_error_total` | Counter | Number of errors occurring in Optimize, grouped by type. | `ERROR_TYPE` (see below) | The `ERROR_TYPE` label can have the following values: | Value | Description | | ------------------------ | --------------------------------------------------------------------------- | | `too_many_buckets` | Aggregation bucket limit exceeded during report evaluation. | | `version_conflict` | Document version conflict on write, for example, during imports or updates. | | `index_not_found` | Required index is missing, for example, in metadata queries or scrollers. | | `search_context_missing` | Search context expired during pagination, for example, JSON export. | | `nested_limit_exceeded` | Nested document limit exceeded in complex queries. | | `elasticsearch_error` | Generic Elasticsearch error that does not match a more specific type. | | `opensearch_error` | Generic OpenSearch error that does not match a more specific type. | Each time series uses the same metric name and differs only by the `ERROR_TYPE` label value. For example: ``` optimize_error_total{ERROR_TYPE="too_many_buckets"} 5 optimize_error_total{ERROR_TYPE="version_conflict"} 12 ``` ## Optimize report latency metrics Optimize exposes a timer metric for tracking how long report evaluations take. | Metric name | Type | Description | Labels | | --------------------------------- | ----- | ------------------------------ | -------------------------- | | `optimize_report_reportLatency_*` | Timer | Duration of report evaluation. | `REPORT_NAME`, `REPORT_ID` | The `REPORT_NAME` and `REPORT_ID` labels identify the evaluated report or dashboard. Report latency metrics are controlled by the `optimize.metrics.report-latency.enabled=true` configuration property. Set the property to `false` to disable report latency metrics. ## Grafana ### Zeebe Zeebe comes with a pre-built dashboard, available in the repository: [monitor/grafana/zeebe.json](https://github.com/camunda/camunda/blob/main/monitor/grafana/zeebe.json). - [Import](https://grafana.com/docs/grafana/latest/reference/export_import/#importing-a-dashboard) the dashboard into your Grafana instance and select the correct Prometheus data source (if you have more than one). - The dashboard displays a healthy cluster topology, general throughput metrics, handled requests, exported events per second, disk and memory usage, and more. The following image shows an example of the Zeebe Grafana dashboard after import. ![Example Zeebe Grafana dashboard](assets/grafana-preview.png) ### Data layer A pre-built Grafana dashboard is available for the data layer in the repository: [monitor/grafana/data_layer.json](https://github.com/camunda/camunda/blob/main/monitor/grafana/dashboards/data_layer.json) To use it: 1. [Import](https://grafana.com/docs/grafana/latest/reference/export_import/#importing-a-dashboard) the dashboard into your Grafana instance. 2. When prompted, select the appropriate Prometheus data source (especially if multiple are configured). The dashboard provides insights into key data layer components for Camunda versions `>= 8.8`, with a focus on the Camunda exporter through which all data flows. ![Example panels](assets/example-panels-data-layer.png) ## Configure metrics Configure metrics for each Camunda 8 component as follows: - [Orchestration Cluster](/self-managed/components/orchestration-cluster/core-settings/concepts/monitoring.md) - [Web Modeler](/self-managed/components/hub/monitoring.md) --- ## Camunda components troubleshooting ## Helm chart security warning Due to [recent changes](https://github.com/bitnami/charts/issues/30850) in Bitnami's Helm charts (a third-party dependency), you may see a security warning when installing the Camunda Helm chart. This warning appears when a Bitnami subchart detects that an image has been replaced or modified. ### Why the warning appears Camunda repackages the standard Bitnami Keycloak distribution with [Camunda-specific Keycloak](https://github.com/camunda/keycloak) for Identity integration. This customization adds Camunda identity themes. The Bitnami Helm chart detects this image replacement and emits a security warning as a precautionary measure. ### Not a security vulnerability The security warning does not indicate a security vulnerability. This warning can appear in two scenarios: - **Camunda-built images** (such as Keycloak): These are built on official Bitnami images with only Camunda-specific additions (Identity theme, AWS wrapper). They undergo the same security review process as other Camunda components. - **Standard Bitnami images** (such as PostgreSQL or Elasticsearch): These images are secure but may show CVE warnings because of the comprehensive OS layer. In both cases, the security warning is a precautionary measure from Bitnami's detection system and does not indicate a genuine security risk. For detailed information about CVE management and why Bitnami images show security warnings, see [Understanding CVEs in Bitnami images](/self-managed/deployment/helm/configure/registry-and-images/install-bitnami-enterprise-images.md#understanding-cves-in-bitnami-images). ### Suppress the warning To accommodate this image replacement, the Camunda Helm chart enables `allowInsecureImages` by default for Keycloak: ```yaml identityKeycloak: global: security: allowInsecureImages: true ``` If you're using your own Docker registry to host application images, you should also enable this option for any Bitnami-based third-party dependencies, such as PostgreSQL or Elasticsearch sub-charts: ```yaml identityKeycloak: postgresql: global: security: allowInsecureImages: true [...] elasticsearch: global: security: allowInsecureImages: true ``` ## Keycloak requires SSL for requests from external sources When deploying Camunda to a provider, it is important to confirm the IP ranges used for container to container communication align with the IP ranges Keycloak considers "local". By default, Keycloak considers all IPs outside those listed in their [external requests documentation](https://www.keycloak.org/docs/latest/server_admin/#_ssl_modes) to be external and therefore require SSL. As the [Camunda Helm Charts](https://artifacthub.io/packages/helm/camunda/camunda-platform) currently do not provide support for the distribution of the Keycloak TLS key to the other containers, we recommend viewing the solution available in the [Identity documentation](/self-managed/components/management-identity/miscellaneous/troubleshoot-identity.md#solution-2-identity-making-requests-from-an-external-ip-address). ## Identity redirect URL If HTTP to HTTPS redirection is enabled in the load-balancer or Ingress, make sure to use the HTTPS protocol in the values file under `global.identity.auth.[COMPONENT].redirectUrl`. Otherwise, you will get a redirection error in Keycloak. For example: ``` global: identity: auth: operate redirectUrl: https://operate.example.com ``` ## Zeebe Backup with S3 In general, some S3 compatible implementations are not able to properly handle the checksum feature of the S3 client being introduced with version 2.30.0. For more details, you can refer to [the AWS documentation](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/s3-checksums.html). As soon as issues appear related to the checksum, it can be disabled by setting these environment variables on your Zeebe brokers: ``` AWS_REQUEST_CHECKSUM_CALCULATION=WHEN_REQUIRED AWS_RESPONSE_CHECKSUM_CALCULATION=WHEN_REQUIRED ``` This will disable automated creation of checksums. If you are still encountering issues with MD5 checksums required by your provider, enable legacy support for the AWS S3 client by setting: ``` ZEEBE_BROKER_DATA_BACKUP_S3_SUPPORTLEGACYMD5=true ``` **Backups to IBM COS fail with 403 Access Denied** When using an S3 backup store with IBM Cloud Object Storage, you may encounter `403 Access Denied` errors even though the access credentials are valid. This may be caused by a [recent change in the AWS S3 client](https://docs.aws.amazon.com/sdkref/latest/guide/feature-dataintegrity.html), which now calculates checksums for data integrity by default. IBM COS does not appear to support this feature. To resolve this issue, you can restore the previous behavior by setting the following environment variable on your Zeebe brokers: ``` AWS_REQUEST_CHECKSUM_CALCULATION=WHEN_REQUIRED ``` This will prevent the S3 client from calculating the additional checksums and should resolve the issue. **Backups to Dell EMC ECS fail with 400 Bad Request** When using an S3 backup store with Dell EMC ECS, you may encounter the following error: `The Content-SHA256 you specified did not match what we received (Service: S3, Status Code: 400)` This issue is caused by a recent change in the AWS S3 client, which now signs streaming chunked uploads differently. Dell EMC ECS does not support chunked encoding. To resolve this issue, set the following environment variable on your Zeebe brokers: ``` AWS_REQUEST_CHECKSUM_CALCULATION=WHEN_REQUIRED ``` This disables the additional checksum calculation in the S3 client and should resolve the issue. ## Zeebe backup with Azure Blob Storage When using an Azure backup store, requests to the backup API may time out due to [a bug in the Azure SDK](https://github.com/Azure/azure-sdk-for-java/issues/46231). This issue is caused by a deadlock in the Azure SDK when virtual threads are used. It is more likely to occur on systems with many partitions per broker and limited CPU resources. To mitigate this, set the following environment variable on your Zeebe brokers to disable virtual threads in the Azure SDK: ``` AZURE_SDK_SHARED_THREADPOOL_USEVIRTUALTHREADS=false ``` ## Enable Azure logging for troubleshooting When using Azure Blob Storage as a backup store, you can enable logging to troubleshoot issues with the Azure SDK. To do this, go through the following steps: 1. Add logging for Azure SDK, and set it to debug through the Zeebe Broker loggers endpoint: `curl 'http://localhost:9600/actuator/loggers/com.azure' -i -X POST -H 'Content-Type: application/json' -d '{"configuredLevel":"debug"}'` 2. Add the following environment variable to the Zeebe Broker StatefulSet. `AZURE_HTTP_LOG_DETAIL_LEVEL=BASIC` ## Zeebe Ingress (gRPC) Zeebe requires an Ingress controller that supports `gRPC` which is built on top of `HTTP/2` transport layer. Therefore, to expose Zeebe Gateway externally, you need the following: 1. An Ingress controller that supports `gRPC` ([ingress-nginx controller](https://github.com/kubernetes/ingress-nginx) supports it out of the box). 2. TLS (HTTPS) via [Application-Layer Protocol Negotiation (ALPN)](https://www.rfc-editor.org/rfc/rfc7301.html) enabled in the Zeebe Gateway Ingress object. However, according to the official Kubernetes documentation about [Ingress TLS](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls): > There is a gap between TLS features supported by various Ingress controllers. Please refer to documentation on nginx, GCE, or any other platform specific Ingress controller to understand how TLS works in your environment. Therefore, if you are not using the [ingress-nginx controller](https://github.com/kubernetes/ingress-nginx), ensure you pay attention to TLS configuration of the Ingress controller of your choice. Find more details about the Zeebe Ingress setup in the [Kubernetes platforms supported by Camunda](/self-managed/deployment/helm/install/quick-install.md). ## Identity `contextPath` Camunda 8 Self-Managed can be accessed externally via the [combined Ingress setup](/self-managed/deployment/helm/configure/ingress/ingress-setup.md#combined-ingress-setup). In that configuration, Camunda Identity is accessed using a specific path, configured by setting the `contextPath` variable, for example `https://camunda.example.com/identity`. For security reasons, Camunda Identity requires secure access (HTTPS) when a `contextPath` is configured. :::note Due to limitations, the Identity `contextPath` approach is unavailable when using a browser in Incognito mode. ::: ## Web Modeler database schema The Web Modeler `restapi` component requires a [database connection](/self-managed/components/hub/configuration/modeler-configuration.md#database). This connection should not point to the same database as Keycloak does. ## Gateway timeout on redirect A gateway timeout can occur if the headers of a response are too big (for example, if a JWT is returned as `Set-Cookie` header). To avoid this, you can increase the `proxy-buffer-size` of your Ingress controller or Ingress. The setting for **ingress-nginx** can be found [here](https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/nginx-configuration/annotations.md#proxy-buffer-size). ## Helm CLI version and installation failures If you encounter errors during Helm chart installation, such as type mismatches or other template rendering issues, you may be using an unsupported version of the Helm CLI. - For Camunda 8.9 and earlier, use Helm CLI v3.13 or higher. - For Camunda 8.10+ (chart 15.x+), Helm CLI v4.x is required. For chart-to-CLI compatibility across versions, see [Helm 4](/self-managed/deployment/helm/operational-tasks/helm-v4.md). ## DNS disruption issue for Zeebe in Kubernetes clusters (1.29-1.31) Kubernetes clusters running versions 1.29 to 1.31 may experience DNS disruptions during complete node restarts, such as during upgrades or evictions, particularly if the cluster's DNS resolver pods are affected. This issue is specifically noticeable for Zeebe (Netty), as it will no longer be able to form a cluster because of improper DNS responses. This occurs because Zeebe continues to communicate with a non-existent DNS resolver, caused by improper cleanup of conntrack entries for UDP connections. Details on this issue can be found in [this Kubernetes issue](https://github.com/kubernetes/kubernetes/issues/125467) and has been resolved in the following patch releases: - Kubernetes 1.29.10 - Kubernetes 1.30.6 - Kubernetes 1.31.2 Kubernetes versions 1.32 and versions before 1.29 are not affected. If an immediate cluster upgrade to a fixed version is not possible, the following temporary workarounds can be applied if you encounter DNS issues: - Restart the `kube-proxy` pod(s) - Delete the affected Zeebe pod ## Anomaly detection scripts The [c8-sm-checks](https://github.com/camunda/c8-sm-checks) project introduces a set of scripts to aid detection of Camunda deployment anomalies. These scripts perform health checks on various aspects of the Kubernetes installation and Zeebe components, providing insights into potential issues that may affect the performance or stability. ### Usage Each script in the `c8-sm-checks` project can be executed independently, allowing you to target specific areas for troubleshooting and verification. To utilize these scripts effectively, ensure you have the necessary permissions and access to your Kubernetes cluster. Additionally, make sure you have the required dependencies installed on your system, such as `kubectl`, `helm`, `curl`, and `grpcurl`. For detailed documentation and usage instructions for each script, refer to the [c8-sm-checks GitHub repository](https://github.com/camunda/c8-sm-checks). Additionally, you can use the `-h` option with each script to display help information directly from the command line. Before using it, clone the `c8-sm-checks` repository to your local environment by running the following command: ```bash git clone https://github.com/camunda/c8-sm-checks.git cd c8-sm-checks git checkout v1.4.0 ``` ### Kubernetes connectivity scripts These scripts enable you to verify the connectivity and configuration of your Kubernetes cluster, including checks for deployment status, service availability, and Ingress configuration. #### Kubernetes permissions When utilizing the anomaly detection scripts within a Kubernetes environment, ensure the user has specific permissions: - **List pods**: Required for `kubectl get pods` to fetch pod details in the namespace. - **Execute commands in pods**: Necessary for running commands inside pods via `kubectl exec`. - **List services**: Needed for `kubectl get services` to retrieve service information. - **List ingresses**: Required by `kubectl get ingress` to obtain Ingress objects. - **Get Ingress details**: Necessary for `kubectl get ingress` to fetch Ingress configurations. #### Deployment check (`./checks/kube/deployment.sh`) This script checks the status of a Helm deployment in the specified namespace, ensuring that all required containers are present and ready. You can customize the list of containers to check based on your deployment topology. ```bash ./checks/kube/deployment.sh -n camunda-primary -d camunda -c "zeebe,zeebe-gateway,web-modeler" ``` #### Connectivity check (`./checks/kube/connectivity.sh`) This script verifies Kubernetes connectivity and associated configuration, checking for the presence of services and ingresses that conform to the required specifications. ```bash ./checks/kube/connectivity.sh -n camunda-primary ``` ### Zeebe connectivity scripts These scripts focus on verifying the connectivity and health of Zeebe components within your deployment. You can check token generation, gRPC connectivity, and other essential aspects of your Zeebe setup. #### gRPC Zeebe check (`./checks/zeebe/connectivity.sh`) This script verifies connectivity to a Zeebe instance using HTTP/2 and gRPC protocols, providing insights into the health and status of your Zeebe deployment. ```bash ./checks/zeebe/connectivity.sh -a https://local.distro.example.com/auth/realms/camunda-platform/protocol/openid-connect/token -i myclientid -s 0Rn28VrQxGNxowrCWe6wbujwFghO4990 -u orchestration-api -H zeebe.local.distro.example.com:443 ``` Find more information on [how to register your application on Identity](https://github.com/camunda-community-hub/camunda-8-examples/blob/main/payment-example-project/kube/README.md#4-generating-an-m2m-token-for-our-application). ### IRSA configuration check The AWS EKS IRSA configuration scripts are focused on verifying the correct setup of IAM Roles for Service Accounts (IRSA) within your Kubernetes deployment on AWS. These scripts ensure that your Kubernetes service accounts are correctly associated with IAM roles, allowing components like PostgreSQL, OpenSearch, and others in your deployment to securely interact with AWS resources. For detailed usage instructions and setup information, please refer to the [IRSA guide](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/irsa.md#irsa-check-script). ### Interpretation of the results Each script produces an output indicating the status of individual checks, which can be either `[OK]`, which signals a healthy status, or `[FAIL]`, which signals an unhealthy status. While the scripts continue execution even if a check fails, it may be necessary to review the logs to identify the failed element. At the end of each script, a global check status is provided, indicating whether any tests failed and the corresponding error code. For example: ``` [FAIL] ./checks/zeebe/connectivity.sh: At least one of the tests failed (error code: 5). ``` ### Handling errors If a check fails, it indicates a deviation from the expected configuration on a normal setup. Resolving the error involves studying the failed check and applying the best practices outlined in the documentation (use the search feature to find the associated recommendation for a failed check). For example: ``` [FAIL] None of the ingresses contain the annotation nginx.ingress.kubernetes.io/backend-protocol: GRPC, which is required for Zeebe ingress. ``` The error message suggests adjusting the Ingress configuration to include the required annotation. One can also explore the source of the script to have a better understanding of the reason for the failure. :::note Sometimes, some checks may not be applicable to your setup if it's custom (for example, with the previous example the Ingress you use may not be [ingress-nginx](https://kubernetes.github.io/ingress-nginx/)). ::: ## Basic authentication Performance Throughput when using Basic authentication is very limited, supporting only a few API requests per second. Workloads greater than that which can be supported by Basic authentication may cause request processing to stall, as queued requests can time out before they are processed. Development and testing scenarios that are performance-sensitive may [disable authentication entirely](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md#no-authentication-local-development), or use [OIDC Authentication](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md#oidc-access-token-authentication-using-client-credentials). ## Find available container image versions When working with custom registries or air-gapped environments, you may need to verify which image versions are available before deployment. Use [skopeo](https://github.com/containers/skopeo) to list available tags: ```shell # For open source images (no authentication required) skopeo --override-os linux inspect docker://registry.camunda.cloud/camunda/zeebe | jq '.RepoTags' # For enterprise images (requires authentication) skopeo login registry.camunda.cloud --username --password skopeo --override-os linux inspect docker://registry.camunda.cloud/vendor-ee/elasticsearch | jq '.RepoTags' ``` ## Incorrect authorizations when deploying resources from Modeler If you encounter missing or invalid authorizations when deploying resources or starting process instances from Web Modeler or Desktop Modeler, review which credentials are being used: - **Web Modeler** deploys as your logged-in user, so ensure that your [user](/components/admin/user.md) has the required permissions. - **Desktop Modeler** uses the client credentials you provide, so ensure that your [client](/components/admin/client.md) has the required permissions. ## Zeebe data loss after PVC deletion If all Zeebe data is lost after a PersistentVolumeClaim (PVC) was deleted, the likely cause is that your StorageClass uses the `Delete` reclaim policy instead of `Retain`. ### Symptoms - All process definitions and instances are gone - Zeebe brokers start fresh with no historical data - Secondary storage still has data, but Zeebe does not ### Root cause - Your StorageClass has `reclaimPolicy: Delete` (the default in most Kubernetes distributions) - A PVC was deleted (manually, by automation, or during cluster maintenance) - Kubernetes automatically deleted the underlying PersistentVolume and all its data ### How to check your current configuration ```bash # Check your StorageClass reclaim policy kubectl get storageclass # Look for the RECLAIMPOLICY column - it should show "Retain" for production workloads NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE gp3 ebs.csi.aws.com Delete WaitForFirstConsumer # ❌ Problem ebs-sc ebs.csi.aws.com Retain WaitForFirstConsumer # ✅ Correct ``` ### Solution 1. **Create a new StorageClass with `Retain` policy** before deploying Camunda: ```yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: camunda-storage provisioner: # e.g., ebs.csi.aws.com, disk.csi.azure.com reclaimPolicy: Retain volumeBindingMode: WaitForFirstConsumer ``` 2. **For existing PersistentVolumes**, you can patch them to use `Retain`: ```bash kubectl patch pv -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}' ``` ### Why this matters For stateful applications like Zeebe that store critical business data, the `Retain` policy is the industry-standard best practice. This ensures that even if a PVC is accidentally deleted, the underlying data remains intact and can be recovered. For more information, see the official Kubernetes documentation on [changing the reclaim policy of a PersistentVolume](https://kubernetes.io/docs/tasks/administer-cluster/change-pv-reclaim-policy/#why-change-reclaim-policy-of-a-persistentvolume). --- ## Administrator quickstart With this guide, you'll learn how to deploy Camunda 8 Self-Managed as an administrator. Select the deployment approach that best fits your needs: --- ## Configure Camunda 8 Run Use this page to configure Camunda 8 Run beyond the default local quickstart. ## Configuration options The following options provide a convenient way to override settings for quick tests and interactions in Camunda 8 Run. For more advanced or permanent configuration, modify the default `configuration/application.yaml` or supply a custom file using the `--config` flag. | Argument | Description | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `--config ` | Applies the specified Zeebe [`application.yaml`](/self-managed/components/orchestration-cluster/zeebe/configuration/configuration.md). | | `--extra-driver ` | Copies an external JDBC driver into `camunda-zeebe-/lib` before startup. Use this when running against Oracle, MySQL, or other databases that require a driver that is not bundled with Camunda 8 Run. Repeat the flag to copy multiple JARs. | | `--username ` | Configures the first user’s username as ``. | | `--password ` | Configures the first user’s password as ``. | | `--keystore ` | Configures the TLS certificate for HTTPS. If not specified, HTTP is used. For more information, see [enable TLS](#enable-tls). | | `--keystorePassword ` | Provides the password for the JKS keystore file. | | `--port ` | Sets the Camunda core port (default: `8080`). | | `--log-level ` | Sets the log level for the Camunda core. | | `--startup-url` | The URL to open after startup (for example, `http://localhost:8080/operate`). By default, Operate is opened. | ## Enable authentication and authorization By default, Camunda 8 Run is optimized for local development. The web applications use local credentials, but the Orchestration Cluster API is unprotected and authorization checks are disabled. To protect API requests and enable authorization checks, update your `application.yaml`. Example configuration: ```yaml camunda: security: initialization: users: - username: demo password: demo name: Demo email: demo@example.com authentication: method: BASIC unprotected-api: false authorizations: enabled: true ``` Start Camunda 8 Run with the configuration: ```bash ./start.sh --config application.yaml ``` ```bash .\c8run.exe start --config application.yaml ``` Once enabled, API requests must include valid credentials. For example: ```shell curl --request GET 'http://localhost:8080/v2/topology' \ -u demo:demo \ --header 'Content-Type: application/json' \ --data-raw '{}' ``` To add additional users, extend the configuration: ```yaml camunda: security: initialization: users: - username: user password: user name: user email: user@example.com defaultRoles: admin: users: - user ``` ## Use Camunda APIs Camunda 8 Run exposes the Orchestration Cluster REST API locally by default at `http://localhost:8080/v2`. - For local development, Camunda 8 Run exposes the API without requiring credentials unless you enable API protection. - If you enable Basic authentication, include the configured username and password in your requests. - For API concepts, endpoints, and examples, use the [Orchestration Cluster REST API overview](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md). - For deployment-specific authentication details, use [Orchestration Cluster REST API authentication](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md). Quick connectivity check: ```bash curl http://localhost:8080/v2/topology ``` ## Use built-in and custom connectors Camunda 8 Run includes Connectors for local development. For custom connectors: 1. Place the connector JAR in the appropriate `custom_connectors` directory: ```bash # macOS/Linux c8run/custom_connectors/your-connector.jar # Windows c8run\custom_connectors\your-connector.jar ``` 2. Ensure the corresponding element template is available in a valid Desktop Modeler search path. 3. Restart Camunda 8 Run after adding or updating connectors. 4. Check `c8run/logs/connectors.log` if the connector fails to load. For connector secrets: - In non-Docker mode, export secrets as environment variables. - In the Docker Compose setup, add secrets to the `connector-secrets.txt` file in the Docker Compose folder. For connector development and packaging details, see [Connector SDK](/components/connectors/custom-built-connectors/connector-sdk.md). ## Enable TLS TLS can be enabled by providing a local keystore file using the [`--keystore` and `--keystorePassword` configuration options](#configuration-options) at startup. Camunda 8 Run accepts `.jks` certificate files. Although Camunda 8 Run supports TLS, this is intended only for testing. :::note If you use a proxy together with TLS, ensure internal Camunda services are excluded from proxy routing. JVM-level proxy settings apply to all internal HTTP clients and may block communication between components such as Zeebe, Operate, Admin, or the connector runtime. Add these services to your `nonProxyHosts` configuration. For details, see [HTTP proxy configuration](/self-managed/components/connectors/http-proxy-configuration.md). ::: ## Access metrics Metrics are enabled in Camunda 8 Run by default and can be accessed at [http://localhost:9600/actuator/prometheus](http://localhost:9600/actuator/prometheus). For more information, see the [metrics](/self-managed/operational-guides/monitoring/metrics.md) documentation. ## Environment variables The following advanced configuration options can be provided via environment variables: | Variable | Description | | ----------- | ---------------------------------------------------------------- | | `JAVA_OPTS` | Allows you to override Java command line parameters for Camunda. | ## Next steps - Review [configure secondary storage in Camunda 8 Run](./secondary-storage.md). - Review [install and start Camunda 8 Run](./install-start.md). - Identify and resolve [common issues when starting, configuring, or using Camunda 8 Run](../c8run-troubleshooting.md). --- ## Install and start Camunda 8 Run Use this page to install Camunda 8 Run locally, start it on macOS, Linux, or Windows, and shut it down cleanly. ## Prerequisites - **OpenJDK 21–25**: Required for running Camunda 8 as a Java application. - **[Desktop Modeler](/components/modeler/desktop-modeler/install-the-modeler.md)** - **If using Ubuntu**: Ubuntu 22.04 or newer :::note After installing OpenJDK, ensure `JAVA_HOME` is set by running `java -version` in a **new** terminal. If no version of Java is found, follow your chosen installation's instructions for setting `JAVA_HOME` before continuing. ::: ## Install and start Camunda 8 Run 1. Download the latest release of for your operating system and architecture. Opening the `.tgz` file extracts the Camunda 8 Run script into a new directory. 2. Navigate to the new `c8run` directory. 3. Start Camunda 8 Run by following the steps below, depending on your operating system. Run the helper script: ```bash ./start.sh ``` Or use the CLI command: ```bash ./c8run start ``` Use the CLI command: ```bash .\c8run.exe start ``` If startup is successful, a browser window for Operate will open automatically. Alternatively, you can access Operate at [http://localhost:8080/operate](http://localhost:8080/operate). :::note If Camunda 8 Run fails to start, run the [shutdown script](#shut-down-camunda-8-run) to end the current processes, then run the start script again. ::: For container-based local deployments, see the [developer quickstart with Docker Compose](../docker-compose.md). For CLI flags and permanent configuration, see [configure Camunda 8 Run](./configuration.md). ## Shut down Camunda 8 Run To shut down Camunda 8 Run and end all running processes, run the following command from the `c8run` directory: ```bash ./shutdown.sh ``` ```bash .\c8run.exe stop ``` For Docker Compose environments, use the stop commands in the [developer quickstart with Docker Compose](../docker-compose.md). ## Next steps - Review [configure Camunda 8 Run](./configuration.md). - Review [configure secondary storage in Camunda 8 Run](./secondary-storage.md). - Identify and resolve [common issues when starting, configuring, or using Camunda 8 Run](../c8run-troubleshooting.md). --- ## Configure secondary storage in Camunda 8 Run Camunda 8 Run supports multiple [secondary storage](/reference/glossary.md#secondary-storage) options. **H2 is the default secondary storage** for lightweight Camunda 8 Run setups and quickstarts. If you need full-text indexing, search, or advanced analytics, use an external Elasticsearch instance. ## Configure or switch secondary storage (H2 or Elasticsearch) Choose the backend that fits your local development and testing needs. ### Default: H2 (Camunda 8 Run) The default Camunda 8 Run configuration uses an H2 database for secondary storage. This is convenient for local development and demos and stores data on disk. File-based H2 example: ```yaml data: secondary-storage: type: rdbms rdbms: url: jdbc:h2:file:./camunda-data/h2db username: sa password: ```
Full example configuration ```yaml camunda: data: secondary-storage: type: rdbms rdbms: url: jdbc:h2:file:./camunda-data/h2db username: sa password: flushInterval: PT0.5S queueSize: 1000 security: initialization: users: - username: demo password: demo name: Demo email: demo@example.com authentication: method: BASIC unprotected-api: true authorizations: enabled: false zeebe: broker: network: host: localhost advertisedHost: localhost gateway: cluster: initialContactPoints: zeebe:26502 memberId: identity spring: profiles: active: "broker,consolidated-auth,identity,tasklist" ```
### External relational database options Use an external relational database for secondary storage: PostgreSQL, MariaDB, MySQL, Oracle, or Microsoft SQL Server. 1. Set `camunda.data.secondary-storage` with the JDBC URL and credentials. 2. Ensure the database is reachable before starting Camunda 8 Run. 3. If you already run a database, reuse the same configuration and update the URL, host or port, and credentials. 4. If a separate JDBC driver is required, add it with `--extra-driver` or drop the JAR into `camunda-zeebe-/lib`. **Secondary storage config** ```yaml camunda: data: secondary-storage: type: rdbms rdbms: url: jdbc:postgresql://localhost:5432/camunda_secondary username: camunda password: camunda ``` **Start database (Docker)** ```bash docker run -d --name camunda-postgres \ -e POSTGRES_USER=camunda \ -e POSTGRES_PASSWORD=camunda \ -e POSTGRES_DB=camunda_secondary \ -p 5432:5432 postgres:latest ``` **Secondary storage config** ```yaml camunda: data: secondary-storage: type: rdbms rdbms: url: jdbc:mariadb://localhost:3306/camunda_secondary?serverTimezone=UTC username: camunda password: camunda ``` **Start database (Docker)** ```bash docker run -d --name camunda-mariadb \ -e MARIADB_USER=camunda \ -e MARIADB_PASSWORD=camunda \ -e MARIADB_ROOT_PASSWORD=rootcamunda \ -e MARIADB_DATABASE=camunda_secondary \ -p 3306:3306 mariadb:11.4 ``` :::note No extra driver is required. MariaDB ships with the distribution. ::: **Secondary storage config** ```yaml camunda: data: secondary-storage: type: rdbms rdbms: url: jdbc:mysql://localhost:3307/camunda_secondary?serverTimezone=UTC username: camunda password: camunda ``` **Start database (Docker)** ```bash docker run -d --name camunda-mysql \ -e MYSQL_ROOT_PASSWORD=rootcamunda \ -e MYSQL_USER=camunda \ -e MYSQL_PASSWORD=camunda \ -e MYSQL_DATABASE=camunda_secondary \ -p 3306:3306 mysql:8.4 ``` :::note MySQL requires the official Connector/J driver. Copy the JAR into `camunda-zeebe-/lib` or pass `--extra-driver /path/to/mysql-connector.jar` to `./c8run start`. Ensure the JDBC URL uses the host port you expose. ::: **Secondary storage config** ```yaml camunda: data: secondary-storage: type: rdbms rdbms: url: jdbc:oracle:thin:@//localhost:1521/FREEPDB1 username: camunda password: camunda ``` **Start database (Docker)** ```bash docker run -d --name camunda-oracle \ -p 1521:1521 \ -e ORACLE_PASSWORD=camunda \ -e APP_USER=camunda \ -e APP_USER_PASSWORD=camunda \ gvenzl/oracle-free:23-slim ``` :::note Download the Oracle JDBC driver, for example `ojdbc11.jar`, and place it in `camunda-zeebe-/lib` or pass `--extra-driver /path/to/ojdbc11.jar`. ::: **Secondary storage config** ```yaml camunda: data: secondary-storage: type: rdbms rdbms: url: jdbc:sqlserver://localhost:1433;databaseName=camunda_secondary;encrypt=false username: camunda password: Camunda123! ``` **Start database (Docker)** ```bash docker run -d --name camunda-mssql \ -e ACCEPT_EULA=Y \ -e MSSQL_SA_PASSWORD=Camunda123! \ -p 1433:1433 \ mcr.microsoft.com/mssql/server:2022-latest ``` ### Optional: Elasticsearch If you need indexing, search, or full Operate or Tasklist functionality, use an external Elasticsearch instance. To use Elasticsearch: ```yaml data: secondary-storage: type: elasticsearch elasticsearch: url: http://localhost:9200/ ``` Start Camunda 8 Run with `--config ` and point the configuration to your external cluster. ## Switching between storage types and migration notes - Switching the secondary storage type, for example H2 to Elasticsearch, currently does **not** preserve existing secondary-store data. The system starts with a fresh secondary store. - If you upgrade from alpha1 or alpha2 and keep the same secondary storage backend, no migration steps are required. - To switch storage, update `data.secondary-storage` in `application.yaml` and restart Camunda 8 Run. Choose **H2** for quick local development and **Elasticsearch** for production-like scenarios where advanced search and analytics are required. ## Operate limitations in 8.9 Operate can run against the default H2 store, but some user-facing Operate features are intentionally limited: - Operate may not provide complete analytics, advanced search, or long-running query features when backed by H2. - Performance and scaling behavior when using H2 differs from Elasticsearch in production scenarios. - Users who require full Operate feature parity should use an external Elasticsearch instance until full H2 parity is confirmed. ## Primary vs. secondary storage Camunda 8 uses two layers of storage: - **[Primary storage](/reference/glossary.md#primary-storage)** is handled by the Zeebe Broker to store workflow execution data. - **[Secondary storage](/reference/glossary.md#secondary-storage)** is used by applications like Operate, Tasklist, and Admin to read and present that data. For more details on how these layers interact, see [secondary storage architecture](/self-managed/concepts/secondary-storage/index.md). Camunda 8 Run uses v2 APIs by default, so no additional configuration is required when H2 becomes the default in a future release. ## Known limitations - Tasklist can use H2 through the v2 APIs. Operate support for H2 is under active development and may have limitations in current alpha versions. - H2 is intended for testing and local development only. - H2 data persists to the configured file path by default. Keep the path stable to avoid accidental data loss. - Performance and memory use may vary depending on the local environment. ## Next steps - Review [RDBMS version support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md). - Review [backup and restore for RDBMS](/self-managed/operational-guides/backup-restore/rdbms/backup.md). - Identify and resolve [common issues when starting, configuring, or using Camunda 8 Run](../c8run-troubleshooting.md). --- ## Troubleshoot Camunda 8 Run Camunda 8 Run provides log files in the `c8run/logs` directory that can help diagnose most issues. Check these logs first when troubleshooting: - `c8run.log` – main log for Camunda 8 Run - `connectors.log` – Connectors component If you configured external Elasticsearch, inspect that deployment's logs separately. ## Startup failures ### Port conflicts **Problem:** Camunda 8 Run fails to start because ports are already in use. **Solution:** 1. Check if the default ports are already occupied: - `8080` – Camunda core (Operate, Tasklist, Admin, APIs) - `8086` – Connectors API - `26500` – Zeebe gRPC gateway - `9600` – Prometheus metrics 2. Stop processes using these ports or change the Camunda core port: ```bash # macOS/Linux lsof -i :8080 # Windows netstat -ano | findstr :8080 # Start Camunda using a different port ./c8run start --port 8081 ``` 3. If you also run the Docker Compose quickstart or other local containers, ensure they are not using these ports: ```bash docker ps docker stop ``` ### Java version issues **Problem:** Camunda 8 Run fails to start due to incorrect Java version or missing `JAVA_HOME`. **Solution:** 1. Verify Java is installed (OpenJDK 21–25 required): ```bash java -version ``` 2. Ensure `JAVA_HOME` is set: ```bash # macOS/Linux echo $JAVA_HOME # Windows echo %JAVA_HOME% ``` 3. Set `JAVA_HOME` if needed: ```bash # macOS export JAVA_HOME=$(/usr/libexec/java_home -v 21) # Linux export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 # Windows (PowerShell) setx JAVA_HOME "C:\Program Files\Java\jdk-21" ``` Replace `21` in the examples with the version you installed (21–25), and open a new terminal after setting `JAVA_HOME`. ### Incomplete startup **Problem:** Camunda 8 Run starts but some components fail to load or the browser does not open. **Solution:** 1. Stop Camunda: ```bash # macOS/Linux ./c8run stop # Windows c8run.exe stop ``` 2. Start it again: ```bash # macOS/Linux ./c8run start # Windows c8run.exe start ``` 3. Access components manually if the browser does not open automatically: - Operate: [http://localhost:8080/operate](http://localhost:8080/operate) - Tasklist: [http://localhost:8080/tasklist](http://localhost:8080/tasklist) ## Memory and performance issues ### Out of memory errors **Problem:** Camunda 8 Run becomes unresponsive. **Solution:** 1. Increase JVM heap for Camunda: ```bash # macOS/Linux export JAVA_OPTS="-Xmx4g" # Windows (Command Prompt) set JAVA_OPTS=-Xmx4g # Windows (PowerShell) $env:JAVA_OPTS="-Xmx4g" ``` 2. For resource-constrained environments, consider using H2 instead of Elasticsearch for testing. ### Slow performance **Problem:** Camunda 8 Run is slow or processes take a long time to appear in Operate. **Solution:** 1. Ensure the system meets the minimum requirements (8 GB RAM recommended). 2. Close unnecessary applications to free system resources. 3. If you use external Elasticsearch, check cluster health: ```bash curl http://localhost:9200/_cluster/health ``` On Windows, open this page directly: [http://localhost:9200/\_cluster/health](http://localhost:9200/_cluster/health) ## External Elasticsearch issues ### Cannot connect to Elasticsearch **Problem:** Camunda 8 Run starts with Elasticsearch configured as secondary storage, but search-backed features do not work or startup fails. **Solution:** 1. Verify the Elasticsearch cluster is reachable: ```bash curl http://localhost:9200 ``` 2. Confirm `application.yaml` points Camunda 8 Run to that cluster: ```yaml camunda: data: secondary-storage: type: elasticsearch elasticsearch: url: http://localhost:9200/ ``` 3. If the cluster requires authentication or TLS, add the corresponding credentials and security settings to the same configuration block. 4. Start Camunda 8 Run with the configuration file: ```bash # macOS/Linux ./c8run start --config custom-application.yaml # Windows c8run.exe start --config custom-application.yaml ``` ### Elasticsearch index or permission errors **Problem:** Operate or Tasklist show errors related to Elasticsearch indices. **Solution:** 1. Ensure the Elasticsearch user has permission to create, read, and write the required Camunda indices. For restricted setups, see [Configure Elasticsearch without cluster privileges](/self-managed/concepts/databases/elasticsearch/elasticsearch-without-cluster-privileges.md). 2. Verify the Elasticsearch cluster is healthy and has sufficient free disk space. 3. For local development, if you need to recreate the secondary store, stop Camunda 8 Run, delete the Camunda indices from your external Elasticsearch instance using your Elasticsearch tooling, and start Camunda 8 Run again. This rebuilds the secondary store from scratch. ## Authentication and access issues ### Cannot log in to web interfaces **Problem:** Default credentials (demo/demo) do not work or the login page does not appear. **Solution:** 1. Verify authentication settings (default: demo/demo). 2. If custom authentication is configured in `application.yaml`, ensure it is correct: ```yaml camunda: security: authentication: method: BASIC initialization: users: - username: demo password: demo ``` 3. Clear browser cache and cookies, then try again. 4. If you used command-line overrides at startup (such as `--username` or `--password`), ensure the values are correct: ```bash # macOS/Linux ./start.sh --username myuser --password mypassword # Windows c8run.exe start --username myuser --password mypassword ``` ### API authentication errors **Problem:** API calls fail with authentication errors even when authentication is disabled by default. **Solution:** 1. Verify that API authentication is disabled (this is the default): ```yaml camunda: security: authentication: unprotected-api: true ``` 2. If API authentication is enabled, include credentials in your API requests: ```bash curl -u demo:demo http://localhost:8080/v2/topology ``` Windows (PowerShell alternative without curl): ```powershell $pair = "demo:demo" bytes = [System.Text.Encoding]::ASCII.GetBytes($pair) $base64 = [System.Convert]::ToBase64String($bytes) Invoke-WebRequest -Uri http://localhost:8080/v2/topology -Headers @{Authorization="Basic $base64"} ``` ## Configuration issues ### Custom configuration not loading **Problem:** Changes in `application.yaml` do not take effect. **Solution:** 1. Pass the configuration file explicitly: ```bash # macOS/Linux ./start.sh --config /path/to/application.yaml # Windows c8run.exe start --config C:\path\to\application.yaml ``` 2. Verify that the YAML syntax is correct (spacing, indentation, no tabs). 3. Fully restart Camunda 8 Run after making configuration changes. ### TLS/HTTPS issues **Problem:** HTTPS is not working or certificate errors occur. **Solution:** 1. Verify the keystore file path, format, and password: ```bash # macOS/Linux ./start.sh --keystore /path/to/keystore.jks --keystorePassword yourpassword # Windows c8run.exe start --keystore C:\path\to\keystore.jks --keystorePassword yourpassword ``` 2. Remember that TLS support in Camunda 8 Run is intended for testing only, not for production environments. 3. Validate the keystore: ```bash keytool -list -keystore keystore.jks ``` ## Data and persistence issues ### Lost data after restart **Problem:** Data such as deployments, process instances, or users disappears after restarting Camunda 8 Run. **Solution:** 1. If using H2 in-memory mode, switch to file-based persistence so data is written to disk: ```yaml camunda: data: secondary-storage: type: rdbms rdbms: url: jdbc:h2:file:./camunda-data/h2db ``` 2. Check that the application has permission to write to the data directory (for example, `camunda-data/` or any configured mount path). ## Connector issues ### Custom connectors not loading **Problem:** Custom connector JARs are not recognized by Camunda 8 Run. **Solution:** 1. Verify that the connector JAR file is placed in the correct directory: ```bash # macOS/Linux c8run/custom_connectors/your-connector.jar # Windows c8run\custom_connectors\your-connector.jar ``` 2. Ensure that the corresponding element template is available in a valid Desktop Modeler search path. 3. Restart Camunda 8 Run after adding or updating connectors. 4. Check the `connectors.log` file for specific error messages that may explain why the connector failed to load. ### Connector secrets not working **Problem:** Connectors cannot access configured secrets. **Solution:** 1. For non-Docker mode, export connector secrets as environment variables: ```bash export MY_SECRET_KEY=secret_value ``` 2. For the Docker Compose setup, add secrets to the `connector-secrets.txt` file located in the Docker Compose folder. 3. Restart Camunda 8 Run after adding or modifying secrets. ## Ubuntu-specific issues **Problem:** Camunda 8 Run fails to start or behaves unpredictably on older Ubuntu versions. **Solution:** Use **Ubuntu 22.04 or newer**, as earlier versions may not support required Java versions or system dependencies required by Camunda 8 Run. --- ## Developer quickstart – Camunda 8 Run :::note Camunda 8 Run provides a lightweight, self-managed environment for local development and prototyping. It is not intended for production use. For production deployments, install the Orchestration Cluster manually as a Java application. For detailed steps, see the [manual installation](../../../deployment/manual/install) guide. ::: Camunda 8 Run is a local distribution of Camunda 8 that bundles the Camunda 8 runtime, core services, startup scripts, and a launcher application for Windows, macOS, and Linux. Camunda 8 Run enables you to run the [Orchestration Cluster](../../../reference/glossary.md#orchestration-cluster) with minimal configuration. It is intended for developers who want to model BPMN diagrams, deploy them, and interact with running [process instances](/reference/glossary.md#process-instance) in a simple environment. Camunda 8 Run includes the following: - Orchestration Cluster - [Connectors](/reference/glossary.md#connector) - H2 (default [secondary storage](/reference/glossary.md#secondary-storage) for Camunda 8 Run) Camunda 8 Run also supports document storage and management with [document handling](/self-managed/concepts/document-handling/overview.md). :::note For the latest list of supported relational databases and versions, see the [RDBMS version support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md). ::: ## Pages in this section Step through the Camunda 8 Run guide via the following topics: - [Install and start](./c8run/install-start.md) - [Configure](./c8run/configuration.md) - [Configure secondary storage](./c8run/secondary-storage.md) - [Troubleshoot](./c8run-troubleshooting.md) ## Common tasks If you are looking for a specific task from the previous single-page guide, use the links below. ## Install and start Camunda 8 Run {#install-and-start-camunda-8-run} For prerequisites, local startup, and shutdown steps, see [install and start Camunda 8 Run](./c8run/install-start.md). For container-based local deployment, see the [developer quickstart with Docker Compose](./docker-compose.md). ### Configuration options {#configuration-options} For CLI flags, startup overrides, TLS flags, and `--config` usage, see [configure Camunda 8 Run](./c8run/configuration.md#configuration-options). ### Enable authentication and authorization {#enable-authentication-and-authorization} For `application.yaml` examples that protect the API and enable authorization checks, see [enable authentication and authorization](./c8run/configuration.md#enable-authentication-and-authorization). ### Use Camunda APIs {#use-camunda-apis} For local API endpoints, authentication expectations, and links to the canonical API docs, see [use Camunda APIs](./c8run/configuration.md#use-camunda-apis). ### Use built-in and custom connectors {#use-built-in-and-custom-connectors} For local connector usage, custom connector placement, and connector secrets guidance, see [use built-in and custom connectors](./c8run/configuration.md#use-built-in-and-custom-connectors). ### Configure or switch secondary storage (H2 or Elasticsearch) {#configure-or-switch-secondary-storage-h2-or-elasticsearch} For H2, external relational databases, Elasticsearch, and migration notes, see [configure secondary storage in Camunda 8 Run](./c8run/secondary-storage.md#configure-or-switch-secondary-storage-h2-or-elasticsearch). #### Default: H2 (Camunda 8 Run) {#default-h2-camunda-8-run} For the default H2 configuration and limitations, see [default H2 in Camunda 8 Run](./c8run/secondary-storage.md#default-h2-camunda-8-run). #### External relational database options {#external-relational-database-options} For PostgreSQL, MariaDB, MySQL, Oracle, and Microsoft SQL Server examples, see [external relational database options](./c8run/secondary-storage.md#external-relational-database-options). ## Shut down Camunda 8 Run {#shut-down-camunda-8-run} For local shutdown commands, see [shut down Camunda 8 Run](./c8run/install-start.md#shut-down-camunda-8-run). ## Next steps - Review [backup and restore for RDBMS](/self-managed/operational-guides/backup-restore/rdbms/backup.md). - Identify and resolve [common issues when starting, configuring, or using Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run-troubleshooting.md). --- ## Configure Docker Compose environments Use this page to choose the Docker Compose file that matches your local setup, find component URLs, and review authentication defaults. ## Choose a Docker Compose configuration Camunda provides three Docker Compose configurations in the [Camunda Distributions repository](https://github.com/camunda/camunda-distributions): | Configuration file | Description | | :-------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `docker-compose.yaml` | Default lightweight configuration. Includes the Orchestration Cluster, Connectors, and Elasticsearch. Use this for most local development scenarios. | | `docker-compose-full.yaml` | Full configuration. Includes the Orchestration Cluster, Connectors, Optimize, Console, Management Identity, Keycloak, PostgreSQL, and Web Modeler. Use this when you need management components, process optimization, or browser-based modeling. | | `docker-compose-web-modeler.yaml` | Standalone Web Modeler configuration. Runs only Web Modeler and its dependencies. For deployment details, see [deploy with Web Modeler](./connectors-and-modeling.md#deploy-with-web-modeler). | To start a specific configuration, run one of the following commands: - Default lightweight configuration: ```shell docker compose up -d ``` - Full configuration: ```shell docker compose -f docker-compose-full.yaml up -d ``` - Standalone Web Modeler: ```shell docker compose -f docker-compose-web-modeler.yaml up -d ``` :::note In these quickstart configurations, the Orchestration Cluster uses Elasticsearch as secondary storage by default. The PostgreSQL container in the full configuration is used by management components such as Management Identity and Web Modeler, not as Orchestration Cluster secondary storage. If you want to run the Orchestration Cluster with RDBMS secondary storage, see [configure secondary storage with Docker Compose](./secondary-storage.md). ::: ## Access components Once the containers are running, you can access the components in your browser. Use the following default credentials for web interfaces: - **Username:** `demo` - **Password:** `demo` ### Orchestration Cluster The Orchestration Cluster is the core of Camunda 8 and provides process automation capabilities. | Component | URL | Description | | :----------------------------- | :--------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Operate | [http://localhost:8080/operate](http://localhost:8080/operate) | Monitor and troubleshoot process instances. See [Introduction to Operate](/components/operate/operate-introduction.md) and [Process instance creation](/components/concepts/process-instance-creation.md). | | Tasklist | [http://localhost:8080/tasklist](http://localhost:8080/tasklist) | Complete user tasks in running process instances. See [User tasks](/components/modeler/bpmn/user-tasks/user-tasks.md). | | Orchestration Cluster Admin | [http://localhost:8080/admin](http://localhost:8080/admin) | Manage users and permissions in the lightweight configuration. | | Orchestration Cluster REST API | `http://localhost:8080/v2` | REST API for process automation. | | Orchestration Cluster gRPC API | `localhost:26500` | gRPC API for high-performance process automation. | ### Management and modeling components The following components are available in the full configuration only: | Component | URL | Description | | :------------------ | :--------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Console | [http://localhost:8087](http://localhost:8087) | [Manage clusters](/components/hub/organization/manage-clusters/manage-cluster.md) and component configurations. | | Optimize | [http://localhost:8083](http://localhost:8083) | [Analyze and improve](/components/optimize/what-is-optimize.md) process performance. | | Management Identity | [http://localhost:8084](http://localhost:8084) | [Manage users](/self-managed/components/management-identity/overview.md) for Console, Optimize, and Web Modeler. | | Web Modeler | [http://localhost:8070](http://localhost:8070) | Model [BPMN](/components/modeler/bpmn/bpmn.md) processes, [DMN](/components/modeler/dmn/dmn.md) decisions, and [forms](/components/modeler/forms/camunda-forms-reference.md). | ### External dependencies | Component | Configuration | URL | Description | | :------------ | :------------------- | :----------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Elasticsearch | Lightweight and full | [http://localhost:9200](http://localhost:9200) | Used by the Orchestration Cluster as secondary storage, and by Optimize in the full configuration. | | Keycloak | Full | [http://localhost:18080/auth/](http://localhost:18080/auth/) | OIDC provider for Management Identity. The lightweight configuration uses the embedded Orchestration Cluster Admin instead. Access Keycloak with `admin` / `admin`. | | PostgreSQL | Full | `localhost:5432` | Database for Management Identity and Web Modeler. In these quickstart configurations, the Orchestration Cluster continues to use Elasticsearch as secondary storage. | ## Authentication :::note By default, the lightweight configuration uses [Basic authentication for the Orchestration Cluster](/self-managed/concepts/authentication/authentication-to-orchestration-cluster.md#basic-authentication). The full configuration uses Keycloak for [Management Identity authentication](/self-managed/concepts/authentication/authentication-to-management-components.md). ::: ### Lightweight configuration - **Web UI:** Log in to Operate and Tasklist with `demo` / `demo`. - **APIs:** REST and gRPC APIs are publicly accessible by default. ### Full configuration - **Web UI:** Log in to Operate, Tasklist, Console, Optimize, and Web Modeler with `demo` / `demo`. - **APIs:** REST and gRPC APIs require OAuth with the following settings: - **Client ID:** `orchestration` - **Client secret:** `secret` - **OAuth URL:** `http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token` - **Audience:** `orchestration-api` For details, see [Orchestration Cluster REST API authentication](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-authentication.md). ## Next steps - Review [install and start with Docker Compose](./install-start.md). - Review [configure secondary storage with Docker Compose](./secondary-storage.md). - Review [use connectors and deploy processes with Docker Compose](./connectors-and-modeling.md). --- ## Use connectors and deploy processes with Docker Compose Use this page to work with connectors and local modeling tools in the Docker Compose quickstart. ## Use connectors Both the lightweight and full Docker Compose configurations include built-in connectors for integrating with external systems. The connector runtime executes outbound connectors called from BPMN processes, and inbound connectors that trigger process instances from external events. For connector overviews and installation details, see: - [Available connectors](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md) - [Connector installation guide](/self-managed/components/connectors/overview.md) ### Connector secrets When you run Camunda locally with Docker Compose, some [connectors](/components/connectors/out-of-the-box-connectors/available-connectors-overview.md) require credentials or API keys to connect with external services such as Slack, SendGrid, or AWS. Store those values as secrets instead of hardcoding them in your process models. You can add secrets to the connector runtime with the included `connector-secrets.txt` file: 1. Open `connector-secrets.txt` in the extracted directory. 1. Add secrets in the format `NAME=VALUE`, one per line: ```text SLACK_TOKEN=xoxb-your-token-here SENDGRID_API_KEY=SG.your-api-key ``` 1. Save the file. The secrets become available in connector configurations with the syntax `{{secrets.NAME}}`. For example, `{{secrets.SLACK_TOKEN}}`. :::warning Do not commit `connector-secrets.txt` to version control with real credentials. Use placeholder values in the repository and configure actual secrets in each environment. ::: For more details, see [configure connector secrets](/self-managed/components/connectors/connectors-configuration.md). ### Custom connectors In addition to the built-in connectors, you can add custom connectors. To include custom connectors: - Create a new Docker image that bundles your connectors, as described in the [Connectors repository](https://github.com/camunda/connectors). - Mount the connector JARs as volumes into the `/opt/app` directory in the Docker Compose file. Each connector JAR must include all required dependencies inside the JAR. ## Deploy and execute processes You can deploy and execute processes with either Desktop Modeler or Web Modeler. ### Deploy with Desktop Modeler [Desktop Modeler](https://camunda.com/download/modeler/) is a free, open-source desktop application for modeling BPMN, DMN, and Camunda Forms. #### Lightweight configuration To deploy from Desktop Modeler to the lightweight configuration: 1. Open Desktop Modeler and click the deployment icon. 1. Select **Camunda 8 Self-Managed**. 1. Configure the connection: - **Cluster endpoint:** `http://localhost:8088/v2` - **Authentication:** **None** 1. Click **Deploy**. For more details, see [deploy to Self-Managed from Desktop Modeler](/self-managed/components/modeler/desktop-modeler/deploy-to-self-managed.md). #### Full configuration To deploy from Desktop Modeler to the full configuration: 1. Open Desktop Modeler and click the deployment icon. 1. Select **Camunda 8 Self-Managed**. 1. Configure the connection: - **Cluster endpoint:** `http://localhost:8088/v2` - **Authentication:** **OAuth** - **OAuth URL:** `http://localhost:18080/auth/realms/camunda-platform/protocol/openid-connect/token` - **Client ID:** `orchestration` - **Client secret:** `secret` - **Audience:** `orchestration-api` 1. Click **Deploy**. :::tip The full configuration uses Keycloak for OIDC authentication. The client credentials are preconfigured in the `.env` file and admin configuration. ::: ### Deploy with Web Modeler :::note Non-production installations of Web Modeler are limited to five collaborators per project. See [Licensing](/reference/licenses.md). ::: [Web Modeler](/components/hub/workspace/modeler/launch-modeler.md) provides a browser-based interface for creating and deploying BPMN, DMN, and form diagrams. It is included in the full configuration by default, and can also run as a standalone setup. #### Standalone setup To start Web Modeler and its dependencies independently, run: ```shell docker compose -f docker-compose-web-modeler.yaml up -d ``` To stop Web Modeler and remove all data and volumes, run: ```shell docker compose -f docker-compose-web-modeler.yaml down -v ``` #### Deploy or execute a process When you use the full configuration, Web Modeler connects automatically to the local Orchestration Cluster started by `docker-compose-full.yaml`. You can deploy and run processes directly from the Web Modeler interface. 1. Log in to Web Modeler at [http://localhost:8070](http://localhost:8070) with `demo` / `demo`. 1. [Create a new project](/components/hub/workspace/modeler/launch-modeler.md) or open an existing BPMN diagram. 1. Use the visual modeler to [design your BPMN process](/components/modeler/bpmn/bpmn.md). 1. Click **Deploy** to deploy the diagram to the preconfigured Orchestration Cluster. 1. After deployment, you can [create process instances](/components/concepts/process-instance-creation.md) and monitor them in [Operate](http://localhost:8080/operate). Web Modeler uses the `BEARER_TOKEN` authentication method to communicate with the Orchestration Cluster. The user's authentication token from Management Identity is used automatically for deployment. :::note Web Modeler is not included in the lightweight configuration. To use Web Modeler with the lightweight configuration: 1. Run Web Modeler separately with `docker-compose-web-modeler.yaml`. 1. Manually configure the cluster connection in Web Modeler. 1. Use `NONE` or `BASIC` authentication for the lightweight Orchestration Cluster. For details, see [configure Web Modeler clusters](/self-managed/components/hub/configuration/modeler-configuration.md#clusters). ::: #### Emails The Docker Compose setup includes [Mailpit](https://github.com/axllent/mailpit) as a test SMTP server. Mailpit captures all emails sent by Web Modeler, but does not forward them to actual recipients. You can access emails in Mailpit at [http://localhost:8075](http://localhost:8075). ## Next steps - Follow the [getting started guide](/guides/getting-started-example.md) to create a Java project and connect to your local cluster. - Learn [BPMN fundamentals](/components/modeler/bpmn/bpmn-primer.md) and [best practices](/components/best-practices/best-practices-overview.md). - Explore the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) and [client libraries](/apis-tools/working-with-apis-tools.md). --- ## Install and start with Docker Compose Use this page to install the Docker Compose distribution locally, start Camunda 8, and stop the environment cleanly. ## Prerequisites The following prerequisites are required to run Camunda 8 Self-Managed with Docker Compose: | Prerequisite | Description | | :------------- | :------------------------------------------------------------------------------------------------------------------------- | | Docker Compose | Version 1.27.0 or later, which supports the [latest Compose specification](https://docs.docker.com/compose/compose-file/). | | Docker | Version 20.10.16 or later. | :::tip If Docker Compose reports errors such as unsupported attributes when loading the Camunda Compose files, confirm that you are using the Docker Compose v2 plugin: ```shell docker compose version ``` Run the commands in this guide with `docker compose`, not `docker-compose`. If needed, upgrade Docker Desktop or the Docker Engine Compose plugin, then retry. ::: ## Install and start Camunda 8 with Docker Compose To start a complete Camunda 8 Self-Managed environment locally: 1. Download the latest Camunda 8 Docker Compose distribution, then extract it. 1. In the extracted directory, run: ```shell docker compose up -d ``` 1. Wait for the environment to initialize. This can take several minutes. If you use the full configuration, monitor the logs, especially the Keycloak container log, to ensure all components start. For available Compose files, component URLs, and authentication defaults, see [configure Docker Compose environments](./configuration.md). ## Stop Camunda 8 with Docker Compose To stop all containers and remove associated data, run: ```shell docker compose down -v # or for the full configuration: docker compose -f docker-compose-full.yaml down -v ``` :::caution The `-v` flag deletes all volumes, including process data, users, and other persisted state. Omit `-v` if you want to keep your data. ::: ## Next steps - Review [configure Docker Compose environments](./configuration.md). - Review [configure secondary storage with Docker Compose](./secondary-storage.md). - Review [use connectors and deploy processes with Docker Compose](./connectors-and-modeling.md). --- ## Configure secondary storage with Docker Compose Use this page to configure secondary storage for the Orchestration Cluster in the Docker Compose quickstart. ## Configure secondary storage for the Orchestration Cluster The lightweight `docker-compose.yaml` starts the Orchestration Cluster with Elasticsearch as secondary storage. To test another backend, add a `docker-compose.override.yaml` file next to the extracted Compose files and override the `camunda` service there. The full `docker-compose-full.yaml` configuration already includes PostgreSQL for Management Identity and Web Modeler. That database is separate from the Orchestration Cluster secondary storage. If you want the Orchestration Cluster itself to use RDBMS, configure the `camunda` service as shown in the examples below. Use this workflow for each example: 1. Create `docker-compose.override.yaml` in the extracted distribution directory. 1. Copy the backend-specific example into that file. 1. If the backend requires an external JDBC driver, place the driver JAR directly in `./driver-lib` and keep the `./driver-lib:/driver-lib` volume mount from the example. 1. Start the updated stack with the command shown below the example. :::note Camunda configures the built-in exporter automatically from `camunda.data.secondary-storage.*`. You do not need to add a separate exporter class for the standard Docker Compose quickstart. ::: :::note Some existing pages still use the legacy environment variable prefix `CAMUNDA_DATA_SECONDARYSTORAGE_*`. The examples on this page use `CAMUNDA_DATA_SECONDARY_STORAGE_*` consistently. ::: ### Use RDBMS secondary storage These examples switch the Orchestration Cluster from Elasticsearch to RDBMS. They are suitable for local development and evaluation. PostgreSQL and H2 are the simplest starting points. MariaDB and SQL Server are also bundled in the image. MySQL and Oracle require you to provide the JDBC driver. :::note The Orchestration Cluster supports RDBMS as secondary storage. Operate support on RDBMS is still limited in 8.9-alpha3. Before you use these examples beyond local development, review the [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md#operate-with-rdbms). ::: ```yaml services: camunda: environment: CAMUNDA_DATA_SECONDARY_STORAGE_TYPE: rdbms CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_DATABASEVENDORID: postgresql CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_URL: jdbc:postgresql://postgres:5432/camunda_secondary CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_USERNAME: camunda CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_PASSWORD: camunda depends_on: - postgres postgres: image: postgres:16 environment: POSTGRES_DB: camunda_secondary POSTGRES_USER: camunda POSTGRES_PASSWORD: camunda ports: - "5432:5432" volumes: - postgres-secondary-data:/var/lib/postgresql/data volumes: postgres-secondary-data: ``` ```shell docker compose up -d camunda postgres ``` ```yaml services: camunda: environment: CAMUNDA_DATA_SECONDARY_STORAGE_TYPE: rdbms CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_DATABASEVENDORID: mariadb CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_URL: jdbc:mariadb://mariadb:3306/camunda_secondary?serverTimezone=UTC CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_USERNAME: camunda CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_PASSWORD: camunda depends_on: - mariadb mariadb: image: mariadb:11.4 environment: MARIADB_DATABASE: camunda_secondary MARIADB_USER: camunda MARIADB_PASSWORD: camunda MARIADB_ROOT_PASSWORD: rootcamunda ports: - "3306:3306" volumes: - mariadb-secondary-data:/var/lib/mysql volumes: mariadb-secondary-data: ``` ```shell docker compose up -d camunda mariadb ``` ```yaml services: camunda: environment: CAMUNDA_DATA_SECONDARY_STORAGE_TYPE: rdbms CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_DATABASEVENDORID: mysql CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_URL: jdbc:mysql://mysql:3306/camunda_secondary?serverTimezone=UTC CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_USERNAME: camunda CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_PASSWORD: camunda depends_on: - mysql volumes: - ./driver-lib:/driver-lib mysql: image: mysql:8.4 environment: MYSQL_DATABASE: camunda_secondary MYSQL_USER: camunda MYSQL_PASSWORD: camunda MYSQL_ROOT_PASSWORD: rootcamunda ports: - "3306:3306" volumes: - mysql-secondary-data:/var/lib/mysql volumes: mysql-secondary-data: ``` ```shell docker compose up -d camunda mysql ``` Place the MySQL Connector/J JAR directly in `./driver-lib` before you start the stack. ```yaml services: camunda: environment: CAMUNDA_DATA_SECONDARY_STORAGE_TYPE: rdbms CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_DATABASEVENDORID: oracle CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_URL: jdbc:oracle:thin:@oracle:1521/FREEPDB1 CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_USERNAME: camunda CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_PASSWORD: camunda depends_on: - oracle volumes: - ./driver-lib:/driver-lib oracle: image: gvenzl/oracle-free:23-slim environment: ORACLE_PASSWORD: oracle APP_USER: camunda APP_USER_PASSWORD: camunda ports: - "1521:1521" volumes: - oracle-secondary-data:/opt/oracle/oradata volumes: oracle-secondary-data: ``` ```shell docker compose up -d camunda oracle ``` Place the Oracle JDBC driver JAR directly in `./driver-lib` before you start the stack. ```yaml services: camunda: environment: CAMUNDA_DATA_SECONDARY_STORAGE_TYPE: rdbms CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_DATABASEVENDORID: mssql CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_URL: jdbc:sqlserver://mssql:1433;databaseName=camunda_secondary;encrypt=false CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_USERNAME: sa CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_PASSWORD: Camunda123! depends_on: - mssql mssql: image: mcr.microsoft.com/mssql/server:2022-latest environment: ACCEPT_EULA: "Y" MSSQL_SA_PASSWORD: Camunda123! MSSQL_PID: Developer ports: - "1433:1433" volumes: - mssql-secondary-data:/var/opt/mssql volumes: mssql-secondary-data: ``` ```shell docker compose up -d mssql docker compose exec mssql /opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -P 'Camunda123!' -Q "IF DB_ID('camunda_secondary') IS NULL CREATE DATABASE camunda_secondary" docker compose up -d camunda ``` ```yaml services: camunda: environment: CAMUNDA_DATA_SECONDARY_STORAGE_TYPE: rdbms CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_DATABASEVENDORID: h2 CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_URL: jdbc:h2:file:./camunda-data/h2db CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_USERNAME: sa CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_PASSWORD: "" volumes: - h2-secondary-data:/usr/local/camunda/camunda-data volumes: h2-secondary-data: ``` ```shell docker compose up -d camunda ``` Use H2 only for development, testing, and evaluation. It is not a production backend. ### Switch between RDBMS, Elasticsearch, and OpenSearch To switch back from RDBMS to a document-store backend, change the `CAMUNDA_DATA_SECONDARY_STORAGE_TYPE` value and keep only the backend-specific connection settings you need. ```yaml services: camunda: environment: CAMUNDA_DATA_SECONDARY_STORAGE_TYPE: elasticsearch CAMUNDA_DATA_SECONDARY_STORAGE_ELASTICSEARCH_URL: http://elasticsearch:9200 CAMUNDA_DATA_SECONDARY_STORAGE_ELASTICSEARCH_USERNAME: "" CAMUNDA_DATA_SECONDARY_STORAGE_ELASTICSEARCH_PASSWORD: "" ``` ```shell docker compose up -d camunda elasticsearch ``` This matches the default lightweight quickstart backend. ```yaml services: camunda: environment: CAMUNDA_DATA_SECONDARY_STORAGE_TYPE: opensearch CAMUNDA_DATA_SECONDARY_STORAGE_OPENSEARCH_URL: http://opensearch:9200 depends_on: - opensearch opensearch: image: opensearchproject/opensearch:2.19.3 environment: discovery.type: single-node OPENSEARCH_JAVA_OPTS: -Xms512m -Xmx512m DISABLE_SECURITY_PLUGIN: "true" ports: - "9200:9200" - "9600:9600" volumes: - opensearch-secondary-data:/usr/share/opensearch/data volumes: opensearch-secondary-data: ``` ```shell docker compose up -d camunda opensearch ``` ### Secondary storage environment variables Use these variables when you adapt the examples to your own local setup: | Variable | Use | | :------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------- | | `CAMUNDA_DATA_SECONDARY_STORAGE_TYPE` | Selects the backend family: `rdbms`, `elasticsearch`, or `opensearch`. | | `CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_URL` | JDBC connection string for the relational database used as secondary storage. | | `CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_USERNAME` | Database username for RDBMS secondary storage. | | `CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_PASSWORD` | Database password for RDBMS secondary storage. | | `CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_DATABASEVENDORID` | Optional vendor override. Use `postgresql`, `mariadb`, `mysql`, `oracle`, `mssql`, or `h2` when you want to make the backend explicit. | | `CAMUNDA_DATA_SECONDARY_STORAGE_RDBMS_AUTO_DDL` | Controls whether Camunda creates and updates the schema automatically. The default is `true`. | | `CAMUNDA_DATA_SECONDARY_STORAGE_ELASTICSEARCH_URL` | Endpoint for Elasticsearch when `type=elasticsearch`. | | `CAMUNDA_DATA_SECONDARY_STORAGE_OPENSEARCH_URL` | Endpoint for OpenSearch when `type=opensearch`. | For additional secondary storage settings, see [Configure secondary storage](/self-managed/concepts/secondary-storage/configuring-secondary-storage.md) and [Configure RDBMS for manual installations](/self-managed/deployment/manual/rdbms/configuration.md). ## Next steps - Review [configure Docker Compose environments](./configuration.md). - Review [use connectors and deploy processes with Docker Compose](./connectors-and-modeling.md). --- ## Developer quickstart with Docker Compose Get started with Docker Compose to run Camunda 8 Self-Managed locally. The default lightweight configuration includes the Orchestration Cluster, Connectors, and Elasticsearch. The full configuration additionally includes Optimize, Console, Management Identity, Web Modeler, Keycloak, and PostgreSQL. Docker Compose also supports [document handling](/self-managed/concepts/document-handling/overview.md), configurable secondary storage, built-in connectors, custom connectors, and local modeling workflows with Desktop Modeler and Web Modeler. :::note The [Docker images](/self-managed/deployment/docker/docker.md) are supported for production usage. The Docker Compose files are intended for local development and evaluation, and are not designed for production. For production deployments, use [Kubernetes with Helm](/self-managed/deployment/helm/install/index.md). ::: Camunda 8 with Docker Compose includes the following: - Orchestration Cluster - Connectors - Elasticsearch as the default secondary storage in the lightweight configuration ## Pages in this section Step through the Docker Compose guide with the following topics: - [Install and start](./docker-compose/install-start.md) - [Configuration](./docker-compose/configuration.md) - [Secondary storage](./docker-compose/secondary-storage.md) - [Connectors and modeling](./docker-compose/connectors-and-modeling.md) ## Common tasks If you are looking for a specific task from the previous single-page guide, use the links below. ## Install and start Camunda 8 with Docker Compose {#install-and-start-camunda-8-with-docker-compose} For prerequisites, startup commands, and shutdown commands, see [install and start with Docker Compose](./docker-compose/install-start.md). ### Choose a Docker Compose configuration {#choose-a-docker-compose-configuration} For lightweight, full, and standalone Web Modeler configurations, plus component URLs and authentication defaults, see [configure Docker Compose environments](./docker-compose/configuration.md#choose-a-docker-compose-configuration). ### Configure secondary storage {#configure-secondary-storage} For Elasticsearch, OpenSearch, PostgreSQL, MariaDB, MySQL, Oracle, Microsoft SQL Server, and H2 examples, see [configure secondary storage with Docker Compose](./docker-compose/secondary-storage.md#configure-secondary-storage-for-the-orchestration-cluster). ### Use connectors and deploy processes {#use-connectors-and-deploy-processes} For connector secrets, custom connectors, Desktop Modeler, and Web Modeler, see [use connectors and deploy processes with Docker Compose](./docker-compose/connectors-and-modeling.md#use-connectors). ## Stop Camunda 8 with Docker Compose {#stop-camunda-8-with-docker-compose} For shutdown commands and volume cleanup guidance, see [install and start with Docker Compose](./docker-compose/install-start.md#stop-camunda-8-with-docker-compose). ## Next steps - Follow the [getting started guide](/guides/getting-started-example.md) to create a Java project and connect to your local cluster. - Explore the [Orchestration Cluster REST API](/apis-tools/orchestration-cluster-api-rest/orchestration-cluster-api-rest-overview.md) and [client libraries](/apis-tools/working-with-apis-tools.md). - When you are ready for production, deploy with [Kubernetes and Helm](/self-managed/deployment/helm/install/index.md). --- ## Developer quickstart This quickstart guides application engineers through deploying **Camunda 8 Self-Managed** to a local developer environment. --- ## Quickstart Welcome to the **Quickstart** section for Camunda 8 Self-Managed. This guide is designed to help you get up and running quickly with Camunda 8 in a local environment—whether you're a developer building process solutions or an administrator responsible for deploying and running Camunda clusters. You’ll find two tailored quickstart paths: - A **developer quickstart** using lightweight tools like **Camunda 8 Run** and **Docker Compose**. - An **administrator quickstart** using **Kind (Kubernetes in Docker)** to simulate real-world infrastructure. :::note These quickstarts are intended for local testing and learning purposes only. They are not designed for production deployments. ::: ## Quickstart for developers If you're a developer looking to evaluate or build process solutions with Camunda 8, this path is for you. It focuses on getting a minimal setup running quickly using: - **Camunda 8 Run**: A lightweight distribution of the Camunda engine. - **Docker Compose**: For spinning up required services like [Zeebe](/reference/glossary.md#zeebe), Operate, and Tasklist. [Get started with the developer quickstart](./developer-quickstart.md) ## Quickstart for administrators If you're an administrator exploring how Camunda 8 runs in a Kubernetes-like environment, this guide walks you through: - Using **Kind** to simulate a Kubernetes cluster locally. - Deploying the full Camunda 8 stack using Helm charts. [Get started with the administrator quickstart](/self-managed/deployment/helm/cloud-providers/kind.md) ## What you’ll learn In each quickstart, you will: - Spin up a local environment with the core Camunda 8 components. - Deploy a sample process definition. - Interact with the [Orchestration Cluster](/reference/glossary.md#orchestration-cluster) web applications (Operate and Tasklist). - Understand the responsibilities associated with your chosen role (developer or administrator). ## Prerequisites Each quickstart guide lists the required tools and setup steps. In general, you’ll need: - Docker and Docker Compose (for developers). - Kind, kubectl, and Helm (for administrators). - A basic understanding of BPMN and distributed systems is helpful but not required. ## Next steps After completing a quickstart, you can: - Dive deeper into platform components like Zeebe, Operate, and Identity. - Learn about production deployment options. - Explore real-world examples and best practices. --- ## Components - Zeebe Broker and Gateway - Operate - Tasklist - Connectors - Optimize - Identity - Web Modeler - Console All components except Web Modeler and Console are single Java applications. Depending on your needs, you might not need all of the above components to successfully use Camunda 8. Camunda 8 Self-Managed users may also use [Desktop Modeler](../../components/modeler/desktop-modeler/install-the-modeler.md) as an addition to these components. Desktop Modeler can be used by process developers to build BPMN diagrams, DMN diagrams, or [Camunda Forms](/components/modeler/forms/utilizing-forms.md) for automation. :::note To obtain or retrieve your Camunda 8 credentials for Enterprise licenses, visit the [contact page](/reference/contact.md). ::: --- ## Licensing(React-components) Camunda 8 Self-Managed only Installations of Camunda 8 Self-Managed which require a license can provide their license key to the components as an environment variable: | Environment variable | Description | Default value | | --------------------- | -------------------------------------------------------------------- | ------------- | | `CAMUNDA_LICENSE_KEY` | Your Camunda 8 license key, if your installation requires a license. | None | For Helm installations, license keys can be configured globally in your `values.yaml` file. See the [License key](/self-managed/deployment/helm/configure/license-key.md) for more details. :::note Camunda 8 components without a valid license may display **Non-Production License** in the navigation bar and issue warnings in the logs. These warnings have no impact on startup or functionality. **Web Modeler without a license:** Web Modeler is limited to **five concurrent users** when running without a valid enterprise license. This applies to Self-Managed installations used for testing or development purposes. To support additional users or for production use, obtain a Camunda Self-Managed Enterprise Edition license by visiting the [Camunda Enterprise page](https://camunda.com/platform/camunda-platform-enterprise-contact/). ::: --- ## Container deployment overview With container-based deployments, you can run the [Camunda 8 Orchestration Cluster](./reference-architecture.md#orchestration-cluster-vs-web-modeler-and-console) in a portable, consistent runtime with the benefits of containerization, without managing Kubernetes. The following container deployment option is currently available: - **[Amazon ECS with Fargate](/self-managed/deployment/containers/cloud-providers/amazon/aws-ecs.md)**: Deploy to Amazon ECS with Fargate and Aurora PostgreSQL. --- ## Kubernetes deployment overview This reference architecture provides guidance for deploying Camunda 8 Self-Managed within a Kubernetes cluster. This deployment method is ideal for users who want to leverage the benefits of containerization and self-healing. It supports quick scalability and offers increased resilience through multi-zone deployments. ## Key features - **Scalability & high availability**: Camunda 8 can scale dynamically to meet demand and supports high-availability configurations. - **Fault tolerance & resilience**: Deploy Camunda 8 across availability zones or regions to improve fault tolerance and increase uptime for your workflows. While Kubernetes introduces a steeper learning curve, once configured properly it provides significant benefits—such as self-healing capabilities that automatically restart failed containers and reschedule workloads to maintain availability. Kubernetes also benefits from a robust ecosystem of [**Cloud Native Computing Foundation (CNCF) projects**](https://www.cncf.io/), enabling seamless integration with tools for monitoring, observability, logging, and security. Solutions like [Prometheus](https://prometheus.io/) and [Grafana](https://grafana.com/) offer comprehensive monitoring, while service meshes like [Istio](https://istio.io/) enhance traffic management and security. By leveraging this ecosystem, organizations can extend Kubernetes to fit specific operational needs—improving automation, scalability, and visibility across their infrastructure. ## Reference implementations This section includes reference deployment architectures: ### Amazon EKS - [Amazon EKS single-region](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/terraform-setup.md): Standard production setup. - [Amazon EKS dual-region](/self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/dual-region.md): Advanced multi-region setup. ### Red Hat OpenShift on AWS (ROSA) - [ROSA single-region](/self-managed/deployment/helm/cloud-providers/amazon/openshift/terraform-setup.md): Standard production setup. - [ROSA dual-region](/self-managed/deployment/helm/cloud-providers/amazon/openshift/terraform-setup-dual-region.md): Advanced multi-region setup. ### Microsoft Azure - [Microsoft AKS single-region](/self-managed/deployment/helm/cloud-providers/azure/microsoft-aks/terraform-setup.md): Standard production setup. For common issues and mitigation strategies, refer to the [deployment troubleshooting guide](/self-managed/operational-guides/troubleshooting.md). ## Architecture The [reference architecture overview](/self-managed/reference-architecture/reference-architecture.md#orchestration-cluster-vs-web-modeler-and-console) explains the distinction between these components: - **Orchestration Cluster**: Core process execution engine (Zeebe, Operate, Tasklist, Admin) with tightly integrated components (Optimize, Connectors). - **Web Modeler, Console, and Management Identity**: Management and design tools (Web Modeler, Console, Management Identity) for modeling and deploying diagrams, and monitoring the health of orchestration clusters. See the reference architecture for details on how these components communicate. _Infrastructure diagram for a single-region setup (click the image to open the PDF version)_ [![Architecture Overview](./img/k8s-single.jpg)](./img/k8s-single.pdf) This Kubernetes architecture illustrates a high-availability setup across multiple availability zones (A, B, and C), with key networking components to ensure scalability, security, and reliability. We recommend using multiple availability zones to improve fault tolerance and eliminate single points of failure. To control access, deploy Camunda 8 in a private subnet and manage inbound traffic using an Ingress and Load Balancer. :::note The database is not shown in the diagram. It should be hosted outside the Kubernetes cluster, ideally within the same private network or subnet. Many organizations use a dedicated external database setup with tightly controlled access. ::: ### Kubernetes A production deployment is recommended. For more information, see the [production deployment guide](/self-managed/deployment/helm/install/production/index.md) and the [components](#components) section. The following visuals provide a simplified view of the deployed namespaces using the [Camunda 8 Helm chart](/self-managed/deployment/helm/install/quick-install.md). For clarity, ConfigMaps, Secrets, RBAC, and ReplicaSets are omitted. #### Orchestration Cluster ![Orchestration Cluster](./img/k8s-cluster-view-orchestration.jpg) The Helm chart uses a single Ingress by default, enabling a unified domain with each application accessible via a dedicated path. Most Camunda 8 components are stateless and deployed as **Deployments**. However, the Orchestration Cluster contains Zeebe brokers, which require a **StatefulSet** to maintain consistent volume mounts. This ensures stable pod ordering and identifiers. StatefulSet names remain consistent even as they represent the entire Orchestration Cluster—simplifying migration from existing setups. The Orchestration Cluster exposes two services: 1. A [**headless service**](https://kubernetes.io/docs/concepts/services-networking/service/#headless-services) for internal communication between Zeebe brokers. This service skips load balancing and resolves to pod IPs for direct peer-to-peer communication. 2. A **standard service** for external applications. This service distributes traffic randomly (via `kube-proxy`) and is suitable for clients or other services connecting to the cluster. #### Web Modeler and Console ![Web Modeler and Console](./img/k8s-cluster-view-managing.jpg) Web Modeler, Console, and Management Identity are stateless and deployed as **Deployments**, with data stored in an external SQL database. This makes them easy to scale as needed. Each namespace uses its own Ingress, as Ingress resources are namespace-scoped (not cluster-wide). This requires separate subdomains for each Ingress. For more details, see the [production deployment guide](/self-managed/deployment/helm/install/production/index.md). ### High availability (HA) :::caution Non-HA importer / archiver This applies to **Optimize**: When scaling from a single pod to multiple pods, ensure that the `importer / archiver` is enabled on only one pod. Enabling it on more than one pod may cause data inconsistencies. This is a known limitation and will be addressed in a future update. See the [Optimize system configuration guide](/self-managed/components/optimize/configuration/system-configuration-platform-8.md#general-settings) for example settings. ::: For high availability, we recommend a minimum of **four Kubernetes nodes** to ensure fault tolerance and support leader election. This is especially important when using the [Raft protocol]() for consensus within the Orchestration Cluster. For more details, refer to the [clustering documentation](/components/zeebe/technical-concepts/clustering.md). While Deployments and StatefulSets in Kubernetes can scale independently of physical hardware, four nodes are typically required to support: - The default three-node Orchestration Cluster (incl. Zeebe, Operate, Tasklist and Admin) - Other Camunda 8 components (Web Modeler, Management Identity, Console, Optimize) Depending on your specific use case, you may need to scale **horizontally** (more nodes) or **vertically** (larger nodes) to meet resource requirements. By default, node affinity rules prevent all Orchestration Cluster pods from being scheduled on the same node. This requires at least three nodes for proper operation. For details, see the [Kubernetes node affinity documentation](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). To further improve fault tolerance, distribute the Orchestration Cluster and other components across **multiple availability zones**. Use affinity and anti-affinity rules to ensure workloads remain available even if a zone fails. ### Components Camunda 8 deployments typically separate workloads into two logical groups: - **Orchestration Cluster** - **Web Modeler and Console** We recommend deploying these groups into separate [Kubernetes namespaces](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/). This separation supports multi-tenancy, improves isolation, and allows flexible scaling. However, deploying all components in a single namespace is also possible for smaller environments. A **multi-namespace setup** enables: - Independent scaling of orchestration clusters based on workload - Shared access to centralized components (e.g., Management Identity) #### Orchestration Cluster namespace As shown in the [architecture diagram](#orchestration-cluster), the Orchestration Cluster is deployed as a StatefulSet and packaged as a single container image. It includes the following components: - [Zeebe](/components/zeebe/zeebe-overview.md) — workflow engine and broker - [Operate](/components/operate/operate-introduction.md) — visibility and troubleshooting UI - [Tasklist](/components/tasklist/introduction-to-tasklist.md) — UI for human tasks - [Admin](/self-managed/components/orchestration-cluster/admin/overview.md) — authentication and access control Also included in this namespace are components that are tightly integrated with the cluster: - [Optimize](/components/optimize/what-is-optimize.md) — reporting and analytics - [Connectors](/components/connectors/introduction.md) — external system integrations #### Web Modeler and Console namespace As shown in the [architecture diagram](#web-modeler-and-console), this namespace contains: - Web Modeler — browser-based BPMN editor - Console — administrative interface - [Management Identity](/self-managed/components/management-identity/overview.md) — centralized access control for Web Modeler, Console, Optimize This namespace also requires an OIDC-compatible Identity Provider (IdP) for Management Identity. You can use any compatible provider (for example, Keycloak deployed via the [Keycloak Operator](/self-managed/deployment/helm/configure/operator-based-infrastructure.md#keycloak-deployment) or Microsoft Entra ID). :::tip Why isn't an IdP included by default? The choice of identity provider is highly specific to each organization's security requirements, existing infrastructure, and compliance needs. Rather than bundling a default IdP that may not match your setup, the reference architecture leaves this choice to you. This approach gives you full control over your authentication stack and avoids unnecessary complexity for teams that already have an IdP in place. ::: :::warning Identity separation Console, Optimize, and Web Modeler rely on Management Identity (formerly Identity). This service is separate from the embedded Admin in the Orchestration Cluster and incompatible with it. To share the same user base and API clients across both, you must use OIDC. ::: For configuration details, see: - [Connect Orchestration Cluster to an OIDC provider](/self-managed/concepts/authentication/authentication-to-orchestration-cluster.md#oidc) - [Connect Management Identity to an OIDC provider](/self-managed/components/management-identity/configuration/connect-to-an-oidc-provider.md) The Orchestration Cluster can be configured to authenticate with OIDC by connecting to the Management Identity service deployed in this namespace. ## Requirements This guide focuses on a single-region application, but can be adapted for a multi-region setup once you understand the basics of a single region. For details on multi-region configurations, especially dual-region setups, refer to the [dedicated guide](/self-managed/concepts/multi-region/dual-region.md). ### Infrastructure We recommend using a [certified Kubernetes](https://www.cncf.io/training/certification/software-conformance/#benefits) distribution. Camunda 8 is not tied to a specific Kubernetes version. To simplify deployment, we provide a [Helm chart](/self-managed/deployment/helm/install/quick-install.md) that supports the Kubernetes [official support cycle](https://kubernetes.io/releases/). #### Minimum cluster requirements The following are suggested minimum requirements. Sizing depends heavily on your specific use cases and workload. Refer to [sizing your environment](/components/best-practices/architecture/sizing-your-environment.md) and [Zeebe resource planning](/self-managed/components/orchestration-cluster/zeebe/operations/resource-planning.md), and conduct benchmarking to determine your exact needs. - **4 Kubernetes nodes** - CPU: 4 modern cores - Memory: 16 GiB - **Persistent volumes** - 1,000 IOPS - 32 GiB - _Avoid burstable disk types_ #### Networking Networking is largely managed through services and load balancers. The following outlines typical port usage, which may require whitelisting in private networks: - Stable, high-speed connection - Firewall rules for: - `80`: Web UI (Console, Management Identity, Web Modeler, and IdP if co-located) - `82`: Metrics (Management Identity) - `8080`: REST/Web UI (Connectors, Orchestration Cluster) - `8091`: Management (Web Modeler) - `8092`: Management (Optimize) - `9100`: Management (Console) - `9600`: Management (Orchestration Cluster) - `26500`: gRPC endpoint - `26501`: Gateway-to-broker - `26502`: Inter-broker A load balancer is recommended to distribute traffic and expose Camunda 8 to users as needed. The exposed Kubernetes service port may differ from the internal component port. Refer to the rendered Helm chart or component configuration for accurate target ports. :::note Databases Database ports are not included here, as databases should be maintained outside of Camunda. Default ports may vary by environment. Typical defaults include: - `5432`: PostgreSQL - `9200`, `9300`, `9600`: Document-store secondary storage (Elasticsearch/OpenSearch) ::: ##### Load balancer The Zeebe Gateway as part of the Orchestration Cluster requires gRPC, which itself requires HTTP/2 to be used. It is recommended to secure the endpoint with a TLS certificate. :::tip If you do not rely on the gRPC capabilities of Camunda 8, you can safely disregard this and use the Orchestration Cluster REST API instead. ::: By default, the Camunda 8 Helm chart is compatible with the [Ingress-nginx controller](https://github.com/kubernetes/ingress-nginx), which supports gRPC and HTTP/2. This solution is applicable independent of the cloud provider. `Ingress-nginx` deploys a Network Load Balancer (layer 4). The following annotation is added by the Helm chart to enable gRPC: ```yaml annotations: nginx.ingress.kubernetes.io/backend-protocol: "GRPC" ``` ### Application The Helm chart required for deploying on Kubernetes is [publicly available](https://helm.camunda.io/). Camunda maintains the required Docker images consumed by the Helm chart. These images are available on [DockerHub](https://hub.docker.com/u/camunda) or [Camunda Enterprise Registry](https://registry.camunda.cloud). The `Dockerfile` and its default configuration are available as part of the [Camunda repository](https://github.com/camunda/camunda/blob/main/Dockerfile). ### Database The following databases are required: | Database | Requirement | | :------------------------------- | :------------------------------------------------------------------------------------------------- | | Document-store secondary storage | Required by Orchestration Cluster and Optimize in this topology (Elasticsearch/OpenSearch). | | PostgreSQL | Required by Management Identity and Web Modeler. Also required by Keycloak if deployed in-cluster. | :::info OpenSearch support Camunda 8 supports both [Amazon OpenSearch](https://aws.amazon.com/opensearch-service) and the open-source [OpenSearch](https://opensearch.org/) distribution. ::: For backend trade-offs and production guidance, see [secondary storage architecture](/self-managed/reference-architecture/reference-architecture.md#secondary-storage-architecture). For more general context, see the [reference architecture overview](/self-managed/reference-architecture/reference-architecture.md#architecture). Sizing is use case dependent. It is crucial to conduct thorough load testing and benchmarking to determine the appropriate sizing for your specific environment and workload. Once deployed, the included [Grafana dashboard](/self-managed/operational-guides/monitoring/metrics.md#grafana) can be used with [Prometheus](https://prometheus.io/) to monitor for bottlenecks when exporting data from the Orchestration Cluster to your database. ## Distributions ### OpenShift Red Hat OpenShift, a Kubernetes distribution maintained by [Red Hat](https://www.redhat.com/en/technologies/cloud-computing/openshift), provides options for both managed and on-premises hosting. #### Minimum cluster requirements - Instance type: 4 vCPUs (x86_64, >3.1 GHz), 16 GiB memory - Number of dedicated nodes: 4 - Volume type: SSD - 1,000–3,000 IOPS per volume - Throughput of 1,000 MB/s per volume #### Supported versions :::info Supported versions As stated in the general [supported environments](/reference/supported-environments.md) policy, Camunda 8 Self-Managed runs on any [Certified Kubernetes](https://www.cncf.io/training/certification/software-conformance/) distribution. For OpenShift specifically, this means any release in the Red Hat **General Availability**, **Full Support**, or **Maintenance Support** lifecycle phases (see the [Red Hat OpenShift Container Platform Life Cycle Policy](https://access.redhat.com/support/policy/updates/openshift)), within the upstream [Kubernetes version skew policy](https://kubernetes.io/releases/version-skew-policy/). Our reference architectures are continuously validated against the latest stable OpenShift release available in Red Hat's GA channel. Newly released OpenShift minor versions are evaluated and validated shortly after their GA. :::caution Versions compatibility Camunda 8 supports OpenShift versions in the Red Hat General Availability, Full Support, and Maintenance Support lifecycle phases. For more information, refer to the [Red Hat OpenShift Container Platform Life Cycle Policy](https://access.redhat.com/support/policy/updates/openshift). Our reference architectures are continuously validated against the latest stable OpenShift release available in Red Hat's GA channel. Newly released OpenShift minor versions are evaluated and validated shortly after their GA. ::: ## Cloud specifics ### Amazon EKS #### Minimum cluster requirements - Instance type: `m6i.xlarge` (4 vCPUs, 16 GiB memory) - Number of Kubernetes nodes: 4 - Volume type: SSD `gp3` - 3,000 IOPS baseline - Requires [Amazon EBS CSI driver](https://docs.aws.amazon.com/eks/latest/userguide/ebs-csi.html) to be installed and a `gp3` StorageClass [configured](https://docs.aws.amazon.com/eks/latest/userguide/create-storage-class.html) - Volume alternative: `gp2` - Only if `gp3` isn't available - IOPS performance [varies based on volume size](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/general-purpose.html#gp2-performance) - Minimum 34 GiB for >1,000 IOPS #### Load balancer The following AWS load balancers are supported by Camunda 8: - Application Load Balancer (ALB) - Network Load Balancer (NLB) The Classic Load Balancer (CLB) is the previous generation and is not supported by Camunda 8. ##### Application Load Balancer (ALB) AWS offers an [Application Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html) (ALB), which requires TLS termination in the load balancer and supports AWS Certificate Manager (ACM). To use an Application Load Balancer: - Deploy the [AWS Load Balancer Controller](https://kubernetes-sigs.github.io/aws-load-balancer-controller/) - Set up a [certificate in AWS Certificate Manager](https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-request-public.html) - Configure Ingress for Camunda using the [AWS example](https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/main/docs/examples/grpc_server.md), which results in the following annotations on the Camunda Ingress: ```yaml alb.ingress.kubernetes.io/ssl-redirect: "443" alb.ingress.kubernetes.io/backend-protocol-version: GRPC alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]' alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip ``` The setup does not require configuration of [TLS on the Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls). If the AWS Load Balancer Controller is correctly configured, it automatically retrieves the appropriate certificate from ACM based on the host name. :::note AWS ALB known limitations Application Load Balancers (ALB) support HTTP/2 over HTTPS listeners and allow a maximum of 128 streams per client HTTP/2 connection. The HTTP/2 server-push feature is not supported. For details, see [AWS ALB protocols](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-target-groups.html#target-group-protocol-version:~:text=The%20maximum%20number%20of%20streams,client%20HTTP%2F2%20connection%20is%20128). If you need more than 128 streams per client, see [Network Load Balancer](#network-load-balancer-nlb). ::: ##### Network load balancer (NLB) Camunda 8 is compatible with [Ingress-nginx](https://github.com/kubernetes/ingress-nginx), which deploys a Network Load Balancer. In this setup, TLS must be terminated within the Ingress, so AWS Certificate Manager (ACM) cannot be used. ACM does not allow exporting the private key required for TLS termination inside the Ingress. ### Microsoft AKS #### Minimum cluster requirements - Instance type: Standard_D4as_v4 (4 vCPUs, 16 GiB memory) - Number of Kubernetes nodes: 4 - Volume type: Premium SSD v2 - 3,000 IOPS baseline - Several [known limitations](https://learn.microsoft.com/en-us/azure/virtual-machines/disks-types#premium-ssd-v2-limitations), e.g., lack of [Azure Backup support](https://learn.microsoft.com/en-us/azure/backup/disk-backup-support-matrix#limitations) - Volume alternative: Premium SSD - IOPS performance [varies based on volume size](https://learn.microsoft.com/en-us/azure/virtual-machines/disks-types#premium-ssds) - Minimum 256 GiB (P15) for > 1,000 IOPS #### Load balancer Azure offers the **Application Gateway for Containers (AGC)**, which supports gRPC and HTTP/2 via the `GRPCRoute` resource in the [Kubernetes Gateway API](https://kubernetes.io/docs/concepts/services-networking/gateway/). Configuration details are available in the [official Azure documentation](https://learn.microsoft.com/en-us/azure/application-gateway/for-containers/grpc). ### Google GKE #### Minimum cluster requirements - Instance type: n(1|2)-standard-4 (4 vCPUs, 15 / 16 GiB memory) - Number of Kubernetes nodes: 4 - Volume type: Performance (SSD) persistent disks - IOPS performance [varies based on volume size](https://cloud.google.com/compute/docs/disks/performance#performance_factors) - Minimum 34 GiB for > 1,000 IOPS #### Load balancer If you are using the [GKE Ingress](https://cloud.google.com/kubernetes-engine/docs/concepts/ingress) (Ingress-gce), you may need to use `cloud.google.com/app-protocols` annotations in the **Zeebe Gateway** service. For more details, visit the GKE guide [using HTTP/2 for load balancing with Ingress](https://cloud.google.com/kubernetes-engine/docs/how-to/ingress-http2). --- ## Manual deployment overview This reference architecture provides guidance on deploying Camunda 8 Self-Managed as a standalone Java application. This deployment method is ideal for users who prefer manual deployment on bare metal servers or virtual machines (VMs), offering full control over the environment and configuration. It is particularly suited for scenarios with specific infrastructure requirements or highly customized setups. :::note This method of deployment requires a solid understanding of infrastructure, networking, and application management. Consider evaluating your [deployment platform options](/self-managed/reference-architecture/reference-architecture.md) based on your familiarity and need. If you prefer a simpler and managed solution, [Camunda 8 SaaS](https://camunda.com/platform/) can significantly reduce maintenance efforts, allowing you to focus on your core business needs. ::: ## Key features - **Single application JAR**: Starting from Camunda 8.8, all core components (Zeebe, Tasklist, Operate, and Admin) are bundled into a single JAR file. This simplifies deployment by reducing the number of artifacts to manage. This bundled component is called Orchestration Cluster. - **Full control**: Users are responsible for all aspects of deployment, including installation, configuration, scaling, and maintenance. This offers maximum flexibility for custom environments. Other deployment options, such as containerized deployments or managed services, might offer more convenience and automation. However, VM based deployment gives you the flexibility to tailor the deployment to your exact needs, which can be beneficial for regulated or highly customized environments. For documentation on the Orchestration Cluster, Web Modeler and Console separation, refer to the [reference architecture overview](/self-managed/reference-architecture/reference-architecture.md#orchestration-cluster-vs-web-modeler-and-console). ## Reference implementations This section includes deployment reference architectures for manual setups: - [Amazon EC2 deployment](/self-managed/deployment/manual/cloud-providers/amazon/aws-ec2.md) - a standard production setup with support for high availability. ## Considerations - This overview page focuses on deploying the [Orchestration Cluster](/self-managed/reference-architecture/reference-architecture.md#orchestration-cluster), the single JAR composed of Admin, Operate, Tasklist, and Zeebe, as well as the connectors runtime. Web Modeler, Console, Optimize, and Management Identity deployments are not included. - General guidance and examples focuses on **unix** users, but can be adapted by Windows users with options like [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) or included `batch` files. ## Architecture ![Single JAR](./img/manual-single.jpg) This above diagram illustrates a single-machine deployment using the single JAR package. While simple and effective for lightweight setups, scaling to multiple machines requires careful planning. ### High Availability (HA) ![HA JAR](./img/manual-ha.jpg) For high availability, a minimum of three machines is recommended to ensure fault tolerance and enable master election in case of failures. Refer to the [clustering documentation](/components/zeebe/technical-concepts/clustering.md) to learn more about the raft protocol and clustering concepts. ### Components The Orchestration Cluster is packaged as a single JAR file and includes the following components: - [Zeebe](/components/zeebe/zeebe-overview.md) - [Operate](/components/operate/operate-introduction.md) - [Tasklist](/components/tasklist/introduction-to-tasklist.md) - [Admin](/self-managed/components/orchestration-cluster/admin/overview.md) It facilitates: 1. **gRPC communication**: For client workers. 2. **HTTP endpoints**: Used by the Orchestration Cluster REST API and Web UI. Both types of endpoints can be routed through a load balancer to maintain availability, ensuring that the system remains accessible even if a machine becomes unavailable. While using a load balancer is optional, it is recommended for enhanced availability and security. Alternatively, you can expose static machines, ports, and IPs directly. However, direct exposure is generally discouraged due to security concerns. Connectors expose additional HTTP(s) endpoints for handling incoming webhooks, which can also be routed through the same HTTP load balancer. The Orchestration Cluster relies on a configured [secondary storage](/reference/glossary.md#secondary-storage) backend for indexing and search. Depending on your deployment and configuration, this backend can use a document-store backend ([Elasticsearch/OpenSearch](/reference/glossary.md#elasticsearchopensearch)) or an [RDBMS](/reference/glossary.md#rdbms) for supported scenarios. :::note Secondary storage is configurable. For backend trade-offs and production guidance, see [secondary storage architecture](/self-managed/reference-architecture/reference-architecture.md#secondary-storage-architecture). For RDBMS configuration details, see [RDBMS configuration](/self-managed/concepts/databases/relational-db/configuration.md) and the glossary entry [RDBMS](/reference/glossary.md#rdbms). ::: Components within the Orchestration Cluster communicate seamlessly, particularly: - **Zeebe brokers** exchange data over gRPC endpoints for efficient inter-broker communication. ## Requirements Before implementing a reference architecture, review the requirements and guidance outlined below. We are differentiating between `Infrastructure` and `Application` requirements. ### Infrastructure Any of the following are just suggestions for the minimum viable setup, the sizing heavily depends on your use cases and usage. It is recommended to understand the documentation on [sizing your environment](/components/best-practices/architecture/sizing-your-environment.md) and run benchmarking to confirm your required needs. #### Minimum Requirements Per Host - Modern CPU: 2 cores - Memory: 4 GB RAM - Storage: 32 GB SSD (**1,000** IOPS recommended; avoid burstable disk types) Suggested instance types from cloud providers: - AWS: [m7i](https://aws.amazon.com/ec2/instance-types/m7i/) series - GCP: [n1](https://cloud.google.com/compute/docs/general-purpose-machines#n1_machines) series #### Networking - Stable and high-speed network connection - Configured firewall rules to allow necessary traffic: - **8080**: Web UI / REST endpoint (Orchestration Cluster) - **9090**: Connectors - **9600**: Management endpoint (Orchestration Cluster) - **26500**: gRPC endpoint. - **26501**: Gateway-to-broker communication. - **26502**: Inter-broker communication. - Load balancer for distributing traffic (if required) :::info Customizing ports Some ports can be overwritten and are not definitive, you may conduct the documentation of each component to see how it can be done, in case you want to use a different port. Or in our example `Connectors` and `Web UIs` overlap on 8080 due to which we moved connectors to a different port. ::: ### Application - Java Virtual Machine, see [supported environments](/reference/supported-environments.md) for version details. ### Database - Secondary storage backend (supported RDBMS or Elasticsearch/OpenSearch, depending on your architecture), see [supported environments](/reference/supported-environments.md) for version details. Our recommendation is to use an external managed offer as we will not go into detail on how to manage and maintain your database. --- ## Camunda 8 reference architectures Reference architectures provide a blueprint for designing and implementing scalable, robust, and adaptable systems. The reference architectures published here help enterprise architects, developers, and IT managers streamline deployments and improve system reliability. ## Overview Reference architectures are not a one-size-fits-all solution. Each organization has unique requirements and constraints that may require modifications to the provided blueprints. Use these reference architectures as a starting point for your Camunda 8 implementation. Adapt them to ensure they align with your goals and infrastructure. ### Target users - **Enterprise architects**: Design and plan the overall system structure. - **Developers**: Understand the components and their interactions. - **IT managers**: Ensure the system meets business requirements and is maintainable. ### Key benefits - **Accelerated deployment**: Predefined best practices simplify setup, reducing time and effort to deploy a reliable workflow automation solution. - **Consistency**: Standardized components and configurations reduce errors and simplify maintenance. - **Enhanced security**: Incorporates best practices for securing Camunda 8 deployments, including encryption, authentication, and access controls. ### Support considerations Deviations from the reference architecture are expected. However, changes can introduce additional complexity, making troubleshooting more difficult. When modifications are required, document them to support future maintenance and troubleshooting. Camunda publishes [supported environments](/reference/supported-environments.md) to help you navigate supported configurations. ## Architecture ### Orchestration Cluster vs Web Modeler and Console When designing a reference architecture, it's essential to understand the differences between Orchestration Cluster, Web Modeler, and Console Self-Managed. These components serve different purposes and include distinct elements. #### Orchestration Cluster ![Orchestration Cluster](./img/orchestration-cluster.jpg) The Orchestration Cluster is the core of Camunda. The following components are bundled into a single artifact: - [Zeebe](/components/zeebe/zeebe-overview.md): Highly scalable, cloud-native workflow engine that tracks the state of active process instances and drives business processes from start to finish. - [Operate](/components/operate/operate-introduction.md): Monitoring tool for visualizing and troubleshooting process instances running in Zeebe. - [Tasklist](/components/tasklist/introduction-to-tasklist.md): User interface for interacting with user tasks, including assigning and completing them. - [Admin](/self-managed/components/orchestration-cluster/admin/overview.md): Integrated authentication and authorization service for managing access to all Orchestration Cluster components and APIs. Tightly integrated with the Orchestration Cluster: - [Optimize](/components/optimize/what-is-optimize.md): Business intelligence tool for analyzing bottlenecks and examining improvements in automated processes. - [Connectors](/components/connectors/introduction.md): Reusable building blocks for easily connecting processes to external systems, applications, and data. This unified architecture ensures seamless communication, consistent state management, and reliable process execution across all components. #### Camunda Hub ![Camunda Hub](./img/management-cluster.jpg) Camunda Hub is designed to interact with multiple orchestration clusters: - [Camunda Hub](/components/hub/index.md): Manage organizational resources, analyze operations and business value, and deliver agentic processes at scale with Camunda Hub. - [Management Identity](/self-managed/components/management-identity/overview.md): Centralized authentication and authorization service. :::note Admin separation Camunda Hub uses a separate Management Identity deployment, distinct from the embedded Admin in the Orchestration Cluster. Optimize also requires Management Identity and cannot use the embedded Orchestration Cluster Admin. ::: :::tip New in Camunda 8.8 Starting with Camunda 8.8, Admin and Management Identity have been redesigned for clearer separation of concerns and improved flexibility. ::: #### Admin vs Management Identity The following table outlines the key differences between Admin and Management Identity: | Category | Admin | Management Identity | | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Scope | Provides access and permission management for all Orchestration Cluster components: Zeebe, Operate, Tasklist, and the Orchestration Cluster REST and gRPC API. | Manages access for platform components such as Web Modeler, Console, and Optimize. | | Unified access management | Authentication and authorizations are handled directly by the Orchestration Cluster across all components and APIs, eliminating any dependency on Management Identity. | Continues to manage access for Web Modeler, Console, and Optimize. | | Authentication | No authentication: No authentication required for API access. Form-based login in the UI. Users and groups are managed in Admin.Basic authentication: API access with Basic authentication. Form-based login in the UI. Users and groups are managed in Admin.OIDC: Any compatible identity provider (for example, Keycloak, Microsoft Entra ID, Okta). | Direct Keycloak integration (default).OIDC: Any compatible identity provider (for example, Keycloak, Microsoft Entra ID, Okta). | | Authorizations | Fine-grained [authorizations](/components/concepts/access-control/authorizations.md) provide consistent access control for process instances, tasks, and decisions across components and APIs. | | | Keycloak integration | Treated as a standard external identity provider integrated via OIDC, making it easier to use other providers without special integration. | Default Keycloak integration, with OIDC available for other providers. | | Tenant management | Tenants are directly managed within the Orchestration Cluster, allowing per-cluster tenant management. | No longer manages tenants for Orchestration Cluster components. Tenants apply only to Optimize. | For production environments, use an external [identity provider](/self-managed/deployment/helm/configure/authentication-and-authorization/external-oidc-provider.md) to connect both environments. ### Databases Databases can be deployed as part of the Camunda clusters, but using external databases or managed services offers several advantages: - **Flexibility**: Choose the database technology that fits your needs and existing infrastructure. See [supported environments](/reference/supported-environments.md#component-requirements). - **Scalability**: External databases can be scaled independently of Camunda components for better performance and resource management. - **Maintenance**: Database management, including upgrades, can be handled separately. - **Compliance**: External databases support specific governance and compliance requirements. While some guides explain how to deploy databases with Camunda, the recommendation is to manage databases externally for greater control and flexibility. ### Secondary storage architecture Choose the secondary storage architecture before you finalize a production deployment pattern. This decision applies across manual, containerized, and Kubernetes deployments. For production, use an external managed service or an externally operated database cluster whenever possible. Camunda does not manage database high availability, failover, backups, or lifecycle operations for you. #### Production topology baseline For a production Orchestration Cluster, use these baseline assumptions regardless of deployment method: - Run at least three brokers across three availability zones for high availability. - Use one secondary storage backend family for the Orchestration Cluster's web applications and APIs. - Keep the secondary storage backend in the same region as the Orchestration Cluster to reduce latency and failure domains. - Treat secondary storage as part of your production data layer, with its own backup, monitoring, and scaling plan. #### Compare Elasticsearch/OpenSearch and RDBMS Both backend families are supported for production in the right scenarios. Choose based on query patterns, operational preferences, and component requirements. | Topic | Elasticsearch/OpenSearch | RDBMS | | ---------------------------------- | ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | Best fit | Search-heavy, filter-heavy, analytics-heavy workloads | Teams that prefer relational database operations and moderate query workloads | | Write throughput profile | Higher write throughput in published comparison tests | Lower write throughput than Elasticsearch/OpenSearch in current published tests | | Read/query profile | Better suited for broad filtering, sorting, aggregations, and dashboard-style queries | Better suited for key-based access and moderate query workloads; broad filters and statistics queries need closer validation | | Optimize support | Required for Optimize | Optimize still requires Elasticsearch or OpenSearch | | Operational model | Adds a document-store technology to your stack | Reuses standard relational database tooling and operational practices | | Migration between backend families | Not supported as an in-place production migration | Not supported as an in-place production migration | Use benchmarking and workload validation before choosing a backend for production. For current published PostgreSQL results and caveats, see [RDBMS benchmark results](/self-managed/concepts/secondary-storage/rdbms-benchmark-results.md). For general capacity planning, see [sizing your environment](/components/best-practices/architecture/sizing-your-environment.md). #### Backend-specific guidance Choose Elasticsearch/OpenSearch when: - You expect heavy search, filtering, sorting, or dashboard-style query workloads. - You need Optimize and want to avoid running two secondary storage technologies. - You want the strongest current fit for large query-heavy environments. Choose RDBMS when: - You already operate relational databases at scale and want to align with existing tooling. - Your workloads are moderate and you can validate query performance with production-like data. - You prefer a relational secondary storage model for Orchestration Cluster APIs and web applications. If you deploy Optimize with RDBMS-based secondary storage, plan for both backends: RDBMS for the Orchestration Cluster and Elasticsearch or OpenSearch for Optimize. For supported versions and configuration details, see: - [Secondary storage overview](/self-managed/concepts/secondary-storage/index.md) - [Configure secondary storage](/self-managed/concepts/secondary-storage/configuring-secondary-storage.md) - [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) - [Supported environments](/reference/supported-environments.md) ### High availability (HA) High availability (HA) ensures that a system remains operational even when components fail. All components can run in HA mode, but Optimize requires special consideration: the importer/archiver must run on only one replica at a time. See the [Optimize configuration](/self-managed/components/optimize/configuration/system-configuration-platform-8.md#general-settings) for details. Consider regional and zonal placement of workloads. Use at least three zones in a region to maintain availability if a zone fails. For more information on how Zeebe handles fault tolerance, see the [Raft consensus chapter](/components/zeebe/technical-concepts/clustering.md#raft-consensus-and-replication-protocol). If running a single instance, implement [regular backups](/self-managed/operational-guides/backup-restore/backup-and-restore.md), as resilience will be limited. ## Available reference architectures :::note Documentation update in progress This documentation is being updated to provide clearer general guidance. Some Docker documentation may still point to older guides. ::: Choose a reference architecture based on factors such as your organization’s goals, infrastructure, and requirements. Use the following guides to plan your deployment: ### Kubernetes Kubernetes is a powerful orchestration platform for containerized applications. A Kubernetes reference architecture provides guidelines for setting up clusters, managing workloads, and ensuring high availability and scalability. - Ideal for organizations adopting containerization and microservices (see [Cloud Native Computing Foundation](https://www.cncf.io/)). - Suitable for dynamic scaling and high availability. - Best for teams experienced in managing containerized environments. - Offers high resilience but comes with a steep learning curve. See [Kubernetes deployment overview](/self-managed/reference-architecture/kubernetes.md). ### Containers Containers, such as Docker, provide a portable and consistent runtime environment. They simplify development, testing, and deployment across environments by encapsulating applications and their dependencies. - A middle ground between manual setups and Kubernetes that provides the benefits of containerization without the overhead of Kubernetes. - Containers can run on any system that supports the container runtime, ensuring consistency across development, testing, and production environments. - Each container runs in its own isolated environment, which helps prevent conflicts between applications and improves security. - Containers can be easily scaled up or down to handle varying workloads, providing flexibility in resource management. See [Camunda Docker images](/self-managed/deployment/docker/docker.md). ### Manual (bare metal/virtual machines) For organizations that prefer traditional infrastructure, bare metal or VM-based reference architectures offer a structured approach to system deployment. These architectures provide best practices for setting up physical servers or VMs, configuring networks, and managing storage using Infrastructure as Service cloud providers. They are suitable for environments where containerization or use of Kubernetes services may not be feasible. - Suitable for IaaS, bare metal, or traditional infrastructures. - Ideal for traditional setups needing highly customized security, strict data residency, or industry-specific regulatory compliance. - Applicable for high availability but requires more detailed planning. - Best for teams with expertise in managing physical servers or virtual machines. See [Manual deployment overview](/self-managed/reference-architecture/manual.md). ### Local development For local evaluation or development, use [Camunda 8 Run](/self-managed/quickstart/developer-quickstart/c8run.md), a simplified distribution for developers. --- ## Install Camunda 8 Self-Managed for production and advanced development setups Use this overview to choose an installation approach for Camunda 8 Self-Managed in production-ready environments (cloud or on-premises), and in advanced development setups that mirror production for CI/CD, integration testing, or shared clusters. ## Production installations :::note Starting in 8.9, Camunda 8 Run uses H2 as the default secondary storage out of the box. Elasticsearch remains a supported alternative in Camunda 8 Run. OpenSearch and RDBMS-based secondary storage are supported in Self-Managed deployments. See the [Camunda 8 Run configuration docs](../quickstart/developer-quickstart/c8run.md) for backend configuration details. ::: - [**Helm/Kubernetes**](/self-managed/deployment/helm/install/quick-install.md) (Recommended): We recommend using Kubernetes and Helm to run Camunda 8 Self-Managed in production. With the right configuration, Camunda 8 Self-Managed can be deployed on any Certified Kubernetes distribution (cloud or on-premises). We also officially support a variety of providers like [Red Hat OpenShift](../../self-managed/deployment/helm/cloud-providers/openshift/redhat-openshift.md) and [Amazon EKS](../../self-managed/deployment/helm/cloud-providers/amazon/amazon-eks/amazon-eks.md). - [**Docker**](/self-managed/deployment/docker/docker.md): Run Camunda components as [Docker images](https://hub.docker.com/u/camunda) in production on Linux systems. Windows and macOS are supported for development environments only. - [**Manual**](/self-managed/deployment/manual/install.md): Run each Java application on virtual machines or bare-metal servers with a supported Java Virtual Machine (JVM). This offers flexibility but requires manual configuration of component interactions. Use this approach only when necessary. Windows and macOS are supported for development environments only. :::info To run Camunda 8 in a local environment for development or evaluation purposes only, see [running locally](/self-managed/quickstart/developer-quickstart.md). ::: ## Production storage choices Choosing the right storage configuration is a critical step for production deployments: - Install Camunda 8 using one of the production deployment options above (Helm/Kubernetes recommended). - Review storage concepts: [primary and secondary storage overview](/components/concepts/concepts-overview.md) and [details on secondary storage](/self-managed/concepts/secondary-storage/index.md). - Provision your chosen storage backend(s) before enabling web applications such as Operate, Tasklist, or Optimize. Guidance: - Prefer managed services or operator-based infrastructure for production deployments to reduce operational overhead and improve reliability (for example, managed secondary storage services such as Elasticsearch/OpenSearch or managed RDBMS). Choose the backend that best fits your operational model, performance profile, and compliance requirements. - Benchmark and size your environment using [sizing your environment](/components/best-practices/architecture/sizing-your-environment.md) and the Camunda benchmark project referenced there. :::info For environment compatibility and supported versions, see [supported environments](/reference/supported-environments.md). ::: --- ## Upgrade Camunda components from 8.8 to 8.9 :::note This page is a work in progress for Camunda 8.9 and will be updated as upgrade requirements are finalized. ::: Review component-level actions that may be required when upgrading a Camunda 8 Self-Managed deployment from 8.8.x to 8.9.x. ## About Use this page with the deployment upgrade guide for your environment. Start with the [Upgrade Camunda 8 overview](/self-managed/upgrade/index.md), then apply any component-specific steps that match your setup. ## Connectors ### Default secret provider prefix change (breaking) Starting with Camunda 8.9, the environment-based secret provider uses `SECRET_` as the default prefix. Only environment variables starting with the configured prefix are available as connector secrets. - **Before 8.9**: With no prefix configured, all environment variables were accessible as connector secrets. - **From 8.9**: Only environment variables starting with `SECRET_` (or [your configured prefix](/self-managed/components/connectors/connectors-configuration.md#configure-a-custom-prefix)) are considered connector secrets. This is a **breaking change** for Self-Managed deployments that relied on unprefixed environment variables as secrets. Existing, unprefixed secrets will no longer resolve after upgrading. **Choose one of the following options:** - **Use the default secure prefix:** Update your connector secret environment variables to use the `SECRET_` prefix, and ensure the prefix is explicitly set: ``` camunda.connector.secretprovider.environment.prefix=SECRET_ ``` - **Configure a custom prefix:** Configure your own prefix via: - Java property: `camunda.connector.secretprovider.environment.prefix` - Environment variable: `CAMUNDA_CONNECTOR_SECRETPROVIDER_ENVIRONMENT_PREFIX` - **Restore the previous behavior (unsafe):** To allow all environment variables as secrets as in earlier versions, set the prefix to an empty value: ``` camunda.connector.secretprovider.environment.prefix= ``` This mode logs a warning, and Camunda does not recommend it for production environments. For full configuration details, see [connector secrets configuration](/self-managed/components/connectors/connectors-configuration.md#secrets). ## Optimize ### Exporter filters and Optimize upgrade safety With exporter-side filters in Camunda 8.9 and later, you can limit the records exported to Optimize. However, this can affect Optimize imports when Optimize uses the legacy Elasticsearch or OpenSearch exporter as its data source. Do not change exporter filters during upgrade or migration windows. While upgrading components or running importer-based migrations, keep the exporter configuration stable. Changing which records are exported (for example, by filtering variables or processes) during an Optimize import can affect its sequence-based import logic and lead to gaps or inconsistencies in reports. Treat enabling exporter filters on an existing cluster as a data-model change. When you start exporting only a subset of records for Optimize, the contents of the `zeebe-record-*` indices that Optimize reads effectively change. For production environments, follow the full re-import guidance in the Optimize system configuration to realign Optimize with the filtered data set rather than relying on incremental imports alone. For details on: - What exporter filters are and how to configure them, see: - [Elasticsearch exporter](../../components/orchestration-cluster/zeebe/exporters/elasticsearch-exporter.md) - [OpenSearch exporter](../../components/orchestration-cluster/zeebe/exporters/opensearch-exporter.md) - When to use exporter filters for Optimize and how to perform a safe re-import, see: - [Camunda 8 system configuration (Optimize)](../../components/optimize/configuration/system-configuration-platform-8.md) ## Exported records ### `PROCESS_INSTANCE_CREATION` records #### Variables no longer included in the `CREATED` event Previously, exported `zeebe-record-process-instance-creation*` records with intent `CREATED` contained variables. Starting with 8.9, these records no longer include variables. If you rely on variables from this event in a custom exporter or integration, use `zeebe-record-variable*` records with intent `CREATED` instead. These variable records are exported during process instance creation and contain the same variable data. ## Web Modeler ### Migrate `webapp` configuration In Web Modeler 8.9, the separate `webapp` component has been removed, and its functionality is now integrated into the `restapi` component. This means that the configuration values listed in the subsections below, which were previously expected by the `webapp`, now need to be configured for the `restapi`. :::note No action is required if you use the recommended Helm chart installation method without any custom configuration. The `restapi` is already configured correctly using the values provided for the [Helm chart parameters](/self-managed/deployment/helm/chart-parameters.md). ::: In the following cases, you need to update the application configuration for Web Modeler manually: - If you're using custom configuration settings for the `webapp`, for example, for feature flags or a proxy server, **migrate the settings to the `restapi` as described below**. - If you've completely replaced the `restapi` configuration using the Helm chart's [`webModeler.restapi.configuration` option](/self-managed/deployment/helm/configure/application-configs.md#componentnameconfiguration), **add the properties listed below to the `restapi` configuration**. You at least need to add the required options that don't have a default value. Otherwise, the application will fail to start. :::tip Consider switching to [`webModeler.restapi.extraConfiguration`](/self-managed/deployment/helm/configure/application-configs.md#componentnameextraconfiguration), with which you can add or override settings without replacing the default configuration entirely. ::: You can set the configuration values for the `restapi` either as environment variables or application properties. :::warning For custom settings that you do not migrate from the `webapp` to the `restapi`, Web Modeler will fall back to the default value (if applicable) after the upgrade to 8.9. This might cause unexpected behavior. Make sure to check which settings you have customized that need to be migrated. ::: #### OAuth2 configuration If you've set custom values for any of the settings below in the `webapp` configuration, add them to the `restapi` configuration instead. Refer to the [configuration page](/self-managed/components/hub/configuration/modeler-configuration.md#identity--keycloak) for details on these properties. :::note The name of the environment variable for the username claim has changed from `CAMUNDA_IDENTITY_USERNAMECLAIM` (version 8.8 / `webapp`) to `CAMUNDA_MODELER_OAUTH2_TOKEN_USERNAMECLAIM` (version 8.9 / `restapi`). ::: | Environment variable | Application property | Required? | Default value | | :------------------------------------------- | :-------------------------------------------------------- | :-------- | :------------ | | `OAUTH2_CLIENT_ID` | `camunda.modeler.oauth2.client-id` | yes | - | | `OAUTH2_CLIENT_FETCH_REQUEST_CREDENTIALS` | `camunda.modeler.oauth2.client.fetch-request-credentials` | no | - | | `CAMUNDA_MODELER_OAUTH2_TOKEN_USERNAMECLAIM` | `camunda.modeler.oauth2.token.username-claim` | no | `name` | #### WebSocket client configuration If you've set custom values for the WebSocket/Pusher client settings in the `webapp` configuration, add them to the `restapi` configuration instead. Refer to the [configuration page](/self-managed/components/hub/configuration/modeler-configuration.md#websocket) for details on these properties. | Environment variable | Application property | Required? | Default value | | :------------------------ | :---------------------------------------- | :-------- | :------------ | | `CLIENT_PUSHER_HOST` | `camunda.modeler.pusher.client.host` | yes | - | | `CLIENT_PUSHER_PORT` | `camunda.modeler.pusher.client.port` | no | `80` | | `CLIENT_PUSHER_PATH` | `camunda.modeler.pusher.client.path` | no | `/` | | `CLIENT_PUSHER_FORCE_TLS` | `camunda.modeler.pusher.client.force-tls` | no | `false` | #### Feature flags If you've set any of the following feature flags in the `webapp` configuration, add them to the `restapi` configuration instead. Refer to the [configuration page](/self-managed/components/hub/configuration/modeler-configuration.md#feature-flags) for details on these properties. | Environment variable | Application property | Required? | Default value | | :------------------------------ | :------------------------------------------------ | :-------- | :------------ | | `MARKETPLACE_ENABLED` | `camunda.marketplace.enabled` | no | `true` | | `FEATURE_AI_ENABLED` | `camunda.modeler.feature.ai-enabled` | no | `true` | | `ZEEBE_BPMN_DEPLOYMENT_ENABLED` | `camunda.modeler.feature.bpmn-deployment-enabled` | no | `true` | | `ZEEBE_DMN_DEPLOYMENT_ENABLED` | `camunda.modeler.feature.dmn-deployment-enabled` | no | `true` | | `PLAY_ENABLED` | `camunda.modeler.feature.play-enabled` | no | `true` | #### Resource import If you've enabled the resource import from private IP addresses in the `webapp` configuration, enable it in the `restapi` configuration instead. Refer to the [configuration page](/self-managed/components/hub/configuration/modeler-configuration.md#unstable-configuration-options) for details on this property. :::note The name of the environment variable has changed from `IMPORT_RESOURCES_ALLOW_PRIVATE_IP_ADDRESS` (version 8.8 / `webapp`) to `CAMUNDA_MODELER_RESOURCE_IMPORT_ALLOW_PRIVATE_IP_ADDRESS` (version 8.9 / `restapi`). ::: | Environment variable | Application property | Required? | Default value | | :--------------------------------------------------------- | :--------------------------------------------------------- | :-------- | :------------ | | `CAMUNDA_MODELER_RESOURCE_IMPORT_ALLOW_PRIVATE_IP_ADDRESS` | `camunda.modeler.resource-import.allow-private-ip-address` | no | `false` | #### Logging If you've changed the client log level for the `webapp`, configure it for the `restapi` instead. Refer to the [logging documentation](/self-managed/components/hub/configuration/logging.md#client-log-level) for more details. | Environment variable | Application property | Required? | Default value | | :------------------- | :------------------------------------- | :-------- | :------------ | | `LOG_LEVEL_CLIENT` | `camunda.modeler.client.logging.level` | no | `WARN` | #### Proxy server If you've configured an HTTP proxy server for the `webapp`, configure it for the `restapi` instead as described in the [proxy configuration troubleshooting guide](/self-managed/components/hub/troubleshooting/troubleshoot-proxy-configuration.md#resolution). #### SSL/TLS certificates If you've provided custom (self-signed) SSL certificates for the `webapp`, configure them for the `restapi` instead as described in the [SSL configuration guide](/self-managed/components/hub/configuration/modeler-ssl.md#optional-provide-a-custom-certificate). ### Logging framework Web Modeler `restapi` now uses [Apache Log4j 2](https://logging.apache.org/log4j/2.x/) instead of Logback. If you use a custom Logback configuration, migrate it to Log4j 2. See the [Log4j migration guide](https://logging.apache.org/log4j/2.x/migrate-from-logback.html) for details. ### Default logging configuration The default logging configuration is included here for reference. The default layout displays: - time (hours, minutes, seconds, milliseconds) - thread name - MDC context (if present) - log level - logger name - message #### Example pattern ```perl %d{HH:mm:ss.SSS} [%t] %notEmpty{[%X] }%-5level %logger{36} - %msg%n ``` For advanced configuration options, see [logging](/self-managed/components/hub/configuration/logging.md). ### Embedded web server Web Modeler now uses [Apache Tomcat](https://tomcat.apache.org/) as the embedded web server instead of [Undertow](https://undertow.io/). If you use a custom Undertow configuration, review and migrate it. Refer to: - [Spring Boot application properties](https://docs.spring.io/spring-boot/3.5/appendix/application-properties/index.html#appendix.application-properties.server) - [Spring embedded web server documentation](https://docs.spring.io/spring-boot/3.5/how-to/webserver.html#howto.webserver.configure) #### Remove or replace Undertow properties If your configuration includes: ```yaml server.undertow.* ``` These properties no longer apply when you switch to Tomcat, and must be removed or replaced. ##### Access log properties | Undertow property | Replacement or action | | :---------------------------------- | :------------------------------------------------------------- | | `server.undertow.accesslog.enabled` | Replace with `server.tomcat.accesslog.enabled` | | `server.undertow.accesslog.pattern` | Replace with `server.tomcat.accesslog.pattern` | | `server.undertow.accesslog.dir` | Replace with `server.tomcat.basedir` or a custom log directory | | `server.undertow.accesslog.prefix` | Tomcat uses `server.tomcat.accesslog.prefix` | | `server.undertow.accesslog.rotate` | Review rotation behavior; Tomcat rotates differently | ##### Threading properties | Undertow property | Tomcat equivalent or action | | -------------------------------- | ------------------------------------------------------------- | | `server.undertow.threads.io` | Use `server.tomcat.max-connections` or a connector customizer | | `server.undertow.threads.worker` | Replace with `server.tomcat.max-threads` | | `server.undertow.buffer-size` | No direct equivalent; Tomcat uses internal buffer sizes | | `server.undertow.direct-buffers` | No direct equivalent; Tomcat uses NIO buffers differently | ##### Builder and handler customizations The following properties do not have direct Tomcat equivalents and must be implemented programmatically or removed if not required: ```yaml server.undertow.eager-filter-init server.undertow.allow-encoded-slash server.undertow.decode-url server.undertow.max-http-post-size server.undertow.no-request-timeout ``` Implement customizations using: - `TomcatServletWebServerFactory` - `TomcatConnectorCustomizer` More information on handling large payloads can be found [here](#large-payload-handling). #### Review Tomcat properties If you already define: ```yaml server.tomcat.* ``` Review these settings for correctness. ##### Access logs | Tomcat property | Description | | :---------------------------------- | :------------------------ | | `server.tomcat.accesslog.enabled` | Enable Tomcat access logs | | `server.tomcat.accesslog.directory` | Log directory | | `server.tomcat.accesslog.pattern` | Log format | | `server.tomcat.accesslog.prefix` | File name prefix | | `server.tomcat.accesslog.suffix` | File name suffix | :::note Tomcat rotates differently than Undertow. Verify log retention and file sizes. ::: ##### Performance and threading | Property | Description | | --------------------------------- | ----------------------------------- | | `server.tomcat.max-connections` | Maximum concurrent connections | | `server.tomcat.accept-count` | Queue size for incoming connections | | `server.tomcat.max-threads` | Maximum request-processing threads | | `server.tomcat.min-spare-threads` | Minimum idle threads | Tomcat uses a standard blocking I/O thread model, unlike Undertow’s worker and IO threads. ##### MBean registry Tomcat disables the MBean registry by default: Enable it if you require Java Management Extensions (JMX) metrics: ```yaml server.tomcat.mbeanregistry.enabled: true ``` ##### Additional Tomcat settings | Property | Notes | | ---------------------------------- | ------------------------------------------------ | | `server.tomcat.connection-timeout` | Review default values; they differ from Undertow | #### Review behavioral differences When migrating configuration, also review behavioral differences between Undertow and Tomcat. ##### Large payload handling Undertow used `server.undertow.max-http-post-size`. Tomcat uses: ```yaml spring.servlet.multipart.max-file-size spring.servlet.multipart.max-request-size server.tomcat.max-swallow-size ``` Review these settings if your deployment handles large uploads. --- ## Elasticsearch 7 to 8 compatibility changes for Camunda Learn about changes and compatibility considerations when upgrading from Elasticsearch 7 to Elasticsearch 8 in a Camunda 8 environment. ## About Before upgrading Elasticsearch, review the Elasticsearch breaking changes and confirm compatibility with Camunda-supported versions. When upgrading Elasticsearch, follow the official [Elasticsearch breaking changes guide](https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking-changes.html). Also review the Camunda 8 [supported environments](/reference/supported-environments.md) to confirm the supported Elasticsearch versions. ## Elasticsearch Curator Camunda 8 is not compatible with Elasticsearch Curator for Elasticsearch 8. Curator is commonly used to manage [data retention](/components/saas/data-retention.md) by deleting indices. When upgrading to Elasticsearch 8, replace Curator with [Index Lifecycle Management (ILM)](https://www.elastic.co/guide/en/elasticsearch/reference/current/index-lifecycle-management.html). Disable Curator only after the upgrade to Elasticsearch 8 is complete. Disabling Curator earlier can result in indices without an ILM policy, which prevents automatic deletion. For details on configuring ILM for Zeebe indices, see [Elasticsearch exporter retention settings](/self-managed/components/orchestration-cluster/zeebe/exporters/elasticsearch-exporter.md#retention). :::tip You can replace Curator with ILM while still running Elasticsearch 7. This can simplify incremental upgrades from Elasticsearch 7 to 8. ::: --- ## Upgrade Camunda components :::note This section is a work in progress for Camunda 8.9 and will be updated as upgrade requirements are finalized. ::: Upgrade individual Camunda 8 components when moving from version 8.8 to 8.9. ## About Component-level steps may be required depending on which Camunda components you use and how your environment is configured. :::caution Component-level changes are part of the overall upgrade process. Follow the upgrade path for your deployment method and apply component-level steps when required by your configuration. See [Upgrade Camunda 8](../index.md). ::: ## When component-level changes apply Additional steps may be required if your deployment: - Uses non-default or customized component configuration - Manages components outside the standard Helm or Manual upgrade flow - Is affected by component-specific changes introduced in Camunda 8.9. :::info For release context, see: - [What’s new in Camunda 8.9](/reference/announcements-release-notes/890/whats-new-in-89.md) - [8.9 release notes](/reference/announcements-release-notes/890/890-release-notes.md). ::: ## Upgrade components from 8.8 to 8.9 This section links to component-level guidance for the 8.8 to 8.9 upgrade. [Upgrade Camunda components from 8.8 to 8.9](880-to-890.md) ## Component-specific guidance Some components require additional, targeted steps depending on how they are deployed. ### Database Follow database-specific guidance if your Camunda upgrade includes changes to the underlying data store. ### Identity provider Follow identity-provider–specific guidance if your setup requires separate upgrade or compatibility steps. --- ## Keycloak compatibility considerations for Camunda Review Keycloak upgrade considerations and compatibility requirements when used with Camunda. ## About When upgrading Keycloak in a Camunda Self-Managed environment, follow the official [Keycloak upgrade guide](https://www.keycloak.org/docs/latest/upgrading/index.html). Before upgrading, review the Camunda [supported environments](/reference/supported-environments.md#camunda-8-self-managed) to ensure the Keycloak version you plan to use is compatible with your Camunda release. Keycloak upgrades can affect how users and permissions are resolved in Camunda components such as Optimize and Web Modeler. Review the following constraint carefully before proceeding. :::danger Preserve the existing Keycloak database When upgrading Keycloak, ensure that you reuse the existing Keycloak database. **Do not** upgrade by creating a new Keycloak instance and re-importing users from external identity providers (for example, LDAP). Doing so will generate new internal Keycloak IDs, which can prevent users from accessing existing data such as Optimize collections and [Web Modeler projects](/self-managed/components/hub/troubleshooting/troubleshoot-missing-data.md). ::: --- ## Upgrade Camunda 8.8 to 8.9 using Helm Upgrade a Helm-managed Camunda 8 Self-Managed deployment from version 8.8 to 8.9. :::info Upgrade procedure All Camunda 8 upgrades must follow the required upgrade procedure: upgrade one minor version at a time and do not skip minors. For best stability and fix coverage, use the latest available patch in each minor before and after the minor upgrade. See [version compatibility checks](../../components/orchestration-cluster/core-settings/concepts/version-compatibility.md#required-upgrade-procedure). ::: :::warning Plan your move to the Helm v4 CLI before upgrading to 8.10 Camunda 8.9 (chart 14.x) is the last minor that supports the Helm v3 CLI. Camunda 8.10 (chart 15.x) requires the Helm v4 CLI. Chart 14.x also supports Helm v4, so switch your tooling to the Helm v4 CLI while running 8.9 to be ready before you upgrade to 8.10. No release-state migration is required when switching CLIs. See [Move from the Helm v3 CLI to v4](/self-managed/deployment/helm/operational-tasks/moving-helm-v3-to-v4.md). ::: ## Prerequisites Before upgrading, ensure you have met the following prerequisites: | Prerequisite | Description | | :----------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Confirm `8.8.x` baseline | Confirm your deployment is running an `8.8.x` version before upgrading to 8.9. If you are upgrading from a version earlier than 8.8, see [Upgrading from an earlier version](/self-managed/upgrade/index.md#upgrading-from-an-earlier-version). For best stability and fix coverage, use the latest available `8.8.x` patch. | | Confirm upgrade eligibility | Review [Prepare for upgrade](/self-managed/upgrade/prepare-for-upgrade.md) to confirm upgrade eligibility and complete any required pre-upgrade actions for 8.9. | | Confirm Helm chart support | Review the Camunda Helm chart [version matrix](https://helm.camunda.io/camunda-platform/version-matrix/) and confirm:The Helm chart version deploying 8.9 is supported for your Kubernetes version.Your Helm client version is supported for that chart. | | Backups | Create and verify backups for your Camunda data. See [Backup and restore](../../../self-managed/operational-guides/backup-restore/backup-and-restore.md). | | Check your `values.yaml` | Review your existing `values.yaml` for deprecated secret keys that must be migrated, and for any Elasticsearch, Ingress, or authentication settings that may need updating. | | Migrate deprecated secret keys | If your `values.yaml` still uses deprecated secret keys from 8.8 (for example, `global.license.key`, `*.existingSecret` at the old paths), migrate them to the new `*.secret.existingSecret` pattern. See [Configuration changes](#deprecated-secret-keys-removed) below. | | Set secondary storage type | Camunda 8.9 no longer defaults to Elasticsearch. Explicitly setting `orchestration.data.secondaryStorage.type` is recommended. See [Secondary storage type](#secondary-storage-type-is-now-required). | | Test upgrade | Test the upgrade in a non-production environment using a copy of your production configuration. | ## Create your 8.9 values file Use your existing 8.8 configuration as the starting point for the upgrade. 1. Copy your current `values.yaml` and name the file `values-8.9.yaml`. 1. Download the default values for the Camunda 8.9 Helm chart: ```bash helm repo update helm show values camunda/camunda-platform --version > values-8.9-default.yaml ``` To identify the latest available chart version, run: ```bash helm search repo camunda/camunda-platform --versions ``` 1. Compare `values-8.9.yaml` with `values-8.9-default.yaml` to identify: - new configuration options, - changed defaults, and - deprecated or removed settings. For each key you have set in `values-8.9.yaml`, search for it in `values-8.9-default.yaml` and check whether: - The key still exists (if not, it was removed or renamed). - The key moved under a new parent (for example, `existingSecret` → `secret.existingSecret`). - The structure changed (for example, from a map to an array, or from an object to a flat string). - A sibling key that previously had a default now has an empty default (for example, `existingSecretKey` changing from a meaningful default to `""`), meaning you must now set it explicitly. 1. Update `values-8.9.yaml` using the configuration tables below before running `helm upgrade`. ## Update your values file to 8.9 Use the following sections to update `values-8.9.yaml` for the 8.8 to 8.9 upgrade. ### Configuration changes The following sections list configuration keys that changed between chart 13.x (8.8) and 14.x (8.9). If your `values.yaml` uses any of these keys, update them before upgrading. #### Deprecated secret keys removed Secret configuration keys that were deprecated in 8.8 are removed in 8.9. If your `values.yaml` still references any of the old paths, update them to use the new `*.secret.*` pattern. For the full list of affected keys, see [Helm chart: Deprecated secret keys removed](/reference/announcements-release-notes/890/890-announcements.md#helm-chart-deprecated-secret-keys-removed). The following table summarizes the pattern change by component. - In 8.8, many `existingSecretKey` fields had built-in defaults (for example, `identity-admin-client-token` for admin, `identity-optimize-client-token` for Optimize). - In 8.9, these defaults are empty (`""`). When migrating to the new `*.secret.existingSecret` pattern, you must also set `*.secret.existingSecretKey` to the key name within your Kubernetes Secret. | **Component** | **13.x/8.8 key pattern** | **14.x/8.9 key pattern** | | :------------------- | :--------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- | | License | `global.license.key`, `global.license.existingSecret` | `global.license.secret.inlineSecret`, `global.license.secret.existingSecret` | | Elasticsearch auth | `global.elasticsearch.auth.password`, `global.elasticsearch.auth.existingSecret` | `global.elasticsearch.auth.secret.inlineSecret`, `global.elasticsearch.auth.secret.existingSecret` | | OpenSearch auth | `global.opensearch.auth.password`, `global.opensearch.auth.existingSecret` | `global.opensearch.auth.secret.inlineSecret`, `global.opensearch.auth.secret.existingSecret` | | Identity auth | `global.identity.auth.{admin,identity,optimize,core}.existingSecret` | `global.identity.auth.{admin,identity,optimize,core}.secret.existingSecret` | | Document Store (AWS) | `global.documentStore.type.aws.existingSecret` | `global.documentStore.type.aws.accessKeyId.secret.existingSecret`, `global.documentStore.type.aws.secretAccessKey.secret.existingSecret` | | Document Store (GCP) | `global.documentStore.type.gcp.existingSecret` | `global.documentStore.type.gcp.secret.existingSecret` | | Identity user | `identity.firstUser.password`, `identity.firstUser.existingSecret` | `identity.firstUser.secret.inlineSecret`, `identity.firstUser.secret.existingSecret` | | Identity DB | `identity.externalDatabase.password`, `identity.externalDatabase.existingSecret` | `identity.externalDatabase.secret.inlineSecret`, `identity.externalDatabase.secret.existingSecret` | | Web Modeler DB | `webModeler.restapi.externalDatabase.password`, `webModeler.restapi.externalDatabase.existingSecret` | `webModeler.restapi.externalDatabase.secret.inlineSecret`, `webModeler.restapi.externalDatabase.secret.existingSecret` | | Web Modeler mail | `webModeler.restapi.mail.smtpPassword` | `webModeler.restapi.mail.secret.inlineSecret` | | Connectors | `connectors.security.authentication.oidc.existingSecret` | `connectors.security.authentication.oidc.secret.existingSecret` | | Orchestration | `orchestration.security.authentication.oidc.existingSecret` | `orchestration.security.authentication.oidc.secret.existingSecret` | Additionally, `global.secrets.*` (including `autoGenerated`, `name`, `annotations`) has been removed entirely. All secrets must now be explicitly provided. See [Secret management](/self-managed/deployment/helm/configure/secret-management.md). :::note Console secret removed `global.identity.auth.console.existingSecret` no longer has any effect in 8.9 as the Console component no longer uses an Identity client secret. Leaving it in your values file will not cause errors, but you can safely remove it. ::: #### Other key changes | **13.x/8.8 values** | **14.x/8.9 values** | | :--------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `webModeler.restapi.externalDatabase.user` | Renamed to `webModeler.restapi.externalDatabase.username`. | | `webModeler.webapp.*` | You can remove all keys under `webModeler.webapp`. The `webapp` component no longer exists in 8.9; its functionality has been integrated into `restapi`. See [migrate `webapp` configuration](/self-managed/upgrade/components/880-to-890.md#migrate-webapp-configuration) for details. | | `.extraConfiguration: {}` (object) | Changed to an array: `.extraConfiguration: []`. Affects all components that support this key (connectors, orchestration, identity, console, optimize, webModeler). See [migrate extraConfiguration from 8.8 to 8.9](/self-managed/deployment/helm/configure/application-configs.md#migrate-extraconfiguration-from-88-to-89). | | `global.elasticsearch.enabled` | `optimize.database.elasticsearch.enabled`; set `orchestration.data.secondaryStorage.type` to `elasticsearch`. | | `global.elasticsearch.external` | `optimize.database.elasticsearch.external`; set `orchestration.data.secondaryStorage.type` to `elasticsearch`. | | `global.elasticsearch.tls.enabled` | `optimize.database.elasticsearch.tls.enabled`; fill in `orchestration.data.secondaryStorage.elasticsearch.tls.secret`. | | `global.elasticsearch.tls.secret.existingSecret` | `optimize.database.elasticsearch.tls.secret.existingSecret` and `orchestration.data.secondaryStorage.elasticsearch.tls.secret.existingSecret` | | `global.elasticsearch.tls.secret.existingSecretKey` | `optimize.database.elasticsearch.tls.secret.existingSecretKey` and `orchestration.data.secondaryStorage.elasticsearch.tls.secret.existingSecretKey` | | `global.elasticsearch.auth.username` | `optimize.database.elasticsearch.auth.username` and `orchestration.data.secondaryStorage.elasticsearch.auth.username` | | `global.elasticsearch.auth.secret.inlineSecret` | `optimize.database.elasticsearch.auth.secret.inlineSecret` and `orchestration.data.secondaryStorage.elasticsearch.auth.secret.inlineSecret` | | `global.elasticsearch.auth.secret.existingSecret` | `optimize.database.elasticsearch.auth.secret.existingSecret` and `orchestration.data.secondaryStorage.elasticsearch.auth.secret.existingSecret` | | `global.elasticsearch.auth.secret.existingSecretKey` | `optimize.database.elasticsearch.auth.secret.existingSecretKey` and `orchestration.data.secondaryStorage.elasticsearch.auth.secret.existingSecretKey` | | `global.elasticsearch.url.protocol` | `optimize.database.elasticsearch.url.protocol` and `orchestration.data.secondaryStorage.elasticsearch.url` | | `global.elasticsearch.url.host` | `optimize.database.elasticsearch.url.host` and `orchestration.data.secondaryStorage.elasticsearch.url` | | `global.elasticsearch.url.port` | `optimize.database.elasticsearch.url.port` and `orchestration.data.secondaryStorage.elasticsearch.url` | | `global.elasticsearch.clusterName` | Removed. No direct equivalent exists in component-specific configuration. Use `orchestration.extraConfiguration` to configure the app directly. | | `global.elasticsearch.prefix` | `optimize.database.elasticsearch.prefix`; no direct equivalent exists in `orchestration`. Use `orchestration.extraConfiguration` to configure the app directly. | | `global.opensearch.enabled` | `optimize.database.opensearch.enabled`; set `orchestration.data.secondaryStorage.type` to `opensearch`. | | `global.opensearch.aws.enabled` | `optimize.database.opensearch.aws.enabled` | | `global.opensearch.tls.enabled` | `optimize.database.opensearch.tls.enabled`; fill in `orchestration.data.secondaryStorage.opensearch.tls.secret`. | | `global.opensearch.tls.secret.existingSecret` | `optimize.database.opensearch.tls.secret.existingSecret` and `orchestration.data.secondaryStorage.opensearch.tls.secret.existingSecret` | | `global.opensearch.tls.secret.existingSecretKey` | `optimize.database.opensearch.tls.secret.existingSecretKey` and `orchestration.data.secondaryStorage.opensearch.tls.secret.existingSecretKey` | | `global.opensearch.auth.username` | `optimize.database.opensearch.auth.username` and `orchestration.data.secondaryStorage.opensearch.auth.username` | | `global.opensearch.auth.secret.inlineSecret` | `optimize.database.opensearch.auth.secret.inlineSecret` and `orchestration.data.secondaryStorage.opensearch.auth.secret.inlineSecret` | | `global.opensearch.auth.secret.existingSecret` | `optimize.database.opensearch.auth.secret.existingSecret` and `orchestration.data.secondaryStorage.opensearch.auth.secret.existingSecret` | | `global.opensearch.auth.secret.existingSecretKey` | `optimize.database.opensearch.auth.secret.existingSecretKey` and `orchestration.data.secondaryStorage.opensearch.auth.secret.existingSecretKey` | | `global.opensearch.url.protocol` | `optimize.database.opensearch.url.protocol` and `orchestration.data.secondaryStorage.opensearch.url` | | `global.opensearch.url.host` | `optimize.database.opensearch.url.host` and `orchestration.data.secondaryStorage.opensearch.url` | | `global.opensearch.url.port` | `optimize.database.opensearch.url.port` and `orchestration.data.secondaryStorage.opensearch.url` | | `global.opensearch.clusterName` | Removed. No direct equivalent exists in component-specific configuration. Use `orchestration.extraConfiguration` to configure the app directly. | | `global.opensearch.prefix` | `optimize.database.opensearch.prefix`; no direct equivalent exists in `orchestration`. Use `orchestration.extraConfiguration` to configure the app directly. | #### Elasticsearch is now disabled by default The Elasticsearch subchart is no longer enabled by default. If you are using Elasticsearch, you must explicitly enable it in your `values-8.9.yaml`: ```yaml global: elasticsearch: enabled: true elasticsearch: enabled: true ``` #### Secondary storage type is now required Camunda 8.9 no longer defaults to a secondary storage type. Explicitly setting `orchestration.data.secondaryStorage.type` in your `values.yaml` is recommended: ```yaml orchestration: data: secondaryStorage: type: elasticsearch # or "opensearch" or "rdbms" ``` The chart can auto-detect the type from `global.elasticsearch.enabled` or `global.opensearch.enabled` if you don't set it explicitly, but Helm will fail with a validation error if no secondary storage is configured at all. Alternatively, if you do not need secondary storage, set `global.noSecondaryStorage: true` to run in engine-only mode. This disables all secondary-storage-dependent features and components and requires OIDC authentication. #### Default REST port changed to 8080 The Orchestration Cluster's default HTTP port changed from 8090 to 8080. If you have hardcoded port 8090 in network policies, Ingress rules, health check probes, or service mesh configuration, update these references to 8080 or explicitly set `orchestration.service.httpPort: 8090` in your `values.yaml`. #### TLS secret pattern deprecated The legacy TLS secret configuration using `*.tls.existingSecret` is deprecated. Migrate to `*.tls.secret.existingSecret`. The legacy keys still work in 8.9 but will be removed in a future version. Affected paths: - `global.elasticsearch.tls.existingSecret` → `global.elasticsearch.tls.secret.existingSecret` - `global.opensearch.tls.existingSecret` → `global.opensearch.tls.secret.existingSecret` - `console.tls.existingSecret` → `console.tls.secret.existingSecret` Additionally, the JKS nesting level has been flattened: - `global.elasticsearch.tls.jks.secret.existingSecret` → `global.elasticsearch.tls.secret.existingSecret` - `global.opensearch.tls.jks.secret.existingSecret` → `global.opensearch.tls.secret.existingSecret` #### Bitnami subcharts deprecated The Bitnami-based subcharts (`identityPostgresql`, `identityKeycloak`, `webModelerPostgresql`, `elasticsearch`) are deprecated in 8.9 and will be removed in 8.10. If any are enabled, Helm prints a deprecation warning. Plan migration to externally managed services before upgrading to 8.10. #### `global.elasticsearch` and `global.opensearch` deprecated The `global.elasticsearch.*` and `global.opensearch.*` configuration trees are deprecated in 8.9 and will be removed in 8.10. Migrate to the new component-specific configuration: | **8.8 key path** | **8.9 replacement (Orchestration)** | **8.9 replacement (Optimize)** | | :----------------------- | :---------------------------------------------------- | :---------------------------------- | | `global.elasticsearch.*` | `orchestration.data.secondaryStorage.elasticsearch.*` | `optimize.database.elasticsearch.*` | | `global.opensearch.*` | `orchestration.data.secondaryStorage.opensearch.*` | `optimize.database.opensearch.*` | The legacy keys still work in 8.9 with deprecation warnings. Existing deployments will continue to function without changes. #### Identity profile renamed to admin The orchestration profile key `orchestration.profiles.identity` is deprecated and renamed to `orchestration.profiles.admin`. The chart automatically migrates the old key and prints a deprecation warning. Update your `values-8.9.yaml` to use `orchestration.profiles.admin` directly. #### Web Modeler `webapp` component removed Web Modeler's `webapp` component has been removed in 8.9, and its functionality is now integrated into the `restapi` component. You should remove all configuration values under `webModeler.webapp.*` from your `values.yaml`, as they no longer serve any purpose and will be ignored. :::info Depending on your setup, you might need to migrate custom configuration settings from the `webapp` to the `restapi`. See the [component upgrade guide](/self-managed/upgrade/components/880-to-890.md#migrate-webapp-configuration) for details. ::: #### Keycloak auth secret pattern deprecated The legacy Keycloak auth secret configuration using `global.identity.keycloak.auth.existingSecret` and `global.identity.keycloak.auth.existingSecretKey` is deprecated. Migrate to the new standard secret pattern: - `global.identity.keycloak.auth.existingSecret` → `global.identity.keycloak.auth.secret.existingSecret` - `global.identity.keycloak.auth.existingSecretKey` → `global.identity.keycloak.auth.secret.existingSecretKey` The legacy keys still work in 8.9 via normalizers. See [Secret management](/self-managed/deployment/helm/configure/secret-management.md). ## Example: upgrade `values.yaml` from 8.8 to 8.9 This section shows a before-and-after example of a Helm `values.yaml` migrated from Camunda 8.8 to 8.9, covering the most common upgrade scenarios.
View example values files ```yaml # ========================= # Camunda 8.8 values.yaml # ========================= global: # Secret auto-generation (removed in 8.9) secrets: autoGenerated: true # License using deprecated key license: existingSecret: camunda-license existingSecretKey: license-key # Elasticsearch auth using deprecated keys elasticsearch: # enabled: true was the default in 8.8 auth: existingSecret: es-credentials existingSecretKey: password tls: existingSecret: es-tls-secret identity: auth: admin: existingSecret: admin-credentials existingSecretKey: password keycloak: auth: existingSecret: keycloak-credentials existingSecretKey: admin-password identity: firstUser: existingSecret: identity-first-user existingSecretKey: password externalDatabase: existingSecret: identity-db-credentials existingSecretPasswordKey: password webModeler: webapp: replicas: 2 resources: requests: cpu: 200m memory: 256Mi restapi: externalDatabase: user: modeler-user existingSecret: modeler-db-credentials existingSecretPasswordKey: password mail: smtpPassword: connectors: security: authentication: oidc: existingSecret: connectors-oidc existingSecretKey: client-secret extraConfiguration: customKey: customValue orchestration: profiles: identity: true ``` ```yaml # ========================= # Camunda 8.9 values-8.9.yaml # ========================= global: # global.secrets removed — all secrets must be explicit # License using new secret pattern license: secret: existingSecret: camunda-license existingSecretKey: license-key # Elasticsearch: must be explicitly enabled + auth via new pattern elasticsearch: enabled: true auth: secret: existingSecret: es-credentials existingSecretKey: password tls: secret: existingSecret: es-tls-secret # Was global.elasticsearch.tls.existingSecret identity: auth: admin: secret: existingSecret: admin-credentials existingSecretKey: password keycloak: auth: secret: existingSecret: keycloak-credentials existingSecretKey: admin-password # Elasticsearch subchart must be explicitly enabled elasticsearch: enabled: true orchestration: data: secondaryStorage: type: elasticsearch # Recommended — no longer defaults profiles: admin: true # Renamed from "identity" identity: firstUser: secret: existingSecret: identity-first-user existingSecretKey: password externalDatabase: secret: existingSecret: identity-db-credentials existingSecretKey: password webModeler: # webModeler.webapp removed — restapi now serves the web application restapi: externalDatabase: username: modeler-user # Renamed from "user" secret: existingSecret: modeler-db-credentials existingSecretKey: password mail: secret: inlineSecret: # Or use existingSecret connectors: security: authentication: oidc: secret: existingSecret: connectors-oidc existingSecretKey: client-secret extraConfiguration: # Changed from map to ordered list (all components) - file: customKey content: customValue ```
## Prepare and run the upgrade Add any upgrade-specific configuration required for the 8.9 upgrade. ### Migrate extraConfiguration format In Camunda 8.9, the `.extraConfiguration` Helm value changed from a **map** to an **ordered list**. If your `values.yaml` uses `extraConfiguration` for any component, you must convert it to the new format before upgrading. See [Migrate extraConfiguration from 8.8 to 8.9](/self-managed/deployment/helm/configure/application-configs.md#migrate-extraconfiguration-from-88-to-89) for detailed instructions and examples. ## Run the Helm upgrade After updating your values file with the required 8.9 configuration changes, run the Helm upgrade: ```bash helm repo update helm upgrade camunda camunda/camunda-platform \ --namespace \ -f values-8.9.yaml ``` ## Monitor and validate the upgrade After triggering the Helm upgrade, monitor the rollout to ensure all pods return to a healthy state. :::note Unlike the 8.7 to 8.8 upgrade, the 8.8 to 8.9 upgrade does not run migration jobs. Only standard pod rollouts occur. ::: ### Watch pod rollout progress Watch the pods being upgraded via: ```bash kubectl -n get pods -w ``` You should see pods terminating and restarting with updated images. ### Inspect logs (if required) If a pod fails to start, inspect its logs: ```bash kubectl -n logs --previous ``` ### Validate the upgrade 1. Confirm all pods are healthy: ```bash kubectl -n get pods ``` 1. Confirm all pods are running `8.9.x` images: ```bash kubectl -n get pods -o jsonpath="{range .items[*]}{.metadata.name}{':\t'}{range .spec.containers[*]}{.image}{'\n'}{end}{end}" ``` 1. Verify access to Camunda components. For example: ```bash https:////operate https:////tasklist https:////identity ``` 1. Verify authentication and authorization behavior, based on your configuration. 1. Verify workers. Confirm your workers can still poll and complete jobs (for example, by running a known process end-to-end and checking worker logs/metrics). ## Troubleshooting ### Upgrade failed due to missing secrets If your upgrade fails due to missing credentials (often when migrating from chart-managed secret generation to Kubernetes secrets), see [Extract plaintext values and reference them as Kubernetes Secrets](/self-managed/deployment/helm/configure/secret-management.md#extract-plaintext-values-and-reference-them-as-kubernetes-secrets). The [Bitnami](https://github.com/bitnami/charts/tree/master/bitnami/common) library chart used by Camunda can block upgrades when credentials were previously generated and persisted (for example, in PVCs). This is expected behavior to prevent secret regeneration. For additional context, see . The following is an example of the error you might see: ```shell Error: UPGRADE FAILED: execution error at (camunda-platform/charts/identity/templates/tasklist-secret.yaml:10:22): PASSWORDS ERROR: You must provide your current passwords when upgrading the release. Note that even after reinstallation, old credentials may be needed as they may be kept in persistent volume claims. Further information can be obtained at https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues/#credential-errors-while-upgrading-chart-releases 'global.identity.auth.tasklist.existingSecret' must not be empty, please add '--set global.identity.auth.tasklist.existingSecret=$TASKLIST_SECRET' to the command. To get the current value: export TASKLIST_SECRET=$(kubectl get secret --namespace "camunda" "camunda-platform-test-tasklist-identity-secret" -o jsonpath="{.data.tasklist-secret}" | base64 --decode) ``` To complete the upgrade, migrate plaintext values to Kubernetes secrets and reference them from your values file as described in the [secret management guide](/self-managed/deployment/helm/configure/secret-management.md#extract-plaintext-values-and-reference-them-as-kubernetes-secrets). ### Upgrade failed with secondary storage validation error If Helm fails with a validation error about secondary storage type, the chart could not auto-detect which secondary storage to use. This happens when `orchestration.data.secondaryStorage.type` is not set and none of `global.elasticsearch.enabled`, `global.opensearch.enabled`, or `orchestration.exporters.rdbms.enabled` are true. Add the required configuration to your values file: ```yaml orchestration: data: secondaryStorage: type: elasticsearch # or "opensearch" or "rdbms" ``` Alternatively, if you do not need secondary storage, set `global.noSecondaryStorage: true` to run in engine-only mode. --- ## Upgrade Helm chart Upgrade a Camunda 8 Self-Managed deployment installation using the official Camunda Helm charts. :::caution earlier versions If you are upgrading from a version earlier than 8.8, see [upgrading from an earlier version](/self-managed/upgrade/index.md#upgrading-from-an-earlier-version). ::: :::warning Upgrading to Camunda 8.10 Camunda 8.10 (chart 15.x) requires the Helm CLI v4. Switch to the Helm v4 CLI before you run `helm upgrade`. No release-state migration is required. See [Move from the Helm v3 CLI to v4](/self-managed/deployment/helm/operational-tasks/moving-helm-v3-to-v4.md). ::: ## Upgrade guides Use the following guides to upgrade a Camunda 8 Self-Managed deployment installation using the official Camunda Helm charts. ### Helm chart version The Camunda Helm chart version is independent from the Camunda application version. Use the Helm chart [version matrix](https://helm.camunda.io/camunda-platform/version-matrix/) to identify the Helm chart version that deploys your Camunda application version. You can also list available chart versions using the Helm CLI: ```bash helm repo update helm search repo camunda/camunda-platform --versions ``` ## Upgrade notes ### Bitnami Docker repository migration On August 28, 2025, Bitnami migrated its container images from [bitnami](https://hub.docker.com/u/bitnami) to [bitnamilegacy](https://hub.docker.com/u/bitnamilegacy). The Camunda Helm charts have been updated to use the new repository. If you are still using a Camunda Helm chart that references the old repository, use the to override the image repositories. See the [Bitnami GitHub announcement](https://github.com/bitnami/containers/issues/83267) for details. ## Related resources - [Helm chart version matrix](https://helm.camunda.io/camunda-platform/version-matrix/) - [Component upgrade from 8.8 to 8.9](/self-managed/upgrade/components/880-to-890.md) --- ## Upgrade to Camunda 8.9 Upgrade your Camunda 8 Self-Managed deployment from version 8.8 to 8.9. Get started by preparing your Self-Managed environment for upgrade to Camunda 8.9. Confirm upgrade eligibility, understand platform-level changes, and identify actions you might need to take before upgrading. Prepare for upgrade ## About Upgrade a Camunda 8 Self-Managed deployment from version 8.8 to 8.9. This guide applies to Self-Managed installations only and does not apply to Camunda SaaS. :::caution version 8.8 required Camunda 8 upgrades must be performed sequentially. If your deployment is running a version **earlier than 8.8**, you must complete the required version-specific upgrades listed in [upgrading from an earlier version](#upgrading-from-an-earlier-version) before you can upgrade to 8.9. ::: ## Prepare for upgrade Review required preparation steps and important changes before upgrading to Camunda 8.9. [Prepare for upgrade](prepare-for-upgrade.md) ## Upgrade guides Choose the upgrade guide that matches how your environment is deployed: ## Docker Compose deployments Docker Compose is supported for development and testing environments only. Camunda does not provide an automated upgrade process for Docker Compose deployments. To upgrade, manually upgrade each component by following the component upgrade guide: [Component upgrade from 8.8 to 8.9](./components/880-to-890.md) For production environments, use Kubernetes with the official Camunda Helm chart or create a custom deployment process using Infrastructure as Code tools such as Terraform, Ansible, or AWS CloudFormation. ## Upgrade components Some upgrades require additional component-level steps depending on which components you use or how your environment is configured. Follow the upgrade guide for your deployment method, and refer to this guide for any component-specific changes or migrations required for your setup. [Component upgrade from 8.8 to 8.9](./components/880-to-890.md) ## 8.9 release information Learn about new features, breaking changes, and deprecations in Camunda 8.9: - [What's new in Camunda 8.9](/reference/announcements-release-notes/890/whats-new-in-89.md) - [8.9 Release announcements](/reference/announcements-release-notes/890/890-announcements.md) - [8.9 Release notes](/reference/announcements-release-notes/890/890-release-notes.md) ## Upgrading from an earlier version **Camunda 8 upgrades must be performed sequentially.** - You must upgrade sequentially, one minor version at a time (for example, 8.7 → 8.8 → 8.9). - For best stability and fix coverage, use the latest available patch in each minor before and after each minor upgrade. - For example, you must upgrade from 8.7 to 8.8 before you can upgrade to 8.9. - **Do not skip releases**. Use the following version-specific upgrade guides to upgrade sequentially until you reach Camunda 8.8 before you proceed with the 8.9 upgrade. :::note Each guide covers only the changes required for that specific version upgrade. ::: ### Kubernetes with Helm - Upgrade from Camunda 8.7 to 8.8 - Upgrade from Camunda 8.6 to 8.7 - Upgrade from Camunda 8.5 to 8.6 - Upgrade from Camunda 8.4 to 8.5 - Upgrade from Camunda 8.3 to 8.4 - Upgrade from Camunda 8.2 to 8.3 ### Component-based upgrades - Component upgrade from 8.7 to 8.8 - Component upgrade from 8.6 to 8.7 - Component upgrade from 8.5 to 8.6 - Component upgrade from 8.4 to 8.5 - Component upgrade from 8.3 to 8.4 - Component upgrade from 8.2 to 8.3 --- ## Manually upgrade a local Camunda installation Upgrade a local, Self-Managed Camunda installation that was deployed from an archive distribution. ## About This guide applies to patch and minor version upgrades when Camunda is installed directly on a machine and managed manually. 1. [Plan the upgrade](#plan-the-upgrade): Identify your current version, choose the target version, and confirm the upgrade path and required changes. 2. [Back up your installation](#back-up-your-installation): Take a full backup of your Orchestration cluster, including data and configuration files. 3. [Run the upgrade](#run-the-upgrade): Stop Camunda, unpack the new version, merge configuration changes, and restart Camunda. ## Before you begin Make sure you have the following: - Administrative access to the machine running Camunda. - Enough disk space to store both the existing installation and the new archive during the upgrade. - A supported upgrade path that does not skip a minor version. ## Plan the upgrade Before making any changes: 1. Identify the currently running Camunda version (for example, from startup logs or a `README.txt` file). 1. Identify the target version you want to upgrade to. 1. Confirm that the upgrade path does not skip a minor version. If it does, perform the required intermediate upgrades first. See [Upgrading from an earlier version](/self-managed/upgrade/index.md#upgrading-from-an-earlier-version). 1. Review the component upgrade guide for any component-specific changes that apply to your setup. See [Upgrade Camunda components](/self-managed/upgrade/components/index.md). 1. Review the relevant release notes for behavioral changes or removed configuration options. See [Release notes overview](/reference/announcements-release-notes/overview.md). ## Back up your installation Always take a full backup before upgrading. See [Back up Camunda](/self-managed/operational-guides/backup-restore/backup-and-restore.md) for detailed steps. - **Patch upgrades**: A backup is recommended. - **Minor upgrades**: A backup is strongly recommended, as schema or configuration changes may be involved. Also back up any locally modified configuration files, for example: ```bash cp application.yaml application.yaml.bak ``` ## Run the upgrade ### Step 1: Stop Camunda Stop all running Camunda processes on the machine before replacing files. Upgrades replace files in place. Leaving Camunda running can result in corrupted or partially applied upgrades. ### Step 2: Clean up the existing installation In the current installation directory, remove the `lib/` folder. This prevents orphaned JAR files from older versions from causing classpath conflicts after the upgrade. ### Step 3: Apply the new version 1. Download the archive for the target Camunda version. 1. Extract the archive over the existing installation directory. 1. Review and merge changes in `application.yaml`. As an alternative, you can extract the new version into a separate directory and copy files over manually. Make sure you preserve: - The `data/` directory, which contains Zeebe partition data. - The `config/` directory, which contains your configuration files. When upgrading, merge any new default configuration options introduced in the target version. ### Step 4: Start Camunda and verify 1. Start Camunda. 1. Monitor the startup logs and verify that: - No configuration errors occur. - Any migration or deprecation warnings are understood and addressed. ## Upgrade additional installations If your environment includes multiple manual Camunda installations (for example, multiple nodes or connectors), repeat the same upgrade steps for each installation. ## Roll back Camunda does not support rolling back to a previous minor version. If you need to revert an upgrade, restore the latest backup using the [restore procedure](/self-managed/operational-guides/backup-restore/backup-and-restore.md). --- ## Prepare for upgrade Prepare your Self-Managed environment for an upgrade to Camunda 8.9. ## About Use this guide to confirm upgrade eligibility, understand platform-level changes, and identify actions you may need to take before running an upgrade. All Camunda upgrades must follow the required upgrade procedure: upgrade one minor version at a time and never skip minors. For best stability and fix coverage, use the latest available patch in each minor before and after the minor upgrade. See [version compatibility checks](../components/orchestration-cluster/core-settings/concepts/version-compatibility.md#required-upgrade-procedure). ## Evaluate your current environment Before upgrading, verify that your current installation meets the minimum requirements. | Area | What to check | | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Camunda version | Direct upgrades to 8.9 are supported from 8.8.x. If you are running an earlier version, first upgrade to 8.8. For best stability and fix coverage, use the latest available 8.8.x patch before upgrading. See [upgrading from an earlier version](/self-managed/upgrade/index.md#upgrading-from-an-earlier-version). | | Environment support | Ensure your platform and dependencies are supported in 8.9. See [supported environments](/reference/supported-environments.md). | | Customizations | Identify non-default values in Helm values, application YAML files, Ingress configuration, exporters, and secondary storage setup (for example, Elasticsearch/OpenSearch or RDBMS). | ## Review pre-upgrade actions required for Camunda 8.9 This section lists actions you must complete or review before upgrading to Camunda 8.9. | Impact | Area | What's changed / Action required | | :------------------------------------------------------------------------------------------- | :------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Action required | Helm chart: Secret configuration | Deprecated secret keys are removed in 8.9.If your `values.yaml` uses old key paths (for example, `global.license.key`, `*.existingSecret` at legacy paths), update them to the new `*.secret.existingSecret` pattern before upgrading.Additionally, `global.secrets.autoGenerated` is removed. All secrets must be explicitly provided. See [Helm chart: Deprecated secret keys removed](/reference/announcements-release-notes/890/890-announcements.md#helm-chart-deprecated-secret-keys-removed) and [Secret management](/self-managed/deployment/helm/configure/secret-management.md). | | Action required | Helm chart: document-store default | The Elasticsearch subchart is no longer enabled by default.If you use Elasticsearch, explicitly set `global.elasticsearch.enabled: true` and `elasticsearch.enabled: true` in your values file.You must also set `orchestration.data.secondaryStorage.type` explicitly (no default), whether you use Elasticsearch, OpenSearch, or RDBMS. | | Check | Helm chart: REST port | The default HTTP port for the Orchestration Cluster changed from 8090 to 8080. Check and update any hardcoded port references in network policies, Ingress rules, or monitoring. | | If customized | Web Modeler: Removed `webapp` component | Web Modeler's separate `webapp` component has been removed, and its functionality is now integrated into the `restapi` component. If you use custom configuration settings for the `webapp` or replace the `restapi` configuration using `webModeler.restapi.configuration`, you need to update your configuration. See the [component upgrade guide](/self-managed/upgrade/components/880-to-890.md#migrate-webapp-configuration). | | If customized | Web Modeler: Logging framework | Web Modeler `restapi` now uses Apache Log4j 2 instead of Logback. If you have a custom Logback configuration, migrate it before upgrading. See the [Log4j migration guide](https://logging.apache.org/log4j/2.x/migrate-from-logback.html). | | If customized | Web Modeler: Embedded web server | Web Modeler now uses Apache Tomcat instead of Undertow. If you have custom Undertow configuration (`server.undertow.*`), migrate it to Tomcat equivalents. See the [component upgrade guide](./components/880-to-890.md#embedded-web-server) for a property mapping table. | | Recommended | Helm chart: TLS secret pattern | Legacy TLS secret configuration (`*.tls.existingSecret`) is deprecated. Migrate to `*.tls.secret.existingSecret`. The legacy keys still work in 8.9. | | Recommended | Helm chart: Bitnami subcharts | The Bitnami-based subcharts (`identityPostgresql`, `identityKeycloak`, `webModelerPostgresql`, `elasticsearch`) are deprecated in 8.9 and will be removed in 8.10. Plan migration to externally managed services. | | Recommended | Helm chart: secondary storage global config | `global.elasticsearch.*` and `global.opensearch.*` are deprecated in 8.9 and will be removed in 8.10. Migrate to `orchestration.data.secondaryStorage.elasticsearch/opensearch.*` and `optimize.database.elasticsearch/opensearch.*`. Legacy keys still work in 8.9. | | Recommended | Helm chart: Orchestration profile | `orchestration.profiles.identity` is deprecated and renamed to `orchestration.profiles.admin`. The chart auto-migrates the old key with a deprecation warning. Update your values file to use the new key. | | Recommended | Helm chart: Keycloak auth secret | `global.identity.keycloak.auth.existingSecret` and `existingSecretKey` are deprecated. Migrate to `global.identity.keycloak.auth.secret.existingSecret` and `existingSecretKey`. Legacy keys still work in 8.9 via normalizers. See [Secret management](/self-managed/deployment/helm/configure/secret-management.md). | :::info For a full list of changes, see the [8.9 release announcements](/reference/announcements-release-notes/890/890-announcements.md) and [release notes](/reference/announcements-release-notes/890/890-release-notes.md). ::: ## Verify infrastructure compatibility Review your infrastructure to confirm compatibility with Camunda 8.9. | Area | 8.9 requirement | Action | | :------------------------------------------- | :------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Secondary storage (Elasticsearch/OpenSearch) | Elasticsearch 8.19+, OpenSearch 2.19+. Elasticsearch 9.2+ and OpenSearch 3.4+ now supported. | Upgrade the cluster to the minimum version. Check the [supported environments](/reference/supported-environments.md) matrix to confirm compatibility. | | Secondary storage (RDBMS) | Supported vendor and version required for your selected component set. | Check the [RDBMS support policy](/self-managed/concepts/databases/relational-db/rdbms-support-policy.md) and confirm any component-specific limitations before upgrading. | | CPU/Memory | Same consolidated Orchestration StatefulSet as 8.8. | No new requirements compared to 8.8. | | Storage | Same or higher IOPS as 8.8. | No change from 8.8. | ## Next steps Once you have confirmed upgrade eligibility and completed any required preparation steps, proceed with the upgrade method that matches your deployment: - [Upgrade Helm chart](/self-managed/upgrade/helm/index.md) - [Manual upgrade](/self-managed/upgrade/manual/index.md) :::tip For more information on component-specific changes, see the [component upgrade guide](/self-managed/upgrade/components/index.md) and version-specific documentation. :::